3 * VMS-specific routines for perl5
5 * Copyright (C) 1993-2015 by Charles Bailey and others.
7 * You may distribute under the terms of either the GNU General Public
8 * License or the Artistic License, as specified in the README file.
12 * Yet small as was their hunted band
13 * still fell and fearless was each hand,
14 * and strong deeds they wrought yet oft,
15 * and loved the woods, whose ways more soft
16 * them seemed than thralls of that black throne
17 * to live and languish in halls of stone.
18 * "The Lay of Leithian", Canto II, lines 135-40
20 * [p.162 of _The Lays of Beleriand_]
28 #include <climsgdef.h>
38 #include <libclidef.h>
40 #include <lib$routines.h>
52 #include <str$routines.h>
58 #define NO_EFN EFN$C_ENF
62 #pragma member_alignment save
63 #pragma nomember_alignment longword
68 unsigned short * retadr;
70 #pragma member_alignment restore
72 /* Older versions of ssdef.h don't have these */
73 #ifndef SS$_INVFILFOROP
74 # define SS$_INVFILFOROP 3930
76 #ifndef SS$_NOSUCHOBJECT
77 # define SS$_NOSUCHOBJECT 2696
80 /* We implement I/O here, so we will be mixing PerlIO and stdio calls. */
81 #define PERLIO_NOT_STDIO 0
83 /* Don't replace system definitions of vfork, getenv, lstat, and stat,
84 * code below needs to get to the underlying CRTL routines. */
85 #define DONT_MASK_RTL_CALLS
89 /* Anticipating future expansion in lexical warnings . . . */
91 # define WARN_INTERNAL WARN_MISC
94 #ifdef VMS_LONGNAME_SUPPORT
95 #include <libfildef.h>
98 #if __CRTL_VER >= 80200000
106 #define lstat(_x, _y) stat(_x, _y)
109 /* Routine to create a decterm for use with the Perl debugger */
110 /* No headers, this information was found in the Programming Concepts Manual */
112 static int (*decw_term_port)
113 (const struct dsc$descriptor_s * display,
114 const struct dsc$descriptor_s * setup_file,
115 const struct dsc$descriptor_s * customization,
116 struct dsc$descriptor_s * result_device_name,
117 unsigned short * result_device_name_length,
120 void * char_change_buffer) = 0;
122 #if defined(NEED_AN_H_ERRNO)
126 #if defined(__DECC) || defined(__DECCXX)
127 #pragma member_alignment save
128 #pragma nomember_alignment longword
130 #pragma message disable misalgndmem
133 unsigned short int buflen;
134 unsigned short int itmcode;
136 unsigned short int *retlen;
139 struct filescan_itmlst_2 {
140 unsigned short length;
141 unsigned short itmcode;
146 unsigned short length;
147 char str[VMS_MAXRSS];
148 unsigned short pad; /* for longword struct alignment */
151 #if defined(__DECC) || defined(__DECCXX)
152 #pragma message restore
153 #pragma member_alignment restore
156 #define do_fileify_dirspec(a,b,c,d) mp_do_fileify_dirspec(aTHX_ a,b,c,d)
157 #define do_pathify_dirspec(a,b,c,d) mp_do_pathify_dirspec(aTHX_ a,b,c,d)
158 #define do_tovmsspec(a,b,c,d) mp_do_tovmsspec(aTHX_ a,b,c,0,d)
159 #define do_tovmspath(a,b,c,d) mp_do_tovmspath(aTHX_ a,b,c,d)
160 #define do_rmsexpand(a,b,c,d,e,f,g) mp_do_rmsexpand(aTHX_ a,b,c,d,e,f,g)
161 #define do_vms_realpath(a,b,c) mp_do_vms_realpath(aTHX_ a,b,c)
162 #define do_vms_realname(a,b,c) mp_do_vms_realname(aTHX_ a,b,c)
163 #define do_tounixspec(a,b,c,d) mp_do_tounixspec(aTHX_ a,b,c,d)
164 #define do_tounixpath(a,b,c,d) mp_do_tounixpath(aTHX_ a,b,c,d)
165 #define do_vms_case_tolerant(a) mp_do_vms_case_tolerant(a)
166 #define expand_wild_cards(a,b,c,d) mp_expand_wild_cards(aTHX_ a,b,c,d)
167 #define getredirection(a,b) mp_getredirection(aTHX_ a,b)
169 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int *);
170 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int *);
171 static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *);
172 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int *);
174 static char * int_rmsexpand_vms(
175 const char * filespec, char * outbuf, unsigned opts);
176 static char * int_rmsexpand_tovms(
177 const char * filespec, char * outbuf, unsigned opts);
178 static char *int_tovmsspec
179 (const char *path, char *buf, int dir_flag, int * utf8_flag);
180 static char * int_fileify_dirspec(const char *dir, char *buf, int *utf8_fl);
181 static char * int_tounixspec(const char *spec, char *buf, int * utf8_fl);
182 static char * int_tovmspath(const char *path, char *buf, int * utf8_fl);
184 /* see system service docs for $TRNLNM -- NOT the same as LNM$_MAX_INDEX */
185 #define PERL_LNM_MAX_ALLOWED_INDEX 127
187 /* OpenVMS User's Guide says at least 9 iterative translations will be performed,
188 * depending on the facility. SHOW LOGICAL does 10, so we'll imitate that for
191 #define PERL_LNM_MAX_ITER 10
193 /* New values with 7.3-2*/ /* why does max DCL have 4 byte subtracted from it? */
194 #define MAX_DCL_SYMBOL (8192)
195 #define MAX_DCL_LINE_LENGTH (4096 - 4)
197 static char *__mystrtolower(char *str)
199 if (str) for (; *str; ++str) *str= tolower(*str);
203 static struct dsc$descriptor_s fildevdsc =
204 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "LNM$FILE_DEV" };
205 static struct dsc$descriptor_s crtlenvdsc =
206 { 8, DSC$K_DTYPE_T, DSC$K_CLASS_S, "CRTL_ENV" };
207 static struct dsc$descriptor_s *fildev[] = { &fildevdsc, NULL };
208 static struct dsc$descriptor_s *defenv[] = { &fildevdsc, &crtlenvdsc, NULL };
209 static struct dsc$descriptor_s **env_tables = defenv;
210 static bool will_taint = FALSE; /* tainting active, but no PL_curinterp yet */
212 /* True if we shouldn't treat barewords as logicals during directory */
214 static int no_translate_barewords;
216 /* DECC Features that may need to affect how Perl interprets
217 * displays filename information
219 static int decc_disable_to_vms_logname_translation = 1;
220 static int decc_disable_posix_root = 1;
221 int decc_efs_case_preserve = 0;
222 static int decc_efs_charset = 0;
223 static int decc_efs_charset_index = -1;
224 static int decc_filename_unix_no_version = 0;
225 static int decc_filename_unix_only = 0;
226 int decc_filename_unix_report = 0;
227 int decc_posix_compliant_pathnames = 0;
228 int decc_readdir_dropdotnotype = 0;
229 static int vms_process_case_tolerant = 1;
230 int vms_vtf7_filenames = 0;
231 int gnv_unix_shell = 0;
232 static int vms_unlink_all_versions = 0;
233 static int vms_posix_exit = 0;
235 /* bug workarounds if needed */
236 int decc_bug_devnull = 1;
237 int vms_bug_stat_filename = 0;
239 static int vms_debug_on_exception = 0;
240 static int vms_debug_fileify = 0;
242 /* Simple logical name translation */
244 simple_trnlnm(const char * logname, char * value, int value_len)
246 const $DESCRIPTOR(table_dsc, "LNM$FILE_DEV");
247 const unsigned long attr = LNM$M_CASE_BLIND;
248 struct dsc$descriptor_s name_dsc;
250 unsigned short result;
251 struct itmlst_3 itlst[2] = {{value_len, LNM$_STRING, value, &result},
254 name_dsc.dsc$w_length = strlen(logname);
255 name_dsc.dsc$a_pointer = (char *)logname;
256 name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
257 name_dsc.dsc$b_class = DSC$K_CLASS_S;
259 status = sys$trnlnm(&attr, &table_dsc, &name_dsc, 0, itlst);
261 if ($VMS_STATUS_SUCCESS(status)) {
263 /* Null terminate and return the string */
264 /*--------------------------------------*/
273 /* Is this a UNIX file specification?
274 * No longer a simple check with EFS file specs
275 * For now, not a full check, but need to
276 * handle POSIX ^UP^ specifications
277 * Fixing to handle ^/ cases would require
278 * changes to many other conversion routines.
282 is_unix_filespec(const char *path)
288 if (strncmp(path,"\"^UP^",5) != 0) {
289 pch1 = strchr(path, '/');
294 /* If the user wants UNIX files, "." needs to be treated as in UNIX */
295 if (decc_filename_unix_report || decc_filename_unix_only) {
296 if (strcmp(path,".") == 0)
304 /* This routine converts a UCS-2 character to be VTF-7 encoded.
308 ucs2_to_vtf7(char *outspec, unsigned long ucs2_char, int * output_cnt)
310 unsigned char * ucs_ptr;
313 ucs_ptr = (unsigned char *)&ucs2_char;
317 hex = (ucs_ptr[1] >> 4) & 0xf;
319 outspec[2] = hex + '0';
321 outspec[2] = (hex - 9) + 'A';
322 hex = ucs_ptr[1] & 0xF;
324 outspec[3] = hex + '0';
326 outspec[3] = (hex - 9) + 'A';
328 hex = (ucs_ptr[0] >> 4) & 0xf;
330 outspec[4] = hex + '0';
332 outspec[4] = (hex - 9) + 'A';
333 hex = ucs_ptr[1] & 0xF;
335 outspec[5] = hex + '0';
337 outspec[5] = (hex - 9) + 'A';
343 /* This handles the conversion of a UNIX extended character set to a ^
344 * escaped VMS character.
345 * in a UNIX file specification.
347 * The output count variable contains the number of characters added
348 * to the output string.
350 * The return value is the number of characters read from the input string
353 copy_expand_unix_filename_escape(char *outspec, const char *inspec, int *output_cnt, const int * utf8_fl)
360 utf8_flag = *utf8_fl;
364 if (*inspec >= 0x80) {
365 if (utf8_fl && vms_vtf7_filenames) {
366 unsigned long ucs_char;
370 if ((*inspec & 0xE0) == 0xC0) {
372 ucs_char = ((inspec[0] & 0x1F) << 6) + (inspec[1] & 0x3f);
373 if (ucs_char >= 0x80) {
374 ucs2_to_vtf7(outspec, ucs_char, output_cnt);
377 } else if ((*inspec & 0xF0) == 0xE0) {
379 ucs_char = ((inspec[0] & 0xF) << 12) +
380 ((inspec[1] & 0x3f) << 6) +
382 if (ucs_char >= 0x800) {
383 ucs2_to_vtf7(outspec, ucs_char, output_cnt);
387 #if 0 /* I do not see longer sequences supported by OpenVMS */
388 /* Maybe some one can fix this later */
389 } else if ((*inspec & 0xF8) == 0xF0) {
392 } else if ((*inspec & 0xFC) == 0xF8) {
395 } else if ((*inspec & 0xFE) == 0xFC) {
402 /* High bit set, but not a Unicode character! */
404 /* Non printing DECMCS or ISO Latin-1 character? */
405 if ((unsigned char)*inspec <= 0x9F) {
409 hex = (*inspec >> 4) & 0xF;
411 outspec[1] = hex + '0';
413 outspec[1] = (hex - 9) + 'A';
417 outspec[2] = hex + '0';
419 outspec[2] = (hex - 9) + 'A';
423 } else if ((unsigned char)*inspec == 0xA0) {
429 } else if ((unsigned char)*inspec == 0xFF) {
441 /* Is this a macro that needs to be passed through?
442 * Macros start with $( and an alpha character, followed
443 * by a string of alpha numeric characters ending with a )
444 * If this does not match, then encode it as ODS-5.
446 if ((inspec[0] == '$') && (inspec[1] == '(')) {
449 if (isalnum(inspec[2]) || (inspec[2] == '.') || (inspec[2] == '_')) {
451 outspec[0] = inspec[0];
452 outspec[1] = inspec[1];
453 outspec[2] = inspec[2];
455 while(isalnum(inspec[tcnt]) ||
456 (inspec[2] == '.') || (inspec[2] == '_')) {
457 outspec[tcnt] = inspec[tcnt];
460 if (inspec[tcnt] == ')') {
461 outspec[tcnt] = inspec[tcnt];
478 if (decc_efs_charset == 0)
505 /* Don't escape again if following character is
506 * already something we escape.
508 if (strchr(".~!#&\'`()+@{},;[]%^=_\\", *(inspec+1))) {
514 /* But otherwise fall through and escape it. */
516 /* Assume that this is to be escaped */
518 outspec[1] = *inspec;
522 case ' ': /* space */
523 /* Assume that this is to be escaped */
539 /* This handles the expansion of a '^' prefix to the proper character
540 * in a UNIX file specification.
542 * The output count variable contains the number of characters added
543 * to the output string.
545 * The return value is the number of characters read from the input
549 copy_expand_vms_filename_escape(char *outspec, const char *inspec, int *output_cnt)
556 if (*inspec == '^') {
559 /* Spaces and non-trailing dots should just be passed through,
560 * but eat the escape character.
567 case '_': /* space */
573 /* Hmm. Better leave the escape escaped. */
579 case 'U': /* Unicode - FIX-ME this is wrong. */
582 scnt = strspn(inspec, "0123456789ABCDEFabcdef");
585 scnt = sscanf(inspec, "%2x%2x", &c1, &c2);
586 outspec[0] = c1 & 0xff;
587 outspec[1] = c2 & 0xff;
594 /* Error - do best we can to continue */
604 scnt = strspn(inspec, "0123456789ABCDEFabcdef");
608 scnt = sscanf(inspec, "%2x", &c1);
609 outspec[0] = c1 & 0xff;
630 /* vms_split_path - Verify that the input file specification is a
631 * VMS format file specification, and provide pointers to the components of
632 * it. With EFS format filenames, this is virtually the only way to
633 * parse a VMS path specification into components.
635 * If the sum of the components do not add up to the length of the
636 * string, then the passed file specification is probably a UNIX style
640 vms_split_path(const char * path, char * * volume, int * vol_len, char * * root, int * root_len,
641 char * * dir, int * dir_len, char * * name, int * name_len,
642 char * * ext, int * ext_len, char * * version, int * ver_len)
644 struct dsc$descriptor path_desc;
648 struct filescan_itmlst_2 item_list[9];
649 const int filespec = 0;
650 const int nodespec = 1;
651 const int devspec = 2;
652 const int rootspec = 3;
653 const int dirspec = 4;
654 const int namespec = 5;
655 const int typespec = 6;
656 const int verspec = 7;
658 /* Assume the worst for an easy exit */
672 path_desc.dsc$a_pointer = (char *)path; /* cast ok */
673 path_desc.dsc$w_length = strlen(path);
674 path_desc.dsc$b_dtype = DSC$K_DTYPE_T;
675 path_desc.dsc$b_class = DSC$K_CLASS_S;
677 /* Get the total length, if it is shorter than the string passed
678 * then this was probably not a VMS formatted file specification
680 item_list[filespec].itmcode = FSCN$_FILESPEC;
681 item_list[filespec].length = 0;
682 item_list[filespec].component = NULL;
684 /* If the node is present, then it gets considered as part of the
685 * volume name to hopefully make things simple.
687 item_list[nodespec].itmcode = FSCN$_NODE;
688 item_list[nodespec].length = 0;
689 item_list[nodespec].component = NULL;
691 item_list[devspec].itmcode = FSCN$_DEVICE;
692 item_list[devspec].length = 0;
693 item_list[devspec].component = NULL;
695 /* root is a special case, adding it to either the directory or
696 * the device components will probably complicate things for the
697 * callers of this routine, so leave it separate.
699 item_list[rootspec].itmcode = FSCN$_ROOT;
700 item_list[rootspec].length = 0;
701 item_list[rootspec].component = NULL;
703 item_list[dirspec].itmcode = FSCN$_DIRECTORY;
704 item_list[dirspec].length = 0;
705 item_list[dirspec].component = NULL;
707 item_list[namespec].itmcode = FSCN$_NAME;
708 item_list[namespec].length = 0;
709 item_list[namespec].component = NULL;
711 item_list[typespec].itmcode = FSCN$_TYPE;
712 item_list[typespec].length = 0;
713 item_list[typespec].component = NULL;
715 item_list[verspec].itmcode = FSCN$_VERSION;
716 item_list[verspec].length = 0;
717 item_list[verspec].component = NULL;
719 item_list[8].itmcode = 0;
720 item_list[8].length = 0;
721 item_list[8].component = NULL;
723 status = sys$filescan
724 ((const struct dsc$descriptor_s *)&path_desc, item_list,
726 _ckvmssts_noperl(status); /* All failure status values indicate a coding error */
728 /* If we parsed it successfully these two lengths should be the same */
729 if (path_desc.dsc$w_length != item_list[filespec].length)
732 /* If we got here, then it is a VMS file specification */
735 /* set the volume name */
736 if (item_list[nodespec].length > 0) {
737 *volume = item_list[nodespec].component;
738 *vol_len = item_list[nodespec].length + item_list[devspec].length;
741 *volume = item_list[devspec].component;
742 *vol_len = item_list[devspec].length;
745 *root = item_list[rootspec].component;
746 *root_len = item_list[rootspec].length;
748 *dir = item_list[dirspec].component;
749 *dir_len = item_list[dirspec].length;
751 /* Now fun with versions and EFS file specifications
752 * The parser can not tell the difference when a "." is a version
753 * delimiter or a part of the file specification.
755 if ((decc_efs_charset) &&
756 (item_list[verspec].length > 0) &&
757 (item_list[verspec].component[0] == '.')) {
758 *name = item_list[namespec].component;
759 *name_len = item_list[namespec].length + item_list[typespec].length;
760 *ext = item_list[verspec].component;
761 *ext_len = item_list[verspec].length;
766 *name = item_list[namespec].component;
767 *name_len = item_list[namespec].length;
768 *ext = item_list[typespec].component;
769 *ext_len = item_list[typespec].length;
770 *version = item_list[verspec].component;
771 *ver_len = item_list[verspec].length;
776 /* Routine to determine if the file specification ends with .dir */
778 is_dir_ext(char * e_spec, int e_len, char * vs_spec, int vs_len)
781 /* e_len must be 4, and version must be <= 2 characters */
782 if (e_len != 4 || vs_len > 2)
785 /* If a version number is present, it needs to be one */
786 if ((vs_len == 2) && (vs_spec[1] != '1'))
789 /* Look for the DIR on the extension */
790 if (vms_process_case_tolerant) {
791 if ((toupper(e_spec[1]) == 'D') &&
792 (toupper(e_spec[2]) == 'I') &&
793 (toupper(e_spec[3]) == 'R')) {
797 /* Directory extensions are supposed to be in upper case only */
798 /* I would not be surprised if this rule can not be enforced */
799 /* if and when someone fully debugs the case sensitive mode */
800 if ((e_spec[1] == 'D') &&
801 (e_spec[2] == 'I') &&
802 (e_spec[3] == 'R')) {
811 * Routine to retrieve the maximum equivalence index for an input
812 * logical name. Some calls to this routine have no knowledge if
813 * the variable is a logical or not. So on error we return a max
816 /*{{{int my_maxidx(const char *lnm) */
818 my_maxidx(const char *lnm)
822 int attr = LNM$M_CASE_BLIND;
823 struct dsc$descriptor lnmdsc;
824 struct itmlst_3 itlst[2] = {{sizeof(midx), LNM$_MAX_INDEX, &midx, 0},
827 lnmdsc.dsc$w_length = strlen(lnm);
828 lnmdsc.dsc$b_dtype = DSC$K_DTYPE_T;
829 lnmdsc.dsc$b_class = DSC$K_CLASS_S;
830 lnmdsc.dsc$a_pointer = (char *) lnm; /* Cast ok for read only parameter */
832 status = sys$trnlnm(&attr, &fildevdsc, &lnmdsc, 0, itlst);
833 if ((status & 1) == 0)
840 /* Routine to remove the 2-byte prefix from the translation of a
841 * process-permanent file (PPF).
843 static inline unsigned short int
844 S_remove_ppf_prefix(const char *lnm, char *eqv, unsigned short int eqvlen)
846 if (*((int *)lnm) == *((int *)"SYS$") &&
847 eqvlen >= 4 && eqv[0] == 0x1b && eqv[1] == 0x00 &&
848 ( (lnm[4] == 'O' && !strcmp(lnm,"SYS$OUTPUT")) ||
849 (lnm[4] == 'I' && !strcmp(lnm,"SYS$INPUT")) ||
850 (lnm[4] == 'E' && !strcmp(lnm,"SYS$ERROR")) ||
851 (lnm[4] == 'C' && !strcmp(lnm,"SYS$COMMAND")) ) ) {
853 memmove(eqv, eqv+4, eqvlen-4);
859 /*{{{int vmstrnenv(const char *lnm, char *eqv, unsigned long int idx, struct dsc$descriptor_s **tabvec, unsigned long int flags) */
861 Perl_vmstrnenv(const char *lnm, char *eqv, unsigned long int idx,
862 struct dsc$descriptor_s **tabvec, unsigned long int flags)
865 char uplnm[LNM$C_NAMLENGTH+1], *cp2;
866 unsigned short int eqvlen, curtab, ivlnm = 0, ivsym = 0, ivenv = 0, secure;
867 bool found_in_crtlenv = 0, found_in_clisym = 0;
868 unsigned long int retsts, attr = LNM$M_CASE_BLIND;
870 unsigned char acmode;
871 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
872 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
873 struct itmlst_3 lnmlst[3] = {{sizeof idx, LNM$_INDEX, &idx, 0},
874 {LNM$C_NAMLENGTH, LNM$_STRING, eqv, &eqvlen},
876 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
877 #if defined(PERL_IMPLICIT_CONTEXT)
880 aTHX = PERL_GET_INTERP;
886 if (!lnm || !eqv || ((idx != 0) && ((idx-1) > PERL_LNM_MAX_ALLOWED_INDEX))) {
887 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return 0;
889 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
890 *cp2 = _toupper(*cp1);
891 if (cp1 - lnm > LNM$C_NAMLENGTH) {
892 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
896 lnmdsc.dsc$w_length = cp1 - lnm;
897 lnmdsc.dsc$a_pointer = uplnm;
898 uplnm[lnmdsc.dsc$w_length] = '\0';
899 secure = flags & PERL__TRNENV_SECURE;
900 acmode = secure ? PSL$C_EXEC : PSL$C_USER;
901 if (!tabvec || !*tabvec) tabvec = env_tables;
903 for (curtab = 0; tabvec[curtab]; curtab++) {
904 if (!str$case_blind_compare(tabvec[curtab],&crtlenv)) {
905 if (!ivenv && !secure) {
910 #if defined(PERL_IMPLICIT_CONTEXT)
913 "Can't read CRTL environ\n");
916 Perl_warn(aTHX_ "Can't read CRTL environ\n");
919 retsts = SS$_NOLOGNAM;
920 for (i = 0; environ[i]; i++) {
921 if ((eq = strchr(environ[i],'=')) &&
922 lnmdsc.dsc$w_length == (eq - environ[i]) &&
923 !strncmp(environ[i],lnm,eq - environ[i])) {
925 for (eqvlen = 0; eq[eqvlen]; eqvlen++) eqv[eqvlen] = eq[eqvlen];
926 if (!eqvlen) continue;
931 if (retsts != SS$_NOLOGNAM) {
932 found_in_crtlenv = 1;
937 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
938 !str$case_blind_compare(&tmpdsc,&clisym)) {
939 if (!ivsym && !secure) {
940 unsigned short int deflen = LNM$C_NAMLENGTH;
941 struct dsc$descriptor_d eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
942 /* dynamic dsc to accommodate possible long value */
943 _ckvmssts_noperl(lib$sget1_dd(&deflen,&eqvdsc));
944 retsts = lib$get_symbol(&lnmdsc,&eqvdsc,&eqvlen,0);
946 if (eqvlen > MAX_DCL_SYMBOL) {
947 set_errno(EVMSERR); set_vaxc_errno(LIB$_STRTRU);
948 eqvlen = MAX_DCL_SYMBOL;
949 /* Special hack--we might be called before the interpreter's */
950 /* fully initialized, in which case either thr or PL_curcop */
951 /* might be bogus. We have to check, since ckWARN needs them */
952 /* both to be valid if running threaded */
953 #if defined(PERL_IMPLICIT_CONTEXT)
956 "Value of CLI symbol \"%s\" too long",lnm);
959 if (ckWARN(WARN_MISC)) {
960 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of CLI symbol \"%s\" too long",lnm);
963 strncpy(eqv,eqvdsc.dsc$a_pointer,eqvlen);
965 _ckvmssts_noperl(lib$sfree1_dd(&eqvdsc));
966 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
967 if (retsts == LIB$_NOSUCHSYM) continue;
973 if ( (idx == 0) && (flags & PERL__TRNENV_JOIN_SEARCHLIST) ) {
974 midx = my_maxidx(lnm);
975 for (idx = 0, cp2 = eqv; idx <= midx; idx++) {
976 lnmlst[1].bufadr = cp2;
978 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
979 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; break; }
980 if (retsts == SS$_NOLOGNAM) break;
981 eqvlen = S_remove_ppf_prefix(uplnm, eqv, eqvlen);
985 if ((retsts == SS$_IVLOGNAM) ||
986 (retsts == SS$_NOLOGNAM)) { continue; }
987 eqvlen = strlen(eqv);
990 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
991 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
992 if (retsts == SS$_NOLOGNAM) continue;
993 eqvlen = S_remove_ppf_prefix(uplnm, eqv, eqvlen);
999 /* An index only makes sense for logical names, so make sure we aren't
1000 * iterating over an index for an environ var or DCL symbol and getting
1001 * the same answer ad infinitum.
1003 if (idx > 0 && (found_in_crtlenv || found_in_clisym)) {
1006 else if (retsts & 1) { eqv[eqvlen] = '\0'; return eqvlen; }
1007 else if (retsts == LIB$_NOSUCHSYM ||
1008 retsts == SS$_NOLOGNAM) {
1009 /* Unsuccessful lookup is normal -- no need to set errno */
1012 else if (retsts == LIB$_INVSYMNAM ||
1013 retsts == SS$_IVLOGNAM ||
1014 retsts == SS$_IVLOGTAB) {
1015 set_errno(EINVAL); set_vaxc_errno(retsts);
1017 else _ckvmssts_noperl(retsts);
1019 } /* end of vmstrnenv */
1022 /*{{{ int my_trnlnm(const char *lnm, char *eqv, unsigned long int idx)*/
1023 /* Define as a function so we can access statics. */
1025 Perl_my_trnlnm(pTHX_ const char *lnm, char *eqv, unsigned long int idx)
1029 #if defined(PERL_IMPLICIT_CONTEXT)
1032 #ifdef SECURE_INTERNAL_GETENV
1033 flags = (PL_curinterp ? TAINTING_get : will_taint) ?
1034 PERL__TRNENV_SECURE : 0;
1037 return vmstrnenv(lnm, eqv, idx, fildev, flags);
1042 * Note: Uses Perl temp to store result so char * can be returned to
1043 * caller; this pointer will be invalidated at next Perl statement
1045 * We define this as a function rather than a macro in terms of my_getenv_len()
1046 * so that it'll work when PL_curinterp is undefined (and we therefore can't
1049 /*{{{ char *my_getenv(const char *lnm, bool sys)*/
1051 Perl_my_getenv(pTHX_ const char *lnm, bool sys)
1054 static char *__my_getenv_eqv = NULL;
1055 char uplnm[LNM$C_NAMLENGTH+1], *cp2, *eqv;
1056 unsigned long int idx = 0;
1057 int success, secure;
1061 midx = my_maxidx(lnm) + 1;
1063 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
1064 /* Set up a temporary buffer for the return value; Perl will
1065 * clean it up at the next statement transition */
1066 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
1067 if (!tmpsv) return NULL;
1071 /* Assume no interpreter ==> single thread */
1072 if (__my_getenv_eqv != NULL) {
1073 Renew(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
1076 Newx(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
1078 eqv = __my_getenv_eqv;
1081 for (cp1 = lnm, cp2 = eqv; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
1082 if (cp1 - lnm == 7 && !strncmp(eqv,"DEFAULT",7)) {
1084 getcwd(eqv,LNM$C_NAMLENGTH);
1088 /* Get rid of "000000/ in rooted filespecs */
1091 zeros = strstr(eqv, "/000000/");
1092 if (zeros != NULL) {
1094 mlen = len - (zeros - eqv) - 7;
1095 memmove(zeros, &zeros[7], mlen);
1103 /* Impose security constraints only if tainting */
1105 /* Impose security constraints only if tainting */
1106 secure = PL_curinterp ? TAINTING_get : will_taint;
1113 #ifdef SECURE_INTERNAL_GETENV
1114 secure ? PERL__TRNENV_SECURE : 0
1120 /* For the getenv interface we combine all the equivalence names
1121 * of a search list logical into one value to acquire a maximum
1122 * value length of 255*128 (assuming %ENV is using logicals).
1124 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
1126 /* If the name contains a semicolon-delimited index, parse it
1127 * off and make sure we only retrieve the equivalence name for
1129 if ((cp2 = strchr(lnm,';')) != NULL) {
1130 my_strlcpy(uplnm, lnm, cp2 - lnm + 1);
1131 idx = strtoul(cp2+1,NULL,0);
1133 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
1136 success = vmstrnenv(lnm,eqv,idx,secure ? fildev : NULL,flags);
1138 return success ? eqv : NULL;
1141 } /* end of my_getenv() */
1145 /*{{{ SV *my_getenv_len(const char *lnm, bool sys)*/
1147 Perl_my_getenv_len(pTHX_ const char *lnm, unsigned long *len, bool sys)
1151 unsigned long idx = 0;
1153 static char *__my_getenv_len_eqv = NULL;
1157 midx = my_maxidx(lnm) + 1;
1159 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
1160 /* Set up a temporary buffer for the return value; Perl will
1161 * clean it up at the next statement transition */
1162 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
1163 if (!tmpsv) return NULL;
1167 /* Assume no interpreter ==> single thread */
1168 if (__my_getenv_len_eqv != NULL) {
1169 Renew(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
1172 Newx(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
1174 buf = __my_getenv_len_eqv;
1177 for (cp1 = lnm, cp2 = buf; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
1178 if (cp1 - lnm == 7 && !strncmp(buf,"DEFAULT",7)) {
1181 getcwd(buf,LNM$C_NAMLENGTH);
1184 /* Get rid of "000000/ in rooted filespecs */
1186 zeros = strstr(buf, "/000000/");
1187 if (zeros != NULL) {
1189 mlen = *len - (zeros - buf) - 7;
1190 memmove(zeros, &zeros[7], mlen);
1199 /* Impose security constraints only if tainting */
1200 secure = PL_curinterp ? TAINTING_get : will_taint;
1207 #ifdef SECURE_INTERNAL_GETENV
1208 secure ? PERL__TRNENV_SECURE : 0
1214 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
1216 if ((cp2 = strchr(lnm,';')) != NULL) {
1217 my_strlcpy(buf, lnm, cp2 - lnm + 1);
1218 idx = strtoul(cp2+1,NULL,0);
1220 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
1223 *len = vmstrnenv(lnm,buf,idx,secure ? fildev : NULL,flags);
1225 /* Get rid of "000000/ in rooted filespecs */
1228 zeros = strstr(buf, "/000000/");
1229 if (zeros != NULL) {
1231 mlen = *len - (zeros - buf) - 7;
1232 memmove(zeros, &zeros[7], mlen);
1238 return *len ? buf : NULL;
1241 } /* end of my_getenv_len() */
1244 static void create_mbx(unsigned short int *, struct dsc$descriptor_s *);
1246 static void riseandshine(unsigned long int dummy) { sys$wake(0,0); }
1248 /*{{{ void prime_env_iter() */
1250 prime_env_iter(void)
1251 /* Fill the %ENV associative array with all logical names we can
1252 * find, in preparation for iterating over it.
1255 static int primed = 0;
1256 HV *seenhv = NULL, *envhv;
1258 char cmd[LNM$C_NAMLENGTH+24], mbxnam[LNM$C_NAMLENGTH], *buf = NULL;
1259 unsigned short int chan;
1260 #ifndef CLI$M_TRUSTED
1261 # define CLI$M_TRUSTED 0x40 /* Missing from VAXC headers */
1263 unsigned long int defflags = CLI$M_NOWAIT | CLI$M_NOKEYPAD | CLI$M_TRUSTED;
1264 unsigned long int mbxbufsiz, flags, retsts, subpid = 0, substs = 0;
1266 bool have_sym = FALSE, have_lnm = FALSE;
1267 struct dsc$descriptor_s tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1268 $DESCRIPTOR(cmddsc,cmd); $DESCRIPTOR(nldsc,"_NLA0:");
1269 $DESCRIPTOR(clidsc,"DCL"); $DESCRIPTOR(clitabdsc,"DCLTABLES");
1270 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
1271 $DESCRIPTOR(local,"_LOCAL"); $DESCRIPTOR(mbxdsc,mbxnam);
1272 #if defined(PERL_IMPLICIT_CONTEXT)
1275 #if defined(USE_ITHREADS)
1276 static perl_mutex primenv_mutex;
1277 MUTEX_INIT(&primenv_mutex);
1280 #if defined(PERL_IMPLICIT_CONTEXT)
1281 /* We jump through these hoops because we can be called at */
1282 /* platform-specific initialization time, which is before anything is */
1283 /* set up--we can't even do a plain dTHX since that relies on the */
1284 /* interpreter structure to be initialized */
1286 aTHX = PERL_GET_INTERP;
1288 /* we never get here because the NULL pointer will cause the */
1289 /* several of the routines called by this routine to access violate */
1291 /* This routine is only called by hv.c/hv_iterinit which has a */
1292 /* context, so the real fix may be to pass it through instead of */
1293 /* the hoops above */
1298 if (primed || !PL_envgv) return;
1299 MUTEX_LOCK(&primenv_mutex);
1300 if (primed) { MUTEX_UNLOCK(&primenv_mutex); return; }
1301 envhv = GvHVn(PL_envgv);
1302 /* Perform a dummy fetch as an lval to insure that the hash table is
1303 * set up. Otherwise, the hv_store() will turn into a nullop. */
1304 (void) hv_fetch(envhv,"DEFAULT",7,TRUE);
1306 for (i = 0; env_tables[i]; i++) {
1307 if (!have_sym && (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
1308 !str$case_blind_compare(&tmpdsc,&clisym)) have_sym = 1;
1309 if (!have_lnm && str$case_blind_compare(env_tables[i],&crtlenv)) have_lnm = 1;
1311 if (have_sym || have_lnm) {
1312 long int syiitm = SYI$_MAXBUF, dviitm = DVI$_DEVNAM;
1313 _ckvmssts(lib$getsyi(&syiitm, &mbxbufsiz, 0, 0, 0, 0));
1314 _ckvmssts(sys$crembx(0,&chan,mbxbufsiz,mbxbufsiz,0xff0f,0,0));
1315 _ckvmssts(lib$getdvi(&dviitm, &chan, NULL, NULL, &mbxdsc, &mbxdsc.dsc$w_length));
1318 for (i--; i >= 0; i--) {
1319 if (!str$case_blind_compare(env_tables[i],&crtlenv)) {
1322 for (j = 0; environ[j]; j++) {
1323 if (!(start = strchr(environ[j],'='))) {
1324 if (ckWARN(WARN_INTERNAL))
1325 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed CRTL environ value \"%s\"\n",environ[j]);
1329 sv = newSVpv(start,0);
1331 (void) hv_store(envhv,environ[j],start - environ[j] - 1,sv,0);
1336 else if ((tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
1337 !str$case_blind_compare(&tmpdsc,&clisym)) {
1338 my_strlcpy(cmd, "Show Symbol/Global *", sizeof(cmd));
1339 cmddsc.dsc$w_length = 20;
1340 if (env_tables[i]->dsc$w_length == 12 &&
1341 (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer + 6) &&
1342 !str$case_blind_compare(&tmpdsc,&local)) my_strlcpy(cmd+12, "Local *", sizeof(cmd)-12);
1343 flags = defflags | CLI$M_NOLOGNAM;
1346 my_strlcpy(cmd, "Show Logical *", sizeof(cmd));
1347 if (str$case_blind_compare(env_tables[i],&fildevdsc)) {
1348 my_strlcat(cmd," /Table=", sizeof(cmd));
1349 cmddsc.dsc$w_length = my_strlcat(cmd, env_tables[i]->dsc$a_pointer, sizeof(cmd));
1351 else cmddsc.dsc$w_length = 14; /* N.B. We test this below */
1352 flags = defflags | CLI$M_NOCLISYM;
1355 /* Create a new subprocess to execute each command, to exclude the
1356 * remote possibility that someone could subvert a mbx or file used
1357 * to write multiple commands to a single subprocess.
1360 retsts = lib$spawn(&cmddsc,&nldsc,&mbxdsc,&flags,0,&subpid,&substs,
1361 0,&riseandshine,0,0,&clidsc,&clitabdsc);
1362 flags &= ~CLI$M_TRUSTED; /* Just in case we hit a really old version */
1363 defflags &= ~CLI$M_TRUSTED;
1364 } while (retsts == LIB$_INVARG && (flags | CLI$M_TRUSTED));
1366 if (!buf) Newx(buf,mbxbufsiz + 1,char);
1367 if (seenhv) SvREFCNT_dec(seenhv);
1370 char *cp1, *cp2, *key;
1371 unsigned long int sts, iosb[2], retlen, keylen;
1374 sts = sys$qiow(0,chan,IO$_READVBLK,iosb,0,0,buf,mbxbufsiz,0,0,0,0);
1375 if (sts & 1) sts = iosb[0] & 0xffff;
1376 if (sts == SS$_ENDOFFILE) {
1378 while (substs == 0) { sys$hiber(); wakect++;}
1379 if (wakect > 1) sys$wake(0,0); /* Stole someone else's wake */
1384 retlen = iosb[0] >> 16;
1385 if (!retlen) continue; /* blank line */
1387 if (iosb[1] != subpid) {
1389 Perl_croak(aTHX_ "Unknown process %x sent message to prime_env_iter: %s",buf);
1393 if (sts == SS$_BUFFEROVF && ckWARN(WARN_INTERNAL))
1394 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Buffer overflow in prime_env_iter: %s",buf);
1396 for (cp1 = buf; *cp1 && isspace(*cp1); cp1++) ;
1397 if (*cp1 == '(' || /* Logical name table name */
1398 *cp1 == '=' /* Next eqv of searchlist */) continue;
1399 if (*cp1 == '"') cp1++;
1400 for (cp2 = cp1; *cp2 && *cp2 != '"' && *cp2 != ' '; cp2++) ;
1401 key = cp1; keylen = cp2 - cp1;
1402 if (keylen && hv_exists(seenhv,key,keylen)) continue;
1403 while (*cp2 && *cp2 != '=') cp2++;
1404 while (*cp2 && *cp2 == '=') cp2++;
1405 while (*cp2 && *cp2 == ' ') cp2++;
1406 if (*cp2 == '"') { /* String translation; may embed "" */
1407 for (cp1 = buf + retlen; *cp1 != '"'; cp1--) ;
1408 cp2++; cp1--; /* Skip "" surrounding translation */
1410 else { /* Numeric translation */
1411 for (cp1 = cp2; *cp1 && *cp1 != ' '; cp1++) ;
1412 cp1--; /* stop on last non-space char */
1414 if ((!keylen || (cp1 - cp2 < -1)) && ckWARN(WARN_INTERNAL)) {
1415 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed message in prime_env_iter: |%s|",buf);
1418 PERL_HASH(hash,key,keylen);
1420 if (cp1 == cp2 && *cp2 == '.') {
1421 /* A single dot usually means an unprintable character, such as a null
1422 * to indicate a zero-length value. Get the actual value to make sure.
1424 char lnm[LNM$C_NAMLENGTH+1];
1425 char eqv[MAX_DCL_SYMBOL+1];
1427 strncpy(lnm, key, keylen);
1428 trnlen = vmstrnenv(lnm, eqv, 0, fildev, 0);
1429 sv = newSVpvn(eqv, strlen(eqv));
1432 sv = newSVpvn(cp2,cp1 - cp2 + 1);
1436 hv_store(envhv,key,keylen,sv,hash);
1437 hv_store(seenhv,key,keylen,&PL_sv_yes,hash);
1439 if (cmddsc.dsc$w_length == 14) { /* We just read LNM$FILE_DEV */
1440 /* get the PPFs for this process, not the subprocess */
1441 const char *ppfs[] = {"SYS$COMMAND", "SYS$INPUT", "SYS$OUTPUT", "SYS$ERROR", NULL};
1442 char eqv[LNM$C_NAMLENGTH+1];
1444 for (i = 0; ppfs[i]; i++) {
1445 trnlen = vmstrnenv(ppfs[i],eqv,0,fildev,0);
1446 sv = newSVpv(eqv,trnlen);
1448 hv_store(envhv,ppfs[i],strlen(ppfs[i]),sv,0);
1453 if (have_sym || have_lnm) _ckvmssts(sys$dassgn(chan));
1454 if (buf) Safefree(buf);
1455 if (seenhv) SvREFCNT_dec(seenhv);
1456 MUTEX_UNLOCK(&primenv_mutex);
1459 } /* end of prime_env_iter */
1463 /*{{{ int vmssetenv(const char *lnm, const char *eqv)*/
1464 /* Define or delete an element in the same "environment" as
1465 * vmstrnenv(). If an element is to be deleted, it's removed from
1466 * the first place it's found. If it's to be set, it's set in the
1467 * place designated by the first element of the table vector.
1468 * Like setenv() returns 0 for success, non-zero on error.
1471 Perl_vmssetenv(pTHX_ const char *lnm, const char *eqv, struct dsc$descriptor_s **tabvec)
1474 char uplnm[LNM$C_NAMLENGTH], *cp2, *c;
1475 unsigned short int curtab, ivlnm = 0, ivsym = 0, ivenv = 0;
1477 unsigned long int retsts, usermode = PSL$C_USER;
1478 struct itmlst_3 *ile, *ilist;
1479 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,uplnm},
1480 eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
1481 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1482 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
1483 $DESCRIPTOR(local,"_LOCAL");
1486 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1487 return SS$_IVLOGNAM;
1490 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
1491 *cp2 = _toupper(*cp1);
1492 if (cp1 - lnm > LNM$C_NAMLENGTH) {
1493 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1494 return SS$_IVLOGNAM;
1497 lnmdsc.dsc$w_length = cp1 - lnm;
1498 if (!tabvec || !*tabvec) tabvec = env_tables;
1500 if (!eqv) { /* we're deleting n element */
1501 for (curtab = 0; tabvec[curtab]; curtab++) {
1502 if (!ivenv && !str$case_blind_compare(tabvec[curtab],&crtlenv)) {
1504 for (i = 0; environ[i]; i++) { /* If it's an environ elt, reset */
1505 if ((cp1 = strchr(environ[i],'=')) &&
1506 lnmdsc.dsc$w_length == (cp1 - environ[i]) &&
1507 !strncmp(environ[i],lnm,cp1 - environ[i])) {
1512 ivenv = 1; retsts = SS$_NOLOGNAM;
1514 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
1515 !str$case_blind_compare(&tmpdsc,&clisym)) {
1516 unsigned int symtype;
1517 if (tabvec[curtab]->dsc$w_length == 12 &&
1518 (tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer + 6) &&
1519 !str$case_blind_compare(&tmpdsc,&local))
1520 symtype = LIB$K_CLI_LOCAL_SYM;
1521 else symtype = LIB$K_CLI_GLOBAL_SYM;
1522 retsts = lib$delete_symbol(&lnmdsc,&symtype);
1523 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
1524 if (retsts == LIB$_NOSUCHSYM) continue;
1528 retsts = sys$dellnm(tabvec[curtab],&lnmdsc,&usermode); /* try user mode first */
1529 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
1530 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
1531 retsts = lib$delete_logical(&lnmdsc,tabvec[curtab]); /* then supervisor mode */
1532 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
1536 else { /* we're defining a value */
1537 if (!ivenv && !str$case_blind_compare(tabvec[0],&crtlenv)) {
1538 return setenv(lnm,eqv,1) ? vaxc$errno : 0;
1541 eqvdsc.dsc$a_pointer = (char *) eqv; /* cast ok to readonly parameter */
1542 eqvdsc.dsc$w_length = strlen(eqv);
1543 if ((tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer) &&
1544 !str$case_blind_compare(&tmpdsc,&clisym)) {
1545 unsigned int symtype;
1546 if (tabvec[0]->dsc$w_length == 12 &&
1547 (tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer + 6) &&
1548 !str$case_blind_compare(&tmpdsc,&local))
1549 symtype = LIB$K_CLI_LOCAL_SYM;
1550 else symtype = LIB$K_CLI_GLOBAL_SYM;
1551 retsts = lib$set_symbol(&lnmdsc,&eqvdsc,&symtype);
1554 if (!*eqv) eqvdsc.dsc$w_length = 1;
1555 if (eqvdsc.dsc$w_length > LNM$C_NAMLENGTH) {
1557 nseg = (eqvdsc.dsc$w_length + LNM$C_NAMLENGTH - 1) / LNM$C_NAMLENGTH;
1558 if (nseg > PERL_LNM_MAX_ALLOWED_INDEX + 1) {
1559 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of logical \"%s\" too long. Truncating to %i bytes",
1560 lnm, LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1));
1561 eqvdsc.dsc$w_length = LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1);
1562 nseg = PERL_LNM_MAX_ALLOWED_INDEX + 1;
1565 Newx(ilist,nseg+1,struct itmlst_3);
1568 set_errno(ENOMEM); set_vaxc_errno(SS$_INSFMEM);
1571 memset(ilist, 0, (sizeof(struct itmlst_3) * (nseg+1)));
1573 for (j = 0, c = eqvdsc.dsc$a_pointer; j < nseg; j++, ile++, c += LNM$C_NAMLENGTH) {
1574 ile->itmcode = LNM$_STRING;
1576 if ((j+1) == nseg) {
1577 ile->buflen = strlen(c);
1578 /* in case we are truncating one that's too long */
1579 if (ile->buflen > LNM$C_NAMLENGTH) ile->buflen = LNM$C_NAMLENGTH;
1582 ile->buflen = LNM$C_NAMLENGTH;
1586 retsts = lib$set_logical(&lnmdsc,0,tabvec[0],0,ilist);
1590 retsts = lib$set_logical(&lnmdsc,&eqvdsc,tabvec[0],0,0);
1595 if (!(retsts & 1)) {
1597 case LIB$_AMBSYMDEF: case LIB$_INSCLIMEM:
1598 case SS$_NOLOGTAB: case SS$_TOOMANYLNAM: case SS$_IVLOGTAB:
1599 set_errno(EVMSERR); break;
1600 case LIB$_INVARG: case LIB$_INVSYMNAM: case SS$_IVLOGNAM:
1601 case LIB$_NOSUCHSYM: case SS$_NOLOGNAM:
1602 set_errno(EINVAL); break;
1604 set_errno(EACCES); break;
1609 set_vaxc_errno(retsts);
1610 return (int) retsts || 44; /* retsts should never be 0, but just in case */
1613 /* We reset error values on success because Perl does an hv_fetch()
1614 * before each hv_store(), and if the thing we're setting didn't
1615 * previously exist, we've got a leftover error message. (Of course,
1616 * this fails in the face of
1617 * $foo = $ENV{nonexistent}; $ENV{existent} = 'foo';
1618 * in that the error reported in $! isn't spurious,
1619 * but it's right more often than not.)
1621 set_errno(0); set_vaxc_errno(retsts);
1625 } /* end of vmssetenv() */
1628 /*{{{ void my_setenv(const char *lnm, const char *eqv)*/
1629 /* This has to be a function since there's a prototype for it in proto.h */
1631 Perl_my_setenv(pTHX_ const char *lnm, const char *eqv)
1634 int len = strlen(lnm);
1638 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
1639 if (!strcmp(uplnm,"DEFAULT")) {
1640 if (eqv && *eqv) my_chdir(eqv);
1645 (void) vmssetenv(lnm,eqv,NULL);
1649 /*{{{static void vmssetuserlnm(char *name, char *eqv); */
1651 * sets a user-mode logical in the process logical name table
1652 * used for redirection of sys$error
1655 Perl_vmssetuserlnm(const char *name, const char *eqv)
1657 $DESCRIPTOR(d_tab, "LNM$PROCESS");
1658 struct dsc$descriptor_d d_name = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
1659 unsigned long int iss, attr = LNM$M_CONFINE;
1660 unsigned char acmode = PSL$C_USER;
1661 struct itmlst_3 lnmlst[2] = {{0, LNM$_STRING, 0, 0},
1663 d_name.dsc$a_pointer = (char *)name; /* Cast OK for read only parameter */
1664 d_name.dsc$w_length = strlen(name);
1666 lnmlst[0].buflen = strlen(eqv);
1667 lnmlst[0].bufadr = (char *)eqv; /* Cast OK for read only parameter */
1669 iss = sys$crelnm(&attr,&d_tab,&d_name,&acmode,lnmlst);
1670 if (!(iss&1)) lib$signal(iss);
1675 /*{{{ char *my_crypt(const char *textpasswd, const char *usrname)*/
1676 /* my_crypt - VMS password hashing
1677 * my_crypt() provides an interface compatible with the Unix crypt()
1678 * C library function, and uses sys$hash_password() to perform VMS
1679 * password hashing. The quadword hashed password value is returned
1680 * as a NUL-terminated 8 character string. my_crypt() does not change
1681 * the case of its string arguments; in order to match the behavior
1682 * of LOGINOUT et al., alphabetic characters in both arguments must
1683 * be upcased by the caller.
1685 * - fix me to call ACM services when available
1688 Perl_my_crypt(pTHX_ const char *textpasswd, const char *usrname)
1690 # ifndef UAI$C_PREFERRED_ALGORITHM
1691 # define UAI$C_PREFERRED_ALGORITHM 127
1693 unsigned char alg = UAI$C_PREFERRED_ALGORITHM;
1694 unsigned short int salt = 0;
1695 unsigned long int sts;
1697 unsigned short int dsc$w_length;
1698 unsigned char dsc$b_type;
1699 unsigned char dsc$b_class;
1700 const char * dsc$a_pointer;
1701 } usrdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
1702 txtdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1703 struct itmlst_3 uailst[3] = {
1704 { sizeof alg, UAI$_ENCRYPT, &alg, 0},
1705 { sizeof salt, UAI$_SALT, &salt, 0},
1706 { 0, 0, NULL, NULL}};
1707 static char hash[9];
1709 usrdsc.dsc$w_length = strlen(usrname);
1710 usrdsc.dsc$a_pointer = usrname;
1711 if (!((sts = sys$getuai(0, 0, &usrdsc, uailst, 0, 0, 0)) & 1)) {
1713 case SS$_NOGRPPRV: case SS$_NOSYSPRV:
1717 set_errno(ESRCH); /* There isn't a Unix no-such-user error */
1722 set_vaxc_errno(sts);
1723 if (sts != RMS$_RNF) return NULL;
1726 txtdsc.dsc$w_length = strlen(textpasswd);
1727 txtdsc.dsc$a_pointer = textpasswd;
1728 if (!((sts = sys$hash_password(&txtdsc, alg, salt, &usrdsc, &hash)) & 1)) {
1729 set_errno(EVMSERR); set_vaxc_errno(sts); return NULL;
1732 return (char *) hash;
1734 } /* end of my_crypt() */
1738 static char *mp_do_rmsexpand(pTHX_ const char *, char *, int, const char *, unsigned, int *, int *);
1739 static char *mp_do_fileify_dirspec(pTHX_ const char *, char *, int, int *);
1740 static char *mp_do_tovmsspec(pTHX_ const char *, char *, int, int, int *);
1742 /* 8.3, remove() is now broken on symbolic links */
1743 static int rms_erase(const char * vmsname);
1747 * A little hack to get around a bug in some implementation of remove()
1748 * that do not know how to delete a directory
1750 * Delete any file to which user has control access, regardless of whether
1751 * delete access is explicitly allowed.
1752 * Limitations: User must have write access to parent directory.
1753 * Does not block signals or ASTs; if interrupted in midstream
1754 * may leave file with an altered ACL.
1757 /*{{{int mp_do_kill_file(const char *name, int dirflag)*/
1759 mp_do_kill_file(pTHX_ const char *name, int dirflag)
1763 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
1764 unsigned long int cxt = 0, aclsts, fndsts;
1766 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1768 unsigned char myace$b_length;
1769 unsigned char myace$b_type;
1770 unsigned short int myace$w_flags;
1771 unsigned long int myace$l_access;
1772 unsigned long int myace$l_ident;
1773 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
1774 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0},
1775 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
1777 findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0},
1778 {sizeof oldace, ACL$C_READACE, &oldace, 0},{0,0,0,0}},
1779 addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}},
1780 dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}},
1781 lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}},
1782 ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}};
1784 /* Expand the input spec using RMS, since the CRTL remove() and
1785 * system services won't do this by themselves, so we may miss
1786 * a file "hiding" behind a logical name or search list. */
1787 vmsname = (char *)PerlMem_malloc(NAM$C_MAXRSS+1);
1788 if (vmsname == NULL) _ckvmssts_noperl(SS$_INSFMEM);
1790 rslt = int_rmsexpand_tovms(name, vmsname, PERL_RMSEXPAND_M_SYMLINK);
1792 PerlMem_free(vmsname);
1796 /* Erase the file */
1797 rmsts = rms_erase(vmsname);
1799 /* Did it succeed */
1800 if ($VMS_STATUS_SUCCESS(rmsts)) {
1801 PerlMem_free(vmsname);
1805 /* If not, can changing protections help? */
1806 if (rmsts != RMS$_PRV) {
1807 set_vaxc_errno(rmsts);
1808 PerlMem_free(vmsname);
1812 /* No, so we get our own UIC to use as a rights identifier,
1813 * and the insert an ACE at the head of the ACL which allows us
1814 * to delete the file.
1816 _ckvmssts_noperl(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
1817 fildsc.dsc$w_length = strlen(vmsname);
1818 fildsc.dsc$a_pointer = vmsname;
1820 newace.myace$l_ident = oldace.myace$l_ident;
1822 if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) {
1824 case RMS$_FNF: case RMS$_DNF: case SS$_NOSUCHOBJECT:
1825 set_errno(ENOENT); break;
1827 set_errno(ENOTDIR); break;
1829 set_errno(ENODEV); break;
1830 case RMS$_SYN: case SS$_INVFILFOROP:
1831 set_errno(EINVAL); break;
1833 set_errno(EACCES); break;
1835 _ckvmssts_noperl(aclsts);
1837 set_vaxc_errno(aclsts);
1838 PerlMem_free(vmsname);
1841 /* Grab any existing ACEs with this identifier in case we fail */
1842 aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt);
1843 if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY
1844 || fndsts == SS$_NOMOREACE ) {
1845 /* Add the new ACE . . . */
1846 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1))
1849 rmsts = rms_erase(vmsname);
1850 if ($VMS_STATUS_SUCCESS(rmsts)) {
1855 /* We blew it - dir with files in it, no write priv for
1856 * parent directory, etc. Put things back the way they were. */
1857 if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1))
1860 addlst[0].bufadr = &oldace;
1861 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1))
1868 fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0);
1869 /* We just deleted it, so of course it's not there. Some versions of
1870 * VMS seem to return success on the unlock operation anyhow (after all
1871 * the unlock is successful), but others don't.
1873 if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL;
1874 if (aclsts & 1) aclsts = fndsts;
1875 if (!(aclsts & 1)) {
1877 set_vaxc_errno(aclsts);
1880 PerlMem_free(vmsname);
1883 } /* end of kill_file() */
1887 /*{{{int do_rmdir(char *name)*/
1889 Perl_do_rmdir(pTHX_ const char *name)
1895 /* lstat returns a VMS fileified specification of the name */
1896 /* that is looked up, and also lets verifies that this is a directory */
1898 retval = flex_lstat(name, &st);
1902 /* Due to a historical feature, flex_stat/lstat can not see some */
1903 /* Unix format file names that the rest of the CRTL can see */
1904 /* Fixing that feature will cause some perl tests to fail */
1905 /* So try this one more time. */
1907 retval = lstat(name, &st.crtl_stat);
1911 /* force it to a file spec for the kill file to work. */
1912 ret_spec = do_fileify_dirspec(name, st.st_devnam, 0, NULL);
1913 if (ret_spec == NULL) {
1919 if (!S_ISDIR(st.st_mode)) {
1924 dirfile = st.st_devnam;
1926 /* It may be possible for flex_stat to find a file and vmsify() to */
1927 /* fail with ODS-2 specifications. mp_do_kill_file can not deal */
1928 /* with that case, so fail it */
1929 if (dirfile[0] == 0) {
1934 retval = mp_do_kill_file(aTHX_ dirfile, 1);
1939 } /* end of do_rmdir */
1943 * Delete any file to which user has control access, regardless of whether
1944 * delete access is explicitly allowed.
1945 * Limitations: User must have write access to parent directory.
1946 * Does not block signals or ASTs; if interrupted in midstream
1947 * may leave file with an altered ACL.
1950 /*{{{int kill_file(char *name)*/
1952 Perl_kill_file(pTHX_ const char *name)
1958 /* Convert the filename to VMS format and see if it is a directory */
1959 /* flex_lstat returns a vmsified file specification */
1960 rmsts = flex_lstat(name, &st);
1963 /* Due to a historical feature, flex_stat/lstat can not see some */
1964 /* Unix format file names that the rest of the CRTL can see when */
1965 /* ODS-2 file specifications are in use. */
1966 /* Fixing that feature will cause some perl tests to fail */
1967 /* [.lib.ExtUtils.t]Manifest.t is one of them */
1969 vmsfile = (char *) name; /* cast ok */
1972 vmsfile = st.st_devnam;
1973 if (vmsfile[0] == 0) {
1974 /* It may be possible for flex_stat to find a file and vmsify() */
1975 /* to fail with ODS-2 specifications. mp_do_kill_file can not */
1976 /* deal with that case, so fail it */
1982 /* Remove() is allowed to delete directories, according to the X/Open
1984 * This may need special handling to work with the ACL hacks.
1986 if (S_ISDIR(st.st_mode)) {
1987 rmsts = mp_do_kill_file(aTHX_ vmsfile, 1);
1991 rmsts = mp_do_kill_file(aTHX_ vmsfile, 0);
1993 /* Need to delete all versions ? */
1994 if ((rmsts == 0) && (vms_unlink_all_versions == 1)) {
1997 /* Just use lstat() here as do not need st_dev */
1998 /* and we know that the file is in VMS format or that */
1999 /* because of a historical bug, flex_stat can not see the file */
2000 while (lstat(vmsfile, (stat_t *)&st) == 0) {
2001 rmsts = mp_do_kill_file(aTHX_ vmsfile, 0);
2006 /* Make sure that we do not loop forever */
2017 } /* end of kill_file() */
2021 /*{{{int my_mkdir(char *,Mode_t)*/
2023 Perl_my_mkdir(pTHX_ const char *dir, Mode_t mode)
2025 STRLEN dirlen = strlen(dir);
2027 /* zero length string sometimes gives ACCVIO */
2028 if (dirlen == 0) return -1;
2030 /* CRTL mkdir() doesn't tolerate trailing /, since that implies
2031 * null file name/type. However, it's commonplace under Unix,
2032 * so we'll allow it for a gain in portability.
2034 if (dir[dirlen-1] == '/') {
2035 char *newdir = savepvn(dir,dirlen-1);
2036 int ret = mkdir(newdir,mode);
2040 else return mkdir(dir,mode);
2041 } /* end of my_mkdir */
2044 /*{{{int my_chdir(char *)*/
2046 Perl_my_chdir(pTHX_ const char *dir)
2048 STRLEN dirlen = strlen(dir);
2049 const char *dir1 = dir;
2051 /* POSIX says we should set ENOENT for zero length string. */
2053 SETERRNO(ENOENT, RMS$_DNF);
2057 /* Perl is passing the output of the DCL SHOW DEFAULT with leading spaces.
2058 * This does not work if DECC$EFS_CHARSET is active. Hack it here
2059 * so that existing scripts do not need to be changed.
2061 while ((dirlen > 0) && (*dir1 == ' ')) {
2066 /* some versions of CRTL chdir() doesn't tolerate trailing /, since
2068 * null file name/type. However, it's commonplace under Unix,
2069 * so we'll allow it for a gain in portability.
2071 * '/' is valid when SYS$POSIX_ROOT or POSIX compliant pathnames are active.
2073 if ((dirlen > 1) && (dir1[dirlen-1] == '/')) {
2076 newdir = (char *)PerlMem_malloc(dirlen);
2078 _ckvmssts_noperl(SS$_INSFMEM);
2079 memcpy(newdir, dir1, dirlen-1);
2080 newdir[dirlen-1] = '\0';
2081 ret = chdir(newdir);
2082 PerlMem_free(newdir);
2085 else return chdir(dir1);
2086 } /* end of my_chdir */
2090 /*{{{int my_chmod(char *, mode_t)*/
2092 Perl_my_chmod(pTHX_ const char *file_spec, mode_t mode)
2097 STRLEN speclen = strlen(file_spec);
2099 /* zero length string sometimes gives ACCVIO */
2100 if (speclen == 0) return -1;
2102 /* some versions of CRTL chmod() doesn't tolerate trailing /, since
2103 * that implies null file name/type. However, it's commonplace under Unix,
2104 * so we'll allow it for a gain in portability.
2106 * Tests are showing that chmod() on VMS 8.3 is only accepting directories
2107 * in VMS file.dir notation.
2109 changefile = (char *) file_spec; /* cast ok */
2110 ret = flex_lstat(file_spec, &st);
2113 /* Due to a historical feature, flex_stat/lstat can not see some */
2114 /* Unix format file names that the rest of the CRTL can see when */
2115 /* ODS-2 file specifications are in use. */
2116 /* Fixing that feature will cause some perl tests to fail */
2117 /* [.lib.ExtUtils.t]Manifest.t is one of them */
2121 /* It may be possible to get here with nothing in st_devname */
2122 /* chmod still may work though */
2123 if (st.st_devnam[0] != 0) {
2124 changefile = st.st_devnam;
2127 ret = chmod(changefile, mode);
2129 } /* end of my_chmod */
2133 /*{{{FILE *my_tmpfile()*/
2140 if ((fp = tmpfile())) return fp;
2142 cp = (char *)PerlMem_malloc(L_tmpnam+24);
2143 if (cp == NULL) _ckvmssts_noperl(SS$_INSFMEM);
2145 if (decc_filename_unix_only == 0)
2146 strcpy(cp,"Sys$Scratch:");
2149 tmpnam(cp+strlen(cp));
2150 strcat(cp,".Perltmp");
2151 fp = fopen(cp,"w+","fop=dlt");
2159 * The C RTL's sigaction fails to check for invalid signal numbers so we
2160 * help it out a bit. The docs are correct, but the actual routine doesn't
2161 * do what the docs say it will.
2163 /*{{{int Perl_my_sigaction (pTHX_ int, const struct sigaction*, struct sigaction*);*/
2165 Perl_my_sigaction (pTHX_ int sig, const struct sigaction* act,
2166 struct sigaction* oact)
2168 if (sig == SIGKILL || sig == SIGSTOP || sig == SIGCONT) {
2169 SETERRNO(EINVAL, SS$_INVARG);
2172 return sigaction(sig, act, oact);
2176 #include <errnodef.h>
2178 /* We implement our own kill() using the undocumented system service
2179 sys$sigprc for one of two reasons:
2181 1.) If the kill() in an older CRTL uses sys$forcex, causing the
2182 target process to do a sys$exit, which usually can't be handled
2183 gracefully...certainly not by Perl and the %SIG{} mechanism.
2185 2.) If the kill() in the CRTL can't be called from a signal
2186 handler without disappearing into the ether, i.e., the signal
2187 it purportedly sends is never trapped. Still true as of VMS 7.3.
2189 sys$sigprc has the same parameters as sys$forcex, but throws an exception
2190 in the target process rather than calling sys$exit.
2192 Note that distinguishing SIGSEGV from SIGBUS requires an extra arg
2193 on the ACCVIO condition, which sys$sigprc (and sys$forcex) don't
2194 provide. On VMS 7.0+ this is taken care of by doing sys$sigprc
2195 with condition codes C$_SIG0+nsig*8, catching the exception on the
2196 target process and resignaling with appropriate arguments.
2198 But we don't have that VMS 7.0+ exception handler, so if you
2199 Perl_my_kill(.., SIGSEGV) it will show up as a SIGBUS. Oh well.
2201 Also note that SIGTERM is listed in the docs as being "unimplemented",
2202 yet always seems to be signaled with a VMS condition code of 4 (and
2203 correctly handled for that code). So we hardwire it in.
2205 Unlike the VMS 7.0+ CRTL kill() function, we actually check the signal
2206 number to see if it's valid. So Perl_my_kill(pid,0) returns -1 rather
2207 than signalling with an unrecognized (and unhandled by CRTL) code.
2210 #define _MY_SIG_MAX 28
2213 Perl_sig_to_vmscondition_int(int sig)
2215 static unsigned int sig_code[_MY_SIG_MAX+1] =
2218 SS$_HANGUP, /* 1 SIGHUP */
2219 SS$_CONTROLC, /* 2 SIGINT */
2220 SS$_CONTROLY, /* 3 SIGQUIT */
2221 SS$_RADRMOD, /* 4 SIGILL */
2222 SS$_BREAK, /* 5 SIGTRAP */
2223 SS$_OPCCUS, /* 6 SIGABRT */
2224 SS$_COMPAT, /* 7 SIGEMT */
2225 SS$_HPARITH, /* 8 SIGFPE AXP */
2226 SS$_ABORT, /* 9 SIGKILL */
2227 SS$_ACCVIO, /* 10 SIGBUS */
2228 SS$_ACCVIO, /* 11 SIGSEGV */
2229 SS$_BADPARAM, /* 12 SIGSYS */
2230 SS$_NOMBX, /* 13 SIGPIPE */
2231 SS$_ASTFLT, /* 14 SIGALRM */
2248 static int initted = 0;
2251 sig_code[16] = C$_SIGUSR1;
2252 sig_code[17] = C$_SIGUSR2;
2253 sig_code[20] = C$_SIGCHLD;
2254 sig_code[28] = C$_SIGWINCH;
2257 if (sig < _SIG_MIN) return 0;
2258 if (sig > _MY_SIG_MAX) return 0;
2259 return sig_code[sig];
2263 Perl_sig_to_vmscondition(int sig)
2266 if (vms_debug_on_exception != 0)
2267 lib$signal(SS$_DEBUG);
2269 return Perl_sig_to_vmscondition_int(sig);
2273 #ifdef KILL_BY_SIGPRC
2274 #define sys$sigprc SYS$SIGPRC
2278 int sys$sigprc(unsigned int *pidadr,
2279 struct dsc$descriptor_s *prcname,
2286 Perl_my_kill(int pid, int sig)
2291 /* sig 0 means validate the PID */
2292 /*------------------------------*/
2294 const unsigned long int jpicode = JPI$_PID;
2297 status = lib$getjpi(&jpicode, &pid, NULL, &ret_pid, NULL, NULL);
2298 if ($VMS_STATUS_SUCCESS(status))
2301 case SS$_NOSUCHNODE:
2302 case SS$_UNREACHABLE:
2316 code = Perl_sig_to_vmscondition_int(sig);
2319 SETERRNO(EINVAL, SS$_BADPARAM);
2323 /* Per official UNIX specification: If pid = 0, or negative then
2324 * signals are to be sent to multiple processes.
2325 * pid = 0 - all processes in group except ones that the system exempts
2326 * pid = -1 - all processes except ones that the system exempts
2327 * pid = -n - all processes in group (abs(n)) except ...
2329 * Handle these via killpg, which is redundant for the -n case, since OP_KILL
2330 * in doio.c already does that. killpg currently does not support the -1 case.
2334 return killpg(-pid, sig);
2337 iss = sys$sigprc((unsigned int *)&pid,0,code);
2338 if (iss&1) return 0;
2342 set_errno(EPERM); break;
2344 case SS$_NOSUCHNODE:
2345 case SS$_UNREACHABLE:
2346 set_errno(ESRCH); break;
2348 set_errno(ENOMEM); break;
2350 _ckvmssts_noperl(iss);
2353 set_vaxc_errno(iss);
2360 Perl_my_killpg(pid_t master_pid, int signum)
2363 unsigned long int jpi_context;
2364 unsigned short int iosb[4];
2365 struct itmlst_3 il3[3];
2367 /* All processes on the system? Seems dangerous, but it looks
2368 * like we could implement this pretty easily with a wildcard
2369 * input to sys$process_scan.
2371 if (master_pid == -1) {
2372 SETERRNO(ENOTSUP, SS$_UNSUPPORTED);
2376 /* All processes in the current process group; find the master
2377 * pid for the current process.
2379 if (master_pid == 0) {
2381 il3[i].buflen = sizeof( int );
2382 il3[i].itmcode = JPI$_MASTER_PID;
2383 il3[i].bufadr = &master_pid;
2384 il3[i++].retlen = NULL;
2388 il3[i].bufadr = NULL;
2389 il3[i++].retlen = NULL;
2391 status = sys$getjpiw(EFN$C_ENF, NULL, NULL, il3, iosb, NULL, 0);
2392 if ($VMS_STATUS_SUCCESS(status))
2400 SETERRNO(EPERM, status);
2402 case SS$_NOMOREPROC:
2404 case SS$_NOSUCHNODE:
2405 case SS$_UNREACHABLE:
2406 SETERRNO(ESRCH, status);
2410 SETERRNO(EINVAL, status);
2413 SETERRNO(EVMSERR, status);
2415 if (!$VMS_STATUS_SUCCESS(status))
2419 /* Set up a process context for those processes we will scan
2420 * with sys$getjpiw. Ask for all processes belonging to the
2426 il3[i].itmcode = PSCAN$_MASTER_PID;
2427 il3[i].bufadr = (void *)master_pid;
2428 il3[i++].retlen = NULL;
2432 il3[i].bufadr = NULL;
2433 il3[i++].retlen = NULL;
2435 status = sys$process_scan(&jpi_context, il3);
2443 SETERRNO(EINVAL, status);
2446 SETERRNO(EVMSERR, status);
2448 if (!$VMS_STATUS_SUCCESS(status))
2452 il3[i].buflen = sizeof(int);
2453 il3[i].itmcode = JPI$_PID;
2454 il3[i].bufadr = &pid;
2455 il3[i++].retlen = NULL;
2459 il3[i].bufadr = NULL;
2460 il3[i++].retlen = NULL;
2462 /* Loop through the processes matching our specified criteria
2466 /* Find the next process...
2468 status = sys$getjpiw( EFN$C_ENF, &jpi_context, NULL, il3, iosb, NULL, 0);
2469 if ($VMS_STATUS_SUCCESS(status)) status = iosb[0];
2473 if (kill(pid, signum) == -1)
2476 continue; /* next process */
2479 SETERRNO(EPERM, status);
2481 case SS$_NOMOREPROC:
2484 case SS$_NOSUCHNODE:
2485 case SS$_UNREACHABLE:
2486 SETERRNO(ESRCH, status);
2490 SETERRNO(EINVAL, status);
2493 SETERRNO(EVMSERR, status);
2496 if (!$VMS_STATUS_SUCCESS(status))
2500 /* Release context-related resources.
2502 (void) sys$process_scan(&jpi_context);
2504 if (status != SS$_NOMOREPROC)
2510 /* Routine to convert a VMS status code to a UNIX status code.
2511 ** More tricky than it appears because of conflicting conventions with
2514 ** VMS status codes are a bit mask, with the least significant bit set for
2517 ** Special UNIX status of EVMSERR indicates that no translation is currently
2518 ** available, and programs should check the VMS status code.
2520 ** Programs compiled with _POSIX_EXIT have a special encoding that requires
2524 #ifndef C_FACILITY_NO
2525 #define C_FACILITY_NO 0x350000
2528 #define DCL_IVVERB 0x38090
2532 Perl_vms_status_to_unix(int vms_status, int child_flag)
2540 /* Assume the best or the worst */
2541 if (vms_status & STS$M_SUCCESS)
2544 unix_status = EVMSERR;
2546 msg_status = vms_status & ~STS$M_CONTROL;
2548 facility = vms_status & STS$M_FAC_NO;
2549 fac_sp = vms_status & STS$M_FAC_SP;
2550 msg_no = vms_status & (STS$M_MSG_NO | STS$M_SEVERITY);
2552 if (((facility == 0) || (fac_sp == 0)) && (child_flag == 0)) {
2558 unix_status = EFAULT;
2560 case SS$_DEVOFFLINE:
2561 unix_status = EBUSY;
2564 unix_status = ENOTCONN;
2572 case SS$_INVFILFOROP:
2576 unix_status = EINVAL;
2578 case SS$_UNSUPPORTED:
2579 unix_status = ENOTSUP;
2584 unix_status = EACCES;
2586 case SS$_DEVICEFULL:
2587 unix_status = ENOSPC;
2590 unix_status = ENODEV;
2592 case SS$_NOSUCHFILE:
2593 case SS$_NOSUCHOBJECT:
2594 unix_status = ENOENT;
2596 case SS$_ABORT: /* Fatal case */
2597 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_ERROR): /* Error case */
2598 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_WARNING): /* Warning case */
2599 unix_status = EINTR;
2602 unix_status = E2BIG;
2605 unix_status = ENOMEM;
2608 unix_status = EPERM;
2610 case SS$_NOSUCHNODE:
2611 case SS$_UNREACHABLE:
2612 unix_status = ESRCH;
2615 unix_status = ECHILD;
2618 if ((facility == 0) && (msg_no < 8)) {
2619 /* These are not real VMS status codes so assume that they are
2620 ** already UNIX status codes
2622 unix_status = msg_no;
2628 /* Translate a POSIX exit code to a UNIX exit code */
2629 if ((facility == C_FACILITY_NO) && ((msg_no & 0xA000) == 0xA000)) {
2630 unix_status = (msg_no & 0x07F8) >> 3;
2634 /* Documented traditional behavior for handling VMS child exits */
2635 /*--------------------------------------------------------------*/
2636 if (child_flag != 0) {
2638 /* Success / Informational return 0 */
2639 /*----------------------------------*/
2640 if (msg_no & STS$K_SUCCESS)
2643 /* Warning returns 1 */
2644 /*-------------------*/
2645 if ((msg_no & (STS$K_ERROR | STS$K_SEVERE)) == 0)
2648 /* Everything else pass through the severity bits */
2649 /*------------------------------------------------*/
2650 return (msg_no & STS$M_SEVERITY);
2653 /* Normal VMS status to ERRNO mapping attempt */
2654 /*--------------------------------------------*/
2655 switch(msg_status) {
2656 /* case RMS$_EOF: */ /* End of File */
2657 case RMS$_FNF: /* File Not Found */
2658 case RMS$_DNF: /* Dir Not Found */
2659 unix_status = ENOENT;
2661 case RMS$_RNF: /* Record Not Found */
2662 unix_status = ESRCH;
2665 unix_status = ENOTDIR;
2668 unix_status = ENODEV;
2673 unix_status = EBADF;
2676 unix_status = EEXIST;
2680 case LIB$_INVSTRDES:
2682 case LIB$_NOSUCHSYM:
2683 case LIB$_INVSYMNAM:
2685 unix_status = EINVAL;
2691 unix_status = E2BIG;
2693 case RMS$_PRV: /* No privilege */
2694 case RMS$_ACC: /* ACP file access failed */
2695 case RMS$_WLK: /* Device write locked */
2696 unix_status = EACCES;
2698 case RMS$_MKD: /* Failed to mark for delete */
2699 unix_status = EPERM;
2701 /* case RMS$_NMF: */ /* No more files */
2709 /* Try to guess at what VMS error status should go with a UNIX errno
2710 * value. This is hard to do as there could be many possible VMS
2711 * error statuses that caused the errno value to be set.
2715 Perl_unix_status_to_vms(int unix_status)
2717 int test_unix_status;
2719 /* Trivial cases first */
2720 /*---------------------*/
2721 if (unix_status == EVMSERR)
2724 /* Is vaxc$errno sane? */
2725 /*---------------------*/
2726 test_unix_status = Perl_vms_status_to_unix(vaxc$errno, 0);
2727 if (test_unix_status == unix_status)
2730 /* If way out of range, must be VMS code already */
2731 /*-----------------------------------------------*/
2732 if (unix_status > EVMSERR)
2735 /* If out of range, punt */
2736 /*-----------------------*/
2737 if (unix_status > __ERRNO_MAX)
2741 /* Ok, now we have to do it the hard way. */
2742 /*----------------------------------------*/
2743 switch(unix_status) {
2744 case 0: return SS$_NORMAL;
2745 case EPERM: return SS$_NOPRIV;
2746 case ENOENT: return SS$_NOSUCHOBJECT;
2747 case ESRCH: return SS$_UNREACHABLE;
2748 case EINTR: return SS$_ABORT;
2751 case E2BIG: return SS$_BUFFEROVF;
2753 case EBADF: return RMS$_IFI;
2754 case ECHILD: return SS$_NONEXPR;
2756 case ENOMEM: return SS$_INSFMEM;
2757 case EACCES: return SS$_FILACCERR;
2758 case EFAULT: return SS$_ACCVIO;
2760 case EBUSY: return SS$_DEVOFFLINE;
2761 case EEXIST: return RMS$_FEX;
2763 case ENODEV: return SS$_NOSUCHDEV;
2764 case ENOTDIR: return RMS$_DIR;
2766 case EINVAL: return SS$_INVARG;
2772 case ENOSPC: return SS$_DEVICEFULL;
2773 case ESPIPE: return LIB$_INVARG;
2778 case ERANGE: return LIB$_INVARG;
2779 /* case EWOULDBLOCK */
2780 /* case EINPROGRESS */
2783 /* case EDESTADDRREQ */
2785 /* case EPROTOTYPE */
2786 /* case ENOPROTOOPT */
2787 /* case EPROTONOSUPPORT */
2788 /* case ESOCKTNOSUPPORT */
2789 /* case EOPNOTSUPP */
2790 /* case EPFNOSUPPORT */
2791 /* case EAFNOSUPPORT */
2792 /* case EADDRINUSE */
2793 /* case EADDRNOTAVAIL */
2795 /* case ENETUNREACH */
2796 /* case ENETRESET */
2797 /* case ECONNABORTED */
2798 /* case ECONNRESET */
2801 case ENOTCONN: return SS$_CLEARED;
2802 /* case ESHUTDOWN */
2803 /* case ETOOMANYREFS */
2804 /* case ETIMEDOUT */
2805 /* case ECONNREFUSED */
2807 /* case ENAMETOOLONG */
2808 /* case EHOSTDOWN */
2809 /* case EHOSTUNREACH */
2810 /* case ENOTEMPTY */
2822 /* case ECANCELED */
2826 return SS$_UNSUPPORTED;
2832 /* case EABANDONED */
2834 return SS$_ABORT; /* punt */
2839 /* default piping mailbox size */
2840 #define PERL_BUFSIZ 8192
2844 create_mbx(unsigned short int *chan, struct dsc$descriptor_s *namdsc)
2846 unsigned long int mbxbufsiz;
2847 static unsigned long int syssize = 0;
2848 unsigned long int dviitm = DVI$_DEVNAM;
2849 char csize[LNM$C_NAMLENGTH+1];
2853 unsigned long syiitm = SYI$_MAXBUF;
2855 * Get the SYSGEN parameter MAXBUF
2857 * If the logical 'PERL_MBX_SIZE' is defined
2858 * use the value of the logical instead of PERL_BUFSIZ, but
2859 * keep the size between 128 and MAXBUF.
2862 _ckvmssts_noperl(lib$getsyi(&syiitm, &syssize, 0, 0, 0, 0));
2865 if (vmstrnenv("PERL_MBX_SIZE", csize, 0, fildev, 0)) {
2866 mbxbufsiz = atoi(csize);
2868 mbxbufsiz = PERL_BUFSIZ;
2870 if (mbxbufsiz < 128) mbxbufsiz = 128;
2871 if (mbxbufsiz > syssize) mbxbufsiz = syssize;
2873 _ckvmssts_noperl(sts = sys$crembx(0,chan,mbxbufsiz,mbxbufsiz,0,0,0));
2875 sts = lib$getdvi(&dviitm, chan, NULL, NULL, namdsc, &namdsc->dsc$w_length);
2876 _ckvmssts_noperl(sts);
2877 namdsc->dsc$a_pointer[namdsc->dsc$w_length] = '\0';
2879 } /* end of create_mbx() */
2882 /*{{{ my_popen and my_pclose*/
2884 typedef struct _iosb IOSB;
2885 typedef struct _iosb* pIOSB;
2886 typedef struct _pipe Pipe;
2887 typedef struct _pipe* pPipe;
2888 typedef struct pipe_details Info;
2889 typedef struct pipe_details* pInfo;
2890 typedef struct _srqp RQE;
2891 typedef struct _srqp* pRQE;
2892 typedef struct _tochildbuf CBuf;
2893 typedef struct _tochildbuf* pCBuf;
2896 unsigned short status;
2897 unsigned short count;
2898 unsigned long dvispec;
2901 #pragma member_alignment save
2902 #pragma nomember_alignment quadword
2903 struct _srqp { /* VMS self-relative queue entry */
2904 unsigned long qptr[2];
2906 #pragma member_alignment restore
2907 static RQE RQE_ZERO = {0,0};
2909 struct _tochildbuf {
2912 unsigned short size;
2920 unsigned short chan_in;
2921 unsigned short chan_out;
2923 unsigned int bufsize;
2935 #if defined(PERL_IMPLICIT_CONTEXT)
2936 void *thx; /* Either a thread or an interpreter */
2937 /* pointer, depending on how we're built */
2945 PerlIO *fp; /* file pointer to pipe mailbox */
2946 int useFILE; /* using stdio, not perlio */
2947 int pid; /* PID of subprocess */
2948 int mode; /* == 'r' if pipe open for reading */
2949 int done; /* subprocess has completed */
2950 int waiting; /* waiting for completion/closure */
2951 int closing; /* my_pclose is closing this pipe */
2952 unsigned long completion; /* termination status of subprocess */
2953 pPipe in; /* pipe in to sub */
2954 pPipe out; /* pipe out of sub */
2955 pPipe err; /* pipe of sub's sys$error */
2956 int in_done; /* true when in pipe finished */
2959 unsigned short xchan; /* channel to debug xterm */
2960 unsigned short xchan_valid; /* channel is assigned */
2963 struct exit_control_block
2965 struct exit_control_block *flink;
2966 unsigned long int (*exit_routine)(void);
2967 unsigned long int arg_count;
2968 unsigned long int *status_address;
2969 unsigned long int exit_status;
2972 typedef struct _closed_pipes Xpipe;
2973 typedef struct _closed_pipes* pXpipe;
2975 struct _closed_pipes {
2976 int pid; /* PID of subprocess */
2977 unsigned long completion; /* termination status of subprocess */
2979 #define NKEEPCLOSED 50
2980 static Xpipe closed_list[NKEEPCLOSED];
2981 static int closed_index = 0;
2982 static int closed_num = 0;
2984 #define RETRY_DELAY "0 ::0.20"
2985 #define MAX_RETRY 50
2987 static int pipe_ef = 0; /* first call to safe_popen inits these*/
2988 static unsigned long mypid;
2989 static unsigned long delaytime[2];
2991 static pInfo open_pipes = NULL;
2992 static $DESCRIPTOR(nl_desc, "NL:");
2994 #define PIPE_COMPLETION_WAIT 30 /* seconds, for EOF/FORCEX wait */
2998 static unsigned long int
2999 pipe_exit_routine(void)
3002 unsigned long int retsts = SS$_NORMAL, abort = SS$_TIMEOUT;
3003 int sts, did_stuff, j;
3006 * Flush any pending i/o, but since we are in process run-down, be
3007 * careful about referencing PerlIO structures that may already have
3008 * been deallocated. We may not even have an interpreter anymore.
3013 #if defined(PERL_IMPLICIT_CONTEXT)
3014 /* We need to use the Perl context of the thread that created */
3018 aTHX = info->err->thx;
3020 aTHX = info->out->thx;
3022 aTHX = info->in->thx;
3025 #if defined(USE_ITHREADS)
3029 && PL_perlio_fd_refcnt
3032 PerlIO_flush(info->fp);
3034 fflush((FILE *)info->fp);
3040 next we try sending an EOF...ignore if doesn't work, make sure we
3047 _ckvmssts_noperl(sys$setast(0));
3048 if (info->in && !info->in->shut_on_empty) {
3049 _ckvmssts_noperl(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
3054 _ckvmssts_noperl(sys$setast(1));
3058 /* wait for EOF to have effect, up to ~ 30 sec [default] */
3060 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
3065 _ckvmssts_noperl(sys$setast(0));
3066 if (info->waiting && info->done)
3068 nwait += info->waiting;
3069 _ckvmssts_noperl(sys$setast(1));
3079 _ckvmssts_noperl(sys$setast(0));
3080 if (!info->done) { /* Tap them gently on the shoulder . . .*/
3081 sts = sys$forcex(&info->pid,0,&abort);
3082 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
3085 _ckvmssts_noperl(sys$setast(1));
3089 /* again, wait for effect */
3091 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
3096 _ckvmssts_noperl(sys$setast(0));
3097 if (info->waiting && info->done)
3099 nwait += info->waiting;
3100 _ckvmssts_noperl(sys$setast(1));
3109 _ckvmssts_noperl(sys$setast(0));
3110 if (!info->done) { /* We tried to be nice . . . */
3111 sts = sys$delprc(&info->pid,0);
3112 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
3113 info->done = 1; /* sys$delprc is as done as we're going to get. */
3115 _ckvmssts_noperl(sys$setast(1));
3121 #if defined(PERL_IMPLICIT_CONTEXT)
3122 /* We need to use the Perl context of the thread that created */
3125 if (open_pipes->err)
3126 aTHX = open_pipes->err->thx;
3127 else if (open_pipes->out)
3128 aTHX = open_pipes->out->thx;
3129 else if (open_pipes->in)
3130 aTHX = open_pipes->in->thx;
3132 if ((sts = my_pclose(open_pipes->fp)) == -1) retsts = vaxc$errno;
3133 else if (!(sts & 1)) retsts = sts;
3138 static struct exit_control_block pipe_exitblock =
3139 {(struct exit_control_block *) 0,
3140 pipe_exit_routine, 0, &pipe_exitblock.exit_status, 0};
3142 static void pipe_mbxtofd_ast(pPipe p);
3143 static void pipe_tochild1_ast(pPipe p);
3144 static void pipe_tochild2_ast(pPipe p);
3147 popen_completion_ast(pInfo info)
3149 pInfo i = open_pipes;
3152 info->completion &= 0x0FFFFFFF; /* strip off "control" field */
3153 closed_list[closed_index].pid = info->pid;
3154 closed_list[closed_index].completion = info->completion;
3156 if (closed_index == NKEEPCLOSED)
3161 if (i == info) break;
3164 if (!i) return; /* unlinked, probably freed too */
3169 Writing to subprocess ...
3170 if my_pclose'd: EOF already sent, should shutdown chan_in part of pipe
3172 chan_out may be waiting for "done" flag, or hung waiting
3173 for i/o completion to child...cancel the i/o. This will
3174 put it into "snarf mode" (done but no EOF yet) that discards
3177 Output from subprocess (stdout, stderr) needs to be flushed and
3178 shut down. We try sending an EOF, but if the mbx is full the pipe
3179 routine should still catch the "shut_on_empty" flag, telling it to
3180 use immediate-style reads so that "mbx empty" -> EOF.
3184 if (info->in && !info->in_done) { /* only for mode=w */
3185 if (info->in->shut_on_empty && info->in->need_wake) {
3186 info->in->need_wake = FALSE;
3187 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,info->in,0));
3189 _ckvmssts_noperl(sys$cancel(info->in->chan_out));
3193 if (info->out && !info->out_done) { /* were we also piping output? */
3194 info->out->shut_on_empty = TRUE;
3195 iss = sys$qio(0,info->out->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
3196 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
3197 _ckvmssts_noperl(iss);
3200 if (info->err && !info->err_done) { /* we were piping stderr */
3201 info->err->shut_on_empty = TRUE;
3202 iss = sys$qio(0,info->err->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
3203 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
3204 _ckvmssts_noperl(iss);
3206 _ckvmssts_noperl(sys$setef(pipe_ef));
3210 static unsigned long int setup_cmddsc(pTHX_ const char *cmd, int check_img, int *suggest_quote, struct dsc$descriptor_s **pvmscmd);
3211 static void vms_execfree(struct dsc$descriptor_s *vmscmd);
3212 static void pipe_infromchild_ast(pPipe p);
3215 I'm using LIB$(GET|FREE)_VM here so that we can allocate and deallocate
3216 inside an AST routine without worrying about reentrancy and which Perl
3217 memory allocator is being used.
3219 We read data and queue up the buffers, then spit them out one at a
3220 time to the output mailbox when the output mailbox is ready for one.
3223 #define INITIAL_TOCHILDQUEUE 2
3226 pipe_tochild_setup(pTHX_ char *rmbx, char *wmbx)
3230 char mbx1[64], mbx2[64];
3231 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3232 DSC$K_CLASS_S, mbx1},
3233 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
3234 DSC$K_CLASS_S, mbx2};
3235 unsigned int dviitm = DVI$_DEVBUFSIZ;
3239 _ckvmssts_noperl(lib$get_vm(&n, &p));
3241 create_mbx(&p->chan_in , &d_mbx1);
3242 create_mbx(&p->chan_out, &d_mbx2);
3243 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3246 p->shut_on_empty = FALSE;
3247 p->need_wake = FALSE;
3250 p->iosb.status = SS$_NORMAL;
3251 p->iosb2.status = SS$_NORMAL;
3257 #ifdef PERL_IMPLICIT_CONTEXT
3261 n = sizeof(CBuf) + p->bufsize;
3263 for (j = 0; j < INITIAL_TOCHILDQUEUE; j++) {
3264 _ckvmssts_noperl(lib$get_vm(&n, &b));
3265 b->buf = (char *) b + sizeof(CBuf);
3266 _ckvmssts_noperl(lib$insqhi(b, &p->free));
3269 pipe_tochild2_ast(p);
3270 pipe_tochild1_ast(p);
3276 /* reads the MBX Perl is writing, and queues */
3279 pipe_tochild1_ast(pPipe p)
3282 int iss = p->iosb.status;
3283 int eof = (iss == SS$_ENDOFFILE);
3285 #ifdef PERL_IMPLICIT_CONTEXT
3291 p->shut_on_empty = TRUE;
3293 _ckvmssts_noperl(sys$dassgn(p->chan_in));
3295 _ckvmssts_noperl(iss);
3299 b->size = p->iosb.count;
3300 _ckvmssts_noperl(sts = lib$insqhi(b, &p->wait));
3302 p->need_wake = FALSE;
3303 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,p,0));
3306 p->retry = 1; /* initial call */
3309 if (eof) { /* flush the free queue, return when done */
3310 int n = sizeof(CBuf) + p->bufsize;
3312 iss = lib$remqti(&p->free, &b);
3313 if (iss == LIB$_QUEWASEMP) return;
3314 _ckvmssts_noperl(iss);
3315 _ckvmssts_noperl(lib$free_vm(&n, &b));
3319 iss = lib$remqti(&p->free, &b);
3320 if (iss == LIB$_QUEWASEMP) {
3321 int n = sizeof(CBuf) + p->bufsize;
3322 _ckvmssts_noperl(lib$get_vm(&n, &b));
3323 b->buf = (char *) b + sizeof(CBuf);
3325 _ckvmssts_noperl(iss);
3329 iss = sys$qio(0,p->chan_in,
3330 IO$_READVBLK|(p->shut_on_empty ? IO$M_NOWAIT : 0),
3332 pipe_tochild1_ast, p, b->buf, p->bufsize, 0, 0, 0, 0);
3333 if (iss == SS$_ENDOFFILE && p->shut_on_empty) iss = SS$_NORMAL;
3334 _ckvmssts_noperl(iss);
3338 /* writes queued buffers to output, waits for each to complete before
3342 pipe_tochild2_ast(pPipe p)
3345 int iss = p->iosb2.status;
3346 int n = sizeof(CBuf) + p->bufsize;
3347 int done = (p->info && p->info->done) ||
3348 iss == SS$_CANCEL || iss == SS$_ABORT;
3349 #if defined(PERL_IMPLICIT_CONTEXT)
3354 if (p->type) { /* type=1 has old buffer, dispose */
3355 if (p->shut_on_empty) {
3356 _ckvmssts_noperl(lib$free_vm(&n, &b));
3358 _ckvmssts_noperl(lib$insqhi(b, &p->free));
3363 iss = lib$remqti(&p->wait, &b);
3364 if (iss == LIB$_QUEWASEMP) {
3365 if (p->shut_on_empty) {
3367 _ckvmssts_noperl(sys$dassgn(p->chan_out));
3368 *p->pipe_done = TRUE;
3369 _ckvmssts_noperl(sys$setef(pipe_ef));
3371 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF,
3372 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3376 p->need_wake = TRUE;
3379 _ckvmssts_noperl(iss);
3386 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF,
3387 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3389 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEVBLK,
3390 &p->iosb2, pipe_tochild2_ast, p, b->buf, b->size, 0, 0, 0, 0));
3399 pipe_infromchild_setup(pTHX_ char *rmbx, char *wmbx)
3402 char mbx1[64], mbx2[64];
3403 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3404 DSC$K_CLASS_S, mbx1},
3405 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
3406 DSC$K_CLASS_S, mbx2};
3407 unsigned int dviitm = DVI$_DEVBUFSIZ;
3409 int n = sizeof(Pipe);
3410 _ckvmssts_noperl(lib$get_vm(&n, &p));
3411 create_mbx(&p->chan_in , &d_mbx1);
3412 create_mbx(&p->chan_out, &d_mbx2);
3414 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3415 n = p->bufsize * sizeof(char);
3416 _ckvmssts_noperl(lib$get_vm(&n, &p->buf));
3417 p->shut_on_empty = FALSE;
3420 p->iosb.status = SS$_NORMAL;
3421 #if defined(PERL_IMPLICIT_CONTEXT)
3424 pipe_infromchild_ast(p);
3432 pipe_infromchild_ast(pPipe p)
3434 int iss = p->iosb.status;
3435 int eof = (iss == SS$_ENDOFFILE);
3436 int myeof = (eof && (p->iosb.dvispec == mypid || p->iosb.dvispec == 0));
3437 int kideof = (eof && (p->iosb.dvispec == p->info->pid));
3438 #if defined(PERL_IMPLICIT_CONTEXT)
3442 if (p->info && p->info->closing && p->chan_out) { /* output shutdown */
3443 _ckvmssts_noperl(sys$dassgn(p->chan_out));
3448 input shutdown if EOF from self (done or shut_on_empty)
3449 output shutdown if closing flag set (my_pclose)
3450 send data/eof from child or eof from self
3451 otherwise, re-read (snarf of data from child)
3456 if (myeof && p->chan_in) { /* input shutdown */
3457 _ckvmssts_noperl(sys$dassgn(p->chan_in));
3462 if (myeof || kideof) { /* pass EOF to parent */
3463 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF, &p->iosb,
3464 pipe_infromchild_ast, p,
3467 } else if (eof) { /* eat EOF --- fall through to read*/
3469 } else { /* transmit data */
3470 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEVBLK,&p->iosb,
3471 pipe_infromchild_ast,p,
3472 p->buf, p->iosb.count, 0, 0, 0, 0));
3478 /* everything shut? flag as done */
3480 if (!p->chan_in && !p->chan_out) {
3481 *p->pipe_done = TRUE;
3482 _ckvmssts_noperl(sys$setef(pipe_ef));
3486 /* write completed (or read, if snarfing from child)
3487 if still have input active,
3488 queue read...immediate mode if shut_on_empty so we get EOF if empty
3490 check if Perl reading, generate EOFs as needed
3496 iss = sys$qio(0,p->chan_in,IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0),&p->iosb,
3497 pipe_infromchild_ast,p,
3498 p->buf, p->bufsize, 0, 0, 0, 0);
3499 if (p->shut_on_empty && iss == SS$_ENDOFFILE) iss = SS$_NORMAL;
3500 _ckvmssts_noperl(iss);
3501 } else { /* send EOFs for extra reads */
3502 p->iosb.status = SS$_ENDOFFILE;
3503 p->iosb.dvispec = 0;
3504 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_SETMODE|IO$M_READATTN,
3506 pipe_infromchild_ast, p, 0, 0, 0, 0));
3512 pipe_mbxtofd_setup(pTHX_ int fd, char *out)
3516 unsigned long dviitm = DVI$_DEVBUFSIZ;
3518 struct dsc$descriptor_s d_mbx = {sizeof mbx, DSC$K_DTYPE_T,
3519 DSC$K_CLASS_S, mbx};
3520 int n = sizeof(Pipe);
3522 /* things like terminals and mbx's don't need this filter */
3523 if (fd && fstat(fd,&s) == 0) {
3524 unsigned long devchar;
3526 unsigned short dev_len;
3527 struct dsc$descriptor_s d_dev;
3529 struct item_list_3 items[3];
3531 unsigned short dvi_iosb[4];
3533 cptr = getname(fd, out, 1);
3534 if (cptr == NULL) _ckvmssts_noperl(SS$_NOSUCHDEV);
3535 d_dev.dsc$a_pointer = out;
3536 d_dev.dsc$w_length = strlen(out);
3537 d_dev.dsc$b_dtype = DSC$K_DTYPE_T;
3538 d_dev.dsc$b_class = DSC$K_CLASS_S;
3541 items[0].code = DVI$_DEVCHAR;
3542 items[0].bufadr = &devchar;
3543 items[0].retadr = NULL;
3545 items[1].code = DVI$_FULLDEVNAM;
3546 items[1].bufadr = device;
3547 items[1].retadr = &dev_len;
3551 status = sys$getdviw
3552 (NO_EFN, 0, &d_dev, items, dvi_iosb, NULL, NULL, NULL);
3553 _ckvmssts_noperl(status);
3554 if ($VMS_STATUS_SUCCESS(dvi_iosb[0])) {
3555 device[dev_len] = 0;
3557 if (!(devchar & DEV$M_DIR)) {
3558 strcpy(out, device);
3564 _ckvmssts_noperl(lib$get_vm(&n, &p));
3565 p->fd_out = dup(fd);
3566 create_mbx(&p->chan_in, &d_mbx);
3567 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3568 n = (p->bufsize+1) * sizeof(char);
3569 _ckvmssts_noperl(lib$get_vm(&n, &p->buf));
3570 p->shut_on_empty = FALSE;
3575 _ckvmssts_noperl(sys$qio(0, p->chan_in, IO$_READVBLK, &p->iosb,
3576 pipe_mbxtofd_ast, p,
3577 p->buf, p->bufsize, 0, 0, 0, 0));
3583 pipe_mbxtofd_ast(pPipe p)
3585 int iss = p->iosb.status;
3586 int done = p->info->done;
3588 int eof = (iss == SS$_ENDOFFILE);
3589 int myeof = eof && ((p->iosb.dvispec == mypid)||(p->iosb.dvispec == 0));
3590 int err = !(iss&1) && !eof;
3591 #if defined(PERL_IMPLICIT_CONTEXT)
3595 if (done && myeof) { /* end piping */
3597 sys$dassgn(p->chan_in);
3598 *p->pipe_done = TRUE;
3599 _ckvmssts_noperl(sys$setef(pipe_ef));
3603 if (!err && !eof) { /* good data to send to file */
3604 p->buf[p->iosb.count] = '\n';
3605 iss2 = write(p->fd_out, p->buf, p->iosb.count+1);
3608 if (p->retry < MAX_RETRY) {
3609 _ckvmssts_noperl(sys$setimr(0,delaytime,pipe_mbxtofd_ast,p));
3615 _ckvmssts_noperl(iss);
3619 iss = sys$qio(0, p->chan_in, IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0), &p->iosb,
3620 pipe_mbxtofd_ast, p,
3621 p->buf, p->bufsize, 0, 0, 0, 0);
3622 if (p->shut_on_empty && (iss == SS$_ENDOFFILE)) iss = SS$_NORMAL;
3623 _ckvmssts_noperl(iss);
3627 typedef struct _pipeloc PLOC;
3628 typedef struct _pipeloc* pPLOC;
3632 char dir[NAM$C_MAXRSS+1];
3634 static pPLOC head_PLOC = 0;
3637 free_pipelocs(pTHX_ void *head)
3640 pPLOC *pHead = (pPLOC *)head;
3652 store_pipelocs(pTHX)
3660 char temp[NAM$C_MAXRSS+1];
3664 free_pipelocs(aTHX_ &head_PLOC);
3666 /* the . directory from @INC comes last */
3668 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3669 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3670 p->next = head_PLOC;
3672 strcpy(p->dir,"./");
3674 /* get the directory from $^X */
3676 unixdir = (char *)PerlMem_malloc(VMS_MAXRSS);
3677 if (unixdir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3679 #ifdef PERL_IMPLICIT_CONTEXT
3680 if (aTHX && PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
3682 if (PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
3684 my_strlcpy(temp, PL_origargv[0], sizeof(temp));
3685 x = strrchr(temp,']');
3687 x = strrchr(temp,'>');
3689 /* It could be a UNIX path */
3690 x = strrchr(temp,'/');
3696 /* Got a bare name, so use default directory */
3701 if ((tounixpath_utf8(temp, unixdir, NULL)) != NULL) {
3702 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3703 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3704 p->next = head_PLOC;
3706 my_strlcpy(p->dir, unixdir, sizeof(p->dir));
3710 /* reverse order of @INC entries, skip "." since entered above */
3712 #ifdef PERL_IMPLICIT_CONTEXT
3715 if (PL_incgv) av = GvAVn(PL_incgv);
3717 for (i = 0; av && i <= AvFILL(av); i++) {
3718 dirsv = *av_fetch(av,i,TRUE);
3720 if (SvROK(dirsv)) continue;
3721 dir = SvPVx(dirsv,n_a);
3722 if (strcmp(dir,".") == 0) continue;
3723 if ((tounixpath_utf8(dir, unixdir, NULL)) == NULL)
3726 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3727 p->next = head_PLOC;
3729 my_strlcpy(p->dir, unixdir, sizeof(p->dir));
3732 /* most likely spot (ARCHLIB) put first in the list */
3735 if ((tounixpath_utf8(ARCHLIB_EXP, unixdir, NULL)) != NULL) {
3736 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3737 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3738 p->next = head_PLOC;
3740 my_strlcpy(p->dir, unixdir, sizeof(p->dir));
3743 PerlMem_free(unixdir);
3746 static I32 Perl_cando_by_name_int(pTHX_ I32 bit, bool effective,
3747 const char *fname, int opts);
3748 #if !defined(PERL_IMPLICIT_CONTEXT)
3749 #define cando_by_name_int Perl_cando_by_name_int
3751 #define cando_by_name_int(a,b,c,d) Perl_cando_by_name_int(aTHX_ a,b,c,d)
3757 static int vmspipe_file_status = 0;
3758 static char vmspipe_file[NAM$C_MAXRSS+1];
3760 /* already found? Check and use ... need read+execute permission */
3762 if (vmspipe_file_status == 1) {
3763 if (cando_by_name_int(S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)
3764 && cando_by_name_int
3765 (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) {
3766 return vmspipe_file;
3768 vmspipe_file_status = 0;
3771 /* scan through stored @INC, $^X */
3773 if (vmspipe_file_status == 0) {
3774 char file[NAM$C_MAXRSS+1];
3775 pPLOC p = head_PLOC;
3780 dirlen = my_strlcpy(file, p->dir, sizeof(file));
3781 my_strlcat(file, "vmspipe.com", sizeof(file));
3784 exp_res = int_rmsexpand_tovms(file, vmspipe_file, 0);
3785 if (!exp_res) continue;
3787 if (cando_by_name_int
3788 (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 vmspipe_file_status = 1;
3792 return vmspipe_file;
3795 vmspipe_file_status = -1; /* failed, use tempfiles */
3802 vmspipe_tempfile(pTHX)
3804 char file[NAM$C_MAXRSS+1];
3806 static int index = 0;
3810 /* create a tempfile */
3812 /* we can't go from W, shr=get to R, shr=get without
3813 an intermediate vulnerable state, so don't bother trying...
3815 and lib$spawn doesn't shr=put, so have to close the write
3817 So... match up the creation date/time and the FID to
3818 make sure we're dealing with the same file
3823 if (!decc_filename_unix_only) {
3824 sprintf(file,"sys$scratch:perlpipe_%08.8x_%d.com",mypid,index);
3825 fp = fopen(file,"w");
3827 sprintf(file,"sys$login:perlpipe_%08.8x_%d.com",mypid,index);
3828 fp = fopen(file,"w");
3830 sprintf(file,"sys$disk:[]perlpipe_%08.8x_%d.com",mypid,index);
3831 fp = fopen(file,"w");
3836 sprintf(file,"/tmp/perlpipe_%08.8x_%d.com",mypid,index);
3837 fp = fopen(file,"w");
3839 sprintf(file,"/sys$login/perlpipe_%08.8x_%d.com",mypid,index);
3840 fp = fopen(file,"w");
3842 sprintf(file,"./perlpipe_%08.8x_%d.com",mypid,index);
3843 fp = fopen(file,"w");
3847 if (!fp) return 0; /* we're hosed */
3849 fprintf(fp,"$! 'f$verify(0)'\n");
3850 fprintf(fp,"$! --- protect against nonstandard definitions ---\n");
3851 fprintf(fp,"$ perl_cfile = f$environment(\"procedure\")\n");
3852 fprintf(fp,"$ perl_define = \"define/nolog\"\n");
3853 fprintf(fp,"$ perl_on = \"set noon\"\n");
3854 fprintf(fp,"$ perl_exit = \"exit\"\n");
3855 fprintf(fp,"$ perl_del = \"delete\"\n");
3856 fprintf(fp,"$ pif = \"if\"\n");
3857 fprintf(fp,"$! --- define i/o redirection (sys$output set by lib$spawn)\n");
3858 fprintf(fp,"$ pif perl_popen_in .nes. \"\" then perl_define/user/name_attributes=confine sys$input 'perl_popen_in'\n");
3859 fprintf(fp,"$ pif perl_popen_err .nes. \"\" then perl_define/user/name_attributes=confine sys$error 'perl_popen_err'\n");
3860 fprintf(fp,"$ pif perl_popen_out .nes. \"\" then perl_define sys$output 'perl_popen_out'\n");
3861 fprintf(fp,"$! --- build command line to get max possible length\n");
3862 fprintf(fp,"$c=perl_popen_cmd0\n");
3863 fprintf(fp,"$c=c+perl_popen_cmd1\n");
3864 fprintf(fp,"$c=c+perl_popen_cmd2\n");
3865 fprintf(fp,"$x=perl_popen_cmd3\n");
3866 fprintf(fp,"$c=c+x\n");
3867 fprintf(fp,"$ perl_on\n");
3868 fprintf(fp,"$ 'c'\n");
3869 fprintf(fp,"$ perl_status = $STATUS\n");
3870 fprintf(fp,"$ perl_del 'perl_cfile'\n");
3871 fprintf(fp,"$ perl_exit 'perl_status'\n");
3874 fgetname(fp, file, 1);
3875 fstat(fileno(fp), &s0.crtl_stat);
3878 if (decc_filename_unix_only)
3879 int_tounixspec(file, file, NULL);
3880 fp = fopen(file,"r","shr=get");
3882 fstat(fileno(fp), &s1.crtl_stat);
3884 cmp_result = VMS_INO_T_COMPARE(s0.crtl_stat.st_ino, s1.crtl_stat.st_ino);
3885 if ((cmp_result != 0) && (s0.st_ctime != s1.st_ctime)) {
3895 vms_is_syscommand_xterm(void)
3897 const static struct dsc$descriptor_s syscommand_dsc =
3898 { 11, DSC$K_DTYPE_T, DSC$K_CLASS_S, "SYS$COMMAND" };
3900 const static struct dsc$descriptor_s decwdisplay_dsc =
3901 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "DECW$DISPLAY" };
3903 struct item_list_3 items[2];
3904 unsigned short dvi_iosb[4];
3905 unsigned long devchar;
3906 unsigned long devclass;
3909 /* Very simple check to guess if sys$command is a decterm? */
3910 /* First see if the DECW$DISPLAY: device exists */
3912 items[0].code = DVI$_DEVCHAR;
3913 items[0].bufadr = &devchar;
3914 items[0].retadr = NULL;
3918 status = sys$getdviw
3919 (NO_EFN, 0, &decwdisplay_dsc, items, dvi_iosb, NULL, NULL, NULL);
3921 if ($VMS_STATUS_SUCCESS(status)) {
3922 status = dvi_iosb[0];
3925 if (!$VMS_STATUS_SUCCESS(status)) {
3926 SETERRNO(EVMSERR, status);
3930 /* If it does, then for now assume that we are on a workstation */
3931 /* Now verify that SYS$COMMAND is a terminal */
3932 /* for creating the debugger DECTerm */
3935 items[0].code = DVI$_DEVCLASS;
3936 items[0].bufadr = &devclass;
3937 items[0].retadr = NULL;
3941 status = sys$getdviw
3942 (NO_EFN, 0, &syscommand_dsc, items, dvi_iosb, NULL, NULL, NULL);
3944 if ($VMS_STATUS_SUCCESS(status)) {
3945 status = dvi_iosb[0];
3948 if (!$VMS_STATUS_SUCCESS(status)) {
3949 SETERRNO(EVMSERR, status);
3953 if (devclass == DC$_TERM) {
3960 /* If we are on a DECTerm, we can pretend to fork xterms when requested */
3962 create_forked_xterm(pTHX_ const char *cmd, const char *mode)
3967 char device_name[65];
3968 unsigned short device_name_len;
3969 struct dsc$descriptor_s customization_dsc;
3970 struct dsc$descriptor_s device_name_dsc;
3972 char customization[200];
3976 unsigned short p_chan;
3978 unsigned short iosb[4];
3979 const char * cust_str =
3980 "DECW$TERMINAL.iconName:\tPerl Dbg\nDECW$TERMINAL.title:\t%40s\n";
3981 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3982 DSC$K_CLASS_S, mbx1};
3984 /* LIB$FIND_IMAGE_SIGNAL needs a handler */
3985 /*---------------------------------------*/
3986 VAXC$ESTABLISH((__vms_handler)lib$sig_to_ret);
3989 /* Make sure that this is from the Perl debugger */
3990 ret_char = strstr(cmd," xterm ");
3991 if (ret_char == NULL)
3993 cptr = ret_char + 7;
3994 ret_char = strstr(cmd,"tty");
3995 if (ret_char == NULL)
3997 ret_char = strstr(cmd,"sleep");
3998 if (ret_char == NULL)
4001 if (decw_term_port == 0) {
4002 $DESCRIPTOR(filename1_dsc, "DECW$TERMINALSHR12");
4003 $DESCRIPTOR(filename2_dsc, "DECW$TERMINALSHR");
4004 $DESCRIPTOR(decw_term_port_dsc, "DECW$TERM_PORT");
4006 status = lib$find_image_symbol
4008 &decw_term_port_dsc,
4009 (void *)&decw_term_port,
4013 /* Try again with the other image name */
4014 if (!$VMS_STATUS_SUCCESS(status)) {
4016 status = lib$find_image_symbol
4018 &decw_term_port_dsc,
4019 (void *)&decw_term_port,
4028 /* No decw$term_port, give it up */
4029 if (!$VMS_STATUS_SUCCESS(status))
4032 /* Are we on a workstation? */
4033 /* to do: capture the rows / columns and pass their properties */
4034 ret_stat = vms_is_syscommand_xterm();
4038 /* Make the title: */
4039 ret_char = strstr(cptr,"-title");
4040 if (ret_char != NULL) {
4041 while ((*cptr != 0) && (*cptr != '\"')) {
4047 while ((*cptr != 0) && (*cptr != '\"')) {
4060 strcpy(title,"Perl Debug DECTerm");
4062 sprintf(customization, cust_str, title);
4064 customization_dsc.dsc$a_pointer = customization;
4065 customization_dsc.dsc$w_length = strlen(customization);
4066 customization_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
4067 customization_dsc.dsc$b_class = DSC$K_CLASS_S;
4069 device_name_dsc.dsc$a_pointer = device_name;
4070 device_name_dsc.dsc$w_length = sizeof device_name -1;
4071 device_name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
4072 device_name_dsc.dsc$b_class = DSC$K_CLASS_S;
4074 device_name_len = 0;
4076 /* Try to create the window */
4077 status = (*decw_term_port)
4086 if (!$VMS_STATUS_SUCCESS(status)) {
4087 SETERRNO(EVMSERR, status);
4091 device_name[device_name_len] = '\0';
4093 /* Need to set this up to look like a pipe for cleanup */
4095 status = lib$get_vm(&n, &info);
4096 if (!$VMS_STATUS_SUCCESS(status)) {
4097 SETERRNO(ENOMEM, status);
4103 info->completion = 0;
4104 info->closing = FALSE;
4111 info->in_done = TRUE;
4112 info->out_done = TRUE;
4113 info->err_done = TRUE;
4115 /* Assign a channel on this so that it will persist, and not login */
4116 /* We stash this channel in the info structure for reference. */
4117 /* The created xterm self destructs when the last channel is removed */
4118 /* and it appears that perl5db.pl (perl debugger) does this routinely */
4119 /* So leave this assigned. */
4120 device_name_dsc.dsc$w_length = device_name_len;
4121 status = sys$assign(&device_name_dsc,&info->xchan,0,0);
4122 if (!$VMS_STATUS_SUCCESS(status)) {
4123 SETERRNO(EVMSERR, status);
4126 info->xchan_valid = 1;
4128 /* Now create a mailbox to be read by the application */
4130 create_mbx(&p_chan, &d_mbx1);
4132 /* write the name of the created terminal to the mailbox */
4133 status = sys$qiow(NO_EFN, p_chan, IO$_WRITEVBLK|IO$M_NOW,
4134 iosb, NULL, NULL, device_name, device_name_len, 0, 0, 0, 0);
4136 if (!$VMS_STATUS_SUCCESS(status)) {
4137 SETERRNO(EVMSERR, status);
4141 info->fp = PerlIO_open(mbx1, mode);
4143 /* Done with this channel */
4146 /* If any errors, then clean up */
4149 _ckvmssts_noperl(lib$free_vm(&n, &info));
4157 static I32 my_pclose_pinfo(pTHX_ pInfo info);
4160 safe_popen(pTHX_ const char *cmd, const char *in_mode, int *psts)
4162 static int handler_set_up = FALSE;
4164 unsigned long int sts, flags = CLI$M_NOWAIT;
4165 /* The use of a GLOBAL table (as was done previously) rendered
4166 * Perl's qx() or `` unusable from a C<$ SET SYMBOL/SCOPE=NOGLOBAL> DCL
4167 * environment. Hence we've switched to LOCAL symbol table.
4169 unsigned int table = LIB$K_CLI_LOCAL_SYM;
4171 char *p, mode[10], symbol[MAX_DCL_SYMBOL+1], *vmspipe;
4172 char *in, *out, *err, mbx[512];
4174 char tfilebuf[NAM$C_MAXRSS+1];
4176 char cmd_sym_name[20];
4177 struct dsc$descriptor_s d_symbol= {0, DSC$K_DTYPE_T,
4178 DSC$K_CLASS_S, symbol};
4179 struct dsc$descriptor_s vmspipedsc = {0, DSC$K_DTYPE_T,
4181 struct dsc$descriptor_s d_sym_cmd = {0, DSC$K_DTYPE_T,
4182 DSC$K_CLASS_S, cmd_sym_name};
4183 struct dsc$descriptor_s *vmscmd;
4184 $DESCRIPTOR(d_sym_in ,"PERL_POPEN_IN");
4185 $DESCRIPTOR(d_sym_out,"PERL_POPEN_OUT");
4186 $DESCRIPTOR(d_sym_err,"PERL_POPEN_ERR");
4188 /* Check here for Xterm create request. This means looking for
4189 * "3>&1 xterm\b" and "\btty 1>&3\b$" in the command, and that it
4190 * is possible to create an xterm.
4192 if (*in_mode == 'r') {
4195 #if defined(PERL_IMPLICIT_CONTEXT)
4196 /* Can not fork an xterm with a NULL context */
4197 /* This probably could never happen */
4201 xterm_fd = create_forked_xterm(aTHX_ cmd, in_mode);
4202 if (xterm_fd != NULL)
4206 if (!head_PLOC) store_pipelocs(aTHX); /* at least TRY to use a static vmspipe file */
4208 /* once-per-program initialization...
4209 note that the SETAST calls and the dual test of pipe_ef
4210 makes sure that only the FIRST thread through here does
4211 the initialization...all other threads wait until it's
4214 Yeah, uglier than a pthread call, it's got all the stuff inline
4215 rather than in a separate routine.
4219 _ckvmssts_noperl(sys$setast(0));
4221 unsigned long int pidcode = JPI$_PID;
4222 $DESCRIPTOR(d_delay, RETRY_DELAY);
4223 _ckvmssts_noperl(lib$get_ef(&pipe_ef));
4224 _ckvmssts_noperl(lib$getjpi(&pidcode,0,0,&mypid,0,0));
4225 _ckvmssts_noperl(sys$bintim(&d_delay, delaytime));
4227 if (!handler_set_up) {
4228 _ckvmssts_noperl(sys$dclexh(&pipe_exitblock));
4229 handler_set_up = TRUE;
4231 _ckvmssts_noperl(sys$setast(1));
4234 /* see if we can find a VMSPIPE.COM */
4237 vmspipe = find_vmspipe(aTHX);
4239 vmspipedsc.dsc$w_length = my_strlcpy(tfilebuf+1, vmspipe, sizeof(tfilebuf)-1) + 1;
4240 } else { /* uh, oh...we're in tempfile hell */
4241 tpipe = vmspipe_tempfile(aTHX);
4242 if (!tpipe) { /* a fish popular in Boston */
4243 if (ckWARN(WARN_PIPE)) {
4244 Perl_warner(aTHX_ packWARN(WARN_PIPE),"unable to find VMSPIPE.COM for i/o piping");
4248 fgetname(tpipe,tfilebuf+1,1);
4249 vmspipedsc.dsc$w_length = strlen(tfilebuf);
4251 vmspipedsc.dsc$a_pointer = tfilebuf;
4253 sts = setup_cmddsc(aTHX_ cmd,0,0,&vmscmd);
4256 case RMS$_FNF: case RMS$_DNF:
4257 set_errno(ENOENT); break;
4259 set_errno(ENOTDIR); break;
4261 set_errno(ENODEV); break;
4263 set_errno(EACCES); break;
4265 set_errno(EINVAL); break;
4266 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
4267 set_errno(E2BIG); break;
4268 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
4269 _ckvmssts_noperl(sts); /* fall through */
4270 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
4273 set_vaxc_errno(sts);
4274 if (*in_mode != 'n' && ckWARN(WARN_PIPE)) {
4275 Perl_warner(aTHX_ packWARN(WARN_PIPE),"Can't pipe \"%*s\": %s", strlen(cmd), cmd, Strerror(errno));
4281 _ckvmssts_noperl(lib$get_vm(&n, &info));
4283 my_strlcpy(mode, in_mode, sizeof(mode));
4286 info->completion = 0;
4287 info->closing = FALSE;
4294 info->in_done = TRUE;
4295 info->out_done = TRUE;
4296 info->err_done = TRUE;
4298 info->xchan_valid = 0;
4300 in = (char *)PerlMem_malloc(VMS_MAXRSS);
4301 if (in == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4302 out = (char *)PerlMem_malloc(VMS_MAXRSS);
4303 if (out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4304 err = (char *)PerlMem_malloc(VMS_MAXRSS);
4305 if (err == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4307 in[0] = out[0] = err[0] = '\0';
4309 if ((p = strchr(mode,'F')) != NULL) { /* F -> use FILE* */
4313 if ((p = strchr(mode,'W')) != NULL) { /* W -> wait for completion */
4318 if (*mode == 'r') { /* piping from subroutine */
4320 info->out = pipe_infromchild_setup(aTHX_ mbx,out);
4322 info->out->pipe_done = &info->out_done;
4323 info->out_done = FALSE;
4324 info->out->info = info;
4326 if (!info->useFILE) {
4327 info->fp = PerlIO_open(mbx, mode);
4329 info->fp = (PerlIO *) freopen(mbx, mode, stdin);
4330 vmssetuserlnm("SYS$INPUT", mbx);
4333 if (!info->fp && info->out) {
4334 sys$cancel(info->out->chan_out);
4336 while (!info->out_done) {
4338 _ckvmssts_noperl(sys$setast(0));
4339 done = info->out_done;
4340 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4341 _ckvmssts_noperl(sys$setast(1));
4342 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4345 if (info->out->buf) {
4346 n = info->out->bufsize * sizeof(char);
4347 _ckvmssts_noperl(lib$free_vm(&n, &info->out->buf));
4350 _ckvmssts_noperl(lib$free_vm(&n, &info->out));
4352 _ckvmssts_noperl(lib$free_vm(&n, &info));
4357 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4359 info->err->pipe_done = &info->err_done;
4360 info->err_done = FALSE;
4361 info->err->info = info;
4364 } else if (*mode == 'w') { /* piping to subroutine */
4366 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
4368 info->out->pipe_done = &info->out_done;
4369 info->out_done = FALSE;
4370 info->out->info = info;
4373 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4375 info->err->pipe_done = &info->err_done;
4376 info->err_done = FALSE;
4377 info->err->info = info;
4380 info->in = pipe_tochild_setup(aTHX_ in,mbx);
4381 if (!info->useFILE) {
4382 info->fp = PerlIO_open(mbx, mode);
4384 info->fp = (PerlIO *) freopen(mbx, mode, stdout);
4385 vmssetuserlnm("SYS$OUTPUT", mbx);
4389 info->in->pipe_done = &info->in_done;
4390 info->in_done = FALSE;
4391 info->in->info = info;
4395 if (!info->fp && info->in) {
4397 _ckvmssts_noperl(sys$qiow(0,info->in->chan_in, IO$_WRITEOF, 0,
4398 0, 0, 0, 0, 0, 0, 0, 0));
4400 while (!info->in_done) {
4402 _ckvmssts_noperl(sys$setast(0));
4403 done = info->in_done;
4404 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4405 _ckvmssts_noperl(sys$setast(1));
4406 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4409 if (info->in->buf) {
4410 n = info->in->bufsize * sizeof(char);
4411 _ckvmssts_noperl(lib$free_vm(&n, &info->in->buf));
4414 _ckvmssts_noperl(lib$free_vm(&n, &info->in));
4416 _ckvmssts_noperl(lib$free_vm(&n, &info));
4422 } else if (*mode == 'n') { /* separate subprocess, no Perl i/o */
4423 /* Let the child inherit standard input, unless it's a directory. */
4425 if (my_trnlnm("SYS$INPUT", in, 0)) {
4426 if (flex_stat(in, &st) != 0 || S_ISDIR(st.st_mode))
4430 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
4432 info->out->pipe_done = &info->out_done;
4433 info->out_done = FALSE;
4434 info->out->info = info;
4437 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4439 info->err->pipe_done = &info->err_done;
4440 info->err_done = FALSE;
4441 info->err->info = info;
4445 d_symbol.dsc$w_length = my_strlcpy(symbol, in, sizeof(symbol));
4446 _ckvmssts_noperl(lib$set_symbol(&d_sym_in, &d_symbol, &table));
4448 d_symbol.dsc$w_length = my_strlcpy(symbol, err, sizeof(symbol));
4449 _ckvmssts_noperl(lib$set_symbol(&d_sym_err, &d_symbol, &table));
4451 d_symbol.dsc$w_length = my_strlcpy(symbol, out, sizeof(symbol));
4452 _ckvmssts_noperl(lib$set_symbol(&d_sym_out, &d_symbol, &table));
4454 /* Done with the names for the pipes */
4459 p = vmscmd->dsc$a_pointer;
4460 while (*p == ' ' || *p == '\t') p++; /* remove leading whitespace */
4461 if (*p == '$') p++; /* remove leading $ */
4462 while (*p == ' ' || *p == '\t') p++;
4464 for (j = 0; j < 4; j++) {
4465 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
4466 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
4468 d_symbol.dsc$w_length = my_strlcpy(symbol, p, sizeof(symbol));
4469 _ckvmssts_noperl(lib$set_symbol(&d_sym_cmd, &d_symbol, &table));
4471 if (strlen(p) > MAX_DCL_SYMBOL) {
4472 p += MAX_DCL_SYMBOL;
4477 _ckvmssts_noperl(sys$setast(0));
4478 info->next=open_pipes; /* prepend to list */
4480 _ckvmssts_noperl(sys$setast(1));
4481 /* Omit arg 2 (input file) so the child will get the parent's SYS$INPUT
4482 * and SYS$COMMAND. vmspipe.com will redefine SYS$INPUT, but we'll still
4483 * have SYS$COMMAND if we need it.
4485 _ckvmssts_noperl(lib$spawn(&vmspipedsc, 0, &nl_desc, &flags,
4486 0, &info->pid, &info->completion,
4487 0, popen_completion_ast,info,0,0,0));
4489 /* if we were using a tempfile, close it now */
4491 if (tpipe) fclose(tpipe);
4493 /* once the subprocess is spawned, it has copied the symbols and
4494 we can get rid of ours */
4496 for (j = 0; j < 4; j++) {
4497 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
4498 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
4499 _ckvmssts_noperl(lib$delete_symbol(&d_sym_cmd, &table));
4501 _ckvmssts_noperl(lib$delete_symbol(&d_sym_in, &table));
4502 _ckvmssts_noperl(lib$delete_symbol(&d_sym_err, &table));
4503 _ckvmssts_noperl(lib$delete_symbol(&d_sym_out, &table));
4504 vms_execfree(vmscmd);
4506 #ifdef PERL_IMPLICIT_CONTEXT
4509 PL_forkprocess = info->pid;
4516 _ckvmssts_noperl(sys$setast(0));
4518 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4519 _ckvmssts_noperl(sys$setast(1));
4520 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4522 *psts = info->completion;
4523 /* Caller thinks it is open and tries to close it. */
4524 /* This causes some problems, as it changes the error status */
4525 /* my_pclose(info->fp); */
4527 /* If we did not have a file pointer open, then we have to */
4528 /* clean up here or eventually we will run out of something */
4530 if (info->fp == NULL) {
4531 my_pclose_pinfo(aTHX_ info);
4539 } /* end of safe_popen */
4542 /*{{{ PerlIO *my_popen(char *cmd, char *mode)*/
4544 Perl_my_popen(pTHX_ const char *cmd, const char *mode)
4548 TAINT_PROPER("popen");
4549 PERL_FLUSHALL_FOR_CHILD;
4550 return safe_popen(aTHX_ cmd,mode,&sts);
4556 /* Routine to close and cleanup a pipe info structure */
4559 my_pclose_pinfo(pTHX_ pInfo info) {
4561 unsigned long int retsts;
4565 /* If we were writing to a subprocess, insure that someone reading from
4566 * the mailbox gets an EOF. It looks like a simple fclose() doesn't
4567 * produce an EOF record in the mailbox.
4569 * well, at least sometimes it *does*, so we have to watch out for
4570 * the first EOF closing the pipe (and DASSGN'ing the channel)...
4574 #if defined(USE_ITHREADS)
4578 && PL_perlio_fd_refcnt
4581 PerlIO_flush(info->fp);
4583 fflush((FILE *)info->fp);
4586 _ckvmssts(sys$setast(0));
4587 info->closing = TRUE;
4588 done = info->done && info->in_done && info->out_done && info->err_done;
4589 /* hanging on write to Perl's input? cancel it */
4590 if (info->mode == 'r' && info->out && !info->out_done) {
4591 if (info->out->chan_out) {
4592 _ckvmssts(sys$cancel(info->out->chan_out));
4593 if (!info->out->chan_in) { /* EOF generation, need AST */
4594 _ckvmssts(sys$dclast(pipe_infromchild_ast,info->out,0));
4598 if (info->in && !info->in_done && !info->in->shut_on_empty) /* EOF if hasn't had one yet */
4599 _ckvmssts(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
4601 _ckvmssts(sys$setast(1));
4604 #if defined(USE_ITHREADS)
4608 && PL_perlio_fd_refcnt
4611 PerlIO_close(info->fp);
4613 fclose((FILE *)info->fp);
4616 we have to wait until subprocess completes, but ALSO wait until all
4617 the i/o completes...otherwise we'll be freeing the "info" structure
4618 that the i/o ASTs could still be using...
4622 _ckvmssts(sys$setast(0));
4623 done = info->done && info->in_done && info->out_done && info->err_done;
4624 if (!done) _ckvmssts(sys$clref(pipe_ef));
4625 _ckvmssts(sys$setast(1));
4626 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4628 retsts = info->completion;
4630 /* remove from list of open pipes */
4631 _ckvmssts(sys$setast(0));
4633 for (next = open_pipes; next != NULL; last = next, next = next->next) {
4639 last->next = info->next;
4641 open_pipes = info->next;
4642 _ckvmssts(sys$setast(1));
4644 /* free buffers and structures */
4647 if (info->in->buf) {
4648 n = info->in->bufsize * sizeof(char);
4649 _ckvmssts(lib$free_vm(&n, &info->in->buf));
4652 _ckvmssts(lib$free_vm(&n, &info->in));
4655 if (info->out->buf) {
4656 n = info->out->bufsize * sizeof(char);
4657 _ckvmssts(lib$free_vm(&n, &info->out->buf));
4660 _ckvmssts(lib$free_vm(&n, &info->out));
4663 if (info->err->buf) {
4664 n = info->err->bufsize * sizeof(char);
4665 _ckvmssts(lib$free_vm(&n, &info->err->buf));
4668 _ckvmssts(lib$free_vm(&n, &info->err));
4671 _ckvmssts(lib$free_vm(&n, &info));
4677 /*{{{ I32 my_pclose(PerlIO *fp)*/
4678 I32 Perl_my_pclose(pTHX_ PerlIO *fp)
4680 pInfo info, last = NULL;
4683 /* Fixme - need ast and mutex protection here */
4684 for (info = open_pipes; info != NULL; last = info, info = info->next)
4685 if (info->fp == fp) break;
4687 if (info == NULL) { /* no such pipe open */
4688 set_errno(ECHILD); /* quoth POSIX */
4689 set_vaxc_errno(SS$_NONEXPR);
4693 ret_status = my_pclose_pinfo(aTHX_ info);
4697 } /* end of my_pclose() */
4699 /* Roll our own prototype because we want this regardless of whether
4700 * _VMS_WAIT is defined.
4706 __pid_t __vms_waitpid( __pid_t __pid, int *__stat_loc, int __options );
4711 /* sort-of waitpid; special handling of pipe clean-up for subprocesses
4712 created with popen(); otherwise partially emulate waitpid() unless
4713 we have a suitable one from the CRTL that came with VMS 7.2 and later.
4714 Also check processes not considered by the CRTL waitpid().
4716 /*{{{Pid_t my_waitpid(Pid_t pid, int *statusp, int flags)*/
4718 Perl_my_waitpid(pTHX_ Pid_t pid, int *statusp, int flags)
4725 if (statusp) *statusp = 0;
4727 for (info = open_pipes; info != NULL; info = info->next)
4728 if (info->pid == pid) break;
4730 if (info != NULL) { /* we know about this child */
4731 while (!info->done) {
4732 _ckvmssts(sys$setast(0));
4734 if (!done) _ckvmssts(sys$clref(pipe_ef));
4735 _ckvmssts(sys$setast(1));
4736 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4739 if (statusp) *statusp = info->completion;
4743 /* child that already terminated? */
4745 for (j = 0; j < NKEEPCLOSED && j < closed_num; j++) {
4746 if (closed_list[j].pid == pid) {
4747 if (statusp) *statusp = closed_list[j].completion;
4752 /* fall through if this child is not one of our own pipe children */
4754 /* waitpid() became available in the CRTL as of VMS 7.0, but only
4755 * in 7.2 did we get a version that fills in the VMS completion
4756 * status as Perl has always tried to do.
4759 sts = __vms_waitpid( pid, statusp, flags );
4761 if ( sts == 0 || !(sts == -1 && errno == ECHILD) )
4764 /* If the real waitpid tells us the child does not exist, we
4765 * fall through here to implement waiting for a child that
4766 * was created by some means other than exec() (say, spawned
4767 * from DCL) or to wait for a process that is not a subprocess
4768 * of the current process.
4772 $DESCRIPTOR(intdsc,"0 00:00:01");
4773 unsigned long int ownercode = JPI$_OWNER, ownerpid;
4774 unsigned long int pidcode = JPI$_PID, mypid;
4775 unsigned long int interval[2];
4776 unsigned int jpi_iosb[2];
4777 struct itmlst_3 jpilist[2] = {
4778 {sizeof(ownerpid), JPI$_OWNER, &ownerpid, 0},
4783 /* Sorry folks, we don't presently implement rooting around for
4784 the first child we can find, and we definitely don't want to
4785 pass a pid of -1 to $getjpi, where it is a wildcard operation.
4791 /* Get the owner of the child so I can warn if it's not mine. If the
4792 * process doesn't exist or I don't have the privs to look at it,
4793 * I can go home early.
4795 sts = sys$getjpiw(0,&pid,NULL,&jpilist,&jpi_iosb,NULL,NULL);
4796 if (sts & 1) sts = jpi_iosb[0];
4808 set_vaxc_errno(sts);
4812 if (ckWARN(WARN_EXEC)) {
4813 /* remind folks they are asking for non-standard waitpid behavior */
4814 _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
4815 if (ownerpid != mypid)
4816 Perl_warner(aTHX_ packWARN(WARN_EXEC),
4817 "waitpid: process %x is not a child of process %x",
4821 /* simply check on it once a second until it's not there anymore. */
4823 _ckvmssts(sys$bintim(&intdsc,interval));
4824 while ((sts=lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0)) & 1) {
4825 _ckvmssts(sys$schdwk(0,0,interval,0));
4826 _ckvmssts(sys$hiber());
4828 if (sts == SS$_NONEXPR) sts = SS$_NORMAL;
4833 } /* end of waitpid() */
4838 /*{{{ char *my_gconvert(double val, int ndig, int trail, char *buf) */
4840 my_gconvert(double val, int ndig, int trail, char *buf)
4842 static char __gcvtbuf[DBL_DIG+1];
4845 loc = buf ? buf : __gcvtbuf;
4848 if (!buf && ndig > DBL_DIG) ndig = DBL_DIG;
4849 return gcvt(val,ndig,loc);
4852 loc[0] = '0'; loc[1] = '\0';
4859 #if !defined(NAML$C_MAXRSS)
4861 rms_free_search_context(struct FAB * fab)
4865 nam = fab->fab$l_nam;
4866 nam->nam$b_nop |= NAM$M_SYNCHK;
4867 nam->nam$l_rlf = NULL;
4869 return sys$parse(fab, NULL, NULL);
4872 #define rms_setup_nam(nam) struct NAM nam = cc$rms_nam
4873 #define rms_clear_nam_nop(nam) nam.nam$b_nop = 0;
4874 #define rms_set_nam_nop(nam, opt) nam.nam$b_nop |= (opt)
4875 #define rms_set_nam_fnb(nam, opt) nam.nam$l_fnb |= (opt)
4876 #define rms_is_nam_fnb(nam, opt) (nam.nam$l_fnb & (opt))
4877 #define rms_nam_esll(nam) nam.nam$b_esl
4878 #define rms_nam_esl(nam) nam.nam$b_esl
4879 #define rms_nam_name(nam) nam.nam$l_name
4880 #define rms_nam_namel(nam) nam.nam$l_name
4881 #define rms_nam_type(nam) nam.nam$l_type
4882 #define rms_nam_typel(nam) nam.nam$l_type
4883 #define rms_nam_ver(nam) nam.nam$l_ver
4884 #define rms_nam_verl(nam) nam.nam$l_ver
4885 #define rms_nam_rsll(nam) nam.nam$b_rsl
4886 #define rms_nam_rsl(nam) nam.nam$b_rsl
4887 #define rms_bind_fab_nam(fab, nam) fab.fab$l_nam = &nam
4888 #define rms_set_fna(fab, nam, name, size) \
4889 { fab.fab$b_fns = size; fab.fab$l_fna = name; }
4890 #define rms_get_fna(fab, nam) fab.fab$l_fna
4891 #define rms_set_dna(fab, nam, name, size) \
4892 { fab.fab$b_dns = size; fab.fab$l_dna = name; }
4893 #define rms_nam_dns(fab, nam) fab.fab$b_dns
4894 #define rms_set_esa(nam, name, size) \
4895 { nam.nam$b_ess = size; nam.nam$l_esa = name; }
4896 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
4897 { nam.nam$l_esa = s_name; nam.nam$b_ess = s_size;}
4898 #define rms_set_rsa(nam, name, size) \
4899 { nam.nam$l_rsa = name; nam.nam$b_rss = size; }
4900 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
4901 { nam.nam$l_rsa = s_name; nam.nam$b_rss = s_size; }
4902 #define rms_nam_name_type_l_size(nam) \
4903 (nam.nam$b_name + nam.nam$b_type)
4906 rms_free_search_context(struct FAB * fab)
4910 nam = fab->fab$l_naml;
4911 nam->naml$b_nop |= NAM$M_SYNCHK;
4912 nam->naml$l_rlf = NULL;
4913 nam->naml$l_long_defname_size = 0;
4916 return sys$parse(fab, NULL, NULL);
4919 #define rms_setup_nam(nam) struct NAML nam = cc$rms_naml
4920 #define rms_clear_nam_nop(nam) nam.naml$b_nop = 0;
4921 #define rms_set_nam_nop(nam, opt) nam.naml$b_nop |= (opt)
4922 #define rms_set_nam_fnb(nam, opt) nam.naml$l_fnb |= (opt)
4923 #define rms_is_nam_fnb(nam, opt) (nam.naml$l_fnb & (opt))
4924 #define rms_nam_esll(nam) nam.naml$l_long_expand_size
4925 #define rms_nam_esl(nam) nam.naml$b_esl
4926 #define rms_nam_name(nam) nam.naml$l_name
4927 #define rms_nam_namel(nam) nam.naml$l_long_name
4928 #define rms_nam_type(nam) nam.naml$l_type
4929 #define rms_nam_typel(nam) nam.naml$l_long_type
4930 #define rms_nam_ver(nam) nam.naml$l_ver
4931 #define rms_nam_verl(nam) nam.naml$l_long_ver
4932 #define rms_nam_rsll(nam) nam.naml$l_long_result_size
4933 #define rms_nam_rsl(nam) nam.naml$b_rsl
4934 #define rms_bind_fab_nam(fab, nam) fab.fab$l_naml = &nam
4935 #define rms_set_fna(fab, nam, name, size) \
4936 { fab.fab$b_fns = 0; fab.fab$l_fna = (char *) -1; \
4937 nam.naml$l_long_filename_size = size; \
4938 nam.naml$l_long_filename = name;}
4939 #define rms_get_fna(fab, nam) nam.naml$l_long_filename
4940 #define rms_set_dna(fab, nam, name, size) \
4941 { fab.fab$b_dns = 0; fab.fab$l_dna = (char *) -1; \
4942 nam.naml$l_long_defname_size = size; \
4943 nam.naml$l_long_defname = name; }
4944 #define rms_nam_dns(fab, nam) nam.naml$l_long_defname_size
4945 #define rms_set_esa(nam, name, size) \
4946 { nam.naml$b_ess = 0; nam.naml$l_esa = (char *) -1; \
4947 nam.naml$l_long_expand_alloc = size; \
4948 nam.naml$l_long_expand = name; }
4949 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
4950 { nam.naml$l_esa = s_name; nam.naml$b_ess = s_size; \
4951 nam.naml$l_long_expand = l_name; \
4952 nam.naml$l_long_expand_alloc = l_size; }
4953 #define rms_set_rsa(nam, name, size) \
4954 { nam.naml$l_rsa = NULL; nam.naml$b_rss = 0; \
4955 nam.naml$l_long_result = name; \
4956 nam.naml$l_long_result_alloc = size; }
4957 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
4958 { nam.naml$l_rsa = s_name; nam.naml$b_rss = s_size; \
4959 nam.naml$l_long_result = l_name; \
4960 nam.naml$l_long_result_alloc = l_size; }
4961 #define rms_nam_name_type_l_size(nam) \
4962 (nam.naml$l_long_name_size + nam.naml$l_long_type_size)
4967 * The CRTL for 8.3 and later can create symbolic links in any mode,
4968 * however in 8.3 the unlink/remove/delete routines will only properly handle
4969 * them if one of the PCP modes is active.
4972 rms_erase(const char * vmsname)
4975 struct FAB myfab = cc$rms_fab;
4976 rms_setup_nam(mynam);
4978 rms_set_fna(myfab, mynam, (char *)vmsname, strlen(vmsname)); /* cast ok */
4979 rms_bind_fab_nam(myfab, mynam);
4981 #ifdef NAML$M_OPEN_SPECIAL
4982 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
4985 status = sys$erase(&myfab, 0, 0);
4992 vms_rename_with_acl(pTHX_ const struct dsc$descriptor_s * vms_src_dsc,
4993 const struct dsc$descriptor_s * vms_dst_dsc,
4994 unsigned long flags)
4996 /* VMS and UNIX handle file permissions differently and the
4997 * the same ACL trick may be needed for renaming files,
4998 * especially if they are directories.
5001 /* todo: get kill_file and rename to share common code */
5002 /* I can not find online documentation for $change_acl
5003 * it appears to be replaced by $set_security some time ago */
5005 const unsigned int access_mode = 0;
5006 $DESCRIPTOR(obj_file_dsc,"FILE");
5009 unsigned long int jpicode = JPI$_UIC;
5010 int aclsts, fndsts, rnsts = -1;
5011 unsigned int ctx = 0;
5012 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
5013 struct dsc$descriptor_s * clean_dsc;
5016 unsigned char myace$b_length;
5017 unsigned char myace$b_type;
5018 unsigned short int myace$w_flags;
5019 unsigned long int myace$l_access;
5020 unsigned long int myace$l_ident;
5021 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
5022 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL,
5024 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
5027 findlst[3] = {{sizeof oldace, OSS$_ACL_FIND_ENTRY, &oldace, 0},
5028 {sizeof oldace, OSS$_ACL_READ_ENTRY, &oldace, 0},
5030 addlst[2] = {{sizeof newace, OSS$_ACL_ADD_ENTRY, &newace, 0},{0,0,0,0}},
5031 dellst[2] = {{sizeof newace, OSS$_ACL_DELETE_ENTRY, &newace, 0},
5035 /* Expand the input spec using RMS, since we do not want to put
5036 * ACLs on the target of a symbolic link */
5037 vmsname = (char *)PerlMem_malloc(NAM$C_MAXRSS+1);
5038 if (vmsname == NULL)
5041 rslt = int_rmsexpand_tovms(vms_src_dsc->dsc$a_pointer,
5043 PERL_RMSEXPAND_M_SYMLINK);
5045 PerlMem_free(vmsname);
5049 /* So we get our own UIC to use as a rights identifier,
5050 * and the insert an ACE at the head of the ACL which allows us
5051 * to delete the file.
5053 _ckvmssts_noperl(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
5055 fildsc.dsc$w_length = strlen(vmsname);
5056 fildsc.dsc$a_pointer = vmsname;
5058 newace.myace$l_ident = oldace.myace$l_ident;
5061 /* Grab any existing ACEs with this identifier in case we fail */
5062 clean_dsc = &fildsc;
5063 aclsts = fndsts = sys$get_security(&obj_file_dsc,
5071 if ($VMS_STATUS_SUCCESS(fndsts) || (fndsts == SS$_ACLEMPTY)) {
5072 /* Add the new ACE . . . */
5074 /* if the sys$get_security succeeded, then ctx is valid, and the
5075 * object/file descriptors will be ignored. But otherwise they
5078 aclsts = sys$set_security(&obj_file_dsc, &fildsc, NULL,
5079 OSS$M_RELCTX, addlst, &ctx, &access_mode);
5080 if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) {
5082 set_vaxc_errno(aclsts);
5083 PerlMem_free(vmsname);
5087 rnsts = lib$rename_file(vms_src_dsc, vms_dst_dsc,
5090 NULL, NULL, NULL, NULL, NULL, NULL, NULL);
5092 if ($VMS_STATUS_SUCCESS(rnsts)) {
5093 clean_dsc = (struct dsc$descriptor_s *)vms_dst_dsc;
5096 /* Put things back the way they were. */
5098 aclsts = sys$get_security(&obj_file_dsc,
5106 if ($VMS_STATUS_SUCCESS(aclsts)) {
5110 if (!$VMS_STATUS_SUCCESS(fndsts))
5111 sec_flags = OSS$M_RELCTX;
5113 /* Get rid of the new ACE */
5114 aclsts = sys$set_security(NULL, NULL, NULL,
5115 sec_flags, dellst, &ctx, &access_mode);
5117 /* If there was an old ACE, put it back */
5118 if ($VMS_STATUS_SUCCESS(aclsts) && $VMS_STATUS_SUCCESS(fndsts)) {
5119 addlst[0].bufadr = &oldace;
5120 aclsts = sys$set_security(NULL, NULL, NULL,
5121 OSS$M_RELCTX, addlst, &ctx, &access_mode);
5122 if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) {
5124 set_vaxc_errno(aclsts);
5130 /* Try to clear the lock on the ACL list */
5131 aclsts2 = sys$set_security(NULL, NULL, NULL,
5132 OSS$M_RELCTX, NULL, &ctx, &access_mode);
5134 /* Rename errors are most important */
5135 if (!$VMS_STATUS_SUCCESS(rnsts))
5138 set_vaxc_errno(aclsts);
5143 if (aclsts != SS$_ACLEMPTY)
5150 PerlMem_free(vmsname);
5155 /*{{{int rename(const char *, const char * */
5156 /* Not exactly what X/Open says to do, but doing it absolutely right
5157 * and efficiently would require a lot more work. This should be close
5158 * enough to pass all but the most strict X/Open compliance test.
5161 Perl_rename(pTHX_ const char *src, const char * dst)
5170 /* Validate the source file */
5171 src_sts = flex_lstat(src, &src_st);
5174 /* No source file or other problem */
5177 if (src_st.st_devnam[0] == 0) {
5178 /* This may be possible so fail if it is seen. */
5183 dst_sts = flex_lstat(dst, &dst_st);
5186 if (dst_st.st_dev != src_st.st_dev) {
5187 /* Must be on the same device */
5192 /* VMS_INO_T_COMPARE is true if the inodes are different
5193 * to match the output of memcmp
5196 if (!VMS_INO_T_COMPARE(src_st.st_ino, dst_st.st_ino)) {
5197 /* That was easy, the files are the same! */
5201 if (S_ISDIR(src_st.st_mode) && !S_ISDIR(dst_st.st_mode)) {
5202 /* If source is a directory, so must be dest */
5210 if ((dst_sts == 0) &&
5211 (vms_unlink_all_versions || S_ISDIR(dst_st.st_mode))) {
5213 /* We have issues here if vms_unlink_all_versions is set
5214 * If the destination exists, and is not a directory, then
5215 * we must delete in advance.
5217 * If the src is a directory, then we must always pre-delete
5220 * If we successfully delete the dst in advance, and the rename fails
5221 * X/Open requires that errno be EIO.
5225 if (!S_ISDIR(dst_st.st_mode) || S_ISDIR(src_st.st_mode)) {
5227 d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam,
5228 S_ISDIR(dst_st.st_mode));
5230 /* Need to delete all versions ? */
5231 if ((d_sts == 0) && (vms_unlink_all_versions == 1)) {
5234 while (lstat(dst_st.st_devnam, &dst_st.crtl_stat) == 0) {
5235 d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam, 0);
5240 /* Make sure that we do not loop forever */
5252 /* We killed the destination, so only errno now is EIO */
5257 /* Originally the idea was to call the CRTL rename() and only
5258 * try the lib$rename_file if it failed.
5259 * It turns out that there are too many variants in what the
5260 * the CRTL rename might do, so only use lib$rename_file
5265 /* Is the source and dest both in VMS format */
5266 /* if the source is a directory, then need to fileify */
5267 /* and dest must be a directory or non-existent. */
5272 unsigned long flags;
5273 struct dsc$descriptor_s old_file_dsc;
5274 struct dsc$descriptor_s new_file_dsc;
5276 /* We need to modify the src and dst depending
5277 * on if one or more of them are directories.
5280 vms_dst = (char *)PerlMem_malloc(VMS_MAXRSS);
5281 if (vms_dst == NULL)
5282 _ckvmssts_noperl(SS$_INSFMEM);
5284 if (S_ISDIR(src_st.st_mode)) {
5286 char * vms_dir_file;
5288 vms_dir_file = (char *)PerlMem_malloc(VMS_MAXRSS);
5289 if (vms_dir_file == NULL)
5290 _ckvmssts_noperl(SS$_INSFMEM);
5292 /* If the dest is a directory, we must remove it */
5295 d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam, 1);
5297 PerlMem_free(vms_dst);
5305 /* The dest must be a VMS file specification */
5306 ret_str = int_tovmsspec(dst, vms_dst, 0, NULL);
5307 if (ret_str == NULL) {
5308 PerlMem_free(vms_dst);
5313 /* The source must be a file specification */
5314 ret_str = do_fileify_dirspec(vms_dst, vms_dir_file, 0, NULL);
5315 if (ret_str == NULL) {
5316 PerlMem_free(vms_dst);
5317 PerlMem_free(vms_dir_file);
5321 PerlMem_free(vms_dst);
5322 vms_dst = vms_dir_file;
5325 /* File to file or file to new dir */
5327 if ((dst_sts == 0) && S_ISDIR(dst_st.st_mode)) {
5328 /* VMS pathify a dir target */
5329 ret_str = int_tovmspath(dst, vms_dst, NULL);
5330 if (ret_str == NULL) {
5331 PerlMem_free(vms_dst);
5336 char * v_spec, * r_spec, * d_spec, * n_spec;
5337 char * e_spec, * vs_spec;
5338 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
5340 /* fileify a target VMS file specification */
5341 ret_str = int_tovmsspec(dst, vms_dst, 0, NULL);
5342 if (ret_str == NULL) {
5343 PerlMem_free(vms_dst);
5348 sts = vms_split_path(vms_dst, &v_spec, &v_len, &r_spec, &r_len,
5349 &d_spec, &d_len, &n_spec, &n_len, &e_spec,
5350 &e_len, &vs_spec, &vs_len);
5353 /* Get rid of the version */
5357 /* Need to specify a '.' so that the extension */
5358 /* is not inherited */
5359 strcat(vms_dst,".");
5365 old_file_dsc.dsc$a_pointer = src_st.st_devnam;
5366 old_file_dsc.dsc$w_length = strlen(src_st.st_devnam);
5367 old_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
5368 old_file_dsc.dsc$b_class = DSC$K_CLASS_S;
5370 new_file_dsc.dsc$a_pointer = vms_dst;
5371 new_file_dsc.dsc$w_length = strlen(vms_dst);
5372 new_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
5373 new_file_dsc.dsc$b_class = DSC$K_CLASS_S;
5376 #if defined(NAML$C_MAXRSS)
5377 flags |= 4; /* LIB$M_FIL_LONG_NAMES (bit 2) */
5380 sts = lib$rename_file(&old_file_dsc,
5384 NULL, NULL, NULL, NULL, NULL, NULL, NULL);
5385 if (!$VMS_STATUS_SUCCESS(sts)) {
5387 /* We could have failed because VMS style permissions do not
5388 * permit renames that UNIX will allow. Just like the hack
5391 sts = vms_rename_with_acl(aTHX_ &old_file_dsc, &new_file_dsc, flags);
5394 PerlMem_free(vms_dst);
5395 if (!$VMS_STATUS_SUCCESS(sts)) {
5402 if (vms_unlink_all_versions) {
5403 /* Now get rid of any previous versions of the source file that
5409 src_sts = mp_do_kill_file(aTHX_ src_st.st_devnam,
5410 S_ISDIR(src_st.st_mode));
5411 while (lstat(src_st.st_devnam, &src_st.crtl_stat) == 0) {
5412 src_sts = mp_do_kill_file(aTHX_ src_st.st_devnam,
5413 S_ISDIR(src_st.st_mode));
5418 /* Make sure that we do not loop forever */
5427 /* We deleted the destination, so must force the error to be EIO */
5428 if ((retval != 0) && (pre_delete != 0))
5436 /*{{{char *do_rmsexpand(char *fspec, char *out, int ts, char *def, unsigned opts)*/
5437 /* Shortcut for common case of simple calls to $PARSE and $SEARCH
5438 * to expand file specification. Allows for a single default file
5439 * specification and a simple mask of options. If outbuf is non-NULL,
5440 * it must point to a buffer at least NAM$C_MAXRSS bytes long, into which
5441 * the resultant file specification is placed. If outbuf is NULL, the
5442 * resultant file specification is placed into a static buffer.
5443 * The third argument, if non-NULL, is taken to be a default file
5444 * specification string. The fourth argument is unused at present.
5445 * rmesexpand() returns the address of the resultant string if
5446 * successful, and NULL on error.
5448 * New functionality for previously unused opts value:
5449 * PERL_RMSEXPAND_M_VMS - Force output file specification to VMS format.
5450 * PERL_RMSEXPAND_M_LONG - Want output in long formst
5451 * PERL_RMSEXPAND_M_VMS_IN - Input is already in VMS, so do not vmsify
5452 * PERL_RMSEXPAND_M_SYMLINK - Use symbolic link, not target
5454 static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *);
5458 (const char *filespec,
5460 const char *defspec,
5466 const char * in_spec;
5468 const char * def_spec;
5469 char * vmsfspec, *vmsdefspec;
5473 struct FAB myfab = cc$rms_fab;
5474 rms_setup_nam(mynam);
5476 unsigned long int retsts, trimver, trimtype, haslower = 0, isunix = 0;
5479 /* temp hack until UTF8 is actually implemented */
5480 if (fs_utf8 != NULL)
5483 if (!filespec || !*filespec) {
5484 set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL);
5494 if ((opts & PERL_RMSEXPAND_M_VMS_IN) == 0) {
5495 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
5496 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
5498 /* If this is a UNIX file spec, convert it to VMS */
5499 sts = vms_split_path(filespec, &v_spec, &v_len, &r_spec, &r_len,
5500 &d_spec, &d_len, &n_spec, &n_len, &e_spec,
5501 &e_len, &vs_spec, &vs_len);
5506 vmsfspec = (char *)PerlMem_malloc(VMS_MAXRSS);
5507 if (vmsfspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5508 ret_spec = int_tovmsspec(filespec, vmsfspec, 0, fs_utf8);
5509 if (ret_spec == NULL) {
5510 PerlMem_free(vmsfspec);
5513 in_spec = (const char *)vmsfspec;
5515 /* Unless we are forcing to VMS format, a UNIX input means
5516 * UNIX output, and that requires long names to be used
5518 if ((opts & PERL_RMSEXPAND_M_VMS) == 0)
5519 #if defined(NAML$C_MAXRSS)
5520 opts |= PERL_RMSEXPAND_M_LONG;
5530 rms_set_fna(myfab, mynam, (char *)in_spec, strlen(in_spec)); /* cast ok */
5531 rms_bind_fab_nam(myfab, mynam);
5533 /* Process the default file specification if present */
5535 if (defspec && *defspec) {
5537 t_isunix = is_unix_filespec(defspec);
5539 vmsdefspec = (char *)PerlMem_malloc(VMS_MAXRSS);
5540 if (vmsdefspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5541 ret_spec = int_tovmsspec(defspec, vmsdefspec, 0, dfs_utf8);
5543 if (ret_spec == NULL) {
5544 /* Clean up and bail */
5545 PerlMem_free(vmsdefspec);
5546 if (vmsfspec != NULL)
5547 PerlMem_free(vmsfspec);
5550 def_spec = (const char *)vmsdefspec;
5552 rms_set_dna(myfab, mynam,
5553 (char *)def_spec, strlen(def_spec)); /* cast ok */
5556 /* Now we need the expansion buffers */
5557 esa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
5558 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5559 #if defined(NAML$C_MAXRSS)
5560 esal = (char *)PerlMem_malloc(VMS_MAXRSS);
5561 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5563 rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS-1);
5565 /* If a NAML block is used RMS always writes to the long and short
5566 * addresses unless you suppress the short name.
5568 #if defined(NAML$C_MAXRSS)
5569 outbufl = (char *)PerlMem_malloc(VMS_MAXRSS);
5570 if (outbufl == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5572 rms_set_rsal(mynam, outbuf, NAM$C_MAXRSS, outbufl, (VMS_MAXRSS - 1));
5574 #ifdef NAM$M_NO_SHORT_UPCASE
5575 if (decc_efs_case_preserve)
5576 rms_set_nam_nop(mynam, NAM$M_NO_SHORT_UPCASE);
5579 /* We may not want to follow symbolic links */
5580 #ifdef NAML$M_OPEN_SPECIAL
5581 if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0)
5582 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5585 /* First attempt to parse as an existing file */
5586 retsts = sys$parse(&myfab,0,0);
5587 if (!(retsts & STS$K_SUCCESS)) {
5589 /* Could not find the file, try as syntax only if error is not fatal */
5590 rms_set_nam_nop(mynam, NAM$M_SYNCHK);
5591 if (retsts == RMS$_DNF ||
5592 retsts == RMS$_DIR ||
5593 retsts == RMS$_DEV ||
5594 retsts == RMS$_PRV) {
5595 retsts = sys$parse(&myfab,0,0);
5596 if (retsts & STS$K_SUCCESS) goto int_expanded;
5599 /* Still could not parse the file specification */
5600 /*----------------------------------------------*/
5601 sts = rms_free_search_context(&myfab); /* Free search context */
5602 if (vmsdefspec != NULL)
5603 PerlMem_free(vmsdefspec);
5604 if (vmsfspec != NULL)
5605 PerlMem_free(vmsfspec);
5606 if (outbufl != NULL)
5607 PerlMem_free(outbufl);
5611 set_vaxc_errno(retsts);
5612 if (retsts == RMS$_PRV) set_errno(EACCES);
5613 else if (retsts == RMS$_DEV) set_errno(ENODEV);
5614 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
5615 else set_errno(EVMSERR);
5618 retsts = sys$search(&myfab,0,0);
5619 if (!(retsts & STS$K_SUCCESS) && retsts != RMS$_FNF) {
5620 sts = rms_free_search_context(&myfab); /* Free search context */
5621 if (vmsdefspec != NULL)
5622 PerlMem_free(vmsdefspec);
5623 if (vmsfspec != NULL)
5624 PerlMem_free(vmsfspec);
5625 if (outbufl != NULL)
5626 PerlMem_free(outbufl);
5630 set_vaxc_errno(retsts);
5631 if (retsts == RMS$_PRV) set_errno(EACCES);
5632 else set_errno(EVMSERR);
5636 /* If the input filespec contained any lowercase characters,
5637 * downcase the result for compatibility with Unix-minded code. */
5639 if (!decc_efs_case_preserve) {
5641 for (tbuf = rms_get_fna(myfab, mynam); *tbuf; tbuf++)
5642 if (islower(*tbuf)) { haslower = 1; break; }
5645 /* Is a long or a short name expected */
5646 /*------------------------------------*/
5648 #if defined(NAML$C_MAXRSS)
5649 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5650 if (rms_nam_rsll(mynam)) {
5652 speclen = rms_nam_rsll(mynam);
5655 spec_buf = esal; /* Not esa */
5656 speclen = rms_nam_esll(mynam);
5661 if (rms_nam_rsl(mynam)) {
5663 speclen = rms_nam_rsl(mynam);
5666 spec_buf = esa; /* Not esal */
5667 speclen = rms_nam_esl(mynam);
5669 #if defined(NAML$C_MAXRSS)
5672 spec_buf[speclen] = '\0';
5674 /* Trim off null fields added by $PARSE
5675 * If type > 1 char, must have been specified in original or default spec
5676 * (not true for version; $SEARCH may have added version of existing file).
5678 trimver = !rms_is_nam_fnb(mynam, NAM$M_EXP_VER);
5679 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5680 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
5681 ((rms_nam_verl(mynam) - rms_nam_typel(mynam)) == 1);
5684 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
5685 ((rms_nam_ver(mynam) - rms_nam_type(mynam)) == 1);
5687 if (trimver || trimtype) {
5688 if (defspec && *defspec) {
5689 char *defesal = NULL;
5690 char *defesa = NULL;
5691 defesa = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
5692 if (defesa != NULL) {
5693 struct FAB deffab = cc$rms_fab;
5694 #if defined(NAML$C_MAXRSS)
5695 defesal = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
5696 if (defesal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5698 rms_setup_nam(defnam);
5700 rms_bind_fab_nam(deffab, defnam);
5704 (deffab, defnam, (char *)defspec, rms_nam_dns(myfab, mynam));
5706 /* RMS needs the esa/esal as a work area if wildcards are involved */
5707 rms_set_esal(defnam, defesa, NAM$C_MAXRSS, defesal, VMS_MAXRSS - 1);
5709 rms_clear_nam_nop(defnam);
5710 rms_set_nam_nop(defnam, NAM$M_SYNCHK);
5711 #ifdef NAM$M_NO_SHORT_UPCASE
5712 if (decc_efs_case_preserve)
5713 rms_set_nam_nop(defnam, NAM$M_NO_SHORT_UPCASE);
5715 #ifdef NAML$M_OPEN_SPECIAL
5716 if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0)
5717 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5719 if (sys$parse(&deffab,0,0) & STS$K_SUCCESS) {
5721 trimver = !rms_is_nam_fnb(defnam, NAM$M_EXP_VER);
5724 trimtype = !rms_is_nam_fnb(defnam, NAM$M_EXP_TYPE);
5727 if (defesal != NULL)
5728 PerlMem_free(defesal);
5729 PerlMem_free(defesa);
5731 _ckvmssts_noperl(SS$_INSFMEM);
5735 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5736 if (*(rms_nam_verl(mynam)) != '\"')
5737 speclen = rms_nam_verl(mynam) - spec_buf;
5740 if (*(rms_nam_ver(mynam)) != '\"')
5741 speclen = rms_nam_ver(mynam) - spec_buf;
5745 /* If we didn't already trim version, copy down */
5746 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5747 if (speclen > rms_nam_verl(mynam) - spec_buf)
5749 (rms_nam_typel(mynam),
5750 rms_nam_verl(mynam),
5751 speclen - (rms_nam_verl(mynam) - spec_buf));
5752 speclen -= rms_nam_verl(mynam) - rms_nam_typel(mynam);
5755 if (speclen > rms_nam_ver(mynam) - spec_buf)
5757 (rms_nam_type(mynam),
5759 speclen - (rms_nam_ver(mynam) - spec_buf));
5760 speclen -= rms_nam_ver(mynam) - rms_nam_type(mynam);
5765 /* Done with these copies of the input files */
5766 /*-------------------------------------------*/
5767 if (vmsfspec != NULL)
5768 PerlMem_free(vmsfspec);
5769 if (vmsdefspec != NULL)
5770 PerlMem_free(vmsdefspec);
5772 /* If we just had a directory spec on input, $PARSE "helpfully"
5773 * adds an empty name and type for us */
5774 #if defined(NAML$C_MAXRSS)
5775 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5776 if (rms_nam_namel(mynam) == rms_nam_typel(mynam) &&
5777 rms_nam_verl(mynam) == rms_nam_typel(mynam) + 1 &&
5778 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
5779 speclen = rms_nam_namel(mynam) - spec_buf;
5784 if (rms_nam_name(mynam) == rms_nam_type(mynam) &&
5785 rms_nam_ver(mynam) == rms_nam_ver(mynam) + 1 &&
5786 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
5787 speclen = rms_nam_name(mynam) - spec_buf;
5790 /* Posix format specifications must have matching quotes */
5791 if (speclen < (VMS_MAXRSS - 1)) {
5792 if (decc_posix_compliant_pathnames && (spec_buf[0] == '\"')) {
5793 if ((speclen > 1) && (spec_buf[speclen-1] != '\"')) {
5794 spec_buf[speclen] = '\"';
5799 spec_buf[speclen] = '\0';
5800 if (haslower && !decc_efs_case_preserve) __mystrtolower(spec_buf);
5802 /* Have we been working with an expanded, but not resultant, spec? */
5803 /* Also, convert back to Unix syntax if necessary. */
5807 #if defined(NAML$C_MAXRSS)
5808 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5809 rsl = rms_nam_rsll(mynam);
5813 rsl = rms_nam_rsl(mynam);
5816 /* rsl is not present, it means that spec_buf is either */
5817 /* esa or esal, and needs to be copied to outbuf */
5818 /* convert to Unix if desired */
5820 ret_spec = int_tounixspec(spec_buf, outbuf, fs_utf8);
5822 /* VMS file specs are not in UTF-8 */
5823 if (fs_utf8 != NULL)
5825 my_strlcpy(outbuf, spec_buf, VMS_MAXRSS);
5830 /* Now spec_buf is either outbuf or outbufl */
5831 /* We need the result into outbuf */
5833 /* If we need this in UNIX, then we need another buffer */
5834 /* to keep things in order */
5836 char * new_src = NULL;
5837 if (spec_buf == outbuf) {
5838 new_src = (char *)PerlMem_malloc(VMS_MAXRSS);
5839 my_strlcpy(new_src, spec_buf, VMS_MAXRSS);
5843 ret_spec = int_tounixspec(src, outbuf, fs_utf8);
5845 PerlMem_free(new_src);
5848 /* VMS file specs are not in UTF-8 */
5849 if (fs_utf8 != NULL)
5852 /* Copy the buffer if needed */
5853 if (outbuf != spec_buf)
5854 my_strlcpy(outbuf, spec_buf, VMS_MAXRSS);
5860 /* Need to clean up the search context */
5861 rms_set_rsal(mynam, NULL, 0, NULL, 0);
5862 sts = rms_free_search_context(&myfab); /* Free search context */
5864 /* Clean up the extra buffers */
5868 if (outbufl != NULL)
5869 PerlMem_free(outbufl);
5871 /* Return the result */
5875 /* Common simple case - Expand an already VMS spec */
5877 int_rmsexpand_vms(const char * filespec, char * outbuf, unsigned opts) {
5878 opts |= PERL_RMSEXPAND_M_VMS_IN;
5879 return int_rmsexpand(filespec, outbuf, NULL, opts, NULL, NULL);
5882 /* Common simple case - Expand to a VMS spec */
5884 int_rmsexpand_tovms(const char * filespec, char * outbuf, unsigned opts) {
5885 opts |= PERL_RMSEXPAND_M_VMS;
5886 return int_rmsexpand(filespec, outbuf, NULL, opts, NULL, NULL);
5890 /* Entry point used by perl routines */
5893 (pTHX_ const char *filespec,
5896 const char *defspec,
5901 static char __rmsexpand_retbuf[VMS_MAXRSS];
5902 char * expanded, *ret_spec, *ret_buf;
5906 if (ret_buf == NULL) {
5908 Newx(expanded, VMS_MAXRSS, char);
5909 if (expanded == NULL)
5910 _ckvmssts(SS$_INSFMEM);
5913 ret_buf = __rmsexpand_retbuf;
5918 ret_spec = int_rmsexpand(filespec, ret_buf, defspec,
5919 opts, fs_utf8, dfs_utf8);
5921 if (ret_spec == NULL) {
5922 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
5930 /* External entry points */
5932 Perl_rmsexpand(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
5934 return do_rmsexpand(spec, buf, 0, def, opt, NULL, NULL);
5938 Perl_rmsexpand_ts(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
5940 return do_rmsexpand(spec, buf, 1, def, opt, NULL, NULL);
5944 Perl_rmsexpand_utf8(pTHX_ const char *spec, char *buf, const char *def,
5945 unsigned opt, int * fs_utf8, int * dfs_utf8)
5947 return do_rmsexpand(spec, buf, 0, def, opt, fs_utf8, dfs_utf8);
5951 Perl_rmsexpand_utf8_ts(pTHX_ const char *spec, char *buf, const char *def,
5952 unsigned opt, int * fs_utf8, int * dfs_utf8)
5954 return do_rmsexpand(spec, buf, 1, def, opt, fs_utf8, dfs_utf8);
5959 ** The following routines are provided to make life easier when
5960 ** converting among VMS-style and Unix-style directory specifications.
5961 ** All will take input specifications in either VMS or Unix syntax. On
5962 ** failure, all return NULL. If successful, the routines listed below
5963 ** return a pointer to a buffer containing the appropriately
5964 ** reformatted spec (and, therefore, subsequent calls to that routine
5965 ** will clobber the result), while the routines of the same names with
5966 ** a _ts suffix appended will return a pointer to a mallocd string
5967 ** containing the appropriately reformatted spec.
5968 ** In all cases, only explicit syntax is altered; no check is made that
5969 ** the resulting string is valid or that the directory in question
5972 ** fileify_dirspec() - convert a directory spec into the name of the
5973 ** directory file (i.e. what you can stat() to see if it's a dir).
5974 ** The style (VMS or Unix) of the result is the same as the style
5975 ** of the parameter passed in.
5976 ** pathify_dirspec() - convert a directory spec into a path (i.e.
5977 ** what you prepend to a filename to indicate what directory it's in).
5978 ** The style (VMS or Unix) of the result is the same as the style
5979 ** of the parameter passed in.
5980 ** tounixpath() - convert a directory spec into a Unix-style path.
5981 ** tovmspath() - convert a directory spec into a VMS-style path.
5982 ** tounixspec() - convert any file spec into a Unix-style file spec.
5983 ** tovmsspec() - convert any file spec into a VMS-style spec.
5984 ** xxxxx_utf8() - Variants that support UTF8 encoding of Unix-Style file spec.
5986 ** Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>
5987 ** Permission is given to distribute this code as part of the Perl
5988 ** standard distribution under the terms of the GNU General Public
5989 ** License or the Perl Artistic License. Copies of each may be
5990 ** found in the Perl standard distribution.
5993 /*{{{ char * int_fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/
5995 int_fileify_dirspec(const char *dir, char *buf, int *utf8_fl)
5997 unsigned long int dirlen, retlen, hasfilename = 0;
5998 char *cp1, *cp2, *lastdir;
5999 char *trndir, *vmsdir;
6000 unsigned short int trnlnm_iter_count;
6002 if (utf8_fl != NULL)
6005 if (!dir || !*dir) {
6006 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
6008 dirlen = strlen(dir);
6009 while (dirlen && dir[dirlen-1] == '/') --dirlen;
6010 if (!dirlen) { /* We had Unixish '/' -- substitute top of current tree */
6011 if (!decc_posix_compliant_pathnames && decc_disable_posix_root) {
6018 if (dirlen > (VMS_MAXRSS - 1)) {
6019 set_errno(ENAMETOOLONG); set_vaxc_errno(RMS$_SYN);
6022 trndir = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
6023 if (trndir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6024 if (!strpbrk(dir+1,"/]>:") &&
6025 (!decc_posix_compliant_pathnames && decc_disable_posix_root)) {
6026 strcpy(trndir,*dir == '/' ? dir + 1: dir);
6027 trnlnm_iter_count = 0;
6028 while (!strpbrk(trndir,"/]>:") && simple_trnlnm(trndir,trndir,VMS_MAXRSS-1)) {
6029 trnlnm_iter_count++;
6030 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
6032 dirlen = strlen(trndir);
6035 memcpy(trndir, dir, dirlen);
6036 trndir[dirlen] = '\0';
6039 /* At this point we are done with *dir and use *trndir which is a
6040 * copy that can be modified. *dir must not be modified.
6043 /* If we were handed a rooted logical name or spec, treat it like a
6044 * simple directory, so that
6045 * $ Define myroot dev:[dir.]
6046 * ... do_fileify_dirspec("myroot",buf,1) ...
6047 * does something useful.
6049 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".]")) {
6050 trndir[--dirlen] = '\0';
6051 trndir[dirlen-1] = ']';
6053 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".>")) {
6054 trndir[--dirlen] = '\0';
6055 trndir[dirlen-1] = '>';
6058 if ((cp1 = strrchr(trndir,']')) != NULL || (cp1 = strrchr(trndir,'>')) != NULL) {
6059 /* If we've got an explicit filename, we can just shuffle the string. */
6060 if (*(cp1+1)) hasfilename = 1;
6061 /* Similarly, we can just back up a level if we've got multiple levels
6062 of explicit directories in a VMS spec which ends with directories. */
6064 for (cp2 = cp1; cp2 > trndir; cp2--) {
6066 if ((cp2 - 1 > trndir) && (*(cp2 - 1) != '^')) {
6067 /* fix-me, can not scan EFS file specs backward like this */
6068 *cp2 = *cp1; *cp1 = '\0';
6073 if (*cp2 == '[' || *cp2 == '<') break;
6078 vmsdir = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
6079 if (vmsdir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6080 cp1 = strpbrk(trndir,"]:>");
6081 if (cp1 && *(cp1+1) == ':') /* DECNet node spec with :: */
6082 cp1 = strpbrk(cp1+2,"]:>");
6084 if (hasfilename || !cp1) { /* filename present or not VMS */
6086 if (trndir[0] == '.') {
6087 if (trndir[1] == '\0' || (trndir[1] == '/' && trndir[2] == '\0')) {
6088 PerlMem_free(trndir);
6089 PerlMem_free(vmsdir);
6090 return int_fileify_dirspec("[]", buf, NULL);
6092 else if (trndir[1] == '.' &&
6093 (trndir[2] == '\0' || (trndir[2] == '/' && trndir[3] == '\0'))) {
6094 PerlMem_free(trndir);
6095 PerlMem_free(vmsdir);
6096 return int_fileify_dirspec("[-]", buf, NULL);
6099 if (dirlen && trndir[dirlen-1] == '/') { /* path ends with '/'; just add .dir;1 */
6100 dirlen -= 1; /* to last element */
6101 lastdir = strrchr(trndir,'/');
6103 else if ((cp1 = strstr(trndir,"/.")) != NULL) {
6104 /* If we have "/." or "/..", VMSify it and let the VMS code
6105 * below expand it, rather than repeating the code to handle
6106 * relative components of a filespec here */
6108 if (*(cp1+2) == '.') cp1++;
6109 if (*(cp1+2) == '/' || *(cp1+2) == '\0') {
6111 if (int_tovmsspec(trndir, vmsdir, 0, utf8_fl) == NULL) {
6112 PerlMem_free(trndir);
6113 PerlMem_free(vmsdir);
6116 if (strchr(vmsdir,'/') != NULL) {
6117 /* If int_tovmsspec() returned it, it must have VMS syntax
6118 * delimiters in it, so it's a mixed VMS/Unix spec. We take
6119 * the time to check this here only so we avoid a recursion
6120 * loop; otherwise, gigo.
6122 PerlMem_free(trndir);
6123 PerlMem_free(vmsdir);
6124 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN);
6127 if (int_fileify_dirspec(vmsdir, trndir, NULL) == NULL) {
6128 PerlMem_free(trndir);
6129 PerlMem_free(vmsdir);
6132 ret_chr = int_tounixspec(trndir, buf, utf8_fl);
6133 PerlMem_free(trndir);
6134 PerlMem_free(vmsdir);
6138 } while ((cp1 = strstr(cp1,"/.")) != NULL);
6139 lastdir = strrchr(trndir,'/');
6141 else if (dirlen >= 7 && !strcmp(&trndir[dirlen-7],"/000000")) {
6143 /* Ditto for specs that end in an MFD -- let the VMS code
6144 * figure out whether it's a real device or a rooted logical. */
6146 /* This should not happen any more. Allowing the fake /000000
6147 * in a UNIX pathname causes all sorts of problems when trying
6148 * to run in UNIX emulation. So the VMS to UNIX conversions
6149 * now remove the fake /000000 directories.
6152 trndir[dirlen] = '/'; trndir[dirlen+1] = '\0';
6153 if (int_tovmsspec(trndir, vmsdir, 0, NULL) == NULL) {
6154 PerlMem_free(trndir);
6155 PerlMem_free(vmsdir);
6158 if (int_fileify_dirspec(vmsdir, trndir, NULL) == NULL) {
6159 PerlMem_free(trndir);
6160 PerlMem_free(vmsdir);
6163 ret_chr = int_tounixspec(trndir, buf, utf8_fl);
6164 PerlMem_free(trndir);
6165 PerlMem_free(vmsdir);
6170 if ( !(lastdir = cp1 = strrchr(trndir,'/')) &&
6171 !(lastdir = cp1 = strrchr(trndir,']')) &&
6172 !(lastdir = cp1 = strrchr(trndir,'>'))) cp1 = trndir;
6174 cp2 = strrchr(cp1,'.');
6176 int e_len, vs_len = 0;
6179 cp3 = strchr(cp2,';');
6180 e_len = strlen(cp2);
6182 vs_len = strlen(cp3);
6183 e_len = e_len - vs_len;
6185 is_dir = is_dir_ext(cp2, e_len, cp3, vs_len);
6187 if (!decc_efs_charset) {
6188 /* If this is not EFS, then not a directory */
6189 PerlMem_free(trndir);
6190 PerlMem_free(vmsdir);
6192 set_vaxc_errno(RMS$_DIR);
6196 /* Ok, here we have an issue, technically if a .dir shows */
6197 /* from inside a directory, then we should treat it as */
6198 /* xxx^.dir.dir. But we do not have that context at this */
6199 /* point unless this is totally restructured, so we remove */
6200 /* The .dir for now, and fix this better later */
6201 dirlen = cp2 - trndir;
6203 if (decc_efs_charset && !strchr(trndir,'/')) {
6204 /* Dots are allowed in dir names, so escape them if input not in Unix syntax. */
6205 char *cp4 = is_dir ? (cp2 - 1) : cp2;
6207 for (; cp4 > cp1; cp4--) {
6209 if ((cp4 - 1 > trndir) && (*(cp4 - 1) != '^')) {
6210 memmove(cp4 + 1, cp4, trndir + dirlen - cp4 + 1);
6221 retlen = dirlen + 6;
6222 memcpy(buf, trndir, dirlen);
6225 /* We've picked up everything up to the directory file name.
6226 Now just add the type and version, and we're set. */
6227 if ((!decc_efs_case_preserve) && vms_process_case_tolerant)
6231 if (!decc_filename_unix_no_version)
6233 PerlMem_free(trndir);
6234 PerlMem_free(vmsdir);
6237 else { /* VMS-style directory spec */
6239 char *esa, *esal, term, *cp;
6242 unsigned long int cmplen, haslower = 0;
6243 struct FAB dirfab = cc$rms_fab;
6244 rms_setup_nam(savnam);
6245 rms_setup_nam(dirnam);
6247 esa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
6248 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6250 #if defined(NAML$C_MAXRSS)
6251 esal = (char *)PerlMem_malloc(VMS_MAXRSS);
6252 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6254 rms_set_fna(dirfab, dirnam, trndir, strlen(trndir));
6255 rms_bind_fab_nam(dirfab, dirnam);
6256 rms_set_dna(dirfab, dirnam, ".DIR;1", 6);
6257 rms_set_esal(dirnam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1));
6258 #ifdef NAM$M_NO_SHORT_UPCASE
6259 if (decc_efs_case_preserve)
6260 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
6263 for (cp = trndir; *cp; cp++)
6264 if (islower(*cp)) { haslower = 1; break; }
6265 if (!((sts = sys$parse(&dirfab)) & STS$K_SUCCESS)) {
6266 if ((dirfab.fab$l_sts == RMS$_DIR) ||
6267 (dirfab.fab$l_sts == RMS$_DNF) ||
6268 (dirfab.fab$l_sts == RMS$_PRV)) {
6269 rms_set_nam_nop(dirnam, NAM$M_SYNCHK);
6270 sts = sys$parse(&dirfab);
6276 PerlMem_free(trndir);
6277 PerlMem_free(vmsdir);
6279 set_vaxc_errno(dirfab.fab$l_sts);
6285 /* Does the file really exist? */
6286 if (sys$search(&dirfab)& STS$K_SUCCESS) {
6287 /* Yes; fake the fnb bits so we'll check type below */
6288 rms_set_nam_fnb(dirnam, (NAM$M_EXP_TYPE | NAM$M_EXP_VER));
6290 else { /* No; just work with potential name */
6291 if (dirfab.fab$l_sts == RMS$_FNF
6292 || dirfab.fab$l_sts == RMS$_DNF
6293 || dirfab.fab$l_sts == RMS$_FND)
6297 fab_sts = dirfab.fab$l_sts;
6298 sts = rms_free_search_context(&dirfab);
6302 PerlMem_free(trndir);
6303 PerlMem_free(vmsdir);
6304 set_errno(EVMSERR); set_vaxc_errno(fab_sts);
6310 /* Make sure we are using the right buffer */
6311 #if defined(NAML$C_MAXRSS)
6314 my_esa_len = rms_nam_esll(dirnam);
6318 my_esa_len = rms_nam_esl(dirnam);
6319 #if defined(NAML$C_MAXRSS)
6322 my_esa[my_esa_len] = '\0';
6323 if (!rms_is_nam_fnb(dirnam, (NAM$M_EXP_DEV | NAM$M_EXP_DIR))) {
6324 cp1 = strchr(my_esa,']');
6325 if (!cp1) cp1 = strchr(my_esa,'>');
6326 if (cp1) { /* Should always be true */
6327 my_esa_len -= cp1 - my_esa - 1;
6328 memmove(my_esa, cp1 + 1, my_esa_len);
6331 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_TYPE)) { /* Was type specified? */
6332 /* Yep; check version while we're at it, if it's there. */
6333 cmplen = rms_is_nam_fnb(dirnam, NAM$M_EXP_VER) ? 6 : 4;
6334 if (strncmp(rms_nam_typel(dirnam), ".DIR;1", cmplen)) {
6335 /* Something other than .DIR[;1]. Bzzt. */
6336 sts = rms_free_search_context(&dirfab);
6340 PerlMem_free(trndir);
6341 PerlMem_free(vmsdir);
6343 set_vaxc_errno(RMS$_DIR);
6348 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_NAME)) {
6349 /* They provided at least the name; we added the type, if necessary, */
6350 my_strlcpy(buf, my_esa, VMS_MAXRSS);
6351 sts = rms_free_search_context(&dirfab);
6352 PerlMem_free(trndir);
6356 PerlMem_free(vmsdir);
6359 if ((cp1 = strstr(esa,".][000000]")) != NULL) {
6360 for (cp2 = cp1 + 9; *cp2; cp1++,cp2++) *cp1 = *cp2;
6364 if ((cp1 = strrchr(my_esa,']')) == NULL) cp1 = strrchr(my_esa,'>');
6365 if (cp1 == NULL) { /* should never happen */
6366 sts = rms_free_search_context(&dirfab);
6367 PerlMem_free(trndir);
6371 PerlMem_free(vmsdir);
6376 retlen = strlen(my_esa);
6377 cp1 = strrchr(my_esa,'.');
6378 /* ODS-5 directory specifications can have extra "." in them. */
6379 /* Fix-me, can not scan EFS file specifications backwards */
6380 while (cp1 != NULL) {
6381 if ((cp1-1 == my_esa) || (*(cp1-1) != '^'))
6385 while ((cp1 > my_esa) && (*cp1 != '.'))
6392 if ((cp1) != NULL) {
6393 /* There's more than one directory in the path. Just roll back. */
6395 my_strlcpy(buf, my_esa, VMS_MAXRSS);
6398 if (rms_is_nam_fnb(dirnam, NAM$M_ROOT_DIR)) {
6399 /* Go back and expand rooted logical name */
6400 rms_set_nam_nop(dirnam, NAM$M_SYNCHK | NAM$M_NOCONCEAL);
6401 #ifdef NAM$M_NO_SHORT_UPCASE
6402 if (decc_efs_case_preserve)
6403 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
6405 if (!(sys$parse(&dirfab) & STS$K_SUCCESS)) {
6406 sts = rms_free_search_context(&dirfab);
6410 PerlMem_free(trndir);
6411 PerlMem_free(vmsdir);
6413 set_vaxc_errno(dirfab.fab$l_sts);
6417 /* This changes the length of the string of course */
6419 my_esa_len = rms_nam_esll(dirnam);
6421 my_esa_len = rms_nam_esl(dirnam);
6424 retlen = my_esa_len - 9; /* esa - '][' - '].DIR;1' */
6425 cp1 = strstr(my_esa,"][");
6426 if (!cp1) cp1 = strstr(my_esa,"]<");
6427 dirlen = cp1 - my_esa;
6428 memcpy(buf, my_esa, dirlen);
6429 if (!strncmp(cp1+2,"000000]",7)) {
6430 buf[dirlen-1] = '\0';
6431 /* fix-me Not full ODS-5, just extra dots in directories for now */
6432 cp1 = buf + dirlen - 1;
6438 if (*(cp1-1) != '^')
6443 if (*cp1 == '.') *cp1 = ']';
6445 memmove(cp1+8, cp1+1, buf+dirlen-cp1);
6446 memmove(cp1+1,"000000]",7);
6450 memmove(buf+dirlen, cp1+2, retlen-dirlen);
6452 /* Convert last '.' to ']' */
6454 while (*cp != '[') {
6457 /* Do not trip on extra dots in ODS-5 directories */
6458 if ((cp1 == buf) || (*(cp1-1) != '^'))
6462 if (*cp1 == '.') *cp1 = ']';
6464 memmove(cp1+8, cp1+1, buf+dirlen-cp1);
6465 memmove(cp1+1,"000000]",7);
6469 else { /* This is a top-level dir. Add the MFD to the path. */
6470 cp1 = strrchr(my_esa, ':');
6472 memmove(buf, my_esa, cp1 - my_esa + 1);
6473 memmove(buf + (cp1 - my_esa) + 1, "[000000]", 8);
6474 memmove(buf + (cp1 - my_esa) + 9, cp1 + 2, retlen - (cp1 - my_esa + 2));
6475 buf[retlen + 7] = '\0'; /* We've inserted '000000]' */
6478 sts = rms_free_search_context(&dirfab);
6479 /* We've set up the string up through the filename. Add the
6480 type and version, and we're done. */
6481 strcat(buf,".DIR;1");
6483 /* $PARSE may have upcased filespec, so convert output to lower
6484 * case if input contained any lowercase characters. */
6485 if (haslower && !decc_efs_case_preserve) __mystrtolower(buf);
6486 PerlMem_free(trndir);
6490 PerlMem_free(vmsdir);
6493 } /* end of int_fileify_dirspec() */
6496 /*{{{ char *fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/
6498 mp_do_fileify_dirspec(pTHX_ const char *dir,char *buf,int ts, int *utf8_fl)
6500 static char __fileify_retbuf[VMS_MAXRSS];
6501 char * fileified, *ret_spec, *ret_buf;
6505 if (ret_buf == NULL) {
6507 Newx(fileified, VMS_MAXRSS, char);
6508 if (fileified == NULL)
6509 _ckvmssts(SS$_INSFMEM);
6510 ret_buf = fileified;
6512 ret_buf = __fileify_retbuf;
6516 ret_spec = int_fileify_dirspec(dir, ret_buf, utf8_fl);
6518 if (ret_spec == NULL) {
6519 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
6521 Safefree(fileified);
6525 } /* end of do_fileify_dirspec() */
6528 /* External entry points */
6530 Perl_fileify_dirspec(pTHX_ const char *dir, char *buf)
6532 return do_fileify_dirspec(dir, buf, 0, NULL);
6536 Perl_fileify_dirspec_ts(pTHX_ const char *dir, char *buf)
6538 return do_fileify_dirspec(dir, buf, 1, NULL);
6542 Perl_fileify_dirspec_utf8(pTHX_ const char *dir, char *buf, int * utf8_fl)
6544 return do_fileify_dirspec(dir, buf, 0, utf8_fl);
6548 Perl_fileify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int * utf8_fl)
6550 return do_fileify_dirspec(dir, buf, 1, utf8_fl);
6554 int_pathify_dirspec_simple(const char * dir, char * buf,
6555 char * v_spec, int v_len, char * r_spec, int r_len,
6556 char * d_spec, int d_len, char * n_spec, int n_len,
6557 char * e_spec, int e_len, char * vs_spec, int vs_len)
6560 /* VMS specification - Try to do this the simple way */
6561 if ((v_len + r_len > 0) || (d_len > 0)) {
6564 /* No name or extension component, already a directory */
6565 if ((n_len + e_len + vs_len) == 0) {
6570 /* Special case, we may get [.foo]bar instead of [.foo]bar.dir */
6571 /* This results from catfile() being used instead of catdir() */
6572 /* So even though it should not work, we need to allow it */
6574 /* If this is .DIR;1 then do a simple conversion */
6575 is_dir = is_dir_ext(e_spec, e_len, vs_spec, vs_len);
6576 if (is_dir || (e_len == 0) && (d_len > 0)) {
6578 len = v_len + r_len + d_len - 1;
6579 char dclose = d_spec[d_len - 1];
6580 memcpy(buf, dir, len);
6583 memcpy(&buf[len], n_spec, n_len);
6586 buf[len + 1] = '\0';
6591 else if (d_len > 0) {
6592 /* In the olden days, a directory needed to have a .DIR */
6593 /* extension to be a valid directory, but now it could */
6594 /* be a symbolic link */
6596 len = v_len + r_len + d_len - 1;
6597 char dclose = d_spec[d_len - 1];
6598 memcpy(buf, dir, len);
6601 memcpy(&buf[len], n_spec, n_len);
6604 if (decc_efs_charset) {
6606 && (toupper(e_spec[1]) == 'D')
6607 && (toupper(e_spec[2]) == 'I')
6608 && (toupper(e_spec[3]) == 'R')) {
6610 /* Corner case: directory spec with invalid version.
6611 * Valid would have followed is_dir path above.
6613 SETERRNO(ENOTDIR, RMS$_DIR);
6619 memcpy(&buf[len], e_spec, e_len);
6624 SETERRNO(ENOTDIR, RMS$_DIR);
6629 buf[len + 1] = '\0';
6634 set_vaxc_errno(RMS$_DIR);
6640 set_vaxc_errno(RMS$_DIR);
6646 /* Internal routine to make sure or convert a directory to be in a */
6647 /* path specification. No utf8 flag because it is not changed or used */
6649 int_pathify_dirspec(const char *dir, char *buf)
6651 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
6652 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
6653 char * exp_spec, *ret_spec;
6655 unsigned short int trnlnm_iter_count;
6659 if (vms_debug_fileify) {
6661 fprintf(stderr, "int_pathify_dirspec: dir = NULL\n");
6663 fprintf(stderr, "int_pathify_dirspec: dir = %s\n", dir);
6666 /* We may need to lower case the result if we translated */
6667 /* a logical name or got the current working directory */
6670 if (!dir || !*dir) {
6672 set_vaxc_errno(SS$_BADPARAM);
6676 trndir = (char *)PerlMem_malloc(VMS_MAXRSS);
6678 _ckvmssts_noperl(SS$_INSFMEM);
6680 /* If no directory specified use the current default */
6682 my_strlcpy(trndir, dir, VMS_MAXRSS);
6684 getcwd(trndir, VMS_MAXRSS - 1);
6688 /* now deal with bare names that could be logical names */
6689 trnlnm_iter_count = 0;
6690 while (!strpbrk(trndir,"/]:>") && !no_translate_barewords
6691 && simple_trnlnm(trndir, trndir, VMS_MAXRSS)) {
6692 trnlnm_iter_count++;
6694 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER)
6696 trnlen = strlen(trndir);
6698 /* Trap simple rooted lnms, and return lnm:[000000] */
6699 if (!strcmp(trndir+trnlen-2,".]")) {
6700 my_strlcpy(buf, dir, VMS_MAXRSS);
6701 strcat(buf, ":[000000]");
6702 PerlMem_free(trndir);
6704 if (vms_debug_fileify) {
6705 fprintf(stderr, "int_pathify_dirspec: buf = %s\n", buf);
6711 /* At this point we do not work with *dir, but the copy in *trndir */
6713 if (need_to_lower && !decc_efs_case_preserve) {
6714 /* Legacy mode, lower case the returned value */
6715 __mystrtolower(trndir);
6719 /* Some special cases, '..', '.' */
6721 if ((trndir[0] == '.') && ((trndir[1] == '.') || (trndir[1] == '\0'))) {
6722 /* Force UNIX filespec */
6726 /* Is this Unix or VMS format? */
6727 sts = vms_split_path(trndir, &v_spec, &v_len, &r_spec, &r_len,
6728 &d_spec, &d_len, &n_spec, &n_len, &e_spec,
6729 &e_len, &vs_spec, &vs_len);
6732 /* Just a filename? */
6733 if ((v_len + r_len + d_len) == 0) {
6735 /* Now we have a problem, this could be Unix or VMS */
6736 /* We have to guess. .DIR usually means VMS */
6738 /* In UNIX report mode, the .DIR extension is removed */
6739 /* if one shows up, it is for a non-directory or a directory */
6740 /* in EFS charset mode */
6742 /* So if we are in Unix report mode, assume that this */
6743 /* is a relative Unix directory specification */
6746 if (!decc_filename_unix_report && decc_efs_charset) {
6748 is_dir = is_dir_ext(e_spec, e_len, vs_spec, vs_len);
6751 /* Traditional mode, assume .DIR is directory */
6754 memcpy(&buf[2], n_spec, n_len);
6755 buf[n_len + 2] = ']';
6756 buf[n_len + 3] = '\0';
6757 PerlMem_free(trndir);
6758 if (vms_debug_fileify) {
6760 "int_pathify_dirspec: buf = %s\n",
6770 ret_spec = int_pathify_dirspec_simple(trndir, buf,
6771 v_spec, v_len, r_spec, r_len,
6772 d_spec, d_len, n_spec, n_len,
6773 e_spec, e_len, vs_spec, vs_len);
6775 if (ret_spec != NULL) {
6776 PerlMem_free(trndir);
6777 if (vms_debug_fileify) {
6779 "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
6784 /* Simple way did not work, which means that a logical name */
6785 /* was present for the directory specification. */
6786 /* Need to use an rmsexpand variant to decode it completely */
6787 exp_spec = (char *)PerlMem_malloc(VMS_MAXRSS);
6788 if (exp_spec == NULL)
6789 _ckvmssts_noperl(SS$_INSFMEM);
6791 ret_spec = int_rmsexpand_vms(trndir, exp_spec, PERL_RMSEXPAND_M_LONG);
6792 if (ret_spec != NULL) {
6793 sts = vms_split_path(exp_spec, &v_spec, &v_len,
6794 &r_spec, &r_len, &d_spec, &d_len,
6795 &n_spec, &n_len, &e_spec,
6796 &e_len, &vs_spec, &vs_len);
6798 ret_spec = int_pathify_dirspec_simple(
6799 exp_spec, buf, v_spec, v_len, r_spec, r_len,
6800 d_spec, d_len, n_spec, n_len,
6801 e_spec, e_len, vs_spec, vs_len);
6803 if ((ret_spec != NULL) && (!decc_efs_case_preserve)) {
6804 /* Legacy mode, lower case the returned value */
6805 __mystrtolower(ret_spec);
6808 set_vaxc_errno(RMS$_DIR);
6813 PerlMem_free(exp_spec);
6814 PerlMem_free(trndir);
6815 if (vms_debug_fileify) {
6816 if (ret_spec == NULL)
6817 fprintf(stderr, "int_pathify_dirspec: ret_spec = NULL\n");
6820 "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
6825 /* Unix specification, Could be trivial conversion, */
6826 /* but have to deal with trailing '.dir' or extra '.' */
6831 STRLEN dir_len = strlen(trndir);
6833 lastslash = strrchr(trndir, '/');
6834 if (lastslash == NULL)
6841 /* '..' or '.' are valid directory components */
6843 if (lastslash[0] == '.') {
6844 if (lastslash[1] == '\0') {
6846 } else if (lastslash[1] == '.') {
6847 if (lastslash[2] == '\0') {
6850 /* And finally allow '...' */
6851 if ((lastslash[2] == '.') && (lastslash[3] == '\0')) {
6859 lastdot = strrchr(lastslash, '.');
6861 if (lastdot != NULL) {
6863 /* '.dir' is discarded, and any other '.' is invalid */
6864 e_len = strlen(lastdot);
6866 is_dir = is_dir_ext(lastdot, e_len, NULL, 0);
6869 dir_len = dir_len - 4;
6873 my_strlcpy(buf, trndir, VMS_MAXRSS);
6874 if (buf[dir_len - 1] != '/') {
6876 buf[dir_len + 1] = '\0';
6879 /* Under ODS-2 rules, '.' becomes '_', so fix it up */
6880 if (!decc_efs_charset) {
6883 if (str[0] == '.') {
6886 while ((dots[cnt] == '.') && (cnt < 3))
6889 if ((dots[cnt] == '\0') || (dots[cnt] == '/')) {
6895 for (; *str; ++str) {
6896 while (*str == '/') {
6902 /* Have to skip up to three dots which could be */
6903 /* directories, 3 dots being a VMS extension for Perl */
6906 while ((dots[cnt] == '.') && (cnt < 3)) {
6909 if (dots[cnt] == '\0')
6911 if ((cnt > 1) && (dots[cnt] != '/')) {
6917 /* too many dots? */
6918 if ((cnt == 0) || (cnt > 3)) {
6922 if (!dir_start && (*str == '.')) {
6927 PerlMem_free(trndir);
6929 if (vms_debug_fileify) {
6930 if (ret_spec == NULL)
6931 fprintf(stderr, "int_pathify_dirspec: ret_spec = NULL\n");
6934 "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
6940 /*{{{ char *pathify_dirspec[_ts](char *path, char *buf)*/
6942 mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int * utf8_fl)
6944 static char __pathify_retbuf[VMS_MAXRSS];
6945 char * pathified, *ret_spec, *ret_buf;
6949 if (ret_buf == NULL) {
6951 Newx(pathified, VMS_MAXRSS, char);
6952 if (pathified == NULL)
6953 _ckvmssts(SS$_INSFMEM);
6954 ret_buf = pathified;
6956 ret_buf = __pathify_retbuf;
6960 ret_spec = int_pathify_dirspec(dir, ret_buf);
6962 if (ret_spec == NULL) {
6963 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
6965 Safefree(pathified);
6970 } /* end of do_pathify_dirspec() */
6973 /* External entry points */
6975 Perl_pathify_dirspec(pTHX_ const char *dir, char *buf)
6977 return do_pathify_dirspec(dir, buf, 0, NULL);
6981 Perl_pathify_dirspec_ts(pTHX_ const char *dir, char *buf)
6983 return do_pathify_dirspec(dir, buf, 1, NULL);
6987 Perl_pathify_dirspec_utf8(pTHX_ const char *dir, char *buf, int *utf8_fl)
6989 return do_pathify_dirspec(dir, buf, 0, utf8_fl);
6993 Perl_pathify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int *utf8_fl)
6995 return do_pathify_dirspec(dir, buf, 1, utf8_fl);
6998 /* Internal tounixspec routine that does not use a thread context */
6999 /*{{{ char *int_tounixspec[_ts](char *spec, char *buf, int *)*/
7001 int_tounixspec(const char *spec, char *rslt, int * utf8_fl)
7003 char *dirend, *cp1, *cp3, *tmp;
7006 unsigned short int trnlnm_iter_count;
7007 int cmp_rslt, outchars_added;
7008 if (utf8_fl != NULL)
7011 if (vms_debug_fileify) {
7013 fprintf(stderr, "int_tounixspec: spec = NULL\n");
7015 fprintf(stderr, "int_tounixspec: spec = %s\n", spec);
7021 set_vaxc_errno(SS$_BADPARAM);
7024 if (strlen(spec) > (VMS_MAXRSS-1)) {
7026 set_vaxc_errno(SS$_BUFFEROVF);
7030 /* New VMS specific format needs translation
7031 * glob passes filenames with trailing '\n' and expects this preserved.
7033 if (decc_posix_compliant_pathnames) {
7034 if (strncmp(spec, "\"^UP^", 5) == 0) {
7040 tunix = (char *)PerlMem_malloc(VMS_MAXRSS);
7041 if (tunix == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7042 tunix_len = my_strlcpy(tunix, spec, VMS_MAXRSS);
7044 if (tunix[tunix_len - 1] == '\n') {
7045 tunix[tunix_len - 1] = '\"';
7046 tunix[tunix_len] = '\0';
7050 uspec = decc$translate_vms(tunix);
7051 PerlMem_free(tunix);
7052 if ((int)uspec > 0) {
7053 my_strlcpy(rslt, uspec, VMS_MAXRSS);
7058 /* If we can not translate it, makemaker wants as-is */
7059 my_strlcpy(rslt, spec, VMS_MAXRSS);
7066 cmp_rslt = 0; /* Presume VMS */
7067 cp1 = strchr(spec, '/');
7071 /* Look for EFS ^/ */
7072 if (decc_efs_charset) {
7073 while (cp1 != NULL) {
7076 /* Found illegal VMS, assume UNIX */
7081 cp1 = strchr(cp1, '/');
7085 /* Look for "." and ".." */
7086 if (decc_filename_unix_report) {
7087 if (spec[0] == '.') {
7088 if ((spec[1] == '\0') || (spec[1] == '\n')) {
7092 if ((spec[1] == '.') && ((spec[2] == '\0') || (spec[2] == '\n'))) {
7102 /* This is already UNIX or at least nothing VMS understands,
7103 * so all we can reasonably do is unescape extended chars.
7107 cp2 += copy_expand_vms_filename_escape(cp1, cp2, &outchars_added);
7108 cp1 += outchars_added;
7111 if (vms_debug_fileify) {
7112 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
7117 dirend = strrchr(spec,']');
7118 if (dirend == NULL) dirend = strrchr(spec,'>');
7119 if (dirend == NULL) dirend = strchr(spec,':');
7120 if (dirend == NULL) {
7122 cp2 += copy_expand_vms_filename_escape(cp1, cp2, &outchars_added);
7123 cp1 += outchars_added;
7126 if (vms_debug_fileify) {
7127 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
7132 /* Special case 1 - sys$posix_root = / */
7133 if (!decc_disable_posix_root) {
7134 if (strncasecmp(spec, "SYS$POSIX_ROOT:", 15) == 0) {
7141 /* Special case 2 - Convert NLA0: to /dev/null */
7142 cmp_rslt = strncasecmp(spec,"NLA0:", 5);
7143 if (cmp_rslt == 0) {
7144 strcpy(rslt, "/dev/null");
7147 if (spec[6] != '\0') {
7154 /* Also handle special case "SYS$SCRATCH:" */
7155 cmp_rslt = strncasecmp(spec,"SYS$SCRATCH:", 12);
7156 tmp = (char *)PerlMem_malloc(VMS_MAXRSS);
7157 if (tmp == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7158 if (cmp_rslt == 0) {
7161 islnm = simple_trnlnm("TMP", tmp, VMS_MAXRSS-1);
7163 strcpy(rslt, "/tmp");
7166 if (spec[12] != '\0') {
7174 if (*cp2 != '[' && *cp2 != '<') {
7177 else { /* the VMS spec begins with directories */
7179 if (*cp2 == ']' || *cp2 == '>') {
7183 else if ( *cp2 != '^' && *cp2 != '.' && *cp2 != '-') { /* add the implied device */
7184 if (getcwd(tmp, VMS_MAXRSS-1 ,1) == NULL) {
7186 if (vms_debug_fileify) {
7187 fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7191 trnlnm_iter_count = 0;
7194 while (*cp3 != ':' && *cp3) cp3++;
7196 if (strchr(cp3,']') != NULL) break;
7197 trnlnm_iter_count++;
7198 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER+1) break;
7199 } while (vmstrnenv(tmp,tmp,0,fildev,0));
7204 *(cp1++) = *(cp3++);
7205 if (cp1 - rslt > (VMS_MAXRSS - 1)) {
7207 set_errno(ENAMETOOLONG);
7208 set_vaxc_errno(SS$_BUFFEROVF);
7209 if (vms_debug_fileify) {
7210 fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7212 return NULL; /* No room */
7217 if ((*cp2 == '^')) {
7218 cp2 += copy_expand_vms_filename_escape(cp1, cp2, &outchars_added);
7219 cp1 += outchars_added;
7221 else if ( *cp2 == '.') {
7222 if (*(cp2+1) == '.' && *(cp2+2) == '.') {
7223 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
7230 for (; cp2 <= dirend; cp2++) {
7231 if ((*cp2 == '^')) {
7232 /* EFS file escape -- unescape it. */
7233 cp2 += copy_expand_vms_filename_escape(cp1, cp2, &outchars_added) - 1;
7234 cp1 += outchars_added;
7236 else if (*cp2 == ':') {
7238 if (*(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
7240 else if (*cp2 == ']' || *cp2 == '>') {
7241 if (*(cp1-1) != '/') *(cp1++) = '/'; /* Don't double after ellipsis */
7243 else if ((*cp2 == '.') && (*cp2-1 != '^')) {
7245 if (*(cp2+1) == ']' || *(cp2+1) == '>') {
7246 while (*(cp2+1) == ']' || *(cp2+1) == '>' ||
7247 *(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
7248 if (!strncmp(cp2,"[000000",7) && (*(cp2+7) == ']' ||
7249 *(cp2+7) == '>' || *(cp2+7) == '.')) cp2 += 7;
7251 else if ( *(cp2+1) == '.' && *(cp2+2) == '.') {
7252 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) ='/';
7256 else if (*cp2 == '-') {
7257 if (*(cp2-1) == '[' || *(cp2-1) == '<' || *(cp2-1) == '.') {
7258 while (*cp2 == '-') {
7260 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
7262 if (*cp2 != '.' && *cp2 != ']' && *cp2 != '>') { /* we don't allow */
7263 /* filespecs like */
7264 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); /* [fred.--foo.bar] */
7265 if (vms_debug_fileify) {
7266 fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7271 else *(cp1++) = *cp2;
7273 else *(cp1++) = *cp2;
7275 /* Translate the rest of the filename. */
7279 /* Fixme - for compatibility with the CRTL we should be removing */
7280 /* spaces from the file specifications, but this may show that */
7281 /* some tests that were appearing to pass are not really passing */
7287 cp2 += copy_expand_vms_filename_escape(cp1, cp2, &outchars_added);
7288 cp1 += outchars_added;
7291 if (decc_filename_unix_no_version) {
7292 /* Easy, drop the version */
7297 /* Punt - passing the version as a dot will probably */
7298 /* break perl in weird ways, but so did passing */
7299 /* through the ; as a version. Follow the CRTL and */
7300 /* hope for the best. */
7307 /* We will need to fix this properly later */
7308 /* As Perl may be installed on an ODS-5 volume, but not */
7309 /* have the EFS_CHARSET enabled, it still may encounter */
7310 /* filenames with extra dots in them, and a precedent got */
7311 /* set which allowed them to work, that we will uphold here */
7312 /* If extra dots are present in a name and no ^ is on them */
7313 /* VMS assumes that the first one is the extension delimiter */
7314 /* the rest have an implied ^. */
7316 /* this is also a conflict as the . is also a version */
7317 /* delimiter in VMS, */
7319 *(cp1++) = *(cp2++);
7323 /* This is an extension */
7324 if (decc_readdir_dropdotnotype) {
7326 if ((!*cp2) || (*cp2 == ';') || (*cp2 == '.')) {
7327 /* Drop the dot for the extension */
7335 *(cp1++) = *(cp2++);
7340 /* This still leaves /000000/ when working with a
7341 * VMS device root or concealed root.
7347 ulen = strlen(rslt);
7349 /* Get rid of "000000/ in rooted filespecs */
7351 zeros = strstr(rslt, "/000000/");
7352 if (zeros != NULL) {
7354 mlen = ulen - (zeros - rslt) - 7;
7355 memmove(zeros, &zeros[7], mlen);
7362 if (vms_debug_fileify) {
7363 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
7367 } /* end of int_tounixspec() */
7370 /*{{{ char *tounixspec[_ts](char *spec, char *buf, int *)*/
7372 mp_do_tounixspec(pTHX_ const char *spec, char *buf, int ts, int * utf8_fl)
7374 static char __tounixspec_retbuf[VMS_MAXRSS];
7375 char * unixspec, *ret_spec, *ret_buf;
7379 if (ret_buf == NULL) {
7381 Newx(unixspec, VMS_MAXRSS, char);
7382 if (unixspec == NULL)
7383 _ckvmssts(SS$_INSFMEM);
7386 ret_buf = __tounixspec_retbuf;
7390 ret_spec = int_tounixspec(spec, ret_buf, utf8_fl);
7392 if (ret_spec == NULL) {
7393 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
7400 } /* end of do_tounixspec() */
7402 /* External entry points */
7404 Perl_tounixspec(pTHX_ const char *spec, char *buf)
7406 return do_tounixspec(spec, buf, 0, NULL);
7410 Perl_tounixspec_ts(pTHX_ const char *spec, char *buf)
7412 return do_tounixspec(spec,buf,1, NULL);
7416 Perl_tounixspec_utf8(pTHX_ const char *spec, char *buf, int * utf8_fl)
7418 return do_tounixspec(spec,buf,0, utf8_fl);
7422 Perl_tounixspec_utf8_ts(pTHX_ const char *spec, char *buf, int * utf8_fl)
7424 return do_tounixspec(spec,buf,1, utf8_fl);
7428 This procedure is used to identify if a path is based in either
7429 the old SYS$POSIX_ROOT: or the new 8.3 RMS based POSIX root, and
7430 it returns the OpenVMS format directory for it.
7432 It is expecting specifications of only '/' or '/xxxx/'
7434 If a posix root does not exist, or 'xxxx' is not a directory
7435 in the posix root, it returns a failure.
7437 FIX-ME: xxxx could be in UTF-8 and needs to be returned in VTF-7.
7439 It is used only internally by posix_to_vmsspec_hardway().
7443 posix_root_to_vms(char *vmspath, int vmspath_len,
7444 const char *unixpath, const int * utf8_fl)
7447 struct FAB myfab = cc$rms_fab;
7448 rms_setup_nam(mynam);
7449 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7450 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7451 char * esa, * esal, * rsa, * rsal;
7457 unixlen = strlen(unixpath);
7462 #if __CRTL_VER >= 80200000
7463 /* If not a posix spec already, convert it */
7464 if (decc_posix_compliant_pathnames) {
7465 if (strncmp(unixpath,"\"^UP^",5) != 0) {
7466 sprintf(vmspath,"\"^UP^%s\"",unixpath);
7469 /* This is already a VMS specification, no conversion */
7471 my_strlcpy(vmspath, unixpath, vmspath_len + 1);
7480 /* Check to see if this is under the POSIX root */
7481 if (decc_disable_posix_root) {
7485 /* Skip leading / */
7486 if (unixpath[0] == '/') {
7492 strcpy(vmspath,"SYS$POSIX_ROOT:");
7494 /* If this is only the / , or blank, then... */
7495 if (unixpath[0] == '\0') {
7496 /* by definition, this is the answer */
7500 /* Need to look up a directory */
7504 /* Copy and add '^' escape characters as needed */
7507 while (unixpath[i] != 0) {
7510 j += copy_expand_unix_filename_escape
7511 (&vmspath[j], &unixpath[i], &k, utf8_fl);
7515 path_len = strlen(vmspath);
7516 if (vmspath[path_len - 1] == '/')
7518 vmspath[path_len] = ']';
7520 vmspath[path_len] = '\0';
7523 vmspath[vmspath_len] = 0;
7524 if (unixpath[unixlen - 1] == '/')
7526 esal = (char *)PerlMem_malloc(VMS_MAXRSS);
7527 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7528 esa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
7529 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7530 rsal = (char *)PerlMem_malloc(VMS_MAXRSS);
7531 if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7532 rsa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
7533 if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7534 rms_set_fna(myfab, mynam, (char *) vmspath, strlen(vmspath)); /* cast ok */
7535 rms_bind_fab_nam(myfab, mynam);
7536 rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS - 1);
7537 rms_set_rsal(mynam, rsa, NAM$C_MAXRSS, rsal, VMS_MAXRSS - 1);
7538 if (decc_efs_case_preserve)
7539 mynam.naml$b_nop |= NAM$M_NO_SHORT_UPCASE;
7540 #ifdef NAML$M_OPEN_SPECIAL
7541 mynam.naml$l_input_flags |= NAML$M_OPEN_SPECIAL;
7544 /* Set up the remaining naml fields */
7545 sts = sys$parse(&myfab);
7547 /* It failed! Try again as a UNIX filespec */
7556 /* get the Device ID and the FID */
7557 sts = sys$search(&myfab);
7559 /* These are no longer needed */
7564 /* on any failure, returned the POSIX ^UP^ filespec */
7569 specdsc.dsc$a_pointer = vmspath;
7570 specdsc.dsc$w_length = vmspath_len;
7572 dvidsc.dsc$a_pointer = &mynam.naml$t_dvi[1];
7573 dvidsc.dsc$w_length = mynam.naml$t_dvi[0];
7574 sts = lib$fid_to_name
7575 (&dvidsc, mynam.naml$w_fid, &specdsc, &specdsc.dsc$w_length);
7577 /* on any failure, returned the POSIX ^UP^ filespec */
7579 /* This can happen if user does not have permission to read directories */
7580 if (strncmp(unixpath,"\"^UP^",5) != 0)
7581 sprintf(vmspath,"\"^UP^%s\"",unixpath);
7583 my_strlcpy(vmspath, unixpath, vmspath_len + 1);
7586 vmspath[specdsc.dsc$w_length] = 0;
7588 /* Are we expecting a directory? */
7589 if (dir_flag != 0) {
7595 i = specdsc.dsc$w_length - 1;
7599 /* Version must be '1' */
7600 if (vmspath[i--] != '1')
7602 /* Version delimiter is one of ".;" */
7603 if ((vmspath[i] != '.') && (vmspath[i] != ';'))
7606 if (vmspath[i--] != 'R')
7608 if (vmspath[i--] != 'I')
7610 if (vmspath[i--] != 'D')
7612 if (vmspath[i--] != '.')
7614 eptr = &vmspath[i+1];
7616 if ((vmspath[i] == ']') || (vmspath[i] == '>')) {
7617 if (vmspath[i-1] != '^') {
7625 /* Get rid of 6 imaginary zero directory filename */
7626 vmspath[i+1] = '\0';
7630 if (vmspath[i] == '0')
7644 /* /dev/mumble needs to be handled special.
7645 /dev/null becomes NLA0:, And there is the potential for other stuff
7646 like /dev/tty which may need to be mapped to something.
7650 slash_dev_special_to_vms(const char *unixptr, char *vmspath, int vmspath_len)
7657 nextslash = strchr(unixptr, '/');
7658 len = strlen(unixptr);
7659 if (nextslash != NULL)
7660 len = nextslash - unixptr;
7661 cmp = strncmp("null", unixptr, 5);
7663 if (vmspath_len >= 6) {
7664 strcpy(vmspath, "_NLA0:");
7672 /* The built in routines do not understand perl's special needs, so
7673 doing a manual conversion from UNIX to VMS
7675 If the utf8_fl is not null and points to a non-zero value, then
7676 treat 8 bit characters as UTF-8.
7678 The sequence starting with '$(' and ending with ')' will be passed
7679 through with out interpretation instead of being escaped.
7683 posix_to_vmsspec_hardway(char *vmspath, int vmspath_len, const char *unixpath,
7684 int dir_flag, int * utf8_fl)
7688 const char *unixptr;
7689 const char *unixend;
7691 const char *lastslash;
7692 const char *lastdot;
7698 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
7699 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
7701 if (utf8_fl != NULL)
7707 /* Ignore leading "/" characters */
7708 while((unixptr[0] == '/') && (unixptr[1] == '/')) {
7711 unixlen = strlen(unixptr);
7713 /* Do nothing with blank paths */
7720 /* This could have a "^UP^ on the front */
7721 if (strncmp(unixptr,"\"^UP^",5) == 0) {
7727 lastslash = strrchr(unixptr,'/');
7728 lastdot = strrchr(unixptr,'.');
7729 unixend = strrchr(unixptr,'\"');
7730 if (!quoted || !((unixend != NULL) && (unixend[1] == '\0'))) {
7731 unixend = unixptr + unixlen;
7734 /* last dot is last dot or past end of string */
7735 if (lastdot == NULL)
7736 lastdot = unixptr + unixlen;
7738 /* if no directories, set last slash to beginning of string */
7739 if (lastslash == NULL) {
7740 lastslash = unixptr;
7743 /* Watch out for trailing "." after last slash, still a directory */
7744 if ((lastslash[1] == '.') && (lastslash[2] == '\0')) {
7745 lastslash = unixptr + unixlen;
7748 /* Watch out for trailing ".." after last slash, still a directory */
7749 if ((lastslash[1] == '.')&&(lastslash[2] == '.')&&(lastslash[3] == '\0')) {
7750 lastslash = unixptr + unixlen;
7753 /* dots in directories are aways escaped */
7754 if (lastdot < lastslash)
7755 lastdot = unixptr + unixlen;
7758 /* if (unixptr < lastslash) then we are in a directory */
7765 /* Start with the UNIX path */
7766 if (*unixptr != '/') {
7767 /* relative paths */
7769 /* If allowing logical names on relative pathnames, then handle here */
7770 if ((unixptr[0] != '.') && !decc_disable_to_vms_logname_translation &&
7771 !decc_posix_compliant_pathnames) {
7777 /* Find the next slash */
7778 nextslash = strchr(unixptr,'/');
7780 esa = (char *)PerlMem_malloc(vmspath_len);
7781 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7783 trn = (char *)PerlMem_malloc(VMS_MAXRSS);
7784 if (trn == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7786 if (nextslash != NULL) {
7788 seg_len = nextslash - unixptr;
7789 memcpy(esa, unixptr, seg_len);
7793 seg_len = my_strlcpy(esa, unixptr, sizeof(esa));
7795 /* trnlnm(section) */
7796 islnm = vmstrnenv(esa, trn, 0, fildev, 0);
7799 /* Now fix up the directory */
7801 /* Split up the path to find the components */
7802 sts = vms_split_path
7820 /* A logical name must be a directory or the full
7821 specification. It is only a full specification if
7822 it is the only component */
7823 if ((unixptr[seg_len] == '\0') ||
7824 (unixptr[seg_len+1] == '\0')) {
7826 /* Is a directory being required? */
7827 if (((n_len + e_len) != 0) && (dir_flag !=0)) {
7828 /* Not a logical name */
7833 if ((unixptr[seg_len] == '/') || (dir_flag != 0)) {
7834 /* This must be a directory */
7835 if (((n_len + e_len) == 0)&&(seg_len <= vmspath_len)) {
7836 vmslen = my_strlcpy(vmsptr, esa, vmspath_len - 1);
7837 vmsptr[vmslen] = ':';
7839 vmsptr[vmslen] = '\0';
7847 /* must be dev/directory - ignore version */
7848 if ((n_len + e_len) != 0)
7851 /* transfer the volume */
7852 if (v_len > 0 && ((v_len + vmslen) < vmspath_len)) {
7853 memcpy(vmsptr, v_spec, v_len);
7859 /* unroot the rooted directory */
7860 if ((r_len > 0) && ((r_len + d_len + vmslen) < vmspath_len)) {
7862 r_spec[r_len - 1] = ']';
7864 /* This should not be there, but nothing is perfect */
7866 cmp = strcmp(&r_spec[1], "000000.");
7876 memcpy(vmsptr, r_spec, r_len);
7882 /* Bring over the directory. */
7884 ((d_len + vmslen) < vmspath_len)) {
7886 d_spec[d_len - 1] = ']';
7888 cmp = strcmp(&d_spec[1], "000000.");
7899 /* Remove the redundant root */
7907 memcpy(vmsptr, d_spec, d_len);
7921 if (lastslash > unixptr) {
7924 /* skip leading ./ */
7926 while ((unixptr[0] == '.') && (unixptr[1] == '/')) {
7932 /* Are we still in a directory? */
7933 if (unixptr <= lastslash) {
7938 /* if not backing up, then it is relative forward. */
7939 if (!((*unixptr == '.') && (unixptr[1] == '.') &&
7940 ((unixptr[2] == '/') || (&unixptr[2] == unixend)))) {
7948 /* Perl wants an empty directory here to tell the difference
7949 * between a DCL command and a filename
7958 /* Handle two special files . and .. */
7959 if (unixptr[0] == '.') {
7960 if (&unixptr[1] == unixend) {
7967 if ((unixptr[1] == '.') && (&unixptr[2] == unixend)) {
7978 else { /* Absolute PATH handling */
7982 /* Need to find out where root is */
7984 /* In theory, this procedure should never get an absolute POSIX pathname
7985 * that can not be found on the POSIX root.
7986 * In practice, that can not be relied on, and things will show up
7987 * here that are a VMS device name or concealed logical name instead.
7988 * So to make things work, this procedure must be tolerant.
7990 esa = (char *)PerlMem_malloc(vmspath_len);
7991 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7994 nextslash = strchr(&unixptr[1],'/');
7996 if (nextslash != NULL) {
7998 seg_len = nextslash - &unixptr[1];
7999 my_strlcpy(vmspath, unixptr, seg_len + 2);
8002 cmp = strncmp(vmspath, "dev", 4);
8004 sts = slash_dev_special_to_vms(unixptr, vmspath, vmspath_len);
8005 if (sts == SS$_NORMAL)
8009 sts = posix_root_to_vms(esa, vmspath_len, vmspath, utf8_fl);
8012 if ($VMS_STATUS_SUCCESS(sts)) {
8013 /* This is verified to be a real path */
8015 sts = posix_root_to_vms(esa, vmspath_len, "/", NULL);
8016 if ($VMS_STATUS_SUCCESS(sts)) {
8017 vmslen = my_strlcpy(vmspath, esa, vmspath_len + 1);
8018 vmsptr = vmspath + vmslen;
8020 if (unixptr < lastslash) {
8029 cmp = strcmp(rptr,"000000.");
8034 } /* removing 6 zeros */
8035 } /* vmslen < 7, no 6 zeros possible */
8036 } /* Not in a directory */
8037 } /* Posix root found */
8039 /* No posix root, fall back to default directory */
8040 strcpy(vmspath, "SYS$DISK:[");
8041 vmsptr = &vmspath[10];
8043 if (unixptr > lastslash) {
8052 } /* end of verified real path handling */
8057 /* Ok, we have a device or a concealed root that is not in POSIX
8058 * or we have garbage. Make the best of it.
8061 /* Posix to VMS destroyed this, so copy it again */
8062 my_strlcpy(vmspath, &unixptr[1], seg_len + 1);
8063 vmslen = strlen(vmspath); /* We know we're truncating. */
8064 vmsptr = &vmsptr[vmslen];
8067 /* Now do we need to add the fake 6 zero directory to it? */
8069 if ((*lastslash == '/') && (nextslash < lastslash)) {
8070 /* No there is another directory */
8077 /* now we have foo:bar or foo:[000000]bar to decide from */
8078 islnm = vmstrnenv(vmspath, esa, 0, fildev, 0);
8080 if (!islnm && !decc_posix_compliant_pathnames) {
8082 cmp = strncmp("bin", vmspath, 4);
8084 /* bin => SYS$SYSTEM: */
8085 islnm = vmstrnenv("SYS$SYSTEM:", esa, 0, fildev, 0);
8088 /* tmp => SYS$SCRATCH: */
8089 cmp = strncmp("tmp", vmspath, 4);
8091 islnm = vmstrnenv("SYS$SCRATCH:", esa, 0, fildev, 0);
8096 trnend = islnm ? islnm - 1 : 0;
8098 /* if this was a logical name, ']' or '>' must be present */
8099 /* if not a logical name, then assume a device and hope. */
8100 islnm = trnend ? (esa[trnend] == ']' || esa[trnend] == '>') : 0;
8102 /* if log name and trailing '.' then rooted - treat as device */
8103 add_6zero = islnm ? (esa[trnend-1] == '.') : 0;
8105 /* Fix me, if not a logical name, a device lookup should be
8106 * done to see if the device is file structured. If the device
8107 * is not file structured, the 6 zeros should not be put on.
8109 * As it is, perl is occasionally looking for dev:[000000]tty.
8110 * which looks a little strange.
8112 * Not that easy to detect as "/dev" may be file structured with
8113 * special device files.
8116 if (!islnm && (add_6zero == 0) && (*nextslash == '/') &&
8117 (&nextslash[1] == unixend)) {
8118 /* No real directory present */
8123 /* Put the device delimiter on */
8126 unixptr = nextslash;
8129 /* Start directory if needed */
8130 if (!islnm || add_6zero) {
8136 /* add fake 000000] if needed */
8149 } /* non-POSIX translation */
8151 } /* End of relative/absolute path handling */
8153 while ((unixptr <= unixend) && (vmslen < vmspath_len)){
8160 if (dir_start != 0) {
8162 /* First characters in a directory are handled special */
8163 while ((*unixptr == '/') ||
8164 ((*unixptr == '.') &&
8165 ((unixptr[1]=='.') || (unixptr[1]=='/') ||
8166 (&unixptr[1]==unixend)))) {
8171 /* Skip redundant / in specification */
8172 while ((*unixptr == '/') && (dir_start != 0)) {
8175 if (unixptr == lastslash)
8178 if (unixptr == lastslash)
8181 /* Skip redundant ./ characters */
8182 while ((*unixptr == '.') &&
8183 ((unixptr[1] == '/')||(&unixptr[1] == unixend))) {
8186 if (unixptr == lastslash)
8188 if (*unixptr == '/')
8191 if (unixptr == lastslash)
8194 /* Skip redundant ../ characters */
8195 while ((*unixptr == '.') && (unixptr[1] == '.') &&
8196 ((unixptr[2] == '/') || (&unixptr[2] == unixend))) {
8197 /* Set the backing up flag */
8203 unixptr++; /* first . */
8204 unixptr++; /* second . */
8205 if (unixptr == lastslash)
8207 if (*unixptr == '/') /* The slash */
8210 if (unixptr == lastslash)
8213 /* To do: Perl expects /.../ to be translated to [...] on VMS */
8214 /* Not needed when VMS is pretending to be UNIX. */
8216 /* Is this loop stuck because of too many dots? */
8217 if (loop_flag == 0) {
8218 /* Exit the loop and pass the rest through */
8223 /* Are we done with directories yet? */
8224 if (unixptr >= lastslash) {
8226 /* Watch out for trailing dots */
8235 if (*unixptr == '/')
8239 /* Have we stopped backing up? */
8244 /* dir_start continues to be = 1 */
8246 if (*unixptr == '-') {
8248 *vmsptr++ = *unixptr++;
8252 /* Now are we done with directories yet? */
8253 if (unixptr >= lastslash) {
8255 /* Watch out for trailing dots */
8271 if (unixptr >= unixend)
8274 /* Normal characters - More EFS work probably needed */
8280 /* remove multiple / */
8281 while (unixptr[1] == '/') {
8284 if (unixptr == lastslash) {
8285 /* Watch out for trailing dots */
8297 /* To do: Perl expects /.../ to be translated to [...] on VMS */
8298 /* Not needed when VMS is pretending to be UNIX. */
8302 if (unixptr != unixend)
8307 if ((unixptr < lastdot) || (unixptr < lastslash) ||
8308 (&unixptr[1] == unixend)) {
8314 /* trailing dot ==> '^..' on VMS */
8315 if (unixptr == unixend) {
8323 *vmsptr++ = *unixptr++;
8327 if (quoted && (&unixptr[1] == unixend)) {
8331 in_cnt = copy_expand_unix_filename_escape
8332 (vmsptr, unixptr, &out_cnt, utf8_fl);
8342 in_cnt = copy_expand_unix_filename_escape
8343 (vmsptr, unixptr, &out_cnt, utf8_fl);
8350 /* Make sure directory is closed */
8351 if (unixptr == lastslash) {
8353 vmsptr2 = vmsptr - 1;
8355 if (*vmsptr2 != ']') {
8358 /* directories do not end in a dot bracket */
8359 if (*vmsptr2 == '.') {
8363 if (*vmsptr2 != '^') {
8364 vmsptr--; /* back up over the dot */
8372 /* Add a trailing dot if a file with no extension */
8373 vmsptr2 = vmsptr - 1;
8375 (*vmsptr2 != ']') && (*vmsptr2 != '*') && (*vmsptr2 != '%') &&
8376 (*vmsptr2 != ')') && (*lastdot != '.') && (*vmsptr2 != ':')) {
8386 /* A convenience macro for copying dots in filenames and escaping
8387 * them when they haven't already been escaped, with guards to
8388 * avoid checking before the start of the buffer or advancing
8389 * beyond the end of it (allowing room for the NUL terminator).
8391 #define VMSEFS_DOT_WITH_ESCAPE(vmsefsdot,vmsefsbuf,vmsefsbufsiz) STMT_START { \
8392 if ( ((vmsefsdot) > (vmsefsbuf) && *((vmsefsdot) - 1) != '^' \
8393 || ((vmsefsdot) == (vmsefsbuf))) \
8394 && (vmsefsdot) < (vmsefsbuf) + (vmsefsbufsiz) - 3 \
8396 *((vmsefsdot)++) = '^'; \
8398 if ((vmsefsdot) < (vmsefsbuf) + (vmsefsbufsiz) - 2) \
8399 *((vmsefsdot)++) = '.'; \
8402 /*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/
8404 int_tovmsspec(const char *path, char *rslt, int dir_flag, int * utf8_flag)
8410 unsigned long int infront = 0, hasdir = 1;
8413 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
8414 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
8416 if (vms_debug_fileify) {
8418 fprintf(stderr, "int_tovmsspec: path = NULL\n");
8420 fprintf(stderr, "int_tovmsspec: path = %s\n", path);
8424 /* If we fail, we should be setting errno */
8426 set_vaxc_errno(SS$_BADPARAM);
8429 rslt_len = VMS_MAXRSS-1;
8431 /* '.' and '..' are "[]" and "[-]" for a quick check */
8432 if (path[0] == '.') {
8433 if (path[1] == '\0') {
8435 if (utf8_flag != NULL)
8440 if (path[1] == '.' && path[2] == '\0') {
8442 if (utf8_flag != NULL)
8449 /* Posix specifications are now a native VMS format */
8450 /*--------------------------------------------------*/
8451 #if __CRTL_VER >= 80200000
8452 if (decc_posix_compliant_pathnames) {
8453 if (strncmp(path,"\"^UP^",5) == 0) {
8454 posix_to_vmsspec_hardway(rslt, rslt_len, path, dir_flag, utf8_flag);
8460 /* This is really the only way to see if this is already in VMS format */
8461 sts = vms_split_path
8476 /* FIX-ME - If dir_flag is non-zero, then this is a mp_do_vmspath()
8477 replacement, because the above parse just took care of most of
8478 what is needed to do vmspath when the specification is already
8481 And if it is not already, it is easier to do the conversion as
8482 part of this routine than to call this routine and then work on
8486 /* If VMS punctuation was found, it is already VMS format */
8487 if ((v_len != 0) || (r_len != 0) || (d_len != 0) || (vs_len != 0)) {
8488 if (utf8_flag != NULL)
8490 my_strlcpy(rslt, path, VMS_MAXRSS);
8491 if (vms_debug_fileify) {
8492 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8496 /* Now, what to do with trailing "." cases where there is no
8497 extension? If this is a UNIX specification, and EFS characters
8498 are enabled, then the trailing "." should be converted to a "^.".
8499 But if this was already a VMS specification, then it should be
8502 So in the case of ambiguity, leave the specification alone.
8506 /* If there is a possibility of UTF8, then if any UTF8 characters
8507 are present, then they must be converted to VTF-7
8509 if (utf8_flag != NULL)
8511 my_strlcpy(rslt, path, VMS_MAXRSS);
8512 if (vms_debug_fileify) {
8513 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8518 dirend = strrchr(path,'/');
8520 if (dirend == NULL) {
8521 /* If we get here with no Unix directory delimiters, then this is an
8522 * ambiguous file specification, such as a Unix glob specification, a
8523 * shell or make macro, or a filespec that would be valid except for
8524 * unescaped extended characters. The safest thing if it's a macro
8525 * is to pass it through as-is.
8527 if (strstr(path, "$(")) {
8528 my_strlcpy(rslt, path, VMS_MAXRSS);
8529 if (vms_debug_fileify) {
8530 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8536 else if (*(dirend+1) == '.') { /* do we have trailing "/." or "/.." or "/..."? */
8537 if (!*(dirend+2)) dirend +=2;
8538 if (*(dirend+2) == '.' && !*(dirend+3)) dirend += 3;
8539 if (*(dirend+2) == '.' && *(dirend+3) == '.' && !*(dirend+4)) dirend += 4;
8544 lastdot = strrchr(cp2,'.');
8550 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
8552 if (decc_disable_posix_root) {
8553 strcpy(rslt,"sys$disk:[000000]");
8556 strcpy(rslt,"sys$posix_root:[000000]");
8558 if (utf8_flag != NULL)
8560 if (vms_debug_fileify) {
8561 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8565 while (*(++cp2) != '/' && *cp2) *(cp1++) = *cp2;
8567 trndev = (char *)PerlMem_malloc(VMS_MAXRSS);
8568 if (trndev == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8569 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8571 /* DECC special handling */
8573 if (strcmp(rslt,"bin") == 0) {
8574 strcpy(rslt,"sys$system");
8577 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8579 else if (strcmp(rslt,"tmp") == 0) {
8580 strcpy(rslt,"sys$scratch");
8583 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8585 else if (!decc_disable_posix_root) {
8586 strcpy(rslt, "sys$posix_root");
8590 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
8591 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8593 else if (strcmp(rslt,"dev") == 0) {
8594 if (strncmp(cp2,"/null", 5) == 0) {
8595 if ((cp2[5] == 0) || (cp2[5] == '/')) {
8596 strcpy(rslt,"NLA0");
8600 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8606 trnend = islnm ? strlen(trndev) - 1 : 0;
8607 islnm = trnend ? (trndev[trnend] == ']' || trndev[trnend] == '>') : 0;
8608 rooted = islnm ? (trndev[trnend-1] == '.') : 0;
8609 /* If the first element of the path is a logical name, determine
8610 * whether it has to be translated so we can add more directories. */
8611 if (!islnm || rooted) {
8614 if (cp2 == dirend) while (infront++ < 6) *(cp1++) = '0';
8618 if (cp2 != dirend) {
8619 my_strlcpy(rslt, trndev, VMS_MAXRSS);
8620 cp1 = rslt + trnend;
8627 if (decc_disable_posix_root) {
8633 PerlMem_free(trndev);
8638 if (*(cp2+1) == '/' || *(cp2+1) == '\0') {
8639 cp2 += 2; /* skip over "./" - it's redundant */
8640 *(cp1++) = '.'; /* but it does indicate a relative dirspec */
8642 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
8643 *(cp1++) = '-'; /* "../" --> "-" */
8646 else if (*(cp2+1) == '.' && *(cp2+2) == '.' &&
8647 (*(cp2+3) == '/' || *(cp2+3) == '\0')) {
8648 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
8649 if (!*(cp2+4)) *(cp1++) = '.'; /* Simulate trailing '/' for later */
8652 else if ((cp2 != lastdot) || (lastdot < dirend)) {
8653 /* Escape the extra dots in EFS file specifications */
8656 if (cp2 > dirend) cp2 = dirend;
8658 else *(cp1++) = '.';
8660 for (; cp2 < dirend; cp2++) {
8662 if (*(cp2-1) == '/') continue;
8663 if (cp1 > rslt && *(cp1-1) != '.') *(cp1++) = '.';
8666 else if (!infront && *cp2 == '.') {
8667 if (cp2+1 == dirend || *(cp2+1) == '\0') { cp2++; break; }
8668 else if (*(cp2+1) == '/') cp2++; /* skip over "./" - it's redundant */
8669 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
8670 if (cp1 > rslt && (*(cp1-1) == '-' || *(cp1-1) == '[')) *(cp1++) = '-'; /* handle "../" */
8671 else if (cp1 > rslt + 1 && *(cp1-2) == '[') *(cp1-1) = '-';
8676 if (cp2 == dirend) break;
8678 else if ( *(cp2+1) == '.' && *(cp2+2) == '.' &&
8679 (*(cp2+3) == '/' || *(cp2+3) == '\0') ) {
8680 if (cp1 > rslt && *(cp1-1) != '.') *(cp1++) = '.'; /* May already have 1 from '/' */
8681 *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
8683 *(cp1++) = '.'; /* Simulate trailing '/' */
8684 cp2 += 2; /* for loop will incr this to == dirend */
8686 else cp2 += 3; /* Trailing '/' was there, so skip it, too */
8689 if (decc_efs_charset == 0) {
8690 if (cp1 > rslt && *(cp1-1) == '^')
8691 cp1--; /* remove the escape, if any */
8692 *(cp1++) = '_'; /* fix up syntax - '.' in name not allowed */
8695 VMSEFS_DOT_WITH_ESCAPE(cp1, rslt, VMS_MAXRSS);
8700 if (!infront && cp1 > rslt && *(cp1-1) == '-') *(cp1++) = '.';
8702 if (decc_efs_charset == 0) {
8703 if (cp1 > rslt && *(cp1-1) == '^')
8704 cp1--; /* remove the escape, if any */
8708 VMSEFS_DOT_WITH_ESCAPE(cp1, rslt, VMS_MAXRSS);
8713 cp2 += copy_expand_unix_filename_escape(cp1, cp2, &out_cnt, utf8_flag);
8714 cp2--; /* we're in a loop that will increment this */
8720 if (cp1 > rslt && *(cp1-1) == '.') cp1--; /* Unix spec ending in '/' ==> trailing '.' */
8721 if (hasdir) *(cp1++) = ']';
8722 if (*cp2 && *cp2 == '/') cp2++; /* check in case we ended with trailing '/' */
8729 if (decc_efs_charset == 0)
8735 if (cp2 >= path && (cp2 == path || *(cp2-1) != '^')) /* not previously escaped */
8741 if (((cp2 < lastdot) || (cp2[1] == '\0')) &&
8742 decc_readdir_dropdotnotype) {
8743 VMSEFS_DOT_WITH_ESCAPE(cp1, rslt, VMS_MAXRSS);
8746 /* trailing dot ==> '^..' on VMS */
8753 *(cp1++) = *(cp2++);
8758 /* This could be a macro to be passed through */
8759 *(cp1++) = *(cp2++);
8761 const char * save_cp2;
8765 /* paranoid check */
8771 *(cp1++) = *(cp2++);
8772 if (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
8773 *(cp1++) = *(cp2++);
8774 while (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
8775 *(cp1++) = *(cp2++);
8778 *(cp1++) = *(cp2++);
8782 if (is_macro == 0) {
8783 /* Not really a macro - never mind */
8796 /* Don't escape again if following character is
8797 * already something we escape.
8799 if (strchr("\"~`!#%^&()=+\'@[]{}:\\|<>_.", *(cp2+1))) {
8800 *(cp1++) = *(cp2++);
8803 /* But otherwise fall through and escape it. */
8820 if (cp2 >= path && *(cp2-1) != '^') /* not previously escaped */
8822 *(cp1++) = *(cp2++);
8825 /* If it doesn't look like the beginning of a version number,
8826 * or we've been promised there are no version numbers, then
8829 if (decc_filename_unix_no_version) {
8833 size_t all_nums = strspn(cp2+1, "0123456789");
8834 if (all_nums > 5 || *(cp2 + all_nums + 1) != '\0')
8837 *(cp1++) = *(cp2++);
8840 *(cp1++) = *(cp2++);
8843 if ((no_type_seen == 1) && decc_readdir_dropdotnotype) {
8847 /* Fix me for "^]", but that requires making sure that you do
8848 * not back up past the start of the filename
8850 if ((*lcp1 != ']') && (*lcp1 != '*') && (*lcp1 != '%'))
8855 if (utf8_flag != NULL)
8857 if (vms_debug_fileify) {
8858 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8862 } /* end of int_tovmsspec() */
8865 /*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/
8867 mp_do_tovmsspec(pTHX_ const char *path, char *buf, int ts, int dir_flag, int * utf8_flag)
8869 static char __tovmsspec_retbuf[VMS_MAXRSS];
8870 char * vmsspec, *ret_spec, *ret_buf;
8874 if (ret_buf == NULL) {
8876 Newx(vmsspec, VMS_MAXRSS, char);
8877 if (vmsspec == NULL)
8878 _ckvmssts(SS$_INSFMEM);
8881 ret_buf = __tovmsspec_retbuf;
8885 ret_spec = int_tovmsspec(path, ret_buf, 0, utf8_flag);
8887 if (ret_spec == NULL) {
8888 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
8895 } /* end of mp_do_tovmsspec() */
8897 /* External entry points */
8899 Perl_tovmsspec(pTHX_ const char *path, char *buf)
8901 return do_tovmsspec(path, buf, 0, NULL);
8905 Perl_tovmsspec_ts(pTHX_ const char *path, char *buf)
8907 return do_tovmsspec(path, buf, 1, NULL);
8911 Perl_tovmsspec_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
8913 return do_tovmsspec(path, buf, 0, utf8_fl);
8917 Perl_tovmsspec_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
8919 return do_tovmsspec(path, buf, 1, utf8_fl);
8922 /*{{{ char *int_tovmspath(char *path, char *buf, const int *)*/
8923 /* Internal routine for use with out an explicit context present */
8925 int_tovmspath(const char *path, char *buf, int * utf8_fl)
8927 char * ret_spec, *pathified;
8932 pathified = (char *)PerlMem_malloc(VMS_MAXRSS);
8933 if (pathified == NULL)
8934 _ckvmssts_noperl(SS$_INSFMEM);
8936 ret_spec = int_pathify_dirspec(path, pathified);
8938 if (ret_spec == NULL) {
8939 PerlMem_free(pathified);
8943 ret_spec = int_tovmsspec(pathified, buf, 0, utf8_fl);
8945 PerlMem_free(pathified);
8950 /*{{{ char *tovmspath[_ts](char *path, char *buf, const int *)*/
8952 mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl)
8954 static char __tovmspath_retbuf[VMS_MAXRSS];
8956 char *pathified, *vmsified, *cp;
8958 if (path == NULL) return NULL;
8959 pathified = (char *)PerlMem_malloc(VMS_MAXRSS);
8960 if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
8961 if (int_pathify_dirspec(path, pathified) == NULL) {
8962 PerlMem_free(pathified);
8968 Newx(vmsified, VMS_MAXRSS, char);
8969 if (do_tovmsspec(pathified, buf ? buf : vmsified, 0, NULL) == NULL) {
8970 PerlMem_free(pathified);
8971 if (vmsified) Safefree(vmsified);
8974 PerlMem_free(pathified);
8979 vmslen = strlen(vmsified);
8980 Newx(cp,vmslen+1,char);
8981 memcpy(cp,vmsified,vmslen);
8987 my_strlcpy(__tovmspath_retbuf, vmsified, sizeof(__tovmspath_retbuf));
8989 return __tovmspath_retbuf;
8992 } /* end of do_tovmspath() */
8994 /* External entry points */
8996 Perl_tovmspath(pTHX_ const char *path, char *buf)
8998 return do_tovmspath(path, buf, 0, NULL);
9002 Perl_tovmspath_ts(pTHX_ const char *path, char *buf)
9004 return do_tovmspath(path, buf, 1, NULL);
9008 Perl_tovmspath_utf8(pTHX_ const char *path, char *buf, int *utf8_fl)
9010 return do_tovmspath(path, buf, 0, utf8_fl);
9014 Perl_tovmspath_utf8_ts(pTHX_ const char *path, char *buf, int *utf8_fl)
9016 return do_tovmspath(path, buf, 1, utf8_fl);
9020 /*{{{ char *tounixpath[_ts](char *path, char *buf, int * utf8_fl)*/
9022 mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl)
9024 static char __tounixpath_retbuf[VMS_MAXRSS];
9026 char *pathified, *unixified, *cp;
9028 if (path == NULL) return NULL;
9029 pathified = (char *)PerlMem_malloc(VMS_MAXRSS);
9030 if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
9031 if (int_pathify_dirspec(path, pathified) == NULL) {
9032 PerlMem_free(pathified);
9038 Newx(unixified, VMS_MAXRSS, char);
9040 if (do_tounixspec(pathified,buf ? buf : unixified,0,NULL) == NULL) {
9041 PerlMem_free(pathified);
9042 if (unixified) Safefree(unixified);
9045 PerlMem_free(pathified);
9050 unixlen = strlen(unixified);
9051 Newx(cp,unixlen+1,char);
9052 memcpy(cp,unixified,unixlen);
9054 Safefree(unixified);
9058 my_strlcpy(__tounixpath_retbuf, unixified, sizeof(__tounixpath_retbuf));
9059 Safefree(unixified);
9060 return __tounixpath_retbuf;
9063 } /* end of do_tounixpath() */
9065 /* External entry points */
9067 Perl_tounixpath(pTHX_ const char *path, char *buf)
9069 return do_tounixpath(path, buf, 0, NULL);
9073 Perl_tounixpath_ts(pTHX_ const char *path, char *buf)
9075 return do_tounixpath(path, buf, 1, NULL);
9079 Perl_tounixpath_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
9081 return do_tounixpath(path, buf, 0, utf8_fl);
9085 Perl_tounixpath_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
9087 return do_tounixpath(path, buf, 1, utf8_fl);
9091 * @(#)argproc.c 2.2 94/08/16 Mark Pizzolato (mark AT infocomm DOT com)
9093 *****************************************************************************
9095 * Copyright (C) 1989-1994, 2007 by *
9096 * Mark Pizzolato - INFO COMM, Danville, California (510) 837-5600 *
9098 * Permission is hereby granted for the reproduction of this software *
9099 * on condition that this copyright notice is included in source *
9100 * distributions of the software. The code may be modified and *
9101 * distributed under the same terms as Perl itself. *
9103 * 27-Aug-1994 Modified for inclusion in perl5 *
9104 * by Charles Bailey (bailey AT newman DOT upenn DOT edu) *
9105 *****************************************************************************
9109 * getredirection() is intended to aid in porting C programs
9110 * to VMS (Vax-11 C). The native VMS environment does not support
9111 * '>' and '<' I/O redirection, or command line wild card expansion,
9112 * or a command line pipe mechanism using the '|' AND background
9113 * command execution '&'. All of these capabilities are provided to any
9114 * C program which calls this procedure as the first thing in the
9116 * The piping mechanism will probably work with almost any 'filter' type
9117 * of program. With suitable modification, it may useful for other
9118 * portability problems as well.
9120 * Author: Mark Pizzolato (mark AT infocomm DOT com)
9124 struct list_item *next;
9128 static void add_item(struct list_item **head,
9129 struct list_item **tail,
9133 static void mp_expand_wild_cards(pTHX_ char *item,
9134 struct list_item **head,
9135 struct list_item **tail,
9138 static int background_process(pTHX_ int argc, char **argv);
9140 static void pipe_and_fork(pTHX_ char **cmargv);
9142 /*{{{ void getredirection(int *ac, char ***av)*/
9144 mp_getredirection(pTHX_ int *ac, char ***av)
9146 * Process vms redirection arg's. Exit if any error is seen.
9147 * If getredirection() processes an argument, it is erased
9148 * from the vector. getredirection() returns a new argc and argv value.
9149 * In the event that a background command is requested (by a trailing "&"),
9150 * this routine creates a background subprocess, and simply exits the program.
9152 * Warning: do not try to simplify the code for vms. The code
9153 * presupposes that getredirection() is called before any data is
9154 * read from stdin or written to stdout.
9156 * Normal usage is as follows:
9162 * getredirection(&argc, &argv);
9166 int argc = *ac; /* Argument Count */
9167 char **argv = *av; /* Argument Vector */
9168 char *ap; /* Argument pointer */
9169 int j; /* argv[] index */
9170 int item_count = 0; /* Count of Items in List */
9171 struct list_item *list_head = 0; /* First Item in List */
9172 struct list_item *list_tail; /* Last Item in List */
9173 char *in = NULL; /* Input File Name */
9174 char *out = NULL; /* Output File Name */
9175 char *outmode = "w"; /* Mode to Open Output File */
9176 char *err = NULL; /* Error File Name */
9177 char *errmode = "w"; /* Mode to Open Error File */
9178 int cmargc = 0; /* Piped Command Arg Count */
9179 char **cmargv = NULL;/* Piped Command Arg Vector */
9182 * First handle the case where the last thing on the line ends with
9183 * a '&'. This indicates the desire for the command to be run in a
9184 * subprocess, so we satisfy that desire.
9187 if (0 == strcmp("&", ap))
9188 exit(background_process(aTHX_ --argc, argv));
9189 if (*ap && '&' == ap[strlen(ap)-1])
9191 ap[strlen(ap)-1] = '\0';
9192 exit(background_process(aTHX_ argc, argv));
9195 * Now we handle the general redirection cases that involve '>', '>>',
9196 * '<', and pipes '|'.
9198 for (j = 0; j < argc; ++j)
9200 if (0 == strcmp("<", argv[j]))
9204 fprintf(stderr,"No input file after < on command line");
9205 exit(LIB$_WRONUMARG);
9210 if ('<' == *(ap = argv[j]))
9215 if (0 == strcmp(">", ap))
9219 fprintf(stderr,"No output file after > on command line");
9220 exit(LIB$_WRONUMARG);
9239 fprintf(stderr,"No output file after > or >> on command line");
9240 exit(LIB$_WRONUMARG);
9244 if (('2' == *ap) && ('>' == ap[1]))
9261 fprintf(stderr,"No output file after 2> or 2>> on command line");
9262 exit(LIB$_WRONUMARG);
9266 if (0 == strcmp("|", argv[j]))
9270 fprintf(stderr,"No command into which to pipe on command line");
9271 exit(LIB$_WRONUMARG);
9273 cmargc = argc-(j+1);
9274 cmargv = &argv[j+1];
9278 if ('|' == *(ap = argv[j]))
9286 expand_wild_cards(ap, &list_head, &list_tail, &item_count);
9289 * Allocate and fill in the new argument vector, Some Unix's terminate
9290 * the list with an extra null pointer.
9292 argv = (char **) PerlMem_malloc((item_count+1) * sizeof(char *));
9293 if (argv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9295 for (j = 0; j < item_count; ++j, list_head = list_head->next)
9296 argv[j] = list_head->value;
9302 fprintf(stderr,"'|' and '>' may not both be specified on command line");
9303 exit(LIB$_INVARGORD);
9305 pipe_and_fork(aTHX_ cmargv);
9308 /* Check for input from a pipe (mailbox) */
9310 if (in == NULL && 1 == isapipe(0))
9312 char mbxname[L_tmpnam];
9314 long int dvi_item = DVI$_DEVBUFSIZ;
9315 $DESCRIPTOR(mbxnam, "");
9316 $DESCRIPTOR(mbxdevnam, "");
9318 /* Input from a pipe, reopen it in binary mode to disable */
9319 /* carriage control processing. */
9321 fgetname(stdin, mbxname, 1);
9322 mbxnam.dsc$a_pointer = mbxname;
9323 mbxnam.dsc$w_length = strlen(mbxnam.dsc$a_pointer);
9324 lib$getdvi(&dvi_item, 0, &mbxnam, &bufsize, 0, 0);
9325 mbxdevnam.dsc$a_pointer = mbxname;
9326 mbxdevnam.dsc$w_length = sizeof(mbxname);
9327 dvi_item = DVI$_DEVNAM;
9328 lib$getdvi(&dvi_item, 0, &mbxnam, 0, &mbxdevnam, &mbxdevnam.dsc$w_length);
9329 mbxdevnam.dsc$a_pointer[mbxdevnam.dsc$w_length] = '\0';
9332 freopen(mbxname, "rb", stdin);
9335 fprintf(stderr,"Can't reopen input pipe (name: %s) in binary mode",mbxname);
9339 if ((in != NULL) && (NULL == freopen(in, "r", stdin, "mbc=32", "mbf=2")))
9341 fprintf(stderr,"Can't open input file %s as stdin",in);
9344 if ((out != NULL) && (NULL == freopen(out, outmode, stdout, "mbc=32", "mbf=2")))
9346 fprintf(stderr,"Can't open output file %s as stdout",out);
9349 if (out != NULL) vmssetuserlnm("SYS$OUTPUT", out);
9352 if (strcmp(err,"&1") == 0) {
9353 dup2(fileno(stdout), fileno(stderr));
9354 vmssetuserlnm("SYS$ERROR", "SYS$OUTPUT");
9357 if (NULL == (tmperr = fopen(err, errmode, "mbc=32", "mbf=2")))
9359 fprintf(stderr,"Can't open error file %s as stderr",err);
9363 if (NULL == freopen(err, "a", stderr, "mbc=32", "mbf=2"))
9367 vmssetuserlnm("SYS$ERROR", err);
9370 #ifdef ARGPROC_DEBUG
9371 PerlIO_printf(Perl_debug_log, "Arglist:\n");
9372 for (j = 0; j < *ac; ++j)
9373 PerlIO_printf(Perl_debug_log, "argv[%d] = '%s'\n", j, argv[j]);
9375 /* Clear errors we may have hit expanding wildcards, so they don't
9376 show up in Perl's $! later */
9377 set_errno(0); set_vaxc_errno(1);
9378 } /* end of getredirection() */
9382 add_item(struct list_item **head, struct list_item **tail, char *value, int *count)
9386 *head = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
9387 if (head == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9391 (*tail)->next = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
9392 if ((*tail)->next == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9393 *tail = (*tail)->next;
9395 (*tail)->value = value;
9400 mp_expand_wild_cards(pTHX_ char *item, struct list_item **head,
9401 struct list_item **tail, int *count)
9404 unsigned long int context = 0;
9412 $DESCRIPTOR(filespec, "");
9413 $DESCRIPTOR(defaultspec, "SYS$DISK:[]");
9414 $DESCRIPTOR(resultspec, "");
9415 unsigned long int lff_flags = 0;
9419 #ifdef VMS_LONGNAME_SUPPORT
9420 lff_flags = LIB$M_FIL_LONG_NAMES;
9423 for (cp = item; *cp; cp++) {
9424 if (*cp == '*' || *cp == '%' || isspace(*cp)) break;
9425 if (*cp == '.' && *(cp-1) == '.' && *(cp-2) =='.') break;
9427 if (!*cp || isspace(*cp))
9429 add_item(head, tail, item, count);
9434 /* "double quoted" wild card expressions pass as is */
9435 /* From DCL that means using e.g.: */
9436 /* perl program """perl.*""" */
9437 item_len = strlen(item);
9438 if ( '"' == *item && '"' == item[item_len-1] )
9441 item[item_len-2] = '\0';
9442 add_item(head, tail, item, count);
9446 resultspec.dsc$b_dtype = DSC$K_DTYPE_T;
9447 resultspec.dsc$b_class = DSC$K_CLASS_D;
9448 resultspec.dsc$a_pointer = NULL;
9449 vmsspec = (char *)PerlMem_malloc(VMS_MAXRSS);
9450 if (vmsspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9451 if ((isunix = (int) strchr(item,'/')) != (int) NULL)
9452 filespec.dsc$a_pointer = int_tovmsspec(item, vmsspec, 0, NULL);
9453 if (!isunix || !filespec.dsc$a_pointer)
9454 filespec.dsc$a_pointer = item;
9455 filespec.dsc$w_length = strlen(filespec.dsc$a_pointer);
9457 * Only return version specs, if the caller specified a version
9459 had_version = strchr(item, ';');
9461 * Only return device and directory specs, if the caller specified either.
9463 had_device = strchr(item, ':');
9464 had_directory = (isunix || NULL != strchr(item, '[')) || (NULL != strchr(item, '<'));
9466 while ($VMS_STATUS_SUCCESS(sts = lib$find_file
9467 (&filespec, &resultspec, &context,
9468 &defaultspec, 0, &rms_sts, &lff_flags)))
9473 string = (char *)PerlMem_malloc(resultspec.dsc$w_length+1);
9474 if (string == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9475 my_strlcpy(string, resultspec.dsc$a_pointer, resultspec.dsc$w_length+1);
9476 if (NULL == had_version)
9477 *(strrchr(string, ';')) = '\0';
9478 if ((!had_directory) && (had_device == NULL))
9480 if (NULL == (devdir = strrchr(string, ']')))
9481 devdir = strrchr(string, '>');
9482 my_strlcpy(string, devdir + 1, resultspec.dsc$w_length+1);
9485 * Be consistent with what the C RTL has already done to the rest of
9486 * the argv items and lowercase all of these names.
9488 if (!decc_efs_case_preserve) {
9489 for (c = string; *c; ++c)
9493 if (isunix) trim_unixpath(string,item,1);
9494 add_item(head, tail, string, count);
9497 PerlMem_free(vmsspec);
9498 if (sts != RMS$_NMF)
9500 set_vaxc_errno(sts);
9503 case RMS$_FNF: case RMS$_DNF:
9504 set_errno(ENOENT); break;
9506 set_errno(ENOTDIR); break;
9508 set_errno(ENODEV); break;
9509 case RMS$_FNM: case RMS$_SYN:
9510 set_errno(EINVAL); break;
9512 set_errno(EACCES); break;
9514 _ckvmssts_noperl(sts);
9518 add_item(head, tail, item, count);
9519 _ckvmssts_noperl(lib$sfree1_dd(&resultspec));
9520 _ckvmssts_noperl(lib$find_file_end(&context));
9525 pipe_and_fork(pTHX_ char **cmargv)
9528 struct dsc$descriptor_s *vmscmd;
9529 char subcmd[2*MAX_DCL_LINE_LENGTH], *p, *q;
9530 int sts, j, l, ismcr, quote, tquote = 0;
9532 sts = setup_cmddsc(aTHX_ cmargv[0],0,"e,&vmscmd);
9533 vms_execfree(vmscmd);
9538 ismcr = q && toupper(*q) == 'M' && toupper(*(q+1)) == 'C'
9539 && toupper(*(q+2)) == 'R' && !*(q+3);
9541 while (q && l < MAX_DCL_LINE_LENGTH) {
9543 if (j > 0 && quote) {
9549 if (ismcr && j > 1) quote = 1;
9550 tquote = (strchr(q,' ')) != NULL || *q == '\0';
9553 if (quote || tquote) {
9559 if ((quote||tquote) && *q == '"') {
9569 fp = safe_popen(aTHX_ subcmd,"wbF",&sts);
9571 PerlIO_printf(Perl_debug_log,"Can't open output pipe (status %d)",sts);
9576 background_process(pTHX_ int argc, char **argv)
9578 char command[MAX_DCL_SYMBOL + 1] = "$";
9579 $DESCRIPTOR(value, "");
9580 static $DESCRIPTOR(cmd, "BACKGROUND$COMMAND");
9581 static $DESCRIPTOR(null, "NLA0:");
9582 static $DESCRIPTOR(pidsymbol, "SHELL_BACKGROUND_PID");
9584 $DESCRIPTOR(pidstr, "");
9586 unsigned long int flags = 17, one = 1, retsts;
9589 len = my_strlcat(command, argv[0], sizeof(command));
9590 while (--argc && (len < MAX_DCL_SYMBOL))
9592 my_strlcat(command, " \"", sizeof(command));
9593 my_strlcat(command, *(++argv), sizeof(command));
9594 len = my_strlcat(command, "\"", sizeof(command));
9596 value.dsc$a_pointer = command;
9597 value.dsc$w_length = strlen(value.dsc$a_pointer);
9598 _ckvmssts_noperl(lib$set_symbol(&cmd, &value));
9599 retsts = lib$spawn(&cmd, &null, 0, &flags, 0, &pid);
9600 if (retsts == 0x38250) { /* DCL-W-NOTIFY - We must be BATCH, so retry */
9601 _ckvmssts_noperl(lib$spawn(&cmd, &null, 0, &one, 0, &pid));
9604 _ckvmssts_noperl(retsts);
9606 #ifdef ARGPROC_DEBUG
9607 PerlIO_printf(Perl_debug_log, "%s\n", command);
9609 sprintf(pidstring, "%08X", pid);
9610 PerlIO_printf(Perl_debug_log, "%s\n", pidstring);
9611 pidstr.dsc$a_pointer = pidstring;
9612 pidstr.dsc$w_length = strlen(pidstr.dsc$a_pointer);
9613 lib$set_symbol(&pidsymbol, &pidstr);
9617 /***** End of code taken from Mark Pizzolato's argproc.c package *****/
9620 /* OS-specific initialization at image activation (not thread startup) */
9621 /* Older VAXC header files lack these constants */
9622 #ifndef JPI$_RIGHTS_SIZE
9623 # define JPI$_RIGHTS_SIZE 817
9625 #ifndef KGB$M_SUBSYSTEM
9626 # define KGB$M_SUBSYSTEM 0x8
9629 /* Avoid Newx() in vms_image_init as thread context has not been initialized. */
9631 /*{{{void vms_image_init(int *, char ***)*/
9633 vms_image_init(int *argcp, char ***argvp)
9636 char eqv[LNM$C_NAMLENGTH+1] = "";
9637 unsigned int len, tabct = 8, tabidx = 0;
9638 unsigned long int *mask, iosb[2], i, rlst[128], rsz;
9639 unsigned long int iprv[(sizeof(union prvdef) + sizeof(unsigned long int) - 1) / sizeof(unsigned long int)];
9640 unsigned short int dummy, rlen;
9641 struct dsc$descriptor_s **tabvec;
9642 #if defined(PERL_IMPLICIT_CONTEXT)
9645 struct itmlst_3 jpilist[4] = { {sizeof iprv, JPI$_IMAGPRIV, iprv, &dummy},
9646 {sizeof rlst, JPI$_RIGHTSLIST, rlst, &rlen},
9647 { sizeof rsz, JPI$_RIGHTS_SIZE, &rsz, &dummy},
9650 #ifdef KILL_BY_SIGPRC
9651 Perl_csighandler_init();
9654 /* This was moved from the pre-image init handler because on threaded */
9655 /* Perl it was always returning 0 for the default value. */
9656 status = simple_trnlnm("SYS$POSIX_ROOT", eqv, LNM$C_NAMLENGTH);
9659 s = decc$feature_get_index("DECC$DISABLE_POSIX_ROOT");
9662 initial = decc$feature_get_value(s, 4);
9664 /* initial is: 0 if nothing has set the feature */
9665 /* -1 if initialized to default */
9666 /* 1 if set by logical name */
9667 /* 2 if set by decc$feature_set_value */
9668 decc_disable_posix_root = decc$feature_get_value(s, 1);
9670 /* If the value is not valid, force the feature off */
9671 if (decc_disable_posix_root < 0) {
9672 decc$feature_set_value(s, 1, 1);
9673 decc_disable_posix_root = 1;
9677 /* Nothing has asked for it explicitly, so use our own default. */
9678 decc_disable_posix_root = 1;
9679 decc$feature_set_value(s, 1, 1);
9684 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,jpilist,iosb,NULL,NULL));
9685 _ckvmssts_noperl(iosb[0]);
9686 for (i = 0; i < sizeof iprv / sizeof(unsigned long int); i++) {
9687 if (iprv[i]) { /* Running image installed with privs? */
9688 _ckvmssts_noperl(sys$setprv(0,iprv,0,NULL)); /* Turn 'em off. */
9693 /* Rights identifiers might trigger tainting as well. */
9694 if (!will_taint && (rlen || rsz)) {
9695 while (rlen < rsz) {
9696 /* We didn't get all the identifiers on the first pass. Allocate a
9697 * buffer much larger than $GETJPI wants (rsz is size in bytes that
9698 * were needed to hold all identifiers at time of last call; we'll
9699 * allocate that many unsigned long ints), and go back and get 'em.
9700 * If it gave us less than it wanted to despite ample buffer space,
9701 * something's broken. Is your system missing a system identifier?
9703 if (rsz <= jpilist[1].buflen) {
9704 /* Perl_croak accvios when used this early in startup. */
9705 fprintf(stderr, "vms_image_init: $getjpiw refuses to store RIGHTSLIST of %u bytes in buffer of %u bytes.\n%s",
9706 rsz, (unsigned long) jpilist[1].buflen,
9707 "Check your rights database for corruption.\n");
9710 if (jpilist[1].bufadr != rlst) PerlMem_free(jpilist[1].bufadr);
9711 jpilist[1].bufadr = mask = (unsigned long int *) PerlMem_malloc(rsz * sizeof(unsigned long int));
9712 if (mask == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9713 jpilist[1].buflen = rsz * sizeof(unsigned long int);
9714 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,&jpilist[1],iosb,NULL,NULL));
9715 _ckvmssts_noperl(iosb[0]);
9717 mask = (unsigned long int *)jpilist[1].bufadr;
9718 /* Check attribute flags for each identifier (2nd longword); protected
9719 * subsystem identifiers trigger tainting.
9721 for (i = 1; i < (rlen + sizeof(unsigned long int) - 1) / sizeof(unsigned long int); i += 2) {
9722 if (mask[i] & KGB$M_SUBSYSTEM) {
9727 if (mask != rlst) PerlMem_free(mask);
9730 /* When Perl is in decc_filename_unix_report mode and is run from a concealed
9731 * logical, some versions of the CRTL will add a phanthom /000000/
9732 * directory. This needs to be removed.
9734 if (decc_filename_unix_report) {
9737 ulen = strlen(argvp[0][0]);
9739 zeros = strstr(argvp[0][0], "/000000/");
9740 if (zeros != NULL) {
9742 mlen = ulen - (zeros - argvp[0][0]) - 7;
9743 memmove(zeros, &zeros[7], mlen);
9745 argvp[0][0][ulen] = '\0';
9748 /* It also may have a trailing dot that needs to be removed otherwise
9749 * it will be converted to VMS mode incorrectly.
9752 if ((argvp[0][0][ulen] == '.') && (decc_readdir_dropdotnotype))
9753 argvp[0][0][ulen] = '\0';
9756 /* We need to use this hack to tell Perl it should run with tainting,
9757 * since its tainting flag may be part of the PL_curinterp struct, which
9758 * hasn't been allocated when vms_image_init() is called.
9761 char **newargv, **oldargv;
9763 newargv = (char **) PerlMem_malloc(((*argcp)+2) * sizeof(char *));
9764 if (newargv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9765 newargv[0] = oldargv[0];
9766 newargv[1] = (char *)PerlMem_malloc(3 * sizeof(char));
9767 if (newargv[1] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9768 strcpy(newargv[1], "-T");
9769 Copy(&oldargv[1],&newargv[2],(*argcp)-1,char **);
9771 newargv[*argcp] = NULL;
9772 /* We orphan the old argv, since we don't know where it's come from,
9773 * so we don't know how to free it.
9777 else { /* Did user explicitly request tainting? */
9779 char *cp, **av = *argvp;
9780 for (i = 1; i < *argcp; i++) {
9781 if (*av[i] != '-') break;
9782 for (cp = av[i]+1; *cp; cp++) {
9783 if (*cp == 'T') { will_taint = 1; break; }
9784 else if ( (*cp == 'd' || *cp == 'V') && *(cp+1) == ':' ||
9785 strchr("DFIiMmx",*cp)) break;
9787 if (will_taint) break;
9792 len = my_trnlnm("PERL_ENV_TABLES",eqv,tabidx);
9795 tabvec = (struct dsc$descriptor_s **)
9796 PerlMem_malloc(tabct * sizeof(struct dsc$descriptor_s *));
9797 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9799 else if (tabidx >= tabct) {
9801 tabvec = (struct dsc$descriptor_s **) PerlMem_realloc(tabvec, tabct * sizeof(struct dsc$descriptor_s *));
9802 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9804 tabvec[tabidx] = (struct dsc$descriptor_s *) PerlMem_malloc(sizeof(struct dsc$descriptor_s));
9805 if (tabvec[tabidx] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9806 tabvec[tabidx]->dsc$w_length = len;
9807 tabvec[tabidx]->dsc$b_dtype = DSC$K_DTYPE_T;
9808 tabvec[tabidx]->dsc$b_class = DSC$K_CLASS_S;
9809 tabvec[tabidx]->dsc$a_pointer = (char *)PerlMem_malloc(len + 1);
9810 if (tabvec[tabidx]->dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9811 my_strlcpy(tabvec[tabidx]->dsc$a_pointer, eqv, len + 1);
9813 if (tabidx) { tabvec[tabidx] = NULL; env_tables = tabvec; }
9815 getredirection(argcp,argvp);
9816 #if defined(USE_ITHREADS) && ( defined(__DECC) || defined(__DECCXX) )
9818 # include <reentrancy.h>
9819 decc$set_reentrancy(C$C_MULTITHREAD);
9828 * Trim Unix-style prefix off filespec, so it looks like what a shell
9829 * glob expansion would return (i.e. from specified prefix on, not
9830 * full path). Note that returned filespec is Unix-style, regardless
9831 * of whether input filespec was VMS-style or Unix-style.
9833 * fspec is filespec to be trimmed, and wildspec is wildcard spec used to
9834 * determine prefix (both may be in VMS or Unix syntax). opts is a bit
9835 * vector of options; at present, only bit 0 is used, and if set tells
9836 * trim unixpath to try the current default directory as a prefix when
9837 * presented with a possibly ambiguous ... wildcard.
9839 * Returns !=0 on success, with trimmed filespec replacing contents of
9840 * fspec, and 0 on failure, with contents of fpsec unchanged.
9842 /*{{{int trim_unixpath(char *fspec, char *wildspec, int opts)*/
9844 Perl_trim_unixpath(pTHX_ char *fspec, const char *wildspec, int opts)
9846 char *unixified, *unixwild, *tplate, *base, *end, *cp1, *cp2;
9847 int tmplen, reslen = 0, dirs = 0;
9849 if (!wildspec || !fspec) return 0;
9851 unixwild = (char *)PerlMem_malloc(VMS_MAXRSS);
9852 if (unixwild == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9854 if (strpbrk(wildspec,"]>:") != NULL) {
9855 if (int_tounixspec(wildspec, unixwild, NULL) == NULL) {
9856 PerlMem_free(unixwild);
9861 my_strlcpy(unixwild, wildspec, VMS_MAXRSS);
9863 unixified = (char *)PerlMem_malloc(VMS_MAXRSS);
9864 if (unixified == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9865 if (strpbrk(fspec,"]>:") != NULL) {
9866 if (int_tounixspec(fspec, unixified, NULL) == NULL) {
9867 PerlMem_free(unixwild);
9868 PerlMem_free(unixified);
9871 else base = unixified;
9872 /* reslen != 0 ==> we had to unixify resultant filespec, so we must
9873 * check to see that final result fits into (isn't longer than) fspec */
9874 reslen = strlen(fspec);
9878 /* No prefix or absolute path on wildcard, so nothing to remove */
9879 if (!*tplate || *tplate == '/') {
9880 PerlMem_free(unixwild);
9881 if (base == fspec) {
9882 PerlMem_free(unixified);
9885 tmplen = strlen(unixified);
9886 if (tmplen > reslen) {
9887 PerlMem_free(unixified);
9888 return 0; /* not enough space */
9890 /* Copy unixified resultant, including trailing NUL */
9891 memmove(fspec,unixified,tmplen+1);
9892 PerlMem_free(unixified);
9896 for (end = base; *end; end++) ; /* Find end of resultant filespec */
9897 if ((cp1 = strstr(tplate,".../")) == NULL) { /* No ...; just count elts */
9898 for (cp1 = tplate; *cp1; cp1++) if (*cp1 == '/') dirs++;
9899 for (cp1 = end ;cp1 >= base; cp1--)
9900 if ((*cp1 == '/') && !dirs--) /* postdec so we get front of rel path */
9902 if (cp1 != fspec) memmove(fspec,cp1, end - cp1 + 1);
9903 PerlMem_free(unixified);
9904 PerlMem_free(unixwild);
9909 char *front, *nextell, *lcend, *lcfront, *ellipsis = cp1;
9910 int ells = 1, totells, segdirs, match;
9911 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL},
9912 resdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
9914 while ((cp1 = strstr(ellipsis+4,".../")) != NULL) {ellipsis = cp1; ells++;}
9916 for (cp1 = ellipsis+4; *cp1; cp1++) if (*cp1 == '/') dirs++;
9917 tpl = (char *)PerlMem_malloc(VMS_MAXRSS);
9918 if (tpl == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9919 if (ellipsis == tplate && opts & 1) {
9920 /* Template begins with an ellipsis. Since we can't tell how many
9921 * directory names at the front of the resultant to keep for an
9922 * arbitrary starting point, we arbitrarily choose the current
9923 * default directory as a starting point. If it's there as a prefix,
9924 * clip it off. If not, fall through and act as if the leading
9925 * ellipsis weren't there (i.e. return shortest possible path that
9926 * could match template).
9928 if (getcwd(tpl, (VMS_MAXRSS - 1),0) == NULL) {
9930 PerlMem_free(unixified);
9931 PerlMem_free(unixwild);
9934 if (!decc_efs_case_preserve) {
9935 for (cp1 = tpl, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
9936 if (_tolower(*cp1) != _tolower(*cp2)) break;
9938 segdirs = dirs - totells; /* Min # of dirs we must have left */
9939 for (front = cp2+1; *front; front++) if (*front == '/') segdirs--;
9940 if (*cp1 == '\0' && *cp2 == '/' && segdirs < 1) {
9941 memmove(fspec,cp2+1,end - cp2);
9943 PerlMem_free(unixified);
9944 PerlMem_free(unixwild);
9948 /* First off, back up over constant elements at end of path */
9950 for (front = end ; front >= base; front--)
9951 if (*front == '/' && !dirs--) { front++; break; }
9953 lcres = (char *)PerlMem_malloc(VMS_MAXRSS);
9954 if (lcres == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9955 for (cp1=tplate,cp2=lcres; *cp1 && cp2 <= lcres + (VMS_MAXRSS - 1);
9957 if (!decc_efs_case_preserve) {
9958 *cp2 = _tolower(*cp1); /* Make lc copy for match */
9966 PerlMem_free(unixified);
9967 PerlMem_free(unixwild);
9968 PerlMem_free(lcres);
9969 return 0; /* Path too long. */
9972 *cp2 = '\0'; /* Pick up with memcpy later */
9973 lcfront = lcres + (front - base);
9974 /* Now skip over each ellipsis and try to match the path in front of it. */
9976 for (cp1 = ellipsis - 2; cp1 >= tplate; cp1--)
9977 if (*(cp1) == '.' && *(cp1+1) == '.' &&
9978 *(cp1+2) == '.' && *(cp1+3) == '/' ) break;
9979 if (cp1 < tplate) break; /* template started with an ellipsis */
9980 if (cp1 + 4 == ellipsis) { /* Consecutive ellipses */
9981 ellipsis = cp1; continue;
9983 wilddsc.dsc$a_pointer = tpl;
9984 wilddsc.dsc$w_length = resdsc.dsc$w_length = ellipsis - 1 - cp1;
9986 for (segdirs = 0, cp2 = tpl;
9987 cp1 <= ellipsis - 1 && cp2 <= tpl + (VMS_MAXRSS-1);
9989 if (*cp1 == '?') *cp2 = '%'; /* Substitute VMS' wildcard for Unix' */
9991 if (!decc_efs_case_preserve) {
9992 *cp2 = _tolower(*cp1); /* else lowercase for match */
9995 *cp2 = *cp1; /* else preserve case for match */
9998 if (*cp2 == '/') segdirs++;
10000 if (cp1 != ellipsis - 1) {
10002 PerlMem_free(unixified);
10003 PerlMem_free(unixwild);
10004 PerlMem_free(lcres);
10005 return 0; /* Path too long */
10007 /* Back up at least as many dirs as in template before matching */
10008 for (cp1 = lcfront - 1; segdirs && cp1 >= lcres; cp1--)
10009 if (*cp1 == '/' && !segdirs--) { cp1++; break; }
10010 for (match = 0; cp1 > lcres;) {
10011 resdsc.dsc$a_pointer = cp1;
10012 if (str$match_wild(&wilddsc,&resdsc) == STR$_MATCH) {
10014 if (match == 1) lcfront = cp1;
10016 for ( ; cp1 >= lcres; cp1--) if (*cp1 == '/') { cp1++; break; }
10020 PerlMem_free(unixified);
10021 PerlMem_free(unixwild);
10022 PerlMem_free(lcres);
10023 return 0; /* Can't find prefix ??? */
10025 if (match > 1 && opts & 1) {
10026 /* This ... wildcard could cover more than one set of dirs (i.e.
10027 * a set of similar dir names is repeated). If the template
10028 * contains more than 1 ..., upstream elements could resolve the
10029 * ambiguity, but it's not worth a full backtracking setup here.
10030 * As a quick heuristic, clip off the current default directory
10031 * if it's present to find the trimmed spec, else use the
10032 * shortest string that this ... could cover.
10034 char def[NAM$C_MAXRSS+1], *st;
10036 if (getcwd(def, sizeof def,0) == NULL) {
10037 PerlMem_free(unixified);
10038 PerlMem_free(unixwild);
10039 PerlMem_free(lcres);
10043 if (!decc_efs_case_preserve) {
10044 for (cp1 = def, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
10045 if (_tolower(*cp1) != _tolower(*cp2)) break;
10047 segdirs = dirs - totells; /* Min # of dirs we must have left */
10048 for (st = cp2+1; *st; st++) if (*st == '/') segdirs--;
10049 if (*cp1 == '\0' && *cp2 == '/') {
10050 memmove(fspec,cp2+1,end - cp2);
10052 PerlMem_free(unixified);
10053 PerlMem_free(unixwild);
10054 PerlMem_free(lcres);
10057 /* Nope -- stick with lcfront from above and keep going. */
10060 memmove(fspec,base + (lcfront - lcres), lcend - lcfront + 1);
10062 PerlMem_free(unixified);
10063 PerlMem_free(unixwild);
10064 PerlMem_free(lcres);
10068 } /* end of trim_unixpath() */
10073 * VMS readdir() routines.
10074 * Written by Rich $alz, <rsalz@bbn.com> in August, 1990.
10076 * 21-Jul-1994 Charles Bailey bailey@newman.upenn.edu
10077 * Minor modifications to original routines.
10080 /* readdir may have been redefined by reentr.h, so make sure we get
10081 * the local version for what we do here.
10086 #if !defined(PERL_IMPLICIT_CONTEXT)
10087 # define readdir Perl_readdir
10089 # define readdir(a) Perl_readdir(aTHX_ a)
10092 /* Number of elements in vms_versions array */
10093 #define VERSIZE(e) (sizeof e->vms_versions / sizeof e->vms_versions[0])
10096 * Open a directory, return a handle for later use.
10098 /*{{{ DIR *opendir(char*name) */
10100 Perl_opendir(pTHX_ const char *name)
10106 Newx(dir, VMS_MAXRSS, char);
10107 if (int_tovmspath(name, dir, NULL) == NULL) {
10111 /* Check access before stat; otherwise stat does not
10112 * accurately report whether it's a directory.
10114 if (!strstr(dir, "::") /* sys$check_access doesn't do remotes */
10115 && !cando_by_name_int(S_IRUSR,0,dir,PERL_RMSEXPAND_M_VMS_IN)) {
10116 /* cando_by_name has already set errno */
10120 if (flex_stat(dir,&sb) == -1) return NULL;
10121 if (!S_ISDIR(sb.st_mode)) {
10123 set_errno(ENOTDIR); set_vaxc_errno(RMS$_DIR);
10126 /* Get memory for the handle, and the pattern. */
10128 Newx(dd->pattern,strlen(dir)+sizeof "*.*" + 1,char);
10130 /* Fill in the fields; mainly playing with the descriptor. */
10131 sprintf(dd->pattern, "%s*.*",dir);
10136 /* By saying we want the result of readdir() in unix format, we are really
10137 * saying we want all the escapes removed, translating characters that
10138 * must be escaped in a VMS-format name to their unescaped form, which is
10139 * presumably allowed in a Unix-format name.
10141 dd->flags = decc_filename_unix_report ? PERL_VMSDIR_M_UNIXSPECS : 0;
10142 dd->pat.dsc$a_pointer = dd->pattern;
10143 dd->pat.dsc$w_length = strlen(dd->pattern);
10144 dd->pat.dsc$b_dtype = DSC$K_DTYPE_T;
10145 dd->pat.dsc$b_class = DSC$K_CLASS_S;
10146 #if defined(USE_ITHREADS)
10147 Newx(dd->mutex,1,perl_mutex);
10148 MUTEX_INIT( (perl_mutex *) dd->mutex );
10154 } /* end of opendir() */
10158 * Set the flag to indicate we want versions or not.
10160 /*{{{ void vmsreaddirversions(DIR *dd, int flag)*/
10162 vmsreaddirversions(DIR *dd, int flag)
10165 dd->flags |= PERL_VMSDIR_M_VERSIONS;
10167 dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
10172 * Free up an opened directory.
10174 /*{{{ void closedir(DIR *dd)*/
10176 Perl_closedir(DIR *dd)
10180 sts = lib$find_file_end(&dd->context);
10181 Safefree(dd->pattern);
10182 #if defined(USE_ITHREADS)
10183 MUTEX_DESTROY( (perl_mutex *) dd->mutex );
10184 Safefree(dd->mutex);
10191 * Collect all the version numbers for the current file.
10194 collectversions(pTHX_ DIR *dd)
10196 struct dsc$descriptor_s pat;
10197 struct dsc$descriptor_s res;
10199 char *p, *text, *buff;
10201 unsigned long context, tmpsts;
10203 /* Convenient shorthand. */
10206 /* Add the version wildcard, ignoring the "*.*" put on before */
10207 i = strlen(dd->pattern);
10208 Newx(text,i + e->d_namlen + 3,char);
10209 my_strlcpy(text, dd->pattern, i + 1);
10210 sprintf(&text[i - 3], "%s;*", e->d_name);
10212 /* Set up the pattern descriptor. */
10213 pat.dsc$a_pointer = text;
10214 pat.dsc$w_length = i + e->d_namlen - 1;
10215 pat.dsc$b_dtype = DSC$K_DTYPE_T;
10216 pat.dsc$b_class = DSC$K_CLASS_S;
10218 /* Set up result descriptor. */
10219 Newx(buff, VMS_MAXRSS, char);
10220 res.dsc$a_pointer = buff;
10221 res.dsc$w_length = VMS_MAXRSS - 1;
10222 res.dsc$b_dtype = DSC$K_DTYPE_T;
10223 res.dsc$b_class = DSC$K_CLASS_S;
10225 /* Read files, collecting versions. */
10226 for (context = 0, e->vms_verscount = 0;
10227 e->vms_verscount < VERSIZE(e);
10228 e->vms_verscount++) {
10229 unsigned long rsts;
10230 unsigned long flags = 0;
10232 #ifdef VMS_LONGNAME_SUPPORT
10233 flags = LIB$M_FIL_LONG_NAMES;
10235 tmpsts = lib$find_file(&pat, &res, &context, NULL, NULL, &rsts, &flags);
10236 if (tmpsts == RMS$_NMF || context == 0) break;
10238 buff[VMS_MAXRSS - 1] = '\0';
10239 if ((p = strchr(buff, ';')))
10240 e->vms_versions[e->vms_verscount] = atoi(p + 1);
10242 e->vms_versions[e->vms_verscount] = -1;
10245 _ckvmssts(lib$find_file_end(&context));
10249 } /* end of collectversions() */
10252 * Read the next entry from the directory.
10254 /*{{{ struct dirent *readdir(DIR *dd)*/
10256 Perl_readdir(pTHX_ DIR *dd)
10258 struct dsc$descriptor_s res;
10260 unsigned long int tmpsts;
10261 unsigned long rsts;
10262 unsigned long flags = 0;
10263 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
10264 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
10266 /* Set up result descriptor, and get next file. */
10267 Newx(buff, VMS_MAXRSS, char);
10268 res.dsc$a_pointer = buff;
10269 res.dsc$w_length = VMS_MAXRSS - 1;
10270 res.dsc$b_dtype = DSC$K_DTYPE_T;
10271 res.dsc$b_class = DSC$K_CLASS_S;
10273 #ifdef VMS_LONGNAME_SUPPORT
10274 flags = LIB$M_FIL_LONG_NAMES;
10277 tmpsts = lib$find_file
10278 (&dd->pat, &res, &dd->context, NULL, NULL, &rsts, &flags);
10279 if (dd->context == 0)
10280 tmpsts = RMS$_NMF; /* None left. (should be set, but make sure) */
10282 if (!(tmpsts & 1)) {
10285 break; /* no more files considered success */
10287 SETERRNO(EACCES, tmpsts); break;
10289 SETERRNO(ENODEV, tmpsts); break;
10291 SETERRNO(ENOTDIR, tmpsts); break;
10292 case RMS$_FNF: case RMS$_DNF:
10293 SETERRNO(ENOENT, tmpsts); break;
10295 SETERRNO(EVMSERR, tmpsts);
10301 /* Force the buffer to end with a NUL, and downcase name to match C convention. */
10302 buff[res.dsc$w_length] = '\0';
10303 p = buff + res.dsc$w_length;
10304 while (--p >= buff) if (!isspace(*p)) break;
10306 if (!decc_efs_case_preserve) {
10307 for (p = buff; *p; p++) *p = _tolower(*p);
10310 /* Skip any directory component and just copy the name. */
10311 sts = vms_split_path
10326 if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
10328 /* In Unix report mode, remove the ".dir;1" from the name */
10329 /* if it is a real directory. */
10330 if (decc_filename_unix_report && decc_efs_charset) {
10331 if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) {
10335 ret_sts = flex_lstat(buff, &statbuf);
10336 if ((ret_sts == 0) && S_ISDIR(statbuf.st_mode)) {
10343 /* Drop NULL extensions on UNIX file specification */
10344 if ((e_len == 1) && decc_readdir_dropdotnotype) {
10350 memcpy(dd->entry.d_name, n_spec, n_len + e_len);
10351 dd->entry.d_name[n_len + e_len] = '\0';
10352 dd->entry.d_namlen = n_len + e_len;
10354 /* Convert the filename to UNIX format if needed */
10355 if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
10357 /* Translate the encoded characters. */
10358 /* Fixme: Unicode handling could result in embedded 0 characters */
10359 if (strchr(dd->entry.d_name, '^') != NULL) {
10360 char new_name[256];
10362 p = dd->entry.d_name;
10365 int inchars_read, outchars_added;
10366 inchars_read = copy_expand_vms_filename_escape(q, p, &outchars_added);
10368 q += outchars_added;
10370 /* if outchars_added > 1, then this is a wide file specification */
10371 /* Wide file specifications need to be passed in Perl */
10372 /* counted strings apparently with a Unicode flag */
10375 dd->entry.d_namlen = my_strlcpy(dd->entry.d_name, new_name, sizeof(dd->entry.d_name));
10379 dd->entry.vms_verscount = 0;
10380 if (dd->flags & PERL_VMSDIR_M_VERSIONS) collectversions(aTHX_ dd);
10384 } /* end of readdir() */
10388 * Read the next entry from the directory -- thread-safe version.
10390 /*{{{ int readdir_r(DIR *dd, struct dirent *entry, struct dirent **result)*/
10392 Perl_readdir_r(pTHX_ DIR *dd, struct dirent *entry, struct dirent **result)
10396 MUTEX_LOCK( (perl_mutex *) dd->mutex );
10398 entry = readdir(dd);
10400 retval = ( *result == NULL ? errno : 0 );
10402 MUTEX_UNLOCK( (perl_mutex *) dd->mutex );
10406 } /* end of readdir_r() */
10410 * Return something that can be used in a seekdir later.
10412 /*{{{ long telldir(DIR *dd)*/
10414 Perl_telldir(DIR *dd)
10421 * Return to a spot where we used to be. Brute force.
10423 /*{{{ void seekdir(DIR *dd,long count)*/
10425 Perl_seekdir(pTHX_ DIR *dd, long count)
10429 /* If we haven't done anything yet... */
10430 if (dd->count == 0)
10433 /* Remember some state, and clear it. */
10434 old_flags = dd->flags;
10435 dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
10436 _ckvmssts(lib$find_file_end(&dd->context));
10439 /* The increment is in readdir(). */
10440 for (dd->count = 0; dd->count < count; )
10443 dd->flags = old_flags;
10445 } /* end of seekdir() */
10448 /* VMS subprocess management
10450 * my_vfork() - just a vfork(), after setting a flag to record that
10451 * the current script is trying a Unix-style fork/exec.
10453 * vms_do_aexec() and vms_do_exec() are called in response to the
10454 * perl 'exec' function. If this follows a vfork call, then they
10455 * call out the regular perl routines in doio.c which do an
10456 * execvp (for those who really want to try this under VMS).
10457 * Otherwise, they do exactly what the perl docs say exec should
10458 * do - terminate the current script and invoke a new command
10459 * (See below for notes on command syntax.)
10461 * do_aspawn() and do_spawn() implement the VMS side of the perl
10462 * 'system' function.
10464 * Note on command arguments to perl 'exec' and 'system': When handled
10465 * in 'VMSish fashion' (i.e. not after a call to vfork) The args
10466 * are concatenated to form a DCL command string. If the first non-numeric
10467 * arg begins with '$' (i.e. the perl script had "\$ Type" or some such),
10468 * the command string is handed off to DCL directly. Otherwise,
10469 * the first token of the command is taken as the filespec of an image
10470 * to run. The filespec is expanded using a default type of '.EXE' and
10471 * the process defaults for device, directory, etc., and if found, the resultant
10472 * filespec is invoked using the DCL verb 'MCR', and passed the rest of
10473 * the command string as parameters. This is perhaps a bit complicated,
10474 * but I hope it will form a happy medium between what VMS folks expect
10475 * from lib$spawn and what Unix folks expect from exec.
10478 static int vfork_called;
10480 /*{{{int my_vfork(void)*/
10491 vms_execfree(struct dsc$descriptor_s *vmscmd)
10494 if (vmscmd->dsc$a_pointer) {
10495 PerlMem_free(vmscmd->dsc$a_pointer);
10497 PerlMem_free(vmscmd);
10502 setup_argstr(pTHX_ SV *really, SV **mark, SV **sp)
10504 char *junk, *tmps = NULL;
10512 tmps = SvPV(really,rlen);
10514 cmdlen += rlen + 1;
10519 for (idx++; idx <= sp; idx++) {
10521 junk = SvPVx(*idx,rlen);
10522 cmdlen += rlen ? rlen + 1 : 0;
10525 Newx(PL_Cmd, cmdlen+1, char);
10527 if (tmps && *tmps) {
10528 my_strlcpy(PL_Cmd, tmps, cmdlen + 1);
10531 else *PL_Cmd = '\0';
10532 while (++mark <= sp) {
10534 char *s = SvPVx(*mark,n_a);
10536 if (*PL_Cmd) my_strlcat(PL_Cmd, " ", cmdlen+1);
10537 my_strlcat(PL_Cmd, s, cmdlen+1);
10542 } /* end of setup_argstr() */
10545 static unsigned long int
10546 setup_cmddsc(pTHX_ const char *incmd, int check_img, int *suggest_quote,
10547 struct dsc$descriptor_s **pvmscmd)
10551 char image_name[NAM$C_MAXRSS+1];
10552 char image_argv[NAM$C_MAXRSS+1];
10553 $DESCRIPTOR(defdsc,".EXE");
10554 $DESCRIPTOR(defdsc2,".");
10555 struct dsc$descriptor_s resdsc;
10556 struct dsc$descriptor_s *vmscmd;
10557 struct dsc$descriptor_s imgdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10558 unsigned long int cxt = 0, flags = 1, retsts = SS$_NORMAL;
10559 char *s, *rest, *cp, *wordbreak;
10564 vmscmd = (struct dsc$descriptor_s *)PerlMem_malloc(sizeof(struct dsc$descriptor_s));
10565 if (vmscmd == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10567 /* vmsspec is a DCL command buffer, not just a filename */
10568 vmsspec = (char *)PerlMem_malloc(MAX_DCL_LINE_LENGTH + 1);
10569 if (vmsspec == NULL)
10570 _ckvmssts_noperl(SS$_INSFMEM);
10572 resspec = (char *)PerlMem_malloc(VMS_MAXRSS);
10573 if (resspec == NULL)
10574 _ckvmssts_noperl(SS$_INSFMEM);
10576 /* Make a copy for modification */
10577 cmdlen = strlen(incmd);
10578 cmd = (char *)PerlMem_malloc(cmdlen+1);
10579 if (cmd == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10580 my_strlcpy(cmd, incmd, cmdlen + 1);
10584 resdsc.dsc$a_pointer = resspec;
10585 resdsc.dsc$b_dtype = DSC$K_DTYPE_T;
10586 resdsc.dsc$b_class = DSC$K_CLASS_S;
10587 resdsc.dsc$w_length = VMS_MAXRSS - 1;
10589 vmscmd->dsc$a_pointer = NULL;
10590 vmscmd->dsc$b_dtype = DSC$K_DTYPE_T;
10591 vmscmd->dsc$b_class = DSC$K_CLASS_S;
10592 vmscmd->dsc$w_length = 0;
10593 if (pvmscmd) *pvmscmd = vmscmd;
10595 if (suggest_quote) *suggest_quote = 0;
10597 if (strlen(cmd) > MAX_DCL_LINE_LENGTH) {
10599 PerlMem_free(vmsspec);
10600 PerlMem_free(resspec);
10601 return CLI$_BUFOVF; /* continuation lines currently unsupported */
10606 while (*s && isspace(*s)) s++;
10608 if (*s == '@' || *s == '$') {
10609 vmsspec[0] = *s; rest = s + 1;
10610 for (cp = &vmsspec[1]; *rest && isspace(*rest); rest++,cp++) *cp = *rest;
10612 else { cp = vmsspec; rest = s; }
10614 /* If the first word is quoted, then we need to unquote it and
10615 * escape spaces within it. We'll expand into the resspec buffer,
10616 * then copy back into the cmd buffer, expanding the latter if
10619 if (*rest == '"') {
10624 int soff = s - cmd;
10626 for (cp2 = resspec;
10627 *rest && cp2 - resspec < (VMS_MAXRSS - 1);
10630 if (*rest == ' ') { /* Escape ' ' to '^_'. */
10636 else if (*rest == '"') {
10638 if (in_quote) { /* Must be closing quote. */
10651 /* Expand the command buffer if necessary. */
10652 if (clen > cmdlen) {
10653 cmd = (char *)PerlMem_realloc(cmd, clen);
10655 _ckvmssts_noperl(SS$_INSFMEM);
10656 /* Where we are may have changed, so recompute offsets */
10657 r = cmd + (r - s - soff);
10658 rest = cmd + (rest - s - soff);
10662 /* Shift the non-verb portion of the command (if any) up or
10663 * down as necessary.
10666 memmove(rest + clen - cmdlen, rest, s - soff + cmdlen - rest);
10668 /* Copy the unquoted and escaped command verb into place. */
10669 memcpy(r, resspec, cp2 - resspec);
10672 rest = r; /* Rewind for subsequent operations. */
10675 if (*rest == '.' || *rest == '/') {
10677 for (cp2 = resspec;
10678 *rest && !isspace(*rest) && cp2 - resspec < (VMS_MAXRSS - 1);
10679 rest++, cp2++) *cp2 = *rest;
10681 if (int_tovmsspec(resspec, cp, 0, NULL)) {
10684 /* When a UNIX spec with no file type is translated to VMS, */
10685 /* A trailing '.' is appended under ODS-5 rules. */
10686 /* Here we do not want that trailing "." as it prevents */
10687 /* Looking for a implied ".exe" type. */
10688 if (decc_efs_charset) {
10690 i = strlen(vmsspec);
10691 if (vmsspec[i-1] == '.') {
10692 vmsspec[i-1] = '\0';
10697 for (cp2 = vmsspec + strlen(vmsspec);
10698 *rest && cp2 - vmsspec < MAX_DCL_LINE_LENGTH;
10699 rest++, cp2++) *cp2 = *rest;
10704 /* Intuit whether verb (first word of cmd) is a DCL command:
10705 * - if first nonspace char is '@', it's a DCL indirection
10707 * - if verb contains a filespec separator, it's not a DCL command
10708 * - if it doesn't, caller tells us whether to default to a DCL
10709 * command, or to a local image unless told it's DCL (by leading '$')
10713 if (suggest_quote) *suggest_quote = 1;
10715 char *filespec = strpbrk(s,":<[.;");
10716 rest = wordbreak = strpbrk(s," \"\t/");
10717 if (!wordbreak) wordbreak = s + strlen(s);
10718 if (*s == '$') check_img = 0;
10719 if (filespec && (filespec < wordbreak)) isdcl = 0;
10720 else isdcl = !check_img;
10725 imgdsc.dsc$a_pointer = s;
10726 imgdsc.dsc$w_length = wordbreak - s;
10727 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
10729 _ckvmssts_noperl(lib$find_file_end(&cxt));
10730 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
10731 if (!(retsts & 1) && *s == '$') {
10732 _ckvmssts_noperl(lib$find_file_end(&cxt));
10733 imgdsc.dsc$a_pointer++; imgdsc.dsc$w_length--;
10734 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
10736 _ckvmssts_noperl(lib$find_file_end(&cxt));
10737 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
10741 _ckvmssts_noperl(lib$find_file_end(&cxt));
10746 while (*s && !isspace(*s)) s++;
10749 /* check that it's really not DCL with no file extension */
10750 fp = fopen(resspec,"r","ctx=bin","ctx=rec","shr=get");
10752 char b[256] = {0,0,0,0};
10753 read(fileno(fp), b, 256);
10754 isdcl = isprint(b[0]) && isprint(b[1]) && isprint(b[2]) && isprint(b[3]);
10758 /* Check for script */
10760 if ((b[0] == '#') && (b[1] == '!'))
10762 #ifdef ALTERNATE_SHEBANG
10764 shebang_len = strlen(ALTERNATE_SHEBANG);
10765 if (strncmp(b, ALTERNATE_SHEBANG, shebang_len) == 0) {
10767 perlstr = strstr("perl",b);
10768 if (perlstr == NULL)
10776 if (shebang_len > 0) {
10779 char tmpspec[NAM$C_MAXRSS + 1];
10782 /* Image is following after white space */
10783 /*--------------------------------------*/
10784 while (isprint(b[i]) && isspace(b[i]))
10788 while (isprint(b[i]) && !isspace(b[i])) {
10789 tmpspec[j++] = b[i++];
10790 if (j >= NAM$C_MAXRSS)
10795 /* There may be some default parameters to the image */
10796 /*---------------------------------------------------*/
10798 while (isprint(b[i])) {
10799 image_argv[j++] = b[i++];
10800 if (j >= NAM$C_MAXRSS)
10803 while ((j > 0) && !isprint(image_argv[j-1]))
10807 /* It will need to be converted to VMS format and validated */
10808 if (tmpspec[0] != '\0') {
10811 /* Try to find the exact program requested to be run */
10812 /*---------------------------------------------------*/
10813 iname = int_rmsexpand
10814 (tmpspec, image_name, ".exe",
10815 PERL_RMSEXPAND_M_VMS, NULL, NULL);
10816 if (iname != NULL) {
10817 if (cando_by_name_int
10818 (S_IXUSR,0,image_name,PERL_RMSEXPAND_M_VMS_IN)) {
10819 /* MCR prefix needed */
10823 /* Try again with a null type */
10824 /*----------------------------*/
10825 iname = int_rmsexpand
10826 (tmpspec, image_name, ".",
10827 PERL_RMSEXPAND_M_VMS, NULL, NULL);
10828 if (iname != NULL) {
10829 if (cando_by_name_int
10830 (S_IXUSR,0,image_name, PERL_RMSEXPAND_M_VMS_IN)) {
10831 /* MCR prefix needed */
10837 /* Did we find the image to run the script? */
10838 /*------------------------------------------*/
10842 /* Assume DCL or foreign command exists */
10843 /*--------------------------------------*/
10844 tchr = strrchr(tmpspec, '/');
10845 if (tchr != NULL) {
10851 my_strlcpy(image_name, tchr, sizeof(image_name));
10859 if (check_img && isdcl) {
10861 PerlMem_free(resspec);
10862 PerlMem_free(vmsspec);
10866 if (cando_by_name(S_IXUSR,0,resspec)) {
10867 vmscmd->dsc$a_pointer = (char *)PerlMem_malloc(MAX_DCL_LINE_LENGTH);
10868 if (vmscmd->dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10870 my_strlcpy(vmscmd->dsc$a_pointer,"$ MCR ", MAX_DCL_LINE_LENGTH);
10871 if (image_name[0] != 0) {
10872 my_strlcat(vmscmd->dsc$a_pointer, image_name, MAX_DCL_LINE_LENGTH);
10873 my_strlcat(vmscmd->dsc$a_pointer, " ", MAX_DCL_LINE_LENGTH);
10875 } else if (image_name[0] != 0) {
10876 my_strlcpy(vmscmd->dsc$a_pointer, image_name, MAX_DCL_LINE_LENGTH);
10877 my_strlcat(vmscmd->dsc$a_pointer, " ", MAX_DCL_LINE_LENGTH);
10879 my_strlcpy(vmscmd->dsc$a_pointer, "@", MAX_DCL_LINE_LENGTH);
10881 if (suggest_quote) *suggest_quote = 1;
10883 /* If there is an image name, use original command */
10884 if (image_name[0] == 0)
10885 my_strlcat(vmscmd->dsc$a_pointer, resspec, MAX_DCL_LINE_LENGTH);
10888 while (*rest && isspace(*rest)) rest++;
10891 if (image_argv[0] != 0) {
10892 my_strlcat(vmscmd->dsc$a_pointer, image_argv, MAX_DCL_LINE_LENGTH);
10893 my_strlcat(vmscmd->dsc$a_pointer, " ", MAX_DCL_LINE_LENGTH);
10899 rest_len = strlen(rest);
10900 vmscmd_len = strlen(vmscmd->dsc$a_pointer);
10901 if ((rest_len + vmscmd_len) < MAX_DCL_LINE_LENGTH)
10902 my_strlcat(vmscmd->dsc$a_pointer, rest, MAX_DCL_LINE_LENGTH);
10904 retsts = CLI$_BUFOVF;
10906 vmscmd->dsc$w_length = strlen(vmscmd->dsc$a_pointer);
10908 PerlMem_free(vmsspec);
10909 PerlMem_free(resspec);
10910 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
10916 /* It's either a DCL command or we couldn't find a suitable image */
10917 vmscmd->dsc$w_length = strlen(cmd);
10919 vmscmd->dsc$a_pointer = (char *)PerlMem_malloc(vmscmd->dsc$w_length + 1);
10920 my_strlcpy(vmscmd->dsc$a_pointer, cmd, vmscmd->dsc$w_length + 1);
10923 PerlMem_free(resspec);
10924 PerlMem_free(vmsspec);
10926 /* check if it's a symbol (for quoting purposes) */
10927 if (suggest_quote && !*suggest_quote) {
10929 char equiv[LNM$C_NAMLENGTH];
10930 struct dsc$descriptor_s eqvdsc = {sizeof(equiv), DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10931 eqvdsc.dsc$a_pointer = equiv;
10933 iss = lib$get_symbol(vmscmd,&eqvdsc);
10934 if (iss&1 && (*equiv == '$' || *equiv == '@')) *suggest_quote = 1;
10936 if (!(retsts & 1)) {
10937 /* just hand off status values likely to be due to user error */
10938 if (retsts == RMS$_FNF || retsts == RMS$_DNF || retsts == RMS$_PRV ||
10939 retsts == RMS$_DEV || retsts == RMS$_DIR || retsts == RMS$_SYN ||
10940 (retsts & STS$M_CODE) == (SHR$_NOWILD & STS$M_CODE)) return retsts;
10941 else { _ckvmssts_noperl(retsts); }
10944 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
10946 } /* end of setup_cmddsc() */
10949 /* {{{ bool vms_do_aexec(SV *really,SV **mark,SV **sp) */
10951 Perl_vms_do_aexec(pTHX_ SV *really,SV **mark,SV **sp)
10957 if (vfork_called) { /* this follows a vfork - act Unixish */
10959 if (vfork_called < 0) {
10960 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
10963 else return do_aexec(really,mark,sp);
10965 /* no vfork - act VMSish */
10966 cmd = setup_argstr(aTHX_ really,mark,sp);
10967 exec_sts = vms_do_exec(cmd);
10968 Safefree(cmd); /* Clean up from setup_argstr() */
10973 } /* end of vms_do_aexec() */
10976 /* {{{bool vms_do_exec(char *cmd) */
10978 Perl_vms_do_exec(pTHX_ const char *cmd)
10980 struct dsc$descriptor_s *vmscmd;
10982 if (vfork_called) { /* this follows a vfork - act Unixish */
10984 if (vfork_called < 0) {
10985 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
10988 else return do_exec(cmd);
10991 { /* no vfork - act VMSish */
10992 unsigned long int retsts;
10995 TAINT_PROPER("exec");
10996 if ((retsts = setup_cmddsc(aTHX_ cmd,1,0,&vmscmd)) & 1)
10997 retsts = lib$do_command(vmscmd);
11000 case RMS$_FNF: case RMS$_DNF:
11001 set_errno(ENOENT); break;
11003 set_errno(ENOTDIR); break;
11005 set_errno(ENODEV); break;
11007 set_errno(EACCES); break;
11009 set_errno(EINVAL); break;
11010 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
11011 set_errno(E2BIG); break;
11012 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
11013 _ckvmssts_noperl(retsts); /* fall through */
11014 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
11015 set_errno(EVMSERR);
11017 set_vaxc_errno(retsts);
11018 if (ckWARN(WARN_EXEC)) {
11019 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't exec \"%*s\": %s",
11020 vmscmd->dsc$w_length, vmscmd->dsc$a_pointer, Strerror(errno));
11022 vms_execfree(vmscmd);
11027 } /* end of vms_do_exec() */
11030 int do_spawn2(pTHX_ const char *, int);
11033 Perl_do_aspawn(pTHX_ SV* really, SV** mark, SV** sp)
11035 unsigned long int sts;
11041 /* We'll copy the (undocumented?) Win32 behavior and allow a
11042 * numeric first argument. But the only value we'll support
11043 * through do_aspawn is a value of 1, which means spawn without
11044 * waiting for completion -- other values are ignored.
11046 if (SvNIOKp(*(mark+1)) && !SvPOKp(*(mark+1))) {
11048 flags = SvIVx(*mark);
11051 if (flags && flags == 1) /* the Win32 P_NOWAIT value */
11052 flags = CLI$M_NOWAIT;
11056 cmd = setup_argstr(aTHX_ really, mark, sp);
11057 sts = do_spawn2(aTHX_ cmd, flags);
11058 /* pp_sys will clean up cmd */
11062 } /* end of do_aspawn() */
11066 /* {{{int do_spawn(char* cmd) */
11068 Perl_do_spawn(pTHX_ char* cmd)
11070 PERL_ARGS_ASSERT_DO_SPAWN;
11072 return do_spawn2(aTHX_ cmd, 0);
11076 /* {{{int do_spawn_nowait(char* cmd) */
11078 Perl_do_spawn_nowait(pTHX_ char* cmd)
11080 PERL_ARGS_ASSERT_DO_SPAWN_NOWAIT;
11082 return do_spawn2(aTHX_ cmd, CLI$M_NOWAIT);
11086 /* {{{int do_spawn2(char *cmd) */
11088 do_spawn2(pTHX_ const char *cmd, int flags)
11090 unsigned long int sts, substs;
11092 /* The caller of this routine expects to Safefree(PL_Cmd) */
11093 Newx(PL_Cmd,10,char);
11096 TAINT_PROPER("spawn");
11097 if (!cmd || !*cmd) {
11098 sts = lib$spawn(0,0,0,&flags,0,0,&substs,0,0,0,0,0,0);
11101 case RMS$_FNF: case RMS$_DNF:
11102 set_errno(ENOENT); break;
11104 set_errno(ENOTDIR); break;
11106 set_errno(ENODEV); break;
11108 set_errno(EACCES); break;
11110 set_errno(EINVAL); break;
11111 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
11112 set_errno(E2BIG); break;
11113 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
11114 _ckvmssts_noperl(sts); /* fall through */
11115 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
11116 set_errno(EVMSERR);
11118 set_vaxc_errno(sts);
11119 if (ckWARN(WARN_EXEC)) {
11120 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't spawn: %s",
11129 if (flags & CLI$M_NOWAIT)
11132 strcpy(mode, "nW");
11134 fp = safe_popen(aTHX_ cmd, mode, (int *)&sts);
11137 /* sts will be the pid in the nowait case, so leave a
11138 * hint saying not to do any bit shifting to it.
11140 if (flags & CLI$M_NOWAIT)
11141 PL_statusvalue = -1;
11144 } /* end of do_spawn2() */
11148 static unsigned int *sockflags, sockflagsize;
11151 * Shim fdopen to identify sockets for my_fwrite later, since the stdio
11152 * routines found in some versions of the CRTL can't deal with sockets.
11153 * We don't shim the other file open routines since a socket isn't
11154 * likely to be opened by a name.
11156 /*{{{ FILE *my_fdopen(int fd, const char *mode)*/
11158 my_fdopen(int fd, const char *mode)
11160 FILE *fp = fdopen(fd, mode);
11163 unsigned int fdoff = fd / sizeof(unsigned int);
11164 Stat_t sbuf; /* native stat; we don't need flex_stat */
11165 if (!sockflagsize || fdoff > sockflagsize) {
11166 if (sockflags) Renew( sockflags,fdoff+2,unsigned int);
11167 else Newx (sockflags,fdoff+2,unsigned int);
11168 memset(sockflags+sockflagsize,0,fdoff + 2 - sockflagsize);
11169 sockflagsize = fdoff + 2;
11171 if (fstat(fd, &sbuf.crtl_stat) == 0 && S_ISSOCK(sbuf.st_mode))
11172 sockflags[fdoff] |= 1 << (fd % sizeof(unsigned int));
11181 * Clear the corresponding bit when the (possibly) socket stream is closed.
11182 * There still a small hole: we miss an implicit close which might occur
11183 * via freopen(). >> Todo
11185 /*{{{ int my_fclose(FILE *fp)*/
11187 my_fclose(FILE *fp) {
11189 unsigned int fd = fileno(fp);
11190 unsigned int fdoff = fd / sizeof(unsigned int);
11192 if (sockflagsize && fdoff < sockflagsize)
11193 sockflags[fdoff] &= ~(1 << fd % sizeof(unsigned int));
11201 * A simple fwrite replacement which outputs itmsz*nitm chars without
11202 * introducing record boundaries every itmsz chars.
11203 * We are using fputs, which depends on a terminating null. We may
11204 * well be writing binary data, so we need to accommodate not only
11205 * data with nulls sprinkled in the middle but also data with no null
11208 /*{{{ int my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)*/
11210 my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)
11212 char *cp, *end, *cpd;
11214 unsigned int fd = fileno(dest);
11215 unsigned int fdoff = fd / sizeof(unsigned int);
11217 int bufsize = itmsz * nitm + 1;
11219 if (fdoff < sockflagsize &&
11220 (sockflags[fdoff] | 1 << (fd % sizeof(unsigned int)))) {
11221 if (write(fd, src, itmsz * nitm) == EOF) return EOF;
11225 _ckvmssts_noperl(lib$get_vm(&bufsize, &data));
11226 memcpy( data, src, itmsz*nitm );
11227 data[itmsz*nitm] = '\0';
11229 end = data + itmsz * nitm;
11230 retval = (int) nitm; /* on success return # items written */
11233 while (cpd <= end) {
11234 for (cp = cpd; cp <= end; cp++) if (!*cp) break;
11235 if (fputs(cpd,dest) == EOF) { retval = EOF; break; }
11237 if (fputc('\0',dest) == EOF) { retval = EOF; break; }
11241 if (data) _ckvmssts_noperl(lib$free_vm(&bufsize, &data));
11244 } /* end of my_fwrite() */
11247 /*{{{ int my_flush(FILE *fp)*/
11249 Perl_my_flush(pTHX_ FILE *fp)
11252 if ((res = fflush(fp)) == 0 && fp) {
11253 #ifdef VMS_DO_SOCKETS
11255 if (fstat(fileno(fp), &s.crtl_stat) == 0 && !S_ISSOCK(s.st_mode))
11257 res = fsync(fileno(fp));
11260 * If the flush succeeded but set end-of-file, we need to clear
11261 * the error because our caller may check ferror(). BTW, this
11262 * probably means we just flushed an empty file.
11264 if (res == 0 && vaxc$errno == RMS$_EOF) clearerr(fp);
11270 /* fgetname() is not returning the correct file specifications when
11271 * decc_filename_unix_report mode is active. So we have to have it
11272 * aways return filenames in VMS mode and convert it ourselves.
11275 /*{{{ char * my_fgetname(FILE *fp, buf)*/
11277 Perl_my_fgetname(FILE *fp, char * buf) {
11281 retname = fgetname(fp, buf, 1);
11283 /* If we are in VMS mode, then we are done */
11284 if (!decc_filename_unix_report || (retname == NULL)) {
11288 /* Convert this to Unix format */
11289 vms_name = (char *)PerlMem_malloc(VMS_MAXRSS);
11290 my_strlcpy(vms_name, retname, VMS_MAXRSS);
11291 retname = int_tounixspec(vms_name, buf, NULL);
11292 PerlMem_free(vms_name);
11299 * Here are replacements for the following Unix routines in the VMS environment:
11300 * getpwuid Get information for a particular UIC or UID
11301 * getpwnam Get information for a named user
11302 * getpwent Get information for each user in the rights database
11303 * setpwent Reset search to the start of the rights database
11304 * endpwent Finish searching for users in the rights database
11306 * getpwuid, getpwnam, and getpwent return a pointer to the passwd structure
11307 * (defined in pwd.h), which contains the following fields:-
11309 * char *pw_name; Username (in lower case)
11310 * char *pw_passwd; Hashed password
11311 * unsigned int pw_uid; UIC
11312 * unsigned int pw_gid; UIC group number
11313 * char *pw_unixdir; Default device/directory (VMS-style)
11314 * char *pw_gecos; Owner name
11315 * char *pw_dir; Default device/directory (Unix-style)
11316 * char *pw_shell; Default CLI name (eg. DCL)
11318 * If the specified user does not exist, getpwuid and getpwnam return NULL.
11320 * pw_uid is the full UIC (eg. what's returned by stat() in st_uid).
11321 * not the UIC member number (eg. what's returned by getuid()),
11322 * getpwuid() can accept either as input (if uid is specified, the caller's
11323 * UIC group is used), though it won't recognise gid=0.
11325 * Note that in VMS it is necessary to have GRPPRV or SYSPRV to return
11326 * information about other users in your group or in other groups, respectively.
11327 * If the required privilege is not available, then these routines fill only
11328 * the pw_name, pw_uid, and pw_gid fields (the others point to an empty
11331 * By Tim Adye (T.J.Adye@rl.ac.uk), 10th February 1995.
11334 /* sizes of various UAF record fields */
11335 #define UAI$S_USERNAME 12
11336 #define UAI$S_IDENT 31
11337 #define UAI$S_OWNER 31
11338 #define UAI$S_DEFDEV 31
11339 #define UAI$S_DEFDIR 63
11340 #define UAI$S_DEFCLI 31
11341 #define UAI$S_PWD 8
11343 #define valid_uic(uic) ((uic).uic$v_format == UIC$K_UIC_FORMAT && \
11344 (uic).uic$v_member != UIC$K_WILD_MEMBER && \
11345 (uic).uic$v_group != UIC$K_WILD_GROUP)
11347 static char __empty[]= "";
11348 static struct passwd __passwd_empty=
11349 {(char *) __empty, (char *) __empty, 0, 0,
11350 (char *) __empty, (char *) __empty, (char *) __empty, (char *) __empty};
11351 static int contxt= 0;
11352 static struct passwd __pwdcache;
11353 static char __pw_namecache[UAI$S_IDENT+1];
11356 * This routine does most of the work extracting the user information.
11359 fillpasswd (pTHX_ const char *name, struct passwd *pwd)
11362 unsigned char length;
11363 char pw_gecos[UAI$S_OWNER+1];
11365 static union uicdef uic;
11367 unsigned char length;
11368 char pw_dir[UAI$S_DEFDEV+UAI$S_DEFDIR+1];
11371 unsigned char length;
11372 char unixdir[UAI$_DEFDEV+UAI$S_DEFDIR+1];
11375 unsigned char length;
11376 char pw_shell[UAI$S_DEFCLI+1];
11378 static char pw_passwd[UAI$S_PWD+1];
11380 static unsigned short lowner, luic, ldefdev, ldefdir, ldefcli, lpwd;
11381 struct dsc$descriptor_s name_desc;
11382 unsigned long int sts;
11384 static struct itmlst_3 itmlst[]= {
11385 {UAI$S_OWNER+1, UAI$_OWNER, &owner, &lowner},
11386 {sizeof(uic), UAI$_UIC, &uic, &luic},
11387 {UAI$S_DEFDEV+1, UAI$_DEFDEV, &defdev, &ldefdev},
11388 {UAI$S_DEFDIR+1, UAI$_DEFDIR, &defdir, &ldefdir},
11389 {UAI$S_DEFCLI+1, UAI$_DEFCLI, &defcli, &ldefcli},
11390 {UAI$S_PWD, UAI$_PWD, pw_passwd, &lpwd},
11391 {0, 0, NULL, NULL}};
11393 name_desc.dsc$w_length= strlen(name);
11394 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
11395 name_desc.dsc$b_class= DSC$K_CLASS_S;
11396 name_desc.dsc$a_pointer= (char *) name; /* read only pointer */
11398 /* Note that sys$getuai returns many fields as counted strings. */
11399 sts= sys$getuai(0, 0, &name_desc, &itmlst, 0, 0, 0);
11400 if (sts == SS$_NOSYSPRV || sts == SS$_NOGRPPRV || sts == RMS$_RNF) {
11401 set_vaxc_errno(sts); set_errno(sts == RMS$_RNF ? EINVAL : EACCES);
11403 else { _ckvmssts(sts); }
11404 if (!(sts & 1)) return 0; /* out here in case _ckvmssts() doesn't abort */
11406 if ((int) owner.length < lowner) lowner= (int) owner.length;
11407 if ((int) defdev.length < ldefdev) ldefdev= (int) defdev.length;
11408 if ((int) defdir.length < ldefdir) ldefdir= (int) defdir.length;
11409 if ((int) defcli.length < ldefcli) ldefcli= (int) defcli.length;
11410 memcpy(&defdev.pw_dir[ldefdev], &defdir.unixdir[0], ldefdir);
11411 owner.pw_gecos[lowner]= '\0';
11412 defdev.pw_dir[ldefdev+ldefdir]= '\0';
11413 defcli.pw_shell[ldefcli]= '\0';
11414 if (valid_uic(uic)) {
11415 pwd->pw_uid= uic.uic$l_uic;
11416 pwd->pw_gid= uic.uic$v_group;
11419 Perl_warn(aTHX_ "getpwnam returned invalid UIC %#o for user \"%s\"");
11420 pwd->pw_passwd= pw_passwd;
11421 pwd->pw_gecos= owner.pw_gecos;
11422 pwd->pw_dir= defdev.pw_dir;
11423 pwd->pw_unixdir= do_tounixpath(defdev.pw_dir, defdir.unixdir,1,NULL);
11424 pwd->pw_shell= defcli.pw_shell;
11425 if (pwd->pw_unixdir && pwd->pw_unixdir[0]) {
11427 ldir= strlen(pwd->pw_unixdir) - 1;
11428 if (pwd->pw_unixdir[ldir]=='/') pwd->pw_unixdir[ldir]= '\0';
11431 my_strlcpy(pwd->pw_unixdir, pwd->pw_dir, sizeof(pwd->pw_unixdir));
11432 if (!decc_efs_case_preserve)
11433 __mystrtolower(pwd->pw_unixdir);
11438 * Get information for a named user.
11440 /*{{{struct passwd *getpwnam(char *name)*/
11442 Perl_my_getpwnam(pTHX_ const char *name)
11444 struct dsc$descriptor_s name_desc;
11446 unsigned long int sts;
11448 __pwdcache = __passwd_empty;
11449 if (!fillpasswd(aTHX_ name, &__pwdcache)) {
11450 /* We still may be able to determine pw_uid and pw_gid */
11451 name_desc.dsc$w_length= strlen(name);
11452 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
11453 name_desc.dsc$b_class= DSC$K_CLASS_S;
11454 name_desc.dsc$a_pointer= (char *) name;
11455 if ((sts = sys$asctoid(&name_desc, &uic, 0)) == SS$_NORMAL) {
11456 __pwdcache.pw_uid= uic.uic$l_uic;
11457 __pwdcache.pw_gid= uic.uic$v_group;
11460 if (sts == SS$_NOSUCHID || sts == SS$_IVIDENT || sts == RMS$_PRV) {
11461 set_vaxc_errno(sts);
11462 set_errno(sts == RMS$_PRV ? EACCES : EINVAL);
11465 else { _ckvmssts(sts); }
11468 my_strlcpy(__pw_namecache, name, sizeof(__pw_namecache));
11469 __pwdcache.pw_name= __pw_namecache;
11470 return &__pwdcache;
11471 } /* end of my_getpwnam() */
11475 * Get information for a particular UIC or UID.
11476 * Called by my_getpwent with uid=-1 to list all users.
11478 /*{{{struct passwd *my_getpwuid(Uid_t uid)*/
11480 Perl_my_getpwuid(pTHX_ Uid_t uid)
11482 const $DESCRIPTOR(name_desc,__pw_namecache);
11483 unsigned short lname;
11485 unsigned long int status;
11487 if (uid == (unsigned int) -1) {
11489 status = sys$idtoasc(-1, &lname, &name_desc, &uic, 0, &contxt);
11490 if (status == SS$_NOSUCHID || status == RMS$_PRV) {
11491 set_vaxc_errno(status);
11492 set_errno(status == RMS$_PRV ? EACCES : EINVAL);
11496 else { _ckvmssts(status); }
11497 } while (!valid_uic (uic));
11500 uic.uic$l_uic= uid;
11501 if (!uic.uic$v_group)
11502 uic.uic$v_group= PerlProc_getgid();
11503 if (valid_uic(uic))
11504 status = sys$idtoasc(uic.uic$l_uic, &lname, &name_desc, 0, 0, 0);
11505 else status = SS$_IVIDENT;
11506 if (status == SS$_IVIDENT || status == SS$_NOSUCHID ||
11507 status == RMS$_PRV) {
11508 set_vaxc_errno(status); set_errno(status == RMS$_PRV ? EACCES : EINVAL);
11511 else { _ckvmssts(status); }
11513 __pw_namecache[lname]= '\0';
11514 __mystrtolower(__pw_namecache);
11516 __pwdcache = __passwd_empty;
11517 __pwdcache.pw_name = __pw_namecache;
11519 /* Fill in the uid and gid in case fillpasswd can't (eg. no privilege).
11520 The identifier's value is usually the UIC, but it doesn't have to be,
11521 so if we can, we let fillpasswd update this. */
11522 __pwdcache.pw_uid = uic.uic$l_uic;
11523 __pwdcache.pw_gid = uic.uic$v_group;
11525 fillpasswd(aTHX_ __pw_namecache, &__pwdcache);
11526 return &__pwdcache;
11528 } /* end of my_getpwuid() */
11532 * Get information for next user.
11534 /*{{{struct passwd *my_getpwent()*/
11536 Perl_my_getpwent(pTHX)
11538 return (my_getpwuid((unsigned int) -1));
11543 * Finish searching rights database for users.
11545 /*{{{void my_endpwent()*/
11547 Perl_my_endpwent(pTHX)
11550 _ckvmssts(sys$finish_rdb(&contxt));
11556 /* Used for UTC calculation in my_gmtime(), my_localtime(), my_time(),
11557 * my_utime(), and flex_stat(), all of which operate on UTC unless
11558 * VMSISH_TIMES is true.
11560 /* method used to handle UTC conversions:
11561 * 1 == CRTL gmtime(); 2 == SYS$TIMEZONE_DIFFERENTIAL; 3 == no correction
11563 static int gmtime_emulation_type;
11564 /* number of secs to add to UTC POSIX-style time to get local time */
11565 static long int utc_offset_secs;
11567 /* We #defined 'gmtime', 'localtime', and 'time' as 'my_gmtime' etc.
11568 * in vmsish.h. #undef them here so we can call the CRTL routines
11576 static time_t toutc_dst(time_t loc) {
11579 if ((rsltmp = localtime(&loc)) == NULL) return -1u;
11580 loc -= utc_offset_secs;
11581 if (rsltmp->tm_isdst) loc -= 3600;
11584 #define _toutc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
11585 ((gmtime_emulation_type || my_time(NULL)), \
11586 (gmtime_emulation_type == 1 ? toutc_dst(secs) : \
11587 ((secs) - utc_offset_secs))))
11589 static time_t toloc_dst(time_t utc) {
11592 utc += utc_offset_secs;
11593 if ((rsltmp = localtime(&utc)) == NULL) return -1u;
11594 if (rsltmp->tm_isdst) utc += 3600;
11597 #define _toloc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
11598 ((gmtime_emulation_type || my_time(NULL)), \
11599 (gmtime_emulation_type == 1 ? toloc_dst(secs) : \
11600 ((secs) + utc_offset_secs))))
11602 /* my_time(), my_localtime(), my_gmtime()
11603 * By default traffic in UTC time values, using CRTL gmtime() or
11604 * SYS$TIMEZONE_DIFFERENTIAL to determine offset from local time zone.
11605 * Note: We need to use these functions even when the CRTL has working
11606 * UTC support, since they also handle C<use vmsish qw(times);>
11608 * Contributed by Chuck Lane <lane@duphy4.physics.drexel.edu>
11609 * Modified by Charles Bailey <bailey@newman.upenn.edu>
11612 /*{{{time_t my_time(time_t *timep)*/
11614 Perl_my_time(pTHX_ time_t *timep)
11619 if (gmtime_emulation_type == 0) {
11620 time_t base = 15 * 86400; /* 15jan71; to avoid month/year ends between */
11621 /* results of calls to gmtime() and localtime() */
11622 /* for same &base */
11624 gmtime_emulation_type++;
11625 if ((tm_p = gmtime(&base)) == NULL) { /* CRTL gmtime() is a fake */
11626 char off[LNM$C_NAMLENGTH+1];;
11628 gmtime_emulation_type++;
11629 if (!vmstrnenv("SYS$TIMEZONE_DIFFERENTIAL",off,0,fildev,0)) {
11630 gmtime_emulation_type++;
11631 utc_offset_secs = 0;
11632 Perl_warn(aTHX_ "no UTC offset information; assuming local time is UTC");
11634 else { utc_offset_secs = atol(off); }
11636 else { /* We've got a working gmtime() */
11637 struct tm gmt, local;
11640 tm_p = localtime(&base);
11642 utc_offset_secs = (local.tm_mday - gmt.tm_mday) * 86400;
11643 utc_offset_secs += (local.tm_hour - gmt.tm_hour) * 3600;
11644 utc_offset_secs += (local.tm_min - gmt.tm_min) * 60;
11645 utc_offset_secs += (local.tm_sec - gmt.tm_sec);
11650 # ifdef VMSISH_TIME
11651 if (VMSISH_TIME) when = _toloc(when);
11653 if (timep != NULL) *timep = when;
11656 } /* end of my_time() */
11660 /*{{{struct tm *my_gmtime(const time_t *timep)*/
11662 Perl_my_gmtime(pTHX_ const time_t *timep)
11667 if (timep == NULL) {
11668 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11671 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
11674 # ifdef VMSISH_TIME
11675 if (VMSISH_TIME) when = _toutc(when); /* Input was local time */
11677 return gmtime(&when);
11678 } /* end of my_gmtime() */
11682 /*{{{struct tm *my_localtime(const time_t *timep)*/
11684 Perl_my_localtime(pTHX_ const time_t *timep)
11688 if (timep == NULL) {
11689 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11692 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
11693 if (gmtime_emulation_type == 0) my_time(NULL); /* Init UTC */
11696 # ifdef VMSISH_TIME
11697 if (VMSISH_TIME) when = _toutc(when);
11699 /* CRTL localtime() wants UTC as input, does tz correction itself */
11700 return localtime(&when);
11701 } /* end of my_localtime() */
11704 /* Reset definitions for later calls */
11705 #define gmtime(t) my_gmtime(t)
11706 #define localtime(t) my_localtime(t)
11707 #define time(t) my_time(t)
11710 /* my_utime - update modification/access time of a file
11712 * Only the UTC translation is home-grown. The rest is handled by the
11713 * CRTL utime(), which will take into account the relevant feature
11714 * logicals and ODS-5 volume characteristics for true access times.
11718 /* Adjustment from Unix epoch (01-JAN-1970 00:00:00.00)
11719 * to VMS epoch (01-JAN-1858 00:00:00.00)
11720 * in 100 ns intervals.
11722 static const long int utime_baseadjust[2] = { 0x4beb4000, 0x7c9567 };
11724 /*{{{int my_utime(const char *path, const struct utimbuf *utimes)*/
11726 Perl_my_utime(pTHX_ const char *file, const struct utimbuf *utimes)
11728 struct utimbuf utc_utimes, *utc_utimesp;
11730 if (utimes != NULL) {
11731 utc_utimes.actime = utimes->actime;
11732 utc_utimes.modtime = utimes->modtime;
11733 # ifdef VMSISH_TIME
11734 /* If input was local; convert to UTC for sys svc */
11736 utc_utimes.actime = _toutc(utimes->actime);
11737 utc_utimes.modtime = _toutc(utimes->modtime);
11740 utc_utimesp = &utc_utimes;
11743 utc_utimesp = NULL;
11746 return utime(file, utc_utimesp);
11748 } /* end of my_utime() */
11752 * flex_stat, flex_lstat, flex_fstat
11753 * basic stat, but gets it right when asked to stat
11754 * a Unix-style path ending in a directory name (e.g. dir1/dir2/dir3)
11757 #ifndef _USE_STD_STAT
11758 /* encode_dev packs a VMS device name string into an integer to allow
11759 * simple comparisons. This can be used, for example, to check whether two
11760 * files are located on the same device, by comparing their encoded device
11761 * names. Even a string comparison would not do, because stat() reuses the
11762 * device name buffer for each call; so without encode_dev, it would be
11763 * necessary to save the buffer and use strcmp (this would mean a number of
11764 * changes to the standard Perl code, to say nothing of what a Perl script
11765 * would have to do.
11767 * The device lock id, if it exists, should be unique (unless perhaps compared
11768 * with lock ids transferred from other nodes). We have a lock id if the disk is
11769 * mounted cluster-wide, which is when we tend to get long (host-qualified)
11770 * device names. Thus we use the lock id in preference, and only if that isn't
11771 * available, do we try to pack the device name into an integer (flagged by
11772 * the sign bit (LOCKID_MASK) being set).
11774 * Note that encode_dev cannot guarantee an 1-to-1 correspondence twixt device
11775 * name and its encoded form, but it seems very unlikely that we will find
11776 * two files on different disks that share the same encoded device names,
11777 * and even more remote that they will share the same file id (if the test
11778 * is to check for the same file).
11780 * A better method might be to use sys$device_scan on the first call, and to
11781 * search for the device, returning an index into the cached array.
11782 * The number returned would be more intelligible.
11783 * This is probably not worth it, and anyway would take quite a bit longer
11784 * on the first call.
11786 #define LOCKID_MASK 0x80000000 /* Use 0 to force device name use only */
11788 encode_dev (pTHX_ const char *dev)
11791 unsigned long int f;
11796 if (!dev || !dev[0]) return 0;
11800 struct dsc$descriptor_s dev_desc;
11801 unsigned long int status, lockid = 0, item = DVI$_LOCKID;
11803 /* For cluster-mounted disks, the disk lock identifier is unique, so we
11804 can try that first. */
11805 dev_desc.dsc$w_length = strlen (dev);
11806 dev_desc.dsc$b_dtype = DSC$K_DTYPE_T;
11807 dev_desc.dsc$b_class = DSC$K_CLASS_S;
11808 dev_desc.dsc$a_pointer = (char *) dev; /* Read only parameter */
11809 status = lib$getdvi(&item, 0, &dev_desc, &lockid, 0, 0);
11810 if (!$VMS_STATUS_SUCCESS(status)) {
11812 case SS$_NOSUCHDEV:
11813 SETERRNO(ENODEV, status);
11819 if (lockid) return (lockid & ~LOCKID_MASK);
11823 /* Otherwise we try to encode the device name */
11827 for (q = dev + strlen(dev); q--; q >= dev) {
11832 else if (isalpha (toupper (*q)))
11833 c= toupper (*q) - 'A' + (char)10;
11835 continue; /* Skip '$'s */
11837 if (i>6) break; /* 36^7 is too large to fit in an unsigned long int */
11839 enc += f * (unsigned long int) c;
11841 return (enc | LOCKID_MASK); /* May have already overflowed into bit 31 */
11843 } /* end of encode_dev() */
11844 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
11845 device_no = encode_dev(aTHX_ devname)
11847 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
11848 device_no = new_dev_no
11852 is_null_device(const char *name)
11854 if (decc_bug_devnull != 0) {
11855 if (strncmp("/dev/null", name, 9) == 0)
11858 /* The VMS null device is named "_NLA0:", usually abbreviated as "NL:".
11859 The underscore prefix, controller letter, and unit number are
11860 independently optional; for our purposes, the colon punctuation
11861 is not. The colon can be trailed by optional directory and/or
11862 filename, but two consecutive colons indicates a nodename rather
11863 than a device. [pr] */
11864 if (*name == '_') ++name;
11865 if (tolower(*name++) != 'n') return 0;
11866 if (tolower(*name++) != 'l') return 0;
11867 if (tolower(*name) == 'a') ++name;
11868 if (*name == '0') ++name;
11869 return (*name++ == ':') && (*name != ':');
11873 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag);
11875 #define flex_stat_int(a,b,c) Perl_flex_stat_int(aTHX_ a,b,c)
11878 Perl_cando_by_name_int(pTHX_ I32 bit, bool effective, const char *fname, int opts)
11880 char usrname[L_cuserid];
11881 struct dsc$descriptor_s usrdsc =
11882 {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, usrname};
11883 char *vmsname = NULL, *fileified = NULL;
11884 unsigned long int objtyp = ACL$C_FILE, access, retsts, privused, iosb[2], flags;
11885 unsigned short int retlen, trnlnm_iter_count;
11886 struct dsc$descriptor_s namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
11887 union prvdef curprv;
11888 struct itmlst_3 armlst[4] = {{sizeof access, CHP$_ACCESS, &access, &retlen},
11889 {sizeof privused, CHP$_PRIVUSED, &privused, &retlen},
11890 {sizeof flags, CHP$_FLAGS, &flags, &retlen},{0,0,0,0}};
11891 struct itmlst_3 jpilst[3] = {{sizeof curprv, JPI$_CURPRIV, &curprv, &retlen},
11892 {sizeof usrname, JPI$_USERNAME, &usrname, &usrdsc.dsc$w_length},
11894 struct itmlst_3 usrprolst[2] = {{sizeof curprv, CHP$_PRIV, &curprv, &retlen},
11896 struct dsc$descriptor_s usrprodsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
11898 static int profile_context = -1;
11900 if (!fname || !*fname) return FALSE;
11902 /* Make sure we expand logical names, since sys$check_access doesn't */
11903 fileified = (char *)PerlMem_malloc(VMS_MAXRSS);
11904 if (fileified == NULL) _ckvmssts_noperl(SS$_INSFMEM);
11905 if (!strpbrk(fname,"/]>:")) {
11906 my_strlcpy(fileified, fname, VMS_MAXRSS);
11907 trnlnm_iter_count = 0;
11908 while (!strpbrk(fileified,"/]>:") && my_trnlnm(fileified,fileified,0)) {
11909 trnlnm_iter_count++;
11910 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
11915 vmsname = (char *)PerlMem_malloc(VMS_MAXRSS);
11916 if (vmsname == NULL) _ckvmssts_noperl(SS$_INSFMEM);
11917 if ( !(opts & PERL_RMSEXPAND_M_VMS_IN) ) {
11918 /* Don't know if already in VMS format, so make sure */
11919 if (!do_rmsexpand(fname, vmsname, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL)) {
11920 PerlMem_free(fileified);
11921 PerlMem_free(vmsname);
11926 my_strlcpy(vmsname, fname, VMS_MAXRSS);
11929 /* sys$check_access needs a file spec, not a directory spec.
11930 * flex_stat now will handle a null thread context during startup.
11933 retlen = namdsc.dsc$w_length = strlen(vmsname);
11934 if (vmsname[retlen-1] == ']'
11935 || vmsname[retlen-1] == '>'
11936 || vmsname[retlen-1] == ':'
11937 || (!flex_stat_int(vmsname, &st, 1) &&
11938 S_ISDIR(st.st_mode))) {
11940 if (!int_fileify_dirspec(vmsname, fileified, NULL)) {
11941 PerlMem_free(fileified);
11942 PerlMem_free(vmsname);
11951 retlen = namdsc.dsc$w_length = strlen(fname);
11952 namdsc.dsc$a_pointer = (char *)fname;
11955 case S_IXUSR: case S_IXGRP: case S_IXOTH:
11956 access = ARM$M_EXECUTE;
11957 flags = CHP$M_READ;
11959 case S_IRUSR: case S_IRGRP: case S_IROTH:
11960 access = ARM$M_READ;
11961 flags = CHP$M_READ | CHP$M_USEREADALL;
11963 case S_IWUSR: case S_IWGRP: case S_IWOTH:
11964 access = ARM$M_WRITE;
11965 flags = CHP$M_READ | CHP$M_WRITE;
11967 case S_IDUSR: case S_IDGRP: case S_IDOTH:
11968 access = ARM$M_DELETE;
11969 flags = CHP$M_READ | CHP$M_WRITE;
11972 if (fileified != NULL)
11973 PerlMem_free(fileified);
11974 if (vmsname != NULL)
11975 PerlMem_free(vmsname);
11979 /* Before we call $check_access, create a user profile with the current
11980 * process privs since otherwise it just uses the default privs from the
11981 * UAF and might give false positives or negatives. This only works on
11982 * VMS versions v6.0 and later since that's when sys$create_user_profile
11983 * became available.
11986 /* get current process privs and username */
11987 _ckvmssts_noperl(sys$getjpiw(0,0,0,jpilst,iosb,0,0));
11988 _ckvmssts_noperl(iosb[0]);
11990 /* find out the space required for the profile */
11991 _ckvmssts_noperl(sys$create_user_profile(&usrdsc,&usrprolst,0,0,
11992 &usrprodsc.dsc$w_length,&profile_context));
11994 /* allocate space for the profile and get it filled in */
11995 usrprodsc.dsc$a_pointer = (char *)PerlMem_malloc(usrprodsc.dsc$w_length);
11996 if (usrprodsc.dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
11997 _ckvmssts_noperl(sys$create_user_profile(&usrdsc,&usrprolst,0,usrprodsc.dsc$a_pointer,
11998 &usrprodsc.dsc$w_length,&profile_context));
12000 /* use the profile to check access to the file; free profile & analyze results */
12001 retsts = sys$check_access(&objtyp,&namdsc,0,armlst,&profile_context,0,0,&usrprodsc);
12002 PerlMem_free(usrprodsc.dsc$a_pointer);
12003 if (retsts == SS$_NOCALLPRIV) retsts = SS$_NOPRIV; /* not really 3rd party */
12005 if (retsts == SS$_NOPRIV || retsts == SS$_NOSUCHOBJECT ||
12006 retsts == SS$_INVFILFOROP || retsts == RMS$_FNF || retsts == RMS$_SYN ||
12007 retsts == RMS$_DIR || retsts == RMS$_DEV || retsts == RMS$_DNF) {
12008 set_vaxc_errno(retsts);
12009 if (retsts == SS$_NOPRIV) set_errno(EACCES);
12010 else if (retsts == SS$_INVFILFOROP) set_errno(EINVAL);
12011 else set_errno(ENOENT);
12012 if (fileified != NULL)
12013 PerlMem_free(fileified);
12014 if (vmsname != NULL)
12015 PerlMem_free(vmsname);
12018 if (retsts == SS$_NORMAL || retsts == SS$_ACCONFLICT) {
12019 if (fileified != NULL)
12020 PerlMem_free(fileified);
12021 if (vmsname != NULL)
12022 PerlMem_free(vmsname);
12025 _ckvmssts_noperl(retsts);
12027 if (fileified != NULL)
12028 PerlMem_free(fileified);
12029 if (vmsname != NULL)
12030 PerlMem_free(vmsname);
12031 return FALSE; /* Should never get here */
12035 /* Do the permissions allow some operation? Assumes PL_statcache already set. */
12036 /* Do this via $Check_Access on VMS, since the CRTL stat() returns only a
12037 * subset of the applicable information.
12040 Perl_cando(pTHX_ Mode_t bit, bool effective, const Stat_t *statbufp)
12042 return cando_by_name_int
12043 (bit, effective, statbufp->st_devnam, PERL_RMSEXPAND_M_VMS_IN);
12044 } /* end of cando() */
12048 /*{{{I32 cando_by_name(I32 bit, bool effective, char *fname)*/
12050 Perl_cando_by_name(pTHX_ I32 bit, bool effective, const char *fname)
12052 return cando_by_name_int(bit, effective, fname, 0);
12054 } /* end of cando_by_name() */
12058 /*{{{ int flex_fstat(int fd, Stat_t *statbuf)*/
12060 Perl_flex_fstat(pTHX_ int fd, Stat_t *statbufp)
12062 dSAVE_ERRNO; /* fstat may set this even on success */
12063 if (!fstat(fd, &statbufp->crtl_stat)) {
12065 char *vms_filename;
12066 vms_filename = (char *)PerlMem_malloc(VMS_MAXRSS);
12067 if (vms_filename == NULL) _ckvmssts(SS$_INSFMEM);
12069 /* Save name for cando by name in VMS format */
12070 cptr = getname(fd, vms_filename, 1);
12072 /* This should not happen, but just in case */
12073 if (cptr == NULL) {
12074 statbufp->st_devnam[0] = 0;
12077 /* Make sure that the saved name fits in 255 characters */
12078 cptr = int_rmsexpand_vms
12080 statbufp->st_devnam,
12083 statbufp->st_devnam[0] = 0;
12085 PerlMem_free(vms_filename);
12087 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
12089 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
12091 # ifdef VMSISH_TIME
12093 statbufp->st_mtime = _toloc(statbufp->st_mtime);
12094 statbufp->st_atime = _toloc(statbufp->st_atime);
12095 statbufp->st_ctime = _toloc(statbufp->st_ctime);
12103 } /* end of flex_fstat() */
12107 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag)
12109 char *temp_fspec = NULL;
12110 char *fileified = NULL;
12111 const char *save_spec;
12115 char already_fileified = 0;
12123 if (decc_bug_devnull != 0) {
12124 if (is_null_device(fspec)) { /* Fake a stat() for the null device */
12125 memset(statbufp,0,sizeof *statbufp);
12126 VMS_DEVICE_ENCODE(statbufp->st_dev, "_NLA0:", 0);
12127 statbufp->st_mode = S_IFBLK | S_IREAD | S_IWRITE | S_IEXEC;
12128 statbufp->st_uid = 0x00010001;
12129 statbufp->st_gid = 0x0001;
12130 time((time_t *)&statbufp->st_mtime);
12131 statbufp->st_atime = statbufp->st_ctime = statbufp->st_mtime;
12138 #if __CRTL_VER >= 80200000
12140 * If we are in POSIX filespec mode, accept the filename as is.
12142 if (decc_posix_compliant_pathnames == 0) {
12145 /* Try for a simple stat first. If fspec contains a filename without
12146 * a type (e.g. sea:[wine.dark]water), and both sea:[wine.dark]water.dir
12147 * and sea:[wine.dark]water. exist, the CRTL prefers the directory here.
12148 * Similarly, sea:[wine.dark] returns the result for sea:[wine]dark.dir,
12149 * not sea:[wine.dark]., if the latter exists. If the intended target is
12150 * the file with null type, specify this by calling flex_stat() with
12151 * a '.' at the end of fspec.
12154 if (lstat_flag == 0)
12155 retval = stat(fspec, &statbufp->crtl_stat);
12157 retval = lstat(fspec, &statbufp->crtl_stat);
12163 /* In the odd case where we have write but not read access
12164 * to a directory, stat('foo.DIR') works but stat('foo') doesn't.
12166 fileified = (char *)PerlMem_malloc(VMS_MAXRSS);
12167 if (fileified == NULL)
12168 _ckvmssts_noperl(SS$_INSFMEM);
12170 ret_spec = int_fileify_dirspec(fspec, fileified, NULL);
12171 if (ret_spec != NULL) {
12172 if (lstat_flag == 0)
12173 retval = stat(fileified, &statbufp->crtl_stat);
12175 retval = lstat(fileified, &statbufp->crtl_stat);
12176 save_spec = fileified;
12177 already_fileified = 1;
12181 if (retval && vms_bug_stat_filename) {
12183 temp_fspec = (char *)PerlMem_malloc(VMS_MAXRSS);
12184 if (temp_fspec == NULL)
12185 _ckvmssts_noperl(SS$_INSFMEM);
12187 /* We should try again as a vmsified file specification. */
12189 ret_spec = int_tovmsspec(fspec, temp_fspec, 0, NULL);
12190 if (ret_spec != NULL) {
12191 if (lstat_flag == 0)
12192 retval = stat(temp_fspec, &statbufp->crtl_stat);
12194 retval = lstat(temp_fspec, &statbufp->crtl_stat);
12195 save_spec = temp_fspec;
12200 /* Last chance - allow multiple dots without EFS CHARSET */
12201 /* The CRTL stat() falls down hard on multi-dot filenames in unix
12202 * format unless * DECC$EFS_CHARSET is in effect, so temporarily
12203 * enable it if it isn't already.
12205 if (!decc_efs_charset && (decc_efs_charset_index > 0))
12206 decc$feature_set_value(decc_efs_charset_index, 1, 1);
12207 if (lstat_flag == 0)
12208 retval = stat(fspec, &statbufp->crtl_stat);
12210 retval = lstat(fspec, &statbufp->crtl_stat);
12212 if (!decc_efs_charset && (decc_efs_charset_index > 0)) {
12213 decc$feature_set_value(decc_efs_charset_index, 1, 0);
12218 #if __CRTL_VER >= 80200000
12220 if (lstat_flag == 0)
12221 retval = stat(temp_fspec, &statbufp->crtl_stat);
12223 retval = lstat(temp_fspec, &statbufp->crtl_stat);
12224 save_spec = temp_fspec;
12228 /* As you were... */
12229 if (!decc_efs_charset)
12230 decc$feature_set_value(decc$feature_get_index("DECC$EFS_CHARSET"),1,0);
12234 int rmsex_flags = PERL_RMSEXPAND_M_VMS;
12236 /* If this is an lstat, do not follow the link */
12238 rmsex_flags |= PERL_RMSEXPAND_M_SYMLINK;
12240 /* If we used the efs_hack above, we must also use it here for */
12241 /* perl_cando to work */
12242 if (efs_hack && (decc_efs_charset_index > 0)) {
12243 decc$feature_set_value(decc_efs_charset_index, 1, 1);
12246 /* If we've got a directory, save a fileified, expanded version of it
12247 * in st_devnam. If not a directory, just an expanded version.
12249 if (S_ISDIR(statbufp->st_mode) && !already_fileified) {
12250 fileified = (char *)PerlMem_malloc(VMS_MAXRSS);
12251 if (fileified == NULL)
12252 _ckvmssts_noperl(SS$_INSFMEM);
12254 cptr = do_fileify_dirspec(save_spec, fileified, 0, NULL);
12256 save_spec = fileified;
12259 cptr = int_rmsexpand(save_spec,
12260 statbufp->st_devnam,
12266 if (efs_hack && (decc_efs_charset_index > 0)) {
12267 decc$feature_set_value(decc_efs_charset, 1, 0);
12270 /* Fix me: If this is NULL then stat found a file, and we could */
12271 /* not convert the specification to VMS - Should never happen */
12273 statbufp->st_devnam[0] = 0;
12275 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
12277 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
12278 # ifdef VMSISH_TIME
12280 statbufp->st_mtime = _toloc(statbufp->st_mtime);
12281 statbufp->st_atime = _toloc(statbufp->st_atime);
12282 statbufp->st_ctime = _toloc(statbufp->st_ctime);
12286 /* If we were successful, leave errno where we found it */
12287 if (retval == 0) RESTORE_ERRNO;
12289 PerlMem_free(temp_fspec);
12291 PerlMem_free(fileified);
12294 } /* end of flex_stat_int() */
12297 /*{{{ int flex_stat(const char *fspec, Stat_t *statbufp)*/
12299 Perl_flex_stat(pTHX_ const char *fspec, Stat_t *statbufp)
12301 return flex_stat_int(fspec, statbufp, 0);
12305 /*{{{ int flex_lstat(const char *fspec, Stat_t *statbufp)*/
12307 Perl_flex_lstat(pTHX_ const char *fspec, Stat_t *statbufp)
12309 return flex_stat_int(fspec, statbufp, 1);
12314 /* rmscopy - copy a file using VMS RMS routines
12316 * Copies contents and attributes of spec_in to spec_out, except owner
12317 * and protection information. Name and type of spec_in are used as
12318 * defaults for spec_out. The third parameter specifies whether rmscopy()
12319 * should try to propagate timestamps from the input file to the output file.
12320 * If it is less than 0, no timestamps are preserved. If it is 0, then
12321 * rmscopy() will behave similarly to the DCL COPY command: timestamps are
12322 * propagated to the output file at creation iff the output file specification
12323 * did not contain an explicit name or type, and the revision date is always
12324 * updated at the end of the copy operation. If it is greater than 0, then
12325 * it is interpreted as a bitmask, in which bit 0 indicates that timestamps
12326 * other than the revision date should be propagated, and bit 1 indicates
12327 * that the revision date should be propagated.
12329 * Returns 1 on success; returns 0 and sets errno and vaxc$errno on failure.
12331 * Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>.
12332 * Incorporates, with permission, some code from EZCOPY by Tim Adye
12333 * <T.J.Adye@rl.ac.uk>. Permission is given to distribute this code
12334 * as part of the Perl standard distribution under the terms of the
12335 * GNU General Public License or the Perl Artistic License. Copies
12336 * of each may be found in the Perl standard distribution.
12338 /*{{{int rmscopy(char *src, char *dst, int preserve_dates)*/
12340 Perl_rmscopy(pTHX_ const char *spec_in, const char *spec_out, int preserve_dates)
12342 char *vmsin, * vmsout, *esa, *esal, *esa_out, *esal_out,
12343 *rsa, *rsal, *rsa_out, *rsal_out, *ubf;
12344 unsigned long int sts;
12346 struct FAB fab_in, fab_out;
12347 struct RAB rab_in, rab_out;
12348 rms_setup_nam(nam);
12349 rms_setup_nam(nam_out);
12350 struct XABDAT xabdat;
12351 struct XABFHC xabfhc;
12352 struct XABRDT xabrdt;
12353 struct XABSUM xabsum;
12355 vmsin = (char *)PerlMem_malloc(VMS_MAXRSS);
12356 if (vmsin == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12357 vmsout = (char *)PerlMem_malloc(VMS_MAXRSS);
12358 if (vmsout == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12359 if (!spec_in || !*spec_in || !int_tovmsspec(spec_in, vmsin, 1, NULL) ||
12360 !spec_out || !*spec_out || !int_tovmsspec(spec_out, vmsout, 1, NULL)) {
12361 PerlMem_free(vmsin);
12362 PerlMem_free(vmsout);
12363 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12367 esa = (char *)PerlMem_malloc(VMS_MAXRSS);
12368 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12370 #if defined(NAML$C_MAXRSS)
12371 esal = (char *)PerlMem_malloc(VMS_MAXRSS);
12372 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12374 fab_in = cc$rms_fab;
12375 rms_set_fna(fab_in, nam, vmsin, strlen(vmsin));
12376 fab_in.fab$b_shr = FAB$M_SHRPUT | FAB$M_UPI;
12377 fab_in.fab$b_fac = FAB$M_BIO | FAB$M_GET;
12378 fab_in.fab$l_fop = FAB$M_SQO;
12379 rms_bind_fab_nam(fab_in, nam);
12380 fab_in.fab$l_xab = (void *) &xabdat;
12382 rsa = (char *)PerlMem_malloc(VMS_MAXRSS);
12383 if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12385 #if defined(NAML$C_MAXRSS)
12386 rsal = (char *)PerlMem_malloc(VMS_MAXRSS);
12387 if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12389 rms_set_rsal(nam, rsa, NAM$C_MAXRSS, rsal, (VMS_MAXRSS - 1));
12390 rms_set_esal(nam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1));
12391 rms_nam_esl(nam) = 0;
12392 rms_nam_rsl(nam) = 0;
12393 rms_nam_esll(nam) = 0;
12394 rms_nam_rsll(nam) = 0;
12395 #ifdef NAM$M_NO_SHORT_UPCASE
12396 if (decc_efs_case_preserve)
12397 rms_set_nam_nop(nam, NAM$M_NO_SHORT_UPCASE);
12400 xabdat = cc$rms_xabdat; /* To get creation date */
12401 xabdat.xab$l_nxt = (void *) &xabfhc;
12403 xabfhc = cc$rms_xabfhc; /* To get record length */
12404 xabfhc.xab$l_nxt = (void *) &xabsum;
12406 xabsum = cc$rms_xabsum; /* To get key and area information */
12408 if (!((sts = sys$open(&fab_in)) & 1)) {
12409 PerlMem_free(vmsin);
12410 PerlMem_free(vmsout);
12413 PerlMem_free(esal);
12416 PerlMem_free(rsal);
12417 set_vaxc_errno(sts);
12419 case RMS$_FNF: case RMS$_DNF:
12420 set_errno(ENOENT); break;
12422 set_errno(ENOTDIR); break;
12424 set_errno(ENODEV); break;
12426 set_errno(EINVAL); break;
12428 set_errno(EACCES); break;
12430 set_errno(EVMSERR);
12437 fab_out.fab$w_ifi = 0;
12438 fab_out.fab$b_fac = FAB$M_BIO | FAB$M_PUT;
12439 fab_out.fab$b_shr = FAB$M_SHRGET | FAB$M_UPI;
12440 fab_out.fab$l_fop = FAB$M_SQO;
12441 rms_bind_fab_nam(fab_out, nam_out);
12442 rms_set_fna(fab_out, nam_out, vmsout, strlen(vmsout));
12443 dna_len = rms_nam_namel(nam) ? rms_nam_name_type_l_size(nam) : 0;
12444 rms_set_dna(fab_out, nam_out, rms_nam_namel(nam), dna_len);
12445 esa_out = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
12446 if (esa_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12447 rsa_out = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
12448 if (rsa_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12451 #if defined(NAML$C_MAXRSS)
12452 esal_out = (char *)PerlMem_malloc(VMS_MAXRSS);
12453 if (esal_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12454 rsal_out = (char *)PerlMem_malloc(VMS_MAXRSS);
12455 if (rsal_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12457 rms_set_rsal(nam_out, rsa_out, NAM$C_MAXRSS, rsal_out, (VMS_MAXRSS - 1));
12458 rms_set_esal(nam_out, esa_out, NAM$C_MAXRSS, esal_out, (VMS_MAXRSS - 1));
12460 if (preserve_dates == 0) { /* Act like DCL COPY */
12461 rms_set_nam_nop(nam_out, NAM$M_SYNCHK);
12462 fab_out.fab$l_xab = NULL; /* Don't disturb data from input file */
12463 if (!((sts = sys$parse(&fab_out)) & STS$K_SUCCESS)) {
12464 PerlMem_free(vmsin);
12465 PerlMem_free(vmsout);
12468 PerlMem_free(esal);
12471 PerlMem_free(rsal);
12472 PerlMem_free(esa_out);
12473 if (esal_out != NULL)
12474 PerlMem_free(esal_out);
12475 PerlMem_free(rsa_out);
12476 if (rsal_out != NULL)
12477 PerlMem_free(rsal_out);
12478 set_errno(sts == RMS$_SYN ? EINVAL : EVMSERR);
12479 set_vaxc_errno(sts);
12482 fab_out.fab$l_xab = (void *) &xabdat;
12483 if (rms_is_nam_fnb(nam, NAM$M_EXP_NAME | NAM$M_EXP_TYPE))
12484 preserve_dates = 1;
12486 if (preserve_dates < 0) /* Clear all bits; we'll use it as a */
12487 preserve_dates =0; /* bitmask from this point forward */
12489 if (!(preserve_dates & 1)) fab_out.fab$l_xab = (void *) &xabfhc;
12490 if (!((sts = sys$create(&fab_out)) & STS$K_SUCCESS)) {
12491 PerlMem_free(vmsin);
12492 PerlMem_free(vmsout);
12495 PerlMem_free(esal);
12498 PerlMem_free(rsal);
12499 PerlMem_free(esa_out);
12500 if (esal_out != NULL)
12501 PerlMem_free(esal_out);
12502 PerlMem_free(rsa_out);
12503 if (rsal_out != NULL)
12504 PerlMem_free(rsal_out);
12505 set_vaxc_errno(sts);
12508 set_errno(ENOENT); break;
12510 set_errno(ENOTDIR); break;
12512 set_errno(ENODEV); break;
12514 set_errno(EINVAL); break;
12516 set_errno(EACCES); break;
12518 set_errno(EVMSERR);
12522 fab_out.fab$l_fop |= FAB$M_DLT; /* in case we have to bail out */
12523 if (preserve_dates & 2) {
12524 /* sys$close() will process xabrdt, not xabdat */
12525 xabrdt = cc$rms_xabrdt;
12526 xabrdt.xab$q_rdt = xabdat.xab$q_rdt;
12527 fab_out.fab$l_xab = (void *) &xabrdt;
12530 ubf = (char *)PerlMem_malloc(32256);
12531 if (ubf == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12532 rab_in = cc$rms_rab;
12533 rab_in.rab$l_fab = &fab_in;
12534 rab_in.rab$l_rop = RAB$M_BIO;
12535 rab_in.rab$l_ubf = ubf;
12536 rab_in.rab$w_usz = 32256;
12537 if (!((sts = sys$connect(&rab_in)) & 1)) {
12538 sys$close(&fab_in); sys$close(&fab_out);
12539 PerlMem_free(vmsin);
12540 PerlMem_free(vmsout);
12544 PerlMem_free(esal);
12547 PerlMem_free(rsal);
12548 PerlMem_free(esa_out);
12549 if (esal_out != NULL)
12550 PerlMem_free(esal_out);
12551 PerlMem_free(rsa_out);
12552 if (rsal_out != NULL)
12553 PerlMem_free(rsal_out);
12554 set_errno(EVMSERR); set_vaxc_errno(sts);
12558 rab_out = cc$rms_rab;
12559 rab_out.rab$l_fab = &fab_out;
12560 rab_out.rab$l_rbf = ubf;
12561 if (!((sts = sys$connect(&rab_out)) & 1)) {
12562 sys$close(&fab_in); sys$close(&fab_out);
12563 PerlMem_free(vmsin);
12564 PerlMem_free(vmsout);
12568 PerlMem_free(esal);
12571 PerlMem_free(rsal);
12572 PerlMem_free(esa_out);
12573 if (esal_out != NULL)
12574 PerlMem_free(esal_out);
12575 PerlMem_free(rsa_out);
12576 if (rsal_out != NULL)
12577 PerlMem_free(rsal_out);
12578 set_errno(EVMSERR); set_vaxc_errno(sts);
12582 while ((sts = sys$read(&rab_in))) { /* always true */
12583 if (sts == RMS$_EOF) break;
12584 rab_out.rab$w_rsz = rab_in.rab$w_rsz;
12585 if (!(sts & 1) || !((sts = sys$write(&rab_out)) & 1)) {
12586 sys$close(&fab_in); sys$close(&fab_out);
12587 PerlMem_free(vmsin);
12588 PerlMem_free(vmsout);
12592 PerlMem_free(esal);
12595 PerlMem_free(rsal);
12596 PerlMem_free(esa_out);
12597 if (esal_out != NULL)
12598 PerlMem_free(esal_out);
12599 PerlMem_free(rsa_out);
12600 if (rsal_out != NULL)
12601 PerlMem_free(rsal_out);
12602 set_errno(EVMSERR); set_vaxc_errno(sts);
12608 fab_out.fab$l_fop &= ~FAB$M_DLT; /* We got this far; keep the output */
12609 sys$close(&fab_in); sys$close(&fab_out);
12610 sts = (fab_in.fab$l_sts & 1) ? fab_out.fab$l_sts : fab_in.fab$l_sts;
12612 PerlMem_free(vmsin);
12613 PerlMem_free(vmsout);
12617 PerlMem_free(esal);
12620 PerlMem_free(rsal);
12621 PerlMem_free(esa_out);
12622 if (esal_out != NULL)
12623 PerlMem_free(esal_out);
12624 PerlMem_free(rsa_out);
12625 if (rsal_out != NULL)
12626 PerlMem_free(rsal_out);
12629 set_errno(EVMSERR); set_vaxc_errno(sts);
12635 } /* end of rmscopy() */
12639 /*** The following glue provides 'hooks' to make some of the routines
12640 * from this file available from Perl. These routines are sufficiently
12641 * basic, and are required sufficiently early in the build process,
12642 * that's it's nice to have them available to miniperl as well as the
12643 * full Perl, so they're set up here instead of in an extension. The
12644 * Perl code which handles importation of these names into a given
12645 * package lives in [.VMS]Filespec.pm in @INC.
12649 rmsexpand_fromperl(pTHX_ CV *cv)
12652 char *fspec, *defspec = NULL, *rslt;
12654 int fs_utf8, dfs_utf8;
12658 if (!items || items > 2)
12659 Perl_croak(aTHX_ "Usage: VMS::Filespec::rmsexpand(spec[,defspec])");
12660 fspec = SvPV(ST(0),n_a);
12661 fs_utf8 = SvUTF8(ST(0));
12662 if (!fspec || !*fspec) XSRETURN_UNDEF;
12664 defspec = SvPV(ST(1),n_a);
12665 dfs_utf8 = SvUTF8(ST(1));
12667 rslt = do_rmsexpand(fspec,NULL,1,defspec,0,&fs_utf8,&dfs_utf8);
12668 ST(0) = sv_newmortal();
12669 if (rslt != NULL) {
12670 sv_usepvn(ST(0),rslt,strlen(rslt));
12679 vmsify_fromperl(pTHX_ CV *cv)
12686 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsify(spec)");
12687 utf8_fl = SvUTF8(ST(0));
12688 vmsified = do_tovmsspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12689 ST(0) = sv_newmortal();
12690 if (vmsified != NULL) {
12691 sv_usepvn(ST(0),vmsified,strlen(vmsified));
12700 unixify_fromperl(pTHX_ CV *cv)
12707 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixify(spec)");
12708 utf8_fl = SvUTF8(ST(0));
12709 unixified = do_tounixspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12710 ST(0) = sv_newmortal();
12711 if (unixified != NULL) {
12712 sv_usepvn(ST(0),unixified,strlen(unixified));
12721 fileify_fromperl(pTHX_ CV *cv)
12728 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::fileify(spec)");
12729 utf8_fl = SvUTF8(ST(0));
12730 fileified = do_fileify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12731 ST(0) = sv_newmortal();
12732 if (fileified != NULL) {
12733 sv_usepvn(ST(0),fileified,strlen(fileified));
12742 pathify_fromperl(pTHX_ CV *cv)
12749 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::pathify(spec)");
12750 utf8_fl = SvUTF8(ST(0));
12751 pathified = do_pathify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12752 ST(0) = sv_newmortal();
12753 if (pathified != NULL) {
12754 sv_usepvn(ST(0),pathified,strlen(pathified));
12763 vmspath_fromperl(pTHX_ CV *cv)
12770 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmspath(spec)");
12771 utf8_fl = SvUTF8(ST(0));
12772 vmspath = do_tovmspath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12773 ST(0) = sv_newmortal();
12774 if (vmspath != NULL) {
12775 sv_usepvn(ST(0),vmspath,strlen(vmspath));
12784 unixpath_fromperl(pTHX_ CV *cv)
12791 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixpath(spec)");
12792 utf8_fl = SvUTF8(ST(0));
12793 unixpath = do_tounixpath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12794 ST(0) = sv_newmortal();
12795 if (unixpath != NULL) {
12796 sv_usepvn(ST(0),unixpath,strlen(unixpath));
12805 candelete_fromperl(pTHX_ CV *cv)
12813 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::candelete(spec)");
12815 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
12816 Newx(fspec, VMS_MAXRSS, char);
12817 if (fspec == NULL) _ckvmssts(SS$_INSFMEM);
12818 if (isGV_with_GP(mysv)) {
12819 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),fspec)) {
12820 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12828 if (mysv != ST(0) || !(fsp = SvPV(mysv,n_a)) || !*fsp) {
12829 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12836 ST(0) = boolSV(cando_by_name(S_IDUSR,0,fsp));
12842 rmscopy_fromperl(pTHX_ CV *cv)
12845 char *inspec, *outspec, *inp, *outp;
12851 if (items < 2 || items > 3)
12852 Perl_croak(aTHX_ "Usage: File::Copy::rmscopy(from,to[,date_flag])");
12854 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
12855 Newx(inspec, VMS_MAXRSS, char);
12856 if (isGV_with_GP(mysv)) {
12857 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),inspec)) {
12858 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12859 ST(0) = sv_2mortal(newSViv(0));
12866 if (mysv != ST(0) || !(inp = SvPV(mysv,n_a)) || !*inp) {
12867 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12868 ST(0) = sv_2mortal(newSViv(0));
12873 mysv = SvROK(ST(1)) ? SvRV(ST(1)) : ST(1);
12874 Newx(outspec, VMS_MAXRSS, char);
12875 if (isGV_with_GP(mysv)) {
12876 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),outspec)) {
12877 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12878 ST(0) = sv_2mortal(newSViv(0));
12886 if (mysv != ST(1) || !(outp = SvPV(mysv,n_a)) || !*outp) {
12887 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12888 ST(0) = sv_2mortal(newSViv(0));
12894 date_flag = (items == 3) ? SvIV(ST(2)) : 0;
12896 ST(0) = sv_2mortal(newSViv(rmscopy(inp,outp,date_flag)));
12902 /* The mod2fname is limited to shorter filenames by design, so it should
12903 * not be modified to support longer EFS pathnames
12906 mod2fname(pTHX_ CV *cv)
12909 char ultimate_name[NAM$C_MAXRSS+1], work_name[NAM$C_MAXRSS*8 + 1],
12910 workbuff[NAM$C_MAXRSS*1 + 1];
12911 SSize_t counter, num_entries;
12912 /* ODS-5 ups this, but we want to be consistent, so... */
12913 int max_name_len = 39;
12914 AV *in_array = (AV *)SvRV(ST(0));
12916 num_entries = av_tindex(in_array);
12918 /* All the names start with PL_. */
12919 strcpy(ultimate_name, "PL_");
12921 /* Clean up our working buffer */
12922 Zero(work_name, sizeof(work_name), char);
12924 /* Run through the entries and build up a working name */
12925 for(counter = 0; counter <= num_entries; counter++) {
12926 /* If it's not the first name then tack on a __ */
12928 my_strlcat(work_name, "__", sizeof(work_name));
12930 my_strlcat(work_name, SvPV_nolen(*av_fetch(in_array, counter, FALSE)), sizeof(work_name));
12933 /* Check to see if we actually have to bother...*/
12934 if (strlen(work_name) + 3 <= max_name_len) {
12935 my_strlcat(ultimate_name, work_name, sizeof(ultimate_name));
12937 /* It's too darned big, so we need to go strip. We use the same */
12938 /* algorithm as xsubpp does. First, strip out doubled __ */
12939 char *source, *dest, last;
12942 for (source = work_name; *source; source++) {
12943 if (last == *source && last == '_') {
12949 /* Go put it back */
12950 my_strlcpy(work_name, workbuff, sizeof(work_name));
12951 /* Is it still too big? */
12952 if (strlen(work_name) + 3 > max_name_len) {
12953 /* Strip duplicate letters */
12956 for (source = work_name; *source; source++) {
12957 if (last == toupper(*source)) {
12961 last = toupper(*source);
12963 my_strlcpy(work_name, workbuff, sizeof(work_name));
12966 /* Is it *still* too big? */
12967 if (strlen(work_name) + 3 > max_name_len) {
12968 /* Too bad, we truncate */
12969 work_name[max_name_len - 2] = 0;
12971 my_strlcat(ultimate_name, work_name, sizeof(ultimate_name));
12974 /* Okay, return it */
12975 ST(0) = sv_2mortal(newSVpv(ultimate_name, 0));
12980 hushexit_fromperl(pTHX_ CV *cv)
12985 VMSISH_HUSHED = SvTRUE(ST(0));
12987 ST(0) = boolSV(VMSISH_HUSHED);
12993 Perl_vms_start_glob(pTHX_ SV *tmpglob, IO *io)
12996 struct vs_str_st *rslt;
13000 $DESCRIPTOR(dfltdsc,"SYS$DISK:[]*.*;");
13003 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13004 struct dsc$descriptor_vs rsdsc;
13005 unsigned long int cxt = 0, sts = 0, ok = 1, hasdir = 0;
13006 unsigned long hasver = 0, isunix = 0;
13007 unsigned long int lff_flags = 0;
13009 int vms_old_glob = 1;
13011 if (!SvOK(tmpglob)) {
13012 SETERRNO(ENOENT,RMS$_FNF);
13016 vms_old_glob = !decc_filename_unix_report;
13018 #ifdef VMS_LONGNAME_SUPPORT
13019 lff_flags = LIB$M_FIL_LONG_NAMES;
13021 /* The Newx macro will not allow me to assign a smaller array
13022 * to the rslt pointer, so we will assign it to the begin char pointer
13023 * and then copy the value into the rslt pointer.
13025 Newx(begin, VMS_MAXRSS + sizeof(unsigned short int), char);
13026 rslt = (struct vs_str_st *)begin;
13028 rstr = &rslt->str[0];
13029 rsdsc.dsc$a_pointer = (char *) rslt; /* cast required */
13030 rsdsc.dsc$w_maxstrlen = VMS_MAXRSS + sizeof(unsigned short int);
13031 rsdsc.dsc$b_dtype = DSC$K_DTYPE_VT;
13032 rsdsc.dsc$b_class = DSC$K_CLASS_VS;
13034 Newx(vmsspec, VMS_MAXRSS, char);
13036 /* We could find out if there's an explicit dev/dir or version
13037 by peeking into lib$find_file's internal context at
13038 ((struct NAM *)((struct FAB *)cxt)->fab$l_nam)->nam$l_fnb
13039 but that's unsupported, so I don't want to do it now and
13040 have it bite someone in the future. */
13041 /* Fix-me: vms_split_path() is the only way to do this, the
13042 existing method will fail with many legal EFS or UNIX specifications
13045 cp = SvPV(tmpglob,i);
13048 if (cp[i] == ';') hasver = 1;
13049 if (cp[i] == '.') {
13050 if (sts) hasver = 1;
13053 if (cp[i] == '/') {
13054 hasdir = isunix = 1;
13057 if (cp[i] == ']' || cp[i] == '>' || cp[i] == ':') {
13063 /* In UNIX report mode, assume UNIX unless VMS directory delimiters seen */
13064 if ((hasdir == 0) && decc_filename_unix_report) {
13068 if ((tmpfp = PerlIO_tmpfile()) != NULL) {
13069 char * wv_spec, * wr_spec, * wd_spec, * wn_spec, * we_spec, * wvs_spec;
13070 int wv_sts, wv_len, wr_len, wd_len, wn_len, we_len, wvs_len;
13076 stat_sts = PerlLIO_stat(SvPVX_const(tmpglob),&st);
13077 if (!stat_sts && S_ISDIR(st.st_mode)) {
13079 const char * fname;
13082 /* Test to see if SvPVX_const(tmpglob) ends with a VMS */
13083 /* path delimiter of ':>]', if so, then the old behavior has */
13084 /* obviously been specifically requested */
13086 fname = SvPVX_const(tmpglob);
13087 fname_len = strlen(fname);
13088 vms_dir = strpbrk(&fname[fname_len - 1], ":>]");
13089 if (vms_old_glob || (vms_dir != NULL)) {
13090 wilddsc.dsc$a_pointer = tovmspath_utf8(
13091 SvPVX(tmpglob),vmsspec,NULL);
13092 ok = (wilddsc.dsc$a_pointer != NULL);
13093 /* maybe passed 'foo' rather than '[.foo]', thus not
13097 /* Operate just on the directory, the special stat/fstat for */
13098 /* leaves the fileified specification in the st_devnam */
13100 wilddsc.dsc$a_pointer = st.st_devnam;
13105 wilddsc.dsc$a_pointer = tovmsspec_utf8(SvPVX(tmpglob),vmsspec,NULL);
13106 ok = (wilddsc.dsc$a_pointer != NULL);
13109 wilddsc.dsc$w_length = strlen(wilddsc.dsc$a_pointer);
13111 /* If not extended character set, replace ? with % */
13112 /* With extended character set, ? is a wildcard single character */
13113 for (cp=wilddsc.dsc$a_pointer; ok && cp && *cp; cp++) {
13116 if (!decc_efs_charset)
13118 } else if (*cp == '%') {
13120 } else if (*cp == '*') {
13126 wv_sts = vms_split_path(
13127 wilddsc.dsc$a_pointer, &wv_spec, &wv_len, &wr_spec, &wr_len,
13128 &wd_spec, &wd_len, &wn_spec, &wn_len, &we_spec, &we_len,
13129 &wvs_spec, &wvs_len);
13138 while (ok && $VMS_STATUS_SUCCESS(sts)) {
13139 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
13140 int v_sts, v_len, r_len, d_len, n_len, e_len, vs_len;
13144 sts = lib$find_file(&wilddsc,&rsdsc,&cxt,
13145 &dfltdsc,NULL,&rms_sts,&lff_flags);
13146 if (!$VMS_STATUS_SUCCESS(sts))
13149 /* with varying string, 1st word of buffer contains result length */
13150 rstr[rslt->length] = '\0';
13152 /* Find where all the components are */
13153 v_sts = vms_split_path
13168 /* If no version on input, truncate the version on output */
13169 if (!hasver && (vs_len > 0)) {
13176 /* In Unix report mode, remove the ".dir;1" from the name */
13177 /* if it is a real directory */
13178 if (decc_filename_unix_report && decc_efs_charset) {
13179 if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) {
13183 ret_sts = flex_lstat(rstr, &statbuf);
13184 if ((ret_sts == 0) &&
13185 S_ISDIR(statbuf.st_mode)) {
13192 /* No version & a null extension on UNIX handling */
13193 if ((e_len == 1) && decc_readdir_dropdotnotype) {
13199 if (!decc_efs_case_preserve) {
13200 for (cp = rstr; *cp; cp++) *cp = _tolower(*cp);
13203 /* Find File treats a Null extension as return all extensions */
13204 /* This is contrary to Perl expectations */
13206 if (wildstar || wildquery || vms_old_glob) {
13207 /* really need to see if the returned file name matched */
13208 /* but for now will assume that it matches */
13211 /* Exact Match requested */
13212 /* How are directories handled? - like a file */
13213 if ((e_len == we_len) && (n_len == wn_len)) {
13217 t1 = strncmp(e_spec, we_spec, e_len);
13221 t1 = strncmp(n_spec, we_spec, n_len);
13232 if (isunix) trim_unixpath(rstr,SvPVX(tmpglob),1);
13236 /* Start with the name */
13239 strcat(begin,"\n");
13240 ok = (PerlIO_puts(tmpfp,begin) != EOF);
13243 if (cxt) (void)lib$find_file_end(&cxt);
13246 /* Be POSIXish: return the input pattern when no matches */
13247 my_strlcpy(rstr, SvPVX(tmpglob), VMS_MAXRSS);
13249 ok = (PerlIO_puts(tmpfp,rstr) != EOF);
13252 if (ok && sts != RMS$_NMF &&
13253 sts != RMS$_DNF && sts != RMS_FNF) ok = 0;
13256 SETERRNO((sts == RMS$_SYN ? EINVAL : EVMSERR),sts);
13258 PerlIO_close(tmpfp);
13262 PerlIO_rewind(tmpfp);
13263 IoTYPE(io) = IoTYPE_RDONLY;
13264 IoIFP(io) = fp = tmpfp;
13265 IoFLAGS(io) &= ~IOf_UNTAINT; /* maybe redundant */
13275 mp_do_vms_realpath(pTHX_ const char *filespec, char * rslt_spec,
13279 unixrealpath_fromperl(pTHX_ CV *cv)
13282 char *fspec, *rslt_spec, *rslt;
13285 if (!items || items != 1)
13286 Perl_croak(aTHX_ "Usage: VMS::Filespec::unixrealpath(spec)");
13288 fspec = SvPV(ST(0),n_a);
13289 if (!fspec || !*fspec) XSRETURN_UNDEF;
13291 Newx(rslt_spec, VMS_MAXRSS + 1, char);
13292 rslt = do_vms_realpath(fspec, rslt_spec, NULL);
13294 ST(0) = sv_newmortal();
13296 sv_usepvn(ST(0),rslt,strlen(rslt));
13298 Safefree(rslt_spec);
13303 mp_do_vms_realname(pTHX_ const char *filespec, char * rslt_spec,
13307 vmsrealpath_fromperl(pTHX_ CV *cv)
13310 char *fspec, *rslt_spec, *rslt;
13313 if (!items || items != 1)
13314 Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsrealpath(spec)");
13316 fspec = SvPV(ST(0),n_a);
13317 if (!fspec || !*fspec) XSRETURN_UNDEF;
13319 Newx(rslt_spec, VMS_MAXRSS + 1, char);
13320 rslt = do_vms_realname(fspec, rslt_spec, NULL);
13322 ST(0) = sv_newmortal();
13324 sv_usepvn(ST(0),rslt,strlen(rslt));
13326 Safefree(rslt_spec);
13332 * A thin wrapper around decc$symlink to make sure we follow the
13333 * standard and do not create a symlink with a zero-length name,
13334 * and convert the target to Unix format, as the CRTL can't handle
13335 * targets in VMS format.
13337 /*{{{ int my_symlink(pTHX_ const char *contents, const char *link_name)*/
13339 Perl_my_symlink(pTHX_ const char *contents, const char *link_name)
13344 if (!link_name || !*link_name) {
13345 SETERRNO(ENOENT, SS$_NOSUCHFILE);
13349 utarget = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
13350 /* An untranslatable filename should be passed through. */
13351 (void) int_tounixspec(contents, utarget, NULL);
13352 sts = symlink(utarget, link_name);
13353 PerlMem_free(utarget);
13358 #endif /* HAS_SYMLINK */
13360 int do_vms_case_tolerant(void);
13363 case_tolerant_process_fromperl(pTHX_ CV *cv)
13366 ST(0) = boolSV(do_vms_case_tolerant());
13370 #ifdef USE_ITHREADS
13373 Perl_sys_intern_dup(pTHX_ struct interp_intern *src,
13374 struct interp_intern *dst)
13376 PERL_ARGS_ASSERT_SYS_INTERN_DUP;
13378 memcpy(dst,src,sizeof(struct interp_intern));
13384 Perl_sys_intern_clear(pTHX)
13389 Perl_sys_intern_init(pTHX)
13391 unsigned int ix = RAND_MAX;
13396 MY_POSIX_EXIT = vms_posix_exit;
13399 MY_INV_RAND_MAX = 1./x;
13403 init_os_extras(void)
13406 char* file = __FILE__;
13407 if (decc_disable_to_vms_logname_translation) {
13408 no_translate_barewords = TRUE;
13410 no_translate_barewords = FALSE;
13413 newXSproto("VMS::Filespec::rmsexpand",rmsexpand_fromperl,file,"$;$");
13414 newXSproto("VMS::Filespec::vmsify",vmsify_fromperl,file,"$");
13415 newXSproto("VMS::Filespec::unixify",unixify_fromperl,file,"$");
13416 newXSproto("VMS::Filespec::pathify",pathify_fromperl,file,"$");
13417 newXSproto("VMS::Filespec::fileify",fileify_fromperl,file,"$");
13418 newXSproto("VMS::Filespec::vmspath",vmspath_fromperl,file,"$");
13419 newXSproto("VMS::Filespec::unixpath",unixpath_fromperl,file,"$");
13420 newXSproto("VMS::Filespec::candelete",candelete_fromperl,file,"$");
13421 newXSproto("DynaLoader::mod2fname", mod2fname, file, "$");
13422 newXS("File::Copy::rmscopy",rmscopy_fromperl,file);
13423 newXSproto("vmsish::hushed",hushexit_fromperl,file,";$");
13424 newXSproto("VMS::Filespec::unixrealpath",unixrealpath_fromperl,file,"$;$");
13425 newXSproto("VMS::Filespec::vmsrealpath",vmsrealpath_fromperl,file,"$;$");
13426 newXSproto("VMS::Filespec::case_tolerant_process",
13427 case_tolerant_process_fromperl,file,"");
13429 store_pipelocs(aTHX); /* will redo any earlier attempts */
13434 #if __CRTL_VER == 80200000
13435 /* This missed getting in to the DECC SDK for 8.2 */
13436 char *realpath(const char *file_name, char * resolved_name, ...);
13439 /*{{{char *do_vms_realpath(const char *file_name, char *resolved_name)*/
13440 /* wrapper for the realpath() function added with 8.2 RMS SYMLINK SDK.
13441 * The perl fallback routine to provide realpath() is not as efficient
13449 /* Hack, use old stat() as fastest way of getting ino_t and device */
13450 int decc$stat(const char *name, void * statbuf);
13451 #if __CRTL_VER >= 80200000
13452 int decc$lstat(const char *name, void * statbuf);
13454 #define decc$lstat decc$stat
13462 /* Realpath is fragile. In 8.3 it does not work if the feature
13463 * DECC$POSIX_COMPLIANT_PATHNAMES is not enabled, even though symbolic
13464 * links are implemented in RMS, not the CRTL. It also can fail if the
13465 * user does not have read/execute access to some of the directories.
13466 * So in order for Do What I Mean mode to work, if realpath() fails,
13467 * fall back to looking up the filename by the device name and FID.
13470 int vms_fid_to_name(char * outname, int outlen,
13471 const char * name, int lstat_flag, mode_t * mode)
13473 #pragma message save
13474 #pragma message disable MISALGNDSTRCT
13475 #pragma message disable MISALGNDMEM
13476 #pragma member_alignment save
13477 #pragma nomember_alignment
13480 unsigned short st_ino[3];
13481 unsigned short old_st_mode;
13482 unsigned long padl[30]; /* plenty of room */
13484 #pragma message restore
13485 #pragma member_alignment restore
13488 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13489 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13494 /* Need to follow the mostly the same rules as flex_stat_int, or we may get
13495 * unexpected answers
13498 fileified = (char *)PerlMem_malloc(VMS_MAXRSS);
13499 if (fileified == NULL)
13500 _ckvmssts_noperl(SS$_INSFMEM);
13502 temp_fspec = (char *)PerlMem_malloc(VMS_MAXRSS);
13503 if (temp_fspec == NULL)
13504 _ckvmssts_noperl(SS$_INSFMEM);
13507 /* First need to try as a directory */
13508 ret_spec = int_tovmspath(name, temp_fspec, NULL);
13509 if (ret_spec != NULL) {
13510 ret_spec = int_fileify_dirspec(temp_fspec, fileified, NULL);
13511 if (ret_spec != NULL) {
13512 if (lstat_flag == 0)
13513 sts = decc$stat(fileified, &statbuf);
13515 sts = decc$lstat(fileified, &statbuf);
13519 /* Then as a VMS file spec */
13521 ret_spec = int_tovmsspec(name, temp_fspec, 0, NULL);
13522 if (ret_spec != NULL) {
13523 if (lstat_flag == 0) {
13524 sts = decc$stat(temp_fspec, &statbuf);
13526 sts = decc$lstat(temp_fspec, &statbuf);
13532 /* Next try - allow multiple dots with out EFS CHARSET */
13533 /* The CRTL stat() falls down hard on multi-dot filenames in unix
13534 * format unless * DECC$EFS_CHARSET is in effect, so temporarily
13535 * enable it if it isn't already.
13537 if (!decc_efs_charset && (decc_efs_charset_index > 0))
13538 decc$feature_set_value(decc_efs_charset_index, 1, 1);
13539 ret_spec = int_tovmspath(name, temp_fspec, NULL);
13540 if (lstat_flag == 0) {
13541 sts = decc$stat(name, &statbuf);
13543 sts = decc$lstat(name, &statbuf);
13545 if (!decc_efs_charset && (decc_efs_charset_index > 0))
13546 decc$feature_set_value(decc_efs_charset_index, 1, 0);
13550 /* and then because the Perl Unix to VMS conversion is not perfect */
13551 /* Specifically the CRTL removes spaces and possibly other illegal ODS-2 */
13552 /* characters from filenames so we need to try it as-is */
13554 if (lstat_flag == 0) {
13555 sts = decc$stat(name, &statbuf);
13557 sts = decc$lstat(name, &statbuf);
13564 dvidsc.dsc$a_pointer=statbuf.st_dev;
13565 dvidsc.dsc$w_length=strlen(statbuf.st_dev);
13567 specdsc.dsc$a_pointer = outname;
13568 specdsc.dsc$w_length = outlen-1;
13570 vms_sts = lib$fid_to_name
13571 (&dvidsc, statbuf.st_ino, &specdsc, &specdsc.dsc$w_length);
13572 if ($VMS_STATUS_SUCCESS(vms_sts)) {
13573 outname[specdsc.dsc$w_length] = 0;
13575 /* Return the mode */
13577 *mode = statbuf.old_st_mode;
13581 PerlMem_free(temp_fspec);
13582 PerlMem_free(fileified);
13589 mp_do_vms_realpath(pTHX_ const char *filespec, char *outbuf,
13592 char * rslt = NULL;
13595 if (decc_posix_compliant_pathnames > 0 ) {
13596 /* realpath currently only works if posix compliant pathnames are
13597 * enabled. It may start working when they are not, but in that
13598 * case we still want the fallback behavior for backwards compatibility
13600 rslt = realpath(filespec, outbuf);
13604 if (rslt == NULL) {
13606 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
13607 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
13610 /* Fall back to fid_to_name */
13612 Newx(vms_spec, VMS_MAXRSS + 1, char);
13614 sts = vms_fid_to_name(vms_spec, VMS_MAXRSS + 1, filespec, 0, &my_mode);
13618 /* Now need to trim the version off */
13619 sts = vms_split_path
13639 /* Trim off the version */
13640 int file_len = v_len + r_len + d_len + n_len + e_len;
13641 vms_spec[file_len] = 0;
13643 /* Trim off the .DIR if this is a directory */
13644 if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) {
13645 if (S_ISDIR(my_mode)) {
13651 /* Drop NULL extensions on UNIX file specification */
13652 if ((e_len == 1) && decc_readdir_dropdotnotype) {
13657 /* The result is expected to be in UNIX format */
13658 rslt = int_tounixspec(vms_spec, outbuf, utf8_fl);
13660 /* Downcase if input had any lower case letters and
13661 * case preservation is not in effect.
13663 if (!decc_efs_case_preserve) {
13664 for (cp = filespec; *cp; cp++)
13665 if (islower(*cp)) { haslower = 1; break; }
13667 if (haslower) __mystrtolower(rslt);
13672 /* Now for some hacks to deal with backwards and forward */
13673 /* compatibility */
13674 if (!decc_efs_charset) {
13676 /* 1. ODS-2 mode wants to do a syntax only translation */
13677 rslt = int_rmsexpand(filespec, outbuf,
13678 NULL, 0, NULL, utf8_fl);
13681 if (decc_filename_unix_report) {
13683 char * vms_dir_name;
13686 /* 2. ODS-5 / UNIX report mode should return a failure */
13687 /* if the parent directory also does not exist */
13688 /* Otherwise, get the real path for the parent */
13689 /* and add the child to it. */
13691 /* basename / dirname only available for VMS 7.0+ */
13692 /* So we may need to implement them as common routines */
13694 Newx(dir_name, VMS_MAXRSS + 1, char);
13695 Newx(vms_dir_name, VMS_MAXRSS + 1, char);
13696 dir_name[0] = '\0';
13699 /* First try a VMS parse */
13700 sts = vms_split_path
13718 int dir_len = v_len + r_len + d_len + n_len;
13720 memcpy(dir_name, filespec, dir_len);
13721 dir_name[dir_len] = '\0';
13722 file_name = (char *)&filespec[dir_len + 1];
13725 /* This must be UNIX */
13728 tchar = strrchr(filespec, '/');
13730 if (tchar != NULL) {
13731 int dir_len = tchar - filespec;
13732 memcpy(dir_name, filespec, dir_len);
13733 dir_name[dir_len] = '\0';
13734 file_name = (char *) &filespec[dir_len + 1];
13738 /* Dir name is defaulted */
13739 if (dir_name[0] == 0) {
13741 dir_name[1] = '\0';
13744 /* Need realpath for the directory */
13745 sts = vms_fid_to_name(vms_dir_name,
13747 dir_name, 0, NULL);
13750 /* Now need to pathify it. */
13751 char *tdir = int_pathify_dirspec(vms_dir_name,
13754 /* And now add the original filespec to it */
13755 if (file_name != NULL) {
13756 my_strlcat(outbuf, file_name, VMS_MAXRSS);
13760 Safefree(vms_dir_name);
13761 Safefree(dir_name);
13765 Safefree(vms_spec);
13771 mp_do_vms_realname(pTHX_ const char *filespec, char *outbuf,
13774 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
13775 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
13777 /* Fall back to fid_to_name */
13779 sts = vms_fid_to_name(outbuf, VMS_MAXRSS + 1, filespec, 0, NULL);
13786 /* Now need to trim the version off */
13787 sts = vms_split_path
13807 /* Trim off the version */
13808 int file_len = v_len + r_len + d_len + n_len + e_len;
13809 outbuf[file_len] = 0;
13811 /* Downcase if input had any lower case letters and
13812 * case preservation is not in effect.
13814 if (!decc_efs_case_preserve) {
13815 for (cp = filespec; *cp; cp++)
13816 if (islower(*cp)) { haslower = 1; break; }
13818 if (haslower) __mystrtolower(outbuf);
13827 /* External entry points */
13829 Perl_vms_realpath(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
13831 return do_vms_realpath(filespec, outbuf, utf8_fl);
13835 Perl_vms_realname(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
13837 return do_vms_realname(filespec, outbuf, utf8_fl);
13840 /* case_tolerant */
13842 /*{{{int do_vms_case_tolerant(void)*/
13843 /* OpenVMS provides a case sensitive implementation of ODS-5 and this is
13844 * controlled by a process setting.
13847 do_vms_case_tolerant(void)
13849 return vms_process_case_tolerant;
13852 /* External entry points */
13854 Perl_vms_case_tolerant(void)
13856 return do_vms_case_tolerant();
13859 /* Start of DECC RTL Feature handling */
13862 set_feature_default(const char *name, int value)
13868 /* If the feature has been explicitly disabled in the environment,
13869 * then don't enable it here.
13872 status = simple_trnlnm(name, val_str, sizeof(val_str));
13874 val_str[0] = _toupper(val_str[0]);
13875 if (val_str[0] == 'D' || val_str[0] == '0' || val_str[0] == 'F')
13880 index = decc$feature_get_index(name);
13882 status = decc$feature_set_value(index, 1, value);
13883 if (index == -1 || (status == -1)) {
13887 status = decc$feature_get_value(index, 1);
13888 if (status != value) {
13892 /* Various things may check for an environment setting
13893 * rather than the feature directly, so set that too.
13895 vmssetuserlnm(name, value ? "ENABLE" : "DISABLE");
13901 /* C RTL Feature settings */
13903 #if defined(__DECC) || defined(__DECCXX)
13910 vmsperl_set_features(void)
13915 #if defined(JPI$_CASE_LOOKUP_PERM)
13916 const unsigned long int jpicode1 = JPI$_CASE_LOOKUP_PERM;
13917 const unsigned long int jpicode2 = JPI$_CASE_LOOKUP_IMAGE;
13918 unsigned long case_perm;
13919 unsigned long case_image;
13922 /* Allow an exception to bring Perl into the VMS debugger */
13923 vms_debug_on_exception = 0;
13924 status = simple_trnlnm("PERL_VMS_EXCEPTION_DEBUG", val_str, sizeof(val_str));
13926 val_str[0] = _toupper(val_str[0]);
13927 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13928 vms_debug_on_exception = 1;
13930 vms_debug_on_exception = 0;
13933 /* Debug unix/vms file translation routines */
13934 vms_debug_fileify = 0;
13935 status = simple_trnlnm("PERL_VMS_FILEIFY_DEBUG", val_str, sizeof(val_str));
13937 val_str[0] = _toupper(val_str[0]);
13938 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13939 vms_debug_fileify = 1;
13941 vms_debug_fileify = 0;
13945 /* Historically PERL has been doing vmsify / stat differently than */
13946 /* the CRTL. In particular, under some conditions the CRTL will */
13947 /* remove some illegal characters like spaces from filenames */
13948 /* resulting in some differences. The stat()/lstat() wrapper has */
13949 /* been reporting such file names as invalid and fails to stat them */
13950 /* fixing this bug so that stat()/lstat() accept these like the */
13951 /* CRTL does will result in several tests failing. */
13952 /* This should really be fixed, but for now, set up a feature to */
13953 /* enable it so that the impact can be studied. */
13954 vms_bug_stat_filename = 0;
13955 status = simple_trnlnm("PERL_VMS_BUG_STAT_FILENAME", val_str, sizeof(val_str));
13957 val_str[0] = _toupper(val_str[0]);
13958 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13959 vms_bug_stat_filename = 1;
13961 vms_bug_stat_filename = 0;
13965 /* Create VTF-7 filenames from Unicode instead of UTF-8 */
13966 vms_vtf7_filenames = 0;
13967 status = simple_trnlnm("PERL_VMS_VTF7_FILENAMES", val_str, sizeof(val_str));
13969 val_str[0] = _toupper(val_str[0]);
13970 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13971 vms_vtf7_filenames = 1;
13973 vms_vtf7_filenames = 0;
13976 /* unlink all versions on unlink() or rename() */
13977 vms_unlink_all_versions = 0;
13978 status = simple_trnlnm("PERL_VMS_UNLINK_ALL_VERSIONS", val_str, sizeof(val_str));
13980 val_str[0] = _toupper(val_str[0]);
13981 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13982 vms_unlink_all_versions = 1;
13984 vms_unlink_all_versions = 0;
13987 /* Detect running under GNV Bash or other UNIX like shell */
13988 gnv_unix_shell = 0;
13989 status = simple_trnlnm("GNV$UNIX_SHELL", val_str, sizeof(val_str));
13991 gnv_unix_shell = 1;
13992 set_feature_default("DECC$FILENAME_UNIX_NO_VERSION", 1);
13993 set_feature_default("DECC$FILENAME_UNIX_REPORT", 1);
13994 set_feature_default("DECC$READDIR_DROPDOTNOTYPE", 1);
13995 set_feature_default("DECC$DISABLE_POSIX_ROOT", 0);
13996 vms_unlink_all_versions = 1;
13997 vms_posix_exit = 1;
13998 /* Reverse default ordering of PERL_ENV_TABLES. */
13999 defenv[0] = &crtlenvdsc;
14000 defenv[1] = &fildevdsc;
14002 /* Some reasonable defaults that are not CRTL defaults */
14003 set_feature_default("DECC$EFS_CASE_PRESERVE", 1);
14004 set_feature_default("DECC$ARGV_PARSE_STYLE", 1); /* Requires extended parse. */
14005 set_feature_default("DECC$EFS_CHARSET", 1);
14007 /* hacks to see if known bugs are still present for testing */
14009 /* PCP mode requires creating /dev/null special device file */
14010 decc_bug_devnull = 0;
14011 status = simple_trnlnm("DECC_BUG_DEVNULL", val_str, sizeof(val_str));
14013 val_str[0] = _toupper(val_str[0]);
14014 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14015 decc_bug_devnull = 1;
14017 decc_bug_devnull = 0;
14020 s = decc$feature_get_index("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION");
14022 decc_disable_to_vms_logname_translation = decc$feature_get_value(s, 1);
14023 if (decc_disable_to_vms_logname_translation < 0)
14024 decc_disable_to_vms_logname_translation = 0;
14027 s = decc$feature_get_index("DECC$EFS_CASE_PRESERVE");
14029 decc_efs_case_preserve = decc$feature_get_value(s, 1);
14030 if (decc_efs_case_preserve < 0)
14031 decc_efs_case_preserve = 0;
14034 s = decc$feature_get_index("DECC$EFS_CHARSET");
14035 decc_efs_charset_index = s;
14037 decc_efs_charset = decc$feature_get_value(s, 1);
14038 if (decc_efs_charset < 0)
14039 decc_efs_charset = 0;
14042 s = decc$feature_get_index("DECC$FILENAME_UNIX_REPORT");
14044 decc_filename_unix_report = decc$feature_get_value(s, 1);
14045 if (decc_filename_unix_report > 0) {
14046 decc_filename_unix_report = 1;
14047 vms_posix_exit = 1;
14050 decc_filename_unix_report = 0;
14053 s = decc$feature_get_index("DECC$FILENAME_UNIX_ONLY");
14055 decc_filename_unix_only = decc$feature_get_value(s, 1);
14056 if (decc_filename_unix_only > 0) {
14057 decc_filename_unix_only = 1;
14060 decc_filename_unix_only = 0;
14064 s = decc$feature_get_index("DECC$FILENAME_UNIX_NO_VERSION");
14066 decc_filename_unix_no_version = decc$feature_get_value(s, 1);
14067 if (decc_filename_unix_no_version < 0)
14068 decc_filename_unix_no_version = 0;
14071 s = decc$feature_get_index("DECC$READDIR_DROPDOTNOTYPE");
14073 decc_readdir_dropdotnotype = decc$feature_get_value(s, 1);
14074 if (decc_readdir_dropdotnotype < 0)
14075 decc_readdir_dropdotnotype = 0;
14078 #if __CRTL_VER >= 80200000
14079 s = decc$feature_get_index("DECC$POSIX_COMPLIANT_PATHNAMES");
14081 decc_posix_compliant_pathnames = decc$feature_get_value(s, 1);
14082 if (decc_posix_compliant_pathnames < 0)
14083 decc_posix_compliant_pathnames = 0;
14084 if (decc_posix_compliant_pathnames > 4)
14085 decc_posix_compliant_pathnames = 0;
14090 #if defined(JPI$_CASE_LOOKUP_PERM) && defined(PPROP$K_CASE_BLIND)
14092 /* Report true case tolerance */
14093 /*----------------------------*/
14094 status = lib$getjpi(&jpicode1, 0, 0, &case_perm, 0, 0);
14095 if (!$VMS_STATUS_SUCCESS(status))
14096 case_perm = PPROP$K_CASE_BLIND;
14097 status = lib$getjpi(&jpicode2, 0, 0, &case_image, 0, 0);
14098 if (!$VMS_STATUS_SUCCESS(status))
14099 case_image = PPROP$K_CASE_BLIND;
14100 if ((case_perm == PPROP$K_CASE_SENSITIVE) ||
14101 (case_image == PPROP$K_CASE_SENSITIVE))
14102 vms_process_case_tolerant = 0;
14106 /* USE POSIX/DCL Exit codes - Recommended, but needs to default to */
14107 /* for strict backward compatibility */
14108 status = simple_trnlnm("PERL_VMS_POSIX_EXIT", val_str, sizeof(val_str));
14110 val_str[0] = _toupper(val_str[0]);
14111 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14112 vms_posix_exit = 1;
14114 vms_posix_exit = 0;
14118 /* Use 32-bit pointers because that's what the image activator
14119 * assumes for the LIB$INITIALZE psect.
14121 #if __INITIAL_POINTER_SIZE
14122 #pragma pointer_size save
14123 #pragma pointer_size 32
14126 /* Create a reference to the LIB$INITIALIZE function. */
14127 extern void LIB$INITIALIZE(void);
14128 extern void (*vmsperl_unused_global_1)(void) = LIB$INITIALIZE;
14130 /* Create an array of pointers to the init functions in the special
14131 * LIB$INITIALIZE section. In our case, the array only has one entry.
14133 #pragma extern_model save
14134 #pragma extern_model strict_refdef "LIB$INITIALIZE" nopic,gbl,nowrt,noshr,long
14135 extern void (* const vmsperl_unused_global_2[])() =
14137 vmsperl_set_features,
14139 #pragma extern_model restore
14141 #if __INITIAL_POINTER_SIZE
14142 #pragma pointer_size restore
14149 #endif /* defined(__DECC) || defined(__DECCXX) */