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_tovmsspec
300 (const char *path, char *buf, int dir_flag, int * utf8_flag);
302 /* see system service docs for $TRNLNM -- NOT the same as LNM$_MAX_INDEX */
303 #define PERL_LNM_MAX_ALLOWED_INDEX 127
305 /* OpenVMS User's Guide says at least 9 iterative translations will be performed,
306 * depending on the facility. SHOW LOGICAL does 10, so we'll imitate that for
309 #define PERL_LNM_MAX_ITER 10
311 /* New values with 7.3-2*/ /* why does max DCL have 4 byte subtracted from it? */
312 #if __CRTL_VER >= 70302000 && !defined(__VAX)
313 #define MAX_DCL_SYMBOL (8192)
314 #define MAX_DCL_LINE_LENGTH (4096 - 4)
316 #define MAX_DCL_SYMBOL (1024)
317 #define MAX_DCL_LINE_LENGTH (1024 - 4)
320 static char *__mystrtolower(char *str)
322 if (str) for (; *str; ++str) *str= tolower(*str);
326 static struct dsc$descriptor_s fildevdsc =
327 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "LNM$FILE_DEV" };
328 static struct dsc$descriptor_s crtlenvdsc =
329 { 8, DSC$K_DTYPE_T, DSC$K_CLASS_S, "CRTL_ENV" };
330 static struct dsc$descriptor_s *fildev[] = { &fildevdsc, NULL };
331 static struct dsc$descriptor_s *defenv[] = { &fildevdsc, &crtlenvdsc, NULL };
332 static struct dsc$descriptor_s **env_tables = defenv;
333 static bool will_taint = FALSE; /* tainting active, but no PL_curinterp yet */
335 /* True if we shouldn't treat barewords as logicals during directory */
337 static int no_translate_barewords;
340 static int tz_updated = 1;
343 /* DECC Features that may need to affect how Perl interprets
344 * displays filename information
346 static int decc_disable_to_vms_logname_translation = 1;
347 static int decc_disable_posix_root = 1;
348 int decc_efs_case_preserve = 0;
349 static int decc_efs_charset = 0;
350 static int decc_efs_charset_index = -1;
351 static int decc_filename_unix_no_version = 0;
352 static int decc_filename_unix_only = 0;
353 int decc_filename_unix_report = 0;
354 int decc_posix_compliant_pathnames = 0;
355 int decc_readdir_dropdotnotype = 0;
356 static int vms_process_case_tolerant = 1;
357 int vms_vtf7_filenames = 0;
358 int gnv_unix_shell = 0;
359 static int vms_unlink_all_versions = 0;
360 static int vms_posix_exit = 0;
362 /* bug workarounds if needed */
363 int decc_bug_devnull = 1;
364 int decc_dir_barename = 0;
365 int vms_bug_stat_filename = 0;
367 static int vms_debug_on_exception = 0;
368 static int vms_debug_fileify = 0;
370 /* Simple logical name translation */
371 static int simple_trnlnm
372 (const char * logname,
376 const $DESCRIPTOR(table_dsc, "LNM$FILE_DEV");
377 const unsigned long attr = LNM$M_CASE_BLIND;
378 struct dsc$descriptor_s name_dsc;
380 unsigned short result;
381 struct itmlst_3 itlst[2] = {{value_len, LNM$_STRING, value, &result},
384 name_dsc.dsc$w_length = strlen(logname);
385 name_dsc.dsc$a_pointer = (char *)logname;
386 name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
387 name_dsc.dsc$b_class = DSC$K_CLASS_S;
389 status = sys$trnlnm(&attr, &table_dsc, &name_dsc, 0, itlst);
391 if ($VMS_STATUS_SUCCESS(status)) {
393 /* Null terminate and return the string */
394 /*--------------------------------------*/
403 /* Is this a UNIX file specification?
404 * No longer a simple check with EFS file specs
405 * For now, not a full check, but need to
406 * handle POSIX ^UP^ specifications
407 * Fixing to handle ^/ cases would require
408 * changes to many other conversion routines.
411 static int is_unix_filespec(const char *path)
417 if (strncmp(path,"\"^UP^",5) != 0) {
418 pch1 = strchr(path, '/');
423 /* If the user wants UNIX files, "." needs to be treated as in UNIX */
424 if (decc_filename_unix_report || decc_filename_unix_only) {
425 if (strcmp(path,".") == 0)
433 /* This routine converts a UCS-2 character to be VTF-7 encoded.
436 static void ucs2_to_vtf7
438 unsigned long ucs2_char,
441 unsigned char * ucs_ptr;
444 ucs_ptr = (unsigned char *)&ucs2_char;
448 hex = (ucs_ptr[1] >> 4) & 0xf;
450 outspec[2] = hex + '0';
452 outspec[2] = (hex - 9) + 'A';
453 hex = ucs_ptr[1] & 0xF;
455 outspec[3] = hex + '0';
457 outspec[3] = (hex - 9) + 'A';
459 hex = (ucs_ptr[0] >> 4) & 0xf;
461 outspec[4] = hex + '0';
463 outspec[4] = (hex - 9) + 'A';
464 hex = ucs_ptr[1] & 0xF;
466 outspec[5] = hex + '0';
468 outspec[5] = (hex - 9) + 'A';
474 /* This handles the conversion of a UNIX extended character set to a ^
475 * escaped VMS character.
476 * in a UNIX file specification.
478 * The output count variable contains the number of characters added
479 * to the output string.
481 * The return value is the number of characters read from the input string
483 static int copy_expand_unix_filename_escape
484 (char *outspec, const char *inspec, int *output_cnt, const int * utf8_fl)
492 utf8_flag = *utf8_fl;
496 if (*inspec >= 0x80) {
497 if (utf8_fl && vms_vtf7_filenames) {
498 unsigned long ucs_char;
502 if ((*inspec & 0xE0) == 0xC0) {
504 ucs_char = ((inspec[0] & 0x1F) << 6) + (inspec[1] & 0x3f);
505 if (ucs_char >= 0x80) {
506 ucs2_to_vtf7(outspec, ucs_char, output_cnt);
509 } else if ((*inspec & 0xF0) == 0xE0) {
511 ucs_char = ((inspec[0] & 0xF) << 12) +
512 ((inspec[1] & 0x3f) << 6) +
514 if (ucs_char >= 0x800) {
515 ucs2_to_vtf7(outspec, ucs_char, output_cnt);
519 #if 0 /* I do not see longer sequences supported by OpenVMS */
520 /* Maybe some one can fix this later */
521 } else if ((*inspec & 0xF8) == 0xF0) {
524 } else if ((*inspec & 0xFC) == 0xF8) {
527 } else if ((*inspec & 0xFE) == 0xFC) {
534 /* High bit set, but not a Unicode character! */
536 /* Non printing DECMCS or ISO Latin-1 character? */
537 if (*inspec <= 0x9F) {
541 hex = (*inspec >> 4) & 0xF;
543 outspec[1] = hex + '0';
545 outspec[1] = (hex - 9) + 'A';
549 outspec[2] = hex + '0';
551 outspec[2] = (hex - 9) + 'A';
555 } else if (*inspec == 0xA0) {
561 } else if (*inspec == 0xFF) {
573 /* Is this a macro that needs to be passed through?
574 * Macros start with $( and an alpha character, followed
575 * by a string of alpha numeric characters ending with a )
576 * If this does not match, then encode it as ODS-5.
578 if ((inspec[0] == '$') && (inspec[1] == '(')) {
581 if (isalnum(inspec[2]) || (inspec[2] == '.') || (inspec[2] == '_')) {
583 outspec[0] = inspec[0];
584 outspec[1] = inspec[1];
585 outspec[2] = inspec[2];
587 while(isalnum(inspec[tcnt]) ||
588 (inspec[2] == '.') || (inspec[2] == '_')) {
589 outspec[tcnt] = inspec[tcnt];
592 if (inspec[tcnt] == ')') {
593 outspec[tcnt] = inspec[tcnt];
610 if (decc_efs_charset == 0)
637 /* Don't escape again if following character is
638 * already something we escape.
640 if (strchr(".~!#&\'`()+@{},;[]%^=_\\", *(inspec+1))) {
646 /* But otherwise fall through and escape it. */
648 /* Assume that this is to be escaped */
650 outspec[1] = *inspec;
654 case ' ': /* space */
655 /* Assume that this is to be escaped */
670 /* This handles the expansion of a '^' prefix to the proper character
671 * in a UNIX file specification.
673 * The output count variable contains the number of characters added
674 * to the output string.
676 * The return value is the number of characters read from the input
679 static int copy_expand_vms_filename_escape
680 (char *outspec, const char *inspec, int *output_cnt)
687 if (*inspec == '^') {
690 /* Spaces and non-trailing dots should just be passed through,
691 * but eat the escape character.
698 case '_': /* space */
704 /* Hmm. Better leave the escape escaped. */
710 case 'U': /* Unicode - FIX-ME this is wrong. */
713 scnt = strspn(inspec, "0123456789ABCDEFabcdef");
716 scnt = sscanf(inspec, "%2x%2x", &c1, &c2);
717 outspec[0] == c1 & 0xff;
718 outspec[1] == c2 & 0xff;
725 /* Error - do best we can to continue */
735 scnt = strspn(inspec, "0123456789ABCDEFabcdef");
739 scnt = sscanf(inspec, "%2x", &c1);
740 outspec[0] = c1 & 0xff;
764 (const struct dsc$descriptor_s * srcstr,
765 struct filescan_itmlst_2 * valuelist,
766 unsigned long * fldflags,
767 struct dsc$descriptor_s *auxout,
768 unsigned short * retlen);
771 /* vms_split_path - Verify that the input file specification is a
772 * VMS format file specification, and provide pointers to the components of
773 * it. With EFS format filenames, this is virtually the only way to
774 * parse a VMS path specification into components.
776 * If the sum of the components do not add up to the length of the
777 * string, then the passed file specification is probably a UNIX style
780 static int vms_split_path
795 struct dsc$descriptor path_desc;
799 struct filescan_itmlst_2 item_list[9];
800 const int filespec = 0;
801 const int nodespec = 1;
802 const int devspec = 2;
803 const int rootspec = 3;
804 const int dirspec = 4;
805 const int namespec = 5;
806 const int typespec = 6;
807 const int verspec = 7;
809 /* Assume the worst for an easy exit */
824 path_desc.dsc$a_pointer = (char *)path; /* cast ok */
825 path_desc.dsc$w_length = strlen(path);
826 path_desc.dsc$b_dtype = DSC$K_DTYPE_T;
827 path_desc.dsc$b_class = DSC$K_CLASS_S;
829 /* Get the total length, if it is shorter than the string passed
830 * then this was probably not a VMS formatted file specification
832 item_list[filespec].itmcode = FSCN$_FILESPEC;
833 item_list[filespec].length = 0;
834 item_list[filespec].component = NULL;
836 /* If the node is present, then it gets considered as part of the
837 * volume name to hopefully make things simple.
839 item_list[nodespec].itmcode = FSCN$_NODE;
840 item_list[nodespec].length = 0;
841 item_list[nodespec].component = NULL;
843 item_list[devspec].itmcode = FSCN$_DEVICE;
844 item_list[devspec].length = 0;
845 item_list[devspec].component = NULL;
847 /* root is a special case, adding it to either the directory or
848 * the device components will probalby complicate things for the
849 * callers of this routine, so leave it separate.
851 item_list[rootspec].itmcode = FSCN$_ROOT;
852 item_list[rootspec].length = 0;
853 item_list[rootspec].component = NULL;
855 item_list[dirspec].itmcode = FSCN$_DIRECTORY;
856 item_list[dirspec].length = 0;
857 item_list[dirspec].component = NULL;
859 item_list[namespec].itmcode = FSCN$_NAME;
860 item_list[namespec].length = 0;
861 item_list[namespec].component = NULL;
863 item_list[typespec].itmcode = FSCN$_TYPE;
864 item_list[typespec].length = 0;
865 item_list[typespec].component = NULL;
867 item_list[verspec].itmcode = FSCN$_VERSION;
868 item_list[verspec].length = 0;
869 item_list[verspec].component = NULL;
871 item_list[8].itmcode = 0;
872 item_list[8].length = 0;
873 item_list[8].component = NULL;
875 status = sys$filescan
876 ((const struct dsc$descriptor_s *)&path_desc, item_list,
878 _ckvmssts_noperl(status); /* All failure status values indicate a coding error */
880 /* If we parsed it successfully these two lengths should be the same */
881 if (path_desc.dsc$w_length != item_list[filespec].length)
884 /* If we got here, then it is a VMS file specification */
887 /* set the volume name */
888 if (item_list[nodespec].length > 0) {
889 *volume = item_list[nodespec].component;
890 *vol_len = item_list[nodespec].length + item_list[devspec].length;
893 *volume = item_list[devspec].component;
894 *vol_len = item_list[devspec].length;
897 *root = item_list[rootspec].component;
898 *root_len = item_list[rootspec].length;
900 *dir = item_list[dirspec].component;
901 *dir_len = item_list[dirspec].length;
903 /* Now fun with versions and EFS file specifications
904 * The parser can not tell the difference when a "." is a version
905 * delimiter or a part of the file specification.
907 if ((decc_efs_charset) &&
908 (item_list[verspec].length > 0) &&
909 (item_list[verspec].component[0] == '.')) {
910 *name = item_list[namespec].component;
911 *name_len = item_list[namespec].length + item_list[typespec].length;
912 *ext = item_list[verspec].component;
913 *ext_len = item_list[verspec].length;
918 *name = item_list[namespec].component;
919 *name_len = item_list[namespec].length;
920 *ext = item_list[typespec].component;
921 *ext_len = item_list[typespec].length;
922 *version = item_list[verspec].component;
923 *ver_len = item_list[verspec].length;
928 /* Routine to determine if the file specification ends with .dir */
929 static int is_dir_ext(char * e_spec, int e_len, char * vs_spec, int vs_len) {
931 /* e_len must be 4, and version must be <= 2 characters */
932 if (e_len != 4 || vs_len > 2)
935 /* If a version number is present, it needs to be one */
936 if ((vs_len == 2) && (vs_spec[1] != '1'))
939 /* Look for the DIR on the extension */
940 if (vms_process_case_tolerant) {
941 if ((toupper(e_spec[1]) == 'D') &&
942 (toupper(e_spec[2]) == 'I') &&
943 (toupper(e_spec[3]) == 'R')) {
947 /* Directory extensions are supposed to be in upper case only */
948 /* I would not be surprised if this rule can not be enforced */
949 /* if and when someone fully debugs the case sensitive mode */
950 if ((e_spec[1] == 'D') &&
951 (e_spec[2] == 'I') &&
952 (e_spec[3] == 'R')) {
961 * Routine to retrieve the maximum equivalence index for an input
962 * logical name. Some calls to this routine have no knowledge if
963 * the variable is a logical or not. So on error we return a max
966 /*{{{int my_maxidx(const char *lnm) */
968 my_maxidx(const char *lnm)
972 int attr = LNM$M_CASE_BLIND;
973 struct dsc$descriptor lnmdsc;
974 struct itmlst_3 itlst[2] = {{sizeof(midx), LNM$_MAX_INDEX, &midx, 0},
977 lnmdsc.dsc$w_length = strlen(lnm);
978 lnmdsc.dsc$b_dtype = DSC$K_DTYPE_T;
979 lnmdsc.dsc$b_class = DSC$K_CLASS_S;
980 lnmdsc.dsc$a_pointer = (char *) lnm; /* Cast ok for read only parameter */
982 status = sys$trnlnm(&attr, &fildevdsc, &lnmdsc, 0, itlst);
983 if ((status & 1) == 0)
990 /*{{{int vmstrnenv(const char *lnm, char *eqv, unsigned long int idx, struct dsc$descriptor_s **tabvec, unsigned long int flags) */
992 Perl_vmstrnenv(const char *lnm, char *eqv, unsigned long int idx,
993 struct dsc$descriptor_s **tabvec, unsigned long int flags)
996 char uplnm[LNM$C_NAMLENGTH+1], *cp2;
997 unsigned short int eqvlen, curtab, ivlnm = 0, ivsym = 0, ivenv = 0, secure;
998 unsigned long int retsts, attr = LNM$M_CASE_BLIND;
1000 unsigned char acmode;
1001 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
1002 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1003 struct itmlst_3 lnmlst[3] = {{sizeof idx, LNM$_INDEX, &idx, 0},
1004 {LNM$C_NAMLENGTH, LNM$_STRING, eqv, &eqvlen},
1006 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
1007 #if defined(PERL_IMPLICIT_CONTEXT)
1010 aTHX = PERL_GET_INTERP;
1016 if (!lnm || !eqv || ((idx != 0) && ((idx-1) > PERL_LNM_MAX_ALLOWED_INDEX))) {
1017 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return 0;
1019 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
1020 *cp2 = _toupper(*cp1);
1021 if (cp1 - lnm > LNM$C_NAMLENGTH) {
1022 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1026 lnmdsc.dsc$w_length = cp1 - lnm;
1027 lnmdsc.dsc$a_pointer = uplnm;
1028 uplnm[lnmdsc.dsc$w_length] = '\0';
1029 secure = flags & PERL__TRNENV_SECURE;
1030 acmode = secure ? PSL$C_EXEC : PSL$C_USER;
1031 if (!tabvec || !*tabvec) tabvec = env_tables;
1033 for (curtab = 0; tabvec[curtab]; curtab++) {
1034 if (!str$case_blind_compare(tabvec[curtab],&crtlenv)) {
1035 if (!ivenv && !secure) {
1040 #if defined(PERL_IMPLICIT_CONTEXT)
1043 "%%PERL-W-VMS_INIT Can't read CRTL environ\n");
1046 Perl_warn(aTHX_ "Can't read CRTL environ\n");
1049 retsts = SS$_NOLOGNAM;
1050 for (i = 0; environ[i]; i++) {
1051 if ((eq = strchr(environ[i],'=')) &&
1052 lnmdsc.dsc$w_length == (eq - environ[i]) &&
1053 !strncmp(environ[i],uplnm,eq - environ[i])) {
1055 for (eqvlen = 0; eq[eqvlen]; eqvlen++) eqv[eqvlen] = eq[eqvlen];
1056 if (!eqvlen) continue;
1057 retsts = SS$_NORMAL;
1061 if (retsts != SS$_NOLOGNAM) break;
1064 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
1065 !str$case_blind_compare(&tmpdsc,&clisym)) {
1066 if (!ivsym && !secure) {
1067 unsigned short int deflen = LNM$C_NAMLENGTH;
1068 struct dsc$descriptor_d eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
1069 /* dynamic dsc to accomodate possible long value */
1070 _ckvmssts_noperl(lib$sget1_dd(&deflen,&eqvdsc));
1071 retsts = lib$get_symbol(&lnmdsc,&eqvdsc,&eqvlen,0);
1073 if (eqvlen > MAX_DCL_SYMBOL) {
1074 set_errno(EVMSERR); set_vaxc_errno(LIB$_STRTRU);
1075 eqvlen = MAX_DCL_SYMBOL;
1076 /* Special hack--we might be called before the interpreter's */
1077 /* fully initialized, in which case either thr or PL_curcop */
1078 /* might be bogus. We have to check, since ckWARN needs them */
1079 /* both to be valid if running threaded */
1080 #if defined(PERL_IMPLICIT_CONTEXT)
1083 "%Perl-VMS-Init, Value of CLI symbol \"%s\" too long",lnm);
1086 if (ckWARN(WARN_MISC)) {
1087 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of CLI symbol \"%s\" too long",lnm);
1090 strncpy(eqv,eqvdsc.dsc$a_pointer,eqvlen);
1092 _ckvmssts_noperl(lib$sfree1_dd(&eqvdsc));
1093 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
1094 if (retsts == LIB$_NOSUCHSYM) continue;
1099 if ( (idx == 0) && (flags & PERL__TRNENV_JOIN_SEARCHLIST) ) {
1100 midx = my_maxidx(lnm);
1101 for (idx = 0, cp2 = eqv; idx <= midx; idx++) {
1102 lnmlst[1].bufadr = cp2;
1104 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
1105 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; break; }
1106 if (retsts == SS$_NOLOGNAM) break;
1107 /* PPFs have a prefix */
1110 *((int *)uplnm) == *((int *)"SYS$") &&
1112 eqvlen >= 4 && eqv[0] == 0x1b && eqv[1] == 0x00 &&
1113 ( (uplnm[4] == 'O' && !strcmp(uplnm,"SYS$OUTPUT")) ||
1114 (uplnm[4] == 'I' && !strcmp(uplnm,"SYS$INPUT")) ||
1115 (uplnm[4] == 'E' && !strcmp(uplnm,"SYS$ERROR")) ||
1116 (uplnm[4] == 'C' && !strcmp(uplnm,"SYS$COMMAND")) ) ) {
1117 memmove(eqv,eqv+4,eqvlen-4);
1123 if ((retsts == SS$_IVLOGNAM) ||
1124 (retsts == SS$_NOLOGNAM)) { continue; }
1127 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
1128 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
1129 if (retsts == SS$_NOLOGNAM) continue;
1132 eqvlen = strlen(eqv);
1136 if (retsts & 1) { eqv[eqvlen] = '\0'; return eqvlen; }
1137 else if (retsts == LIB$_NOSUCHSYM || retsts == LIB$_INVSYMNAM ||
1138 retsts == SS$_IVLOGNAM || retsts == SS$_IVLOGTAB ||
1139 retsts == SS$_NOLOGNAM) {
1140 set_errno(EINVAL); set_vaxc_errno(retsts);
1142 else _ckvmssts_noperl(retsts);
1144 } /* end of vmstrnenv */
1147 /*{{{ int my_trnlnm(const char *lnm, char *eqv, unsigned long int idx)*/
1148 /* Define as a function so we can access statics. */
1149 int Perl_my_trnlnm(pTHX_ const char *lnm, char *eqv, unsigned long int idx)
1153 #if defined(PERL_IMPLICIT_CONTEXT)
1156 #ifdef SECURE_INTERNAL_GETENV
1157 flags = (PL_curinterp ? PL_tainting : will_taint) ?
1158 PERL__TRNENV_SECURE : 0;
1161 return vmstrnenv(lnm, eqv, idx, fildev, flags);
1166 * Note: Uses Perl temp to store result so char * can be returned to
1167 * caller; this pointer will be invalidated at next Perl statement
1169 * We define this as a function rather than a macro in terms of my_getenv_len()
1170 * so that it'll work when PL_curinterp is undefined (and we therefore can't
1173 /*{{{ char *my_getenv(const char *lnm, bool sys)*/
1175 Perl_my_getenv(pTHX_ const char *lnm, bool sys)
1178 static char *__my_getenv_eqv = NULL;
1179 char uplnm[LNM$C_NAMLENGTH+1], *cp2, *eqv;
1180 unsigned long int idx = 0;
1181 int trnsuccess, success, secure, saverr, savvmserr;
1185 midx = my_maxidx(lnm) + 1;
1187 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
1188 /* Set up a temporary buffer for the return value; Perl will
1189 * clean it up at the next statement transition */
1190 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
1191 if (!tmpsv) return NULL;
1195 /* Assume no interpreter ==> single thread */
1196 if (__my_getenv_eqv != NULL) {
1197 Renew(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
1200 Newx(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
1202 eqv = __my_getenv_eqv;
1205 for (cp1 = lnm, cp2 = eqv; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
1206 if (cp1 - lnm == 7 && !strncmp(eqv,"DEFAULT",7)) {
1208 getcwd(eqv,LNM$C_NAMLENGTH);
1212 /* Get rid of "000000/ in rooted filespecs */
1215 zeros = strstr(eqv, "/000000/");
1216 if (zeros != NULL) {
1218 mlen = len - (zeros - eqv) - 7;
1219 memmove(zeros, &zeros[7], mlen);
1227 /* Impose security constraints only if tainting */
1229 /* Impose security constraints only if tainting */
1230 secure = PL_curinterp ? PL_tainting : will_taint;
1231 saverr = errno; savvmserr = vaxc$errno;
1238 #ifdef SECURE_INTERNAL_GETENV
1239 secure ? PERL__TRNENV_SECURE : 0
1245 /* For the getenv interface we combine all the equivalence names
1246 * of a search list logical into one value to acquire a maximum
1247 * value length of 255*128 (assuming %ENV is using logicals).
1249 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
1251 /* If the name contains a semicolon-delimited index, parse it
1252 * off and make sure we only retrieve the equivalence name for
1254 if ((cp2 = strchr(lnm,';')) != NULL) {
1256 uplnm[cp2-lnm] = '\0';
1257 idx = strtoul(cp2+1,NULL,0);
1259 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
1262 success = vmstrnenv(lnm,eqv,idx,secure ? fildev : NULL,flags);
1264 /* Discard NOLOGNAM on internal calls since we're often looking
1265 * for an optional name, and this "error" often shows up as the
1266 * (bogus) exit status for a die() call later on. */
1267 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
1268 return success ? eqv : NULL;
1271 } /* end of my_getenv() */
1275 /*{{{ SV *my_getenv_len(const char *lnm, bool sys)*/
1277 Perl_my_getenv_len(pTHX_ const char *lnm, unsigned long *len, bool sys)
1281 unsigned long idx = 0;
1283 static char *__my_getenv_len_eqv = NULL;
1284 int secure, saverr, savvmserr;
1287 midx = my_maxidx(lnm) + 1;
1289 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
1290 /* Set up a temporary buffer for the return value; Perl will
1291 * clean it up at the next statement transition */
1292 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
1293 if (!tmpsv) return NULL;
1297 /* Assume no interpreter ==> single thread */
1298 if (__my_getenv_len_eqv != NULL) {
1299 Renew(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
1302 Newx(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
1304 buf = __my_getenv_len_eqv;
1307 for (cp1 = lnm, cp2 = buf; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
1308 if (cp1 - lnm == 7 && !strncmp(buf,"DEFAULT",7)) {
1311 getcwd(buf,LNM$C_NAMLENGTH);
1314 /* Get rid of "000000/ in rooted filespecs */
1316 zeros = strstr(buf, "/000000/");
1317 if (zeros != NULL) {
1319 mlen = *len - (zeros - buf) - 7;
1320 memmove(zeros, &zeros[7], mlen);
1329 /* Impose security constraints only if tainting */
1330 secure = PL_curinterp ? PL_tainting : will_taint;
1331 saverr = errno; savvmserr = vaxc$errno;
1338 #ifdef SECURE_INTERNAL_GETENV
1339 secure ? PERL__TRNENV_SECURE : 0
1345 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
1347 if ((cp2 = strchr(lnm,';')) != NULL) {
1349 buf[cp2-lnm] = '\0';
1350 idx = strtoul(cp2+1,NULL,0);
1352 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
1355 *len = vmstrnenv(lnm,buf,idx,secure ? fildev : NULL,flags);
1357 /* Get rid of "000000/ in rooted filespecs */
1360 zeros = strstr(buf, "/000000/");
1361 if (zeros != NULL) {
1363 mlen = *len - (zeros - buf) - 7;
1364 memmove(zeros, &zeros[7], mlen);
1370 /* Discard NOLOGNAM on internal calls since we're often looking
1371 * for an optional name, and this "error" often shows up as the
1372 * (bogus) exit status for a die() call later on. */
1373 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
1374 return *len ? buf : NULL;
1377 } /* end of my_getenv_len() */
1380 static void create_mbx(unsigned short int *, struct dsc$descriptor_s *);
1382 static void riseandshine(unsigned long int dummy) { sys$wake(0,0); }
1384 /*{{{ void prime_env_iter() */
1386 prime_env_iter(void)
1387 /* Fill the %ENV associative array with all logical names we can
1388 * find, in preparation for iterating over it.
1391 static int primed = 0;
1392 HV *seenhv = NULL, *envhv;
1394 char cmd[LNM$C_NAMLENGTH+24], mbxnam[LNM$C_NAMLENGTH], *buf = NULL;
1395 unsigned short int chan;
1396 #ifndef CLI$M_TRUSTED
1397 # define CLI$M_TRUSTED 0x40 /* Missing from VAXC headers */
1399 unsigned long int defflags = CLI$M_NOWAIT | CLI$M_NOKEYPAD | CLI$M_TRUSTED;
1400 unsigned long int mbxbufsiz, flags, retsts, subpid = 0, substs = 0, wakect = 0;
1402 bool have_sym = FALSE, have_lnm = FALSE;
1403 struct dsc$descriptor_s tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1404 $DESCRIPTOR(cmddsc,cmd); $DESCRIPTOR(nldsc,"_NLA0:");
1405 $DESCRIPTOR(clidsc,"DCL"); $DESCRIPTOR(clitabdsc,"DCLTABLES");
1406 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
1407 $DESCRIPTOR(local,"_LOCAL"); $DESCRIPTOR(mbxdsc,mbxnam);
1408 #if defined(PERL_IMPLICIT_CONTEXT)
1411 #if defined(USE_ITHREADS)
1412 static perl_mutex primenv_mutex;
1413 MUTEX_INIT(&primenv_mutex);
1416 #if defined(PERL_IMPLICIT_CONTEXT)
1417 /* We jump through these hoops because we can be called at */
1418 /* platform-specific initialization time, which is before anything is */
1419 /* set up--we can't even do a plain dTHX since that relies on the */
1420 /* interpreter structure to be initialized */
1422 aTHX = PERL_GET_INTERP;
1424 /* we never get here because the NULL pointer will cause the */
1425 /* several of the routines called by this routine to access violate */
1427 /* This routine is only called by hv.c/hv_iterinit which has a */
1428 /* context, so the real fix may be to pass it through instead of */
1429 /* the hoops above */
1434 if (primed || !PL_envgv) return;
1435 MUTEX_LOCK(&primenv_mutex);
1436 if (primed) { MUTEX_UNLOCK(&primenv_mutex); return; }
1437 envhv = GvHVn(PL_envgv);
1438 /* Perform a dummy fetch as an lval to insure that the hash table is
1439 * set up. Otherwise, the hv_store() will turn into a nullop. */
1440 (void) hv_fetch(envhv,"DEFAULT",7,TRUE);
1442 for (i = 0; env_tables[i]; i++) {
1443 if (!have_sym && (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
1444 !str$case_blind_compare(&tmpdsc,&clisym)) have_sym = 1;
1445 if (!have_lnm && str$case_blind_compare(env_tables[i],&crtlenv)) have_lnm = 1;
1447 if (have_sym || have_lnm) {
1448 long int syiitm = SYI$_MAXBUF, dviitm = DVI$_DEVNAM;
1449 _ckvmssts(lib$getsyi(&syiitm, &mbxbufsiz, 0, 0, 0, 0));
1450 _ckvmssts(sys$crembx(0,&chan,mbxbufsiz,mbxbufsiz,0xff0f,0,0));
1451 _ckvmssts(lib$getdvi(&dviitm, &chan, NULL, NULL, &mbxdsc, &mbxdsc.dsc$w_length));
1454 for (i--; i >= 0; i--) {
1455 if (!str$case_blind_compare(env_tables[i],&crtlenv)) {
1458 for (j = 0; environ[j]; j++) {
1459 if (!(start = strchr(environ[j],'='))) {
1460 if (ckWARN(WARN_INTERNAL))
1461 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed CRTL environ value \"%s\"\n",environ[j]);
1465 sv = newSVpv(start,0);
1467 (void) hv_store(envhv,environ[j],start - environ[j] - 1,sv,0);
1472 else if ((tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
1473 !str$case_blind_compare(&tmpdsc,&clisym)) {
1474 strcpy(cmd,"Show Symbol/Global *");
1475 cmddsc.dsc$w_length = 20;
1476 if (env_tables[i]->dsc$w_length == 12 &&
1477 (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer + 6) &&
1478 !str$case_blind_compare(&tmpdsc,&local)) strcpy(cmd+12,"Local *");
1479 flags = defflags | CLI$M_NOLOGNAM;
1482 strcpy(cmd,"Show Logical *");
1483 if (str$case_blind_compare(env_tables[i],&fildevdsc)) {
1484 strcat(cmd," /Table=");
1485 strncat(cmd,env_tables[i]->dsc$a_pointer,env_tables[i]->dsc$w_length);
1486 cmddsc.dsc$w_length = strlen(cmd);
1488 else cmddsc.dsc$w_length = 14; /* N.B. We test this below */
1489 flags = defflags | CLI$M_NOCLISYM;
1492 /* Create a new subprocess to execute each command, to exclude the
1493 * remote possibility that someone could subvert a mbx or file used
1494 * to write multiple commands to a single subprocess.
1497 retsts = lib$spawn(&cmddsc,&nldsc,&mbxdsc,&flags,0,&subpid,&substs,
1498 0,&riseandshine,0,0,&clidsc,&clitabdsc);
1499 flags &= ~CLI$M_TRUSTED; /* Just in case we hit a really old version */
1500 defflags &= ~CLI$M_TRUSTED;
1501 } while (retsts == LIB$_INVARG && (flags | CLI$M_TRUSTED));
1503 if (!buf) Newx(buf,mbxbufsiz + 1,char);
1504 if (seenhv) SvREFCNT_dec(seenhv);
1507 char *cp1, *cp2, *key;
1508 unsigned long int sts, iosb[2], retlen, keylen;
1511 sts = sys$qiow(0,chan,IO$_READVBLK,iosb,0,0,buf,mbxbufsiz,0,0,0,0);
1512 if (sts & 1) sts = iosb[0] & 0xffff;
1513 if (sts == SS$_ENDOFFILE) {
1515 while (substs == 0) { sys$hiber(); wakect++;}
1516 if (wakect > 1) sys$wake(0,0); /* Stole someone else's wake */
1521 retlen = iosb[0] >> 16;
1522 if (!retlen) continue; /* blank line */
1524 if (iosb[1] != subpid) {
1526 Perl_croak(aTHX_ "Unknown process %x sent message to prime_env_iter: %s",buf);
1530 if (sts == SS$_BUFFEROVF && ckWARN(WARN_INTERNAL))
1531 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Buffer overflow in prime_env_iter: %s",buf);
1533 for (cp1 = buf; *cp1 && isspace(*cp1); cp1++) ;
1534 if (*cp1 == '(' || /* Logical name table name */
1535 *cp1 == '=' /* Next eqv of searchlist */) continue;
1536 if (*cp1 == '"') cp1++;
1537 for (cp2 = cp1; *cp2 && *cp2 != '"' && *cp2 != ' '; cp2++) ;
1538 key = cp1; keylen = cp2 - cp1;
1539 if (keylen && hv_exists(seenhv,key,keylen)) continue;
1540 while (*cp2 && *cp2 != '=') cp2++;
1541 while (*cp2 && *cp2 == '=') cp2++;
1542 while (*cp2 && *cp2 == ' ') cp2++;
1543 if (*cp2 == '"') { /* String translation; may embed "" */
1544 for (cp1 = buf + retlen; *cp1 != '"'; cp1--) ;
1545 cp2++; cp1--; /* Skip "" surrounding translation */
1547 else { /* Numeric translation */
1548 for (cp1 = cp2; *cp1 && *cp1 != ' '; cp1++) ;
1549 cp1--; /* stop on last non-space char */
1551 if ((!keylen || (cp1 - cp2 < -1)) && ckWARN(WARN_INTERNAL)) {
1552 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed message in prime_env_iter: |%s|",buf);
1555 PERL_HASH(hash,key,keylen);
1557 if (cp1 == cp2 && *cp2 == '.') {
1558 /* A single dot usually means an unprintable character, such as a null
1559 * to indicate a zero-length value. Get the actual value to make sure.
1561 char lnm[LNM$C_NAMLENGTH+1];
1562 char eqv[MAX_DCL_SYMBOL+1];
1564 strncpy(lnm, key, keylen);
1565 trnlen = vmstrnenv(lnm, eqv, 0, fildev, 0);
1566 sv = newSVpvn(eqv, strlen(eqv));
1569 sv = newSVpvn(cp2,cp1 - cp2 + 1);
1573 hv_store(envhv,key,keylen,sv,hash);
1574 hv_store(seenhv,key,keylen,&PL_sv_yes,hash);
1576 if (cmddsc.dsc$w_length == 14) { /* We just read LNM$FILE_DEV */
1577 /* get the PPFs for this process, not the subprocess */
1578 const char *ppfs[] = {"SYS$COMMAND", "SYS$INPUT", "SYS$OUTPUT", "SYS$ERROR", NULL};
1579 char eqv[LNM$C_NAMLENGTH+1];
1581 for (i = 0; ppfs[i]; i++) {
1582 trnlen = vmstrnenv(ppfs[i],eqv,0,fildev,0);
1583 sv = newSVpv(eqv,trnlen);
1585 hv_store(envhv,ppfs[i],strlen(ppfs[i]),sv,0);
1590 if (have_sym || have_lnm) _ckvmssts(sys$dassgn(chan));
1591 if (buf) Safefree(buf);
1592 if (seenhv) SvREFCNT_dec(seenhv);
1593 MUTEX_UNLOCK(&primenv_mutex);
1596 } /* end of prime_env_iter */
1600 /*{{{ int vmssetenv(const char *lnm, const char *eqv)*/
1601 /* Define or delete an element in the same "environment" as
1602 * vmstrnenv(). If an element is to be deleted, it's removed from
1603 * the first place it's found. If it's to be set, it's set in the
1604 * place designated by the first element of the table vector.
1605 * Like setenv() returns 0 for success, non-zero on error.
1608 Perl_vmssetenv(pTHX_ const char *lnm, const char *eqv, struct dsc$descriptor_s **tabvec)
1611 char uplnm[LNM$C_NAMLENGTH], *cp2, *c;
1612 unsigned short int curtab, ivlnm = 0, ivsym = 0, ivenv = 0;
1614 unsigned long int retsts, usermode = PSL$C_USER;
1615 struct itmlst_3 *ile, *ilist;
1616 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,uplnm},
1617 eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
1618 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1619 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
1620 $DESCRIPTOR(local,"_LOCAL");
1623 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1624 return SS$_IVLOGNAM;
1627 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
1628 *cp2 = _toupper(*cp1);
1629 if (cp1 - lnm > LNM$C_NAMLENGTH) {
1630 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1631 return SS$_IVLOGNAM;
1634 lnmdsc.dsc$w_length = cp1 - lnm;
1635 if (!tabvec || !*tabvec) tabvec = env_tables;
1637 if (!eqv) { /* we're deleting n element */
1638 for (curtab = 0; tabvec[curtab]; curtab++) {
1639 if (!ivenv && !str$case_blind_compare(tabvec[curtab],&crtlenv)) {
1641 for (i = 0; environ[i]; i++) { /* If it's an environ elt, reset */
1642 if ((cp1 = strchr(environ[i],'=')) &&
1643 lnmdsc.dsc$w_length == (cp1 - environ[i]) &&
1644 !strncmp(environ[i],lnm,cp1 - environ[i])) {
1646 return setenv(lnm,"",1) ? vaxc$errno : 0;
1649 ivenv = 1; retsts = SS$_NOLOGNAM;
1651 if (ckWARN(WARN_INTERNAL))
1652 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't reset CRTL environ elements (%s)",lnm);
1653 ivenv = 1; retsts = SS$_NOSUCHPGM;
1659 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
1660 !str$case_blind_compare(&tmpdsc,&clisym)) {
1661 unsigned int symtype;
1662 if (tabvec[curtab]->dsc$w_length == 12 &&
1663 (tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer + 6) &&
1664 !str$case_blind_compare(&tmpdsc,&local))
1665 symtype = LIB$K_CLI_LOCAL_SYM;
1666 else symtype = LIB$K_CLI_GLOBAL_SYM;
1667 retsts = lib$delete_symbol(&lnmdsc,&symtype);
1668 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
1669 if (retsts == LIB$_NOSUCHSYM) continue;
1673 retsts = sys$dellnm(tabvec[curtab],&lnmdsc,&usermode); /* try user mode first */
1674 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
1675 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
1676 retsts = lib$delete_logical(&lnmdsc,tabvec[curtab]); /* then supervisor mode */
1677 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
1681 else { /* we're defining a value */
1682 if (!ivenv && !str$case_blind_compare(tabvec[0],&crtlenv)) {
1684 return setenv(lnm,eqv,1) ? vaxc$errno : 0;
1686 if (ckWARN(WARN_INTERNAL))
1687 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't set CRTL environ elements (%s=%s)",lnm,eqv);
1688 retsts = SS$_NOSUCHPGM;
1692 eqvdsc.dsc$a_pointer = (char *) eqv; /* cast ok to readonly parameter */
1693 eqvdsc.dsc$w_length = strlen(eqv);
1694 if ((tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer) &&
1695 !str$case_blind_compare(&tmpdsc,&clisym)) {
1696 unsigned int symtype;
1697 if (tabvec[0]->dsc$w_length == 12 &&
1698 (tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer + 6) &&
1699 !str$case_blind_compare(&tmpdsc,&local))
1700 symtype = LIB$K_CLI_LOCAL_SYM;
1701 else symtype = LIB$K_CLI_GLOBAL_SYM;
1702 retsts = lib$set_symbol(&lnmdsc,&eqvdsc,&symtype);
1705 if (!*eqv) eqvdsc.dsc$w_length = 1;
1706 if (eqvdsc.dsc$w_length > LNM$C_NAMLENGTH) {
1708 nseg = (eqvdsc.dsc$w_length + LNM$C_NAMLENGTH - 1) / LNM$C_NAMLENGTH;
1709 if (nseg > PERL_LNM_MAX_ALLOWED_INDEX + 1) {
1710 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of logical \"%s\" too long. Truncating to %i bytes",
1711 lnm, LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1));
1712 eqvdsc.dsc$w_length = LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1);
1713 nseg = PERL_LNM_MAX_ALLOWED_INDEX + 1;
1716 Newx(ilist,nseg+1,struct itmlst_3);
1719 set_errno(ENOMEM); set_vaxc_errno(SS$_INSFMEM);
1722 memset(ilist, 0, (sizeof(struct itmlst_3) * (nseg+1)));
1724 for (j = 0, c = eqvdsc.dsc$a_pointer; j < nseg; j++, ile++, c += LNM$C_NAMLENGTH) {
1725 ile->itmcode = LNM$_STRING;
1727 if ((j+1) == nseg) {
1728 ile->buflen = strlen(c);
1729 /* in case we are truncating one that's too long */
1730 if (ile->buflen > LNM$C_NAMLENGTH) ile->buflen = LNM$C_NAMLENGTH;
1733 ile->buflen = LNM$C_NAMLENGTH;
1737 retsts = lib$set_logical(&lnmdsc,0,tabvec[0],0,ilist);
1741 retsts = lib$set_logical(&lnmdsc,&eqvdsc,tabvec[0],0,0);
1746 if (!(retsts & 1)) {
1748 case LIB$_AMBSYMDEF: case LIB$_INSCLIMEM:
1749 case SS$_NOLOGTAB: case SS$_TOOMANYLNAM: case SS$_IVLOGTAB:
1750 set_errno(EVMSERR); break;
1751 case LIB$_INVARG: case LIB$_INVSYMNAM: case SS$_IVLOGNAM:
1752 case LIB$_NOSUCHSYM: case SS$_NOLOGNAM:
1753 set_errno(EINVAL); break;
1755 set_errno(EACCES); break;
1760 set_vaxc_errno(retsts);
1761 return (int) retsts || 44; /* retsts should never be 0, but just in case */
1764 /* We reset error values on success because Perl does an hv_fetch()
1765 * before each hv_store(), and if the thing we're setting didn't
1766 * previously exist, we've got a leftover error message. (Of course,
1767 * this fails in the face of
1768 * $foo = $ENV{nonexistent}; $ENV{existent} = 'foo';
1769 * in that the error reported in $! isn't spurious,
1770 * but it's right more often than not.)
1772 set_errno(0); set_vaxc_errno(retsts);
1776 } /* end of vmssetenv() */
1779 /*{{{ void my_setenv(const char *lnm, const char *eqv)*/
1780 /* This has to be a function since there's a prototype for it in proto.h */
1782 Perl_my_setenv(pTHX_ const char *lnm, const char *eqv)
1785 int len = strlen(lnm);
1789 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
1790 if (!strcmp(uplnm,"DEFAULT")) {
1791 if (eqv && *eqv) my_chdir(eqv);
1795 #ifndef RTL_USES_UTC
1796 if (len == 6 || len == 2) {
1799 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
1801 if (!strcmp(uplnm,"UCX$TZ")) tz_updated = 1;
1802 if (!strcmp(uplnm,"TZ")) tz_updated = 1;
1806 (void) vmssetenv(lnm,eqv,NULL);
1810 /*{{{static void vmssetuserlnm(char *name, char *eqv); */
1812 * sets a user-mode logical in the process logical name table
1813 * used for redirection of sys$error
1816 Perl_vmssetuserlnm(pTHX_ const char *name, const char *eqv)
1818 $DESCRIPTOR(d_tab, "LNM$PROCESS");
1819 struct dsc$descriptor_d d_name = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
1820 unsigned long int iss, attr = LNM$M_CONFINE;
1821 unsigned char acmode = PSL$C_USER;
1822 struct itmlst_3 lnmlst[2] = {{0, LNM$_STRING, 0, 0},
1824 d_name.dsc$a_pointer = (char *)name; /* Cast OK for read only parameter */
1825 d_name.dsc$w_length = strlen(name);
1827 lnmlst[0].buflen = strlen(eqv);
1828 lnmlst[0].bufadr = (char *)eqv; /* Cast OK for read only parameter */
1830 iss = sys$crelnm(&attr,&d_tab,&d_name,&acmode,lnmlst);
1831 if (!(iss&1)) lib$signal(iss);
1836 /*{{{ char *my_crypt(const char *textpasswd, const char *usrname)*/
1837 /* my_crypt - VMS password hashing
1838 * my_crypt() provides an interface compatible with the Unix crypt()
1839 * C library function, and uses sys$hash_password() to perform VMS
1840 * password hashing. The quadword hashed password value is returned
1841 * as a NUL-terminated 8 character string. my_crypt() does not change
1842 * the case of its string arguments; in order to match the behavior
1843 * of LOGINOUT et al., alphabetic characters in both arguments must
1844 * be upcased by the caller.
1846 * - fix me to call ACM services when available
1849 Perl_my_crypt(pTHX_ const char *textpasswd, const char *usrname)
1851 # ifndef UAI$C_PREFERRED_ALGORITHM
1852 # define UAI$C_PREFERRED_ALGORITHM 127
1854 unsigned char alg = UAI$C_PREFERRED_ALGORITHM;
1855 unsigned short int salt = 0;
1856 unsigned long int sts;
1858 unsigned short int dsc$w_length;
1859 unsigned char dsc$b_type;
1860 unsigned char dsc$b_class;
1861 const char * dsc$a_pointer;
1862 } usrdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
1863 txtdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1864 struct itmlst_3 uailst[3] = {
1865 { sizeof alg, UAI$_ENCRYPT, &alg, 0},
1866 { sizeof salt, UAI$_SALT, &salt, 0},
1867 { 0, 0, NULL, NULL}};
1868 static char hash[9];
1870 usrdsc.dsc$w_length = strlen(usrname);
1871 usrdsc.dsc$a_pointer = usrname;
1872 if (!((sts = sys$getuai(0, 0, &usrdsc, uailst, 0, 0, 0)) & 1)) {
1874 case SS$_NOGRPPRV: case SS$_NOSYSPRV:
1878 set_errno(ESRCH); /* There isn't a Unix no-such-user error */
1883 set_vaxc_errno(sts);
1884 if (sts != RMS$_RNF) return NULL;
1887 txtdsc.dsc$w_length = strlen(textpasswd);
1888 txtdsc.dsc$a_pointer = textpasswd;
1889 if (!((sts = sys$hash_password(&txtdsc, alg, salt, &usrdsc, &hash)) & 1)) {
1890 set_errno(EVMSERR); set_vaxc_errno(sts); return NULL;
1893 return (char *) hash;
1895 } /* end of my_crypt() */
1899 static char *mp_do_rmsexpand(pTHX_ const char *, char *, int, const char *, unsigned, int *, int *);
1900 static char *mp_do_fileify_dirspec(pTHX_ const char *, char *, int, int *);
1901 static char *mp_do_tovmsspec(pTHX_ const char *, char *, int, int, int *);
1903 /* fixup barenames that are directories for internal use.
1904 * There have been problems with the consistent handling of UNIX
1905 * style directory names when routines are presented with a name that
1906 * has no directory delimitors at all. So this routine will eventually
1909 static char * fixup_bare_dirnames(const char * name)
1911 if (decc_disable_to_vms_logname_translation) {
1917 /* 8.3, remove() is now broken on symbolic links */
1918 static int rms_erase(const char * vmsname);
1922 * A little hack to get around a bug in some implemenation of remove()
1923 * that do not know how to delete a directory
1925 * Delete any file to which user has control access, regardless of whether
1926 * delete access is explicitly allowed.
1927 * Limitations: User must have write access to parent directory.
1928 * Does not block signals or ASTs; if interrupted in midstream
1929 * may leave file with an altered ACL.
1932 /*{{{int mp_do_kill_file(const char *name, int dirflag)*/
1934 mp_do_kill_file(pTHX_ const char *name, int dirflag)
1938 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
1939 unsigned long int cxt = 0, aclsts, fndsts, rmsts = -1;
1940 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1942 unsigned char myace$b_length;
1943 unsigned char myace$b_type;
1944 unsigned short int myace$w_flags;
1945 unsigned long int myace$l_access;
1946 unsigned long int myace$l_ident;
1947 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
1948 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0},
1949 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
1951 findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0},
1952 {sizeof oldace, ACL$C_READACE, &oldace, 0},{0,0,0,0}},
1953 addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}},
1954 dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}},
1955 lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}},
1956 ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}};
1958 /* Expand the input spec using RMS, since the CRTL remove() and
1959 * system services won't do this by themselves, so we may miss
1960 * a file "hiding" behind a logical name or search list. */
1961 vmsname = PerlMem_malloc(NAM$C_MAXRSS+1);
1962 if (vmsname == NULL) _ckvmssts_noperl(SS$_INSFMEM);
1964 rslt = do_rmsexpand(name,
1968 PERL_RMSEXPAND_M_VMS | PERL_RMSEXPAND_M_SYMLINK,
1972 PerlMem_free(vmsname);
1976 /* Erase the file */
1977 rmsts = rms_erase(vmsname);
1979 /* Did it succeed */
1980 if ($VMS_STATUS_SUCCESS(rmsts)) {
1981 PerlMem_free(vmsname);
1985 /* If not, can changing protections help? */
1986 if (rmsts != RMS$_PRV) {
1987 set_vaxc_errno(rmsts);
1988 PerlMem_free(vmsname);
1992 /* No, so we get our own UIC to use as a rights identifier,
1993 * and the insert an ACE at the head of the ACL which allows us
1994 * to delete the file.
1996 _ckvmssts_noperl(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
1997 fildsc.dsc$w_length = strlen(vmsname);
1998 fildsc.dsc$a_pointer = vmsname;
2000 newace.myace$l_ident = oldace.myace$l_ident;
2002 if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) {
2004 case RMS$_FNF: case RMS$_DNF: case SS$_NOSUCHOBJECT:
2005 set_errno(ENOENT); break;
2007 set_errno(ENOTDIR); break;
2009 set_errno(ENODEV); break;
2010 case RMS$_SYN: case SS$_INVFILFOROP:
2011 set_errno(EINVAL); break;
2013 set_errno(EACCES); break;
2015 _ckvmssts_noperl(aclsts);
2017 set_vaxc_errno(aclsts);
2018 PerlMem_free(vmsname);
2021 /* Grab any existing ACEs with this identifier in case we fail */
2022 aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt);
2023 if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY
2024 || fndsts == SS$_NOMOREACE ) {
2025 /* Add the new ACE . . . */
2026 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1))
2029 rmsts = rms_erase(vmsname);
2030 if ($VMS_STATUS_SUCCESS(rmsts)) {
2035 /* We blew it - dir with files in it, no write priv for
2036 * parent directory, etc. Put things back the way they were. */
2037 if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1))
2040 addlst[0].bufadr = &oldace;
2041 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1))
2048 fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0);
2049 /* We just deleted it, so of course it's not there. Some versions of
2050 * VMS seem to return success on the unlock operation anyhow (after all
2051 * the unlock is successful), but others don't.
2053 if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL;
2054 if (aclsts & 1) aclsts = fndsts;
2055 if (!(aclsts & 1)) {
2057 set_vaxc_errno(aclsts);
2060 PerlMem_free(vmsname);
2063 } /* end of kill_file() */
2067 /*{{{int do_rmdir(char *name)*/
2069 Perl_do_rmdir(pTHX_ const char *name)
2075 dirfile = PerlMem_malloc(VMS_MAXRSS + 1);
2076 if (dirfile == NULL)
2077 _ckvmssts(SS$_INSFMEM);
2079 /* Force to a directory specification */
2080 if (do_fileify_dirspec(name, dirfile, 0, NULL) == NULL) {
2081 PerlMem_free(dirfile);
2084 if (Perl_flex_lstat(aTHX_ dirfile, &st) || !S_ISDIR(st.st_mode)) {
2089 retval = mp_do_kill_file(aTHX_ dirfile, 1);
2091 PerlMem_free(dirfile);
2094 } /* end of do_rmdir */
2098 * Delete any file to which user has control access, regardless of whether
2099 * delete access is explicitly allowed.
2100 * Limitations: User must have write access to parent directory.
2101 * Does not block signals or ASTs; if interrupted in midstream
2102 * may leave file with an altered ACL.
2105 /*{{{int kill_file(char *name)*/
2107 Perl_kill_file(pTHX_ const char *name)
2109 char rspec[NAM$C_MAXRSS+1];
2114 /* Remove() is allowed to delete directories, according to the X/Open
2116 * This may need special handling to work with the ACL hacks.
2118 if ((flex_lstat(name, &st) == 0) && S_ISDIR(st.st_mode)) {
2119 rmsts = Perl_do_rmdir(aTHX_ name);
2123 rmsts = mp_do_kill_file(aTHX_ name, 0);
2127 } /* end of kill_file() */
2131 /*{{{int my_mkdir(char *,Mode_t)*/
2133 Perl_my_mkdir(pTHX_ const char *dir, Mode_t mode)
2135 STRLEN dirlen = strlen(dir);
2137 /* zero length string sometimes gives ACCVIO */
2138 if (dirlen == 0) return -1;
2140 /* CRTL mkdir() doesn't tolerate trailing /, since that implies
2141 * null file name/type. However, it's commonplace under Unix,
2142 * so we'll allow it for a gain in portability.
2144 if (dir[dirlen-1] == '/') {
2145 char *newdir = savepvn(dir,dirlen-1);
2146 int ret = mkdir(newdir,mode);
2150 else return mkdir(dir,mode);
2151 } /* end of my_mkdir */
2154 /*{{{int my_chdir(char *)*/
2156 Perl_my_chdir(pTHX_ const char *dir)
2158 STRLEN dirlen = strlen(dir);
2160 /* zero length string sometimes gives ACCVIO */
2161 if (dirlen == 0) return -1;
2164 /* Perl is passing the output of the DCL SHOW DEFAULT with leading spaces.
2165 * This does not work if DECC$EFS_CHARSET is active. Hack it here
2166 * so that existing scripts do not need to be changed.
2169 while ((dirlen > 0) && (*dir1 == ' ')) {
2174 /* some versions of CRTL chdir() doesn't tolerate trailing /, since
2176 * null file name/type. However, it's commonplace under Unix,
2177 * so we'll allow it for a gain in portability.
2179 * - Preview- '/' will be valid soon on VMS
2181 if ((dirlen > 1) && (dir1[dirlen-1] == '/')) {
2182 char *newdir = savepvn(dir1,dirlen-1);
2183 int ret = chdir(newdir);
2187 else return chdir(dir1);
2188 } /* end of my_chdir */
2192 /*{{{int my_chmod(char *, mode_t)*/
2194 Perl_my_chmod(pTHX_ const char *file_spec, mode_t mode)
2196 STRLEN speclen = strlen(file_spec);
2198 /* zero length string sometimes gives ACCVIO */
2199 if (speclen == 0) return -1;
2201 /* some versions of CRTL chmod() doesn't tolerate trailing /, since
2202 * that implies null file name/type. However, it's commonplace under Unix,
2203 * so we'll allow it for a gain in portability.
2205 * Tests are showing that chmod() on VMS 8.3 is only accepting directories
2206 * in VMS file.dir notation.
2208 if ((speclen > 1) && (file_spec[speclen-1] == '/')) {
2209 char *vms_src, *vms_dir, *rslt;
2213 /* First convert this to a VMS format specification */
2214 vms_src = PerlMem_malloc(VMS_MAXRSS);
2215 if (vms_src == NULL)
2216 _ckvmssts_noperl(SS$_INSFMEM);
2218 rslt = do_tovmsspec(file_spec, vms_src, 0, NULL);
2220 /* If we fail, then not a file specification */
2221 PerlMem_free(vms_src);
2226 /* Now make it a directory spec so chmod is happy */
2227 vms_dir = PerlMem_malloc(VMS_MAXRSS + 1);
2228 if (vms_dir == NULL)
2229 _ckvmssts_noperl(SS$_INSFMEM);
2230 rslt = do_fileify_dirspec(vms_src, vms_dir, 0, NULL);
2231 PerlMem_free(vms_src);
2235 ret = chmod(vms_dir, mode);
2239 PerlMem_free(vms_dir);
2242 else return chmod(file_spec, mode);
2243 } /* end of my_chmod */
2247 /*{{{FILE *my_tmpfile()*/
2254 if ((fp = tmpfile())) return fp;
2256 cp = PerlMem_malloc(L_tmpnam+24);
2257 if (cp == NULL) _ckvmssts_noperl(SS$_INSFMEM);
2259 if (decc_filename_unix_only == 0)
2260 strcpy(cp,"Sys$Scratch:");
2263 tmpnam(cp+strlen(cp));
2264 strcat(cp,".Perltmp");
2265 fp = fopen(cp,"w+","fop=dlt");
2272 #ifndef HOMEGROWN_POSIX_SIGNALS
2274 * The C RTL's sigaction fails to check for invalid signal numbers so we
2275 * help it out a bit. The docs are correct, but the actual routine doesn't
2276 * do what the docs say it will.
2278 /*{{{int Perl_my_sigaction (pTHX_ int, const struct sigaction*, struct sigaction*);*/
2280 Perl_my_sigaction (pTHX_ int sig, const struct sigaction* act,
2281 struct sigaction* oact)
2283 if (sig == SIGKILL || sig == SIGSTOP || sig == SIGCONT) {
2284 SETERRNO(EINVAL, SS$_INVARG);
2287 return sigaction(sig, act, oact);
2292 #ifdef KILL_BY_SIGPRC
2293 #include <errnodef.h>
2295 /* We implement our own kill() using the undocumented system service
2296 sys$sigprc for one of two reasons:
2298 1.) If the kill() in an older CRTL uses sys$forcex, causing the
2299 target process to do a sys$exit, which usually can't be handled
2300 gracefully...certainly not by Perl and the %SIG{} mechanism.
2302 2.) If the kill() in the CRTL can't be called from a signal
2303 handler without disappearing into the ether, i.e., the signal
2304 it purportedly sends is never trapped. Still true as of VMS 7.3.
2306 sys$sigprc has the same parameters as sys$forcex, but throws an exception
2307 in the target process rather than calling sys$exit.
2309 Note that distinguishing SIGSEGV from SIGBUS requires an extra arg
2310 on the ACCVIO condition, which sys$sigprc (and sys$forcex) don't
2311 provide. On VMS 7.0+ this is taken care of by doing sys$sigprc
2312 with condition codes C$_SIG0+nsig*8, catching the exception on the
2313 target process and resignaling with appropriate arguments.
2315 But we don't have that VMS 7.0+ exception handler, so if you
2316 Perl_my_kill(.., SIGSEGV) it will show up as a SIGBUS. Oh well.
2318 Also note that SIGTERM is listed in the docs as being "unimplemented",
2319 yet always seems to be signaled with a VMS condition code of 4 (and
2320 correctly handled for that code). So we hardwire it in.
2322 Unlike the VMS 7.0+ CRTL kill() function, we actually check the signal
2323 number to see if it's valid. So Perl_my_kill(pid,0) returns -1 rather
2324 than signalling with an unrecognized (and unhandled by CRTL) code.
2327 #define _MY_SIG_MAX 28
2330 Perl_sig_to_vmscondition_int(int sig)
2332 static unsigned int sig_code[_MY_SIG_MAX+1] =
2335 SS$_HANGUP, /* 1 SIGHUP */
2336 SS$_CONTROLC, /* 2 SIGINT */
2337 SS$_CONTROLY, /* 3 SIGQUIT */
2338 SS$_RADRMOD, /* 4 SIGILL */
2339 SS$_BREAK, /* 5 SIGTRAP */
2340 SS$_OPCCUS, /* 6 SIGABRT */
2341 SS$_COMPAT, /* 7 SIGEMT */
2343 SS$_FLTOVF, /* 8 SIGFPE VAX */
2345 SS$_HPARITH, /* 8 SIGFPE AXP */
2347 SS$_ABORT, /* 9 SIGKILL */
2348 SS$_ACCVIO, /* 10 SIGBUS */
2349 SS$_ACCVIO, /* 11 SIGSEGV */
2350 SS$_BADPARAM, /* 12 SIGSYS */
2351 SS$_NOMBX, /* 13 SIGPIPE */
2352 SS$_ASTFLT, /* 14 SIGALRM */
2369 #if __VMS_VER >= 60200000
2370 static int initted = 0;
2373 sig_code[16] = C$_SIGUSR1;
2374 sig_code[17] = C$_SIGUSR2;
2375 #if __CRTL_VER >= 70000000
2376 sig_code[20] = C$_SIGCHLD;
2378 #if __CRTL_VER >= 70300000
2379 sig_code[28] = C$_SIGWINCH;
2384 if (sig < _SIG_MIN) return 0;
2385 if (sig > _MY_SIG_MAX) return 0;
2386 return sig_code[sig];
2390 Perl_sig_to_vmscondition(int sig)
2393 if (vms_debug_on_exception != 0)
2394 lib$signal(SS$_DEBUG);
2396 return Perl_sig_to_vmscondition_int(sig);
2401 Perl_my_kill(int pid, int sig)
2406 int sys$sigprc(unsigned int *pidadr,
2407 struct dsc$descriptor_s *prcname,
2410 /* sig 0 means validate the PID */
2411 /*------------------------------*/
2413 const unsigned long int jpicode = JPI$_PID;
2416 status = lib$getjpi(&jpicode, &pid, NULL, &ret_pid, NULL, NULL);
2417 if ($VMS_STATUS_SUCCESS(status))
2420 case SS$_NOSUCHNODE:
2421 case SS$_UNREACHABLE:
2435 code = Perl_sig_to_vmscondition_int(sig);
2438 SETERRNO(EINVAL, SS$_BADPARAM);
2442 /* Fixme: Per official UNIX specification: If pid = 0, or negative then
2443 * signals are to be sent to multiple processes.
2444 * pid = 0 - all processes in group except ones that the system exempts
2445 * pid = -1 - all processes except ones that the system exempts
2446 * pid = -n - all processes in group (abs(n)) except ...
2447 * For now, just report as not supported.
2451 SETERRNO(ENOTSUP, SS$_UNSUPPORTED);
2455 iss = sys$sigprc((unsigned int *)&pid,0,code);
2456 if (iss&1) return 0;
2460 set_errno(EPERM); break;
2462 case SS$_NOSUCHNODE:
2463 case SS$_UNREACHABLE:
2464 set_errno(ESRCH); break;
2466 set_errno(ENOMEM); break;
2468 _ckvmssts_noperl(iss);
2471 set_vaxc_errno(iss);
2477 /* Routine to convert a VMS status code to a UNIX status code.
2478 ** More tricky than it appears because of conflicting conventions with
2481 ** VMS status codes are a bit mask, with the least significant bit set for
2484 ** Special UNIX status of EVMSERR indicates that no translation is currently
2485 ** available, and programs should check the VMS status code.
2487 ** Programs compiled with _POSIX_EXIT have a special encoding that requires
2491 #ifndef C_FACILITY_NO
2492 #define C_FACILITY_NO 0x350000
2495 #define DCL_IVVERB 0x38090
2498 int Perl_vms_status_to_unix(int vms_status, int child_flag)
2506 /* Assume the best or the worst */
2507 if (vms_status & STS$M_SUCCESS)
2510 unix_status = EVMSERR;
2512 msg_status = vms_status & ~STS$M_CONTROL;
2514 facility = vms_status & STS$M_FAC_NO;
2515 fac_sp = vms_status & STS$M_FAC_SP;
2516 msg_no = vms_status & (STS$M_MSG_NO | STS$M_SEVERITY);
2518 if (((facility == 0) || (fac_sp == 0)) && (child_flag == 0)) {
2524 unix_status = EFAULT;
2526 case SS$_DEVOFFLINE:
2527 unix_status = EBUSY;
2530 unix_status = ENOTCONN;
2538 case SS$_INVFILFOROP:
2542 unix_status = EINVAL;
2544 case SS$_UNSUPPORTED:
2545 unix_status = ENOTSUP;
2550 unix_status = EACCES;
2552 case SS$_DEVICEFULL:
2553 unix_status = ENOSPC;
2556 unix_status = ENODEV;
2558 case SS$_NOSUCHFILE:
2559 case SS$_NOSUCHOBJECT:
2560 unix_status = ENOENT;
2562 case SS$_ABORT: /* Fatal case */
2563 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_ERROR): /* Error case */
2564 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_WARNING): /* Warning case */
2565 unix_status = EINTR;
2568 unix_status = E2BIG;
2571 unix_status = ENOMEM;
2574 unix_status = EPERM;
2576 case SS$_NOSUCHNODE:
2577 case SS$_UNREACHABLE:
2578 unix_status = ESRCH;
2581 unix_status = ECHILD;
2584 if ((facility == 0) && (msg_no < 8)) {
2585 /* These are not real VMS status codes so assume that they are
2586 ** already UNIX status codes
2588 unix_status = msg_no;
2594 /* Translate a POSIX exit code to a UNIX exit code */
2595 if ((facility == C_FACILITY_NO) && ((msg_no & 0xA000) == 0xA000)) {
2596 unix_status = (msg_no & 0x07F8) >> 3;
2600 /* Documented traditional behavior for handling VMS child exits */
2601 /*--------------------------------------------------------------*/
2602 if (child_flag != 0) {
2604 /* Success / Informational return 0 */
2605 /*----------------------------------*/
2606 if (msg_no & STS$K_SUCCESS)
2609 /* Warning returns 1 */
2610 /*-------------------*/
2611 if ((msg_no & (STS$K_ERROR | STS$K_SEVERE)) == 0)
2614 /* Everything else pass through the severity bits */
2615 /*------------------------------------------------*/
2616 return (msg_no & STS$M_SEVERITY);
2619 /* Normal VMS status to ERRNO mapping attempt */
2620 /*--------------------------------------------*/
2621 switch(msg_status) {
2622 /* case RMS$_EOF: */ /* End of File */
2623 case RMS$_FNF: /* File Not Found */
2624 case RMS$_DNF: /* Dir Not Found */
2625 unix_status = ENOENT;
2627 case RMS$_RNF: /* Record Not Found */
2628 unix_status = ESRCH;
2631 unix_status = ENOTDIR;
2634 unix_status = ENODEV;
2639 unix_status = EBADF;
2642 unix_status = EEXIST;
2646 case LIB$_INVSTRDES:
2648 case LIB$_NOSUCHSYM:
2649 case LIB$_INVSYMNAM:
2651 unix_status = EINVAL;
2657 unix_status = E2BIG;
2659 case RMS$_PRV: /* No privilege */
2660 case RMS$_ACC: /* ACP file access failed */
2661 case RMS$_WLK: /* Device write locked */
2662 unix_status = EACCES;
2664 case RMS$_MKD: /* Failed to mark for delete */
2665 unix_status = EPERM;
2667 /* case RMS$_NMF: */ /* No more files */
2675 /* Try to guess at what VMS error status should go with a UNIX errno
2676 * value. This is hard to do as there could be many possible VMS
2677 * error statuses that caused the errno value to be set.
2680 int Perl_unix_status_to_vms(int unix_status)
2682 int test_unix_status;
2684 /* Trivial cases first */
2685 /*---------------------*/
2686 if (unix_status == EVMSERR)
2689 /* Is vaxc$errno sane? */
2690 /*---------------------*/
2691 test_unix_status = Perl_vms_status_to_unix(vaxc$errno, 0);
2692 if (test_unix_status == unix_status)
2695 /* If way out of range, must be VMS code already */
2696 /*-----------------------------------------------*/
2697 if (unix_status > EVMSERR)
2700 /* If out of range, punt */
2701 /*-----------------------*/
2702 if (unix_status > __ERRNO_MAX)
2706 /* Ok, now we have to do it the hard way. */
2707 /*----------------------------------------*/
2708 switch(unix_status) {
2709 case 0: return SS$_NORMAL;
2710 case EPERM: return SS$_NOPRIV;
2711 case ENOENT: return SS$_NOSUCHOBJECT;
2712 case ESRCH: return SS$_UNREACHABLE;
2713 case EINTR: return SS$_ABORT;
2716 case E2BIG: return SS$_BUFFEROVF;
2718 case EBADF: return RMS$_IFI;
2719 case ECHILD: return SS$_NONEXPR;
2721 case ENOMEM: return SS$_INSFMEM;
2722 case EACCES: return SS$_FILACCERR;
2723 case EFAULT: return SS$_ACCVIO;
2725 case EBUSY: return SS$_DEVOFFLINE;
2726 case EEXIST: return RMS$_FEX;
2728 case ENODEV: return SS$_NOSUCHDEV;
2729 case ENOTDIR: return RMS$_DIR;
2731 case EINVAL: return SS$_INVARG;
2737 case ENOSPC: return SS$_DEVICEFULL;
2738 case ESPIPE: return LIB$_INVARG;
2743 case ERANGE: return LIB$_INVARG;
2744 /* case EWOULDBLOCK */
2745 /* case EINPROGRESS */
2748 /* case EDESTADDRREQ */
2750 /* case EPROTOTYPE */
2751 /* case ENOPROTOOPT */
2752 /* case EPROTONOSUPPORT */
2753 /* case ESOCKTNOSUPPORT */
2754 /* case EOPNOTSUPP */
2755 /* case EPFNOSUPPORT */
2756 /* case EAFNOSUPPORT */
2757 /* case EADDRINUSE */
2758 /* case EADDRNOTAVAIL */
2760 /* case ENETUNREACH */
2761 /* case ENETRESET */
2762 /* case ECONNABORTED */
2763 /* case ECONNRESET */
2766 case ENOTCONN: return SS$_CLEARED;
2767 /* case ESHUTDOWN */
2768 /* case ETOOMANYREFS */
2769 /* case ETIMEDOUT */
2770 /* case ECONNREFUSED */
2772 /* case ENAMETOOLONG */
2773 /* case EHOSTDOWN */
2774 /* case EHOSTUNREACH */
2775 /* case ENOTEMPTY */
2787 /* case ECANCELED */
2791 return SS$_UNSUPPORTED;
2797 /* case EABANDONED */
2799 return SS$_ABORT; /* punt */
2802 return SS$_ABORT; /* Should not get here */
2806 /* default piping mailbox size */
2807 #define PERL_BUFSIZ 512
2811 create_mbx(unsigned short int *chan, struct dsc$descriptor_s *namdsc)
2813 unsigned long int mbxbufsiz;
2814 static unsigned long int syssize = 0;
2815 unsigned long int dviitm = DVI$_DEVNAM;
2816 char csize[LNM$C_NAMLENGTH+1];
2820 unsigned long syiitm = SYI$_MAXBUF;
2822 * Get the SYSGEN parameter MAXBUF
2824 * If the logical 'PERL_MBX_SIZE' is defined
2825 * use the value of the logical instead of PERL_BUFSIZ, but
2826 * keep the size between 128 and MAXBUF.
2829 _ckvmssts_noperl(lib$getsyi(&syiitm, &syssize, 0, 0, 0, 0));
2832 if (vmstrnenv("PERL_MBX_SIZE", csize, 0, fildev, 0)) {
2833 mbxbufsiz = atoi(csize);
2835 mbxbufsiz = PERL_BUFSIZ;
2837 if (mbxbufsiz < 128) mbxbufsiz = 128;
2838 if (mbxbufsiz > syssize) mbxbufsiz = syssize;
2840 _ckvmssts_noperl(sts = sys$crembx(0,chan,mbxbufsiz,mbxbufsiz,0,0,0));
2842 sts = lib$getdvi(&dviitm, chan, NULL, NULL, namdsc, &namdsc->dsc$w_length);
2843 _ckvmssts_noperl(sts);
2844 namdsc->dsc$a_pointer[namdsc->dsc$w_length] = '\0';
2846 } /* end of create_mbx() */
2849 /*{{{ my_popen and my_pclose*/
2851 typedef struct _iosb IOSB;
2852 typedef struct _iosb* pIOSB;
2853 typedef struct _pipe Pipe;
2854 typedef struct _pipe* pPipe;
2855 typedef struct pipe_details Info;
2856 typedef struct pipe_details* pInfo;
2857 typedef struct _srqp RQE;
2858 typedef struct _srqp* pRQE;
2859 typedef struct _tochildbuf CBuf;
2860 typedef struct _tochildbuf* pCBuf;
2863 unsigned short status;
2864 unsigned short count;
2865 unsigned long dvispec;
2868 #pragma member_alignment save
2869 #pragma nomember_alignment quadword
2870 struct _srqp { /* VMS self-relative queue entry */
2871 unsigned long qptr[2];
2873 #pragma member_alignment restore
2874 static RQE RQE_ZERO = {0,0};
2876 struct _tochildbuf {
2879 unsigned short size;
2887 unsigned short chan_in;
2888 unsigned short chan_out;
2890 unsigned int bufsize;
2902 #if defined(PERL_IMPLICIT_CONTEXT)
2903 void *thx; /* Either a thread or an interpreter */
2904 /* pointer, depending on how we're built */
2912 PerlIO *fp; /* file pointer to pipe mailbox */
2913 int useFILE; /* using stdio, not perlio */
2914 int pid; /* PID of subprocess */
2915 int mode; /* == 'r' if pipe open for reading */
2916 int done; /* subprocess has completed */
2917 int waiting; /* waiting for completion/closure */
2918 int closing; /* my_pclose is closing this pipe */
2919 unsigned long completion; /* termination status of subprocess */
2920 pPipe in; /* pipe in to sub */
2921 pPipe out; /* pipe out of sub */
2922 pPipe err; /* pipe of sub's sys$error */
2923 int in_done; /* true when in pipe finished */
2926 unsigned short xchan; /* channel to debug xterm */
2927 unsigned short xchan_valid; /* channel is assigned */
2930 struct exit_control_block
2932 struct exit_control_block *flink;
2933 unsigned long int (*exit_routine)();
2934 unsigned long int arg_count;
2935 unsigned long int *status_address;
2936 unsigned long int exit_status;
2939 typedef struct _closed_pipes Xpipe;
2940 typedef struct _closed_pipes* pXpipe;
2942 struct _closed_pipes {
2943 int pid; /* PID of subprocess */
2944 unsigned long completion; /* termination status of subprocess */
2946 #define NKEEPCLOSED 50
2947 static Xpipe closed_list[NKEEPCLOSED];
2948 static int closed_index = 0;
2949 static int closed_num = 0;
2951 #define RETRY_DELAY "0 ::0.20"
2952 #define MAX_RETRY 50
2954 static int pipe_ef = 0; /* first call to safe_popen inits these*/
2955 static unsigned long mypid;
2956 static unsigned long delaytime[2];
2958 static pInfo open_pipes = NULL;
2959 static $DESCRIPTOR(nl_desc, "NL:");
2961 #define PIPE_COMPLETION_WAIT 30 /* seconds, for EOF/FORCEX wait */
2965 static unsigned long int
2969 unsigned long int retsts = SS$_NORMAL, abort = SS$_TIMEOUT;
2970 int sts, did_stuff, need_eof, j;
2973 * Flush any pending i/o, but since we are in process run-down, be
2974 * careful about referencing PerlIO structures that may already have
2975 * been deallocated. We may not even have an interpreter anymore.
2980 #if defined(PERL_IMPLICIT_CONTEXT)
2981 /* We need to use the Perl context of the thread that created */
2985 aTHX = info->err->thx;
2987 aTHX = info->out->thx;
2989 aTHX = info->in->thx;
2992 #if defined(USE_ITHREADS)
2995 && PL_perlio_fd_refcnt)
2996 PerlIO_flush(info->fp);
2998 fflush((FILE *)info->fp);
3004 next we try sending an EOF...ignore if doesn't work, make sure we
3012 _ckvmssts_noperl(sys$setast(0));
3013 if (info->in && !info->in->shut_on_empty) {
3014 _ckvmssts_noperl(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
3019 _ckvmssts_noperl(sys$setast(1));
3023 /* wait for EOF to have effect, up to ~ 30 sec [default] */
3025 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
3030 _ckvmssts_noperl(sys$setast(0));
3031 if (info->waiting && info->done)
3033 nwait += info->waiting;
3034 _ckvmssts_noperl(sys$setast(1));
3044 _ckvmssts_noperl(sys$setast(0));
3045 if (!info->done) { /* Tap them gently on the shoulder . . .*/
3046 sts = sys$forcex(&info->pid,0,&abort);
3047 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
3050 _ckvmssts_noperl(sys$setast(1));
3054 /* again, wait for effect */
3056 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
3061 _ckvmssts_noperl(sys$setast(0));
3062 if (info->waiting && info->done)
3064 nwait += info->waiting;
3065 _ckvmssts_noperl(sys$setast(1));
3074 _ckvmssts_noperl(sys$setast(0));
3075 if (!info->done) { /* We tried to be nice . . . */
3076 sts = sys$delprc(&info->pid,0);
3077 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
3078 info->done = 1; /* sys$delprc is as done as we're going to get. */
3080 _ckvmssts_noperl(sys$setast(1));
3086 #if defined(PERL_IMPLICIT_CONTEXT)
3087 /* We need to use the Perl context of the thread that created */
3090 if (open_pipes->err)
3091 aTHX = open_pipes->err->thx;
3092 else if (open_pipes->out)
3093 aTHX = open_pipes->out->thx;
3094 else if (open_pipes->in)
3095 aTHX = open_pipes->in->thx;
3097 if ((sts = my_pclose(open_pipes->fp)) == -1) retsts = vaxc$errno;
3098 else if (!(sts & 1)) retsts = sts;
3103 static struct exit_control_block pipe_exitblock =
3104 {(struct exit_control_block *) 0,
3105 pipe_exit_routine, 0, &pipe_exitblock.exit_status, 0};
3107 static void pipe_mbxtofd_ast(pPipe p);
3108 static void pipe_tochild1_ast(pPipe p);
3109 static void pipe_tochild2_ast(pPipe p);
3112 popen_completion_ast(pInfo info)
3114 pInfo i = open_pipes;
3119 info->completion &= 0x0FFFFFFF; /* strip off "control" field */
3120 closed_list[closed_index].pid = info->pid;
3121 closed_list[closed_index].completion = info->completion;
3123 if (closed_index == NKEEPCLOSED)
3128 if (i == info) break;
3131 if (!i) return; /* unlinked, probably freed too */
3136 Writing to subprocess ...
3137 if my_pclose'd: EOF already sent, should shutdown chan_in part of pipe
3139 chan_out may be waiting for "done" flag, or hung waiting
3140 for i/o completion to child...cancel the i/o. This will
3141 put it into "snarf mode" (done but no EOF yet) that discards
3144 Output from subprocess (stdout, stderr) needs to be flushed and
3145 shut down. We try sending an EOF, but if the mbx is full the pipe
3146 routine should still catch the "shut_on_empty" flag, telling it to
3147 use immediate-style reads so that "mbx empty" -> EOF.
3151 if (info->in && !info->in_done) { /* only for mode=w */
3152 if (info->in->shut_on_empty && info->in->need_wake) {
3153 info->in->need_wake = FALSE;
3154 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,info->in,0));
3156 _ckvmssts_noperl(sys$cancel(info->in->chan_out));
3160 if (info->out && !info->out_done) { /* were we also piping output? */
3161 info->out->shut_on_empty = TRUE;
3162 iss = sys$qio(0,info->out->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
3163 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
3164 _ckvmssts_noperl(iss);
3167 if (info->err && !info->err_done) { /* we were piping stderr */
3168 info->err->shut_on_empty = TRUE;
3169 iss = sys$qio(0,info->err->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
3170 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
3171 _ckvmssts_noperl(iss);
3173 _ckvmssts_noperl(sys$setef(pipe_ef));
3177 static unsigned long int setup_cmddsc(pTHX_ const char *cmd, int check_img, int *suggest_quote, struct dsc$descriptor_s **pvmscmd);
3178 static void vms_execfree(struct dsc$descriptor_s *vmscmd);
3181 we actually differ from vmstrnenv since we use this to
3182 get the RMS IFI to check if SYS$OUTPUT and SYS$ERROR *really*
3183 are pointing to the same thing
3186 static unsigned short
3187 popen_translate(pTHX_ char *logical, char *result)
3190 $DESCRIPTOR(d_table,"LNM$PROCESS_TABLE");
3191 $DESCRIPTOR(d_log,"");
3193 unsigned short length;
3194 unsigned short code;
3196 unsigned short *retlenaddr;
3198 unsigned short l, ifi;
3200 d_log.dsc$a_pointer = logical;
3201 d_log.dsc$w_length = strlen(logical);
3203 itmlst[0].code = LNM$_STRING;
3204 itmlst[0].length = 255;
3205 itmlst[0].buffer_addr = result;
3206 itmlst[0].retlenaddr = &l;
3209 itmlst[1].length = 0;
3210 itmlst[1].buffer_addr = 0;
3211 itmlst[1].retlenaddr = 0;
3213 iss = sys$trnlnm(0, &d_table, &d_log, 0, itmlst);
3214 if (iss == SS$_NOLOGNAM) {
3218 if (!(iss&1)) lib$signal(iss);
3221 logicals for PPFs have a 4 byte prefix ESC+NUL+(RMS IFI)
3222 strip it off and return the ifi, if any
3225 if (result[0] == 0x1b && result[1] == 0x00) {
3226 memmove(&ifi,result+2,2);
3227 strcpy(result,result+4);
3229 return ifi; /* this is the RMS internal file id */
3232 static void pipe_infromchild_ast(pPipe p);
3235 I'm using LIB$(GET|FREE)_VM here so that we can allocate and deallocate
3236 inside an AST routine without worrying about reentrancy and which Perl
3237 memory allocator is being used.
3239 We read data and queue up the buffers, then spit them out one at a
3240 time to the output mailbox when the output mailbox is ready for one.
3243 #define INITIAL_TOCHILDQUEUE 2
3246 pipe_tochild_setup(pTHX_ char *rmbx, char *wmbx)
3250 char mbx1[64], mbx2[64];
3251 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3252 DSC$K_CLASS_S, mbx1},
3253 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
3254 DSC$K_CLASS_S, mbx2};
3255 unsigned int dviitm = DVI$_DEVBUFSIZ;
3259 _ckvmssts_noperl(lib$get_vm(&n, &p));
3261 create_mbx(&p->chan_in , &d_mbx1);
3262 create_mbx(&p->chan_out, &d_mbx2);
3263 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3266 p->shut_on_empty = FALSE;
3267 p->need_wake = FALSE;
3270 p->iosb.status = SS$_NORMAL;
3271 p->iosb2.status = SS$_NORMAL;
3277 #ifdef PERL_IMPLICIT_CONTEXT
3281 n = sizeof(CBuf) + p->bufsize;
3283 for (j = 0; j < INITIAL_TOCHILDQUEUE; j++) {
3284 _ckvmssts_noperl(lib$get_vm(&n, &b));
3285 b->buf = (char *) b + sizeof(CBuf);
3286 _ckvmssts_noperl(lib$insqhi(b, &p->free));
3289 pipe_tochild2_ast(p);
3290 pipe_tochild1_ast(p);
3296 /* reads the MBX Perl is writing, and queues */
3299 pipe_tochild1_ast(pPipe p)
3302 int iss = p->iosb.status;
3303 int eof = (iss == SS$_ENDOFFILE);
3305 #ifdef PERL_IMPLICIT_CONTEXT
3311 p->shut_on_empty = TRUE;
3313 _ckvmssts_noperl(sys$dassgn(p->chan_in));
3315 _ckvmssts_noperl(iss);
3319 b->size = p->iosb.count;
3320 _ckvmssts_noperl(sts = lib$insqhi(b, &p->wait));
3322 p->need_wake = FALSE;
3323 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,p,0));
3326 p->retry = 1; /* initial call */
3329 if (eof) { /* flush the free queue, return when done */
3330 int n = sizeof(CBuf) + p->bufsize;
3332 iss = lib$remqti(&p->free, &b);
3333 if (iss == LIB$_QUEWASEMP) return;
3334 _ckvmssts_noperl(iss);
3335 _ckvmssts_noperl(lib$free_vm(&n, &b));
3339 iss = lib$remqti(&p->free, &b);
3340 if (iss == LIB$_QUEWASEMP) {
3341 int n = sizeof(CBuf) + p->bufsize;
3342 _ckvmssts_noperl(lib$get_vm(&n, &b));
3343 b->buf = (char *) b + sizeof(CBuf);
3345 _ckvmssts_noperl(iss);
3349 iss = sys$qio(0,p->chan_in,
3350 IO$_READVBLK|(p->shut_on_empty ? IO$M_NOWAIT : 0),
3352 pipe_tochild1_ast, p, b->buf, p->bufsize, 0, 0, 0, 0);
3353 if (iss == SS$_ENDOFFILE && p->shut_on_empty) iss = SS$_NORMAL;
3354 _ckvmssts_noperl(iss);
3358 /* writes queued buffers to output, waits for each to complete before
3362 pipe_tochild2_ast(pPipe p)
3365 int iss = p->iosb2.status;
3366 int n = sizeof(CBuf) + p->bufsize;
3367 int done = (p->info && p->info->done) ||
3368 iss == SS$_CANCEL || iss == SS$_ABORT;
3369 #if defined(PERL_IMPLICIT_CONTEXT)
3374 if (p->type) { /* type=1 has old buffer, dispose */
3375 if (p->shut_on_empty) {
3376 _ckvmssts_noperl(lib$free_vm(&n, &b));
3378 _ckvmssts_noperl(lib$insqhi(b, &p->free));
3383 iss = lib$remqti(&p->wait, &b);
3384 if (iss == LIB$_QUEWASEMP) {
3385 if (p->shut_on_empty) {
3387 _ckvmssts_noperl(sys$dassgn(p->chan_out));
3388 *p->pipe_done = TRUE;
3389 _ckvmssts_noperl(sys$setef(pipe_ef));
3391 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF,
3392 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3396 p->need_wake = TRUE;
3399 _ckvmssts_noperl(iss);
3406 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF,
3407 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3409 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEVBLK,
3410 &p->iosb2, pipe_tochild2_ast, p, b->buf, b->size, 0, 0, 0, 0));
3419 pipe_infromchild_setup(pTHX_ char *rmbx, char *wmbx)
3422 char mbx1[64], mbx2[64];
3423 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3424 DSC$K_CLASS_S, mbx1},
3425 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
3426 DSC$K_CLASS_S, mbx2};
3427 unsigned int dviitm = DVI$_DEVBUFSIZ;
3429 int n = sizeof(Pipe);
3430 _ckvmssts_noperl(lib$get_vm(&n, &p));
3431 create_mbx(&p->chan_in , &d_mbx1);
3432 create_mbx(&p->chan_out, &d_mbx2);
3434 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3435 n = p->bufsize * sizeof(char);
3436 _ckvmssts_noperl(lib$get_vm(&n, &p->buf));
3437 p->shut_on_empty = FALSE;
3440 p->iosb.status = SS$_NORMAL;
3441 #if defined(PERL_IMPLICIT_CONTEXT)
3444 pipe_infromchild_ast(p);
3452 pipe_infromchild_ast(pPipe p)
3454 int iss = p->iosb.status;
3455 int eof = (iss == SS$_ENDOFFILE);
3456 int myeof = (eof && (p->iosb.dvispec == mypid || p->iosb.dvispec == 0));
3457 int kideof = (eof && (p->iosb.dvispec == p->info->pid));
3458 #if defined(PERL_IMPLICIT_CONTEXT)
3462 if (p->info && p->info->closing && p->chan_out) { /* output shutdown */
3463 _ckvmssts_noperl(sys$dassgn(p->chan_out));
3468 input shutdown if EOF from self (done or shut_on_empty)
3469 output shutdown if closing flag set (my_pclose)
3470 send data/eof from child or eof from self
3471 otherwise, re-read (snarf of data from child)
3476 if (myeof && p->chan_in) { /* input shutdown */
3477 _ckvmssts_noperl(sys$dassgn(p->chan_in));
3482 if (myeof || kideof) { /* pass EOF to parent */
3483 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF, &p->iosb,
3484 pipe_infromchild_ast, p,
3487 } else if (eof) { /* eat EOF --- fall through to read*/
3489 } else { /* transmit data */
3490 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEVBLK,&p->iosb,
3491 pipe_infromchild_ast,p,
3492 p->buf, p->iosb.count, 0, 0, 0, 0));
3498 /* everything shut? flag as done */
3500 if (!p->chan_in && !p->chan_out) {
3501 *p->pipe_done = TRUE;
3502 _ckvmssts_noperl(sys$setef(pipe_ef));
3506 /* write completed (or read, if snarfing from child)
3507 if still have input active,
3508 queue read...immediate mode if shut_on_empty so we get EOF if empty
3510 check if Perl reading, generate EOFs as needed
3516 iss = sys$qio(0,p->chan_in,IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0),&p->iosb,
3517 pipe_infromchild_ast,p,
3518 p->buf, p->bufsize, 0, 0, 0, 0);
3519 if (p->shut_on_empty && iss == SS$_ENDOFFILE) iss = SS$_NORMAL;
3520 _ckvmssts_noperl(iss);
3521 } else { /* send EOFs for extra reads */
3522 p->iosb.status = SS$_ENDOFFILE;
3523 p->iosb.dvispec = 0;
3524 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_SETMODE|IO$M_READATTN,
3526 pipe_infromchild_ast, p, 0, 0, 0, 0));
3532 pipe_mbxtofd_setup(pTHX_ int fd, char *out)
3536 unsigned long dviitm = DVI$_DEVBUFSIZ;
3538 struct dsc$descriptor_s d_mbx = {sizeof mbx, DSC$K_DTYPE_T,
3539 DSC$K_CLASS_S, mbx};
3540 int n = sizeof(Pipe);
3542 /* things like terminals and mbx's don't need this filter */
3543 if (fd && fstat(fd,&s) == 0) {
3544 unsigned long dviitm = DVI$_DEVCHAR, devchar;
3546 unsigned short dev_len;
3547 struct dsc$descriptor_s d_dev;
3549 struct item_list_3 items[3];
3551 unsigned short dvi_iosb[4];
3553 cptr = getname(fd, out, 1);
3554 if (cptr == NULL) _ckvmssts_noperl(SS$_NOSUCHDEV);
3555 d_dev.dsc$a_pointer = out;
3556 d_dev.dsc$w_length = strlen(out);
3557 d_dev.dsc$b_dtype = DSC$K_DTYPE_T;
3558 d_dev.dsc$b_class = DSC$K_CLASS_S;
3561 items[0].code = DVI$_DEVCHAR;
3562 items[0].bufadr = &devchar;
3563 items[0].retadr = NULL;
3565 items[1].code = DVI$_FULLDEVNAM;
3566 items[1].bufadr = device;
3567 items[1].retadr = &dev_len;
3571 status = sys$getdviw
3572 (NO_EFN, 0, &d_dev, items, dvi_iosb, NULL, NULL, NULL);
3573 _ckvmssts_noperl(status);
3574 if ($VMS_STATUS_SUCCESS(dvi_iosb[0])) {
3575 device[dev_len] = 0;
3577 if (!(devchar & DEV$M_DIR)) {
3578 strcpy(out, device);
3584 _ckvmssts_noperl(lib$get_vm(&n, &p));
3585 p->fd_out = dup(fd);
3586 create_mbx(&p->chan_in, &d_mbx);
3587 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3588 n = (p->bufsize+1) * sizeof(char);
3589 _ckvmssts_noperl(lib$get_vm(&n, &p->buf));
3590 p->shut_on_empty = FALSE;
3595 _ckvmssts_noperl(sys$qio(0, p->chan_in, IO$_READVBLK, &p->iosb,
3596 pipe_mbxtofd_ast, p,
3597 p->buf, p->bufsize, 0, 0, 0, 0));
3603 pipe_mbxtofd_ast(pPipe p)
3605 int iss = p->iosb.status;
3606 int done = p->info->done;
3608 int eof = (iss == SS$_ENDOFFILE);
3609 int myeof = eof && ((p->iosb.dvispec == mypid)||(p->iosb.dvispec == 0));
3610 int err = !(iss&1) && !eof;
3611 #if defined(PERL_IMPLICIT_CONTEXT)
3615 if (done && myeof) { /* end piping */
3617 sys$dassgn(p->chan_in);
3618 *p->pipe_done = TRUE;
3619 _ckvmssts_noperl(sys$setef(pipe_ef));
3623 if (!err && !eof) { /* good data to send to file */
3624 p->buf[p->iosb.count] = '\n';
3625 iss2 = write(p->fd_out, p->buf, p->iosb.count+1);
3628 if (p->retry < MAX_RETRY) {
3629 _ckvmssts_noperl(sys$setimr(0,delaytime,pipe_mbxtofd_ast,p));
3635 _ckvmssts_noperl(iss);
3639 iss = sys$qio(0, p->chan_in, IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0), &p->iosb,
3640 pipe_mbxtofd_ast, p,
3641 p->buf, p->bufsize, 0, 0, 0, 0);
3642 if (p->shut_on_empty && (iss == SS$_ENDOFFILE)) iss = SS$_NORMAL;
3643 _ckvmssts_noperl(iss);
3647 typedef struct _pipeloc PLOC;
3648 typedef struct _pipeloc* pPLOC;
3652 char dir[NAM$C_MAXRSS+1];
3654 static pPLOC head_PLOC = 0;
3657 free_pipelocs(pTHX_ void *head)
3660 pPLOC *pHead = (pPLOC *)head;
3672 store_pipelocs(pTHX)
3681 char temp[NAM$C_MAXRSS+1];
3685 free_pipelocs(aTHX_ &head_PLOC);
3687 /* the . directory from @INC comes last */
3689 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3690 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3691 p->next = head_PLOC;
3693 strcpy(p->dir,"./");
3695 /* get the directory from $^X */
3697 unixdir = PerlMem_malloc(VMS_MAXRSS);
3698 if (unixdir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3700 #ifdef PERL_IMPLICIT_CONTEXT
3701 if (aTHX && PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
3703 if (PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
3705 strcpy(temp, PL_origargv[0]);
3706 x = strrchr(temp,']');
3708 x = strrchr(temp,'>');
3710 /* It could be a UNIX path */
3711 x = strrchr(temp,'/');
3717 /* Got a bare name, so use default directory */
3722 if ((tounixpath_utf8(temp, unixdir, NULL)) != NULL) {
3723 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3724 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3725 p->next = head_PLOC;
3727 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3728 p->dir[NAM$C_MAXRSS] = '\0';
3732 /* reverse order of @INC entries, skip "." since entered above */
3734 #ifdef PERL_IMPLICIT_CONTEXT
3737 if (PL_incgv) av = GvAVn(PL_incgv);
3739 for (i = 0; av && i <= AvFILL(av); i++) {
3740 dirsv = *av_fetch(av,i,TRUE);
3742 if (SvROK(dirsv)) continue;
3743 dir = SvPVx(dirsv,n_a);
3744 if (strcmp(dir,".") == 0) continue;
3745 if ((tounixpath_utf8(dir, unixdir, NULL)) == NULL)
3748 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3749 p->next = head_PLOC;
3751 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3752 p->dir[NAM$C_MAXRSS] = '\0';
3755 /* most likely spot (ARCHLIB) put first in the list */
3758 if ((tounixpath_utf8(ARCHLIB_EXP, unixdir, NULL)) != NULL) {
3759 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3760 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3761 p->next = head_PLOC;
3763 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3764 p->dir[NAM$C_MAXRSS] = '\0';
3767 PerlMem_free(unixdir);
3771 Perl_cando_by_name_int
3772 (pTHX_ I32 bit, bool effective, const char *fname, int opts);
3773 #if !defined(PERL_IMPLICIT_CONTEXT)
3774 #define cando_by_name_int Perl_cando_by_name_int
3776 #define cando_by_name_int(a,b,c,d) Perl_cando_by_name_int(aTHX_ a,b,c,d)
3782 static int vmspipe_file_status = 0;
3783 static char vmspipe_file[NAM$C_MAXRSS+1];
3785 /* already found? Check and use ... need read+execute permission */
3787 if (vmspipe_file_status == 1) {
3788 if (cando_by_name_int(S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)
3789 && cando_by_name_int
3790 (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) {
3791 return vmspipe_file;
3793 vmspipe_file_status = 0;
3796 /* scan through stored @INC, $^X */
3798 if (vmspipe_file_status == 0) {
3799 char file[NAM$C_MAXRSS+1];
3800 pPLOC p = head_PLOC;
3805 strcpy(file, p->dir);
3806 dirlen = strlen(file);
3807 strncat(file, "vmspipe.com",NAM$C_MAXRSS - dirlen);
3808 file[NAM$C_MAXRSS] = '\0';
3811 exp_res = do_rmsexpand
3812 (file, vmspipe_file, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL);
3813 if (!exp_res) continue;
3815 if (cando_by_name_int
3816 (S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)
3817 && cando_by_name_int
3818 (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) {
3819 vmspipe_file_status = 1;
3820 return vmspipe_file;
3823 vmspipe_file_status = -1; /* failed, use tempfiles */
3830 vmspipe_tempfile(pTHX)
3832 char file[NAM$C_MAXRSS+1];
3834 static int index = 0;
3838 /* create a tempfile */
3840 /* we can't go from W, shr=get to R, shr=get without
3841 an intermediate vulnerable state, so don't bother trying...
3843 and lib$spawn doesn't shr=put, so have to close the write
3845 So... match up the creation date/time and the FID to
3846 make sure we're dealing with the same file
3851 if (!decc_filename_unix_only) {
3852 sprintf(file,"sys$scratch:perlpipe_%08.8x_%d.com",mypid,index);
3853 fp = fopen(file,"w");
3855 sprintf(file,"sys$login:perlpipe_%08.8x_%d.com",mypid,index);
3856 fp = fopen(file,"w");
3858 sprintf(file,"sys$disk:[]perlpipe_%08.8x_%d.com",mypid,index);
3859 fp = fopen(file,"w");
3864 sprintf(file,"/tmp/perlpipe_%08.8x_%d.com",mypid,index);
3865 fp = fopen(file,"w");
3867 sprintf(file,"/sys$login/perlpipe_%08.8x_%d.com",mypid,index);
3868 fp = fopen(file,"w");
3870 sprintf(file,"./perlpipe_%08.8x_%d.com",mypid,index);
3871 fp = fopen(file,"w");
3875 if (!fp) return 0; /* we're hosed */
3877 fprintf(fp,"$! 'f$verify(0)'\n");
3878 fprintf(fp,"$! --- protect against nonstandard definitions ---\n");
3879 fprintf(fp,"$ perl_cfile = f$environment(\"procedure\")\n");
3880 fprintf(fp,"$ perl_define = \"define/nolog\"\n");
3881 fprintf(fp,"$ perl_on = \"set noon\"\n");
3882 fprintf(fp,"$ perl_exit = \"exit\"\n");
3883 fprintf(fp,"$ perl_del = \"delete\"\n");
3884 fprintf(fp,"$ pif = \"if\"\n");
3885 fprintf(fp,"$! --- define i/o redirection (sys$output set by lib$spawn)\n");
3886 fprintf(fp,"$ pif perl_popen_in .nes. \"\" then perl_define/user/name_attributes=confine sys$input 'perl_popen_in'\n");
3887 fprintf(fp,"$ pif perl_popen_err .nes. \"\" then perl_define/user/name_attributes=confine sys$error 'perl_popen_err'\n");
3888 fprintf(fp,"$ pif perl_popen_out .nes. \"\" then perl_define sys$output 'perl_popen_out'\n");
3889 fprintf(fp,"$! --- build command line to get max possible length\n");
3890 fprintf(fp,"$c=perl_popen_cmd0\n");
3891 fprintf(fp,"$c=c+perl_popen_cmd1\n");
3892 fprintf(fp,"$c=c+perl_popen_cmd2\n");
3893 fprintf(fp,"$x=perl_popen_cmd3\n");
3894 fprintf(fp,"$c=c+x\n");
3895 fprintf(fp,"$ perl_on\n");
3896 fprintf(fp,"$ 'c'\n");
3897 fprintf(fp,"$ perl_status = $STATUS\n");
3898 fprintf(fp,"$ perl_del 'perl_cfile'\n");
3899 fprintf(fp,"$ perl_exit 'perl_status'\n");
3902 fgetname(fp, file, 1);
3903 fstat(fileno(fp), (struct stat *)&s0);
3906 if (decc_filename_unix_only)
3907 do_tounixspec(file, file, 0, NULL);
3908 fp = fopen(file,"r","shr=get");
3910 fstat(fileno(fp), (struct stat *)&s1);
3912 cmp_result = VMS_INO_T_COMPARE(s0.crtl_stat.st_ino, s1.crtl_stat.st_ino);
3913 if ((cmp_result != 0) && (s0.st_ctime != s1.st_ctime)) {
3922 static int vms_is_syscommand_xterm(void)
3924 const static struct dsc$descriptor_s syscommand_dsc =
3925 { 11, DSC$K_DTYPE_T, DSC$K_CLASS_S, "SYS$COMMAND" };
3927 const static struct dsc$descriptor_s decwdisplay_dsc =
3928 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "DECW$DISPLAY" };
3930 struct item_list_3 items[2];
3931 unsigned short dvi_iosb[4];
3932 unsigned long devchar;
3933 unsigned long devclass;
3936 /* Very simple check to guess if sys$command is a decterm? */
3937 /* First see if the DECW$DISPLAY: device exists */
3939 items[0].code = DVI$_DEVCHAR;
3940 items[0].bufadr = &devchar;
3941 items[0].retadr = NULL;
3945 status = sys$getdviw
3946 (NO_EFN, 0, &decwdisplay_dsc, items, dvi_iosb, NULL, NULL, NULL);
3948 if ($VMS_STATUS_SUCCESS(status)) {
3949 status = dvi_iosb[0];
3952 if (!$VMS_STATUS_SUCCESS(status)) {
3953 SETERRNO(EVMSERR, status);
3957 /* If it does, then for now assume that we are on a workstation */
3958 /* Now verify that SYS$COMMAND is a terminal */
3959 /* for creating the debugger DECTerm */
3962 items[0].code = DVI$_DEVCLASS;
3963 items[0].bufadr = &devclass;
3964 items[0].retadr = NULL;
3968 status = sys$getdviw
3969 (NO_EFN, 0, &syscommand_dsc, items, dvi_iosb, NULL, NULL, NULL);
3971 if ($VMS_STATUS_SUCCESS(status)) {
3972 status = dvi_iosb[0];
3975 if (!$VMS_STATUS_SUCCESS(status)) {
3976 SETERRNO(EVMSERR, status);
3980 if (devclass == DC$_TERM) {
3987 /* If we are on a DECTerm, we can pretend to fork xterms when requested */
3988 static PerlIO * create_forked_xterm(pTHX_ const char *cmd, const char *mode)
3993 char device_name[65];
3994 unsigned short device_name_len;
3995 struct dsc$descriptor_s customization_dsc;
3996 struct dsc$descriptor_s device_name_dsc;
3999 char customization[200];
4003 unsigned short p_chan;
4005 unsigned short iosb[4];
4006 struct item_list_3 items[2];
4007 const char * cust_str =
4008 "DECW$TERMINAL.iconName:\tPerl Dbg\nDECW$TERMINAL.title:\t%40s\n";
4009 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
4010 DSC$K_CLASS_S, mbx1};
4012 /* LIB$FIND_IMAGE_SIGNAL needs a handler */
4013 /*---------------------------------------*/
4014 VAXC$ESTABLISH((__vms_handler)lib$sig_to_ret);
4017 /* Make sure that this is from the Perl debugger */
4018 ret_char = strstr(cmd," xterm ");
4019 if (ret_char == NULL)
4021 cptr = ret_char + 7;
4022 ret_char = strstr(cmd,"tty");
4023 if (ret_char == NULL)
4025 ret_char = strstr(cmd,"sleep");
4026 if (ret_char == NULL)
4029 if (decw_term_port == 0) {
4030 $DESCRIPTOR(filename1_dsc, "DECW$TERMINALSHR12");
4031 $DESCRIPTOR(filename2_dsc, "DECW$TERMINALSHR");
4032 $DESCRIPTOR(decw_term_port_dsc, "DECW$TERM_PORT");
4034 status = lib$find_image_symbol
4036 &decw_term_port_dsc,
4037 (void *)&decw_term_port,
4041 /* Try again with the other image name */
4042 if (!$VMS_STATUS_SUCCESS(status)) {
4044 status = lib$find_image_symbol
4046 &decw_term_port_dsc,
4047 (void *)&decw_term_port,
4056 /* No decw$term_port, give it up */
4057 if (!$VMS_STATUS_SUCCESS(status))
4060 /* Are we on a workstation? */
4061 /* to do: capture the rows / columns and pass their properties */
4062 ret_stat = vms_is_syscommand_xterm();
4066 /* Make the title: */
4067 ret_char = strstr(cptr,"-title");
4068 if (ret_char != NULL) {
4069 while ((*cptr != 0) && (*cptr != '\"')) {
4075 while ((*cptr != 0) && (*cptr != '\"')) {
4088 strcpy(title,"Perl Debug DECTerm");
4090 sprintf(customization, cust_str, title);
4092 customization_dsc.dsc$a_pointer = customization;
4093 customization_dsc.dsc$w_length = strlen(customization);
4094 customization_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
4095 customization_dsc.dsc$b_class = DSC$K_CLASS_S;
4097 device_name_dsc.dsc$a_pointer = device_name;
4098 device_name_dsc.dsc$w_length = sizeof device_name -1;
4099 device_name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
4100 device_name_dsc.dsc$b_class = DSC$K_CLASS_S;
4102 device_name_len = 0;
4104 /* Try to create the window */
4105 status = (*decw_term_port)
4114 if (!$VMS_STATUS_SUCCESS(status)) {
4115 SETERRNO(EVMSERR, status);
4119 device_name[device_name_len] = '\0';
4121 /* Need to set this up to look like a pipe for cleanup */
4123 status = lib$get_vm(&n, &info);
4124 if (!$VMS_STATUS_SUCCESS(status)) {
4125 SETERRNO(ENOMEM, status);
4131 info->completion = 0;
4132 info->closing = FALSE;
4139 info->in_done = TRUE;
4140 info->out_done = TRUE;
4141 info->err_done = TRUE;
4143 /* Assign a channel on this so that it will persist, and not login */
4144 /* We stash this channel in the info structure for reference. */
4145 /* The created xterm self destructs when the last channel is removed */
4146 /* and it appears that perl5db.pl (perl debugger) does this routinely */
4147 /* So leave this assigned. */
4148 device_name_dsc.dsc$w_length = device_name_len;
4149 status = sys$assign(&device_name_dsc,&info->xchan,0,0);
4150 if (!$VMS_STATUS_SUCCESS(status)) {
4151 SETERRNO(EVMSERR, status);
4154 info->xchan_valid = 1;
4156 /* Now create a mailbox to be read by the application */
4158 create_mbx(&p_chan, &d_mbx1);
4160 /* write the name of the created terminal to the mailbox */
4161 status = sys$qiow(NO_EFN, p_chan, IO$_WRITEVBLK|IO$M_NOW,
4162 iosb, NULL, NULL, device_name, device_name_len, 0, 0, 0, 0);
4164 if (!$VMS_STATUS_SUCCESS(status)) {
4165 SETERRNO(EVMSERR, status);
4169 info->fp = PerlIO_open(mbx1, mode);
4171 /* Done with this channel */
4174 /* If any errors, then clean up */
4177 _ckvmssts_noperl(lib$free_vm(&n, &info));
4185 static I32 my_pclose_pinfo(pTHX_ pInfo info);
4188 safe_popen(pTHX_ const char *cmd, const char *in_mode, int *psts)
4190 static int handler_set_up = FALSE;
4192 unsigned long int sts, flags = CLI$M_NOWAIT;
4193 /* The use of a GLOBAL table (as was done previously) rendered
4194 * Perl's qx() or `` unusable from a C<$ SET SYMBOL/SCOPE=NOGLOBAL> DCL
4195 * environment. Hence we've switched to LOCAL symbol table.
4197 unsigned int table = LIB$K_CLI_LOCAL_SYM;
4199 char *p, mode[10], symbol[MAX_DCL_SYMBOL+1], *vmspipe;
4200 char *in, *out, *err, mbx[512];
4202 char tfilebuf[NAM$C_MAXRSS+1];
4204 char cmd_sym_name[20];
4205 struct dsc$descriptor_s d_symbol= {0, DSC$K_DTYPE_T,
4206 DSC$K_CLASS_S, symbol};
4207 struct dsc$descriptor_s vmspipedsc = {0, DSC$K_DTYPE_T,
4209 struct dsc$descriptor_s d_sym_cmd = {0, DSC$K_DTYPE_T,
4210 DSC$K_CLASS_S, cmd_sym_name};
4211 struct dsc$descriptor_s *vmscmd;
4212 $DESCRIPTOR(d_sym_in ,"PERL_POPEN_IN");
4213 $DESCRIPTOR(d_sym_out,"PERL_POPEN_OUT");
4214 $DESCRIPTOR(d_sym_err,"PERL_POPEN_ERR");
4216 /* Check here for Xterm create request. This means looking for
4217 * "3>&1 xterm\b" and "\btty 1>&3\b$" in the command, and that it
4218 * is possible to create an xterm.
4220 if (*in_mode == 'r') {
4223 xterm_fd = create_forked_xterm(aTHX_ cmd, in_mode);
4224 if (xterm_fd != NULL)
4228 if (!head_PLOC) store_pipelocs(aTHX); /* at least TRY to use a static vmspipe file */
4230 /* once-per-program initialization...
4231 note that the SETAST calls and the dual test of pipe_ef
4232 makes sure that only the FIRST thread through here does
4233 the initialization...all other threads wait until it's
4236 Yeah, uglier than a pthread call, it's got all the stuff inline
4237 rather than in a separate routine.
4241 _ckvmssts_noperl(sys$setast(0));
4243 unsigned long int pidcode = JPI$_PID;
4244 $DESCRIPTOR(d_delay, RETRY_DELAY);
4245 _ckvmssts_noperl(lib$get_ef(&pipe_ef));
4246 _ckvmssts_noperl(lib$getjpi(&pidcode,0,0,&mypid,0,0));
4247 _ckvmssts_noperl(sys$bintim(&d_delay, delaytime));
4249 if (!handler_set_up) {
4250 _ckvmssts_noperl(sys$dclexh(&pipe_exitblock));
4251 handler_set_up = TRUE;
4253 _ckvmssts_noperl(sys$setast(1));
4256 /* see if we can find a VMSPIPE.COM */
4259 vmspipe = find_vmspipe(aTHX);
4261 strcpy(tfilebuf+1,vmspipe);
4262 } else { /* uh, oh...we're in tempfile hell */
4263 tpipe = vmspipe_tempfile(aTHX);
4264 if (!tpipe) { /* a fish popular in Boston */
4265 if (ckWARN(WARN_PIPE)) {
4266 Perl_warner(aTHX_ packWARN(WARN_PIPE),"unable to find VMSPIPE.COM for i/o piping");
4270 fgetname(tpipe,tfilebuf+1,1);
4272 vmspipedsc.dsc$a_pointer = tfilebuf;
4273 vmspipedsc.dsc$w_length = strlen(tfilebuf);
4275 sts = setup_cmddsc(aTHX_ cmd,0,0,&vmscmd);
4278 case RMS$_FNF: case RMS$_DNF:
4279 set_errno(ENOENT); break;
4281 set_errno(ENOTDIR); break;
4283 set_errno(ENODEV); break;
4285 set_errno(EACCES); break;
4287 set_errno(EINVAL); break;
4288 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
4289 set_errno(E2BIG); break;
4290 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
4291 _ckvmssts_noperl(sts); /* fall through */
4292 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
4295 set_vaxc_errno(sts);
4296 if (*in_mode != 'n' && ckWARN(WARN_PIPE)) {
4297 Perl_warner(aTHX_ packWARN(WARN_PIPE),"Can't pipe \"%*s\": %s", strlen(cmd), cmd, Strerror(errno));
4303 _ckvmssts_noperl(lib$get_vm(&n, &info));
4305 strcpy(mode,in_mode);
4308 info->completion = 0;
4309 info->closing = FALSE;
4316 info->in_done = TRUE;
4317 info->out_done = TRUE;
4318 info->err_done = TRUE;
4320 info->xchan_valid = 0;
4322 in = PerlMem_malloc(VMS_MAXRSS);
4323 if (in == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4324 out = PerlMem_malloc(VMS_MAXRSS);
4325 if (out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4326 err = PerlMem_malloc(VMS_MAXRSS);
4327 if (err == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4329 in[0] = out[0] = err[0] = '\0';
4331 if ((p = strchr(mode,'F')) != NULL) { /* F -> use FILE* */
4335 if ((p = strchr(mode,'W')) != NULL) { /* W -> wait for completion */
4340 if (*mode == 'r') { /* piping from subroutine */
4342 info->out = pipe_infromchild_setup(aTHX_ mbx,out);
4344 info->out->pipe_done = &info->out_done;
4345 info->out_done = FALSE;
4346 info->out->info = info;
4348 if (!info->useFILE) {
4349 info->fp = PerlIO_open(mbx, mode);
4351 info->fp = (PerlIO *) freopen(mbx, mode, stdin);
4352 Perl_vmssetuserlnm(aTHX_ "SYS$INPUT",mbx);
4355 if (!info->fp && info->out) {
4356 sys$cancel(info->out->chan_out);
4358 while (!info->out_done) {
4360 _ckvmssts_noperl(sys$setast(0));
4361 done = info->out_done;
4362 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4363 _ckvmssts_noperl(sys$setast(1));
4364 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4367 if (info->out->buf) {
4368 n = info->out->bufsize * sizeof(char);
4369 _ckvmssts_noperl(lib$free_vm(&n, &info->out->buf));
4372 _ckvmssts_noperl(lib$free_vm(&n, &info->out));
4374 _ckvmssts_noperl(lib$free_vm(&n, &info));
4379 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4381 info->err->pipe_done = &info->err_done;
4382 info->err_done = FALSE;
4383 info->err->info = info;
4386 } else if (*mode == 'w') { /* piping to subroutine */
4388 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
4390 info->out->pipe_done = &info->out_done;
4391 info->out_done = FALSE;
4392 info->out->info = info;
4395 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4397 info->err->pipe_done = &info->err_done;
4398 info->err_done = FALSE;
4399 info->err->info = info;
4402 info->in = pipe_tochild_setup(aTHX_ in,mbx);
4403 if (!info->useFILE) {
4404 info->fp = PerlIO_open(mbx, mode);
4406 info->fp = (PerlIO *) freopen(mbx, mode, stdout);
4407 Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",mbx);
4411 info->in->pipe_done = &info->in_done;
4412 info->in_done = FALSE;
4413 info->in->info = info;
4417 if (!info->fp && info->in) {
4419 _ckvmssts_noperl(sys$qiow(0,info->in->chan_in, IO$_WRITEOF, 0,
4420 0, 0, 0, 0, 0, 0, 0, 0));
4422 while (!info->in_done) {
4424 _ckvmssts_noperl(sys$setast(0));
4425 done = info->in_done;
4426 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4427 _ckvmssts_noperl(sys$setast(1));
4428 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4431 if (info->in->buf) {
4432 n = info->in->bufsize * sizeof(char);
4433 _ckvmssts_noperl(lib$free_vm(&n, &info->in->buf));
4436 _ckvmssts_noperl(lib$free_vm(&n, &info->in));
4438 _ckvmssts_noperl(lib$free_vm(&n, &info));
4444 } else if (*mode == 'n') { /* separate subprocess, no Perl i/o */
4445 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
4447 info->out->pipe_done = &info->out_done;
4448 info->out_done = FALSE;
4449 info->out->info = info;
4452 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4454 info->err->pipe_done = &info->err_done;
4455 info->err_done = FALSE;
4456 info->err->info = info;
4460 symbol[MAX_DCL_SYMBOL] = '\0';
4462 strncpy(symbol, in, MAX_DCL_SYMBOL);
4463 d_symbol.dsc$w_length = strlen(symbol);
4464 _ckvmssts_noperl(lib$set_symbol(&d_sym_in, &d_symbol, &table));
4466 strncpy(symbol, err, MAX_DCL_SYMBOL);
4467 d_symbol.dsc$w_length = strlen(symbol);
4468 _ckvmssts_noperl(lib$set_symbol(&d_sym_err, &d_symbol, &table));
4470 strncpy(symbol, out, MAX_DCL_SYMBOL);
4471 d_symbol.dsc$w_length = strlen(symbol);
4472 _ckvmssts_noperl(lib$set_symbol(&d_sym_out, &d_symbol, &table));
4474 /* Done with the names for the pipes */
4479 p = vmscmd->dsc$a_pointer;
4480 while (*p == ' ' || *p == '\t') p++; /* remove leading whitespace */
4481 if (*p == '$') p++; /* remove leading $ */
4482 while (*p == ' ' || *p == '\t') p++;
4484 for (j = 0; j < 4; j++) {
4485 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
4486 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
4488 strncpy(symbol, p, MAX_DCL_SYMBOL);
4489 d_symbol.dsc$w_length = strlen(symbol);
4490 _ckvmssts_noperl(lib$set_symbol(&d_sym_cmd, &d_symbol, &table));
4492 if (strlen(p) > MAX_DCL_SYMBOL) {
4493 p += MAX_DCL_SYMBOL;
4498 _ckvmssts_noperl(sys$setast(0));
4499 info->next=open_pipes; /* prepend to list */
4501 _ckvmssts_noperl(sys$setast(1));
4502 /* Omit arg 2 (input file) so the child will get the parent's SYS$INPUT
4503 * and SYS$COMMAND. vmspipe.com will redefine SYS$INPUT, but we'll still
4504 * have SYS$COMMAND if we need it.
4506 _ckvmssts_noperl(lib$spawn(&vmspipedsc, 0, &nl_desc, &flags,
4507 0, &info->pid, &info->completion,
4508 0, popen_completion_ast,info,0,0,0));
4510 /* if we were using a tempfile, close it now */
4512 if (tpipe) fclose(tpipe);
4514 /* once the subprocess is spawned, it has copied the symbols and
4515 we can get rid of ours */
4517 for (j = 0; j < 4; j++) {
4518 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
4519 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
4520 _ckvmssts_noperl(lib$delete_symbol(&d_sym_cmd, &table));
4522 _ckvmssts_noperl(lib$delete_symbol(&d_sym_in, &table));
4523 _ckvmssts_noperl(lib$delete_symbol(&d_sym_err, &table));
4524 _ckvmssts_noperl(lib$delete_symbol(&d_sym_out, &table));
4525 vms_execfree(vmscmd);
4527 #ifdef PERL_IMPLICIT_CONTEXT
4530 PL_forkprocess = info->pid;
4537 _ckvmssts_noperl(sys$setast(0));
4539 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4540 _ckvmssts_noperl(sys$setast(1));
4541 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4543 *psts = info->completion;
4544 /* Caller thinks it is open and tries to close it. */
4545 /* This causes some problems, as it changes the error status */
4546 /* my_pclose(info->fp); */
4548 /* If we did not have a file pointer open, then we have to */
4549 /* clean up here or eventually we will run out of something */
4551 if (info->fp == NULL) {
4552 my_pclose_pinfo(aTHX_ info);
4560 } /* end of safe_popen */
4563 /*{{{ PerlIO *my_popen(char *cmd, char *mode)*/
4565 Perl_my_popen(pTHX_ const char *cmd, const char *mode)
4569 TAINT_PROPER("popen");
4570 PERL_FLUSHALL_FOR_CHILD;
4571 return safe_popen(aTHX_ cmd,mode,&sts);
4577 /* Routine to close and cleanup a pipe info structure */
4579 static I32 my_pclose_pinfo(pTHX_ pInfo info) {
4581 unsigned long int retsts;
4586 /* If we were writing to a subprocess, insure that someone reading from
4587 * the mailbox gets an EOF. It looks like a simple fclose() doesn't
4588 * produce an EOF record in the mailbox.
4590 * well, at least sometimes it *does*, so we have to watch out for
4591 * the first EOF closing the pipe (and DASSGN'ing the channel)...
4595 #if defined(USE_ITHREADS)
4598 && PL_perlio_fd_refcnt)
4599 PerlIO_flush(info->fp);
4601 fflush((FILE *)info->fp);
4604 _ckvmssts(sys$setast(0));
4605 info->closing = TRUE;
4606 done = info->done && info->in_done && info->out_done && info->err_done;
4607 /* hanging on write to Perl's input? cancel it */
4608 if (info->mode == 'r' && info->out && !info->out_done) {
4609 if (info->out->chan_out) {
4610 _ckvmssts(sys$cancel(info->out->chan_out));
4611 if (!info->out->chan_in) { /* EOF generation, need AST */
4612 _ckvmssts(sys$dclast(pipe_infromchild_ast,info->out,0));
4616 if (info->in && !info->in_done && !info->in->shut_on_empty) /* EOF if hasn't had one yet */
4617 _ckvmssts(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
4619 _ckvmssts(sys$setast(1));
4622 #if defined(USE_ITHREADS)
4625 && PL_perlio_fd_refcnt)
4626 PerlIO_close(info->fp);
4628 fclose((FILE *)info->fp);
4631 we have to wait until subprocess completes, but ALSO wait until all
4632 the i/o completes...otherwise we'll be freeing the "info" structure
4633 that the i/o ASTs could still be using...
4637 _ckvmssts(sys$setast(0));
4638 done = info->done && info->in_done && info->out_done && info->err_done;
4639 if (!done) _ckvmssts(sys$clref(pipe_ef));
4640 _ckvmssts(sys$setast(1));
4641 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4643 retsts = info->completion;
4645 /* remove from list of open pipes */
4646 _ckvmssts(sys$setast(0));
4648 for (next = open_pipes; next != NULL; last = next, next = next->next) {
4654 last->next = info->next;
4656 open_pipes = info->next;
4657 _ckvmssts(sys$setast(1));
4659 /* free buffers and structures */
4662 if (info->in->buf) {
4663 n = info->in->bufsize * sizeof(char);
4664 _ckvmssts(lib$free_vm(&n, &info->in->buf));
4667 _ckvmssts(lib$free_vm(&n, &info->in));
4670 if (info->out->buf) {
4671 n = info->out->bufsize * sizeof(char);
4672 _ckvmssts(lib$free_vm(&n, &info->out->buf));
4675 _ckvmssts(lib$free_vm(&n, &info->out));
4678 if (info->err->buf) {
4679 n = info->err->bufsize * sizeof(char);
4680 _ckvmssts(lib$free_vm(&n, &info->err->buf));
4683 _ckvmssts(lib$free_vm(&n, &info->err));
4686 _ckvmssts(lib$free_vm(&n, &info));
4692 /*{{{ I32 my_pclose(PerlIO *fp)*/
4693 I32 Perl_my_pclose(pTHX_ PerlIO *fp)
4695 pInfo info, last = NULL;
4698 /* Fixme - need ast and mutex protection here */
4699 for (info = open_pipes; info != NULL; last = info, info = info->next)
4700 if (info->fp == fp) break;
4702 if (info == NULL) { /* no such pipe open */
4703 set_errno(ECHILD); /* quoth POSIX */
4704 set_vaxc_errno(SS$_NONEXPR);
4708 ret_status = my_pclose_pinfo(aTHX_ info);
4712 } /* end of my_pclose() */
4714 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
4715 /* Roll our own prototype because we want this regardless of whether
4716 * _VMS_WAIT is defined.
4718 __pid_t __vms_waitpid( __pid_t __pid, int *__stat_loc, int __options );
4720 /* sort-of waitpid; special handling of pipe clean-up for subprocesses
4721 created with popen(); otherwise partially emulate waitpid() unless
4722 we have a suitable one from the CRTL that came with VMS 7.2 and later.
4723 Also check processes not considered by the CRTL waitpid().
4725 /*{{{Pid_t my_waitpid(Pid_t pid, int *statusp, int flags)*/
4727 Perl_my_waitpid(pTHX_ Pid_t pid, int *statusp, int flags)
4734 if (statusp) *statusp = 0;
4736 for (info = open_pipes; info != NULL; info = info->next)
4737 if (info->pid == pid) break;
4739 if (info != NULL) { /* we know about this child */
4740 while (!info->done) {
4741 _ckvmssts(sys$setast(0));
4743 if (!done) _ckvmssts(sys$clref(pipe_ef));
4744 _ckvmssts(sys$setast(1));
4745 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4748 if (statusp) *statusp = info->completion;
4752 /* child that already terminated? */
4754 for (j = 0; j < NKEEPCLOSED && j < closed_num; j++) {
4755 if (closed_list[j].pid == pid) {
4756 if (statusp) *statusp = closed_list[j].completion;
4761 /* fall through if this child is not one of our own pipe children */
4763 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
4765 /* waitpid() became available in the CRTL as of VMS 7.0, but only
4766 * in 7.2 did we get a version that fills in the VMS completion
4767 * status as Perl has always tried to do.
4770 sts = __vms_waitpid( pid, statusp, flags );
4772 if ( sts == 0 || !(sts == -1 && errno == ECHILD) )
4775 /* If the real waitpid tells us the child does not exist, we
4776 * fall through here to implement waiting for a child that
4777 * was created by some means other than exec() (say, spawned
4778 * from DCL) or to wait for a process that is not a subprocess
4779 * of the current process.
4782 #endif /* defined(__CRTL_VER) && __CRTL_VER >= 70200000 */
4785 $DESCRIPTOR(intdsc,"0 00:00:01");
4786 unsigned long int ownercode = JPI$_OWNER, ownerpid;
4787 unsigned long int pidcode = JPI$_PID, mypid;
4788 unsigned long int interval[2];
4789 unsigned int jpi_iosb[2];
4790 struct itmlst_3 jpilist[2] = {
4791 {sizeof(ownerpid), JPI$_OWNER, &ownerpid, 0},
4796 /* Sorry folks, we don't presently implement rooting around for
4797 the first child we can find, and we definitely don't want to
4798 pass a pid of -1 to $getjpi, where it is a wildcard operation.
4804 /* Get the owner of the child so I can warn if it's not mine. If the
4805 * process doesn't exist or I don't have the privs to look at it,
4806 * I can go home early.
4808 sts = sys$getjpiw(0,&pid,NULL,&jpilist,&jpi_iosb,NULL,NULL);
4809 if (sts & 1) sts = jpi_iosb[0];
4821 set_vaxc_errno(sts);
4825 if (ckWARN(WARN_EXEC)) {
4826 /* remind folks they are asking for non-standard waitpid behavior */
4827 _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
4828 if (ownerpid != mypid)
4829 Perl_warner(aTHX_ packWARN(WARN_EXEC),
4830 "waitpid: process %x is not a child of process %x",
4834 /* simply check on it once a second until it's not there anymore. */
4836 _ckvmssts(sys$bintim(&intdsc,interval));
4837 while ((sts=lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0)) & 1) {
4838 _ckvmssts(sys$schdwk(0,0,interval,0));
4839 _ckvmssts(sys$hiber());
4841 if (sts == SS$_NONEXPR) sts = SS$_NORMAL;
4846 } /* end of waitpid() */
4851 /*{{{ char *my_gconvert(double val, int ndig, int trail, char *buf) */
4853 my_gconvert(double val, int ndig, int trail, char *buf)
4855 static char __gcvtbuf[DBL_DIG+1];
4858 loc = buf ? buf : __gcvtbuf;
4860 #ifndef __DECC /* VAXCRTL gcvt uses E format for numbers < 1 */
4862 sprintf(loc,"%.*g",ndig,val);
4868 if (!buf && ndig > DBL_DIG) ndig = DBL_DIG;
4869 return gcvt(val,ndig,loc);
4872 loc[0] = '0'; loc[1] = '\0';
4879 #if defined(__VAX) || !defined(NAML$C_MAXRSS)
4880 static int rms_free_search_context(struct FAB * fab)
4884 nam = fab->fab$l_nam;
4885 nam->nam$b_nop |= NAM$M_SYNCHK;
4886 nam->nam$l_rlf = NULL;
4888 return sys$parse(fab, NULL, NULL);
4891 #define rms_setup_nam(nam) struct NAM nam = cc$rms_nam
4892 #define rms_clear_nam_nop(nam) nam.nam$b_nop = 0;
4893 #define rms_set_nam_nop(nam, opt) nam.nam$b_nop |= (opt)
4894 #define rms_set_nam_fnb(nam, opt) nam.nam$l_fnb |= (opt)
4895 #define rms_is_nam_fnb(nam, opt) (nam.nam$l_fnb & (opt))
4896 #define rms_nam_esll(nam) nam.nam$b_esl
4897 #define rms_nam_esl(nam) nam.nam$b_esl
4898 #define rms_nam_name(nam) nam.nam$l_name
4899 #define rms_nam_namel(nam) nam.nam$l_name
4900 #define rms_nam_type(nam) nam.nam$l_type
4901 #define rms_nam_typel(nam) nam.nam$l_type
4902 #define rms_nam_ver(nam) nam.nam$l_ver
4903 #define rms_nam_verl(nam) nam.nam$l_ver
4904 #define rms_nam_rsll(nam) nam.nam$b_rsl
4905 #define rms_nam_rsl(nam) nam.nam$b_rsl
4906 #define rms_bind_fab_nam(fab, nam) fab.fab$l_nam = &nam
4907 #define rms_set_fna(fab, nam, name, size) \
4908 { fab.fab$b_fns = size; fab.fab$l_fna = name; }
4909 #define rms_get_fna(fab, nam) fab.fab$l_fna
4910 #define rms_set_dna(fab, nam, name, size) \
4911 { fab.fab$b_dns = size; fab.fab$l_dna = name; }
4912 #define rms_nam_dns(fab, nam) fab.fab$b_dns
4913 #define rms_set_esa(nam, name, size) \
4914 { nam.nam$b_ess = size; nam.nam$l_esa = name; }
4915 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
4916 { nam.nam$l_esa = s_name; nam.nam$b_ess = s_size;}
4917 #define rms_set_rsa(nam, name, size) \
4918 { nam.nam$l_rsa = name; nam.nam$b_rss = size; }
4919 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
4920 { nam.nam$l_rsa = s_name; nam.nam$b_rss = s_size; }
4921 #define rms_nam_name_type_l_size(nam) \
4922 (nam.nam$b_name + nam.nam$b_type)
4924 static int rms_free_search_context(struct FAB * fab)
4928 nam = fab->fab$l_naml;
4929 nam->naml$b_nop |= NAM$M_SYNCHK;
4930 nam->naml$l_rlf = NULL;
4931 nam->naml$l_long_defname_size = 0;
4934 return sys$parse(fab, NULL, NULL);
4937 #define rms_setup_nam(nam) struct NAML nam = cc$rms_naml
4938 #define rms_clear_nam_nop(nam) nam.naml$b_nop = 0;
4939 #define rms_set_nam_nop(nam, opt) nam.naml$b_nop |= (opt)
4940 #define rms_set_nam_fnb(nam, opt) nam.naml$l_fnb |= (opt)
4941 #define rms_is_nam_fnb(nam, opt) (nam.naml$l_fnb & (opt))
4942 #define rms_nam_esll(nam) nam.naml$l_long_expand_size
4943 #define rms_nam_esl(nam) nam.naml$b_esl
4944 #define rms_nam_name(nam) nam.naml$l_name
4945 #define rms_nam_namel(nam) nam.naml$l_long_name
4946 #define rms_nam_type(nam) nam.naml$l_type
4947 #define rms_nam_typel(nam) nam.naml$l_long_type
4948 #define rms_nam_ver(nam) nam.naml$l_ver
4949 #define rms_nam_verl(nam) nam.naml$l_long_ver
4950 #define rms_nam_rsll(nam) nam.naml$l_long_result_size
4951 #define rms_nam_rsl(nam) nam.naml$b_rsl
4952 #define rms_bind_fab_nam(fab, nam) fab.fab$l_naml = &nam
4953 #define rms_set_fna(fab, nam, name, size) \
4954 { fab.fab$b_fns = 0; fab.fab$l_fna = (char *) -1; \
4955 nam.naml$l_long_filename_size = size; \
4956 nam.naml$l_long_filename = name;}
4957 #define rms_get_fna(fab, nam) nam.naml$l_long_filename
4958 #define rms_set_dna(fab, nam, name, size) \
4959 { fab.fab$b_dns = 0; fab.fab$l_dna = (char *) -1; \
4960 nam.naml$l_long_defname_size = size; \
4961 nam.naml$l_long_defname = name; }
4962 #define rms_nam_dns(fab, nam) nam.naml$l_long_defname_size
4963 #define rms_set_esa(nam, name, size) \
4964 { nam.naml$b_ess = 0; nam.naml$l_esa = (char *) -1; \
4965 nam.naml$l_long_expand_alloc = size; \
4966 nam.naml$l_long_expand = name; }
4967 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
4968 { nam.naml$l_esa = s_name; nam.naml$b_ess = s_size; \
4969 nam.naml$l_long_expand = l_name; \
4970 nam.naml$l_long_expand_alloc = l_size; }
4971 #define rms_set_rsa(nam, name, size) \
4972 { nam.naml$l_rsa = NULL; nam.naml$b_rss = 0; \
4973 nam.naml$l_long_result = name; \
4974 nam.naml$l_long_result_alloc = size; }
4975 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
4976 { nam.naml$l_rsa = s_name; nam.naml$b_rss = s_size; \
4977 nam.naml$l_long_result = l_name; \
4978 nam.naml$l_long_result_alloc = l_size; }
4979 #define rms_nam_name_type_l_size(nam) \
4980 (nam.naml$l_long_name_size + nam.naml$l_long_type_size)
4985 * The CRTL for 8.3 and later can create symbolic links in any mode,
4986 * however in 8.3 the unlink/remove/delete routines will only properly handle
4987 * them if one of the PCP modes is active.
4989 static int rms_erase(const char * vmsname)
4992 struct FAB myfab = cc$rms_fab;
4993 rms_setup_nam(mynam);
4995 rms_set_fna(myfab, mynam, (char *)vmsname, strlen(vmsname)); /* cast ok */
4996 rms_bind_fab_nam(myfab, mynam);
4998 /* Are we removing all versions? */
4999 if (vms_unlink_all_versions == 1) {
5000 const char * defspec = ";*";
5001 rms_set_dna(myfab, mynam, (char *)defspec, strlen(defspec)); /* cast ok */
5004 #ifdef NAML$M_OPEN_SPECIAL
5005 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5008 status = sys$erase(&myfab, 0, 0);
5015 vms_rename_with_acl(pTHX_ const struct dsc$descriptor_s * vms_src_dsc,
5016 const struct dsc$descriptor_s * vms_dst_dsc,
5017 unsigned long flags)
5019 /* VMS and UNIX handle file permissions differently and the
5020 * the same ACL trick may be needed for renaming files,
5021 * especially if they are directories.
5024 /* todo: get kill_file and rename to share common code */
5025 /* I can not find online documentation for $change_acl
5026 * it appears to be replaced by $set_security some time ago */
5028 const unsigned int access_mode = 0;
5029 $DESCRIPTOR(obj_file_dsc,"FILE");
5032 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
5033 int aclsts, fndsts, rnsts = -1;
5034 unsigned int ctx = 0;
5035 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
5036 struct dsc$descriptor_s * clean_dsc;
5039 unsigned char myace$b_length;
5040 unsigned char myace$b_type;
5041 unsigned short int myace$w_flags;
5042 unsigned long int myace$l_access;
5043 unsigned long int myace$l_ident;
5044 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
5045 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL,
5047 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
5050 findlst[3] = {{sizeof oldace, OSS$_ACL_FIND_ENTRY, &oldace, 0},
5051 {sizeof oldace, OSS$_ACL_READ_ENTRY, &oldace, 0},
5053 addlst[2] = {{sizeof newace, OSS$_ACL_ADD_ENTRY, &newace, 0},{0,0,0,0}},
5054 dellst[2] = {{sizeof newace, OSS$_ACL_DELETE_ENTRY, &newace, 0},
5058 /* Expand the input spec using RMS, since we do not want to put
5059 * ACLs on the target of a symbolic link */
5060 vmsname = PerlMem_malloc(NAM$C_MAXRSS+1);
5061 if (vmsname == NULL)
5064 rslt = do_rmsexpand(vms_src_dsc->dsc$a_pointer,
5068 PERL_RMSEXPAND_M_VMS | PERL_RMSEXPAND_M_SYMLINK,
5072 PerlMem_free(vmsname);
5076 /* So we get our own UIC to use as a rights identifier,
5077 * and the insert an ACE at the head of the ACL which allows us
5078 * to delete the file.
5080 _ckvmssts_noperl(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
5082 fildsc.dsc$w_length = strlen(vmsname);
5083 fildsc.dsc$a_pointer = vmsname;
5085 newace.myace$l_ident = oldace.myace$l_ident;
5088 /* Grab any existing ACEs with this identifier in case we fail */
5089 clean_dsc = &fildsc;
5090 aclsts = fndsts = sys$get_security(&obj_file_dsc,
5098 if ($VMS_STATUS_SUCCESS(fndsts) || (fndsts == SS$_ACLEMPTY)) {
5099 /* Add the new ACE . . . */
5101 /* if the sys$get_security succeeded, then ctx is valid, and the
5102 * object/file descriptors will be ignored. But otherwise they
5105 aclsts = sys$set_security(&obj_file_dsc, &fildsc, NULL,
5106 OSS$M_RELCTX, addlst, &ctx, &access_mode);
5107 if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) {
5109 set_vaxc_errno(aclsts);
5110 PerlMem_free(vmsname);
5114 rnsts = lib$rename_file(vms_src_dsc, vms_dst_dsc,
5117 NULL, NULL, NULL, NULL, NULL, NULL, NULL);
5119 if ($VMS_STATUS_SUCCESS(rnsts)) {
5120 clean_dsc = (struct dsc$descriptor_s *)vms_dst_dsc;
5123 /* Put things back the way they were. */
5125 aclsts = sys$get_security(&obj_file_dsc,
5133 if ($VMS_STATUS_SUCCESS(aclsts)) {
5137 if (!$VMS_STATUS_SUCCESS(fndsts))
5138 sec_flags = OSS$M_RELCTX;
5140 /* Get rid of the new ACE */
5141 aclsts = sys$set_security(NULL, NULL, NULL,
5142 sec_flags, dellst, &ctx, &access_mode);
5144 /* If there was an old ACE, put it back */
5145 if ($VMS_STATUS_SUCCESS(aclsts) && $VMS_STATUS_SUCCESS(fndsts)) {
5146 addlst[0].bufadr = &oldace;
5147 aclsts = sys$set_security(NULL, NULL, NULL,
5148 OSS$M_RELCTX, addlst, &ctx, &access_mode);
5149 if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) {
5151 set_vaxc_errno(aclsts);
5157 /* Try to clear the lock on the ACL list */
5158 aclsts2 = sys$set_security(NULL, NULL, NULL,
5159 OSS$M_RELCTX, NULL, &ctx, &access_mode);
5161 /* Rename errors are most important */
5162 if (!$VMS_STATUS_SUCCESS(rnsts))
5165 set_vaxc_errno(aclsts);
5170 if (aclsts != SS$_ACLEMPTY)
5177 PerlMem_free(vmsname);
5182 /*{{{int rename(const char *, const char * */
5183 /* Not exactly what X/Open says to do, but doing it absolutely right
5184 * and efficiently would require a lot more work. This should be close
5185 * enough to pass all but the most strict X/Open compliance test.
5188 Perl_rename(pTHX_ const char *src, const char * dst)
5197 /* Validate the source file */
5198 src_sts = flex_lstat(src, &src_st);
5201 /* No source file or other problem */
5205 dst_sts = flex_lstat(dst, &dst_st);
5208 if (dst_st.st_dev != src_st.st_dev) {
5209 /* Must be on the same device */
5214 /* VMS_INO_T_COMPARE is true if the inodes are different
5215 * to match the output of memcmp
5218 if (!VMS_INO_T_COMPARE(src_st.st_ino, dst_st.st_ino)) {
5219 /* That was easy, the files are the same! */
5223 if (S_ISDIR(src_st.st_mode) && !S_ISDIR(dst_st.st_mode)) {
5224 /* If source is a directory, so must be dest */
5232 if ((dst_sts == 0) &&
5233 (vms_unlink_all_versions || S_ISDIR(dst_st.st_mode))) {
5235 /* We have issues here if vms_unlink_all_versions is set
5236 * If the destination exists, and is not a directory, then
5237 * we must delete in advance.
5239 * If the src is a directory, then we must always pre-delete
5242 * If we successfully delete the dst in advance, and the rename fails
5243 * X/Open requires that errno be EIO.
5247 if (!S_ISDIR(dst_st.st_mode) || S_ISDIR(src_st.st_mode)) {
5249 d_sts = mp_do_kill_file(aTHX_ dst, S_ISDIR(dst_st.st_mode));
5253 /* We killed the destination, so only errno now is EIO */
5258 /* Originally the idea was to call the CRTL rename() and only
5259 * try the lib$rename_file if it failed.
5260 * It turns out that there are too many variants in what the
5261 * the CRTL rename might do, so only use lib$rename_file
5266 /* Is the source and dest both in VMS format */
5267 /* if the source is a directory, then need to fileify */
5268 /* and dest must be a directory or non-existant. */
5274 unsigned long flags;
5275 struct dsc$descriptor_s old_file_dsc;
5276 struct dsc$descriptor_s new_file_dsc;
5278 /* We need to modify the src and dst depending
5279 * on if one or more of them are directories.
5282 vms_src = PerlMem_malloc(VMS_MAXRSS);
5283 if (vms_src == NULL)
5284 _ckvmssts_noperl(SS$_INSFMEM);
5286 /* Source is always a VMS format file */
5287 ret_str = do_tovmsspec(src, vms_src, 0, NULL);
5288 if (ret_str == NULL) {
5289 PerlMem_free(vms_src);
5294 vms_dst = PerlMem_malloc(VMS_MAXRSS);
5295 if (vms_dst == NULL)
5296 _ckvmssts_noperl(SS$_INSFMEM);
5298 if (S_ISDIR(src_st.st_mode)) {
5300 char * vms_dir_file;
5302 vms_dir_file = PerlMem_malloc(VMS_MAXRSS);
5303 if (vms_dir_file == NULL)
5304 _ckvmssts_noperl(SS$_INSFMEM);
5306 /* The source must be a file specification */
5307 ret_str = do_fileify_dirspec(vms_src, vms_dir_file, 0, NULL);
5308 if (ret_str == NULL) {
5309 PerlMem_free(vms_src);
5310 PerlMem_free(vms_dst);
5311 PerlMem_free(vms_dir_file);
5315 PerlMem_free(vms_src);
5316 vms_src = vms_dir_file;
5318 /* If the dest is a directory, we must remove it
5321 d_sts = mp_do_kill_file(aTHX_ dst, 1);
5323 PerlMem_free(vms_src);
5324 PerlMem_free(vms_dst);
5332 /* The dest must be a VMS file specification */
5333 ret_str = int_tovmsspec(dst, vms_dst, 0, NULL);
5334 if (ret_str == NULL) {
5335 PerlMem_free(vms_src);
5336 PerlMem_free(vms_dst);
5341 /* The source must be a file specification */
5342 vms_dir_file = PerlMem_malloc(VMS_MAXRSS);
5343 if (vms_dir_file == NULL)
5344 _ckvmssts_noperl(SS$_INSFMEM);
5346 ret_str = do_fileify_dirspec(vms_dst, vms_dir_file, 0, NULL);
5347 if (ret_str == NULL) {
5348 PerlMem_free(vms_src);
5349 PerlMem_free(vms_dst);
5350 PerlMem_free(vms_dir_file);
5354 PerlMem_free(vms_dst);
5355 vms_dst = vms_dir_file;
5358 /* File to file or file to new dir */
5360 if ((dst_sts == 0) && S_ISDIR(dst_st.st_mode)) {
5361 /* VMS pathify a dir target */
5362 ret_str = do_tovmspath(dst, vms_dst, 0, NULL);
5363 if (ret_str == NULL) {
5364 PerlMem_free(vms_src);
5365 PerlMem_free(vms_dst);
5371 /* fileify a target VMS file specification */
5372 ret_str = int_tovmsspec(dst, vms_dst, 0, NULL);
5373 if (ret_str == NULL) {
5374 PerlMem_free(vms_src);
5375 PerlMem_free(vms_dst);
5382 old_file_dsc.dsc$a_pointer = vms_src;
5383 old_file_dsc.dsc$w_length = strlen(vms_src);
5384 old_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
5385 old_file_dsc.dsc$b_class = DSC$K_CLASS_S;
5387 new_file_dsc.dsc$a_pointer = vms_dst;
5388 new_file_dsc.dsc$w_length = strlen(vms_dst);
5389 new_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
5390 new_file_dsc.dsc$b_class = DSC$K_CLASS_S;
5393 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5394 flags |= 4; /* LIB$M_FIL_LONG_NAMES (bit 2) */
5397 sts = lib$rename_file(&old_file_dsc,
5401 NULL, NULL, NULL, NULL, NULL, NULL, NULL);
5402 if (!$VMS_STATUS_SUCCESS(sts)) {
5404 /* We could have failed because VMS style permissions do not
5405 * permit renames that UNIX will allow. Just like the hack
5408 sts = vms_rename_with_acl(aTHX_ &old_file_dsc, &new_file_dsc, flags);
5411 PerlMem_free(vms_src);
5412 PerlMem_free(vms_dst);
5413 if (!$VMS_STATUS_SUCCESS(sts)) {
5420 if (vms_unlink_all_versions) {
5421 /* Now get rid of any previous versions of the source file that
5426 src_sts = mp_do_kill_file(aTHX_ src, S_ISDIR(src_st.st_mode));
5430 /* We deleted the destination, so must force the error to be EIO */
5431 if ((retval != 0) && (pre_delete != 0))
5439 /*{{{char *do_rmsexpand(char *fspec, char *out, int ts, char *def, unsigned opts)*/
5440 /* Shortcut for common case of simple calls to $PARSE and $SEARCH
5441 * to expand file specification. Allows for a single default file
5442 * specification and a simple mask of options. If outbuf is non-NULL,
5443 * it must point to a buffer at least NAM$C_MAXRSS bytes long, into which
5444 * the resultant file specification is placed. If outbuf is NULL, the
5445 * resultant file specification is placed into a static buffer.
5446 * The third argument, if non-NULL, is taken to be a default file
5447 * specification string. The fourth argument is unused at present.
5448 * rmesexpand() returns the address of the resultant string if
5449 * successful, and NULL on error.
5451 * New functionality for previously unused opts value:
5452 * PERL_RMSEXPAND_M_VMS - Force output file specification to VMS format.
5453 * PERL_RMSEXPAND_M_LONG - Want output in long formst
5454 * PERL_RMSEXPAND_M_VMS_IN - Input is already in VMS, so do not vmsify
5455 * PERL_RMSEXPAND_M_SYMLINK - Use symbolic link, not target
5457 static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *);
5461 (pTHX_ const char *filespec,
5464 const char *defspec,
5469 static char __rmsexpand_retbuf[VMS_MAXRSS];
5470 char * vmsfspec, *tmpfspec;
5471 char * esa, *cp, *out = NULL;
5475 struct FAB myfab = cc$rms_fab;
5476 rms_setup_nam(mynam);
5478 unsigned long int retsts, trimver, trimtype, haslower = 0, isunix = 0;
5481 /* temp hack until UTF8 is actually implemented */
5482 if (fs_utf8 != NULL)
5485 if (!filespec || !*filespec) {
5486 set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL);
5490 if (ts) out = Newx(outbuf,VMS_MAXRSS,char);
5491 else outbuf = __rmsexpand_retbuf;
5499 if ((opts & PERL_RMSEXPAND_M_VMS_IN) == 0) {
5500 isunix = is_unix_filespec(filespec);
5502 vmsfspec = PerlMem_malloc(VMS_MAXRSS);
5503 if (vmsfspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5504 if (int_tovmsspec(filespec, vmsfspec, 0, fs_utf8) == NULL) {
5505 PerlMem_free(vmsfspec);
5510 filespec = vmsfspec;
5512 /* Unless we are forcing to VMS format, a UNIX input means
5513 * UNIX output, and that requires long names to be used
5515 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5516 if ((opts & PERL_RMSEXPAND_M_VMS) == 0)
5517 opts |= PERL_RMSEXPAND_M_LONG;
5524 rms_set_fna(myfab, mynam, (char *)filespec, strlen(filespec)); /* cast ok */
5525 rms_bind_fab_nam(myfab, mynam);
5527 if (defspec && *defspec) {
5529 t_isunix = is_unix_filespec(defspec);
5531 tmpfspec = PerlMem_malloc(VMS_MAXRSS);
5532 if (tmpfspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5533 if (int_tovmsspec(defspec, tmpfspec, 0, dfs_utf8) == NULL) {
5534 PerlMem_free(tmpfspec);
5535 if (vmsfspec != NULL)
5536 PerlMem_free(vmsfspec);
5543 rms_set_dna(myfab, mynam, (char *)defspec, strlen(defspec)); /* cast ok */
5546 esa = PerlMem_malloc(NAM$C_MAXRSS + 1);
5547 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5548 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5549 esal = PerlMem_malloc(VMS_MAXRSS);
5550 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5552 rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS-1);
5554 /* If a NAML block is used RMS always writes to the long and short
5555 * addresses unless you suppress the short name.
5557 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5558 outbufl = PerlMem_malloc(VMS_MAXRSS);
5559 if (outbufl == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5561 rms_set_rsal(mynam, outbuf, NAM$C_MAXRSS, outbufl, (VMS_MAXRSS - 1));
5563 #ifdef NAM$M_NO_SHORT_UPCASE
5564 if (decc_efs_case_preserve)
5565 rms_set_nam_nop(mynam, NAM$M_NO_SHORT_UPCASE);
5568 /* We may not want to follow symbolic links */
5569 #ifdef NAML$M_OPEN_SPECIAL
5570 if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0)
5571 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5574 /* First attempt to parse as an existing file */
5575 retsts = sys$parse(&myfab,0,0);
5576 if (!(retsts & STS$K_SUCCESS)) {
5578 /* Could not find the file, try as syntax only if error is not fatal */
5579 rms_set_nam_nop(mynam, NAM$M_SYNCHK);
5580 if (retsts == RMS$_DNF || retsts == RMS$_DIR || retsts == RMS$_DEV) {
5581 retsts = sys$parse(&myfab,0,0);
5582 if (retsts & STS$K_SUCCESS) goto expanded;
5585 /* Still could not parse the file specification */
5586 /*----------------------------------------------*/
5587 sts = rms_free_search_context(&myfab); /* Free search context */
5588 if (out) Safefree(out);
5589 if (tmpfspec != NULL)
5590 PerlMem_free(tmpfspec);
5591 if (vmsfspec != NULL)
5592 PerlMem_free(vmsfspec);
5593 if (outbufl != NULL)
5594 PerlMem_free(outbufl);
5598 set_vaxc_errno(retsts);
5599 if (retsts == RMS$_PRV) set_errno(EACCES);
5600 else if (retsts == RMS$_DEV) set_errno(ENODEV);
5601 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
5602 else set_errno(EVMSERR);
5605 retsts = sys$search(&myfab,0,0);
5606 if (!(retsts & STS$K_SUCCESS) && retsts != RMS$_FNF) {
5607 sts = rms_free_search_context(&myfab); /* Free search context */
5608 if (out) Safefree(out);
5609 if (tmpfspec != NULL)
5610 PerlMem_free(tmpfspec);
5611 if (vmsfspec != NULL)
5612 PerlMem_free(vmsfspec);
5613 if (outbufl != NULL)
5614 PerlMem_free(outbufl);
5618 set_vaxc_errno(retsts);
5619 if (retsts == RMS$_PRV) set_errno(EACCES);
5620 else set_errno(EVMSERR);
5624 /* If the input filespec contained any lowercase characters,
5625 * downcase the result for compatibility with Unix-minded code. */
5627 if (!decc_efs_case_preserve) {
5628 for (tbuf = rms_get_fna(myfab, mynam); *tbuf; tbuf++)
5629 if (islower(*tbuf)) { haslower = 1; break; }
5632 /* Is a long or a short name expected */
5633 /*------------------------------------*/
5634 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5635 if (rms_nam_rsll(mynam)) {
5637 speclen = rms_nam_rsll(mynam);
5640 tbuf = esal; /* Not esa */
5641 speclen = rms_nam_esll(mynam);
5645 if (rms_nam_rsl(mynam)) {
5647 speclen = rms_nam_rsl(mynam);
5650 tbuf = esa; /* Not esal */
5651 speclen = rms_nam_esl(mynam);
5654 tbuf[speclen] = '\0';
5656 /* Trim off null fields added by $PARSE
5657 * If type > 1 char, must have been specified in original or default spec
5658 * (not true for version; $SEARCH may have added version of existing file).
5660 trimver = !rms_is_nam_fnb(mynam, NAM$M_EXP_VER);
5661 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5662 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
5663 ((rms_nam_verl(mynam) - rms_nam_typel(mynam)) == 1);
5666 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
5667 ((rms_nam_ver(mynam) - rms_nam_type(mynam)) == 1);
5669 if (trimver || trimtype) {
5670 if (defspec && *defspec) {
5671 char *defesal = NULL;
5672 char *defesa = NULL;
5673 defesa = PerlMem_malloc(VMS_MAXRSS + 1);
5674 if (defesa != NULL) {
5675 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5676 defesal = PerlMem_malloc(VMS_MAXRSS + 1);
5677 if (defesal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5679 struct FAB deffab = cc$rms_fab;
5680 rms_setup_nam(defnam);
5682 rms_bind_fab_nam(deffab, defnam);
5686 (deffab, defnam, (char *)defspec, rms_nam_dns(myfab, mynam));
5688 /* RMS needs the esa/esal as a work area if wildcards are involved */
5689 rms_set_esal(defnam, defesa, NAM$C_MAXRSS, defesal, VMS_MAXRSS - 1);
5691 rms_clear_nam_nop(defnam);
5692 rms_set_nam_nop(defnam, NAM$M_SYNCHK);
5693 #ifdef NAM$M_NO_SHORT_UPCASE
5694 if (decc_efs_case_preserve)
5695 rms_set_nam_nop(defnam, NAM$M_NO_SHORT_UPCASE);
5697 #ifdef NAML$M_OPEN_SPECIAL
5698 if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0)
5699 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5701 if (sys$parse(&deffab,0,0) & STS$K_SUCCESS) {
5703 trimver = !rms_is_nam_fnb(defnam, NAM$M_EXP_VER);
5706 trimtype = !rms_is_nam_fnb(defnam, NAM$M_EXP_TYPE);
5709 if (defesal != NULL)
5710 PerlMem_free(defesal);
5711 PerlMem_free(defesa);
5715 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5716 if (*(rms_nam_verl(mynam)) != '\"')
5717 speclen = rms_nam_verl(mynam) - tbuf;
5720 if (*(rms_nam_ver(mynam)) != '\"')
5721 speclen = rms_nam_ver(mynam) - tbuf;
5725 /* If we didn't already trim version, copy down */
5726 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5727 if (speclen > rms_nam_verl(mynam) - tbuf)
5729 (rms_nam_typel(mynam),
5730 rms_nam_verl(mynam),
5731 speclen - (rms_nam_verl(mynam) - tbuf));
5732 speclen -= rms_nam_verl(mynam) - rms_nam_typel(mynam);
5735 if (speclen > rms_nam_ver(mynam) - tbuf)
5737 (rms_nam_type(mynam),
5739 speclen - (rms_nam_ver(mynam) - tbuf));
5740 speclen -= rms_nam_ver(mynam) - rms_nam_type(mynam);
5745 /* Done with these copies of the input files */
5746 /*-------------------------------------------*/
5747 if (vmsfspec != NULL)
5748 PerlMem_free(vmsfspec);
5749 if (tmpfspec != NULL)
5750 PerlMem_free(tmpfspec);
5752 /* If we just had a directory spec on input, $PARSE "helpfully"
5753 * adds an empty name and type for us */
5754 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5755 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5756 if (rms_nam_namel(mynam) == rms_nam_typel(mynam) &&
5757 rms_nam_verl(mynam) == rms_nam_typel(mynam) + 1 &&
5758 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
5759 speclen = rms_nam_namel(mynam) - tbuf;
5764 if (rms_nam_name(mynam) == rms_nam_type(mynam) &&
5765 rms_nam_ver(mynam) == rms_nam_ver(mynam) + 1 &&
5766 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
5767 speclen = rms_nam_name(mynam) - tbuf;
5770 /* Posix format specifications must have matching quotes */
5771 if (speclen < (VMS_MAXRSS - 1)) {
5772 if (decc_posix_compliant_pathnames && (tbuf[0] == '\"')) {
5773 if ((speclen > 1) && (tbuf[speclen-1] != '\"')) {
5774 tbuf[speclen] = '\"';
5779 tbuf[speclen] = '\0';
5780 if (haslower && !decc_efs_case_preserve) __mystrtolower(tbuf);
5782 /* Have we been working with an expanded, but not resultant, spec? */
5783 /* Also, convert back to Unix syntax if necessary. */
5787 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5788 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5789 rsl = rms_nam_rsll(mynam);
5793 rsl = rms_nam_rsl(mynam);
5797 if (do_tounixspec(tbuf, outbuf ,0 , fs_utf8) == NULL) {
5798 if (out) Safefree(out);
5802 if (outbufl != NULL)
5803 PerlMem_free(outbufl);
5807 else strcpy(outbuf, tbuf);
5810 tmpfspec = PerlMem_malloc(VMS_MAXRSS);
5811 if (tmpfspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5812 if (do_tounixspec(tbuf,tmpfspec,0,fs_utf8) == NULL) {
5813 if (out) Safefree(out);
5817 PerlMem_free(tmpfspec);
5818 if (outbufl != NULL)
5819 PerlMem_free(outbufl);
5822 strcpy(outbuf,tmpfspec);
5823 PerlMem_free(tmpfspec);
5826 rms_set_rsal(mynam, NULL, 0, NULL, 0);
5827 sts = rms_free_search_context(&myfab); /* Free search context */
5831 if (outbufl != NULL)
5832 PerlMem_free(outbufl);
5836 /* External entry points */
5837 char *Perl_rmsexpand(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
5838 { return do_rmsexpand(spec,buf,0,def,opt,NULL,NULL); }
5839 char *Perl_rmsexpand_ts(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
5840 { return do_rmsexpand(spec,buf,1,def,opt,NULL,NULL); }
5841 char *Perl_rmsexpand_utf8
5842 (pTHX_ const char *spec, char *buf, const char *def,
5843 unsigned opt, int * fs_utf8, int * dfs_utf8)
5844 { return do_rmsexpand(spec,buf,0,def,opt, fs_utf8, dfs_utf8); }
5845 char *Perl_rmsexpand_utf8_ts
5846 (pTHX_ const char *spec, char *buf, const char *def,
5847 unsigned opt, int * fs_utf8, int * dfs_utf8)
5848 { return do_rmsexpand(spec,buf,1,def,opt, fs_utf8, dfs_utf8); }
5852 ** The following routines are provided to make life easier when
5853 ** converting among VMS-style and Unix-style directory specifications.
5854 ** All will take input specifications in either VMS or Unix syntax. On
5855 ** failure, all return NULL. If successful, the routines listed below
5856 ** return a pointer to a buffer containing the appropriately
5857 ** reformatted spec (and, therefore, subsequent calls to that routine
5858 ** will clobber the result), while the routines of the same names with
5859 ** a _ts suffix appended will return a pointer to a mallocd string
5860 ** containing the appropriately reformatted spec.
5861 ** In all cases, only explicit syntax is altered; no check is made that
5862 ** the resulting string is valid or that the directory in question
5865 ** fileify_dirspec() - convert a directory spec into the name of the
5866 ** directory file (i.e. what you can stat() to see if it's a dir).
5867 ** The style (VMS or Unix) of the result is the same as the style
5868 ** of the parameter passed in.
5869 ** pathify_dirspec() - convert a directory spec into a path (i.e.
5870 ** what you prepend to a filename to indicate what directory it's in).
5871 ** The style (VMS or Unix) of the result is the same as the style
5872 ** of the parameter passed in.
5873 ** tounixpath() - convert a directory spec into a Unix-style path.
5874 ** tovmspath() - convert a directory spec into a VMS-style path.
5875 ** tounixspec() - convert any file spec into a Unix-style file spec.
5876 ** tovmsspec() - convert any file spec into a VMS-style spec.
5877 ** xxxxx_utf8() - Variants that support UTF8 encoding of Unix-Style file spec.
5879 ** Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>
5880 ** Permission is given to distribute this code as part of the Perl
5881 ** standard distribution under the terms of the GNU General Public
5882 ** License or the Perl Artistic License. Copies of each may be
5883 ** found in the Perl standard distribution.
5886 /*{{{ char *fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/
5887 static char *mp_do_fileify_dirspec(pTHX_ const char *dir,char *buf,int ts, int *utf8_fl)
5889 static char __fileify_retbuf[VMS_MAXRSS];
5890 unsigned long int dirlen, retlen, addmfd = 0, hasfilename = 0;
5891 char *retspec, *cp1, *cp2, *lastdir;
5892 char *trndir, *vmsdir;
5893 unsigned short int trnlnm_iter_count;
5897 if (utf8_fl != NULL)
5900 if (!dir || !*dir) {
5901 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
5903 dirlen = strlen(dir);
5904 while (dirlen && dir[dirlen-1] == '/') --dirlen;
5905 if (!dirlen) { /* We had Unixish '/' -- substitute top of current tree */
5906 if (!decc_posix_compliant_pathnames && decc_disable_posix_root) {
5913 if (dirlen > (VMS_MAXRSS - 1)) {
5914 set_errno(ENAMETOOLONG); set_vaxc_errno(RMS$_SYN);
5917 trndir = PerlMem_malloc(VMS_MAXRSS + 1);
5918 if (trndir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5919 if (!strpbrk(dir+1,"/]>:") &&
5920 (!decc_posix_compliant_pathnames && decc_disable_posix_root)) {
5921 strcpy(trndir,*dir == '/' ? dir + 1: dir);
5922 trnlnm_iter_count = 0;
5923 while (!strpbrk(trndir,"/]>:") && simple_trnlnm(trndir,trndir,VMS_MAXRSS-1)) {
5924 trnlnm_iter_count++;
5925 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
5927 dirlen = strlen(trndir);
5930 strncpy(trndir,dir,dirlen);
5931 trndir[dirlen] = '\0';
5934 /* At this point we are done with *dir and use *trndir which is a
5935 * copy that can be modified. *dir must not be modified.
5938 /* If we were handed a rooted logical name or spec, treat it like a
5939 * simple directory, so that
5940 * $ Define myroot dev:[dir.]
5941 * ... do_fileify_dirspec("myroot",buf,1) ...
5942 * does something useful.
5944 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".]")) {
5945 trndir[--dirlen] = '\0';
5946 trndir[dirlen-1] = ']';
5948 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".>")) {
5949 trndir[--dirlen] = '\0';
5950 trndir[dirlen-1] = '>';
5953 if ((cp1 = strrchr(trndir,']')) != NULL || (cp1 = strrchr(trndir,'>')) != NULL) {
5954 /* If we've got an explicit filename, we can just shuffle the string. */
5955 if (*(cp1+1)) hasfilename = 1;
5956 /* Similarly, we can just back up a level if we've got multiple levels
5957 of explicit directories in a VMS spec which ends with directories. */
5959 for (cp2 = cp1; cp2 > trndir; cp2--) {
5961 if ((cp2 - 1 > trndir) && (*(cp2 - 1) != '^')) {
5962 /* fix-me, can not scan EFS file specs backward like this */
5963 *cp2 = *cp1; *cp1 = '\0';
5968 if (*cp2 == '[' || *cp2 == '<') break;
5973 vmsdir = PerlMem_malloc(VMS_MAXRSS + 1);
5974 if (vmsdir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5975 cp1 = strpbrk(trndir,"]:>");
5976 if (hasfilename || !cp1) { /* Unix-style path or filename */
5977 if (trndir[0] == '.') {
5978 if (trndir[1] == '\0' || (trndir[1] == '/' && trndir[2] == '\0')) {
5979 PerlMem_free(trndir);
5980 PerlMem_free(vmsdir);
5981 return do_fileify_dirspec("[]",buf,ts,NULL);
5983 else if (trndir[1] == '.' &&
5984 (trndir[2] == '\0' || (trndir[2] == '/' && trndir[3] == '\0'))) {
5985 PerlMem_free(trndir);
5986 PerlMem_free(vmsdir);
5987 return do_fileify_dirspec("[-]",buf,ts,NULL);
5990 if (dirlen && trndir[dirlen-1] == '/') { /* path ends with '/'; just add .dir;1 */
5991 dirlen -= 1; /* to last element */
5992 lastdir = strrchr(trndir,'/');
5994 else if ((cp1 = strstr(trndir,"/.")) != NULL) {
5995 /* If we have "/." or "/..", VMSify it and let the VMS code
5996 * below expand it, rather than repeating the code to handle
5997 * relative components of a filespec here */
5999 if (*(cp1+2) == '.') cp1++;
6000 if (*(cp1+2) == '/' || *(cp1+2) == '\0') {
6002 if (int_tovmsspec(trndir, vmsdir, 0, utf8_fl) == NULL) {
6003 PerlMem_free(trndir);
6004 PerlMem_free(vmsdir);
6007 if (strchr(vmsdir,'/') != NULL) {
6008 /* If int_tovmsspec() returned it, it must have VMS syntax
6009 * delimiters in it, so it's a mixed VMS/Unix spec. We take
6010 * the time to check this here only so we avoid a recursion
6011 * loop; otherwise, gigo.
6013 PerlMem_free(trndir);
6014 PerlMem_free(vmsdir);
6015 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN);
6018 if (do_fileify_dirspec(vmsdir,trndir,0,NULL) == NULL) {
6019 PerlMem_free(trndir);
6020 PerlMem_free(vmsdir);
6023 ret_chr = do_tounixspec(trndir,buf,ts,NULL);
6024 PerlMem_free(trndir);
6025 PerlMem_free(vmsdir);
6029 } while ((cp1 = strstr(cp1,"/.")) != NULL);
6030 lastdir = strrchr(trndir,'/');
6032 else if (dirlen >= 7 && !strcmp(&trndir[dirlen-7],"/000000")) {
6034 /* Ditto for specs that end in an MFD -- let the VMS code
6035 * figure out whether it's a real device or a rooted logical. */
6037 /* This should not happen any more. Allowing the fake /000000
6038 * in a UNIX pathname causes all sorts of problems when trying
6039 * to run in UNIX emulation. So the VMS to UNIX conversions
6040 * now remove the fake /000000 directories.
6043 trndir[dirlen] = '/'; trndir[dirlen+1] = '\0';
6044 if (int_tovmsspec(trndir, vmsdir, 0, NULL) == NULL) {
6045 PerlMem_free(trndir);
6046 PerlMem_free(vmsdir);
6049 if (do_fileify_dirspec(vmsdir,trndir,0,NULL) == NULL) {
6050 PerlMem_free(trndir);
6051 PerlMem_free(vmsdir);
6054 ret_chr = do_tounixspec(trndir,buf,ts,NULL);
6055 PerlMem_free(trndir);
6056 PerlMem_free(vmsdir);
6061 if ( !(lastdir = cp1 = strrchr(trndir,'/')) &&
6062 !(lastdir = cp1 = strrchr(trndir,']')) &&
6063 !(lastdir = cp1 = strrchr(trndir,'>'))) cp1 = trndir;
6064 if ((cp2 = strchr(cp1,'.'))) { /* look for explicit type */
6067 /* For EFS or ODS-5 look for the last dot */
6068 if (decc_efs_charset) {
6069 cp2 = strrchr(cp1,'.');
6071 if (vms_process_case_tolerant) {
6072 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
6073 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
6074 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
6075 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
6076 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
6077 (ver || *cp3)))))) {
6078 PerlMem_free(trndir);
6079 PerlMem_free(vmsdir);
6081 set_vaxc_errno(RMS$_DIR);
6086 if (!*(cp2+1) || *(cp2+1) != 'D' || /* Wrong type. */
6087 !*(cp2+2) || *(cp2+2) != 'I' || /* Bzzt. */
6088 !*(cp2+3) || *(cp2+3) != 'R' ||
6089 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
6090 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
6091 (ver || *cp3)))))) {
6092 PerlMem_free(trndir);
6093 PerlMem_free(vmsdir);
6095 set_vaxc_errno(RMS$_DIR);
6099 dirlen = cp2 - trndir;
6103 retlen = dirlen + 6;
6104 if (buf) retspec = buf;
6105 else if (ts) Newx(retspec,retlen+1,char);
6106 else retspec = __fileify_retbuf;
6107 memcpy(retspec,trndir,dirlen);
6108 retspec[dirlen] = '\0';
6110 /* We've picked up everything up to the directory file name.
6111 Now just add the type and version, and we're set. */
6113 /* We should only add type for VMS syntax, but historically Perl
6114 has added it for UNIX style also */
6116 /* Fix me - we should not be using the same routine for VMS and
6117 UNIX format files. Things are too tangled so we need to lookup
6118 what syntax the output is */
6122 lastdir = strrchr(trndir,'/');
6126 lastdir = strpbrk(trndir,"]:>");
6132 if ((is_vms == 0) && (is_unix == 0)) {
6133 /* We still do not know? */
6134 is_unix = decc_filename_unix_report;
6139 if ((is_unix && !decc_efs_charset) || is_vms) {
6141 /* It is a bug to add a .dir to a UNIX format directory spec */
6142 /* However Perl on VMS may have programs that expect this so */
6143 /* If not using EFS character specifications allow it. */
6145 if ((!decc_efs_case_preserve) && vms_process_case_tolerant) {
6146 /* Traditionally Perl expects filenames in lower case */
6147 strcat(retspec, ".dir");
6149 /* VMS expects the .DIR to be in upper case */
6150 strcat(retspec, ".DIR");
6153 /* It is also a bug to put a VMS format version on a UNIX file */
6154 /* specification. Perl self tests are looking for this */
6155 if (is_vms || !(decc_efs_charset || decc_filename_unix_report))
6156 strcat(retspec, ";1");
6158 PerlMem_free(trndir);
6159 PerlMem_free(vmsdir);
6162 else { /* VMS-style directory spec */
6164 char *esa, *esal, term, *cp;
6167 unsigned long int sts, cmplen, haslower = 0;
6168 unsigned int nam_fnb;
6170 struct FAB dirfab = cc$rms_fab;
6171 rms_setup_nam(savnam);
6172 rms_setup_nam(dirnam);
6174 esa = PerlMem_malloc(NAM$C_MAXRSS + 1);
6175 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6177 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
6178 esal = PerlMem_malloc(VMS_MAXRSS);
6179 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6181 rms_set_fna(dirfab, dirnam, trndir, strlen(trndir));
6182 rms_bind_fab_nam(dirfab, dirnam);
6183 rms_set_dna(dirfab, dirnam, ".DIR;1", 6);
6184 rms_set_esal(dirnam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1));
6185 #ifdef NAM$M_NO_SHORT_UPCASE
6186 if (decc_efs_case_preserve)
6187 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
6190 for (cp = trndir; *cp; cp++)
6191 if (islower(*cp)) { haslower = 1; break; }
6192 if (!((sts = sys$parse(&dirfab)) & STS$K_SUCCESS)) {
6193 if ((dirfab.fab$l_sts == RMS$_DIR) || (dirfab.fab$l_sts == RMS$_DNF)) {
6194 rms_set_nam_nop(dirnam, NAM$M_SYNCHK);
6195 sts = sys$parse(&dirfab) & STS$K_SUCCESS;
6201 PerlMem_free(trndir);
6202 PerlMem_free(vmsdir);
6204 set_vaxc_errno(dirfab.fab$l_sts);
6210 /* Does the file really exist? */
6211 if (sys$search(&dirfab)& STS$K_SUCCESS) {
6212 /* Yes; fake the fnb bits so we'll check type below */
6213 rms_set_nam_fnb(dirnam, (NAM$M_EXP_TYPE | NAM$M_EXP_VER));
6215 else { /* No; just work with potential name */
6216 if (dirfab.fab$l_sts == RMS$_FNF) dirnam = savnam;
6219 fab_sts = dirfab.fab$l_sts;
6220 sts = rms_free_search_context(&dirfab);
6224 PerlMem_free(trndir);
6225 PerlMem_free(vmsdir);
6226 set_errno(EVMSERR); set_vaxc_errno(fab_sts);
6232 /* Make sure we are using the right buffer */
6235 my_esa_len = rms_nam_esll(dirnam);
6238 my_esa_len = rms_nam_esl(dirnam);
6240 my_esa[my_esa_len] = '\0';
6241 if (!rms_is_nam_fnb(dirnam, (NAM$M_EXP_DEV | NAM$M_EXP_DIR))) {
6242 cp1 = strchr(my_esa,']');
6243 if (!cp1) cp1 = strchr(my_esa,'>');
6244 if (cp1) { /* Should always be true */
6245 my_esa_len -= cp1 - my_esa - 1;
6246 memmove(my_esa, cp1 + 1, my_esa_len);
6249 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_TYPE)) { /* Was type specified? */
6250 /* Yep; check version while we're at it, if it's there. */
6251 cmplen = rms_is_nam_fnb(dirnam, NAM$M_EXP_VER) ? 6 : 4;
6252 if (strncmp(rms_nam_typel(dirnam), ".DIR;1", cmplen)) {
6253 /* Something other than .DIR[;1]. Bzzt. */
6254 sts = rms_free_search_context(&dirfab);
6258 PerlMem_free(trndir);
6259 PerlMem_free(vmsdir);
6261 set_vaxc_errno(RMS$_DIR);
6266 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_NAME)) {
6267 /* They provided at least the name; we added the type, if necessary, */
6268 if (buf) retspec = buf; /* in sys$parse() */
6269 else if (ts) Newx(retspec, my_esa_len + 1, char);
6270 else retspec = __fileify_retbuf;
6271 strcpy(retspec,my_esa);
6272 sts = rms_free_search_context(&dirfab);
6273 PerlMem_free(trndir);
6277 PerlMem_free(vmsdir);
6280 if ((cp1 = strstr(esa,".][000000]")) != NULL) {
6281 for (cp2 = cp1 + 9; *cp2; cp1++,cp2++) *cp1 = *cp2;
6285 if ((cp1 = strrchr(my_esa,']')) == NULL) cp1 = strrchr(my_esa,'>');
6286 if (cp1 == NULL) { /* should never happen */
6287 sts = rms_free_search_context(&dirfab);
6288 PerlMem_free(trndir);
6292 PerlMem_free(vmsdir);
6297 retlen = strlen(my_esa);
6298 cp1 = strrchr(my_esa,'.');
6299 /* ODS-5 directory specifications can have extra "." in them. */
6300 /* Fix-me, can not scan EFS file specifications backwards */
6301 while (cp1 != NULL) {
6302 if ((cp1-1 == my_esa) || (*(cp1-1) != '^'))
6306 while ((cp1 > my_esa) && (*cp1 != '.'))
6313 if ((cp1) != NULL) {
6314 /* There's more than one directory in the path. Just roll back. */
6316 if (buf) retspec = buf;
6317 else if (ts) Newx(retspec,retlen+7,char);
6318 else retspec = __fileify_retbuf;
6319 strcpy(retspec,my_esa);
6322 if (rms_is_nam_fnb(dirnam, NAM$M_ROOT_DIR)) {
6323 /* Go back and expand rooted logical name */
6324 rms_set_nam_nop(dirnam, NAM$M_SYNCHK | NAM$M_NOCONCEAL);
6325 #ifdef NAM$M_NO_SHORT_UPCASE
6326 if (decc_efs_case_preserve)
6327 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
6329 if (!(sys$parse(&dirfab) & STS$K_SUCCESS)) {
6330 sts = rms_free_search_context(&dirfab);
6334 PerlMem_free(trndir);
6335 PerlMem_free(vmsdir);
6337 set_vaxc_errno(dirfab.fab$l_sts);
6341 /* This changes the length of the string of course */
6343 my_esa_len = rms_nam_esll(dirnam);
6345 my_esa_len = rms_nam_esl(dirnam);
6348 retlen = my_esa_len - 9; /* esa - '][' - '].DIR;1' */
6349 if (buf) retspec = buf;
6350 else if (ts) Newx(retspec,retlen+16,char);
6351 else retspec = __fileify_retbuf;
6352 cp1 = strstr(my_esa,"][");
6353 if (!cp1) cp1 = strstr(my_esa,"]<");
6354 dirlen = cp1 - my_esa;
6355 memcpy(retspec,my_esa,dirlen);
6356 if (!strncmp(cp1+2,"000000]",7)) {
6357 retspec[dirlen-1] = '\0';
6358 /* fix-me Not full ODS-5, just extra dots in directories for now */
6359 cp1 = retspec + dirlen - 1;
6360 while (cp1 > retspec)
6365 if (*(cp1-1) != '^')
6370 if (*cp1 == '.') *cp1 = ']';
6372 memmove(cp1+8,cp1+1,retspec+dirlen-cp1);
6373 memmove(cp1+1,"000000]",7);
6377 memmove(retspec+dirlen,cp1+2,retlen-dirlen);
6378 retspec[retlen] = '\0';
6379 /* Convert last '.' to ']' */
6380 cp1 = retspec+retlen-1;
6381 while (*cp != '[') {
6384 /* Do not trip on extra dots in ODS-5 directories */
6385 if ((cp1 == retspec) || (*(cp1-1) != '^'))
6389 if (*cp1 == '.') *cp1 = ']';
6391 memmove(cp1+8,cp1+1,retspec+dirlen-cp1);
6392 memmove(cp1+1,"000000]",7);
6396 else { /* This is a top-level dir. Add the MFD to the path. */
6397 if (buf) retspec = buf;
6398 else if (ts) Newx(retspec,retlen+16,char);
6399 else retspec = __fileify_retbuf;
6402 while ((*cp1 != ':') && (*cp1 != '\0')) *(cp2++) = *(cp1++);
6403 strcpy(cp2,":[000000]");
6408 sts = rms_free_search_context(&dirfab);
6409 /* We've set up the string up through the filename. Add the
6410 type and version, and we're done. */
6411 strcat(retspec,".DIR;1");
6413 /* $PARSE may have upcased filespec, so convert output to lower
6414 * case if input contained any lowercase characters. */
6415 if (haslower && !decc_efs_case_preserve) __mystrtolower(retspec);
6416 PerlMem_free(trndir);
6420 PerlMem_free(vmsdir);
6423 } /* end of do_fileify_dirspec() */
6425 /* External entry points */
6426 char *Perl_fileify_dirspec(pTHX_ const char *dir, char *buf)
6427 { return do_fileify_dirspec(dir,buf,0,NULL); }
6428 char *Perl_fileify_dirspec_ts(pTHX_ const char *dir, char *buf)
6429 { return do_fileify_dirspec(dir,buf,1,NULL); }
6430 char *Perl_fileify_dirspec_utf8(pTHX_ const char *dir, char *buf, int * utf8_fl)
6431 { return do_fileify_dirspec(dir,buf,0,utf8_fl); }
6432 char *Perl_fileify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int * utf8_fl)
6433 { return do_fileify_dirspec(dir,buf,1,utf8_fl); }
6435 /*{{{ char *pathify_dirspec[_ts](char *path, char *buf)*/
6436 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int * utf8_fl)
6438 static char __pathify_retbuf[VMS_MAXRSS];
6439 unsigned long int retlen;
6440 char *retpath, *cp1, *cp2, *trndir;
6441 unsigned short int trnlnm_iter_count;
6444 if (utf8_fl != NULL)
6447 if (!dir || !*dir) {
6448 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
6451 trndir = PerlMem_malloc(VMS_MAXRSS);
6452 if (trndir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6453 if (*dir) strcpy(trndir,dir);
6454 else getcwd(trndir,VMS_MAXRSS - 1);
6456 trnlnm_iter_count = 0;
6457 while (!strpbrk(trndir,"/]:>") && !no_translate_barewords
6458 && simple_trnlnm(trndir,trndir,VMS_MAXRSS-1)) {
6459 trnlnm_iter_count++;
6460 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
6461 trnlen = strlen(trndir);
6463 /* Trap simple rooted lnms, and return lnm:[000000] */
6464 if (!strcmp(trndir+trnlen-2,".]")) {
6465 if (buf) retpath = buf;
6466 else if (ts) Newx(retpath,strlen(dir)+10,char);
6467 else retpath = __pathify_retbuf;
6468 strcpy(retpath,dir);
6469 strcat(retpath,":[000000]");
6470 PerlMem_free(trndir);
6475 /* At this point we do not work with *dir, but the copy in
6476 * *trndir that is modifiable.
6479 if (!strpbrk(trndir,"]:>")) { /* Unix-style path or plain name */
6480 if (*trndir == '.' && (*(trndir+1) == '\0' ||
6481 (*(trndir+1) == '.' && *(trndir+2) == '\0')))
6482 retlen = 2 + (*(trndir+1) != '\0');
6484 if ( !(cp1 = strrchr(trndir,'/')) &&
6485 !(cp1 = strrchr(trndir,']')) &&
6486 !(cp1 = strrchr(trndir,'>')) ) cp1 = trndir;
6487 if ((cp2 = strchr(cp1,'.')) != NULL &&
6488 (*(cp2-1) != '/' || /* Trailing '.', '..', */
6489 !(*(cp2+1) == '\0' || /* or '...' are dirs. */
6490 (*(cp2+1) == '.' && *(cp2+2) == '\0') ||
6491 (*(cp2+1) == '.' && *(cp2+2) == '.' && *(cp2+3) == '\0')))) {
6494 /* For EFS or ODS-5 look for the last dot */
6495 if (decc_efs_charset) {
6496 cp2 = strrchr(cp1,'.');
6498 if (vms_process_case_tolerant) {
6499 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
6500 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
6501 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
6502 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
6503 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
6504 (ver || *cp3)))))) {
6505 PerlMem_free(trndir);
6507 set_vaxc_errno(RMS$_DIR);
6512 if (!*(cp2+1) || *(cp2+1) != 'D' || /* Wrong type. */
6513 !*(cp2+2) || *(cp2+2) != 'I' || /* Bzzt. */
6514 !*(cp2+3) || *(cp2+3) != 'R' ||
6515 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
6516 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
6517 (ver || *cp3)))))) {
6518 PerlMem_free(trndir);
6520 set_vaxc_errno(RMS$_DIR);
6524 retlen = cp2 - trndir + 1;
6526 else { /* No file type present. Treat the filename as a directory. */
6527 retlen = strlen(trndir) + 1;
6530 if (buf) retpath = buf;
6531 else if (ts) Newx(retpath,retlen+1,char);
6532 else retpath = __pathify_retbuf;
6533 strncpy(retpath, trndir, retlen-1);
6534 if (retpath[retlen-2] != '/') { /* If the path doesn't already end */
6535 retpath[retlen-1] = '/'; /* with '/', add it. */
6536 retpath[retlen] = '\0';
6538 else retpath[retlen-1] = '\0';
6540 else { /* VMS-style directory spec */
6541 char *esa, *esal, *cp;
6544 unsigned long int sts, cmplen, haslower;
6545 struct FAB dirfab = cc$rms_fab;
6547 rms_setup_nam(savnam);
6548 rms_setup_nam(dirnam);
6550 /* If we've got an explicit filename, we can just shuffle the string. */
6551 if ( ( (cp1 = strrchr(trndir,']')) != NULL ||
6552 (cp1 = strrchr(trndir,'>')) != NULL ) && *(cp1+1)) {
6553 if ((cp2 = strchr(cp1,'.')) != NULL) {
6555 if (vms_process_case_tolerant) {
6556 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
6557 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
6558 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
6559 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
6560 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
6561 (ver || *cp3)))))) {
6562 PerlMem_free(trndir);
6564 set_vaxc_errno(RMS$_DIR);
6569 if (!*(cp2+1) || *(cp2+1) != 'D' || /* Wrong type. */
6570 !*(cp2+2) || *(cp2+2) != 'I' || /* Bzzt. */
6571 !*(cp2+3) || *(cp2+3) != 'R' ||
6572 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
6573 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
6574 (ver || *cp3)))))) {
6575 PerlMem_free(trndir);
6577 set_vaxc_errno(RMS$_DIR);
6582 else { /* No file type, so just draw name into directory part */
6583 for (cp2 = cp1; *cp2; cp2++) ;
6586 *(cp2+1) = '\0'; /* OK; trndir is guaranteed to be long enough */
6588 /* We've now got a VMS 'path'; fall through */
6591 dirlen = strlen(trndir);
6592 if (trndir[dirlen-1] == ']' ||
6593 trndir[dirlen-1] == '>' ||
6594 trndir[dirlen-1] == ':') { /* It's already a VMS 'path' */
6595 if (buf) retpath = buf;
6596 else if (ts) Newx(retpath,strlen(trndir)+1,char);
6597 else retpath = __pathify_retbuf;
6598 strcpy(retpath,trndir);
6599 PerlMem_free(trndir);
6602 rms_set_fna(dirfab, dirnam, trndir, dirlen);
6603 esa = PerlMem_malloc(VMS_MAXRSS);
6604 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6606 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
6607 esal = PerlMem_malloc(VMS_MAXRSS);
6608 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6610 rms_set_dna(dirfab, dirnam, ".DIR;1", 6);
6611 rms_bind_fab_nam(dirfab, dirnam);
6612 rms_set_esal(dirnam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS-1);
6613 #ifdef NAM$M_NO_SHORT_UPCASE
6614 if (decc_efs_case_preserve)
6615 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
6618 for (cp = trndir; *cp; cp++)
6619 if (islower(*cp)) { haslower = 1; break; }
6621 if (!(sts = (sys$parse(&dirfab)& STS$K_SUCCESS))) {
6622 if ((dirfab.fab$l_sts == RMS$_DIR) || (dirfab.fab$l_sts == RMS$_DNF)) {
6623 rms_set_nam_nop(dirnam, NAM$M_SYNCHK);
6624 sts = sys$parse(&dirfab) & STS$K_SUCCESS;
6627 PerlMem_free(trndir);
6632 set_vaxc_errno(dirfab.fab$l_sts);
6638 /* Does the file really exist? */
6639 if (!(sys$search(&dirfab)&STS$K_SUCCESS)) {
6640 if (dirfab.fab$l_sts != RMS$_FNF) {
6642 sts1 = rms_free_search_context(&dirfab);
6643 PerlMem_free(trndir);
6648 set_vaxc_errno(dirfab.fab$l_sts);
6651 dirnam = savnam; /* No; just work with potential name */
6654 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_TYPE)) { /* Was type specified? */
6655 /* Yep; check version while we're at it, if it's there. */
6656 cmplen = rms_is_nam_fnb(dirnam, NAM$M_EXP_VER) ? 6 : 4;
6657 if (strncmp(rms_nam_typel(dirnam),".DIR;1",cmplen)) {
6659 /* Something other than .DIR[;1]. Bzzt. */
6660 sts2 = rms_free_search_context(&dirfab);
6661 PerlMem_free(trndir);
6666 set_vaxc_errno(RMS$_DIR);
6670 /* Make sure we are using the right buffer */
6672 /* We only need one, clean up the other */
6674 my_esa_len = rms_nam_esll(dirnam);
6677 my_esa_len = rms_nam_esl(dirnam);
6680 /* Null terminate the buffer */
6681 my_esa[my_esa_len] = '\0';
6683 /* OK, the type was fine. Now pull any file name into the
6685 if ((cp1 = strrchr(my_esa,']'))) *(rms_nam_typel(dirnam)) = ']';
6687 cp1 = strrchr(my_esa,'>');
6688 *(rms_nam_typel(dirnam)) = '>';
6691 *(rms_nam_typel(dirnam) + 1) = '\0';
6692 retlen = (rms_nam_typel(dirnam)) - my_esa + 2;
6693 if (buf) retpath = buf;
6694 else if (ts) Newx(retpath,retlen,char);
6695 else retpath = __pathify_retbuf;
6696 strcpy(retpath,my_esa);
6700 sts = rms_free_search_context(&dirfab);
6701 /* $PARSE may have upcased filespec, so convert output to lower
6702 * case if input contained any lowercase characters. */
6703 if (haslower && !decc_efs_case_preserve) __mystrtolower(retpath);
6706 PerlMem_free(trndir);
6708 } /* end of do_pathify_dirspec() */
6710 /* External entry points */
6711 char *Perl_pathify_dirspec(pTHX_ const char *dir, char *buf)
6712 { return do_pathify_dirspec(dir,buf,0,NULL); }
6713 char *Perl_pathify_dirspec_ts(pTHX_ const char *dir, char *buf)
6714 { return do_pathify_dirspec(dir,buf,1,NULL); }
6715 char *Perl_pathify_dirspec_utf8(pTHX_ const char *dir, char *buf, int *utf8_fl)
6716 { return do_pathify_dirspec(dir,buf,0,utf8_fl); }
6717 char *Perl_pathify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int *utf8_fl)
6718 { return do_pathify_dirspec(dir,buf,1,utf8_fl); }
6720 /*{{{ char *tounixspec[_ts](char *spec, char *buf, int *)*/
6721 static char *mp_do_tounixspec(pTHX_ const char *spec, char *buf, int ts, int * utf8_fl)
6723 static char __tounixspec_retbuf[VMS_MAXRSS];
6724 char *dirend, *rslt, *cp1, *cp3, *tmp;
6726 int devlen, dirlen, retlen = VMS_MAXRSS;
6727 int expand = 1; /* guarantee room for leading and trailing slashes */
6728 unsigned short int trnlnm_iter_count;
6730 if (utf8_fl != NULL)
6733 if (spec == NULL) return NULL;
6734 if (strlen(spec) > (VMS_MAXRSS-1)) return NULL;
6735 if (buf) rslt = buf;
6737 Newx(rslt, VMS_MAXRSS, char);
6739 else rslt = __tounixspec_retbuf;
6741 /* New VMS specific format needs translation
6742 * glob passes filenames with trailing '\n' and expects this preserved.
6744 if (decc_posix_compliant_pathnames) {
6745 if (strncmp(spec, "\"^UP^", 5) == 0) {
6751 tunix = PerlMem_malloc(VMS_MAXRSS);
6752 if (tunix == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6753 strcpy(tunix, spec);
6754 tunix_len = strlen(tunix);
6756 if (tunix[tunix_len - 1] == '\n') {
6757 tunix[tunix_len - 1] = '\"';
6758 tunix[tunix_len] = '\0';
6762 uspec = decc$translate_vms(tunix);
6763 PerlMem_free(tunix);
6764 if ((int)uspec > 0) {
6770 /* If we can not translate it, makemaker wants as-is */
6778 cmp_rslt = 0; /* Presume VMS */
6779 cp1 = strchr(spec, '/');
6783 /* Look for EFS ^/ */
6784 if (decc_efs_charset) {
6785 while (cp1 != NULL) {
6788 /* Found illegal VMS, assume UNIX */
6793 cp1 = strchr(cp1, '/');
6797 /* Look for "." and ".." */
6798 if (decc_filename_unix_report) {
6799 if (spec[0] == '.') {
6800 if ((spec[1] == '\0') || (spec[1] == '\n')) {
6804 if ((spec[1] == '.') && ((spec[2] == '\0') || (spec[2] == '\n'))) {
6810 /* This is already UNIX or at least nothing VMS understands */
6818 dirend = strrchr(spec,']');
6819 if (dirend == NULL) dirend = strrchr(spec,'>');
6820 if (dirend == NULL) dirend = strchr(spec,':');
6821 if (dirend == NULL) {
6826 /* Special case 1 - sys$posix_root = / */
6827 #if __CRTL_VER >= 70000000
6828 if (!decc_disable_posix_root) {
6829 if (strncasecmp(spec, "SYS$POSIX_ROOT:", 15) == 0) {
6837 /* Special case 2 - Convert NLA0: to /dev/null */
6838 #if __CRTL_VER < 70000000
6839 cmp_rslt = strncmp(spec,"NLA0:", 5);
6841 cmp_rslt = strncmp(spec,"nla0:", 5);
6843 cmp_rslt = strncasecmp(spec,"NLA0:", 5);
6845 if (cmp_rslt == 0) {
6846 strcpy(rslt, "/dev/null");
6849 if (spec[6] != '\0') {
6856 /* Also handle special case "SYS$SCRATCH:" */
6857 #if __CRTL_VER < 70000000
6858 cmp_rslt = strncmp(spec,"SYS$SCRATCH:", 12);
6860 cmp_rslt = strncmp(spec,"sys$scratch:", 12);
6862 cmp_rslt = strncasecmp(spec,"SYS$SCRATCH:", 12);
6864 tmp = PerlMem_malloc(VMS_MAXRSS);
6865 if (tmp == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6866 if (cmp_rslt == 0) {
6869 islnm = simple_trnlnm("TMP", tmp, VMS_MAXRSS-1);
6871 strcpy(rslt, "/tmp");
6874 if (spec[12] != '\0') {
6882 if (*cp2 != '[' && *cp2 != '<') {
6885 else { /* the VMS spec begins with directories */
6887 if (*cp2 == ']' || *cp2 == '>') {
6888 *(cp1++) = '.'; *(cp1++) = '/'; *(cp1++) = '\0';
6892 else if ( *cp2 != '^' && *cp2 != '.' && *cp2 != '-') { /* add the implied device */
6893 if (getcwd(tmp, VMS_MAXRSS-1 ,1) == NULL) {
6894 if (ts) Safefree(rslt);
6898 trnlnm_iter_count = 0;
6901 while (*cp3 != ':' && *cp3) cp3++;
6903 if (strchr(cp3,']') != NULL) break;
6904 trnlnm_iter_count++;
6905 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER+1) break;
6906 } while (vmstrnenv(tmp,tmp,0,fildev,0));
6908 ((devlen = strlen(tmp)) + (dirlen = strlen(cp2)) + 1 > retlen)) {
6909 retlen = devlen + dirlen;
6910 Renew(rslt,retlen+1+2*expand,char);
6916 *(cp1++) = *(cp3++);
6917 if (cp1 - rslt > (VMS_MAXRSS - 1) && !ts && !buf) {
6919 return NULL; /* No room */
6924 if ((*cp2 == '^')) {
6925 /* EFS file escape, pass the next character as is */
6926 /* Fix me: HEX encoding for Unicode not implemented */
6929 else if ( *cp2 == '.') {
6930 if (*(cp2+1) == '.' && *(cp2+2) == '.') {
6931 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
6938 for (; cp2 <= dirend; cp2++) {
6939 if ((*cp2 == '^')) {
6940 /* EFS file escape, pass the next character as is */
6941 /* Fix me: HEX encoding for Unicode not implemented */
6942 *(cp1++) = *(++cp2);
6943 /* An escaped dot stays as is -- don't convert to slash */
6944 if (*cp2 == '.') cp2++;
6948 if (*(cp2+1) == '[') cp2++;
6950 else if (*cp2 == ']' || *cp2 == '>') {
6951 if (*(cp1-1) != '/') *(cp1++) = '/'; /* Don't double after ellipsis */
6953 else if ((*cp2 == '.') && (*cp2-1 != '^')) {
6955 if (*(cp2+1) == ']' || *(cp2+1) == '>') {
6956 while (*(cp2+1) == ']' || *(cp2+1) == '>' ||
6957 *(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
6958 if (!strncmp(cp2,"[000000",7) && (*(cp2+7) == ']' ||
6959 *(cp2+7) == '>' || *(cp2+7) == '.')) cp2 += 7;
6961 else if ( *(cp2+1) == '.' && *(cp2+2) == '.') {
6962 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) ='/';
6966 else if (*cp2 == '-') {
6967 if (*(cp2-1) == '[' || *(cp2-1) == '<' || *(cp2-1) == '.') {
6968 while (*cp2 == '-') {
6970 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
6972 if (*cp2 != '.' && *cp2 != ']' && *cp2 != '>') { /* we don't allow */
6973 if (ts) Safefree(rslt); /* filespecs like */
6974 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); /* [fred.--foo.bar] */
6978 else *(cp1++) = *cp2;
6980 else *(cp1++) = *cp2;
6983 if ((*cp2 == '^') && (*(cp2+1) == '.')) cp2++; /* '^.' --> '.' */
6984 *(cp1++) = *(cp2++);
6988 /* This still leaves /000000/ when working with a
6989 * VMS device root or concealed root.
6995 ulen = strlen(rslt);
6997 /* Get rid of "000000/ in rooted filespecs */
6999 zeros = strstr(rslt, "/000000/");
7000 if (zeros != NULL) {
7002 mlen = ulen - (zeros - rslt) - 7;
7003 memmove(zeros, &zeros[7], mlen);
7012 } /* end of do_tounixspec() */
7014 /* External entry points */
7015 char *Perl_tounixspec(pTHX_ const char *spec, char *buf)
7016 { return do_tounixspec(spec,buf,0, NULL); }
7017 char *Perl_tounixspec_ts(pTHX_ const char *spec, char *buf)
7018 { return do_tounixspec(spec,buf,1, NULL); }
7019 char *Perl_tounixspec_utf8(pTHX_ const char *spec, char *buf, int * utf8_fl)
7020 { return do_tounixspec(spec,buf,0, utf8_fl); }
7021 char *Perl_tounixspec_utf8_ts(pTHX_ const char *spec, char *buf, int * utf8_fl)
7022 { return do_tounixspec(spec,buf,1, utf8_fl); }
7024 #if __CRTL_VER >= 70200000 && !defined(__VAX)
7027 This procedure is used to identify if a path is based in either
7028 the old SYS$POSIX_ROOT: or the new 8.3 RMS based POSIX root, and
7029 it returns the OpenVMS format directory for it.
7031 It is expecting specifications of only '/' or '/xxxx/'
7033 If a posix root does not exist, or 'xxxx' is not a directory
7034 in the posix root, it returns a failure.
7036 FIX-ME: xxxx could be in UTF-8 and needs to be returned in VTF-7.
7038 It is used only internally by posix_to_vmsspec_hardway().
7041 static int posix_root_to_vms
7042 (char *vmspath, int vmspath_len,
7043 const char *unixpath,
7044 const int * utf8_fl)
7047 struct FAB myfab = cc$rms_fab;
7048 rms_setup_nam(mynam);
7049 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7050 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7051 char * esa, * esal, * rsa, * rsal;
7058 unixlen = strlen(unixpath);
7063 #if __CRTL_VER >= 80200000
7064 /* If not a posix spec already, convert it */
7065 if (decc_posix_compliant_pathnames) {
7066 if (strncmp(unixpath,"\"^UP^",5) != 0) {
7067 sprintf(vmspath,"\"^UP^%s\"",unixpath);
7070 /* This is already a VMS specification, no conversion */
7072 strncpy(vmspath,unixpath, vmspath_len);
7081 /* Check to see if this is under the POSIX root */
7082 if (decc_disable_posix_root) {
7086 /* Skip leading / */
7087 if (unixpath[0] == '/') {
7093 strcpy(vmspath,"SYS$POSIX_ROOT:");
7095 /* If this is only the / , or blank, then... */
7096 if (unixpath[0] == '\0') {
7097 /* by definition, this is the answer */
7101 /* Need to look up a directory */
7105 /* Copy and add '^' escape characters as needed */
7108 while (unixpath[i] != 0) {
7111 j += copy_expand_unix_filename_escape
7112 (&vmspath[j], &unixpath[i], &k, utf8_fl);
7116 path_len = strlen(vmspath);
7117 if (vmspath[path_len - 1] == '/')
7119 vmspath[path_len] = ']';
7121 vmspath[path_len] = '\0';
7124 vmspath[vmspath_len] = 0;
7125 if (unixpath[unixlen - 1] == '/')
7127 esal = PerlMem_malloc(VMS_MAXRSS);
7128 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7129 esa = PerlMem_malloc(NAM$C_MAXRSS + 1);
7130 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7131 rsal = PerlMem_malloc(VMS_MAXRSS);
7132 if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7133 rsa = PerlMem_malloc(NAM$C_MAXRSS + 1);
7134 if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7135 rms_set_fna(myfab, mynam, (char *) vmspath, strlen(vmspath)); /* cast ok */
7136 rms_bind_fab_nam(myfab, mynam);
7137 rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS - 1);
7138 rms_set_rsal(mynam, rsa, NAM$C_MAXRSS, rsal, VMS_MAXRSS - 1);
7139 if (decc_efs_case_preserve)
7140 mynam.naml$b_nop |= NAM$M_NO_SHORT_UPCASE;
7141 #ifdef NAML$M_OPEN_SPECIAL
7142 mynam.naml$l_input_flags |= NAML$M_OPEN_SPECIAL;
7145 /* Set up the remaining naml fields */
7146 sts = sys$parse(&myfab);
7148 /* It failed! Try again as a UNIX filespec */
7157 /* get the Device ID and the FID */
7158 sts = sys$search(&myfab);
7160 /* These are no longer needed */
7165 /* on any failure, returned the POSIX ^UP^ filespec */
7170 specdsc.dsc$a_pointer = vmspath;
7171 specdsc.dsc$w_length = vmspath_len;
7173 dvidsc.dsc$a_pointer = &mynam.naml$t_dvi[1];
7174 dvidsc.dsc$w_length = mynam.naml$t_dvi[0];
7175 sts = lib$fid_to_name
7176 (&dvidsc, mynam.naml$w_fid, &specdsc, &specdsc.dsc$w_length);
7178 /* on any failure, returned the POSIX ^UP^ filespec */
7180 /* This can happen if user does not have permission to read directories */
7181 if (strncmp(unixpath,"\"^UP^",5) != 0)
7182 sprintf(vmspath,"\"^UP^%s\"",unixpath);
7184 strcpy(vmspath, unixpath);
7187 vmspath[specdsc.dsc$w_length] = 0;
7189 /* Are we expecting a directory? */
7190 if (dir_flag != 0) {
7196 i = specdsc.dsc$w_length - 1;
7200 /* Version must be '1' */
7201 if (vmspath[i--] != '1')
7203 /* Version delimiter is one of ".;" */
7204 if ((vmspath[i] != '.') && (vmspath[i] != ';'))
7207 if (vmspath[i--] != 'R')
7209 if (vmspath[i--] != 'I')
7211 if (vmspath[i--] != 'D')
7213 if (vmspath[i--] != '.')
7215 eptr = &vmspath[i+1];
7217 if ((vmspath[i] == ']') || (vmspath[i] == '>')) {
7218 if (vmspath[i-1] != '^') {
7226 /* Get rid of 6 imaginary zero directory filename */
7227 vmspath[i+1] = '\0';
7231 if (vmspath[i] == '0')
7245 /* /dev/mumble needs to be handled special.
7246 /dev/null becomes NLA0:, And there is the potential for other stuff
7247 like /dev/tty which may need to be mapped to something.
7251 slash_dev_special_to_vms
7252 (const char * unixptr,
7262 nextslash = strchr(unixptr, '/');
7263 len = strlen(unixptr);
7264 if (nextslash != NULL)
7265 len = nextslash - unixptr;
7266 cmp = strncmp("null", unixptr, 5);
7268 if (vmspath_len >= 6) {
7269 strcpy(vmspath, "_NLA0:");
7276 /* The built in routines do not understand perl's special needs, so
7277 doing a manual conversion from UNIX to VMS
7279 If the utf8_fl is not null and points to a non-zero value, then
7280 treat 8 bit characters as UTF-8.
7282 The sequence starting with '$(' and ending with ')' will be passed
7283 through with out interpretation instead of being escaped.
7286 static int posix_to_vmsspec_hardway
7287 (char *vmspath, int vmspath_len,
7288 const char *unixpath,
7293 const char *unixptr;
7294 const char *unixend;
7296 const char *lastslash;
7297 const char *lastdot;
7303 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
7304 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
7306 if (utf8_fl != NULL)
7312 /* Ignore leading "/" characters */
7313 while((unixptr[0] == '/') && (unixptr[1] == '/')) {
7316 unixlen = strlen(unixptr);
7318 /* Do nothing with blank paths */
7325 /* This could have a "^UP^ on the front */
7326 if (strncmp(unixptr,"\"^UP^",5) == 0) {
7332 lastslash = strrchr(unixptr,'/');
7333 lastdot = strrchr(unixptr,'.');
7334 unixend = strrchr(unixptr,'\"');
7335 if (!quoted || !((unixend != NULL) && (unixend[1] == '\0'))) {
7336 unixend = unixptr + unixlen;
7339 /* last dot is last dot or past end of string */
7340 if (lastdot == NULL)
7341 lastdot = unixptr + unixlen;
7343 /* if no directories, set last slash to beginning of string */
7344 if (lastslash == NULL) {
7345 lastslash = unixptr;
7348 /* Watch out for trailing "." after last slash, still a directory */
7349 if ((lastslash[1] == '.') && (lastslash[2] == '\0')) {
7350 lastslash = unixptr + unixlen;
7353 /* Watch out for traiing ".." after last slash, still a directory */
7354 if ((lastslash[1] == '.')&&(lastslash[2] == '.')&&(lastslash[3] == '\0')) {
7355 lastslash = unixptr + unixlen;
7358 /* dots in directories are aways escaped */
7359 if (lastdot < lastslash)
7360 lastdot = unixptr + unixlen;
7363 /* if (unixptr < lastslash) then we are in a directory */
7370 /* Start with the UNIX path */
7371 if (*unixptr != '/') {
7372 /* relative paths */
7374 /* If allowing logical names on relative pathnames, then handle here */
7375 if ((unixptr[0] != '.') && !decc_disable_to_vms_logname_translation &&
7376 !decc_posix_compliant_pathnames) {
7382 /* Find the next slash */
7383 nextslash = strchr(unixptr,'/');
7385 esa = PerlMem_malloc(vmspath_len);
7386 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7388 trn = PerlMem_malloc(VMS_MAXRSS);
7389 if (trn == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7391 if (nextslash != NULL) {
7393 seg_len = nextslash - unixptr;
7394 strncpy(esa, unixptr, seg_len);
7398 strcpy(esa, unixptr);
7399 seg_len = strlen(unixptr);
7401 /* trnlnm(section) */
7402 islnm = vmstrnenv(esa, trn, 0, fildev, 0);
7405 /* Now fix up the directory */
7407 /* Split up the path to find the components */
7408 sts = vms_split_path
7427 /* A logical name must be a directory or the full
7428 specification. It is only a full specification if
7429 it is the only component */
7430 if ((unixptr[seg_len] == '\0') ||
7431 (unixptr[seg_len+1] == '\0')) {
7433 /* Is a directory being required? */
7434 if (((n_len + e_len) != 0) && (dir_flag !=0)) {
7435 /* Not a logical name */
7440 if ((unixptr[seg_len] == '/') || (dir_flag != 0)) {
7441 /* This must be a directory */
7442 if (((n_len + e_len) == 0)&&(seg_len <= vmspath_len)) {
7443 strcpy(vmsptr, esa);
7444 vmslen=strlen(vmsptr);
7445 vmsptr[vmslen] = ':';
7447 vmsptr[vmslen] = '\0';
7455 /* must be dev/directory - ignore version */
7456 if ((n_len + e_len) != 0)
7459 /* transfer the volume */
7460 if (v_len > 0 && ((v_len + vmslen) < vmspath_len)) {
7461 strncpy(vmsptr, v_spec, v_len);
7467 /* unroot the rooted directory */
7468 if ((r_len > 0) && ((r_len + d_len + vmslen) < vmspath_len)) {
7470 r_spec[r_len - 1] = ']';
7472 /* This should not be there, but nothing is perfect */
7474 cmp = strcmp(&r_spec[1], "000000.");
7484 strncpy(vmsptr, r_spec, r_len);
7490 /* Bring over the directory. */
7492 ((d_len + vmslen) < vmspath_len)) {
7494 d_spec[d_len - 1] = ']';
7496 cmp = strcmp(&d_spec[1], "000000.");
7507 /* Remove the redundant root */
7515 strncpy(vmsptr, d_spec, d_len);
7529 if (lastslash > unixptr) {
7532 /* skip leading ./ */
7534 while ((unixptr[0] == '.') && (unixptr[1] == '/')) {
7540 /* Are we still in a directory? */
7541 if (unixptr <= lastslash) {
7546 /* if not backing up, then it is relative forward. */
7547 if (!((*unixptr == '.') && (unixptr[1] == '.') &&
7548 ((unixptr[2] == '/') || (&unixptr[2] == unixend)))) {
7556 /* Perl wants an empty directory here to tell the difference
7557 * between a DCL commmand and a filename
7566 /* Handle two special files . and .. */
7567 if (unixptr[0] == '.') {
7568 if (&unixptr[1] == unixend) {
7575 if ((unixptr[1] == '.') && (&unixptr[2] == unixend)) {
7586 else { /* Absolute PATH handling */
7590 /* Need to find out where root is */
7592 /* In theory, this procedure should never get an absolute POSIX pathname
7593 * that can not be found on the POSIX root.
7594 * In practice, that can not be relied on, and things will show up
7595 * here that are a VMS device name or concealed logical name instead.
7596 * So to make things work, this procedure must be tolerant.
7598 esa = PerlMem_malloc(vmspath_len);
7599 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7602 nextslash = strchr(&unixptr[1],'/');
7604 if (nextslash != NULL) {
7606 seg_len = nextslash - &unixptr[1];
7607 strncpy(vmspath, unixptr, seg_len + 1);
7608 vmspath[seg_len+1] = 0;
7611 cmp = strncmp(vmspath, "dev", 4);
7613 sts = slash_dev_special_to_vms(unixptr, vmspath, vmspath_len);
7614 if (sts = SS$_NORMAL)
7618 sts = posix_root_to_vms(esa, vmspath_len, vmspath, utf8_fl);
7621 if ($VMS_STATUS_SUCCESS(sts)) {
7622 /* This is verified to be a real path */
7624 sts = posix_root_to_vms(esa, vmspath_len, "/", NULL);
7625 if ($VMS_STATUS_SUCCESS(sts)) {
7626 strcpy(vmspath, esa);
7627 vmslen = strlen(vmspath);
7628 vmsptr = vmspath + vmslen;
7630 if (unixptr < lastslash) {
7639 cmp = strcmp(rptr,"000000.");
7644 } /* removing 6 zeros */
7645 } /* vmslen < 7, no 6 zeros possible */
7646 } /* Not in a directory */
7647 } /* Posix root found */
7649 /* No posix root, fall back to default directory */
7650 strcpy(vmspath, "SYS$DISK:[");
7651 vmsptr = &vmspath[10];
7653 if (unixptr > lastslash) {
7662 } /* end of verified real path handling */
7667 /* Ok, we have a device or a concealed root that is not in POSIX
7668 * or we have garbage. Make the best of it.
7671 /* Posix to VMS destroyed this, so copy it again */
7672 strncpy(vmspath, &unixptr[1], seg_len);
7673 vmspath[seg_len] = 0;
7675 vmsptr = &vmsptr[vmslen];
7678 /* Now do we need to add the fake 6 zero directory to it? */
7680 if ((*lastslash == '/') && (nextslash < lastslash)) {
7681 /* No there is another directory */
7688 /* now we have foo:bar or foo:[000000]bar to decide from */
7689 islnm = vmstrnenv(vmspath, esa, 0, fildev, 0);
7691 if (!islnm && !decc_posix_compliant_pathnames) {
7693 cmp = strncmp("bin", vmspath, 4);
7695 /* bin => SYS$SYSTEM: */
7696 islnm = vmstrnenv("SYS$SYSTEM:", esa, 0, fildev, 0);
7699 /* tmp => SYS$SCRATCH: */
7700 cmp = strncmp("tmp", vmspath, 4);
7702 islnm = vmstrnenv("SYS$SCRATCH:", esa, 0, fildev, 0);
7707 trnend = islnm ? islnm - 1 : 0;
7709 /* if this was a logical name, ']' or '>' must be present */
7710 /* if not a logical name, then assume a device and hope. */
7711 islnm = trnend ? (esa[trnend] == ']' || esa[trnend] == '>') : 0;
7713 /* if log name and trailing '.' then rooted - treat as device */
7714 add_6zero = islnm ? (esa[trnend-1] == '.') : 0;
7716 /* Fix me, if not a logical name, a device lookup should be
7717 * done to see if the device is file structured. If the device
7718 * is not file structured, the 6 zeros should not be put on.
7720 * As it is, perl is occasionally looking for dev:[000000]tty.
7721 * which looks a little strange.
7723 * Not that easy to detect as "/dev" may be file structured with
7724 * special device files.
7727 if ((add_6zero == 0) && (*nextslash == '/') &&
7728 (&nextslash[1] == unixend)) {
7729 /* No real directory present */
7734 /* Put the device delimiter on */
7737 unixptr = nextslash;
7740 /* Start directory if needed */
7741 if (!islnm || add_6zero) {
7747 /* add fake 000000] if needed */
7760 } /* non-POSIX translation */
7762 } /* End of relative/absolute path handling */
7764 while ((unixptr <= unixend) && (vmslen < vmspath_len)){
7771 if (dir_start != 0) {
7773 /* First characters in a directory are handled special */
7774 while ((*unixptr == '/') ||
7775 ((*unixptr == '.') &&
7776 ((unixptr[1]=='.') || (unixptr[1]=='/') ||
7777 (&unixptr[1]==unixend)))) {
7782 /* Skip redundant / in specification */
7783 while ((*unixptr == '/') && (dir_start != 0)) {
7786 if (unixptr == lastslash)
7789 if (unixptr == lastslash)
7792 /* Skip redundant ./ characters */
7793 while ((*unixptr == '.') &&
7794 ((unixptr[1] == '/')||(&unixptr[1] == unixend))) {
7797 if (unixptr == lastslash)
7799 if (*unixptr == '/')
7802 if (unixptr == lastslash)
7805 /* Skip redundant ../ characters */
7806 while ((*unixptr == '.') && (unixptr[1] == '.') &&
7807 ((unixptr[2] == '/') || (&unixptr[2] == unixend))) {
7808 /* Set the backing up flag */
7814 unixptr++; /* first . */
7815 unixptr++; /* second . */
7816 if (unixptr == lastslash)
7818 if (*unixptr == '/') /* The slash */
7821 if (unixptr == lastslash)
7824 /* To do: Perl expects /.../ to be translated to [...] on VMS */
7825 /* Not needed when VMS is pretending to be UNIX. */
7827 /* Is this loop stuck because of too many dots? */
7828 if (loop_flag == 0) {
7829 /* Exit the loop and pass the rest through */
7834 /* Are we done with directories yet? */
7835 if (unixptr >= lastslash) {
7837 /* Watch out for trailing dots */
7846 if (*unixptr == '/')
7850 /* Have we stopped backing up? */
7855 /* dir_start continues to be = 1 */
7857 if (*unixptr == '-') {
7859 *vmsptr++ = *unixptr++;
7863 /* Now are we done with directories yet? */
7864 if (unixptr >= lastslash) {
7866 /* Watch out for trailing dots */
7882 if (unixptr >= unixend)
7885 /* Normal characters - More EFS work probably needed */
7891 /* remove multiple / */
7892 while (unixptr[1] == '/') {
7895 if (unixptr == lastslash) {
7896 /* Watch out for trailing dots */
7908 /* To do: Perl expects /.../ to be translated to [...] on VMS */
7909 /* Not needed when VMS is pretending to be UNIX. */
7913 if (unixptr != unixend)
7918 if ((unixptr < lastdot) || (unixptr < lastslash) ||
7919 (&unixptr[1] == unixend)) {
7925 /* trailing dot ==> '^..' on VMS */
7926 if (unixptr == unixend) {
7934 *vmsptr++ = *unixptr++;
7938 if (quoted && (&unixptr[1] == unixend)) {
7942 in_cnt = copy_expand_unix_filename_escape
7943 (vmsptr, unixptr, &out_cnt, utf8_fl);
7953 in_cnt = copy_expand_unix_filename_escape
7954 (vmsptr, unixptr, &out_cnt, utf8_fl);
7961 /* Make sure directory is closed */
7962 if (unixptr == lastslash) {
7964 vmsptr2 = vmsptr - 1;
7966 if (*vmsptr2 != ']') {
7969 /* directories do not end in a dot bracket */
7970 if (*vmsptr2 == '.') {
7974 if (*vmsptr2 != '^') {
7975 vmsptr--; /* back up over the dot */
7983 /* Add a trailing dot if a file with no extension */
7984 vmsptr2 = vmsptr - 1;
7986 (*vmsptr2 != ']') && (*vmsptr2 != '*') && (*vmsptr2 != '%') &&
7987 (*vmsptr2 != ')') && (*lastdot != '.')) {
7998 /* Eventual routine to convert a UTF-8 specification to VTF-7. */
7999 static char * utf8_to_vtf7(char * rslt, const char * path, int *utf8_fl)
8004 /* If a UTF8 flag is being passed, honor it */
8006 if (utf8_fl != NULL) {
8007 utf8_flag = *utf8_fl;
8012 /* If there is a possibility of UTF8, then if any UTF8 characters
8013 are present, then they must be converted to VTF-7
8015 result = strcpy(rslt, path); /* FIX-ME */
8018 result = strcpy(rslt, path);
8025 /*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/
8026 static char *int_tovmsspec
8027 (const char *path, char *rslt, int dir_flag, int * utf8_flag) {
8033 unsigned long int infront = 0, hasdir = 1;
8036 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
8037 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
8039 if (vms_debug_fileify) {
8041 fprintf(stderr, "int_tovmsspec: path = NULL\n");
8043 fprintf(stderr, "int_tovmsspec: path = %s\n", path);
8047 /* If we fail, we should be setting errno */
8049 set_vaxc_errno(SS$_BADPARAM);
8052 rslt_len = VMS_MAXRSS-1;
8054 /* '.' and '..' are "[]" and "[-]" for a quick check */
8055 if (path[0] == '.') {
8056 if (path[1] == '\0') {
8058 if (utf8_flag != NULL)
8063 if (path[1] == '.' && path[2] == '\0') {
8065 if (utf8_flag != NULL)
8072 /* Posix specifications are now a native VMS format */
8073 /*--------------------------------------------------*/
8074 #if __CRTL_VER >= 80200000 && !defined(__VAX)
8075 if (decc_posix_compliant_pathnames) {
8076 if (strncmp(path,"\"^UP^",5) == 0) {
8077 posix_to_vmsspec_hardway(rslt, rslt_len, path, dir_flag, utf8_flag);
8083 /* This is really the only way to see if this is already in VMS format */
8084 sts = vms_split_path
8099 /* FIX-ME - If dir_flag is non-zero, then this is a mp_do_vmspath()
8100 replacement, because the above parse just took care of most of
8101 what is needed to do vmspath when the specification is already
8104 And if it is not already, it is easier to do the conversion as
8105 part of this routine than to call this routine and then work on
8109 /* If VMS punctuation was found, it is already VMS format */
8110 if ((v_len != 0) || (r_len != 0) || (d_len != 0) || (vs_len != 0)) {
8111 if (utf8_flag != NULL)
8114 if (vms_debug_fileify) {
8115 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8119 /* Now, what to do with trailing "." cases where there is no
8120 extension? If this is a UNIX specification, and EFS characters
8121 are enabled, then the trailing "." should be converted to a "^.".
8122 But if this was already a VMS specification, then it should be
8125 So in the case of ambiguity, leave the specification alone.
8129 /* If there is a possibility of UTF8, then if any UTF8 characters
8130 are present, then they must be converted to VTF-7
8132 if (utf8_flag != NULL)
8135 if (vms_debug_fileify) {
8136 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8141 dirend = strrchr(path,'/');
8143 if (dirend == NULL) {
8147 /* If we get here with no UNIX directory delimiters, then this is
8148 not a complete file specification, either garbage a UNIX glob
8149 specification that can not be converted to a VMS wildcard, or
8150 it a UNIX shell macro. MakeMaker wants shell macros passed
8153 utf8 flag setting needs to be preserved.
8158 macro_start = strchr(path,'$');
8159 if (macro_start != NULL) {
8160 if (macro_start[1] == '(') {
8164 if ((decc_efs_charset == 0) || (has_macro)) {
8166 if (vms_debug_fileify) {
8167 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8173 /* If POSIX mode active, handle the conversion */
8174 #if __CRTL_VER >= 80200000 && !defined(__VAX)
8175 if (decc_efs_charset) {
8176 posix_to_vmsspec_hardway(rslt, rslt_len, path, dir_flag, utf8_flag);
8177 if (vms_debug_fileify) {
8178 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8184 if (*(dirend+1) == '.') { /* do we have trailing "/." or "/.." or "/..."? */
8185 if (!*(dirend+2)) dirend +=2;
8186 if (*(dirend+2) == '.' && !*(dirend+3)) dirend += 3;
8187 if (decc_efs_charset == 0) {
8188 if (*(dirend+2) == '.' && *(dirend+3) == '.' && !*(dirend+4)) dirend += 4;
8194 lastdot = strrchr(cp2,'.');
8200 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
8202 if (decc_disable_posix_root) {
8203 strcpy(rslt,"sys$disk:[000000]");
8206 strcpy(rslt,"sys$posix_root:[000000]");
8208 if (utf8_flag != NULL)
8210 if (vms_debug_fileify) {
8211 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8215 while (*(++cp2) != '/' && *cp2) *(cp1++) = *cp2;
8217 trndev = PerlMem_malloc(VMS_MAXRSS);
8218 if (trndev == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8219 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8221 /* DECC special handling */
8223 if (strcmp(rslt,"bin") == 0) {
8224 strcpy(rslt,"sys$system");
8227 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8229 else if (strcmp(rslt,"tmp") == 0) {
8230 strcpy(rslt,"sys$scratch");
8233 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8235 else if (!decc_disable_posix_root) {
8236 strcpy(rslt, "sys$posix_root");
8240 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
8241 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8243 else if (strcmp(rslt,"dev") == 0) {
8244 if (strncmp(cp2,"/null", 5) == 0) {
8245 if ((cp2[5] == 0) || (cp2[5] == '/')) {
8246 strcpy(rslt,"NLA0");
8250 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8256 trnend = islnm ? strlen(trndev) - 1 : 0;
8257 islnm = trnend ? (trndev[trnend] == ']' || trndev[trnend] == '>') : 0;
8258 rooted = islnm ? (trndev[trnend-1] == '.') : 0;
8259 /* If the first element of the path is a logical name, determine
8260 * whether it has to be translated so we can add more directories. */
8261 if (!islnm || rooted) {
8264 if (cp2 == dirend) while (infront++ < 6) *(cp1++) = '0';
8268 if (cp2 != dirend) {
8269 strcpy(rslt,trndev);
8270 cp1 = rslt + trnend;
8277 if (decc_disable_posix_root) {
8283 PerlMem_free(trndev);
8288 if (*(cp2+1) == '/' || *(cp2+1) == '\0') {
8289 cp2 += 2; /* skip over "./" - it's redundant */
8290 *(cp1++) = '.'; /* but it does indicate a relative dirspec */
8292 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
8293 *(cp1++) = '-'; /* "../" --> "-" */
8296 else if (*(cp2+1) == '.' && *(cp2+2) == '.' &&
8297 (*(cp2+3) == '/' || *(cp2+3) == '\0')) {
8298 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
8299 if (!*(cp2+4)) *(cp1++) = '.'; /* Simulate trailing '/' for later */
8302 else if ((cp2 != lastdot) || (lastdot < dirend)) {
8303 /* Escape the extra dots in EFS file specifications */
8306 if (cp2 > dirend) cp2 = dirend;
8308 else *(cp1++) = '.';
8310 for (; cp2 < dirend; cp2++) {
8312 if (*(cp2-1) == '/') continue;
8313 if (*(cp1-1) != '.') *(cp1++) = '.';
8316 else if (!infront && *cp2 == '.') {
8317 if (cp2+1 == dirend || *(cp2+1) == '\0') { cp2++; break; }
8318 else if (*(cp2+1) == '/') cp2++; /* skip over "./" - it's redundant */
8319 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
8320 if (*(cp1-1) == '-' || *(cp1-1) == '[') *(cp1++) = '-'; /* handle "../" */
8321 else if (*(cp1-2) == '[') *(cp1-1) = '-';
8322 else { /* back up over previous directory name */
8324 while (*(cp1-1) != '.' && *(cp1-1) != '[') cp1--;
8325 if (*(cp1-1) == '[') {
8326 memcpy(cp1,"000000.",7);
8331 if (cp2 == dirend) break;
8333 else if ( *(cp2+1) == '.' && *(cp2+2) == '.' &&
8334 (*(cp2+3) == '/' || *(cp2+3) == '\0') ) {
8335 if (*(cp1-1) != '.') *(cp1++) = '.'; /* May already have 1 from '/' */
8336 *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
8338 *(cp1++) = '.'; /* Simulate trailing '/' */
8339 cp2 += 2; /* for loop will incr this to == dirend */
8341 else cp2 += 3; /* Trailing '/' was there, so skip it, too */
8344 if (decc_efs_charset == 0)
8345 *(cp1++) = '_'; /* fix up syntax - '.' in name not allowed */
8347 *(cp1++) = '^'; /* fix up syntax - '.' in name is allowed */
8353 if (!infront && *(cp1-1) == '-') *(cp1++) = '.';
8355 if (decc_efs_charset == 0)
8362 else *(cp1++) = *cp2;
8366 if (*(cp1-1) == '.') cp1--; /* Unix spec ending in '/' ==> trailing '.' */
8367 if (hasdir) *(cp1++) = ']';
8368 if (*cp2) cp2++; /* check in case we ended with trailing '..' */
8369 /* fixme for ODS5 */
8376 if (decc_efs_charset == 0)
8387 if (((cp2 < lastdot) || (cp2[1] == '\0')) &&
8388 decc_readdir_dropdotnotype) {
8393 /* trailing dot ==> '^..' on VMS */
8400 *(cp1++) = *(cp2++);
8405 /* This could be a macro to be passed through */
8406 *(cp1++) = *(cp2++);
8408 const char * save_cp2;
8412 /* paranoid check */
8418 *(cp1++) = *(cp2++);
8419 if (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
8420 *(cp1++) = *(cp2++);
8421 while (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
8422 *(cp1++) = *(cp2++);
8425 *(cp1++) = *(cp2++);
8429 if (is_macro == 0) {
8430 /* Not really a macro - never mind */
8443 /* Don't escape again if following character is
8444 * already something we escape.
8446 if (strchr("\"~`!#%^&()=+\'@[]{}:\\|<>_.", *(cp2+1))) {
8447 *(cp1++) = *(cp2++);
8450 /* But otherwise fall through and escape it. */
8468 *(cp1++) = *(cp2++);
8471 /* FIXME: This needs fixing as Perl is putting ".dir;" on UNIX filespecs
8472 * which is wrong. UNIX notation should be ".dir." unless
8473 * the DECC$FILENAME_UNIX_NO_VERSION is enabled.
8474 * changing this behavior could break more things at this time.
8475 * efs character set effectively does not allow "." to be a version
8476 * delimiter as a further complication about changing this.
8478 if (decc_filename_unix_report != 0) {
8481 *(cp1++) = *(cp2++);
8484 *(cp1++) = *(cp2++);
8487 if ((no_type_seen == 1) && decc_readdir_dropdotnotype) {
8491 /* Fix me for "^]", but that requires making sure that you do
8492 * not back up past the start of the filename
8494 if ((*lcp1 != ']') && (*lcp1 != '*') && (*lcp1 != '%'))
8499 if (utf8_flag != NULL)
8501 if (vms_debug_fileify) {
8502 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8506 } /* end of int_tovmsspec() */
8509 /*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/
8510 static char *mp_do_tovmsspec
8511 (pTHX_ const char *path, char *buf, int ts, int dir_flag, int * utf8_flag) {
8512 static char __tovmsspec_retbuf[VMS_MAXRSS];
8513 char * vmsspec, *ret_spec, *ret_buf;
8517 if (ret_buf == NULL) {
8519 Newx(vmsspec, VMS_MAXRSS, char);
8520 if (vmsspec == NULL)
8521 _ckvmssts(SS$_INSFMEM);
8524 ret_buf = __tovmsspec_retbuf;
8528 ret_spec = int_tovmsspec(path, ret_buf, 0, utf8_flag);
8530 if (ret_spec == NULL) {
8531 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
8538 } /* end of mp_do_tovmsspec() */
8540 /* External entry points */
8541 char *Perl_tovmsspec(pTHX_ const char *path, char *buf)
8542 { return do_tovmsspec(path,buf,0,NULL); }
8543 char *Perl_tovmsspec_ts(pTHX_ const char *path, char *buf)
8544 { return do_tovmsspec(path,buf,1,NULL); }
8545 char *Perl_tovmsspec_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
8546 { return do_tovmsspec(path,buf,0,utf8_fl); }
8547 char *Perl_tovmsspec_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
8548 { return do_tovmsspec(path,buf,1,utf8_fl); }
8550 /*{{{ char *tovmspath[_ts](char *path, char *buf, const int *)*/
8551 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) {
8552 static char __tovmspath_retbuf[VMS_MAXRSS];
8554 char *pathified, *vmsified, *cp;
8556 if (path == NULL) return NULL;
8557 pathified = PerlMem_malloc(VMS_MAXRSS);
8558 if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
8559 if (do_pathify_dirspec(path,pathified,0,NULL) == NULL) {
8560 PerlMem_free(pathified);
8566 Newx(vmsified, VMS_MAXRSS, char);
8567 if (do_tovmsspec(pathified, buf ? buf : vmsified, 0, NULL) == NULL) {
8568 PerlMem_free(pathified);
8569 if (vmsified) Safefree(vmsified);
8572 PerlMem_free(pathified);
8577 vmslen = strlen(vmsified);
8578 Newx(cp,vmslen+1,char);
8579 memcpy(cp,vmsified,vmslen);
8585 strcpy(__tovmspath_retbuf,vmsified);
8587 return __tovmspath_retbuf;
8590 } /* end of do_tovmspath() */
8592 /* External entry points */
8593 char *Perl_tovmspath(pTHX_ const char *path, char *buf)
8594 { return do_tovmspath(path,buf,0, NULL); }
8595 char *Perl_tovmspath_ts(pTHX_ const char *path, char *buf)
8596 { return do_tovmspath(path,buf,1, NULL); }
8597 char *Perl_tovmspath_utf8(pTHX_ const char *path, char *buf, int *utf8_fl)
8598 { return do_tovmspath(path,buf,0,utf8_fl); }
8599 char *Perl_tovmspath_utf8_ts(pTHX_ const char *path, char *buf, int *utf8_fl)
8600 { return do_tovmspath(path,buf,1,utf8_fl); }
8603 /*{{{ char *tounixpath[_ts](char *path, char *buf, int * utf8_fl)*/
8604 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) {
8605 static char __tounixpath_retbuf[VMS_MAXRSS];
8607 char *pathified, *unixified, *cp;
8609 if (path == NULL) return NULL;
8610 pathified = PerlMem_malloc(VMS_MAXRSS);
8611 if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
8612 if (do_pathify_dirspec(path,pathified,0,NULL) == NULL) {
8613 PerlMem_free(pathified);
8619 Newx(unixified, VMS_MAXRSS, char);
8621 if (do_tounixspec(pathified,buf ? buf : unixified,0,NULL) == NULL) {
8622 PerlMem_free(pathified);
8623 if (unixified) Safefree(unixified);
8626 PerlMem_free(pathified);
8631 unixlen = strlen(unixified);
8632 Newx(cp,unixlen+1,char);
8633 memcpy(cp,unixified,unixlen);
8635 Safefree(unixified);
8639 strcpy(__tounixpath_retbuf,unixified);
8640 Safefree(unixified);
8641 return __tounixpath_retbuf;
8644 } /* end of do_tounixpath() */
8646 /* External entry points */
8647 char *Perl_tounixpath(pTHX_ const char *path, char *buf)
8648 { return do_tounixpath(path,buf,0,NULL); }
8649 char *Perl_tounixpath_ts(pTHX_ const char *path, char *buf)
8650 { return do_tounixpath(path,buf,1,NULL); }
8651 char *Perl_tounixpath_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
8652 { return do_tounixpath(path,buf,0,utf8_fl); }
8653 char *Perl_tounixpath_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
8654 { return do_tounixpath(path,buf,1,utf8_fl); }
8657 * @(#)argproc.c 2.2 94/08/16 Mark Pizzolato (mark AT infocomm DOT com)
8659 *****************************************************************************
8661 * Copyright (C) 1989-1994, 2007 by *
8662 * Mark Pizzolato - INFO COMM, Danville, California (510) 837-5600 *
8664 * Permission is hereby granted for the reproduction of this software *
8665 * on condition that this copyright notice is included in source *
8666 * distributions of the software. The code may be modified and *
8667 * distributed under the same terms as Perl itself. *
8669 * 27-Aug-1994 Modified for inclusion in perl5 *
8670 * by Charles Bailey (bailey AT newman DOT upenn DOT edu) *
8671 *****************************************************************************
8675 * getredirection() is intended to aid in porting C programs
8676 * to VMS (Vax-11 C). The native VMS environment does not support
8677 * '>' and '<' I/O redirection, or command line wild card expansion,
8678 * or a command line pipe mechanism using the '|' AND background
8679 * command execution '&'. All of these capabilities are provided to any
8680 * C program which calls this procedure as the first thing in the
8682 * The piping mechanism will probably work with almost any 'filter' type
8683 * of program. With suitable modification, it may useful for other
8684 * portability problems as well.
8686 * Author: Mark Pizzolato (mark AT infocomm DOT com)
8690 struct list_item *next;
8694 static void add_item(struct list_item **head,
8695 struct list_item **tail,
8699 static void mp_expand_wild_cards(pTHX_ char *item,
8700 struct list_item **head,
8701 struct list_item **tail,
8704 static int background_process(pTHX_ int argc, char **argv);
8706 static void pipe_and_fork(pTHX_ char **cmargv);
8708 /*{{{ void getredirection(int *ac, char ***av)*/
8710 mp_getredirection(pTHX_ int *ac, char ***av)
8712 * Process vms redirection arg's. Exit if any error is seen.
8713 * If getredirection() processes an argument, it is erased
8714 * from the vector. getredirection() returns a new argc and argv value.
8715 * In the event that a background command is requested (by a trailing "&"),
8716 * this routine creates a background subprocess, and simply exits the program.
8718 * Warning: do not try to simplify the code for vms. The code
8719 * presupposes that getredirection() is called before any data is
8720 * read from stdin or written to stdout.
8722 * Normal usage is as follows:
8728 * getredirection(&argc, &argv);
8732 int argc = *ac; /* Argument Count */
8733 char **argv = *av; /* Argument Vector */
8734 char *ap; /* Argument pointer */
8735 int j; /* argv[] index */
8736 int item_count = 0; /* Count of Items in List */
8737 struct list_item *list_head = 0; /* First Item in List */
8738 struct list_item *list_tail; /* Last Item in List */
8739 char *in = NULL; /* Input File Name */
8740 char *out = NULL; /* Output File Name */
8741 char *outmode = "w"; /* Mode to Open Output File */
8742 char *err = NULL; /* Error File Name */
8743 char *errmode = "w"; /* Mode to Open Error File */
8744 int cmargc = 0; /* Piped Command Arg Count */
8745 char **cmargv = NULL;/* Piped Command Arg Vector */
8748 * First handle the case where the last thing on the line ends with
8749 * a '&'. This indicates the desire for the command to be run in a
8750 * subprocess, so we satisfy that desire.
8753 if (0 == strcmp("&", ap))
8754 exit(background_process(aTHX_ --argc, argv));
8755 if (*ap && '&' == ap[strlen(ap)-1])
8757 ap[strlen(ap)-1] = '\0';
8758 exit(background_process(aTHX_ argc, argv));
8761 * Now we handle the general redirection cases that involve '>', '>>',
8762 * '<', and pipes '|'.
8764 for (j = 0; j < argc; ++j)
8766 if (0 == strcmp("<", argv[j]))
8770 fprintf(stderr,"No input file after < on command line");
8771 exit(LIB$_WRONUMARG);
8776 if ('<' == *(ap = argv[j]))
8781 if (0 == strcmp(">", ap))
8785 fprintf(stderr,"No output file after > on command line");
8786 exit(LIB$_WRONUMARG);
8805 fprintf(stderr,"No output file after > or >> on command line");
8806 exit(LIB$_WRONUMARG);
8810 if (('2' == *ap) && ('>' == ap[1]))
8827 fprintf(stderr,"No output file after 2> or 2>> on command line");
8828 exit(LIB$_WRONUMARG);
8832 if (0 == strcmp("|", argv[j]))
8836 fprintf(stderr,"No command into which to pipe on command line");
8837 exit(LIB$_WRONUMARG);
8839 cmargc = argc-(j+1);
8840 cmargv = &argv[j+1];
8844 if ('|' == *(ap = argv[j]))
8852 expand_wild_cards(ap, &list_head, &list_tail, &item_count);
8855 * Allocate and fill in the new argument vector, Some Unix's terminate
8856 * the list with an extra null pointer.
8858 argv = (char **) PerlMem_malloc((item_count+1) * sizeof(char *));
8859 if (argv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8861 for (j = 0; j < item_count; ++j, list_head = list_head->next)
8862 argv[j] = list_head->value;
8868 fprintf(stderr,"'|' and '>' may not both be specified on command line");
8869 exit(LIB$_INVARGORD);
8871 pipe_and_fork(aTHX_ cmargv);
8874 /* Check for input from a pipe (mailbox) */
8876 if (in == NULL && 1 == isapipe(0))
8878 char mbxname[L_tmpnam];
8880 long int dvi_item = DVI$_DEVBUFSIZ;
8881 $DESCRIPTOR(mbxnam, "");
8882 $DESCRIPTOR(mbxdevnam, "");
8884 /* Input from a pipe, reopen it in binary mode to disable */
8885 /* carriage control processing. */
8887 fgetname(stdin, mbxname);
8888 mbxnam.dsc$a_pointer = mbxname;
8889 mbxnam.dsc$w_length = strlen(mbxnam.dsc$a_pointer);
8890 lib$getdvi(&dvi_item, 0, &mbxnam, &bufsize, 0, 0);
8891 mbxdevnam.dsc$a_pointer = mbxname;
8892 mbxdevnam.dsc$w_length = sizeof(mbxname);
8893 dvi_item = DVI$_DEVNAM;
8894 lib$getdvi(&dvi_item, 0, &mbxnam, 0, &mbxdevnam, &mbxdevnam.dsc$w_length);
8895 mbxdevnam.dsc$a_pointer[mbxdevnam.dsc$w_length] = '\0';
8898 freopen(mbxname, "rb", stdin);
8901 fprintf(stderr,"Can't reopen input pipe (name: %s) in binary mode",mbxname);
8905 if ((in != NULL) && (NULL == freopen(in, "r", stdin, "mbc=32", "mbf=2")))
8907 fprintf(stderr,"Can't open input file %s as stdin",in);
8910 if ((out != NULL) && (NULL == freopen(out, outmode, stdout, "mbc=32", "mbf=2")))
8912 fprintf(stderr,"Can't open output file %s as stdout",out);
8915 if (out != NULL) Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",out);
8918 if (strcmp(err,"&1") == 0) {
8919 dup2(fileno(stdout), fileno(stderr));
8920 Perl_vmssetuserlnm(aTHX_ "SYS$ERROR","SYS$OUTPUT");
8923 if (NULL == (tmperr = fopen(err, errmode, "mbc=32", "mbf=2")))
8925 fprintf(stderr,"Can't open error file %s as stderr",err);
8929 if (NULL == freopen(err, "a", stderr, "mbc=32", "mbf=2"))
8933 Perl_vmssetuserlnm(aTHX_ "SYS$ERROR",err);
8936 #ifdef ARGPROC_DEBUG
8937 PerlIO_printf(Perl_debug_log, "Arglist:\n");
8938 for (j = 0; j < *ac; ++j)
8939 PerlIO_printf(Perl_debug_log, "argv[%d] = '%s'\n", j, argv[j]);
8941 /* Clear errors we may have hit expanding wildcards, so they don't
8942 show up in Perl's $! later */
8943 set_errno(0); set_vaxc_errno(1);
8944 } /* end of getredirection() */
8947 static void add_item(struct list_item **head,
8948 struct list_item **tail,
8954 *head = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
8955 if (head == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8959 (*tail)->next = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
8960 if ((*tail)->next == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8961 *tail = (*tail)->next;
8963 (*tail)->value = value;
8967 static void mp_expand_wild_cards(pTHX_ char *item,
8968 struct list_item **head,
8969 struct list_item **tail,
8973 unsigned long int context = 0;
8981 $DESCRIPTOR(filespec, "");
8982 $DESCRIPTOR(defaultspec, "SYS$DISK:[]");
8983 $DESCRIPTOR(resultspec, "");
8984 unsigned long int lff_flags = 0;
8988 #ifdef VMS_LONGNAME_SUPPORT
8989 lff_flags = LIB$M_FIL_LONG_NAMES;
8992 for (cp = item; *cp; cp++) {
8993 if (*cp == '*' || *cp == '%' || isspace(*cp)) break;
8994 if (*cp == '.' && *(cp-1) == '.' && *(cp-2) =='.') break;
8996 if (!*cp || isspace(*cp))
8998 add_item(head, tail, item, count);
9003 /* "double quoted" wild card expressions pass as is */
9004 /* From DCL that means using e.g.: */
9005 /* perl program """perl.*""" */
9006 item_len = strlen(item);
9007 if ( '"' == *item && '"' == item[item_len-1] )
9010 item[item_len-2] = '\0';
9011 add_item(head, tail, item, count);
9015 resultspec.dsc$b_dtype = DSC$K_DTYPE_T;
9016 resultspec.dsc$b_class = DSC$K_CLASS_D;
9017 resultspec.dsc$a_pointer = NULL;
9018 vmsspec = PerlMem_malloc(VMS_MAXRSS);
9019 if (vmsspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9020 if ((isunix = (int) strchr(item,'/')) != (int) NULL)
9021 filespec.dsc$a_pointer = int_tovmsspec(item, vmsspec, 0, NULL);
9022 if (!isunix || !filespec.dsc$a_pointer)
9023 filespec.dsc$a_pointer = item;
9024 filespec.dsc$w_length = strlen(filespec.dsc$a_pointer);
9026 * Only return version specs, if the caller specified a version
9028 had_version = strchr(item, ';');
9030 * Only return device and directory specs, if the caller specifed either.
9032 had_device = strchr(item, ':');
9033 had_directory = (isunix || NULL != strchr(item, '[')) || (NULL != strchr(item, '<'));
9035 while ($VMS_STATUS_SUCCESS(sts = lib$find_file
9036 (&filespec, &resultspec, &context,
9037 &defaultspec, 0, &rms_sts, &lff_flags)))
9042 string = PerlMem_malloc(resultspec.dsc$w_length+1);
9043 if (string == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9044 strncpy(string, resultspec.dsc$a_pointer, resultspec.dsc$w_length);
9045 string[resultspec.dsc$w_length] = '\0';
9046 if (NULL == had_version)
9047 *(strrchr(string, ';')) = '\0';
9048 if ((!had_directory) && (had_device == NULL))
9050 if (NULL == (devdir = strrchr(string, ']')))
9051 devdir = strrchr(string, '>');
9052 strcpy(string, devdir + 1);
9055 * Be consistent with what the C RTL has already done to the rest of
9056 * the argv items and lowercase all of these names.
9058 if (!decc_efs_case_preserve) {
9059 for (c = string; *c; ++c)
9063 if (isunix) trim_unixpath(string,item,1);
9064 add_item(head, tail, string, count);
9067 PerlMem_free(vmsspec);
9068 if (sts != RMS$_NMF)
9070 set_vaxc_errno(sts);
9073 case RMS$_FNF: case RMS$_DNF:
9074 set_errno(ENOENT); break;
9076 set_errno(ENOTDIR); break;
9078 set_errno(ENODEV); break;
9079 case RMS$_FNM: case RMS$_SYN:
9080 set_errno(EINVAL); break;
9082 set_errno(EACCES); break;
9084 _ckvmssts_noperl(sts);
9088 add_item(head, tail, item, count);
9089 _ckvmssts_noperl(lib$sfree1_dd(&resultspec));
9090 _ckvmssts_noperl(lib$find_file_end(&context));
9093 static int child_st[2];/* Event Flag set when child process completes */
9095 static unsigned short child_chan;/* I/O Channel for Pipe Mailbox */
9097 static unsigned long int exit_handler(int *status)
9101 if (0 == child_st[0])
9103 #ifdef ARGPROC_DEBUG
9104 PerlIO_printf(Perl_debug_log, "Waiting for Child Process to Finish . . .\n");
9106 fflush(stdout); /* Have to flush pipe for binary data to */
9107 /* terminate properly -- <tp@mccall.com> */
9108 sys$qiow(0, child_chan, IO$_WRITEOF, iosb, 0, 0, 0, 0, 0, 0, 0, 0);
9109 sys$dassgn(child_chan);
9111 sys$synch(0, child_st);
9116 static void sig_child(int chan)
9118 #ifdef ARGPROC_DEBUG
9119 PerlIO_printf(Perl_debug_log, "Child Completion AST\n");
9121 if (child_st[0] == 0)
9125 static struct exit_control_block exit_block =
9130 &exit_block.exit_status,
9135 pipe_and_fork(pTHX_ char **cmargv)
9138 struct dsc$descriptor_s *vmscmd;
9139 char subcmd[2*MAX_DCL_LINE_LENGTH], *p, *q;
9140 int sts, j, l, ismcr, quote, tquote = 0;
9142 sts = setup_cmddsc(aTHX_ cmargv[0],0,"e,&vmscmd);
9143 vms_execfree(vmscmd);
9148 ismcr = q && toupper(*q) == 'M' && toupper(*(q+1)) == 'C'
9149 && toupper(*(q+2)) == 'R' && !*(q+3);
9151 while (q && l < MAX_DCL_LINE_LENGTH) {
9153 if (j > 0 && quote) {
9159 if (ismcr && j > 1) quote = 1;
9160 tquote = (strchr(q,' ')) != NULL || *q == '\0';
9163 if (quote || tquote) {
9169 if ((quote||tquote) && *q == '"') {
9179 fp = safe_popen(aTHX_ subcmd,"wbF",&sts);
9181 PerlIO_printf(Perl_debug_log,"Can't open output pipe (status %d)",sts);
9185 static int background_process(pTHX_ int argc, char **argv)
9187 char command[MAX_DCL_SYMBOL + 1] = "$";
9188 $DESCRIPTOR(value, "");
9189 static $DESCRIPTOR(cmd, "BACKGROUND$COMMAND");
9190 static $DESCRIPTOR(null, "NLA0:");
9191 static $DESCRIPTOR(pidsymbol, "SHELL_BACKGROUND_PID");
9193 $DESCRIPTOR(pidstr, "");
9195 unsigned long int flags = 17, one = 1, retsts;
9198 strcat(command, argv[0]);
9199 len = strlen(command);
9200 while (--argc && (len < MAX_DCL_SYMBOL))
9202 strcat(command, " \"");
9203 strcat(command, *(++argv));
9204 strcat(command, "\"");
9205 len = strlen(command);
9207 value.dsc$a_pointer = command;
9208 value.dsc$w_length = strlen(value.dsc$a_pointer);
9209 _ckvmssts_noperl(lib$set_symbol(&cmd, &value));
9210 retsts = lib$spawn(&cmd, &null, 0, &flags, 0, &pid);
9211 if (retsts == 0x38250) { /* DCL-W-NOTIFY - We must be BATCH, so retry */
9212 _ckvmssts_noperl(lib$spawn(&cmd, &null, 0, &one, 0, &pid));
9215 _ckvmssts_noperl(retsts);
9217 #ifdef ARGPROC_DEBUG
9218 PerlIO_printf(Perl_debug_log, "%s\n", command);
9220 sprintf(pidstring, "%08X", pid);
9221 PerlIO_printf(Perl_debug_log, "%s\n", pidstring);
9222 pidstr.dsc$a_pointer = pidstring;
9223 pidstr.dsc$w_length = strlen(pidstr.dsc$a_pointer);
9224 lib$set_symbol(&pidsymbol, &pidstr);
9228 /***** End of code taken from Mark Pizzolato's argproc.c package *****/
9231 /* OS-specific initialization at image activation (not thread startup) */
9232 /* Older VAXC header files lack these constants */
9233 #ifndef JPI$_RIGHTS_SIZE
9234 # define JPI$_RIGHTS_SIZE 817
9236 #ifndef KGB$M_SUBSYSTEM
9237 # define KGB$M_SUBSYSTEM 0x8
9240 /* Avoid Newx() in vms_image_init as thread context has not been initialized. */
9242 /*{{{void vms_image_init(int *, char ***)*/
9244 vms_image_init(int *argcp, char ***argvp)
9248 char eqv[LNM$C_NAMLENGTH+1] = "";
9249 unsigned int len, tabct = 8, tabidx = 0;
9250 unsigned long int *mask, iosb[2], i, rlst[128], rsz;
9251 unsigned long int iprv[(sizeof(union prvdef) + sizeof(unsigned long int) - 1) / sizeof(unsigned long int)];
9252 unsigned short int dummy, rlen;
9253 struct dsc$descriptor_s **tabvec;
9254 #if defined(PERL_IMPLICIT_CONTEXT)
9257 struct itmlst_3 jpilist[4] = { {sizeof iprv, JPI$_IMAGPRIV, iprv, &dummy},
9258 {sizeof rlst, JPI$_RIGHTSLIST, rlst, &rlen},
9259 { sizeof rsz, JPI$_RIGHTS_SIZE, &rsz, &dummy},
9262 #ifdef KILL_BY_SIGPRC
9263 Perl_csighandler_init();
9266 /* This was moved from the pre-image init handler because on threaded */
9267 /* Perl it was always returning 0 for the default value. */
9268 status = simple_trnlnm("SYS$POSIX_ROOT", val_str, sizeof(val_str));
9271 s = decc$feature_get_index("DECC$DISABLE_POSIX_ROOT");
9274 initial = decc$feature_get_value(s, 4);
9276 /* initial is -1 if nothing has set the feature */
9277 /* initial is 1 if the logical name is present */
9278 decc_disable_posix_root = decc$feature_get_value(s, 1);
9280 /* If the value is not valid, force the feature off */
9281 if (decc_disable_posix_root < 0) {
9282 decc$feature_set_value(s, 1, 1);
9283 decc_disable_posix_root = 1;
9287 /* Traditionally Perl assumes this is off */
9288 decc_disable_posix_root = 1;
9289 decc$feature_set_value(s, 1, 1);
9295 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,jpilist,iosb,NULL,NULL));
9296 _ckvmssts_noperl(iosb[0]);
9297 for (i = 0; i < sizeof iprv / sizeof(unsigned long int); i++) {
9298 if (iprv[i]) { /* Running image installed with privs? */
9299 _ckvmssts_noperl(sys$setprv(0,iprv,0,NULL)); /* Turn 'em off. */
9304 /* Rights identifiers might trigger tainting as well. */
9305 if (!will_taint && (rlen || rsz)) {
9306 while (rlen < rsz) {
9307 /* We didn't get all the identifiers on the first pass. Allocate a
9308 * buffer much larger than $GETJPI wants (rsz is size in bytes that
9309 * were needed to hold all identifiers at time of last call; we'll
9310 * allocate that many unsigned long ints), and go back and get 'em.
9311 * If it gave us less than it wanted to despite ample buffer space,
9312 * something's broken. Is your system missing a system identifier?
9314 if (rsz <= jpilist[1].buflen) {
9315 /* Perl_croak accvios when used this early in startup. */
9316 fprintf(stderr, "vms_image_init: $getjpiw refuses to store RIGHTSLIST of %u bytes in buffer of %u bytes.\n%s",
9317 rsz, (unsigned long) jpilist[1].buflen,
9318 "Check your rights database for corruption.\n");
9321 if (jpilist[1].bufadr != rlst) PerlMem_free(jpilist[1].bufadr);
9322 jpilist[1].bufadr = mask = (unsigned long int *) PerlMem_malloc(rsz * sizeof(unsigned long int));
9323 if (mask == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9324 jpilist[1].buflen = rsz * sizeof(unsigned long int);
9325 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,&jpilist[1],iosb,NULL,NULL));
9326 _ckvmssts_noperl(iosb[0]);
9328 mask = jpilist[1].bufadr;
9329 /* Check attribute flags for each identifier (2nd longword); protected
9330 * subsystem identifiers trigger tainting.
9332 for (i = 1; i < (rlen + sizeof(unsigned long int) - 1) / sizeof(unsigned long int); i += 2) {
9333 if (mask[i] & KGB$M_SUBSYSTEM) {
9338 if (mask != rlst) PerlMem_free(mask);
9341 /* When Perl is in decc_filename_unix_report mode and is run from a concealed
9342 * logical, some versions of the CRTL will add a phanthom /000000/
9343 * directory. This needs to be removed.
9345 if (decc_filename_unix_report) {
9348 ulen = strlen(argvp[0][0]);
9350 zeros = strstr(argvp[0][0], "/000000/");
9351 if (zeros != NULL) {
9353 mlen = ulen - (zeros - argvp[0][0]) - 7;
9354 memmove(zeros, &zeros[7], mlen);
9356 argvp[0][0][ulen] = '\0';
9359 /* It also may have a trailing dot that needs to be removed otherwise
9360 * it will be converted to VMS mode incorrectly.
9363 if ((argvp[0][0][ulen] == '.') && (decc_readdir_dropdotnotype))
9364 argvp[0][0][ulen] = '\0';
9367 /* We need to use this hack to tell Perl it should run with tainting,
9368 * since its tainting flag may be part of the PL_curinterp struct, which
9369 * hasn't been allocated when vms_image_init() is called.
9372 char **newargv, **oldargv;
9374 newargv = (char **) PerlMem_malloc(((*argcp)+2) * sizeof(char *));
9375 if (newargv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9376 newargv[0] = oldargv[0];
9377 newargv[1] = PerlMem_malloc(3 * sizeof(char));
9378 if (newargv[1] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9379 strcpy(newargv[1], "-T");
9380 Copy(&oldargv[1],&newargv[2],(*argcp)-1,char **);
9382 newargv[*argcp] = NULL;
9383 /* We orphan the old argv, since we don't know where it's come from,
9384 * so we don't know how to free it.
9388 else { /* Did user explicitly request tainting? */
9390 char *cp, **av = *argvp;
9391 for (i = 1; i < *argcp; i++) {
9392 if (*av[i] != '-') break;
9393 for (cp = av[i]+1; *cp; cp++) {
9394 if (*cp == 'T') { will_taint = 1; break; }
9395 else if ( (*cp == 'd' || *cp == 'V') && *(cp+1) == ':' ||
9396 strchr("DFIiMmx",*cp)) break;
9398 if (will_taint) break;
9403 len = my_trnlnm("PERL_ENV_TABLES",eqv,tabidx);
9406 tabvec = (struct dsc$descriptor_s **)
9407 PerlMem_malloc(tabct * sizeof(struct dsc$descriptor_s *));
9408 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9410 else if (tabidx >= tabct) {
9412 tabvec = (struct dsc$descriptor_s **) PerlMem_realloc(tabvec, tabct * sizeof(struct dsc$descriptor_s *));
9413 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9415 tabvec[tabidx] = (struct dsc$descriptor_s *) PerlMem_malloc(sizeof(struct dsc$descriptor_s));
9416 if (tabvec[tabidx] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9417 tabvec[tabidx]->dsc$w_length = 0;
9418 tabvec[tabidx]->dsc$b_dtype = DSC$K_DTYPE_T;
9419 tabvec[tabidx]->dsc$b_class = DSC$K_CLASS_D;
9420 tabvec[tabidx]->dsc$a_pointer = NULL;
9421 _ckvmssts_noperl(lib$scopy_r_dx(&len,eqv,tabvec[tabidx]));
9423 if (tabidx) { tabvec[tabidx] = NULL; env_tables = tabvec; }
9425 getredirection(argcp,argvp);
9426 #if defined(USE_ITHREADS) && ( defined(__DECC) || defined(__DECCXX) )
9428 # include <reentrancy.h>
9429 decc$set_reentrancy(C$C_MULTITHREAD);
9438 * Trim Unix-style prefix off filespec, so it looks like what a shell
9439 * glob expansion would return (i.e. from specified prefix on, not
9440 * full path). Note that returned filespec is Unix-style, regardless
9441 * of whether input filespec was VMS-style or Unix-style.
9443 * fspec is filespec to be trimmed, and wildspec is wildcard spec used to
9444 * determine prefix (both may be in VMS or Unix syntax). opts is a bit
9445 * vector of options; at present, only bit 0 is used, and if set tells
9446 * trim unixpath to try the current default directory as a prefix when
9447 * presented with a possibly ambiguous ... wildcard.
9449 * Returns !=0 on success, with trimmed filespec replacing contents of
9450 * fspec, and 0 on failure, with contents of fpsec unchanged.
9452 /*{{{int trim_unixpath(char *fspec, char *wildspec, int opts)*/
9454 Perl_trim_unixpath(pTHX_ char *fspec, const char *wildspec, int opts)
9456 char *unixified, *unixwild,
9457 *template, *base, *end, *cp1, *cp2;
9458 register int tmplen, reslen = 0, dirs = 0;
9460 if (!wildspec || !fspec) return 0;
9462 unixwild = PerlMem_malloc(VMS_MAXRSS);
9463 if (unixwild == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9464 template = unixwild;
9465 if (strpbrk(wildspec,"]>:") != NULL) {
9466 if (do_tounixspec(wildspec,unixwild,0,NULL) == NULL) {
9467 PerlMem_free(unixwild);
9472 strncpy(unixwild, wildspec, VMS_MAXRSS-1);
9473 unixwild[VMS_MAXRSS-1] = 0;
9475 unixified = PerlMem_malloc(VMS_MAXRSS);
9476 if (unixified == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9477 if (strpbrk(fspec,"]>:") != NULL) {
9478 if (do_tounixspec(fspec,unixified,0,NULL) == NULL) {
9479 PerlMem_free(unixwild);
9480 PerlMem_free(unixified);
9483 else base = unixified;
9484 /* reslen != 0 ==> we had to unixify resultant filespec, so we must
9485 * check to see that final result fits into (isn't longer than) fspec */
9486 reslen = strlen(fspec);
9490 /* No prefix or absolute path on wildcard, so nothing to remove */
9491 if (!*template || *template == '/') {
9492 PerlMem_free(unixwild);
9493 if (base == fspec) {
9494 PerlMem_free(unixified);
9497 tmplen = strlen(unixified);
9498 if (tmplen > reslen) {
9499 PerlMem_free(unixified);
9500 return 0; /* not enough space */
9502 /* Copy unixified resultant, including trailing NUL */
9503 memmove(fspec,unixified,tmplen+1);
9504 PerlMem_free(unixified);
9508 for (end = base; *end; end++) ; /* Find end of resultant filespec */
9509 if ((cp1 = strstr(template,".../")) == NULL) { /* No ...; just count elts */
9510 for (cp1 = template; *cp1; cp1++) if (*cp1 == '/') dirs++;
9511 for (cp1 = end ;cp1 >= base; cp1--)
9512 if ((*cp1 == '/') && !dirs--) /* postdec so we get front of rel path */
9514 if (cp1 != fspec) memmove(fspec,cp1, end - cp1 + 1);
9515 PerlMem_free(unixified);
9516 PerlMem_free(unixwild);
9521 char *front, *nextell, *lcend, *lcfront, *ellipsis = cp1;
9522 int ells = 1, totells, segdirs, match;
9523 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL},
9524 resdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
9526 while ((cp1 = strstr(ellipsis+4,".../")) != NULL) {ellipsis = cp1; ells++;}
9528 for (cp1 = ellipsis+4; *cp1; cp1++) if (*cp1 == '/') dirs++;
9529 tpl = PerlMem_malloc(VMS_MAXRSS);
9530 if (tpl == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9531 if (ellipsis == template && opts & 1) {
9532 /* Template begins with an ellipsis. Since we can't tell how many
9533 * directory names at the front of the resultant to keep for an
9534 * arbitrary starting point, we arbitrarily choose the current
9535 * default directory as a starting point. If it's there as a prefix,
9536 * clip it off. If not, fall through and act as if the leading
9537 * ellipsis weren't there (i.e. return shortest possible path that
9538 * could match template).
9540 if (getcwd(tpl, (VMS_MAXRSS - 1),0) == NULL) {
9542 PerlMem_free(unixified);
9543 PerlMem_free(unixwild);
9546 if (!decc_efs_case_preserve) {
9547 for (cp1 = tpl, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
9548 if (_tolower(*cp1) != _tolower(*cp2)) break;
9550 segdirs = dirs - totells; /* Min # of dirs we must have left */
9551 for (front = cp2+1; *front; front++) if (*front == '/') segdirs--;
9552 if (*cp1 == '\0' && *cp2 == '/' && segdirs < 1) {
9553 memmove(fspec,cp2+1,end - cp2);
9555 PerlMem_free(unixified);
9556 PerlMem_free(unixwild);
9560 /* First off, back up over constant elements at end of path */
9562 for (front = end ; front >= base; front--)
9563 if (*front == '/' && !dirs--) { front++; break; }
9565 lcres = PerlMem_malloc(VMS_MAXRSS);
9566 if (lcres == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9567 for (cp1=template,cp2=lcres; *cp1 && cp2 <= lcres + (VMS_MAXRSS - 1);
9569 if (!decc_efs_case_preserve) {
9570 *cp2 = _tolower(*cp1); /* Make lc copy for match */
9578 PerlMem_free(unixified);
9579 PerlMem_free(unixwild);
9580 PerlMem_free(lcres);
9581 return 0; /* Path too long. */
9584 *cp2 = '\0'; /* Pick up with memcpy later */
9585 lcfront = lcres + (front - base);
9586 /* Now skip over each ellipsis and try to match the path in front of it. */
9588 for (cp1 = ellipsis - 2; cp1 >= template; cp1--)
9589 if (*(cp1) == '.' && *(cp1+1) == '.' &&
9590 *(cp1+2) == '.' && *(cp1+3) == '/' ) break;
9591 if (cp1 < template) break; /* template started with an ellipsis */
9592 if (cp1 + 4 == ellipsis) { /* Consecutive ellipses */
9593 ellipsis = cp1; continue;
9595 wilddsc.dsc$a_pointer = tpl;
9596 wilddsc.dsc$w_length = resdsc.dsc$w_length = ellipsis - 1 - cp1;
9598 for (segdirs = 0, cp2 = tpl;
9599 cp1 <= ellipsis - 1 && cp2 <= tpl + (VMS_MAXRSS-1);
9601 if (*cp1 == '?') *cp2 = '%'; /* Substitute VMS' wildcard for Unix' */
9603 if (!decc_efs_case_preserve) {
9604 *cp2 = _tolower(*cp1); /* else lowercase for match */
9607 *cp2 = *cp1; /* else preserve case for match */
9610 if (*cp2 == '/') segdirs++;
9612 if (cp1 != ellipsis - 1) {
9614 PerlMem_free(unixified);
9615 PerlMem_free(unixwild);
9616 PerlMem_free(lcres);
9617 return 0; /* Path too long */
9619 /* Back up at least as many dirs as in template before matching */
9620 for (cp1 = lcfront - 1; segdirs && cp1 >= lcres; cp1--)
9621 if (*cp1 == '/' && !segdirs--) { cp1++; break; }
9622 for (match = 0; cp1 > lcres;) {
9623 resdsc.dsc$a_pointer = cp1;
9624 if (str$match_wild(&wilddsc,&resdsc) == STR$_MATCH) {
9626 if (match == 1) lcfront = cp1;
9628 for ( ; cp1 >= lcres; cp1--) if (*cp1 == '/') { cp1++; break; }
9632 PerlMem_free(unixified);
9633 PerlMem_free(unixwild);
9634 PerlMem_free(lcres);
9635 return 0; /* Can't find prefix ??? */
9637 if (match > 1 && opts & 1) {
9638 /* This ... wildcard could cover more than one set of dirs (i.e.
9639 * a set of similar dir names is repeated). If the template
9640 * contains more than 1 ..., upstream elements could resolve the
9641 * ambiguity, but it's not worth a full backtracking setup here.
9642 * As a quick heuristic, clip off the current default directory
9643 * if it's present to find the trimmed spec, else use the
9644 * shortest string that this ... could cover.
9646 char def[NAM$C_MAXRSS+1], *st;
9648 if (getcwd(def, sizeof def,0) == NULL) {
9649 PerlMem_free(unixified);
9650 PerlMem_free(unixwild);
9651 PerlMem_free(lcres);
9655 if (!decc_efs_case_preserve) {
9656 for (cp1 = def, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
9657 if (_tolower(*cp1) != _tolower(*cp2)) break;
9659 segdirs = dirs - totells; /* Min # of dirs we must have left */
9660 for (st = cp2+1; *st; st++) if (*st == '/') segdirs--;
9661 if (*cp1 == '\0' && *cp2 == '/') {
9662 memmove(fspec,cp2+1,end - cp2);
9664 PerlMem_free(unixified);
9665 PerlMem_free(unixwild);
9666 PerlMem_free(lcres);
9669 /* Nope -- stick with lcfront from above and keep going. */
9672 memmove(fspec,base + (lcfront - lcres), lcend - lcfront + 1);
9674 PerlMem_free(unixified);
9675 PerlMem_free(unixwild);
9676 PerlMem_free(lcres);
9681 } /* end of trim_unixpath() */
9686 * VMS readdir() routines.
9687 * Written by Rich $alz, <rsalz@bbn.com> in August, 1990.
9689 * 21-Jul-1994 Charles Bailey bailey@newman.upenn.edu
9690 * Minor modifications to original routines.
9693 /* readdir may have been redefined by reentr.h, so make sure we get
9694 * the local version for what we do here.
9699 #if !defined(PERL_IMPLICIT_CONTEXT)
9700 # define readdir Perl_readdir
9702 # define readdir(a) Perl_readdir(aTHX_ a)
9705 /* Number of elements in vms_versions array */
9706 #define VERSIZE(e) (sizeof e->vms_versions / sizeof e->vms_versions[0])
9709 * Open a directory, return a handle for later use.
9711 /*{{{ DIR *opendir(char*name) */
9713 Perl_opendir(pTHX_ const char *name)
9719 Newx(dir, VMS_MAXRSS, char);
9720 if (do_tovmspath(name,dir,0,NULL) == NULL) {
9724 /* Check access before stat; otherwise stat does not
9725 * accurately report whether it's a directory.
9727 if (!cando_by_name_int(S_IRUSR,0,dir,PERL_RMSEXPAND_M_VMS_IN)) {
9728 /* cando_by_name has already set errno */
9732 if (flex_stat(dir,&sb) == -1) return NULL;
9733 if (!S_ISDIR(sb.st_mode)) {
9735 set_errno(ENOTDIR); set_vaxc_errno(RMS$_DIR);
9738 /* Get memory for the handle, and the pattern. */
9740 Newx(dd->pattern,strlen(dir)+sizeof "*.*" + 1,char);
9742 /* Fill in the fields; mainly playing with the descriptor. */
9743 sprintf(dd->pattern, "%s*.*",dir);
9748 /* By saying we always want the result of readdir() in unix format, we
9749 * are really saying we want all the escapes removed. Otherwise the caller,
9750 * having no way to know whether it's already in VMS format, might send it
9751 * through tovmsspec again, thus double escaping.
9753 dd->flags = PERL_VMSDIR_M_UNIXSPECS;
9754 dd->pat.dsc$a_pointer = dd->pattern;
9755 dd->pat.dsc$w_length = strlen(dd->pattern);
9756 dd->pat.dsc$b_dtype = DSC$K_DTYPE_T;
9757 dd->pat.dsc$b_class = DSC$K_CLASS_S;
9758 #if defined(USE_ITHREADS)
9759 Newx(dd->mutex,1,perl_mutex);
9760 MUTEX_INIT( (perl_mutex *) dd->mutex );
9766 } /* end of opendir() */
9770 * Set the flag to indicate we want versions or not.
9772 /*{{{ void vmsreaddirversions(DIR *dd, int flag)*/
9774 vmsreaddirversions(DIR *dd, int flag)
9777 dd->flags |= PERL_VMSDIR_M_VERSIONS;
9779 dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
9784 * Free up an opened directory.
9786 /*{{{ void closedir(DIR *dd)*/
9788 Perl_closedir(DIR *dd)
9792 sts = lib$find_file_end(&dd->context);
9793 Safefree(dd->pattern);
9794 #if defined(USE_ITHREADS)
9795 MUTEX_DESTROY( (perl_mutex *) dd->mutex );
9796 Safefree(dd->mutex);
9803 * Collect all the version numbers for the current file.
9806 collectversions(pTHX_ DIR *dd)
9808 struct dsc$descriptor_s pat;
9809 struct dsc$descriptor_s res;
9811 char *p, *text, *buff;
9813 unsigned long context, tmpsts;
9815 /* Convenient shorthand. */
9818 /* Add the version wildcard, ignoring the "*.*" put on before */
9819 i = strlen(dd->pattern);
9820 Newx(text,i + e->d_namlen + 3,char);
9821 strcpy(text, dd->pattern);
9822 sprintf(&text[i - 3], "%s;*", e->d_name);
9824 /* Set up the pattern descriptor. */
9825 pat.dsc$a_pointer = text;
9826 pat.dsc$w_length = i + e->d_namlen - 1;
9827 pat.dsc$b_dtype = DSC$K_DTYPE_T;
9828 pat.dsc$b_class = DSC$K_CLASS_S;
9830 /* Set up result descriptor. */
9831 Newx(buff, VMS_MAXRSS, char);
9832 res.dsc$a_pointer = buff;
9833 res.dsc$w_length = VMS_MAXRSS - 1;
9834 res.dsc$b_dtype = DSC$K_DTYPE_T;
9835 res.dsc$b_class = DSC$K_CLASS_S;
9837 /* Read files, collecting versions. */
9838 for (context = 0, e->vms_verscount = 0;
9839 e->vms_verscount < VERSIZE(e);
9840 e->vms_verscount++) {
9842 unsigned long flags = 0;
9844 #ifdef VMS_LONGNAME_SUPPORT
9845 flags = LIB$M_FIL_LONG_NAMES;
9847 tmpsts = lib$find_file(&pat, &res, &context, NULL, NULL, &rsts, &flags);
9848 if (tmpsts == RMS$_NMF || context == 0) break;
9850 buff[VMS_MAXRSS - 1] = '\0';
9851 if ((p = strchr(buff, ';')))
9852 e->vms_versions[e->vms_verscount] = atoi(p + 1);
9854 e->vms_versions[e->vms_verscount] = -1;
9857 _ckvmssts(lib$find_file_end(&context));
9861 } /* end of collectversions() */
9864 * Read the next entry from the directory.
9866 /*{{{ struct dirent *readdir(DIR *dd)*/
9868 Perl_readdir(pTHX_ DIR *dd)
9870 struct dsc$descriptor_s res;
9872 unsigned long int tmpsts;
9874 unsigned long flags = 0;
9875 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
9876 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
9878 /* Set up result descriptor, and get next file. */
9879 Newx(buff, VMS_MAXRSS, char);
9880 res.dsc$a_pointer = buff;
9881 res.dsc$w_length = VMS_MAXRSS - 1;
9882 res.dsc$b_dtype = DSC$K_DTYPE_T;
9883 res.dsc$b_class = DSC$K_CLASS_S;
9885 #ifdef VMS_LONGNAME_SUPPORT
9886 flags = LIB$M_FIL_LONG_NAMES;
9889 tmpsts = lib$find_file
9890 (&dd->pat, &res, &dd->context, NULL, NULL, &rsts, &flags);
9891 if ( tmpsts == RMS$_NMF || dd->context == 0) return NULL; /* None left. */
9892 if (!(tmpsts & 1)) {
9893 set_vaxc_errno(tmpsts);
9896 set_errno(EACCES); break;
9898 set_errno(ENODEV); break;
9900 set_errno(ENOTDIR); break;
9901 case RMS$_FNF: case RMS$_DNF:
9902 set_errno(ENOENT); break;
9910 /* Force the buffer to end with a NUL, and downcase name to match C convention. */
9911 buff[res.dsc$w_length] = '\0';
9912 p = buff + res.dsc$w_length;
9913 while (--p >= buff) if (!isspace(*p)) break;
9915 if (!decc_efs_case_preserve) {
9916 for (p = buff; *p; p++) *p = _tolower(*p);
9919 /* Skip any directory component and just copy the name. */
9920 sts = vms_split_path
9935 if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
9937 /* In Unix report mode, remove the ".dir;1" from the name */
9938 /* if it is a real directory. */
9939 if (decc_filename_unix_report || decc_efs_charset) {
9940 if ((e_len == 4) && (vs_len == 2) && (vs_spec[1] == '1')) {
9941 if ((toupper(e_spec[1]) == 'D') &&
9942 (toupper(e_spec[2]) == 'I') &&
9943 (toupper(e_spec[3]) == 'R')) {
9947 ret_sts = stat(buff, (stat_t *)&statbuf);
9948 if ((ret_sts == 0) && S_ISDIR(statbuf.st_mode)) {
9956 /* Drop NULL extensions on UNIX file specification */
9957 if ((e_len == 1) && decc_readdir_dropdotnotype) {
9963 strncpy(dd->entry.d_name, n_spec, n_len + e_len);
9964 dd->entry.d_name[n_len + e_len] = '\0';
9965 dd->entry.d_namlen = strlen(dd->entry.d_name);
9967 /* Convert the filename to UNIX format if needed */
9968 if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
9970 /* Translate the encoded characters. */
9971 /* Fixme: Unicode handling could result in embedded 0 characters */
9972 if (strchr(dd->entry.d_name, '^') != NULL) {
9975 p = dd->entry.d_name;
9978 int inchars_read, outchars_added;
9979 inchars_read = copy_expand_vms_filename_escape(q, p, &outchars_added);
9981 q += outchars_added;
9983 /* if outchars_added > 1, then this is a wide file specification */
9984 /* Wide file specifications need to be passed in Perl */
9985 /* counted strings apparently with a Unicode flag */
9988 strcpy(dd->entry.d_name, new_name);
9989 dd->entry.d_namlen = strlen(dd->entry.d_name);
9993 dd->entry.vms_verscount = 0;
9994 if (dd->flags & PERL_VMSDIR_M_VERSIONS) collectversions(aTHX_ dd);
9998 } /* end of readdir() */
10002 * Read the next entry from the directory -- thread-safe version.
10004 /*{{{ int readdir_r(DIR *dd, struct dirent *entry, struct dirent **result)*/
10006 Perl_readdir_r(pTHX_ DIR *dd, struct dirent *entry, struct dirent **result)
10010 MUTEX_LOCK( (perl_mutex *) dd->mutex );
10012 entry = readdir(dd);
10014 retval = ( *result == NULL ? errno : 0 );
10016 MUTEX_UNLOCK( (perl_mutex *) dd->mutex );
10020 } /* end of readdir_r() */
10024 * Return something that can be used in a seekdir later.
10026 /*{{{ long telldir(DIR *dd)*/
10028 Perl_telldir(DIR *dd)
10035 * Return to a spot where we used to be. Brute force.
10037 /*{{{ void seekdir(DIR *dd,long count)*/
10039 Perl_seekdir(pTHX_ DIR *dd, long count)
10043 /* If we haven't done anything yet... */
10044 if (dd->count == 0)
10047 /* Remember some state, and clear it. */
10048 old_flags = dd->flags;
10049 dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
10050 _ckvmssts(lib$find_file_end(&dd->context));
10053 /* The increment is in readdir(). */
10054 for (dd->count = 0; dd->count < count; )
10057 dd->flags = old_flags;
10059 } /* end of seekdir() */
10062 /* VMS subprocess management
10064 * my_vfork() - just a vfork(), after setting a flag to record that
10065 * the current script is trying a Unix-style fork/exec.
10067 * vms_do_aexec() and vms_do_exec() are called in response to the
10068 * perl 'exec' function. If this follows a vfork call, then they
10069 * call out the regular perl routines in doio.c which do an
10070 * execvp (for those who really want to try this under VMS).
10071 * Otherwise, they do exactly what the perl docs say exec should
10072 * do - terminate the current script and invoke a new command
10073 * (See below for notes on command syntax.)
10075 * do_aspawn() and do_spawn() implement the VMS side of the perl
10076 * 'system' function.
10078 * Note on command arguments to perl 'exec' and 'system': When handled
10079 * in 'VMSish fashion' (i.e. not after a call to vfork) The args
10080 * are concatenated to form a DCL command string. If the first non-numeric
10081 * arg begins with '$' (i.e. the perl script had "\$ Type" or some such),
10082 * the command string is handed off to DCL directly. Otherwise,
10083 * the first token of the command is taken as the filespec of an image
10084 * to run. The filespec is expanded using a default type of '.EXE' and
10085 * the process defaults for device, directory, etc., and if found, the resultant
10086 * filespec is invoked using the DCL verb 'MCR', and passed the rest of
10087 * the command string as parameters. This is perhaps a bit complicated,
10088 * but I hope it will form a happy medium between what VMS folks expect
10089 * from lib$spawn and what Unix folks expect from exec.
10092 static int vfork_called;
10094 /*{{{int my_vfork()*/
10105 vms_execfree(struct dsc$descriptor_s *vmscmd)
10108 if (vmscmd->dsc$a_pointer) {
10109 PerlMem_free(vmscmd->dsc$a_pointer);
10111 PerlMem_free(vmscmd);
10116 setup_argstr(pTHX_ SV *really, SV **mark, SV **sp)
10118 char *junk, *tmps = NULL;
10119 register size_t cmdlen = 0;
10126 tmps = SvPV(really,rlen);
10128 cmdlen += rlen + 1;
10133 for (idx++; idx <= sp; idx++) {
10135 junk = SvPVx(*idx,rlen);
10136 cmdlen += rlen ? rlen + 1 : 0;
10139 Newx(PL_Cmd, cmdlen+1, char);
10141 if (tmps && *tmps) {
10142 strcpy(PL_Cmd,tmps);
10145 else *PL_Cmd = '\0';
10146 while (++mark <= sp) {
10148 char *s = SvPVx(*mark,n_a);
10150 if (*PL_Cmd) strcat(PL_Cmd," ");
10156 } /* end of setup_argstr() */
10159 static unsigned long int
10160 setup_cmddsc(pTHX_ const char *incmd, int check_img, int *suggest_quote,
10161 struct dsc$descriptor_s **pvmscmd)
10165 char image_name[NAM$C_MAXRSS+1];
10166 char image_argv[NAM$C_MAXRSS+1];
10167 $DESCRIPTOR(defdsc,".EXE");
10168 $DESCRIPTOR(defdsc2,".");
10169 struct dsc$descriptor_s resdsc;
10170 struct dsc$descriptor_s *vmscmd;
10171 struct dsc$descriptor_s imgdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10172 unsigned long int cxt = 0, flags = 1, retsts = SS$_NORMAL;
10173 register char *s, *rest, *cp, *wordbreak;
10176 register int isdcl;
10178 vmscmd = PerlMem_malloc(sizeof(struct dsc$descriptor_s));
10179 if (vmscmd == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10181 /* vmsspec is a DCL command buffer, not just a filename */
10182 vmsspec = PerlMem_malloc(MAX_DCL_LINE_LENGTH + 1);
10183 if (vmsspec == NULL)
10184 _ckvmssts_noperl(SS$_INSFMEM);
10186 resspec = PerlMem_malloc(VMS_MAXRSS);
10187 if (resspec == NULL)
10188 _ckvmssts_noperl(SS$_INSFMEM);
10190 /* Make a copy for modification */
10191 cmdlen = strlen(incmd);
10192 cmd = PerlMem_malloc(cmdlen+1);
10193 if (cmd == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10194 strncpy(cmd, incmd, cmdlen);
10199 resdsc.dsc$a_pointer = resspec;
10200 resdsc.dsc$b_dtype = DSC$K_DTYPE_T;
10201 resdsc.dsc$b_class = DSC$K_CLASS_S;
10202 resdsc.dsc$w_length = VMS_MAXRSS - 1;
10204 vmscmd->dsc$a_pointer = NULL;
10205 vmscmd->dsc$b_dtype = DSC$K_DTYPE_T;
10206 vmscmd->dsc$b_class = DSC$K_CLASS_S;
10207 vmscmd->dsc$w_length = 0;
10208 if (pvmscmd) *pvmscmd = vmscmd;
10210 if (suggest_quote) *suggest_quote = 0;
10212 if (strlen(cmd) > MAX_DCL_LINE_LENGTH) {
10214 PerlMem_free(vmsspec);
10215 PerlMem_free(resspec);
10216 return CLI$_BUFOVF; /* continuation lines currently unsupported */
10221 while (*s && isspace(*s)) s++;
10223 if (*s == '@' || *s == '$') {
10224 vmsspec[0] = *s; rest = s + 1;
10225 for (cp = &vmsspec[1]; *rest && isspace(*rest); rest++,cp++) *cp = *rest;
10227 else { cp = vmsspec; rest = s; }
10228 if (*rest == '.' || *rest == '/') {
10230 for (cp2 = resspec;
10231 *rest && !isspace(*rest) && cp2 - resspec < (VMS_MAXRSS - 1);
10232 rest++, cp2++) *cp2 = *rest;
10234 if (int_tovmsspec(resspec, cp, 0, NULL)) {
10237 /* When a UNIX spec with no file type is translated to VMS, */
10238 /* A trailing '.' is appended under ODS-5 rules. */
10239 /* Here we do not want that trailing "." as it prevents */
10240 /* Looking for a implied ".exe" type. */
10241 if (decc_efs_charset) {
10243 i = strlen(vmsspec);
10244 if (vmsspec[i-1] == '.') {
10245 vmsspec[i-1] = '\0';
10250 for (cp2 = vmsspec + strlen(vmsspec);
10251 *rest && cp2 - vmsspec < MAX_DCL_LINE_LENGTH;
10252 rest++, cp2++) *cp2 = *rest;
10257 /* Intuit whether verb (first word of cmd) is a DCL command:
10258 * - if first nonspace char is '@', it's a DCL indirection
10260 * - if verb contains a filespec separator, it's not a DCL command
10261 * - if it doesn't, caller tells us whether to default to a DCL
10262 * command, or to a local image unless told it's DCL (by leading '$')
10266 if (suggest_quote) *suggest_quote = 1;
10268 register char *filespec = strpbrk(s,":<[.;");
10269 rest = wordbreak = strpbrk(s," \"\t/");
10270 if (!wordbreak) wordbreak = s + strlen(s);
10271 if (*s == '$') check_img = 0;
10272 if (filespec && (filespec < wordbreak)) isdcl = 0;
10273 else isdcl = !check_img;
10278 imgdsc.dsc$a_pointer = s;
10279 imgdsc.dsc$w_length = wordbreak - s;
10280 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
10282 _ckvmssts_noperl(lib$find_file_end(&cxt));
10283 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
10284 if (!(retsts & 1) && *s == '$') {
10285 _ckvmssts_noperl(lib$find_file_end(&cxt));
10286 imgdsc.dsc$a_pointer++; imgdsc.dsc$w_length--;
10287 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
10289 _ckvmssts_noperl(lib$find_file_end(&cxt));
10290 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
10294 _ckvmssts_noperl(lib$find_file_end(&cxt));
10299 while (*s && !isspace(*s)) s++;
10302 /* check that it's really not DCL with no file extension */
10303 fp = fopen(resspec,"r","ctx=bin","ctx=rec","shr=get");
10305 char b[256] = {0,0,0,0};
10306 read(fileno(fp), b, 256);
10307 isdcl = isprint(b[0]) && isprint(b[1]) && isprint(b[2]) && isprint(b[3]);
10311 /* Check for script */
10313 if ((b[0] == '#') && (b[1] == '!'))
10315 #ifdef ALTERNATE_SHEBANG
10317 shebang_len = strlen(ALTERNATE_SHEBANG);
10318 if (strncmp(b, ALTERNATE_SHEBANG, shebang_len) == 0) {
10320 perlstr = strstr("perl",b);
10321 if (perlstr == NULL)
10329 if (shebang_len > 0) {
10332 char tmpspec[NAM$C_MAXRSS + 1];
10335 /* Image is following after white space */
10336 /*--------------------------------------*/
10337 while (isprint(b[i]) && isspace(b[i]))
10341 while (isprint(b[i]) && !isspace(b[i])) {
10342 tmpspec[j++] = b[i++];
10343 if (j >= NAM$C_MAXRSS)
10348 /* There may be some default parameters to the image */
10349 /*---------------------------------------------------*/
10351 while (isprint(b[i])) {
10352 image_argv[j++] = b[i++];
10353 if (j >= NAM$C_MAXRSS)
10356 while ((j > 0) && !isprint(image_argv[j-1]))
10360 /* It will need to be converted to VMS format and validated */
10361 if (tmpspec[0] != '\0') {
10364 /* Try to find the exact program requested to be run */
10365 /*---------------------------------------------------*/
10366 iname = do_rmsexpand
10367 (tmpspec, image_name, 0, ".exe",
10368 PERL_RMSEXPAND_M_VMS, NULL, NULL);
10369 if (iname != NULL) {
10370 if (cando_by_name_int
10371 (S_IXUSR,0,image_name,PERL_RMSEXPAND_M_VMS_IN)) {
10372 /* MCR prefix needed */
10376 /* Try again with a null type */
10377 /*----------------------------*/
10378 iname = do_rmsexpand
10379 (tmpspec, image_name, 0, ".",
10380 PERL_RMSEXPAND_M_VMS, NULL, NULL);
10381 if (iname != NULL) {
10382 if (cando_by_name_int
10383 (S_IXUSR,0,image_name, PERL_RMSEXPAND_M_VMS_IN)) {
10384 /* MCR prefix needed */
10390 /* Did we find the image to run the script? */
10391 /*------------------------------------------*/
10395 /* Assume DCL or foreign command exists */
10396 /*--------------------------------------*/
10397 tchr = strrchr(tmpspec, '/');
10398 if (tchr != NULL) {
10404 strcpy(image_name, tchr);
10412 if (check_img && isdcl) {
10414 PerlMem_free(resspec);
10415 PerlMem_free(vmsspec);
10419 if (cando_by_name(S_IXUSR,0,resspec)) {
10420 vmscmd->dsc$a_pointer = PerlMem_malloc(MAX_DCL_LINE_LENGTH);
10421 if (vmscmd->dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10423 strcpy(vmscmd->dsc$a_pointer,"$ MCR ");
10424 if (image_name[0] != 0) {
10425 strcat(vmscmd->dsc$a_pointer, image_name);
10426 strcat(vmscmd->dsc$a_pointer, " ");
10428 } else if (image_name[0] != 0) {
10429 strcpy(vmscmd->dsc$a_pointer, image_name);
10430 strcat(vmscmd->dsc$a_pointer, " ");
10432 strcpy(vmscmd->dsc$a_pointer,"@");
10434 if (suggest_quote) *suggest_quote = 1;
10436 /* If there is an image name, use original command */
10437 if (image_name[0] == 0)
10438 strcat(vmscmd->dsc$a_pointer,resspec);
10441 while (*rest && isspace(*rest)) rest++;
10444 if (image_argv[0] != 0) {
10445 strcat(vmscmd->dsc$a_pointer,image_argv);
10446 strcat(vmscmd->dsc$a_pointer, " ");
10452 rest_len = strlen(rest);
10453 vmscmd_len = strlen(vmscmd->dsc$a_pointer);
10454 if ((rest_len + vmscmd_len) < MAX_DCL_LINE_LENGTH)
10455 strcat(vmscmd->dsc$a_pointer,rest);
10457 retsts = CLI$_BUFOVF;
10459 vmscmd->dsc$w_length = strlen(vmscmd->dsc$a_pointer);
10461 PerlMem_free(vmsspec);
10462 PerlMem_free(resspec);
10463 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
10469 /* It's either a DCL command or we couldn't find a suitable image */
10470 vmscmd->dsc$w_length = strlen(cmd);
10472 vmscmd->dsc$a_pointer = PerlMem_malloc(vmscmd->dsc$w_length + 1);
10473 strncpy(vmscmd->dsc$a_pointer,cmd,vmscmd->dsc$w_length);
10474 vmscmd->dsc$a_pointer[vmscmd->dsc$w_length] = 0;
10477 PerlMem_free(resspec);
10478 PerlMem_free(vmsspec);
10480 /* check if it's a symbol (for quoting purposes) */
10481 if (suggest_quote && !*suggest_quote) {
10483 char equiv[LNM$C_NAMLENGTH];
10484 struct dsc$descriptor_s eqvdsc = {sizeof(equiv), DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10485 eqvdsc.dsc$a_pointer = equiv;
10487 iss = lib$get_symbol(vmscmd,&eqvdsc);
10488 if (iss&1 && (*equiv == '$' || *equiv == '@')) *suggest_quote = 1;
10490 if (!(retsts & 1)) {
10491 /* just hand off status values likely to be due to user error */
10492 if (retsts == RMS$_FNF || retsts == RMS$_DNF || retsts == RMS$_PRV ||
10493 retsts == RMS$_DEV || retsts == RMS$_DIR || retsts == RMS$_SYN ||
10494 (retsts & STS$M_CODE) == (SHR$_NOWILD & STS$M_CODE)) return retsts;
10495 else { _ckvmssts_noperl(retsts); }
10498 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
10500 } /* end of setup_cmddsc() */
10503 /* {{{ bool vms_do_aexec(SV *really,SV **mark,SV **sp) */
10505 Perl_vms_do_aexec(pTHX_ SV *really,SV **mark,SV **sp)
10511 if (vfork_called) { /* this follows a vfork - act Unixish */
10513 if (vfork_called < 0) {
10514 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
10517 else return do_aexec(really,mark,sp);
10519 /* no vfork - act VMSish */
10520 cmd = setup_argstr(aTHX_ really,mark,sp);
10521 exec_sts = vms_do_exec(cmd);
10522 Safefree(cmd); /* Clean up from setup_argstr() */
10527 } /* end of vms_do_aexec() */
10530 /* {{{bool vms_do_exec(char *cmd) */
10532 Perl_vms_do_exec(pTHX_ const char *cmd)
10534 struct dsc$descriptor_s *vmscmd;
10536 if (vfork_called) { /* this follows a vfork - act Unixish */
10538 if (vfork_called < 0) {
10539 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
10542 else return do_exec(cmd);
10545 { /* no vfork - act VMSish */
10546 unsigned long int retsts;
10549 TAINT_PROPER("exec");
10550 if ((retsts = setup_cmddsc(aTHX_ cmd,1,0,&vmscmd)) & 1)
10551 retsts = lib$do_command(vmscmd);
10554 case RMS$_FNF: case RMS$_DNF:
10555 set_errno(ENOENT); break;
10557 set_errno(ENOTDIR); break;
10559 set_errno(ENODEV); break;
10561 set_errno(EACCES); break;
10563 set_errno(EINVAL); break;
10564 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
10565 set_errno(E2BIG); break;
10566 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
10567 _ckvmssts_noperl(retsts); /* fall through */
10568 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
10569 set_errno(EVMSERR);
10571 set_vaxc_errno(retsts);
10572 if (ckWARN(WARN_EXEC)) {
10573 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't exec \"%*s\": %s",
10574 vmscmd->dsc$w_length, vmscmd->dsc$a_pointer, Strerror(errno));
10576 vms_execfree(vmscmd);
10581 } /* end of vms_do_exec() */
10584 int do_spawn2(pTHX_ const char *, int);
10587 Perl_do_aspawn(pTHX_ SV* really, SV** mark, SV** sp)
10589 unsigned long int sts;
10595 /* We'll copy the (undocumented?) Win32 behavior and allow a
10596 * numeric first argument. But the only value we'll support
10597 * through do_aspawn is a value of 1, which means spawn without
10598 * waiting for completion -- other values are ignored.
10600 if (SvNIOKp(*(mark+1)) && !SvPOKp(*(mark+1))) {
10602 flags = SvIVx(*mark);
10605 if (flags && flags == 1) /* the Win32 P_NOWAIT value */
10606 flags = CLI$M_NOWAIT;
10610 cmd = setup_argstr(aTHX_ really, mark, sp);
10611 sts = do_spawn2(aTHX_ cmd, flags);
10612 /* pp_sys will clean up cmd */
10616 } /* end of do_aspawn() */
10620 /* {{{int do_spawn(char* cmd) */
10622 Perl_do_spawn(pTHX_ char* cmd)
10624 PERL_ARGS_ASSERT_DO_SPAWN;
10626 return do_spawn2(aTHX_ cmd, 0);
10630 /* {{{int do_spawn_nowait(char* cmd) */
10632 Perl_do_spawn_nowait(pTHX_ char* cmd)
10634 PERL_ARGS_ASSERT_DO_SPAWN_NOWAIT;
10636 return do_spawn2(aTHX_ cmd, CLI$M_NOWAIT);
10640 /* {{{int do_spawn2(char *cmd) */
10642 do_spawn2(pTHX_ const char *cmd, int flags)
10644 unsigned long int sts, substs;
10646 /* The caller of this routine expects to Safefree(PL_Cmd) */
10647 Newx(PL_Cmd,10,char);
10650 TAINT_PROPER("spawn");
10651 if (!cmd || !*cmd) {
10652 sts = lib$spawn(0,0,0,&flags,0,0,&substs,0,0,0,0,0,0);
10655 case RMS$_FNF: case RMS$_DNF:
10656 set_errno(ENOENT); break;
10658 set_errno(ENOTDIR); break;
10660 set_errno(ENODEV); break;
10662 set_errno(EACCES); break;
10664 set_errno(EINVAL); break;
10665 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
10666 set_errno(E2BIG); break;
10667 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
10668 _ckvmssts_noperl(sts); /* fall through */
10669 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
10670 set_errno(EVMSERR);
10672 set_vaxc_errno(sts);
10673 if (ckWARN(WARN_EXEC)) {
10674 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't spawn: %s",
10683 if (flags & CLI$M_NOWAIT)
10686 strcpy(mode, "nW");
10688 fp = safe_popen(aTHX_ cmd, mode, (int *)&sts);
10691 /* sts will be the pid in the nowait case */
10694 } /* end of do_spawn2() */
10698 static unsigned int *sockflags, sockflagsize;
10701 * Shim fdopen to identify sockets for my_fwrite later, since the stdio
10702 * routines found in some versions of the CRTL can't deal with sockets.
10703 * We don't shim the other file open routines since a socket isn't
10704 * likely to be opened by a name.
10706 /*{{{ FILE *my_fdopen(int fd, const char *mode)*/
10707 FILE *my_fdopen(int fd, const char *mode)
10709 FILE *fp = fdopen(fd, mode);
10712 unsigned int fdoff = fd / sizeof(unsigned int);
10713 Stat_t sbuf; /* native stat; we don't need flex_stat */
10714 if (!sockflagsize || fdoff > sockflagsize) {
10715 if (sockflags) Renew( sockflags,fdoff+2,unsigned int);
10716 else Newx (sockflags,fdoff+2,unsigned int);
10717 memset(sockflags+sockflagsize,0,fdoff + 2 - sockflagsize);
10718 sockflagsize = fdoff + 2;
10720 if (fstat(fd, (struct stat *)&sbuf) == 0 && S_ISSOCK(sbuf.st_mode))
10721 sockflags[fdoff] |= 1 << (fd % sizeof(unsigned int));
10730 * Clear the corresponding bit when the (possibly) socket stream is closed.
10731 * There still a small hole: we miss an implicit close which might occur
10732 * via freopen(). >> Todo
10734 /*{{{ int my_fclose(FILE *fp)*/
10735 int my_fclose(FILE *fp) {
10737 unsigned int fd = fileno(fp);
10738 unsigned int fdoff = fd / sizeof(unsigned int);
10740 if (sockflagsize && fdoff < sockflagsize)
10741 sockflags[fdoff] &= ~(1 << fd % sizeof(unsigned int));
10749 * A simple fwrite replacement which outputs itmsz*nitm chars without
10750 * introducing record boundaries every itmsz chars.
10751 * We are using fputs, which depends on a terminating null. We may
10752 * well be writing binary data, so we need to accommodate not only
10753 * data with nulls sprinkled in the middle but also data with no null
10756 /*{{{ int my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)*/
10758 my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)
10760 register char *cp, *end, *cpd, *data;
10761 register unsigned int fd = fileno(dest);
10762 register unsigned int fdoff = fd / sizeof(unsigned int);
10764 int bufsize = itmsz * nitm + 1;
10766 if (fdoff < sockflagsize &&
10767 (sockflags[fdoff] | 1 << (fd % sizeof(unsigned int)))) {
10768 if (write(fd, src, itmsz * nitm) == EOF) return EOF;
10772 _ckvmssts_noperl(lib$get_vm(&bufsize, &data));
10773 memcpy( data, src, itmsz*nitm );
10774 data[itmsz*nitm] = '\0';
10776 end = data + itmsz * nitm;
10777 retval = (int) nitm; /* on success return # items written */
10780 while (cpd <= end) {
10781 for (cp = cpd; cp <= end; cp++) if (!*cp) break;
10782 if (fputs(cpd,dest) == EOF) { retval = EOF; break; }
10784 if (fputc('\0',dest) == EOF) { retval = EOF; break; }
10788 if (data) _ckvmssts_noperl(lib$free_vm(&bufsize, &data));
10791 } /* end of my_fwrite() */
10794 /*{{{ int my_flush(FILE *fp)*/
10796 Perl_my_flush(pTHX_ FILE *fp)
10799 if ((res = fflush(fp)) == 0 && fp) {
10800 #ifdef VMS_DO_SOCKETS
10802 if (fstat(fileno(fp), &s.crtl_stat) == 0 && !S_ISSOCK(s.st_mode))
10804 res = fsync(fileno(fp));
10807 * If the flush succeeded but set end-of-file, we need to clear
10808 * the error because our caller may check ferror(). BTW, this
10809 * probably means we just flushed an empty file.
10811 if (res == 0 && vaxc$errno == RMS$_EOF) clearerr(fp);
10818 * Here are replacements for the following Unix routines in the VMS environment:
10819 * getpwuid Get information for a particular UIC or UID
10820 * getpwnam Get information for a named user
10821 * getpwent Get information for each user in the rights database
10822 * setpwent Reset search to the start of the rights database
10823 * endpwent Finish searching for users in the rights database
10825 * getpwuid, getpwnam, and getpwent return a pointer to the passwd structure
10826 * (defined in pwd.h), which contains the following fields:-
10828 * char *pw_name; Username (in lower case)
10829 * char *pw_passwd; Hashed password
10830 * unsigned int pw_uid; UIC
10831 * unsigned int pw_gid; UIC group number
10832 * char *pw_unixdir; Default device/directory (VMS-style)
10833 * char *pw_gecos; Owner name
10834 * char *pw_dir; Default device/directory (Unix-style)
10835 * char *pw_shell; Default CLI name (eg. DCL)
10837 * If the specified user does not exist, getpwuid and getpwnam return NULL.
10839 * pw_uid is the full UIC (eg. what's returned by stat() in st_uid).
10840 * not the UIC member number (eg. what's returned by getuid()),
10841 * getpwuid() can accept either as input (if uid is specified, the caller's
10842 * UIC group is used), though it won't recognise gid=0.
10844 * Note that in VMS it is necessary to have GRPPRV or SYSPRV to return
10845 * information about other users in your group or in other groups, respectively.
10846 * If the required privilege is not available, then these routines fill only
10847 * the pw_name, pw_uid, and pw_gid fields (the others point to an empty
10850 * By Tim Adye (T.J.Adye@rl.ac.uk), 10th February 1995.
10853 /* sizes of various UAF record fields */
10854 #define UAI$S_USERNAME 12
10855 #define UAI$S_IDENT 31
10856 #define UAI$S_OWNER 31
10857 #define UAI$S_DEFDEV 31
10858 #define UAI$S_DEFDIR 63
10859 #define UAI$S_DEFCLI 31
10860 #define UAI$S_PWD 8
10862 #define valid_uic(uic) ((uic).uic$v_format == UIC$K_UIC_FORMAT && \
10863 (uic).uic$v_member != UIC$K_WILD_MEMBER && \
10864 (uic).uic$v_group != UIC$K_WILD_GROUP)
10866 static char __empty[]= "";
10867 static struct passwd __passwd_empty=
10868 {(char *) __empty, (char *) __empty, 0, 0,
10869 (char *) __empty, (char *) __empty, (char *) __empty, (char *) __empty};
10870 static int contxt= 0;
10871 static struct passwd __pwdcache;
10872 static char __pw_namecache[UAI$S_IDENT+1];
10875 * This routine does most of the work extracting the user information.
10877 static int fillpasswd (pTHX_ const char *name, struct passwd *pwd)
10880 unsigned char length;
10881 char pw_gecos[UAI$S_OWNER+1];
10883 static union uicdef uic;
10885 unsigned char length;
10886 char pw_dir[UAI$S_DEFDEV+UAI$S_DEFDIR+1];
10889 unsigned char length;
10890 char unixdir[UAI$_DEFDEV+UAI$S_DEFDIR+1];
10893 unsigned char length;
10894 char pw_shell[UAI$S_DEFCLI+1];
10896 static char pw_passwd[UAI$S_PWD+1];
10898 static unsigned short lowner, luic, ldefdev, ldefdir, ldefcli, lpwd;
10899 struct dsc$descriptor_s name_desc;
10900 unsigned long int sts;
10902 static struct itmlst_3 itmlst[]= {
10903 {UAI$S_OWNER+1, UAI$_OWNER, &owner, &lowner},
10904 {sizeof(uic), UAI$_UIC, &uic, &luic},
10905 {UAI$S_DEFDEV+1, UAI$_DEFDEV, &defdev, &ldefdev},
10906 {UAI$S_DEFDIR+1, UAI$_DEFDIR, &defdir, &ldefdir},
10907 {UAI$S_DEFCLI+1, UAI$_DEFCLI, &defcli, &ldefcli},
10908 {UAI$S_PWD, UAI$_PWD, pw_passwd, &lpwd},
10909 {0, 0, NULL, NULL}};
10911 name_desc.dsc$w_length= strlen(name);
10912 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
10913 name_desc.dsc$b_class= DSC$K_CLASS_S;
10914 name_desc.dsc$a_pointer= (char *) name; /* read only pointer */
10916 /* Note that sys$getuai returns many fields as counted strings. */
10917 sts= sys$getuai(0, 0, &name_desc, &itmlst, 0, 0, 0);
10918 if (sts == SS$_NOSYSPRV || sts == SS$_NOGRPPRV || sts == RMS$_RNF) {
10919 set_vaxc_errno(sts); set_errno(sts == RMS$_RNF ? EINVAL : EACCES);
10921 else { _ckvmssts(sts); }
10922 if (!(sts & 1)) return 0; /* out here in case _ckvmssts() doesn't abort */
10924 if ((int) owner.length < lowner) lowner= (int) owner.length;
10925 if ((int) defdev.length < ldefdev) ldefdev= (int) defdev.length;
10926 if ((int) defdir.length < ldefdir) ldefdir= (int) defdir.length;
10927 if ((int) defcli.length < ldefcli) ldefcli= (int) defcli.length;
10928 memcpy(&defdev.pw_dir[ldefdev], &defdir.unixdir[0], ldefdir);
10929 owner.pw_gecos[lowner]= '\0';
10930 defdev.pw_dir[ldefdev+ldefdir]= '\0';
10931 defcli.pw_shell[ldefcli]= '\0';
10932 if (valid_uic(uic)) {
10933 pwd->pw_uid= uic.uic$l_uic;
10934 pwd->pw_gid= uic.uic$v_group;
10937 Perl_warn(aTHX_ "getpwnam returned invalid UIC %#o for user \"%s\"");
10938 pwd->pw_passwd= pw_passwd;
10939 pwd->pw_gecos= owner.pw_gecos;
10940 pwd->pw_dir= defdev.pw_dir;
10941 pwd->pw_unixdir= do_tounixpath(defdev.pw_dir, defdir.unixdir,1,NULL);
10942 pwd->pw_shell= defcli.pw_shell;
10943 if (pwd->pw_unixdir && pwd->pw_unixdir[0]) {
10945 ldir= strlen(pwd->pw_unixdir) - 1;
10946 if (pwd->pw_unixdir[ldir]=='/') pwd->pw_unixdir[ldir]= '\0';
10949 strcpy(pwd->pw_unixdir, pwd->pw_dir);
10950 if (!decc_efs_case_preserve)
10951 __mystrtolower(pwd->pw_unixdir);
10956 * Get information for a named user.
10958 /*{{{struct passwd *getpwnam(char *name)*/
10959 struct passwd *Perl_my_getpwnam(pTHX_ const char *name)
10961 struct dsc$descriptor_s name_desc;
10963 unsigned long int status, sts;
10965 __pwdcache = __passwd_empty;
10966 if (!fillpasswd(aTHX_ name, &__pwdcache)) {
10967 /* We still may be able to determine pw_uid and pw_gid */
10968 name_desc.dsc$w_length= strlen(name);
10969 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
10970 name_desc.dsc$b_class= DSC$K_CLASS_S;
10971 name_desc.dsc$a_pointer= (char *) name;
10972 if ((sts = sys$asctoid(&name_desc, &uic, 0)) == SS$_NORMAL) {
10973 __pwdcache.pw_uid= uic.uic$l_uic;
10974 __pwdcache.pw_gid= uic.uic$v_group;
10977 if (sts == SS$_NOSUCHID || sts == SS$_IVIDENT || sts == RMS$_PRV) {
10978 set_vaxc_errno(sts);
10979 set_errno(sts == RMS$_PRV ? EACCES : EINVAL);
10982 else { _ckvmssts(sts); }
10985 strncpy(__pw_namecache, name, sizeof(__pw_namecache));
10986 __pw_namecache[sizeof __pw_namecache - 1] = '\0';
10987 __pwdcache.pw_name= __pw_namecache;
10988 return &__pwdcache;
10989 } /* end of my_getpwnam() */
10993 * Get information for a particular UIC or UID.
10994 * Called by my_getpwent with uid=-1 to list all users.
10996 /*{{{struct passwd *my_getpwuid(Uid_t uid)*/
10997 struct passwd *Perl_my_getpwuid(pTHX_ Uid_t uid)
10999 const $DESCRIPTOR(name_desc,__pw_namecache);
11000 unsigned short lname;
11002 unsigned long int status;
11004 if (uid == (unsigned int) -1) {
11006 status = sys$idtoasc(-1, &lname, &name_desc, &uic, 0, &contxt);
11007 if (status == SS$_NOSUCHID || status == RMS$_PRV) {
11008 set_vaxc_errno(status);
11009 set_errno(status == RMS$_PRV ? EACCES : EINVAL);
11013 else { _ckvmssts(status); }
11014 } while (!valid_uic (uic));
11017 uic.uic$l_uic= uid;
11018 if (!uic.uic$v_group)
11019 uic.uic$v_group= PerlProc_getgid();
11020 if (valid_uic(uic))
11021 status = sys$idtoasc(uic.uic$l_uic, &lname, &name_desc, 0, 0, 0);
11022 else status = SS$_IVIDENT;
11023 if (status == SS$_IVIDENT || status == SS$_NOSUCHID ||
11024 status == RMS$_PRV) {
11025 set_vaxc_errno(status); set_errno(status == RMS$_PRV ? EACCES : EINVAL);
11028 else { _ckvmssts(status); }
11030 __pw_namecache[lname]= '\0';
11031 __mystrtolower(__pw_namecache);
11033 __pwdcache = __passwd_empty;
11034 __pwdcache.pw_name = __pw_namecache;
11036 /* Fill in the uid and gid in case fillpasswd can't (eg. no privilege).
11037 The identifier's value is usually the UIC, but it doesn't have to be,
11038 so if we can, we let fillpasswd update this. */
11039 __pwdcache.pw_uid = uic.uic$l_uic;
11040 __pwdcache.pw_gid = uic.uic$v_group;
11042 fillpasswd(aTHX_ __pw_namecache, &__pwdcache);
11043 return &__pwdcache;
11045 } /* end of my_getpwuid() */
11049 * Get information for next user.
11051 /*{{{struct passwd *my_getpwent()*/
11052 struct passwd *Perl_my_getpwent(pTHX)
11054 return (my_getpwuid((unsigned int) -1));
11059 * Finish searching rights database for users.
11061 /*{{{void my_endpwent()*/
11062 void Perl_my_endpwent(pTHX)
11065 _ckvmssts(sys$finish_rdb(&contxt));
11071 #ifdef HOMEGROWN_POSIX_SIGNALS
11072 /* Signal handling routines, pulled into the core from POSIX.xs.
11074 * We need these for threads, so they've been rolled into the core,
11075 * rather than left in POSIX.xs.
11077 * (DRS, Oct 23, 1997)
11080 /* sigset_t is atomic under VMS, so these routines are easy */
11081 /*{{{int my_sigemptyset(sigset_t *) */
11082 int my_sigemptyset(sigset_t *set) {
11083 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
11084 *set = 0; return 0;
11089 /*{{{int my_sigfillset(sigset_t *)*/
11090 int my_sigfillset(sigset_t *set) {
11092 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
11093 for (i = 0; i < NSIG; i++) *set |= (1 << i);
11099 /*{{{int my_sigaddset(sigset_t *set, int sig)*/
11100 int my_sigaddset(sigset_t *set, int sig) {
11101 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
11102 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
11103 *set |= (1 << (sig - 1));
11109 /*{{{int my_sigdelset(sigset_t *set, int sig)*/
11110 int my_sigdelset(sigset_t *set, int sig) {
11111 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
11112 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
11113 *set &= ~(1 << (sig - 1));
11119 /*{{{int my_sigismember(sigset_t *set, int sig)*/
11120 int my_sigismember(sigset_t *set, int sig) {
11121 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
11122 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
11123 return *set & (1 << (sig - 1));
11128 /*{{{int my_sigprocmask(int how, sigset_t *set, sigset_t *oset)*/
11129 int my_sigprocmask(int how, sigset_t *set, sigset_t *oset) {
11132 /* If set and oset are both null, then things are badly wrong. Bail out. */
11133 if ((oset == NULL) && (set == NULL)) {
11134 set_errno(EFAULT); set_vaxc_errno(SS$_ACCVIO);
11138 /* If set's null, then we're just handling a fetch. */
11140 tempmask = sigblock(0);
11145 tempmask = sigsetmask(*set);
11148 tempmask = sigblock(*set);
11151 tempmask = sigblock(0);
11152 sigsetmask(*oset & ~tempmask);
11155 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11160 /* Did they pass us an oset? If so, stick our holding mask into it */
11167 #endif /* HOMEGROWN_POSIX_SIGNALS */
11170 /* Used for UTC calculation in my_gmtime(), my_localtime(), my_time(),
11171 * my_utime(), and flex_stat(), all of which operate on UTC unless
11172 * VMSISH_TIMES is true.
11174 /* method used to handle UTC conversions:
11175 * 1 == CRTL gmtime(); 2 == SYS$TIMEZONE_DIFFERENTIAL; 3 == no correction
11177 static int gmtime_emulation_type;
11178 /* number of secs to add to UTC POSIX-style time to get local time */
11179 static long int utc_offset_secs;
11181 /* We #defined 'gmtime', 'localtime', and 'time' as 'my_gmtime' etc.
11182 * in vmsish.h. #undef them here so we can call the CRTL routines
11191 * DEC C previous to 6.0 corrupts the behavior of the /prefix
11192 * qualifier with the extern prefix pragma. This provisional
11193 * hack circumvents this prefix pragma problem in previous
11196 #if defined(__VMS_VER) && __VMS_VER >= 70000000
11197 # if defined(VMS_WE_ARE_CASE_SENSITIVE) && (__DECC_VER < 60000000)
11198 # pragma __extern_prefix save
11199 # pragma __extern_prefix "" /* set to empty to prevent prefixing */
11200 # define gmtime decc$__utctz_gmtime
11201 # define localtime decc$__utctz_localtime
11202 # define time decc$__utc_time
11203 # pragma __extern_prefix restore
11205 struct tm *gmtime(), *localtime();
11211 static time_t toutc_dst(time_t loc) {
11214 if ((rsltmp = localtime(&loc)) == NULL) return -1;
11215 loc -= utc_offset_secs;
11216 if (rsltmp->tm_isdst) loc -= 3600;
11219 #define _toutc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
11220 ((gmtime_emulation_type || my_time(NULL)), \
11221 (gmtime_emulation_type == 1 ? toutc_dst(secs) : \
11222 ((secs) - utc_offset_secs))))
11224 static time_t toloc_dst(time_t utc) {
11227 utc += utc_offset_secs;
11228 if ((rsltmp = localtime(&utc)) == NULL) return -1;
11229 if (rsltmp->tm_isdst) utc += 3600;
11232 #define _toloc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
11233 ((gmtime_emulation_type || my_time(NULL)), \
11234 (gmtime_emulation_type == 1 ? toloc_dst(secs) : \
11235 ((secs) + utc_offset_secs))))
11237 #ifndef RTL_USES_UTC
11240 ucx$tz = "EST5EDT4,M4.1.0,M10.5.0" typical
11241 DST starts on 1st sun of april at 02:00 std time
11242 ends on last sun of october at 02:00 dst time
11243 see the UCX management command reference, SET CONFIG TIMEZONE
11244 for formatting info.
11246 No, it's not as general as it should be, but then again, NOTHING
11247 will handle UK times in a sensible way.
11252 parse the DST start/end info:
11253 (Jddd|ddd|Mmon.nth.dow)[/hh:mm:ss]
11257 tz_parse_startend(char *s, struct tm *w, int *past)
11259 int dinm[] = {31,28,31,30,31,30,31,31,30,31,30,31};
11260 int ly, dozjd, d, m, n, hour, min, sec, j, k;
11265 if (!past) return 0;
11268 if (w->tm_year % 4 == 0) ly = 1;
11269 if (w->tm_year % 100 == 0) ly = 0;
11270 if (w->tm_year+1900 % 400 == 0) ly = 1;
11273 dozjd = isdigit(*s);
11274 if (*s == 'J' || *s == 'j' || dozjd) {
11275 if (!dozjd && !isdigit(*++s)) return 0;
11278 d = d*10 + *s++ - '0';
11280 d = d*10 + *s++ - '0';
11283 if (d == 0) return 0;
11284 if (d > 366) return 0;
11286 if (!dozjd && d > 58 && ly) d++; /* after 28 feb */
11289 } else if (*s == 'M' || *s == 'm') {
11290 if (!isdigit(*++s)) return 0;
11292 if (isdigit(*s)) m = 10*m + *s++ - '0';
11293 if (*s != '.') return 0;
11294 if (!isdigit(*++s)) return 0;
11296 if (n < 1 || n > 5) return 0;
11297 if (*s != '.') return 0;
11298 if (!isdigit(*++s)) return 0;
11300 if (d > 6) return 0;
11304 if (!isdigit(*++s)) return 0;
11306 if (isdigit(*s)) hour = 10*hour + *s++ - '0';
11308 if (!isdigit(*++s)) return 0;
11310 if (isdigit(*s)) min = 10*min + *s++ - '0';
11312 if (!isdigit(*++s)) return 0;
11314 if (isdigit(*s)) sec = 10*sec + *s++ - '0';
11324 if (w->tm_yday < d) goto before;
11325 if (w->tm_yday > d) goto after;
11327 if (w->tm_mon+1 < m) goto before;
11328 if (w->tm_mon+1 > m) goto after;
11330 j = (42 + w->tm_wday - w->tm_mday)%7; /*dow of mday 0 */
11331 k = d - j; /* mday of first d */
11332 if (k <= 0) k += 7;
11333 k += 7 * ((n>4?4:n)-1); /* mday of n'th d */
11334 if (n == 5 && k+7 <= dinm[w->tm_mon]) k += 7;
11335 if (w->tm_mday < k) goto before;
11336 if (w->tm_mday > k) goto after;
11339 if (w->tm_hour < hour) goto before;
11340 if (w->tm_hour > hour) goto after;
11341 if (w->tm_min < min) goto before;
11342 if (w->tm_min > min) goto after;
11343 if (w->tm_sec < sec) goto before;
11357 /* parse the offset: (+|-)hh[:mm[:ss]] */
11360 tz_parse_offset(char *s, int *offset)
11362 int hour = 0, min = 0, sec = 0;
11365 if (!offset) return 0;
11367 if (*s == '-') {neg++; s++;}
11368 if (*s == '+') s++;
11369 if (!isdigit(*s)) return 0;
11371 if (isdigit(*s)) hour = hour*10+(*s++ - '0');
11372 if (hour > 24) return 0;
11374 if (!isdigit(*++s)) return 0;
11376 if (isdigit(*s)) min = min*10 + (*s++ - '0');
11377 if (min > 59) return 0;
11379 if (!isdigit(*++s)) return 0;
11381 if (isdigit(*s)) sec = sec*10 + (*s++ - '0');
11382 if (sec > 59) return 0;
11386 *offset = (hour*60+min)*60 + sec;
11387 if (neg) *offset = -*offset;
11392 input time is w, whatever type of time the CRTL localtime() uses.
11393 sets dst, the zone, and the gmtoff (seconds)
11395 caches the value of TZ and UCX$TZ env variables; note that
11396 my_setenv looks for these and sets a flag if they're changed
11399 We have to watch out for the "australian" case (dst starts in
11400 october, ends in april)...flagged by "reverse" and checked by
11401 scanning through the months of the previous year.
11406 tz_parse(pTHX_ time_t *w, int *dst, char *zone, int *gmtoff)
11411 char *dstzone, *tz, *s_start, *s_end;
11412 int std_off, dst_off, isdst;
11413 int y, dststart, dstend;
11414 static char envtz[1025]; /* longer than any logical, symbol, ... */
11415 static char ucxtz[1025];
11416 static char reversed = 0;
11422 reversed = -1; /* flag need to check */
11423 envtz[0] = ucxtz[0] = '\0';
11424 tz = my_getenv("TZ",0);
11425 if (tz) strcpy(envtz, tz);
11426 tz = my_getenv("UCX$TZ",0);
11427 if (tz) strcpy(ucxtz, tz);
11428 if (!envtz[0] && !ucxtz[0]) return 0; /* we give up */
11431 if (!*tz) tz = ucxtz;
11434 while (isalpha(*s)) s++;
11435 s = tz_parse_offset(s, &std_off);
11437 if (!*s) { /* no DST, hurray we're done! */
11443 while (isalpha(*s)) s++;
11444 s2 = tz_parse_offset(s, &dst_off);
11448 dst_off = std_off - 3600;
11451 if (!*s) { /* default dst start/end?? */
11452 if (tz != ucxtz) { /* if TZ tells zone only, UCX$TZ tells rule */
11453 s = strchr(ucxtz,',');
11455 if (!s || !*s) s = ",M4.1.0,M10.5.0"; /* we know we do dst, default rule */
11457 if (*s != ',') return 0;
11460 when = _toutc(when); /* convert to utc */
11461 when = when - std_off; /* convert to pseudolocal time*/
11463 w2 = localtime(&when);
11466 s = tz_parse_startend(s_start,w2,&dststart);
11468 if (*s != ',') return 0;
11471 when = _toutc(when); /* convert to utc */
11472 when = when - dst_off; /* convert to pseudolocal time*/
11473 w2 = localtime(&when);
11474 if (w2->tm_year != y) { /* spans a year, just check one time */
11475 when += dst_off - std_off;
11476 w2 = localtime(&when);
11479 s = tz_parse_startend(s_end,w2,&dstend);
11482 if (reversed == -1) { /* need to check if start later than end */
11486 if (when < 2*365*86400) {
11487 when += 2*365*86400;
11491 w2 =localtime(&when);
11492 when = when + (15 - w2->tm_yday) * 86400; /* jan 15 */
11494 for (j = 0; j < 12; j++) {
11495 w2 =localtime(&when);
11496 tz_parse_startend(s_start,w2,&ds);
11497 tz_parse_startend(s_end,w2,&de);
11498 if (ds != de) break;
11502 if (de && !ds) reversed = 1;
11505 isdst = dststart && !dstend;
11506 if (reversed) isdst = dststart || !dstend;
11509 if (dst) *dst = isdst;
11510 if (gmtoff) *gmtoff = isdst ? dst_off : std_off;
11511 if (isdst) tz = dstzone;
11513 while(isalpha(*tz)) *zone++ = *tz++;
11519 #endif /* !RTL_USES_UTC */
11521 /* my_time(), my_localtime(), my_gmtime()
11522 * By default traffic in UTC time values, using CRTL gmtime() or
11523 * SYS$TIMEZONE_DIFFERENTIAL to determine offset from local time zone.
11524 * Note: We need to use these functions even when the CRTL has working
11525 * UTC support, since they also handle C<use vmsish qw(times);>
11527 * Contributed by Chuck Lane <lane@duphy4.physics.drexel.edu>
11528 * Modified by Charles Bailey <bailey@newman.upenn.edu>
11531 /*{{{time_t my_time(time_t *timep)*/
11532 time_t Perl_my_time(pTHX_ time_t *timep)
11537 if (gmtime_emulation_type == 0) {
11539 time_t base = 15 * 86400; /* 15jan71; to avoid month/year ends between */
11540 /* results of calls to gmtime() and localtime() */
11541 /* for same &base */
11543 gmtime_emulation_type++;
11544 if ((tm_p = gmtime(&base)) == NULL) { /* CRTL gmtime() is a fake */
11545 char off[LNM$C_NAMLENGTH+1];;
11547 gmtime_emulation_type++;
11548 if (!vmstrnenv("SYS$TIMEZONE_DIFFERENTIAL",off,0,fildev,0)) {
11549 gmtime_emulation_type++;
11550 utc_offset_secs = 0;
11551 Perl_warn(aTHX_ "no UTC offset information; assuming local time is UTC");
11553 else { utc_offset_secs = atol(off); }
11555 else { /* We've got a working gmtime() */
11556 struct tm gmt, local;
11559 tm_p = localtime(&base);
11561 utc_offset_secs = (local.tm_mday - gmt.tm_mday) * 86400;
11562 utc_offset_secs += (local.tm_hour - gmt.tm_hour) * 3600;
11563 utc_offset_secs += (local.tm_min - gmt.tm_min) * 60;
11564 utc_offset_secs += (local.tm_sec - gmt.tm_sec);
11569 # ifdef VMSISH_TIME
11570 # ifdef RTL_USES_UTC
11571 if (VMSISH_TIME) when = _toloc(when);
11573 if (!VMSISH_TIME) when = _toutc(when);
11576 if (timep != NULL) *timep = when;
11579 } /* end of my_time() */
11583 /*{{{struct tm *my_gmtime(const time_t *timep)*/
11585 Perl_my_gmtime(pTHX_ const time_t *timep)
11591 if (timep == NULL) {
11592 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11595 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
11598 # ifdef VMSISH_TIME
11599 if (VMSISH_TIME) when = _toutc(when); /* Input was local time */
11601 # ifdef RTL_USES_UTC /* this implies that the CRTL has a working gmtime() */
11602 return gmtime(&when);
11604 /* CRTL localtime() wants local time as input, so does no tz correction */
11605 rsltmp = localtime(&when);
11606 if (rsltmp) rsltmp->tm_isdst = 0; /* We already took DST into account */
11609 } /* end of my_gmtime() */
11613 /*{{{struct tm *my_localtime(const time_t *timep)*/
11615 Perl_my_localtime(pTHX_ const time_t *timep)
11617 time_t when, whenutc;
11621 if (timep == NULL) {
11622 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11625 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
11626 if (gmtime_emulation_type == 0) my_time(NULL); /* Init UTC */
11629 # ifdef RTL_USES_UTC
11630 # ifdef VMSISH_TIME
11631 if (VMSISH_TIME) when = _toutc(when);
11633 /* CRTL localtime() wants UTC as input, does tz correction itself */
11634 return localtime(&when);
11636 # else /* !RTL_USES_UTC */
11638 # ifdef VMSISH_TIME
11639 if (!VMSISH_TIME) when = _toloc(whenutc); /* input was UTC */
11640 if (VMSISH_TIME) whenutc = _toutc(when); /* input was truelocal */
11643 #ifndef RTL_USES_UTC
11644 if (tz_parse(aTHX_ &when, &dst, 0, &offset)) { /* truelocal determines DST*/
11645 when = whenutc - offset; /* pseudolocal time*/
11648 /* CRTL localtime() wants local time as input, so does no tz correction */
11649 rsltmp = localtime(&when);
11650 if (rsltmp && gmtime_emulation_type != 1) rsltmp->tm_isdst = dst;
11654 } /* end of my_localtime() */
11657 /* Reset definitions for later calls */
11658 #define gmtime(t) my_gmtime(t)
11659 #define localtime(t) my_localtime(t)
11660 #define time(t) my_time(t)
11663 /* my_utime - update modification/access time of a file
11665 * VMS 7.3 and later implementation
11666 * Only the UTC translation is home-grown. The rest is handled by the
11667 * CRTL utime(), which will take into account the relevant feature
11668 * logicals and ODS-5 volume characteristics for true access times.
11670 * pre VMS 7.3 implementation:
11671 * The calling sequence is identical to POSIX utime(), but under
11672 * VMS with ODS-2, only the modification time is changed; ODS-2 does
11673 * not maintain access times. Restrictions differ from the POSIX
11674 * definition in that the time can be changed as long as the
11675 * caller has permission to execute the necessary IO$_MODIFY $QIO;
11676 * no separate checks are made to insure that the caller is the
11677 * owner of the file or has special privs enabled.
11678 * Code here is based on Joe Meadows' FILE utility.
11682 /* Adjustment from Unix epoch (01-JAN-1970 00:00:00.00)
11683 * to VMS epoch (01-JAN-1858 00:00:00.00)
11684 * in 100 ns intervals.
11686 static const long int utime_baseadjust[2] = { 0x4beb4000, 0x7c9567 };
11688 /*{{{int my_utime(const char *path, const struct utimbuf *utimes)*/
11689 int Perl_my_utime(pTHX_ const char *file, const struct utimbuf *utimes)
11691 #if __CRTL_VER >= 70300000
11692 struct utimbuf utc_utimes, *utc_utimesp;
11694 if (utimes != NULL) {
11695 utc_utimes.actime = utimes->actime;
11696 utc_utimes.modtime = utimes->modtime;
11697 # ifdef VMSISH_TIME
11698 /* If input was local; convert to UTC for sys svc */
11700 utc_utimes.actime = _toutc(utimes->actime);
11701 utc_utimes.modtime = _toutc(utimes->modtime);
11704 utc_utimesp = &utc_utimes;
11707 utc_utimesp = NULL;
11710 return utime(file, utc_utimesp);
11712 #else /* __CRTL_VER < 70300000 */
11716 long int bintime[2], len = 2, lowbit, unixtime,
11717 secscale = 10000000; /* seconds --> 100 ns intervals */
11718 unsigned long int chan, iosb[2], retsts;
11719 char vmsspec[NAM$C_MAXRSS+1], rsa[NAM$C_MAXRSS], esa[NAM$C_MAXRSS];
11720 struct FAB myfab = cc$rms_fab;
11721 struct NAM mynam = cc$rms_nam;
11722 #if defined (__DECC) && defined (__VAX)
11723 /* VAX DEC C atrdef.h has unsigned type for pointer member atr$l_addr,
11724 * at least through VMS V6.1, which causes a type-conversion warning.
11726 # pragma message save
11727 # pragma message disable cvtdiftypes
11729 struct atrdef myatr[2] = {{sizeof bintime, ATR$C_REVDATE, bintime}, {0,0,0}};
11730 struct fibdef myfib;
11731 #if defined (__DECC) && defined (__VAX)
11732 /* This should be right after the declaration of myatr, but due
11733 * to a bug in VAX DEC C, this takes effect a statement early.
11735 # pragma message restore
11737 /* cast ok for read only parameter */
11738 struct dsc$descriptor fibdsc = {sizeof(myfib), DSC$K_DTYPE_Z, DSC$K_CLASS_S,(char *) &myfib},
11739 devdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0},
11740 fnmdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0};
11742 if (file == NULL || *file == '\0') {
11743 SETERRNO(ENOENT, LIB$_INVARG);
11747 /* Convert to VMS format ensuring that it will fit in 255 characters */
11748 if (do_rmsexpand(file, vmsspec, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL) == NULL) {
11749 SETERRNO(ENOENT, LIB$_INVARG);
11752 if (utimes != NULL) {
11753 /* Convert Unix time (seconds since 01-JAN-1970 00:00:00.00)
11754 * to VMS quadword time (100 nsec intervals since 01-JAN-1858 00:00:00.00).
11755 * Since time_t is unsigned long int, and lib$emul takes a signed long int
11756 * as input, we force the sign bit to be clear by shifting unixtime right
11757 * one bit, then multiplying by an extra factor of 2 in lib$emul().
11759 lowbit = (utimes->modtime & 1) ? secscale : 0;
11760 unixtime = (long int) utimes->modtime;
11761 # ifdef VMSISH_TIME
11762 /* If input was UTC; convert to local for sys svc */
11763 if (!VMSISH_TIME) unixtime = _toloc(unixtime);
11765 unixtime >>= 1; secscale <<= 1;
11766 retsts = lib$emul(&secscale, &unixtime, &lowbit, bintime);
11767 if (!(retsts & 1)) {
11768 SETERRNO(EVMSERR, retsts);
11771 retsts = lib$addx(bintime,utime_baseadjust,bintime,&len);
11772 if (!(retsts & 1)) {
11773 SETERRNO(EVMSERR, retsts);
11778 /* Just get the current time in VMS format directly */
11779 retsts = sys$gettim(bintime);
11780 if (!(retsts & 1)) {
11781 SETERRNO(EVMSERR, retsts);
11786 myfab.fab$l_fna = vmsspec;
11787 myfab.fab$b_fns = (unsigned char) strlen(vmsspec);
11788 myfab.fab$l_nam = &mynam;
11789 mynam.nam$l_esa = esa;
11790 mynam.nam$b_ess = (unsigned char) sizeof esa;
11791 mynam.nam$l_rsa = rsa;
11792 mynam.nam$b_rss = (unsigned char) sizeof rsa;
11793 if (decc_efs_case_preserve)
11794 mynam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
11796 /* Look for the file to be affected, letting RMS parse the file
11797 * specification for us as well. I have set errno using only
11798 * values documented in the utime() man page for VMS POSIX.
11800 retsts = sys$parse(&myfab,0,0);
11801 if (!(retsts & 1)) {
11802 set_vaxc_errno(retsts);
11803 if (retsts == RMS$_PRV) set_errno(EACCES);
11804 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
11805 else set_errno(EVMSERR);
11808 retsts = sys$search(&myfab,0,0);
11809 if (!(retsts & 1)) {
11810 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
11811 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
11812 set_vaxc_errno(retsts);
11813 if (retsts == RMS$_PRV) set_errno(EACCES);
11814 else if (retsts == RMS$_FNF) set_errno(ENOENT);
11815 else set_errno(EVMSERR);
11819 devdsc.dsc$w_length = mynam.nam$b_dev;
11820 /* cast ok for read only parameter */
11821 devdsc.dsc$a_pointer = (char *) mynam.nam$l_dev;
11823 retsts = sys$assign(&devdsc,&chan,0,0);
11824 if (!(retsts & 1)) {
11825 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
11826 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
11827 set_vaxc_errno(retsts);
11828 if (retsts == SS$_IVDEVNAM) set_errno(ENOTDIR);
11829 else if (retsts == SS$_NOPRIV) set_errno(EACCES);
11830 else if (retsts == SS$_NOSUCHDEV) set_errno(ENOTDIR);
11831 else set_errno(EVMSERR);
11835 fnmdsc.dsc$a_pointer = mynam.nam$l_name;
11836 fnmdsc.dsc$w_length = mynam.nam$b_name + mynam.nam$b_type + mynam.nam$b_ver;
11838 memset((void *) &myfib, 0, sizeof myfib);
11839 #if defined(__DECC) || defined(__DECCXX)
11840 for (i=0;i<3;i++) myfib.fib$w_fid[i] = mynam.nam$w_fid[i];
11841 for (i=0;i<3;i++) myfib.fib$w_did[i] = mynam.nam$w_did[i];
11842 /* This prevents the revision time of the file being reset to the current
11843 * time as a result of our IO$_MODIFY $QIO. */
11844 myfib.fib$l_acctl = FIB$M_NORECORD;
11846 for (i=0;i<3;i++) myfib.fib$r_fid_overlay.fib$w_fid[i] = mynam.nam$w_fid[i];
11847 for (i=0;i<3;i++) myfib.fib$r_did_overlay.fib$w_did[i] = mynam.nam$w_did[i];
11848 myfib.fib$r_acctl_overlay.fib$l_acctl = FIB$M_NORECORD;
11850 retsts = sys$qiow(0,chan,IO$_MODIFY,iosb,0,0,&fibdsc,&fnmdsc,0,0,myatr,0);
11851 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
11852 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
11853 _ckvmssts(sys$dassgn(chan));
11854 if (retsts & 1) retsts = iosb[0];
11855 if (!(retsts & 1)) {
11856 set_vaxc_errno(retsts);
11857 if (retsts == SS$_NOPRIV) set_errno(EACCES);
11858 else set_errno(EVMSERR);
11864 #endif /* #if __CRTL_VER >= 70300000 */
11866 } /* end of my_utime() */
11870 * flex_stat, flex_lstat, flex_fstat
11871 * basic stat, but gets it right when asked to stat
11872 * a Unix-style path ending in a directory name (e.g. dir1/dir2/dir3)
11875 #ifndef _USE_STD_STAT
11876 /* encode_dev packs a VMS device name string into an integer to allow
11877 * simple comparisons. This can be used, for example, to check whether two
11878 * files are located on the same device, by comparing their encoded device
11879 * names. Even a string comparison would not do, because stat() reuses the
11880 * device name buffer for each call; so without encode_dev, it would be
11881 * necessary to save the buffer and use strcmp (this would mean a number of
11882 * changes to the standard Perl code, to say nothing of what a Perl script
11883 * would have to do.
11885 * The device lock id, if it exists, should be unique (unless perhaps compared
11886 * with lock ids transferred from other nodes). We have a lock id if the disk is
11887 * mounted cluster-wide, which is when we tend to get long (host-qualified)
11888 * device names. Thus we use the lock id in preference, and only if that isn't
11889 * available, do we try to pack the device name into an integer (flagged by
11890 * the sign bit (LOCKID_MASK) being set).
11892 * Note that encode_dev cannot guarantee an 1-to-1 correspondence twixt device
11893 * name and its encoded form, but it seems very unlikely that we will find
11894 * two files on different disks that share the same encoded device names,
11895 * and even more remote that they will share the same file id (if the test
11896 * is to check for the same file).
11898 * A better method might be to use sys$device_scan on the first call, and to
11899 * search for the device, returning an index into the cached array.
11900 * The number returned would be more intelligible.
11901 * This is probably not worth it, and anyway would take quite a bit longer
11902 * on the first call.
11904 #define LOCKID_MASK 0x80000000 /* Use 0 to force device name use only */
11905 static mydev_t encode_dev (pTHX_ const char *dev)
11908 unsigned long int f;
11913 if (!dev || !dev[0]) return 0;
11917 struct dsc$descriptor_s dev_desc;
11918 unsigned long int status, lockid = 0, item = DVI$_LOCKID;
11920 /* For cluster-mounted disks, the disk lock identifier is unique, so we
11921 can try that first. */
11922 dev_desc.dsc$w_length = strlen (dev);
11923 dev_desc.dsc$b_dtype = DSC$K_DTYPE_T;
11924 dev_desc.dsc$b_class = DSC$K_CLASS_S;
11925 dev_desc.dsc$a_pointer = (char *) dev; /* Read only parameter */
11926 status = lib$getdvi(&item, 0, &dev_desc, &lockid, 0, 0);
11927 if (!$VMS_STATUS_SUCCESS(status)) {
11929 case SS$_NOSUCHDEV:
11930 SETERRNO(ENODEV, status);
11936 if (lockid) return (lockid & ~LOCKID_MASK);
11940 /* Otherwise we try to encode the device name */
11944 for (q = dev + strlen(dev); q--; q >= dev) {
11949 else if (isalpha (toupper (*q)))
11950 c= toupper (*q) - 'A' + (char)10;
11952 continue; /* Skip '$'s */
11954 if (i>6) break; /* 36^7 is too large to fit in an unsigned long int */
11956 enc += f * (unsigned long int) c;
11958 return (enc | LOCKID_MASK); /* May have already overflowed into bit 31 */
11960 } /* end of encode_dev() */
11961 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
11962 device_no = encode_dev(aTHX_ devname)
11964 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
11965 device_no = new_dev_no
11969 is_null_device(name)
11972 if (decc_bug_devnull != 0) {
11973 if (strncmp("/dev/null", name, 9) == 0)
11976 /* The VMS null device is named "_NLA0:", usually abbreviated as "NL:".
11977 The underscore prefix, controller letter, and unit number are
11978 independently optional; for our purposes, the colon punctuation
11979 is not. The colon can be trailed by optional directory and/or
11980 filename, but two consecutive colons indicates a nodename rather
11981 than a device. [pr] */
11982 if (*name == '_') ++name;
11983 if (tolower(*name++) != 'n') return 0;
11984 if (tolower(*name++) != 'l') return 0;
11985 if (tolower(*name) == 'a') ++name;
11986 if (*name == '0') ++name;
11987 return (*name++ == ':') && (*name != ':');
11992 Perl_cando_by_name_int
11993 (pTHX_ I32 bit, bool effective, const char *fname, int opts)
11995 char usrname[L_cuserid];
11996 struct dsc$descriptor_s usrdsc =
11997 {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, usrname};
11998 char *vmsname = NULL, *fileified = NULL;
11999 unsigned long int objtyp = ACL$C_FILE, access, retsts, privused, iosb[2], flags;
12000 unsigned short int retlen, trnlnm_iter_count;
12001 struct dsc$descriptor_s namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
12002 union prvdef curprv;
12003 struct itmlst_3 armlst[4] = {{sizeof access, CHP$_ACCESS, &access, &retlen},
12004 {sizeof privused, CHP$_PRIVUSED, &privused, &retlen},
12005 {sizeof flags, CHP$_FLAGS, &flags, &retlen},{0,0,0,0}};
12006 struct itmlst_3 jpilst[3] = {{sizeof curprv, JPI$_CURPRIV, &curprv, &retlen},
12007 {sizeof usrname, JPI$_USERNAME, &usrname, &usrdsc.dsc$w_length},
12009 struct itmlst_3 usrprolst[2] = {{sizeof curprv, CHP$_PRIV, &curprv, &retlen},
12011 struct dsc$descriptor_s usrprodsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
12013 static int profile_context = -1;
12015 if (!fname || !*fname) return FALSE;
12017 /* Make sure we expand logical names, since sys$check_access doesn't */
12018 fileified = PerlMem_malloc(VMS_MAXRSS);
12019 if (fileified == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12020 if (!strpbrk(fname,"/]>:")) {
12021 strcpy(fileified,fname);
12022 trnlnm_iter_count = 0;
12023 while (!strpbrk(fileified,"/]>:") && my_trnlnm(fileified,fileified,0)) {
12024 trnlnm_iter_count++;
12025 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
12030 vmsname = PerlMem_malloc(VMS_MAXRSS);
12031 if (vmsname == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12032 if ( !(opts & PERL_RMSEXPAND_M_VMS_IN) ) {
12033 /* Don't know if already in VMS format, so make sure */
12034 if (!do_rmsexpand(fname, vmsname, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL)) {
12035 PerlMem_free(fileified);
12036 PerlMem_free(vmsname);
12041 strcpy(vmsname,fname);
12044 /* sys$check_access needs a file spec, not a directory spec.
12045 * Don't use flex_stat here, as that depends on thread context
12046 * having been initialized, and we may get here during startup.
12049 retlen = namdsc.dsc$w_length = strlen(vmsname);
12050 if (vmsname[retlen-1] == ']'
12051 || vmsname[retlen-1] == '>'
12052 || vmsname[retlen-1] == ':'
12053 || (!stat(vmsname, (stat_t *)&st) && S_ISDIR(st.st_mode))) {
12055 if (!do_fileify_dirspec(vmsname,fileified,1,NULL)) {
12056 PerlMem_free(fileified);
12057 PerlMem_free(vmsname);
12066 retlen = namdsc.dsc$w_length = strlen(fname);
12067 namdsc.dsc$a_pointer = (char *)fname;
12070 case S_IXUSR: case S_IXGRP: case S_IXOTH:
12071 access = ARM$M_EXECUTE;
12072 flags = CHP$M_READ;
12074 case S_IRUSR: case S_IRGRP: case S_IROTH:
12075 access = ARM$M_READ;
12076 flags = CHP$M_READ | CHP$M_USEREADALL;
12078 case S_IWUSR: case S_IWGRP: case S_IWOTH:
12079 access = ARM$M_WRITE;
12080 flags = CHP$M_READ | CHP$M_WRITE;
12082 case S_IDUSR: case S_IDGRP: case S_IDOTH:
12083 access = ARM$M_DELETE;
12084 flags = CHP$M_READ | CHP$M_WRITE;
12087 if (fileified != NULL)
12088 PerlMem_free(fileified);
12089 if (vmsname != NULL)
12090 PerlMem_free(vmsname);
12094 /* Before we call $check_access, create a user profile with the current
12095 * process privs since otherwise it just uses the default privs from the
12096 * UAF and might give false positives or negatives. This only works on
12097 * VMS versions v6.0 and later since that's when sys$create_user_profile
12098 * became available.
12101 /* get current process privs and username */
12102 _ckvmssts_noperl(sys$getjpiw(0,0,0,jpilst,iosb,0,0));
12103 _ckvmssts_noperl(iosb[0]);
12105 #if defined(__VMS_VER) && __VMS_VER >= 60000000
12107 /* find out the space required for the profile */
12108 _ckvmssts_noperl(sys$create_user_profile(&usrdsc,&usrprolst,0,0,
12109 &usrprodsc.dsc$w_length,&profile_context));
12111 /* allocate space for the profile and get it filled in */
12112 usrprodsc.dsc$a_pointer = PerlMem_malloc(usrprodsc.dsc$w_length);
12113 if (usrprodsc.dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12114 _ckvmssts_noperl(sys$create_user_profile(&usrdsc,&usrprolst,0,usrprodsc.dsc$a_pointer,
12115 &usrprodsc.dsc$w_length,&profile_context));
12117 /* use the profile to check access to the file; free profile & analyze results */
12118 retsts = sys$check_access(&objtyp,&namdsc,0,armlst,&profile_context,0,0,&usrprodsc);
12119 PerlMem_free(usrprodsc.dsc$a_pointer);
12120 if (retsts == SS$_NOCALLPRIV) retsts = SS$_NOPRIV; /* not really 3rd party */
12124 retsts = sys$check_access(&objtyp,&namdsc,&usrdsc,armlst);
12128 if (retsts == SS$_NOPRIV || retsts == SS$_NOSUCHOBJECT ||
12129 retsts == SS$_INVFILFOROP || retsts == RMS$_FNF || retsts == RMS$_SYN ||
12130 retsts == RMS$_DIR || retsts == RMS$_DEV || retsts == RMS$_DNF) {
12131 set_vaxc_errno(retsts);
12132 if (retsts == SS$_NOPRIV) set_errno(EACCES);
12133 else if (retsts == SS$_INVFILFOROP) set_errno(EINVAL);
12134 else set_errno(ENOENT);
12135 if (fileified != NULL)
12136 PerlMem_free(fileified);
12137 if (vmsname != NULL)
12138 PerlMem_free(vmsname);
12141 if (retsts == SS$_NORMAL || retsts == SS$_ACCONFLICT) {
12142 if (fileified != NULL)
12143 PerlMem_free(fileified);
12144 if (vmsname != NULL)
12145 PerlMem_free(vmsname);
12148 _ckvmssts_noperl(retsts);
12150 if (fileified != NULL)
12151 PerlMem_free(fileified);
12152 if (vmsname != NULL)
12153 PerlMem_free(vmsname);
12154 return FALSE; /* Should never get here */
12158 /* Do the permissions allow some operation? Assumes PL_statcache already set. */
12159 /* Do this via $Check_Access on VMS, since the CRTL stat() returns only a
12160 * subset of the applicable information.
12163 Perl_cando(pTHX_ Mode_t bit, bool effective, const Stat_t *statbufp)
12165 return cando_by_name_int
12166 (bit, effective, statbufp->st_devnam, PERL_RMSEXPAND_M_VMS_IN);
12167 } /* end of cando() */
12171 /*{{{I32 cando_by_name(I32 bit, bool effective, char *fname)*/
12173 Perl_cando_by_name(pTHX_ I32 bit, bool effective, const char *fname)
12175 return cando_by_name_int(bit, effective, fname, 0);
12177 } /* end of cando_by_name() */
12181 /*{{{ int flex_fstat(int fd, Stat_t *statbuf)*/
12183 Perl_flex_fstat(pTHX_ int fd, Stat_t *statbufp)
12185 if (!fstat(fd,(stat_t *) statbufp)) {
12187 char *vms_filename;
12188 vms_filename = PerlMem_malloc(VMS_MAXRSS);
12189 if (vms_filename == NULL) _ckvmssts(SS$_INSFMEM);
12191 /* Save name for cando by name in VMS format */
12192 cptr = getname(fd, vms_filename, 1);
12194 /* This should not happen, but just in case */
12195 if (cptr == NULL) {
12196 statbufp->st_devnam[0] = 0;
12199 /* Make sure that the saved name fits in 255 characters */
12200 cptr = do_rmsexpand
12202 statbufp->st_devnam,
12205 PERL_RMSEXPAND_M_VMS | PERL_RMSEXPAND_M_VMS_IN,
12209 statbufp->st_devnam[0] = 0;
12211 PerlMem_free(vms_filename);
12213 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
12215 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
12217 # ifdef RTL_USES_UTC
12218 # ifdef VMSISH_TIME
12220 statbufp->st_mtime = _toloc(statbufp->st_mtime);
12221 statbufp->st_atime = _toloc(statbufp->st_atime);
12222 statbufp->st_ctime = _toloc(statbufp->st_ctime);
12226 # ifdef VMSISH_TIME
12227 if (!VMSISH_TIME) { /* Return UTC instead of local time */
12231 statbufp->st_mtime = _toutc(statbufp->st_mtime);
12232 statbufp->st_atime = _toutc(statbufp->st_atime);
12233 statbufp->st_ctime = _toutc(statbufp->st_ctime);
12240 } /* end of flex_fstat() */
12243 #if !defined(__VAX) && __CRTL_VER >= 80200000
12251 #define lstat(_x, _y) stat(_x, _y)
12254 #define flex_stat_int(a,b,c) Perl_flex_stat_int(aTHX_ a,b,c)
12257 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag)
12259 char fileified[VMS_MAXRSS];
12260 char temp_fspec[VMS_MAXRSS];
12265 if (!fspec) return retval;
12267 strcpy(temp_fspec, fspec);
12269 if (decc_bug_devnull != 0) {
12270 if (is_null_device(temp_fspec)) { /* Fake a stat() for the null device */
12271 memset(statbufp,0,sizeof *statbufp);
12272 VMS_DEVICE_ENCODE(statbufp->st_dev, "_NLA0:", 0);
12273 statbufp->st_mode = S_IFBLK | S_IREAD | S_IWRITE | S_IEXEC;
12274 statbufp->st_uid = 0x00010001;
12275 statbufp->st_gid = 0x0001;
12276 time((time_t *)&statbufp->st_mtime);
12277 statbufp->st_atime = statbufp->st_ctime = statbufp->st_mtime;
12282 /* Try for a directory name first. If fspec contains a filename without
12283 * a type (e.g. sea:[wine.dark]water), and both sea:[wine.dark]water.dir
12284 * and sea:[wine.dark]water. exist, we prefer the directory here.
12285 * Similarly, sea:[wine.dark] returns the result for sea:[wine]dark.dir,
12286 * not sea:[wine.dark]., if the latter exists. If the intended target is
12287 * the file with null type, specify this by calling flex_stat() with
12288 * a '.' at the end of fspec.
12290 * If we are in Posix filespec mode, accept the filename as is.
12294 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12295 /* The CRTL stat() falls down hard on multi-dot filenames in unix format unless
12296 * DECC$EFS_CHARSET is in effect, so temporarily enable it if it isn't already.
12298 if (!decc_efs_charset)
12299 decc$feature_set_value(decc$feature_get_index("DECC$EFS_CHARSET"),1,1);
12302 #if __CRTL_VER >= 80200000 && !defined(__VAX)
12303 if (decc_posix_compliant_pathnames == 0) {
12305 if (do_fileify_dirspec(temp_fspec,fileified,0,NULL) != NULL) {
12306 if (lstat_flag == 0)
12307 retval = stat(fileified,(stat_t *) statbufp);
12309 retval = lstat(fileified,(stat_t *) statbufp);
12310 save_spec = fileified;
12313 if (lstat_flag == 0)
12314 retval = stat(temp_fspec,(stat_t *) statbufp);
12316 retval = lstat(temp_fspec,(stat_t *) statbufp);
12317 save_spec = temp_fspec;
12320 * In debugging, on 8.3 Alpha, I found a case where stat was returning a
12321 * file not found error for a directory named foo:[bar.t] or /foo/bar/t
12322 * and lstat was working correctly for the same file.
12323 * The only syntax that was working for stat was "foo:[bar]t.dir".
12325 * Other directories with the same syntax worked fine.
12326 * So work around the problem when it shows up here.
12329 int save_errno = errno;
12330 if (do_tovmsspec(fspec, temp_fspec, 0, NULL) != NULL) {
12331 if (do_fileify_dirspec(temp_fspec, fileified, 0, NULL) != NULL) {
12332 retval = stat(fileified, (stat_t *) statbufp);
12333 save_spec = fileified;
12336 /* Restore the errno value if third stat does not succeed */
12338 errno = save_errno;
12340 #if __CRTL_VER >= 80200000 && !defined(__VAX)
12342 if (lstat_flag == 0)
12343 retval = stat(temp_fspec,(stat_t *) statbufp);
12345 retval = lstat(temp_fspec,(stat_t *) statbufp);
12346 save_spec = temp_fspec;
12350 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12351 /* As you were... */
12352 if (!decc_efs_charset)
12353 decc$feature_set_value(decc$feature_get_index("DECC$EFS_CHARSET"),1,0);
12358 int rmsex_flags = PERL_RMSEXPAND_M_VMS;
12360 /* If this is an lstat, do not follow the link */
12362 rmsex_flags |= PERL_RMSEXPAND_M_SYMLINK;
12364 cptr = do_rmsexpand
12365 (save_spec, statbufp->st_devnam, 0, NULL, rmsex_flags, NULL, NULL);
12367 statbufp->st_devnam[0] = 0;
12369 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
12371 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
12372 # ifdef RTL_USES_UTC
12373 # ifdef VMSISH_TIME
12375 statbufp->st_mtime = _toloc(statbufp->st_mtime);
12376 statbufp->st_atime = _toloc(statbufp->st_atime);
12377 statbufp->st_ctime = _toloc(statbufp->st_ctime);
12381 # ifdef VMSISH_TIME
12382 if (!VMSISH_TIME) { /* Return UTC instead of local time */
12386 statbufp->st_mtime = _toutc(statbufp->st_mtime);
12387 statbufp->st_atime = _toutc(statbufp->st_atime);
12388 statbufp->st_ctime = _toutc(statbufp->st_ctime);
12392 /* If we were successful, leave errno where we found it */
12393 if (retval == 0) RESTORE_ERRNO;
12396 } /* end of flex_stat_int() */
12399 /*{{{ int flex_stat(const char *fspec, Stat_t *statbufp)*/
12401 Perl_flex_stat(pTHX_ const char *fspec, Stat_t *statbufp)
12403 return flex_stat_int(fspec, statbufp, 0);
12407 /*{{{ int flex_lstat(const char *fspec, Stat_t *statbufp)*/
12409 Perl_flex_lstat(pTHX_ const char *fspec, Stat_t *statbufp)
12411 return flex_stat_int(fspec, statbufp, 1);
12416 /*{{{char *my_getlogin()*/
12417 /* VMS cuserid == Unix getlogin, except calling sequence */
12421 static char user[L_cuserid];
12422 return cuserid(user);
12427 /* rmscopy - copy a file using VMS RMS routines
12429 * Copies contents and attributes of spec_in to spec_out, except owner
12430 * and protection information. Name and type of spec_in are used as
12431 * defaults for spec_out. The third parameter specifies whether rmscopy()
12432 * should try to propagate timestamps from the input file to the output file.
12433 * If it is less than 0, no timestamps are preserved. If it is 0, then
12434 * rmscopy() will behave similarly to the DCL COPY command: timestamps are
12435 * propagated to the output file at creation iff the output file specification
12436 * did not contain an explicit name or type, and the revision date is always
12437 * updated at the end of the copy operation. If it is greater than 0, then
12438 * it is interpreted as a bitmask, in which bit 0 indicates that timestamps
12439 * other than the revision date should be propagated, and bit 1 indicates
12440 * that the revision date should be propagated.
12442 * Returns 1 on success; returns 0 and sets errno and vaxc$errno on failure.
12444 * Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>.
12445 * Incorporates, with permission, some code from EZCOPY by Tim Adye
12446 * <T.J.Adye@rl.ac.uk>. Permission is given to distribute this code
12447 * as part of the Perl standard distribution under the terms of the
12448 * GNU General Public License or the Perl Artistic License. Copies
12449 * of each may be found in the Perl standard distribution.
12451 /*{{{int rmscopy(char *src, char *dst, int preserve_dates)*/
12453 Perl_rmscopy(pTHX_ const char *spec_in, const char *spec_out, int preserve_dates)
12455 char *vmsin, * vmsout, *esa, *esal, *esa_out, *esal_out,
12456 *rsa, *rsal, *rsa_out, *rsal_out, *ubf;
12457 unsigned long int i, sts, sts2;
12459 struct FAB fab_in, fab_out;
12460 struct RAB rab_in, rab_out;
12461 rms_setup_nam(nam);
12462 rms_setup_nam(nam_out);
12463 struct XABDAT xabdat;
12464 struct XABFHC xabfhc;
12465 struct XABRDT xabrdt;
12466 struct XABSUM xabsum;
12468 vmsin = PerlMem_malloc(VMS_MAXRSS);
12469 if (vmsin == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12470 vmsout = PerlMem_malloc(VMS_MAXRSS);
12471 if (vmsout == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12472 if (!spec_in || !*spec_in || !int_tovmsspec(spec_in, vmsin, 1, NULL) ||
12473 !spec_out || !*spec_out || !int_tovmsspec(spec_out, vmsout, 1, NULL)) {
12474 PerlMem_free(vmsin);
12475 PerlMem_free(vmsout);
12476 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12480 esa = PerlMem_malloc(VMS_MAXRSS);
12481 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12483 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
12484 esal = PerlMem_malloc(VMS_MAXRSS);
12485 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12487 fab_in = cc$rms_fab;
12488 rms_set_fna(fab_in, nam, vmsin, strlen(vmsin));
12489 fab_in.fab$b_shr = FAB$M_SHRPUT | FAB$M_UPI;
12490 fab_in.fab$b_fac = FAB$M_BIO | FAB$M_GET;
12491 fab_in.fab$l_fop = FAB$M_SQO;
12492 rms_bind_fab_nam(fab_in, nam);
12493 fab_in.fab$l_xab = (void *) &xabdat;
12495 rsa = PerlMem_malloc(VMS_MAXRSS);
12496 if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12498 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
12499 rsal = PerlMem_malloc(VMS_MAXRSS);
12500 if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12502 rms_set_rsal(nam, rsa, NAM$C_MAXRSS, rsal, (VMS_MAXRSS - 1));
12503 rms_set_esal(nam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1));
12504 rms_nam_esl(nam) = 0;
12505 rms_nam_rsl(nam) = 0;
12506 rms_nam_esll(nam) = 0;
12507 rms_nam_rsll(nam) = 0;
12508 #ifdef NAM$M_NO_SHORT_UPCASE
12509 if (decc_efs_case_preserve)
12510 rms_set_nam_nop(nam, NAM$M_NO_SHORT_UPCASE);
12513 xabdat = cc$rms_xabdat; /* To get creation date */
12514 xabdat.xab$l_nxt = (void *) &xabfhc;
12516 xabfhc = cc$rms_xabfhc; /* To get record length */
12517 xabfhc.xab$l_nxt = (void *) &xabsum;
12519 xabsum = cc$rms_xabsum; /* To get key and area information */
12521 if (!((sts = sys$open(&fab_in)) & 1)) {
12522 PerlMem_free(vmsin);
12523 PerlMem_free(vmsout);
12526 PerlMem_free(esal);
12529 PerlMem_free(rsal);
12530 set_vaxc_errno(sts);
12532 case RMS$_FNF: case RMS$_DNF:
12533 set_errno(ENOENT); break;
12535 set_errno(ENOTDIR); break;
12537 set_errno(ENODEV); break;
12539 set_errno(EINVAL); break;
12541 set_errno(EACCES); break;
12543 set_errno(EVMSERR);
12550 fab_out.fab$w_ifi = 0;
12551 fab_out.fab$b_fac = FAB$M_BIO | FAB$M_PUT;
12552 fab_out.fab$b_shr = FAB$M_SHRGET | FAB$M_UPI;
12553 fab_out.fab$l_fop = FAB$M_SQO;
12554 rms_bind_fab_nam(fab_out, nam_out);
12555 rms_set_fna(fab_out, nam_out, vmsout, strlen(vmsout));
12556 dna_len = rms_nam_namel(nam) ? rms_nam_name_type_l_size(nam) : 0;
12557 rms_set_dna(fab_out, nam_out, rms_nam_namel(nam), dna_len);
12558 esa_out = PerlMem_malloc(NAM$C_MAXRSS + 1);
12559 if (esa_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12560 rsa_out = PerlMem_malloc(NAM$C_MAXRSS + 1);
12561 if (rsa_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12564 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
12565 esal_out = PerlMem_malloc(VMS_MAXRSS);
12566 if (esal_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12567 rsal_out = PerlMem_malloc(VMS_MAXRSS);
12568 if (rsal_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12570 rms_set_rsal(nam_out, rsa_out, NAM$C_MAXRSS, rsal_out, (VMS_MAXRSS - 1));
12571 rms_set_esal(nam_out, esa_out, NAM$C_MAXRSS, esal_out, (VMS_MAXRSS - 1));
12573 if (preserve_dates == 0) { /* Act like DCL COPY */
12574 rms_set_nam_nop(nam_out, NAM$M_SYNCHK);
12575 fab_out.fab$l_xab = NULL; /* Don't disturb data from input file */
12576 if (!((sts = sys$parse(&fab_out)) & STS$K_SUCCESS)) {
12577 PerlMem_free(vmsin);
12578 PerlMem_free(vmsout);
12581 PerlMem_free(esal);
12584 PerlMem_free(rsal);
12585 PerlMem_free(esa_out);
12586 if (esal_out != NULL)
12587 PerlMem_free(esal_out);
12588 PerlMem_free(rsa_out);
12589 if (rsal_out != NULL)
12590 PerlMem_free(rsal_out);
12591 set_errno(sts == RMS$_SYN ? EINVAL : EVMSERR);
12592 set_vaxc_errno(sts);
12595 fab_out.fab$l_xab = (void *) &xabdat;
12596 if (rms_is_nam_fnb(nam, NAM$M_EXP_NAME | NAM$M_EXP_TYPE))
12597 preserve_dates = 1;
12599 if (preserve_dates < 0) /* Clear all bits; we'll use it as a */
12600 preserve_dates =0; /* bitmask from this point forward */
12602 if (!(preserve_dates & 1)) fab_out.fab$l_xab = (void *) &xabfhc;
12603 if (!((sts = sys$create(&fab_out)) & STS$K_SUCCESS)) {
12604 PerlMem_free(vmsin);
12605 PerlMem_free(vmsout);
12608 PerlMem_free(esal);
12611 PerlMem_free(rsal);
12612 PerlMem_free(esa_out);
12613 if (esal_out != NULL)
12614 PerlMem_free(esal_out);
12615 PerlMem_free(rsa_out);
12616 if (rsal_out != NULL)
12617 PerlMem_free(rsal_out);
12618 set_vaxc_errno(sts);
12621 set_errno(ENOENT); break;
12623 set_errno(ENOTDIR); break;
12625 set_errno(ENODEV); break;
12627 set_errno(EINVAL); break;
12629 set_errno(EACCES); break;
12631 set_errno(EVMSERR);
12635 fab_out.fab$l_fop |= FAB$M_DLT; /* in case we have to bail out */
12636 if (preserve_dates & 2) {
12637 /* sys$close() will process xabrdt, not xabdat */
12638 xabrdt = cc$rms_xabrdt;
12640 xabrdt.xab$q_rdt = xabdat.xab$q_rdt;
12642 /* gcc doesn't like the assignment, since its prototype for xab$q_rdt
12643 * is unsigned long[2], while DECC & VAXC use a struct */
12644 memcpy(xabrdt.xab$q_rdt,xabdat.xab$q_rdt,sizeof xabrdt.xab$q_rdt);
12646 fab_out.fab$l_xab = (void *) &xabrdt;
12649 ubf = PerlMem_malloc(32256);
12650 if (ubf == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12651 rab_in = cc$rms_rab;
12652 rab_in.rab$l_fab = &fab_in;
12653 rab_in.rab$l_rop = RAB$M_BIO;
12654 rab_in.rab$l_ubf = ubf;
12655 rab_in.rab$w_usz = 32256;
12656 if (!((sts = sys$connect(&rab_in)) & 1)) {
12657 sys$close(&fab_in); sys$close(&fab_out);
12658 PerlMem_free(vmsin);
12659 PerlMem_free(vmsout);
12663 PerlMem_free(esal);
12666 PerlMem_free(rsal);
12667 PerlMem_free(esa_out);
12668 if (esal_out != NULL)
12669 PerlMem_free(esal_out);
12670 PerlMem_free(rsa_out);
12671 if (rsal_out != NULL)
12672 PerlMem_free(rsal_out);
12673 set_errno(EVMSERR); set_vaxc_errno(sts);
12677 rab_out = cc$rms_rab;
12678 rab_out.rab$l_fab = &fab_out;
12679 rab_out.rab$l_rbf = ubf;
12680 if (!((sts = sys$connect(&rab_out)) & 1)) {
12681 sys$close(&fab_in); sys$close(&fab_out);
12682 PerlMem_free(vmsin);
12683 PerlMem_free(vmsout);
12687 PerlMem_free(esal);
12690 PerlMem_free(rsal);
12691 PerlMem_free(esa_out);
12692 if (esal_out != NULL)
12693 PerlMem_free(esal_out);
12694 PerlMem_free(rsa_out);
12695 if (rsal_out != NULL)
12696 PerlMem_free(rsal_out);
12697 set_errno(EVMSERR); set_vaxc_errno(sts);
12701 while ((sts = sys$read(&rab_in))) { /* always true */
12702 if (sts == RMS$_EOF) break;
12703 rab_out.rab$w_rsz = rab_in.rab$w_rsz;
12704 if (!(sts & 1) || !((sts = sys$write(&rab_out)) & 1)) {
12705 sys$close(&fab_in); sys$close(&fab_out);
12706 PerlMem_free(vmsin);
12707 PerlMem_free(vmsout);
12711 PerlMem_free(esal);
12714 PerlMem_free(rsal);
12715 PerlMem_free(esa_out);
12716 if (esal_out != NULL)
12717 PerlMem_free(esal_out);
12718 PerlMem_free(rsa_out);
12719 if (rsal_out != NULL)
12720 PerlMem_free(rsal_out);
12721 set_errno(EVMSERR); set_vaxc_errno(sts);
12727 fab_out.fab$l_fop &= ~FAB$M_DLT; /* We got this far; keep the output */
12728 sys$close(&fab_in); sys$close(&fab_out);
12729 sts = (fab_in.fab$l_sts & 1) ? fab_out.fab$l_sts : fab_in.fab$l_sts;
12731 PerlMem_free(vmsin);
12732 PerlMem_free(vmsout);
12736 PerlMem_free(esal);
12739 PerlMem_free(rsal);
12740 PerlMem_free(esa_out);
12741 if (esal_out != NULL)
12742 PerlMem_free(esal_out);
12743 PerlMem_free(rsa_out);
12744 if (rsal_out != NULL)
12745 PerlMem_free(rsal_out);
12748 set_errno(EVMSERR); set_vaxc_errno(sts);
12754 } /* end of rmscopy() */
12758 /*** The following glue provides 'hooks' to make some of the routines
12759 * from this file available from Perl. These routines are sufficiently
12760 * basic, and are required sufficiently early in the build process,
12761 * that's it's nice to have them available to miniperl as well as the
12762 * full Perl, so they're set up here instead of in an extension. The
12763 * Perl code which handles importation of these names into a given
12764 * package lives in [.VMS]Filespec.pm in @INC.
12768 rmsexpand_fromperl(pTHX_ CV *cv)
12771 char *fspec, *defspec = NULL, *rslt;
12773 int fs_utf8, dfs_utf8;
12777 if (!items || items > 2)
12778 Perl_croak(aTHX_ "Usage: VMS::Filespec::rmsexpand(spec[,defspec])");
12779 fspec = SvPV(ST(0),n_a);
12780 fs_utf8 = SvUTF8(ST(0));
12781 if (!fspec || !*fspec) XSRETURN_UNDEF;
12783 defspec = SvPV(ST(1),n_a);
12784 dfs_utf8 = SvUTF8(ST(1));
12786 rslt = do_rmsexpand(fspec,NULL,1,defspec,0,&fs_utf8,&dfs_utf8);
12787 ST(0) = sv_newmortal();
12788 if (rslt != NULL) {
12789 sv_usepvn(ST(0),rslt,strlen(rslt));
12798 vmsify_fromperl(pTHX_ CV *cv)
12805 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsify(spec)");
12806 utf8_fl = SvUTF8(ST(0));
12807 vmsified = do_tovmsspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12808 ST(0) = sv_newmortal();
12809 if (vmsified != NULL) {
12810 sv_usepvn(ST(0),vmsified,strlen(vmsified));
12819 unixify_fromperl(pTHX_ CV *cv)
12826 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixify(spec)");
12827 utf8_fl = SvUTF8(ST(0));
12828 unixified = do_tounixspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12829 ST(0) = sv_newmortal();
12830 if (unixified != NULL) {
12831 sv_usepvn(ST(0),unixified,strlen(unixified));
12840 fileify_fromperl(pTHX_ CV *cv)
12847 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::fileify(spec)");
12848 utf8_fl = SvUTF8(ST(0));
12849 fileified = do_fileify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12850 ST(0) = sv_newmortal();
12851 if (fileified != NULL) {
12852 sv_usepvn(ST(0),fileified,strlen(fileified));
12861 pathify_fromperl(pTHX_ CV *cv)
12868 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::pathify(spec)");
12869 utf8_fl = SvUTF8(ST(0));
12870 pathified = do_pathify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12871 ST(0) = sv_newmortal();
12872 if (pathified != NULL) {
12873 sv_usepvn(ST(0),pathified,strlen(pathified));
12882 vmspath_fromperl(pTHX_ CV *cv)
12889 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmspath(spec)");
12890 utf8_fl = SvUTF8(ST(0));
12891 vmspath = do_tovmspath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12892 ST(0) = sv_newmortal();
12893 if (vmspath != NULL) {
12894 sv_usepvn(ST(0),vmspath,strlen(vmspath));
12903 unixpath_fromperl(pTHX_ CV *cv)
12910 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixpath(spec)");
12911 utf8_fl = SvUTF8(ST(0));
12912 unixpath = do_tounixpath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12913 ST(0) = sv_newmortal();
12914 if (unixpath != NULL) {
12915 sv_usepvn(ST(0),unixpath,strlen(unixpath));
12924 candelete_fromperl(pTHX_ CV *cv)
12932 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::candelete(spec)");
12934 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
12935 Newx(fspec, VMS_MAXRSS, char);
12936 if (fspec == NULL) _ckvmssts(SS$_INSFMEM);
12937 if (SvTYPE(mysv) == SVt_PVGV) {
12938 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),fspec)) {
12939 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12947 if (mysv != ST(0) || !(fsp = SvPV(mysv,n_a)) || !*fsp) {
12948 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12955 ST(0) = boolSV(cando_by_name(S_IDUSR,0,fsp));
12961 rmscopy_fromperl(pTHX_ CV *cv)
12964 char *inspec, *outspec, *inp, *outp;
12966 struct dsc$descriptor indsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
12967 outdsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
12968 unsigned long int sts;
12973 if (items < 2 || items > 3)
12974 Perl_croak(aTHX_ "Usage: File::Copy::rmscopy(from,to[,date_flag])");
12976 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
12977 Newx(inspec, VMS_MAXRSS, char);
12978 if (SvTYPE(mysv) == SVt_PVGV) {
12979 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),inspec)) {
12980 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12988 if (mysv != ST(0) || !(inp = SvPV(mysv,n_a)) || !*inp) {
12989 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12995 mysv = SvROK(ST(1)) ? SvRV(ST(1)) : ST(1);
12996 Newx(outspec, VMS_MAXRSS, char);
12997 if (SvTYPE(mysv) == SVt_PVGV) {
12998 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),outspec)) {
12999 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13008 if (mysv != ST(1) || !(outp = SvPV(mysv,n_a)) || !*outp) {
13009 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13016 date_flag = (items == 3) ? SvIV(ST(2)) : 0;
13018 ST(0) = boolSV(rmscopy(inp,outp,date_flag));
13024 /* The mod2fname is limited to shorter filenames by design, so it should
13025 * not be modified to support longer EFS pathnames
13028 mod2fname(pTHX_ CV *cv)
13031 char ultimate_name[NAM$C_MAXRSS+1], work_name[NAM$C_MAXRSS*8 + 1],
13032 workbuff[NAM$C_MAXRSS*1 + 1];
13033 int total_namelen = 3, counter, num_entries;
13034 /* ODS-5 ups this, but we want to be consistent, so... */
13035 int max_name_len = 39;
13036 AV *in_array = (AV *)SvRV(ST(0));
13038 num_entries = av_len(in_array);
13040 /* All the names start with PL_. */
13041 strcpy(ultimate_name, "PL_");
13043 /* Clean up our working buffer */
13044 Zero(work_name, sizeof(work_name), char);
13046 /* Run through the entries and build up a working name */
13047 for(counter = 0; counter <= num_entries; counter++) {
13048 /* If it's not the first name then tack on a __ */
13050 strcat(work_name, "__");
13052 strcat(work_name, SvPV_nolen(*av_fetch(in_array, counter, FALSE)));
13055 /* Check to see if we actually have to bother...*/
13056 if (strlen(work_name) + 3 <= max_name_len) {
13057 strcat(ultimate_name, work_name);
13059 /* It's too darned big, so we need to go strip. We use the same */
13060 /* algorithm as xsubpp does. First, strip out doubled __ */
13061 char *source, *dest, last;
13064 for (source = work_name; *source; source++) {
13065 if (last == *source && last == '_') {
13071 /* Go put it back */
13072 strcpy(work_name, workbuff);
13073 /* Is it still too big? */
13074 if (strlen(work_name) + 3 > max_name_len) {
13075 /* Strip duplicate letters */
13078 for (source = work_name; *source; source++) {
13079 if (last == toupper(*source)) {
13083 last = toupper(*source);
13085 strcpy(work_name, workbuff);
13088 /* Is it *still* too big? */
13089 if (strlen(work_name) + 3 > max_name_len) {
13090 /* Too bad, we truncate */
13091 work_name[max_name_len - 2] = 0;
13093 strcat(ultimate_name, work_name);
13096 /* Okay, return it */
13097 ST(0) = sv_2mortal(newSVpv(ultimate_name, 0));
13102 hushexit_fromperl(pTHX_ CV *cv)
13107 VMSISH_HUSHED = SvTRUE(ST(0));
13109 ST(0) = boolSV(VMSISH_HUSHED);
13115 Perl_vms_start_glob
13116 (pTHX_ SV *tmpglob,
13120 struct vs_str_st *rslt;
13124 $DESCRIPTOR(dfltdsc,"SYS$DISK:[]*.*;");
13127 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13128 struct dsc$descriptor_vs rsdsc;
13129 unsigned long int cxt = 0, sts = 0, ok = 1, hasdir = 0;
13130 unsigned long hasver = 0, isunix = 0;
13131 unsigned long int lff_flags = 0;
13134 if (!SvOK(tmpglob)) {
13135 SETERRNO(ENOENT,RMS$_FNF);
13139 #ifdef VMS_LONGNAME_SUPPORT
13140 lff_flags = LIB$M_FIL_LONG_NAMES;
13142 /* The Newx macro will not allow me to assign a smaller array
13143 * to the rslt pointer, so we will assign it to the begin char pointer
13144 * and then copy the value into the rslt pointer.
13146 Newx(begin, VMS_MAXRSS + sizeof(unsigned short int), char);
13147 rslt = (struct vs_str_st *)begin;
13149 rstr = &rslt->str[0];
13150 rsdsc.dsc$a_pointer = (char *) rslt; /* cast required */
13151 rsdsc.dsc$w_maxstrlen = VMS_MAXRSS + sizeof(unsigned short int);
13152 rsdsc.dsc$b_dtype = DSC$K_DTYPE_VT;
13153 rsdsc.dsc$b_class = DSC$K_CLASS_VS;
13155 Newx(vmsspec, VMS_MAXRSS, char);
13157 /* We could find out if there's an explicit dev/dir or version
13158 by peeking into lib$find_file's internal context at
13159 ((struct NAM *)((struct FAB *)cxt)->fab$l_nam)->nam$l_fnb
13160 but that's unsupported, so I don't want to do it now and
13161 have it bite someone in the future. */
13162 /* Fix-me: vms_split_path() is the only way to do this, the
13163 existing method will fail with many legal EFS or UNIX specifications
13166 cp = SvPV(tmpglob,i);
13169 if (cp[i] == ';') hasver = 1;
13170 if (cp[i] == '.') {
13171 if (sts) hasver = 1;
13174 if (cp[i] == '/') {
13175 hasdir = isunix = 1;
13178 if (cp[i] == ']' || cp[i] == '>' || cp[i] == ':') {
13183 if ((tmpfp = PerlIO_tmpfile()) != NULL) {
13187 stat_sts = PerlLIO_stat(SvPVX_const(tmpglob),&st);
13188 if (!stat_sts && S_ISDIR(st.st_mode)) {
13189 wilddsc.dsc$a_pointer = tovmspath_utf8(SvPVX(tmpglob),vmsspec,NULL);
13190 ok = (wilddsc.dsc$a_pointer != NULL);
13191 /* maybe passed 'foo' rather than '[.foo]', thus not detected above */
13195 wilddsc.dsc$a_pointer = tovmsspec_utf8(SvPVX(tmpglob),vmsspec,NULL);
13196 ok = (wilddsc.dsc$a_pointer != NULL);
13199 wilddsc.dsc$w_length = strlen(wilddsc.dsc$a_pointer);
13201 /* If not extended character set, replace ? with % */
13202 /* With extended character set, ? is a wildcard single character */
13203 if (!decc_efs_case_preserve) {
13204 for (cp=wilddsc.dsc$a_pointer; ok && cp && *cp; cp++)
13205 if (*cp == '?') *cp = '%';
13208 while (ok && $VMS_STATUS_SUCCESS(sts)) {
13209 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
13210 int v_sts, v_len, r_len, d_len, n_len, e_len, vs_len;
13212 sts = lib$find_file(&wilddsc,&rsdsc,&cxt,
13213 &dfltdsc,NULL,&rms_sts,&lff_flags);
13214 if (!$VMS_STATUS_SUCCESS(sts))
13219 /* with varying string, 1st word of buffer contains result length */
13220 rstr[rslt->length] = '\0';
13222 /* Find where all the components are */
13223 v_sts = vms_split_path
13238 /* If no version on input, truncate the version on output */
13239 if (!hasver && (vs_len > 0)) {
13243 /* No version & a null extension on UNIX handling */
13244 if (isunix && (e_len == 1) && decc_readdir_dropdotnotype) {
13250 if (!decc_efs_case_preserve) {
13251 for (cp = rstr; *cp; cp++) *cp = _tolower(*cp);
13255 if (isunix) trim_unixpath(rstr,SvPVX(tmpglob),1);
13259 /* Start with the name */
13262 strcat(begin,"\n");
13263 ok = (PerlIO_puts(tmpfp,begin) != EOF);
13265 if (cxt) (void)lib$find_file_end(&cxt);
13268 /* Be POSIXish: return the input pattern when no matches */
13269 strcpy(rstr,SvPVX(tmpglob));
13271 ok = (PerlIO_puts(tmpfp,rstr) != EOF);
13274 if (ok && sts != RMS$_NMF &&
13275 sts != RMS$_DNF && sts != RMS_FNF) ok = 0;
13278 SETERRNO((sts == RMS$_SYN ? EINVAL : EVMSERR),sts);
13280 PerlIO_close(tmpfp);
13284 PerlIO_rewind(tmpfp);
13285 IoTYPE(io) = IoTYPE_RDONLY;
13286 IoIFP(io) = fp = tmpfp;
13287 IoFLAGS(io) &= ~IOf_UNTAINT; /* maybe redundant */
13297 mp_do_vms_realpath(pTHX_ const char *filespec, char * rslt_spec,
13301 unixrealpath_fromperl(pTHX_ CV *cv)
13304 char *fspec, *rslt_spec, *rslt;
13307 if (!items || items != 1)
13308 Perl_croak(aTHX_ "Usage: VMS::Filespec::unixrealpath(spec)");
13310 fspec = SvPV(ST(0),n_a);
13311 if (!fspec || !*fspec) XSRETURN_UNDEF;
13313 Newx(rslt_spec, VMS_MAXRSS + 1, char);
13314 rslt = do_vms_realpath(fspec, rslt_spec, NULL);
13316 ST(0) = sv_newmortal();
13318 sv_usepvn(ST(0),rslt,strlen(rslt));
13320 Safefree(rslt_spec);
13325 mp_do_vms_realname(pTHX_ const char *filespec, char * rslt_spec,
13329 vmsrealpath_fromperl(pTHX_ CV *cv)
13332 char *fspec, *rslt_spec, *rslt;
13335 if (!items || items != 1)
13336 Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsrealpath(spec)");
13338 fspec = SvPV(ST(0),n_a);
13339 if (!fspec || !*fspec) XSRETURN_UNDEF;
13341 Newx(rslt_spec, VMS_MAXRSS + 1, char);
13342 rslt = do_vms_realname(fspec, rslt_spec, NULL);
13344 ST(0) = sv_newmortal();
13346 sv_usepvn(ST(0),rslt,strlen(rslt));
13348 Safefree(rslt_spec);
13354 * A thin wrapper around decc$symlink to make sure we follow the
13355 * standard and do not create a symlink with a zero-length name.
13357 * Also in ODS-2 mode, existing tests assume that the link target
13358 * will be converted to UNIX format.
13360 /*{{{ int my_symlink(pTHX_ const char *contents, const char *link_name)*/
13361 int Perl_my_symlink(pTHX_ const char *contents, const char *link_name) {
13362 if (!link_name || !*link_name) {
13363 SETERRNO(ENOENT, SS$_NOSUCHFILE);
13367 if (decc_efs_charset) {
13368 return symlink(contents, link_name);
13373 /* Unless we are in ODS-5 mode, convert the symlink target to UNIX */
13374 /* because in order to work, the symlink target must be in UNIX format */
13376 /* As symbolic links can hold things other than files, we will only do */
13377 /* the conversion in in ODS-2 mode */
13379 Newx(utarget, VMS_MAXRSS + 1, char);
13380 if (do_tounixspec(contents, utarget, 0, NULL) == NULL) {
13382 /* This should not fail, as an untranslatable filename */
13383 /* should be passed through */
13384 utarget = (char *)contents;
13386 sts = symlink(utarget, link_name);
13394 #endif /* HAS_SYMLINK */
13396 int do_vms_case_tolerant(void);
13399 case_tolerant_process_fromperl(pTHX_ CV *cv)
13402 ST(0) = boolSV(do_vms_case_tolerant());
13406 #ifdef USE_ITHREADS
13409 Perl_sys_intern_dup(pTHX_ struct interp_intern *src,
13410 struct interp_intern *dst)
13412 PERL_ARGS_ASSERT_SYS_INTERN_DUP;
13414 memcpy(dst,src,sizeof(struct interp_intern));
13420 Perl_sys_intern_clear(pTHX)
13425 Perl_sys_intern_init(pTHX)
13427 unsigned int ix = RAND_MAX;
13432 MY_POSIX_EXIT = vms_posix_exit;
13435 MY_INV_RAND_MAX = 1./x;
13439 init_os_extras(void)
13442 char* file = __FILE__;
13443 if (decc_disable_to_vms_logname_translation) {
13444 no_translate_barewords = TRUE;
13446 no_translate_barewords = FALSE;
13449 newXSproto("VMS::Filespec::rmsexpand",rmsexpand_fromperl,file,"$;$");
13450 newXSproto("VMS::Filespec::vmsify",vmsify_fromperl,file,"$");
13451 newXSproto("VMS::Filespec::unixify",unixify_fromperl,file,"$");
13452 newXSproto("VMS::Filespec::pathify",pathify_fromperl,file,"$");
13453 newXSproto("VMS::Filespec::fileify",fileify_fromperl,file,"$");
13454 newXSproto("VMS::Filespec::vmspath",vmspath_fromperl,file,"$");
13455 newXSproto("VMS::Filespec::unixpath",unixpath_fromperl,file,"$");
13456 newXSproto("VMS::Filespec::candelete",candelete_fromperl,file,"$");
13457 newXSproto("DynaLoader::mod2fname", mod2fname, file, "$");
13458 newXS("File::Copy::rmscopy",rmscopy_fromperl,file);
13459 newXSproto("vmsish::hushed",hushexit_fromperl,file,";$");
13460 newXSproto("VMS::Filespec::unixrealpath",unixrealpath_fromperl,file,"$;$");
13461 newXSproto("VMS::Filespec::vmsrealpath",vmsrealpath_fromperl,file,"$;$");
13462 newXSproto("VMS::Filespec::case_tolerant_process",
13463 case_tolerant_process_fromperl,file,"");
13465 store_pipelocs(aTHX); /* will redo any earlier attempts */
13470 #if __CRTL_VER == 80200000
13471 /* This missed getting in to the DECC SDK for 8.2 */
13472 char *realpath(const char *file_name, char * resolved_name, ...);
13475 /*{{{char *do_vms_realpath(const char *file_name, char *resolved_name)*/
13476 /* wrapper for the realpath() function added with 8.2 RMS SYMLINK SDK.
13477 * The perl fallback routine to provide realpath() is not as efficient
13481 /* Hack, use old stat() as fastest way of getting ino_t and device */
13482 int decc$stat(const char *name, void * statbuf);
13485 /* Realpath is fragile. In 8.3 it does not work if the feature
13486 * DECC$POSIX_COMPLIANT_PATHNAMES is not enabled, even though symbolic
13487 * links are implemented in RMS, not the CRTL. It also can fail if the
13488 * user does not have read/execute access to some of the directories.
13489 * So in order for Do What I Mean mode to work, if realpath() fails,
13490 * fall back to looking up the filename by the device name and FID.
13493 int vms_fid_to_name(char * outname, int outlen, const char * name)
13497 unsigned short st_ino[3];
13498 unsigned short padw;
13499 unsigned long padl[30]; /* plenty of room */
13502 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13503 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13505 sts = decc$stat(name, &statbuf);
13508 dvidsc.dsc$a_pointer=statbuf.st_dev;
13509 dvidsc.dsc$w_length=strlen(statbuf.st_dev);
13511 specdsc.dsc$a_pointer = outname;
13512 specdsc.dsc$w_length = outlen-1;
13514 sts = lib$fid_to_name
13515 (&dvidsc, statbuf.st_ino, &specdsc, &specdsc.dsc$w_length);
13516 if ($VMS_STATUS_SUCCESS(sts)) {
13517 outname[specdsc.dsc$w_length] = 0;
13527 mp_do_vms_realpath(pTHX_ const char *filespec, char *outbuf,
13530 char * rslt = NULL;
13533 if (decc_posix_compliant_pathnames > 0 ) {
13534 /* realpath currently only works if posix compliant pathnames are
13535 * enabled. It may start working when they are not, but in that
13536 * case we still want the fallback behavior for backwards compatibility
13538 rslt = realpath(filespec, outbuf);
13542 if (rslt == NULL) {
13544 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
13545 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
13548 /* Fall back to fid_to_name */
13550 Newx(vms_spec, VMS_MAXRSS + 1, char);
13552 sts = vms_fid_to_name(vms_spec, VMS_MAXRSS + 1, filespec);
13556 /* Now need to trim the version off */
13557 sts = vms_split_path
13577 /* Trim off the version */
13578 int file_len = v_len + r_len + d_len + n_len + e_len;
13579 vms_spec[file_len] = 0;
13581 /* The result is expected to be in UNIX format */
13582 rslt = do_tounixspec(vms_spec, outbuf, 0, utf8_fl);
13584 /* Downcase if input had any lower case letters and
13585 * case preservation is not in effect.
13587 if (!decc_efs_case_preserve) {
13588 for (cp = filespec; *cp; cp++)
13589 if (islower(*cp)) { haslower = 1; break; }
13591 if (haslower) __mystrtolower(rslt);
13596 /* Now for some hacks to deal with backwards and forward */
13598 if (!decc_efs_charset) {
13600 /* 1. ODS-2 mode wants to do a syntax only translation */
13601 rslt = do_rmsexpand(filespec, outbuf,
13602 0, NULL, 0, NULL, utf8_fl);
13605 if (decc_filename_unix_report) {
13607 char * vms_dir_name;
13610 /* 2. ODS-5 / UNIX report mode should return a failure */
13611 /* if the parent directory also does not exist */
13612 /* Otherwise, get the real path for the parent */
13613 /* and add the child to it.
13615 /* basename / dirname only available for VMS 7.0+ */
13616 /* So we may need to implement them as common routines */
13618 Newx(dir_name, VMS_MAXRSS + 1, char);
13619 Newx(vms_dir_name, VMS_MAXRSS + 1, char);
13620 dir_name[0] = '\0';
13623 /* First try a VMS parse */
13624 sts = vms_split_path
13642 int dir_len = v_len + r_len + d_len + n_len;
13644 strncpy(dir_name, filespec, dir_len);
13645 dir_name[dir_len] = '\0';
13646 file_name = (char *)&filespec[dir_len + 1];
13649 /* This must be UNIX */
13652 tchar = strrchr(filespec, '/');
13654 if (tchar != NULL) {
13655 int dir_len = tchar - filespec;
13656 strncpy(dir_name, filespec, dir_len);
13657 dir_name[dir_len] = '\0';
13658 file_name = (char *) &filespec[dir_len + 1];
13662 /* Dir name is defaulted */
13663 if (dir_name[0] == 0) {
13665 dir_name[1] = '\0';
13668 /* Need realpath for the directory */
13669 sts = vms_fid_to_name(vms_dir_name,
13674 /* Now need to pathify it.
13675 char *tdir = do_pathify_dirspec(vms_dir_name,
13678 /* And now add the original filespec to it */
13679 if (file_name != NULL) {
13680 strcat(outbuf, file_name);
13684 Safefree(vms_dir_name);
13685 Safefree(dir_name);
13689 Safefree(vms_spec);
13695 mp_do_vms_realname(pTHX_ const char *filespec, char *outbuf,
13698 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
13699 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
13702 /* Fall back to fid_to_name */
13704 sts = vms_fid_to_name(outbuf, VMS_MAXRSS + 1, filespec);
13711 /* Now need to trim the version off */
13712 sts = vms_split_path
13732 /* Trim off the version */
13733 int file_len = v_len + r_len + d_len + n_len + e_len;
13734 outbuf[file_len] = 0;
13736 /* Downcase if input had any lower case letters and
13737 * case preservation is not in effect.
13739 if (!decc_efs_case_preserve) {
13740 for (cp = filespec; *cp; cp++)
13741 if (islower(*cp)) { haslower = 1; break; }
13743 if (haslower) __mystrtolower(outbuf);
13752 /* External entry points */
13753 char *Perl_vms_realpath(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
13754 { return do_vms_realpath(filespec, outbuf, utf8_fl); }
13756 char *Perl_vms_realname(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
13757 { return do_vms_realname(filespec, outbuf, utf8_fl); }
13759 /* case_tolerant */
13761 /*{{{int do_vms_case_tolerant(void)*/
13762 /* OpenVMS provides a case sensitive implementation of ODS-5 and this is
13763 * controlled by a process setting.
13765 int do_vms_case_tolerant(void)
13767 return vms_process_case_tolerant;
13770 /* External entry points */
13771 #if __CRTL_VER >= 70301000 && !defined(__VAX)
13772 int Perl_vms_case_tolerant(void)
13773 { return do_vms_case_tolerant(); }
13775 int Perl_vms_case_tolerant(void)
13776 { return vms_process_case_tolerant; }
13780 /* Start of DECC RTL Feature handling */
13782 static int sys_trnlnm
13783 (const char * logname,
13787 const $DESCRIPTOR(table_dsc, "LNM$FILE_DEV");
13788 const unsigned long attr = LNM$M_CASE_BLIND;
13789 struct dsc$descriptor_s name_dsc;
13791 unsigned short result;
13792 struct itmlst_3 itlst[2] = {{value_len, LNM$_STRING, value, &result},
13795 name_dsc.dsc$w_length = strlen(logname);
13796 name_dsc.dsc$a_pointer = (char *)logname;
13797 name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
13798 name_dsc.dsc$b_class = DSC$K_CLASS_S;
13800 status = sys$trnlnm(&attr, &table_dsc, &name_dsc, 0, itlst);
13802 if ($VMS_STATUS_SUCCESS(status)) {
13804 /* Null terminate and return the string */
13805 /*--------------------------------------*/
13812 static int sys_crelnm
13813 (const char * logname,
13814 const char * value)
13817 const char * proc_table = "LNM$PROCESS_TABLE";
13818 struct dsc$descriptor_s proc_table_dsc;
13819 struct dsc$descriptor_s logname_dsc;
13820 struct itmlst_3 item_list[2];
13822 proc_table_dsc.dsc$a_pointer = (char *) proc_table;
13823 proc_table_dsc.dsc$w_length = strlen(proc_table);
13824 proc_table_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
13825 proc_table_dsc.dsc$b_class = DSC$K_CLASS_S;
13827 logname_dsc.dsc$a_pointer = (char *) logname;
13828 logname_dsc.dsc$w_length = strlen(logname);
13829 logname_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
13830 logname_dsc.dsc$b_class = DSC$K_CLASS_S;
13832 item_list[0].buflen = strlen(value);
13833 item_list[0].itmcode = LNM$_STRING;
13834 item_list[0].bufadr = (char *)value;
13835 item_list[0].retlen = NULL;
13837 item_list[1].buflen = 0;
13838 item_list[1].itmcode = 0;
13840 ret_val = sys$crelnm
13842 (const struct dsc$descriptor_s *)&proc_table_dsc,
13843 (const struct dsc$descriptor_s *)&logname_dsc,
13845 (const struct item_list_3 *) item_list);
13850 /* C RTL Feature settings */
13852 static int set_features
13853 (int (* init_coroutine)(int *, int *, void *), /* Needs casts if used */
13854 int (* cli_routine)(void), /* Not documented */
13855 void *image_info) /* Not documented */
13861 #if defined(JPI$_CASE_LOOKUP_PERM) && !defined(__VAX)
13862 const unsigned long int jpicode1 = JPI$_CASE_LOOKUP_PERM;
13863 const unsigned long int jpicode2 = JPI$_CASE_LOOKUP_IMAGE;
13864 unsigned long case_perm;
13865 unsigned long case_image;
13868 /* Allow an exception to bring Perl into the VMS debugger */
13869 vms_debug_on_exception = 0;
13870 status = sys_trnlnm("PERL_VMS_EXCEPTION_DEBUG", val_str, sizeof(val_str));
13871 if ($VMS_STATUS_SUCCESS(status)) {
13872 val_str[0] = _toupper(val_str[0]);
13873 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13874 vms_debug_on_exception = 1;
13876 vms_debug_on_exception = 0;
13879 /* Debug unix/vms file translation routines */
13880 vms_debug_fileify = 0;
13881 status = sys_trnlnm("PERL_VMS_FILEIFY_DEBUG", val_str, sizeof(val_str));
13882 if ($VMS_STATUS_SUCCESS(status)) {
13883 val_str[0] = _toupper(val_str[0]);
13884 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13885 vms_debug_fileify = 1;
13887 vms_debug_fileify = 0;
13891 /* Historically PERL has been doing vmsify / stat differently than */
13892 /* the CRTL. In particular, under some conditions the CRTL will */
13893 /* remove some illegal characters like spaces from filenames */
13894 /* resulting in some differences. The stat()/lstat() wrapper has */
13895 /* been reporting such file names as invalid and fails to stat them */
13896 /* fixing this bug so that stat()/lstat() accept these like the */
13897 /* CRTL does will result in several tests failing. */
13898 /* This should really be fixed, but for now, set up a feature to */
13899 /* enable it so that the impact can be studied. */
13900 vms_bug_stat_filename = 0;
13901 status = sys_trnlnm("PERL_VMS_BUG_STAT_FILENAME", val_str, sizeof(val_str));
13902 if ($VMS_STATUS_SUCCESS(status)) {
13903 val_str[0] = _toupper(val_str[0]);
13904 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13905 vms_bug_stat_filename = 1;
13907 vms_bug_stat_filename = 0;
13911 /* Create VTF-7 filenames from Unicode instead of UTF-8 */
13912 vms_vtf7_filenames = 0;
13913 status = sys_trnlnm("PERL_VMS_VTF7_FILENAMES", val_str, sizeof(val_str));
13914 if ($VMS_STATUS_SUCCESS(status)) {
13915 val_str[0] = _toupper(val_str[0]);
13916 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13917 vms_vtf7_filenames = 1;
13919 vms_vtf7_filenames = 0;
13922 /* unlink all versions on unlink() or rename() */
13923 vms_unlink_all_versions = 0;
13924 status = sys_trnlnm
13925 ("PERL_VMS_UNLINK_ALL_VERSIONS", val_str, sizeof(val_str));
13926 if ($VMS_STATUS_SUCCESS(status)) {
13927 val_str[0] = _toupper(val_str[0]);
13928 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13929 vms_unlink_all_versions = 1;
13931 vms_unlink_all_versions = 0;
13934 /* Dectect running under GNV Bash or other UNIX like shell */
13935 #if __CRTL_VER >= 70300000 && !defined(__VAX)
13936 gnv_unix_shell = 0;
13937 status = sys_trnlnm("GNV$UNIX_SHELL", val_str, sizeof(val_str));
13938 if ($VMS_STATUS_SUCCESS(status)) {
13939 gnv_unix_shell = 1;
13940 set_feature_default("DECC$EFS_CASE_PRESERVE", 1);
13941 set_feature_default("DECC$EFS_CHARSET", 1);
13942 set_feature_default("DECC$FILENAME_UNIX_NO_VERSION", 1);
13943 set_feature_default("DECC$FILENAME_UNIX_REPORT", 1);
13944 set_feature_default("DECC$READDIR_DROPDOTNOTYPE", 1);
13945 set_feature_default("DECC$DISABLE_POSIX_ROOT", 0);
13946 vms_unlink_all_versions = 1;
13947 vms_posix_exit = 1;
13951 /* hacks to see if known bugs are still present for testing */
13953 /* PCP mode requires creating /dev/null special device file */
13954 decc_bug_devnull = 0;
13955 status = sys_trnlnm("DECC_BUG_DEVNULL", val_str, sizeof(val_str));
13956 if ($VMS_STATUS_SUCCESS(status)) {
13957 val_str[0] = _toupper(val_str[0]);
13958 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13959 decc_bug_devnull = 1;
13961 decc_bug_devnull = 0;
13964 /* UNIX directory names with no paths are broken in a lot of places */
13965 decc_dir_barename = 1;
13966 status = sys_trnlnm("DECC_DIR_BARENAME", val_str, sizeof(val_str));
13967 if ($VMS_STATUS_SUCCESS(status)) {
13968 val_str[0] = _toupper(val_str[0]);
13969 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13970 decc_dir_barename = 1;
13972 decc_dir_barename = 0;
13975 #if __CRTL_VER >= 70300000 && !defined(__VAX)
13976 s = decc$feature_get_index("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION");
13978 decc_disable_to_vms_logname_translation = decc$feature_get_value(s, 1);
13979 if (decc_disable_to_vms_logname_translation < 0)
13980 decc_disable_to_vms_logname_translation = 0;
13983 s = decc$feature_get_index("DECC$EFS_CASE_PRESERVE");
13985 decc_efs_case_preserve = decc$feature_get_value(s, 1);
13986 if (decc_efs_case_preserve < 0)
13987 decc_efs_case_preserve = 0;
13990 s = decc$feature_get_index("DECC$EFS_CHARSET");
13991 decc_efs_charset_index = s;
13993 decc_efs_charset = decc$feature_get_value(s, 1);
13994 if (decc_efs_charset < 0)
13995 decc_efs_charset = 0;
13998 s = decc$feature_get_index("DECC$FILENAME_UNIX_REPORT");
14000 decc_filename_unix_report = decc$feature_get_value(s, 1);
14001 if (decc_filename_unix_report > 0) {
14002 decc_filename_unix_report = 1;
14003 vms_posix_exit = 1;
14006 decc_filename_unix_report = 0;
14009 s = decc$feature_get_index("DECC$FILENAME_UNIX_ONLY");
14011 decc_filename_unix_only = decc$feature_get_value(s, 1);
14012 if (decc_filename_unix_only > 0) {
14013 decc_filename_unix_only = 1;
14016 decc_filename_unix_only = 0;
14020 s = decc$feature_get_index("DECC$FILENAME_UNIX_NO_VERSION");
14022 decc_filename_unix_no_version = decc$feature_get_value(s, 1);
14023 if (decc_filename_unix_no_version < 0)
14024 decc_filename_unix_no_version = 0;
14027 s = decc$feature_get_index("DECC$READDIR_DROPDOTNOTYPE");
14029 decc_readdir_dropdotnotype = decc$feature_get_value(s, 1);
14030 if (decc_readdir_dropdotnotype < 0)
14031 decc_readdir_dropdotnotype = 0;
14034 #if __CRTL_VER >= 80200000
14035 s = decc$feature_get_index("DECC$POSIX_COMPLIANT_PATHNAMES");
14037 decc_posix_compliant_pathnames = decc$feature_get_value(s, 1);
14038 if (decc_posix_compliant_pathnames < 0)
14039 decc_posix_compliant_pathnames = 0;
14040 if (decc_posix_compliant_pathnames > 4)
14041 decc_posix_compliant_pathnames = 0;
14046 status = sys_trnlnm
14047 ("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION", val_str, sizeof(val_str));
14048 if ($VMS_STATUS_SUCCESS(status)) {
14049 val_str[0] = _toupper(val_str[0]);
14050 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14051 decc_disable_to_vms_logname_translation = 1;
14056 status = sys_trnlnm("DECC$EFS_CASE_PRESERVE", val_str, sizeof(val_str));
14057 if ($VMS_STATUS_SUCCESS(status)) {
14058 val_str[0] = _toupper(val_str[0]);
14059 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14060 decc_efs_case_preserve = 1;
14065 status = sys_trnlnm("DECC$FILENAME_UNIX_REPORT", val_str, sizeof(val_str));
14066 if ($VMS_STATUS_SUCCESS(status)) {
14067 val_str[0] = _toupper(val_str[0]);
14068 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14069 decc_filename_unix_report = 1;
14072 status = sys_trnlnm("DECC$FILENAME_UNIX_ONLY", val_str, sizeof(val_str));
14073 if ($VMS_STATUS_SUCCESS(status)) {
14074 val_str[0] = _toupper(val_str[0]);
14075 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14076 decc_filename_unix_only = 1;
14077 decc_filename_unix_report = 1;
14080 status = sys_trnlnm("DECC$FILENAME_UNIX_NO_VERSION", val_str, sizeof(val_str));
14081 if ($VMS_STATUS_SUCCESS(status)) {
14082 val_str[0] = _toupper(val_str[0]);
14083 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14084 decc_filename_unix_no_version = 1;
14087 status = sys_trnlnm("DECC$READDIR_DROPDOTNOTYPE", val_str, sizeof(val_str));
14088 if ($VMS_STATUS_SUCCESS(status)) {
14089 val_str[0] = _toupper(val_str[0]);
14090 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14091 decc_readdir_dropdotnotype = 1;
14096 #if defined(JPI$_CASE_LOOKUP_PERM) && defined(PPROP$K_CASE_BLIND) && !defined(__VAX)
14098 /* Report true case tolerance */
14099 /*----------------------------*/
14100 status = lib$getjpi(&jpicode1, 0, 0, &case_perm, 0, 0);
14101 if (!$VMS_STATUS_SUCCESS(status))
14102 case_perm = PPROP$K_CASE_BLIND;
14103 status = lib$getjpi(&jpicode2, 0, 0, &case_image, 0, 0);
14104 if (!$VMS_STATUS_SUCCESS(status))
14105 case_image = PPROP$K_CASE_BLIND;
14106 if ((case_perm == PPROP$K_CASE_SENSITIVE) ||
14107 (case_image == PPROP$K_CASE_SENSITIVE))
14108 vms_process_case_tolerant = 0;
14112 /* USE POSIX/DCL Exit codes - Recommended, but needs to default to */
14113 /* for strict backward compatibilty */
14114 status = sys_trnlnm
14115 ("PERL_VMS_POSIX_EXIT", val_str, sizeof(val_str));
14116 if ($VMS_STATUS_SUCCESS(status)) {
14117 val_str[0] = _toupper(val_str[0]);
14118 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14119 vms_posix_exit = 1;
14121 vms_posix_exit = 0;
14125 /* CRTL can be initialized past this point, but not before. */
14126 /* DECC$CRTL_INIT(); */
14133 #pragma extern_model save
14134 #pragma extern_model strict_refdef "LIB$INITIALIZ" nowrt
14135 const __align (LONGWORD) int spare[8] = {0};
14137 /* .psect LIB$INITIALIZE, NOPIC, USR, CON, REL, GBL, NOSHR, NOEXE, RD, NOWRT, LONG */
14138 #if __DECC_VER >= 60560002
14139 #pragma extern_model strict_refdef "LIB$INITIALIZE" nopic, con, rel, gbl, noshr, noexe, nowrt, long
14141 #pragma extern_model strict_refdef "LIB$INITIALIZE" nopic, con, gbl, noshr, nowrt, long
14143 #endif /* __DECC */
14145 const long vms_cc_features = (const long)set_features;
14148 ** Force a reference to LIB$INITIALIZE to ensure it
14149 ** exists in the image.
14151 int lib$initialize(void);
14153 #pragma extern_model strict_refdef
14155 int lib_init_ref = (int) lib$initialize;
14158 #pragma extern_model restore