3 * VMS-specific routines for perl5
6 * August 2005 Convert VMS status code to UNIX status codes
7 * August 2000 tweaks to vms_image_init, my_flush, my_fwrite, cando_by_name,
8 * and Perl_cando by Craig Berry
9 * 29-Aug-2000 Charles Lane's piping improvements rolled in
10 * 20-Aug-1999 revisions by Charles Bailey bailey@newman.upenn.edu
19 #include <climsgdef.h>
30 #include <libclidef.h>
32 #include <lib$routines.h>
35 #if __CRTL_VER >= 70301000 && !defined(__VAX)
45 #include <str$routines.h>
52 #if __CRTL_VER >= 70000000 /* FIXME to earliest version */
54 #define NO_EFN EFN$C_ENF
59 #if __CRTL_VER < 70301000 && __CRTL_VER >= 70300000
60 int decc$feature_get_index(const char *name);
61 char* decc$feature_get_name(int index);
62 int decc$feature_get_value(int index, int mode);
63 int decc$feature_set_value(int index, int mode, int value);
68 #pragma member_alignment save
69 #pragma nomember_alignment longword
74 unsigned short * retadr;
76 #pragma member_alignment restore
78 /* More specific prototype than in starlet_c.h makes programming errors
86 const struct dsc$descriptor_s * devnam,
87 const struct item_list_3 * itmlst,
89 void * (astadr)(unsigned long),
94 #ifdef lib$find_image_symbol
95 #undef lib$find_image_symbol
96 int lib$find_image_symbol
97 (const struct dsc$descriptor_s * imgname,
98 const struct dsc$descriptor_s * symname,
100 const struct dsc$descriptor_s * defspec,
105 #if __CRTL_VER >= 70300000 && !defined(__VAX)
107 static int set_feature_default(const char *name, int value)
112 index = decc$feature_get_index(name);
114 status = decc$feature_set_value(index, 1, value);
115 if (index == -1 || (status == -1)) {
119 status = decc$feature_get_value(index, 1);
120 if (status != value) {
128 /* Older versions of ssdef.h don't have these */
129 #ifndef SS$_INVFILFOROP
130 # define SS$_INVFILFOROP 3930
132 #ifndef SS$_NOSUCHOBJECT
133 # define SS$_NOSUCHOBJECT 2696
136 /* We implement I/O here, so we will be mixing PerlIO and stdio calls. */
137 #define PERLIO_NOT_STDIO 0
139 /* Don't replace system definitions of vfork, getenv, lstat, and stat,
140 * code below needs to get to the underlying CRTL routines. */
141 #define DONT_MASK_RTL_CALLS
145 /* Anticipating future expansion in lexical warnings . . . */
146 #ifndef WARN_INTERNAL
147 # define WARN_INTERNAL WARN_MISC
150 #ifdef VMS_LONGNAME_SUPPORT
151 #include <libfildef.h>
154 #if defined(__VMS_VER) && __VMS_VER >= 70000000 && __DECC_VER >= 50200000
155 # define RTL_USES_UTC 1
158 /* Routine to create a decterm for use with the Perl debugger */
159 /* No headers, this information was found in the Programming Concepts Manual */
161 static int (*decw_term_port)
162 (const struct dsc$descriptor_s * display,
163 const struct dsc$descriptor_s * setup_file,
164 const struct dsc$descriptor_s * customization,
165 struct dsc$descriptor_s * result_device_name,
166 unsigned short * result_device_name_length,
169 void * char_change_buffer) = 0;
171 /* gcc's header files don't #define direct access macros
172 * corresponding to VAXC's variant structs */
174 # define uic$v_format uic$r_uic_form.uic$v_format
175 # define uic$v_group uic$r_uic_form.uic$v_group
176 # define uic$v_member uic$r_uic_form.uic$v_member
177 # define prv$v_bypass prv$r_prvdef_bits0.prv$v_bypass
178 # define prv$v_grpprv prv$r_prvdef_bits0.prv$v_grpprv
179 # define prv$v_readall prv$r_prvdef_bits0.prv$v_readall
180 # define prv$v_sysprv prv$r_prvdef_bits0.prv$v_sysprv
183 #if defined(NEED_AN_H_ERRNO)
188 #pragma message disable pragma
189 #pragma member_alignment save
190 #pragma nomember_alignment longword
192 #pragma message disable misalgndmem
195 unsigned short int buflen;
196 unsigned short int itmcode;
198 unsigned short int *retlen;
201 struct filescan_itmlst_2 {
202 unsigned short length;
203 unsigned short itmcode;
208 unsigned short length;
213 #pragma message restore
214 #pragma member_alignment restore
217 #define do_fileify_dirspec(a,b,c,d) mp_do_fileify_dirspec(aTHX_ a,b,c,d)
218 #define do_pathify_dirspec(a,b,c,d) mp_do_pathify_dirspec(aTHX_ a,b,c,d)
219 #define do_tovmsspec(a,b,c,d) mp_do_tovmsspec(aTHX_ a,b,c,0,d)
220 #define do_tovmspath(a,b,c,d) mp_do_tovmspath(aTHX_ a,b,c,d)
221 #define do_rmsexpand(a,b,c,d,e,f,g) mp_do_rmsexpand(aTHX_ a,b,c,d,e,f,g)
222 #define do_vms_realpath(a,b,c) mp_do_vms_realpath(aTHX_ a,b,c)
223 #define do_tounixspec(a,b,c,d) mp_do_tounixspec(aTHX_ a,b,c,d)
224 #define do_tounixpath(a,b,c,d) mp_do_tounixpath(aTHX_ a,b,c,d)
225 #define do_vms_case_tolerant(a) mp_do_vms_case_tolerant(a)
226 #define expand_wild_cards(a,b,c,d) mp_expand_wild_cards(aTHX_ a,b,c,d)
227 #define getredirection(a,b) mp_getredirection(aTHX_ a,b)
229 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int *);
230 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int *);
231 static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *);
232 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int *);
234 /* see system service docs for $TRNLNM -- NOT the same as LNM$_MAX_INDEX */
235 #define PERL_LNM_MAX_ALLOWED_INDEX 127
237 /* OpenVMS User's Guide says at least 9 iterative translations will be performed,
238 * depending on the facility. SHOW LOGICAL does 10, so we'll imitate that for
241 #define PERL_LNM_MAX_ITER 10
243 /* New values with 7.3-2*/ /* why does max DCL have 4 byte subtracted from it? */
244 #if __CRTL_VER >= 70302000 && !defined(__VAX)
245 #define MAX_DCL_SYMBOL (8192)
246 #define MAX_DCL_LINE_LENGTH (4096 - 4)
248 #define MAX_DCL_SYMBOL (1024)
249 #define MAX_DCL_LINE_LENGTH (1024 - 4)
252 static char *__mystrtolower(char *str)
254 if (str) for (; *str; ++str) *str= tolower(*str);
258 static struct dsc$descriptor_s fildevdsc =
259 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "LNM$FILE_DEV" };
260 static struct dsc$descriptor_s crtlenvdsc =
261 { 8, DSC$K_DTYPE_T, DSC$K_CLASS_S, "CRTL_ENV" };
262 static struct dsc$descriptor_s *fildev[] = { &fildevdsc, NULL };
263 static struct dsc$descriptor_s *defenv[] = { &fildevdsc, &crtlenvdsc, NULL };
264 static struct dsc$descriptor_s **env_tables = defenv;
265 static bool will_taint = FALSE; /* tainting active, but no PL_curinterp yet */
267 /* True if we shouldn't treat barewords as logicals during directory */
269 static int no_translate_barewords;
272 static int tz_updated = 1;
275 /* DECC Features that may need to affect how Perl interprets
276 * displays filename information
278 static int decc_disable_to_vms_logname_translation = 1;
279 static int decc_disable_posix_root = 1;
280 int decc_efs_case_preserve = 0;
281 static int decc_efs_charset = 0;
282 static int decc_filename_unix_no_version = 0;
283 static int decc_filename_unix_only = 0;
284 int decc_filename_unix_report = 0;
285 int decc_posix_compliant_pathnames = 0;
286 int decc_readdir_dropdotnotype = 0;
287 static int vms_process_case_tolerant = 1;
288 int vms_vtf7_filenames = 0;
289 int gnv_unix_shell = 0;
291 /* bug workarounds if needed */
292 int decc_bug_readdir_efs1 = 0;
293 int decc_bug_devnull = 1;
294 int decc_bug_fgetname = 0;
295 int decc_dir_barename = 0;
297 static int vms_debug_on_exception = 0;
299 /* Is this a UNIX file specification?
300 * No longer a simple check with EFS file specs
301 * For now, not a full check, but need to
302 * handle POSIX ^UP^ specifications
303 * Fixing to handle ^/ cases would require
304 * changes to many other conversion routines.
307 static int is_unix_filespec(const char *path)
313 if (strncmp(path,"\"^UP^",5) != 0) {
314 pch1 = strchr(path, '/');
319 /* If the user wants UNIX files, "." needs to be treated as in UNIX */
320 if (decc_filename_unix_report || decc_filename_unix_only) {
321 if (strcmp(path,".") == 0)
329 /* This routine converts a UCS-2 character to be VTF-7 encoded.
332 static void ucs2_to_vtf7
334 unsigned long ucs2_char,
337 unsigned char * ucs_ptr;
340 ucs_ptr = (unsigned char *)&ucs2_char;
344 hex = (ucs_ptr[1] >> 4) & 0xf;
346 outspec[2] = hex + '0';
348 outspec[2] = (hex - 9) + 'A';
349 hex = ucs_ptr[1] & 0xF;
351 outspec[3] = hex + '0';
353 outspec[3] = (hex - 9) + 'A';
355 hex = (ucs_ptr[0] >> 4) & 0xf;
357 outspec[4] = hex + '0';
359 outspec[4] = (hex - 9) + 'A';
360 hex = ucs_ptr[1] & 0xF;
362 outspec[5] = hex + '0';
364 outspec[5] = (hex - 9) + 'A';
370 /* This handles the conversion of a UNIX extended character set to a ^
371 * escaped VMS character.
372 * in a UNIX file specification.
374 * The output count variable contains the number of characters added
375 * to the output string.
377 * The return value is the number of characters read from the input string
379 static int copy_expand_unix_filename_escape
380 (char *outspec, const char *inspec, int *output_cnt, const int * utf8_fl)
388 utf8_flag = *utf8_fl;
392 if (*inspec >= 0x80) {
393 if (utf8_fl && vms_vtf7_filenames) {
394 unsigned long ucs_char;
398 if ((*inspec & 0xE0) == 0xC0) {
400 ucs_char = ((inspec[0] & 0x1F) << 6) + (inspec[1] & 0x3f);
401 if (ucs_char >= 0x80) {
402 ucs2_to_vtf7(outspec, ucs_char, output_cnt);
405 } else if ((*inspec & 0xF0) == 0xE0) {
407 ucs_char = ((inspec[0] & 0xF) << 12) +
408 ((inspec[1] & 0x3f) << 6) +
410 if (ucs_char >= 0x800) {
411 ucs2_to_vtf7(outspec, ucs_char, output_cnt);
415 #if 0 /* I do not see longer sequences supported by OpenVMS */
416 /* Maybe some one can fix this later */
417 } else if ((*inspec & 0xF8) == 0xF0) {
420 } else if ((*inspec & 0xFC) == 0xF8) {
423 } else if ((*inspec & 0xFE) == 0xFC) {
430 /* High bit set, but not a Unicode character! */
432 /* Non printing DECMCS or ISO Latin-1 character? */
433 if (*inspec <= 0x9F) {
437 hex = (*inspec >> 4) & 0xF;
439 outspec[1] = hex + '0';
441 outspec[1] = (hex - 9) + 'A';
445 outspec[2] = hex + '0';
447 outspec[2] = (hex - 9) + 'A';
451 } else if (*inspec == 0xA0) {
457 } else if (*inspec == 0xFF) {
469 /* Is this a macro that needs to be passed through?
470 * Macros start with $( and an alpha character, followed
471 * by a string of alpha numeric characters ending with a )
472 * If this does not match, then encode it as ODS-5.
474 if ((inspec[0] == '$') && (inspec[1] == '(')) {
477 if (isalnum(inspec[2]) || (inspec[2] == '.') || (inspec[2] == '_')) {
479 outspec[0] = inspec[0];
480 outspec[1] = inspec[1];
481 outspec[2] = inspec[2];
483 while(isalnum(inspec[tcnt]) ||
484 (inspec[2] == '.') || (inspec[2] == '_')) {
485 outspec[tcnt] = inspec[tcnt];
488 if (inspec[tcnt] == ')') {
489 outspec[tcnt] = inspec[tcnt];
506 if (decc_efs_charset == 0)
532 /* Don't escape again if following character is
533 * already something we escape.
535 if (strchr(".~!#&\'`()+@{},;[]%^=_", *(inspec+1))) {
541 /* But otherwise fall through and escape it. */
543 /* Assume that this is to be escaped */
545 outspec[1] = *inspec;
549 case ' ': /* space */
550 /* Assume that this is to be escaped */
565 /* This handles the expansion of a '^' prefix to the proper character
566 * in a UNIX file specification.
568 * The output count variable contains the number of characters added
569 * to the output string.
571 * The return value is the number of characters read from the input
574 static int copy_expand_vms_filename_escape
575 (char *outspec, const char *inspec, int *output_cnt)
582 if (*inspec == '^') {
585 /* Spaces and non-trailing dots should just be passed through,
586 * but eat the escape character.
593 case '_': /* space */
599 /* Hmm. Better leave the escape escaped. */
605 case 'U': /* Unicode - FIX-ME this is wrong. */
608 scnt = strspn(inspec, "0123456789ABCDEFabcdef");
611 scnt = sscanf(inspec, "%2x%2x", &c1, &c2);
612 outspec[0] == c1 & 0xff;
613 outspec[1] == c2 & 0xff;
620 /* Error - do best we can to continue */
630 scnt = strspn(inspec, "0123456789ABCDEFabcdef");
634 scnt = sscanf(inspec, "%2x", &c1);
635 outspec[0] = c1 & 0xff;
659 (const struct dsc$descriptor_s * srcstr,
660 struct filescan_itmlst_2 * valuelist,
661 unsigned long * fldflags,
662 struct dsc$descriptor_s *auxout,
663 unsigned short * retlen);
666 /* vms_split_path - Verify that the input file specification is a
667 * VMS format file specification, and provide pointers to the components of
668 * it. With EFS format filenames, this is virtually the only way to
669 * parse a VMS path specification into components.
671 * If the sum of the components do not add up to the length of the
672 * string, then the passed file specification is probably a UNIX style
675 static int vms_split_path
690 struct dsc$descriptor path_desc;
694 struct filescan_itmlst_2 item_list[9];
695 const int filespec = 0;
696 const int nodespec = 1;
697 const int devspec = 2;
698 const int rootspec = 3;
699 const int dirspec = 4;
700 const int namespec = 5;
701 const int typespec = 6;
702 const int verspec = 7;
704 /* Assume the worst for an easy exit */
719 path_desc.dsc$a_pointer = (char *)path; /* cast ok */
720 path_desc.dsc$w_length = strlen(path);
721 path_desc.dsc$b_dtype = DSC$K_DTYPE_T;
722 path_desc.dsc$b_class = DSC$K_CLASS_S;
724 /* Get the total length, if it is shorter than the string passed
725 * then this was probably not a VMS formatted file specification
727 item_list[filespec].itmcode = FSCN$_FILESPEC;
728 item_list[filespec].length = 0;
729 item_list[filespec].component = NULL;
731 /* If the node is present, then it gets considered as part of the
732 * volume name to hopefully make things simple.
734 item_list[nodespec].itmcode = FSCN$_NODE;
735 item_list[nodespec].length = 0;
736 item_list[nodespec].component = NULL;
738 item_list[devspec].itmcode = FSCN$_DEVICE;
739 item_list[devspec].length = 0;
740 item_list[devspec].component = NULL;
742 /* root is a special case, adding it to either the directory or
743 * the device components will probalby complicate things for the
744 * callers of this routine, so leave it separate.
746 item_list[rootspec].itmcode = FSCN$_ROOT;
747 item_list[rootspec].length = 0;
748 item_list[rootspec].component = NULL;
750 item_list[dirspec].itmcode = FSCN$_DIRECTORY;
751 item_list[dirspec].length = 0;
752 item_list[dirspec].component = NULL;
754 item_list[namespec].itmcode = FSCN$_NAME;
755 item_list[namespec].length = 0;
756 item_list[namespec].component = NULL;
758 item_list[typespec].itmcode = FSCN$_TYPE;
759 item_list[typespec].length = 0;
760 item_list[typespec].component = NULL;
762 item_list[verspec].itmcode = FSCN$_VERSION;
763 item_list[verspec].length = 0;
764 item_list[verspec].component = NULL;
766 item_list[8].itmcode = 0;
767 item_list[8].length = 0;
768 item_list[8].component = NULL;
770 status = sys$filescan
771 ((const struct dsc$descriptor_s *)&path_desc, item_list,
773 _ckvmssts_noperl(status); /* All failure status values indicate a coding error */
775 /* If we parsed it successfully these two lengths should be the same */
776 if (path_desc.dsc$w_length != item_list[filespec].length)
779 /* If we got here, then it is a VMS file specification */
782 /* set the volume name */
783 if (item_list[nodespec].length > 0) {
784 *volume = item_list[nodespec].component;
785 *vol_len = item_list[nodespec].length + item_list[devspec].length;
788 *volume = item_list[devspec].component;
789 *vol_len = item_list[devspec].length;
792 *root = item_list[rootspec].component;
793 *root_len = item_list[rootspec].length;
795 *dir = item_list[dirspec].component;
796 *dir_len = item_list[dirspec].length;
798 /* Now fun with versions and EFS file specifications
799 * The parser can not tell the difference when a "." is a version
800 * delimiter or a part of the file specification.
802 if ((decc_efs_charset) &&
803 (item_list[verspec].length > 0) &&
804 (item_list[verspec].component[0] == '.')) {
805 *name = item_list[namespec].component;
806 *name_len = item_list[namespec].length + item_list[typespec].length;
807 *ext = item_list[verspec].component;
808 *ext_len = item_list[verspec].length;
813 *name = item_list[namespec].component;
814 *name_len = item_list[namespec].length;
815 *ext = item_list[typespec].component;
816 *ext_len = item_list[typespec].length;
817 *version = item_list[verspec].component;
818 *ver_len = item_list[verspec].length;
825 * Routine to retrieve the maximum equivalence index for an input
826 * logical name. Some calls to this routine have no knowledge if
827 * the variable is a logical or not. So on error we return a max
830 /*{{{int my_maxidx(const char *lnm) */
832 my_maxidx(const char *lnm)
836 int attr = LNM$M_CASE_BLIND;
837 struct dsc$descriptor lnmdsc;
838 struct itmlst_3 itlst[2] = {{sizeof(midx), LNM$_MAX_INDEX, &midx, 0},
841 lnmdsc.dsc$w_length = strlen(lnm);
842 lnmdsc.dsc$b_dtype = DSC$K_DTYPE_T;
843 lnmdsc.dsc$b_class = DSC$K_CLASS_S;
844 lnmdsc.dsc$a_pointer = (char *) lnm; /* Cast ok for read only parameter */
846 status = sys$trnlnm(&attr, &fildevdsc, &lnmdsc, 0, itlst);
847 if ((status & 1) == 0)
854 /*{{{int vmstrnenv(const char *lnm, char *eqv, unsigned long int idx, struct dsc$descriptor_s **tabvec, unsigned long int flags) */
856 Perl_vmstrnenv(const char *lnm, char *eqv, unsigned long int idx,
857 struct dsc$descriptor_s **tabvec, unsigned long int flags)
860 char uplnm[LNM$C_NAMLENGTH+1], *cp2;
861 unsigned short int eqvlen, curtab, ivlnm = 0, ivsym = 0, ivenv = 0, secure;
862 unsigned long int retsts, attr = LNM$M_CASE_BLIND;
864 unsigned char acmode;
865 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
866 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
867 struct itmlst_3 lnmlst[3] = {{sizeof idx, LNM$_INDEX, &idx, 0},
868 {LNM$C_NAMLENGTH, LNM$_STRING, eqv, &eqvlen},
870 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
871 #if defined(PERL_IMPLICIT_CONTEXT)
874 aTHX = PERL_GET_INTERP;
880 if (!lnm || !eqv || ((idx != 0) && ((idx-1) > PERL_LNM_MAX_ALLOWED_INDEX))) {
881 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return 0;
883 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
884 *cp2 = _toupper(*cp1);
885 if (cp1 - lnm > LNM$C_NAMLENGTH) {
886 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
890 lnmdsc.dsc$w_length = cp1 - lnm;
891 lnmdsc.dsc$a_pointer = uplnm;
892 uplnm[lnmdsc.dsc$w_length] = '\0';
893 secure = flags & PERL__TRNENV_SECURE;
894 acmode = secure ? PSL$C_EXEC : PSL$C_USER;
895 if (!tabvec || !*tabvec) tabvec = env_tables;
897 for (curtab = 0; tabvec[curtab]; curtab++) {
898 if (!str$case_blind_compare(tabvec[curtab],&crtlenv)) {
899 if (!ivenv && !secure) {
904 Perl_warn(aTHX_ "Can't read CRTL environ\n");
907 retsts = SS$_NOLOGNAM;
908 for (i = 0; environ[i]; i++) {
909 if ((eq = strchr(environ[i],'=')) &&
910 lnmdsc.dsc$w_length == (eq - environ[i]) &&
911 !strncmp(environ[i],uplnm,eq - environ[i])) {
913 for (eqvlen = 0; eq[eqvlen]; eqvlen++) eqv[eqvlen] = eq[eqvlen];
914 if (!eqvlen) continue;
919 if (retsts != SS$_NOLOGNAM) break;
922 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
923 !str$case_blind_compare(&tmpdsc,&clisym)) {
924 if (!ivsym && !secure) {
925 unsigned short int deflen = LNM$C_NAMLENGTH;
926 struct dsc$descriptor_d eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
927 /* dynamic dsc to accomodate possible long value */
928 _ckvmssts(lib$sget1_dd(&deflen,&eqvdsc));
929 retsts = lib$get_symbol(&lnmdsc,&eqvdsc,&eqvlen,0);
931 if (eqvlen > MAX_DCL_SYMBOL) {
932 set_errno(EVMSERR); set_vaxc_errno(LIB$_STRTRU);
933 eqvlen = MAX_DCL_SYMBOL;
934 /* Special hack--we might be called before the interpreter's */
935 /* fully initialized, in which case either thr or PL_curcop */
936 /* might be bogus. We have to check, since ckWARN needs them */
937 /* both to be valid if running threaded */
938 if (ckWARN(WARN_MISC)) {
939 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of CLI symbol \"%s\" too long",lnm);
942 strncpy(eqv,eqvdsc.dsc$a_pointer,eqvlen);
944 _ckvmssts(lib$sfree1_dd(&eqvdsc));
945 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
946 if (retsts == LIB$_NOSUCHSYM) continue;
951 if ( (idx == 0) && (flags & PERL__TRNENV_JOIN_SEARCHLIST) ) {
952 midx = my_maxidx(lnm);
953 for (idx = 0, cp2 = eqv; idx <= midx; idx++) {
954 lnmlst[1].bufadr = cp2;
956 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
957 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; break; }
958 if (retsts == SS$_NOLOGNAM) break;
959 /* PPFs have a prefix */
962 *((int *)uplnm) == *((int *)"SYS$") &&
964 eqvlen >= 4 && eqv[0] == 0x1b && eqv[1] == 0x00 &&
965 ( (uplnm[4] == 'O' && !strcmp(uplnm,"SYS$OUTPUT")) ||
966 (uplnm[4] == 'I' && !strcmp(uplnm,"SYS$INPUT")) ||
967 (uplnm[4] == 'E' && !strcmp(uplnm,"SYS$ERROR")) ||
968 (uplnm[4] == 'C' && !strcmp(uplnm,"SYS$COMMAND")) ) ) {
969 memmove(eqv,eqv+4,eqvlen-4);
975 if ((retsts == SS$_IVLOGNAM) ||
976 (retsts == SS$_NOLOGNAM)) { continue; }
979 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
980 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
981 if (retsts == SS$_NOLOGNAM) continue;
984 eqvlen = strlen(eqv);
988 if (retsts & 1) { eqv[eqvlen] = '\0'; return eqvlen; }
989 else if (retsts == LIB$_NOSUCHSYM || retsts == LIB$_INVSYMNAM ||
990 retsts == SS$_IVLOGNAM || retsts == SS$_IVLOGTAB ||
991 retsts == SS$_NOLOGNAM) {
992 set_errno(EINVAL); set_vaxc_errno(retsts);
994 else _ckvmssts(retsts);
996 } /* end of vmstrnenv */
999 /*{{{ int my_trnlnm(const char *lnm, char *eqv, unsigned long int idx)*/
1000 /* Define as a function so we can access statics. */
1001 int Perl_my_trnlnm(pTHX_ const char *lnm, char *eqv, unsigned long int idx)
1003 return vmstrnenv(lnm,eqv,idx,fildev,
1004 #ifdef SECURE_INTERNAL_GETENV
1005 (PL_curinterp ? PL_tainting : will_taint) ? PERL__TRNENV_SECURE : 0
1014 * Note: Uses Perl temp to store result so char * can be returned to
1015 * caller; this pointer will be invalidated at next Perl statement
1017 * We define this as a function rather than a macro in terms of my_getenv_len()
1018 * so that it'll work when PL_curinterp is undefined (and we therefore can't
1021 /*{{{ char *my_getenv(const char *lnm, bool sys)*/
1023 Perl_my_getenv(pTHX_ const char *lnm, bool sys)
1026 static char *__my_getenv_eqv = NULL;
1027 char uplnm[LNM$C_NAMLENGTH+1], *cp2, *eqv;
1028 unsigned long int idx = 0;
1029 int trnsuccess, success, secure, saverr, savvmserr;
1033 midx = my_maxidx(lnm) + 1;
1035 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
1036 /* Set up a temporary buffer for the return value; Perl will
1037 * clean it up at the next statement transition */
1038 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
1039 if (!tmpsv) return NULL;
1043 /* Assume no interpreter ==> single thread */
1044 if (__my_getenv_eqv != NULL) {
1045 Renew(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
1048 Newx(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
1050 eqv = __my_getenv_eqv;
1053 for (cp1 = lnm, cp2 = eqv; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
1054 if (cp1 - lnm == 7 && !strncmp(eqv,"DEFAULT",7)) {
1056 getcwd(eqv,LNM$C_NAMLENGTH);
1060 /* Get rid of "000000/ in rooted filespecs */
1063 zeros = strstr(eqv, "/000000/");
1064 if (zeros != NULL) {
1066 mlen = len - (zeros - eqv) - 7;
1067 memmove(zeros, &zeros[7], mlen);
1075 /* Impose security constraints only if tainting */
1077 /* Impose security constraints only if tainting */
1078 secure = PL_curinterp ? PL_tainting : will_taint;
1079 saverr = errno; savvmserr = vaxc$errno;
1086 #ifdef SECURE_INTERNAL_GETENV
1087 secure ? PERL__TRNENV_SECURE : 0
1093 /* For the getenv interface we combine all the equivalence names
1094 * of a search list logical into one value to acquire a maximum
1095 * value length of 255*128 (assuming %ENV is using logicals).
1097 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
1099 /* If the name contains a semicolon-delimited index, parse it
1100 * off and make sure we only retrieve the equivalence name for
1102 if ((cp2 = strchr(lnm,';')) != NULL) {
1104 uplnm[cp2-lnm] = '\0';
1105 idx = strtoul(cp2+1,NULL,0);
1107 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
1110 success = vmstrnenv(lnm,eqv,idx,secure ? fildev : NULL,flags);
1112 /* Discard NOLOGNAM on internal calls since we're often looking
1113 * for an optional name, and this "error" often shows up as the
1114 * (bogus) exit status for a die() call later on. */
1115 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
1116 return success ? eqv : Nullch;
1119 } /* end of my_getenv() */
1123 /*{{{ SV *my_getenv_len(const char *lnm, bool sys)*/
1125 Perl_my_getenv_len(pTHX_ const char *lnm, unsigned long *len, bool sys)
1129 unsigned long idx = 0;
1131 static char *__my_getenv_len_eqv = NULL;
1132 int secure, saverr, savvmserr;
1135 midx = my_maxidx(lnm) + 1;
1137 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
1138 /* Set up a temporary buffer for the return value; Perl will
1139 * clean it up at the next statement transition */
1140 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
1141 if (!tmpsv) return NULL;
1145 /* Assume no interpreter ==> single thread */
1146 if (__my_getenv_len_eqv != NULL) {
1147 Renew(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
1150 Newx(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
1152 buf = __my_getenv_len_eqv;
1155 for (cp1 = lnm, cp2 = buf; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
1156 if (cp1 - lnm == 7 && !strncmp(buf,"DEFAULT",7)) {
1159 getcwd(buf,LNM$C_NAMLENGTH);
1162 /* Get rid of "000000/ in rooted filespecs */
1164 zeros = strstr(buf, "/000000/");
1165 if (zeros != NULL) {
1167 mlen = *len - (zeros - buf) - 7;
1168 memmove(zeros, &zeros[7], mlen);
1177 /* Impose security constraints only if tainting */
1178 secure = PL_curinterp ? PL_tainting : will_taint;
1179 saverr = errno; savvmserr = vaxc$errno;
1186 #ifdef SECURE_INTERNAL_GETENV
1187 secure ? PERL__TRNENV_SECURE : 0
1193 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
1195 if ((cp2 = strchr(lnm,';')) != NULL) {
1197 buf[cp2-lnm] = '\0';
1198 idx = strtoul(cp2+1,NULL,0);
1200 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
1203 *len = vmstrnenv(lnm,buf,idx,secure ? fildev : NULL,flags);
1205 /* Get rid of "000000/ in rooted filespecs */
1208 zeros = strstr(buf, "/000000/");
1209 if (zeros != NULL) {
1211 mlen = *len - (zeros - buf) - 7;
1212 memmove(zeros, &zeros[7], mlen);
1218 /* Discard NOLOGNAM on internal calls since we're often looking
1219 * for an optional name, and this "error" often shows up as the
1220 * (bogus) exit status for a die() call later on. */
1221 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
1222 return *len ? buf : Nullch;
1225 } /* end of my_getenv_len() */
1228 static void create_mbx(pTHX_ unsigned short int *, struct dsc$descriptor_s *);
1230 static void riseandshine(unsigned long int dummy) { sys$wake(0,0); }
1232 /*{{{ void prime_env_iter() */
1234 prime_env_iter(void)
1235 /* Fill the %ENV associative array with all logical names we can
1236 * find, in preparation for iterating over it.
1239 static int primed = 0;
1240 HV *seenhv = NULL, *envhv;
1242 char cmd[LNM$C_NAMLENGTH+24], mbxnam[LNM$C_NAMLENGTH], *buf = Nullch;
1243 unsigned short int chan;
1244 #ifndef CLI$M_TRUSTED
1245 # define CLI$M_TRUSTED 0x40 /* Missing from VAXC headers */
1247 unsigned long int defflags = CLI$M_NOWAIT | CLI$M_NOKEYPAD | CLI$M_TRUSTED;
1248 unsigned long int mbxbufsiz, flags, retsts, subpid = 0, substs = 0, wakect = 0;
1250 bool have_sym = FALSE, have_lnm = FALSE;
1251 struct dsc$descriptor_s tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1252 $DESCRIPTOR(cmddsc,cmd); $DESCRIPTOR(nldsc,"_NLA0:");
1253 $DESCRIPTOR(clidsc,"DCL"); $DESCRIPTOR(clitabdsc,"DCLTABLES");
1254 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
1255 $DESCRIPTOR(local,"_LOCAL"); $DESCRIPTOR(mbxdsc,mbxnam);
1256 #if defined(PERL_IMPLICIT_CONTEXT)
1259 #if defined(USE_ITHREADS)
1260 static perl_mutex primenv_mutex;
1261 MUTEX_INIT(&primenv_mutex);
1264 #if defined(PERL_IMPLICIT_CONTEXT)
1265 /* We jump through these hoops because we can be called at */
1266 /* platform-specific initialization time, which is before anything is */
1267 /* set up--we can't even do a plain dTHX since that relies on the */
1268 /* interpreter structure to be initialized */
1270 aTHX = PERL_GET_INTERP;
1276 if (primed || !PL_envgv) return;
1277 MUTEX_LOCK(&primenv_mutex);
1278 if (primed) { MUTEX_UNLOCK(&primenv_mutex); return; }
1279 envhv = GvHVn(PL_envgv);
1280 /* Perform a dummy fetch as an lval to insure that the hash table is
1281 * set up. Otherwise, the hv_store() will turn into a nullop. */
1282 (void) hv_fetch(envhv,"DEFAULT",7,TRUE);
1284 for (i = 0; env_tables[i]; i++) {
1285 if (!have_sym && (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
1286 !str$case_blind_compare(&tmpdsc,&clisym)) have_sym = 1;
1287 if (!have_lnm && str$case_blind_compare(env_tables[i],&crtlenv)) have_lnm = 1;
1289 if (have_sym || have_lnm) {
1290 long int syiitm = SYI$_MAXBUF, dviitm = DVI$_DEVNAM;
1291 _ckvmssts(lib$getsyi(&syiitm, &mbxbufsiz, 0, 0, 0, 0));
1292 _ckvmssts(sys$crembx(0,&chan,mbxbufsiz,mbxbufsiz,0xff0f,0,0));
1293 _ckvmssts(lib$getdvi(&dviitm, &chan, NULL, NULL, &mbxdsc, &mbxdsc.dsc$w_length));
1296 for (i--; i >= 0; i--) {
1297 if (!str$case_blind_compare(env_tables[i],&crtlenv)) {
1300 for (j = 0; environ[j]; j++) {
1301 if (!(start = strchr(environ[j],'='))) {
1302 if (ckWARN(WARN_INTERNAL))
1303 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed CRTL environ value \"%s\"\n",environ[j]);
1307 sv = newSVpv(start,0);
1309 (void) hv_store(envhv,environ[j],start - environ[j] - 1,sv,0);
1314 else if ((tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
1315 !str$case_blind_compare(&tmpdsc,&clisym)) {
1316 strcpy(cmd,"Show Symbol/Global *");
1317 cmddsc.dsc$w_length = 20;
1318 if (env_tables[i]->dsc$w_length == 12 &&
1319 (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer + 6) &&
1320 !str$case_blind_compare(&tmpdsc,&local)) strcpy(cmd+12,"Local *");
1321 flags = defflags | CLI$M_NOLOGNAM;
1324 strcpy(cmd,"Show Logical *");
1325 if (str$case_blind_compare(env_tables[i],&fildevdsc)) {
1326 strcat(cmd," /Table=");
1327 strncat(cmd,env_tables[i]->dsc$a_pointer,env_tables[i]->dsc$w_length);
1328 cmddsc.dsc$w_length = strlen(cmd);
1330 else cmddsc.dsc$w_length = 14; /* N.B. We test this below */
1331 flags = defflags | CLI$M_NOCLISYM;
1334 /* Create a new subprocess to execute each command, to exclude the
1335 * remote possibility that someone could subvert a mbx or file used
1336 * to write multiple commands to a single subprocess.
1339 retsts = lib$spawn(&cmddsc,&nldsc,&mbxdsc,&flags,0,&subpid,&substs,
1340 0,&riseandshine,0,0,&clidsc,&clitabdsc);
1341 flags &= ~CLI$M_TRUSTED; /* Just in case we hit a really old version */
1342 defflags &= ~CLI$M_TRUSTED;
1343 } while (retsts == LIB$_INVARG && (flags | CLI$M_TRUSTED));
1345 if (!buf) Newx(buf,mbxbufsiz + 1,char);
1346 if (seenhv) SvREFCNT_dec(seenhv);
1349 char *cp1, *cp2, *key;
1350 unsigned long int sts, iosb[2], retlen, keylen;
1353 sts = sys$qiow(0,chan,IO$_READVBLK,iosb,0,0,buf,mbxbufsiz,0,0,0,0);
1354 if (sts & 1) sts = iosb[0] & 0xffff;
1355 if (sts == SS$_ENDOFFILE) {
1357 while (substs == 0) { sys$hiber(); wakect++;}
1358 if (wakect > 1) sys$wake(0,0); /* Stole someone else's wake */
1363 retlen = iosb[0] >> 16;
1364 if (!retlen) continue; /* blank line */
1366 if (iosb[1] != subpid) {
1368 Perl_croak(aTHX_ "Unknown process %x sent message to prime_env_iter: %s",buf);
1372 if (sts == SS$_BUFFEROVF && ckWARN(WARN_INTERNAL))
1373 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Buffer overflow in prime_env_iter: %s",buf);
1375 for (cp1 = buf; *cp1 && isspace(*cp1); cp1++) ;
1376 if (*cp1 == '(' || /* Logical name table name */
1377 *cp1 == '=' /* Next eqv of searchlist */) continue;
1378 if (*cp1 == '"') cp1++;
1379 for (cp2 = cp1; *cp2 && *cp2 != '"' && *cp2 != ' '; cp2++) ;
1380 key = cp1; keylen = cp2 - cp1;
1381 if (keylen && hv_exists(seenhv,key,keylen)) continue;
1382 while (*cp2 && *cp2 != '=') cp2++;
1383 while (*cp2 && *cp2 == '=') cp2++;
1384 while (*cp2 && *cp2 == ' ') cp2++;
1385 if (*cp2 == '"') { /* String translation; may embed "" */
1386 for (cp1 = buf + retlen; *cp1 != '"'; cp1--) ;
1387 cp2++; cp1--; /* Skip "" surrounding translation */
1389 else { /* Numeric translation */
1390 for (cp1 = cp2; *cp1 && *cp1 != ' '; cp1++) ;
1391 cp1--; /* stop on last non-space char */
1393 if ((!keylen || (cp1 - cp2 < -1)) && ckWARN(WARN_INTERNAL)) {
1394 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed message in prime_env_iter: |%s|",buf);
1397 PERL_HASH(hash,key,keylen);
1399 if (cp1 == cp2 && *cp2 == '.') {
1400 /* A single dot usually means an unprintable character, such as a null
1401 * to indicate a zero-length value. Get the actual value to make sure.
1403 char lnm[LNM$C_NAMLENGTH+1];
1404 char eqv[MAX_DCL_SYMBOL+1];
1406 strncpy(lnm, key, keylen);
1407 trnlen = vmstrnenv(lnm, eqv, 0, fildev, 0);
1408 sv = newSVpvn(eqv, strlen(eqv));
1411 sv = newSVpvn(cp2,cp1 - cp2 + 1);
1415 hv_store(envhv,key,keylen,sv,hash);
1416 hv_store(seenhv,key,keylen,&PL_sv_yes,hash);
1418 if (cmddsc.dsc$w_length == 14) { /* We just read LNM$FILE_DEV */
1419 /* get the PPFs for this process, not the subprocess */
1420 const char *ppfs[] = {"SYS$COMMAND", "SYS$INPUT", "SYS$OUTPUT", "SYS$ERROR", NULL};
1421 char eqv[LNM$C_NAMLENGTH+1];
1423 for (i = 0; ppfs[i]; i++) {
1424 trnlen = vmstrnenv(ppfs[i],eqv,0,fildev,0);
1425 sv = newSVpv(eqv,trnlen);
1427 hv_store(envhv,ppfs[i],strlen(ppfs[i]),sv,0);
1432 if (have_sym || have_lnm) _ckvmssts(sys$dassgn(chan));
1433 if (buf) Safefree(buf);
1434 if (seenhv) SvREFCNT_dec(seenhv);
1435 MUTEX_UNLOCK(&primenv_mutex);
1438 } /* end of prime_env_iter */
1442 /*{{{ int vmssetenv(const char *lnm, const char *eqv)*/
1443 /* Define or delete an element in the same "environment" as
1444 * vmstrnenv(). If an element is to be deleted, it's removed from
1445 * the first place it's found. If it's to be set, it's set in the
1446 * place designated by the first element of the table vector.
1447 * Like setenv() returns 0 for success, non-zero on error.
1450 Perl_vmssetenv(pTHX_ const char *lnm, const char *eqv, struct dsc$descriptor_s **tabvec)
1453 char uplnm[LNM$C_NAMLENGTH], *cp2, *c;
1454 unsigned short int curtab, ivlnm = 0, ivsym = 0, ivenv = 0;
1456 unsigned long int retsts, usermode = PSL$C_USER;
1457 struct itmlst_3 *ile, *ilist;
1458 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,uplnm},
1459 eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
1460 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1461 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
1462 $DESCRIPTOR(local,"_LOCAL");
1465 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1466 return SS$_IVLOGNAM;
1469 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
1470 *cp2 = _toupper(*cp1);
1471 if (cp1 - lnm > LNM$C_NAMLENGTH) {
1472 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1473 return SS$_IVLOGNAM;
1476 lnmdsc.dsc$w_length = cp1 - lnm;
1477 if (!tabvec || !*tabvec) tabvec = env_tables;
1479 if (!eqv) { /* we're deleting n element */
1480 for (curtab = 0; tabvec[curtab]; curtab++) {
1481 if (!ivenv && !str$case_blind_compare(tabvec[curtab],&crtlenv)) {
1483 for (i = 0; environ[i]; i++) { /* If it's an environ elt, reset */
1484 if ((cp1 = strchr(environ[i],'=')) &&
1485 lnmdsc.dsc$w_length == (cp1 - environ[i]) &&
1486 !strncmp(environ[i],lnm,cp1 - environ[i])) {
1488 return setenv(lnm,"",1) ? vaxc$errno : 0;
1491 ivenv = 1; retsts = SS$_NOLOGNAM;
1493 if (ckWARN(WARN_INTERNAL))
1494 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't reset CRTL environ elements (%s)",lnm);
1495 ivenv = 1; retsts = SS$_NOSUCHPGM;
1501 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
1502 !str$case_blind_compare(&tmpdsc,&clisym)) {
1503 unsigned int symtype;
1504 if (tabvec[curtab]->dsc$w_length == 12 &&
1505 (tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer + 6) &&
1506 !str$case_blind_compare(&tmpdsc,&local))
1507 symtype = LIB$K_CLI_LOCAL_SYM;
1508 else symtype = LIB$K_CLI_GLOBAL_SYM;
1509 retsts = lib$delete_symbol(&lnmdsc,&symtype);
1510 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
1511 if (retsts == LIB$_NOSUCHSYM) continue;
1515 retsts = sys$dellnm(tabvec[curtab],&lnmdsc,&usermode); /* try user mode first */
1516 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
1517 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
1518 retsts = lib$delete_logical(&lnmdsc,tabvec[curtab]); /* then supervisor mode */
1519 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
1523 else { /* we're defining a value */
1524 if (!ivenv && !str$case_blind_compare(tabvec[0],&crtlenv)) {
1526 return setenv(lnm,eqv,1) ? vaxc$errno : 0;
1528 if (ckWARN(WARN_INTERNAL))
1529 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't set CRTL environ elements (%s=%s)",lnm,eqv);
1530 retsts = SS$_NOSUCHPGM;
1534 eqvdsc.dsc$a_pointer = (char *) eqv; /* cast ok to readonly parameter */
1535 eqvdsc.dsc$w_length = strlen(eqv);
1536 if ((tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer) &&
1537 !str$case_blind_compare(&tmpdsc,&clisym)) {
1538 unsigned int symtype;
1539 if (tabvec[0]->dsc$w_length == 12 &&
1540 (tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer + 6) &&
1541 !str$case_blind_compare(&tmpdsc,&local))
1542 symtype = LIB$K_CLI_LOCAL_SYM;
1543 else symtype = LIB$K_CLI_GLOBAL_SYM;
1544 retsts = lib$set_symbol(&lnmdsc,&eqvdsc,&symtype);
1547 if (!*eqv) eqvdsc.dsc$w_length = 1;
1548 if (eqvdsc.dsc$w_length > LNM$C_NAMLENGTH) {
1550 nseg = (eqvdsc.dsc$w_length + LNM$C_NAMLENGTH - 1) / LNM$C_NAMLENGTH;
1551 if (nseg > PERL_LNM_MAX_ALLOWED_INDEX + 1) {
1552 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of logical \"%s\" too long. Truncating to %i bytes",
1553 lnm, LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1));
1554 eqvdsc.dsc$w_length = LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1);
1555 nseg = PERL_LNM_MAX_ALLOWED_INDEX + 1;
1558 Newx(ilist,nseg+1,struct itmlst_3);
1561 set_errno(ENOMEM); set_vaxc_errno(SS$_INSFMEM);
1564 memset(ilist, 0, (sizeof(struct itmlst_3) * (nseg+1)));
1566 for (j = 0, c = eqvdsc.dsc$a_pointer; j < nseg; j++, ile++, c += LNM$C_NAMLENGTH) {
1567 ile->itmcode = LNM$_STRING;
1569 if ((j+1) == nseg) {
1570 ile->buflen = strlen(c);
1571 /* in case we are truncating one that's too long */
1572 if (ile->buflen > LNM$C_NAMLENGTH) ile->buflen = LNM$C_NAMLENGTH;
1575 ile->buflen = LNM$C_NAMLENGTH;
1579 retsts = lib$set_logical(&lnmdsc,0,tabvec[0],0,ilist);
1583 retsts = lib$set_logical(&lnmdsc,&eqvdsc,tabvec[0],0,0);
1588 if (!(retsts & 1)) {
1590 case LIB$_AMBSYMDEF: case LIB$_INSCLIMEM:
1591 case SS$_NOLOGTAB: case SS$_TOOMANYLNAM: case SS$_IVLOGTAB:
1592 set_errno(EVMSERR); break;
1593 case LIB$_INVARG: case LIB$_INVSYMNAM: case SS$_IVLOGNAM:
1594 case LIB$_NOSUCHSYM: case SS$_NOLOGNAM:
1595 set_errno(EINVAL); break;
1597 set_errno(EACCES); break;
1602 set_vaxc_errno(retsts);
1603 return (int) retsts || 44; /* retsts should never be 0, but just in case */
1606 /* We reset error values on success because Perl does an hv_fetch()
1607 * before each hv_store(), and if the thing we're setting didn't
1608 * previously exist, we've got a leftover error message. (Of course,
1609 * this fails in the face of
1610 * $foo = $ENV{nonexistent}; $ENV{existent} = 'foo';
1611 * in that the error reported in $! isn't spurious,
1612 * but it's right more often than not.)
1614 set_errno(0); set_vaxc_errno(retsts);
1618 } /* end of vmssetenv() */
1621 /*{{{ void my_setenv(const char *lnm, const char *eqv)*/
1622 /* This has to be a function since there's a prototype for it in proto.h */
1624 Perl_my_setenv(pTHX_ const char *lnm, const char *eqv)
1627 int len = strlen(lnm);
1631 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
1632 if (!strcmp(uplnm,"DEFAULT")) {
1633 if (eqv && *eqv) my_chdir(eqv);
1637 #ifndef RTL_USES_UTC
1638 if (len == 6 || len == 2) {
1641 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
1643 if (!strcmp(uplnm,"UCX$TZ")) tz_updated = 1;
1644 if (!strcmp(uplnm,"TZ")) tz_updated = 1;
1648 (void) vmssetenv(lnm,eqv,NULL);
1652 /*{{{static void vmssetuserlnm(char *name, char *eqv); */
1654 * sets a user-mode logical in the process logical name table
1655 * used for redirection of sys$error
1658 Perl_vmssetuserlnm(pTHX_ const char *name, const char *eqv)
1660 $DESCRIPTOR(d_tab, "LNM$PROCESS");
1661 struct dsc$descriptor_d d_name = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
1662 unsigned long int iss, attr = LNM$M_CONFINE;
1663 unsigned char acmode = PSL$C_USER;
1664 struct itmlst_3 lnmlst[2] = {{0, LNM$_STRING, 0, 0},
1666 d_name.dsc$a_pointer = (char *)name; /* Cast OK for read only parameter */
1667 d_name.dsc$w_length = strlen(name);
1669 lnmlst[0].buflen = strlen(eqv);
1670 lnmlst[0].bufadr = (char *)eqv; /* Cast OK for read only parameter */
1672 iss = sys$crelnm(&attr,&d_tab,&d_name,&acmode,lnmlst);
1673 if (!(iss&1)) lib$signal(iss);
1678 /*{{{ char *my_crypt(const char *textpasswd, const char *usrname)*/
1679 /* my_crypt - VMS password hashing
1680 * my_crypt() provides an interface compatible with the Unix crypt()
1681 * C library function, and uses sys$hash_password() to perform VMS
1682 * password hashing. The quadword hashed password value is returned
1683 * as a NUL-terminated 8 character string. my_crypt() does not change
1684 * the case of its string arguments; in order to match the behavior
1685 * of LOGINOUT et al., alphabetic characters in both arguments must
1686 * be upcased by the caller.
1688 * - fix me to call ACM services when available
1691 Perl_my_crypt(pTHX_ const char *textpasswd, const char *usrname)
1693 # ifndef UAI$C_PREFERRED_ALGORITHM
1694 # define UAI$C_PREFERRED_ALGORITHM 127
1696 unsigned char alg = UAI$C_PREFERRED_ALGORITHM;
1697 unsigned short int salt = 0;
1698 unsigned long int sts;
1700 unsigned short int dsc$w_length;
1701 unsigned char dsc$b_type;
1702 unsigned char dsc$b_class;
1703 const char * dsc$a_pointer;
1704 } usrdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
1705 txtdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1706 struct itmlst_3 uailst[3] = {
1707 { sizeof alg, UAI$_ENCRYPT, &alg, 0},
1708 { sizeof salt, UAI$_SALT, &salt, 0},
1709 { 0, 0, NULL, NULL}};
1710 static char hash[9];
1712 usrdsc.dsc$w_length = strlen(usrname);
1713 usrdsc.dsc$a_pointer = usrname;
1714 if (!((sts = sys$getuai(0, 0, &usrdsc, uailst, 0, 0, 0)) & 1)) {
1716 case SS$_NOGRPPRV: case SS$_NOSYSPRV:
1720 set_errno(ESRCH); /* There isn't a Unix no-such-user error */
1725 set_vaxc_errno(sts);
1726 if (sts != RMS$_RNF) return NULL;
1729 txtdsc.dsc$w_length = strlen(textpasswd);
1730 txtdsc.dsc$a_pointer = textpasswd;
1731 if (!((sts = sys$hash_password(&txtdsc, alg, salt, &usrdsc, &hash)) & 1)) {
1732 set_errno(EVMSERR); set_vaxc_errno(sts); return NULL;
1735 return (char *) hash;
1737 } /* end of my_crypt() */
1741 static char *mp_do_rmsexpand(pTHX_ const char *, char *, int, const char *, unsigned, int *, int *);
1742 static char *mp_do_fileify_dirspec(pTHX_ const char *, char *, int, int *);
1743 static char *mp_do_tovmsspec(pTHX_ const char *, char *, int, int, int *);
1745 /* fixup barenames that are directories for internal use.
1746 * There have been problems with the consistent handling of UNIX
1747 * style directory names when routines are presented with a name that
1748 * has no directory delimitors at all. So this routine will eventually
1751 static char * fixup_bare_dirnames(const char * name)
1753 if (decc_disable_to_vms_logname_translation) {
1760 * A little hack to get around a bug in some implemenation of remove()
1761 * that do not know how to delete a directory
1763 * Delete any file to which user has control access, regardless of whether
1764 * delete access is explicitly allowed.
1765 * Limitations: User must have write access to parent directory.
1766 * Does not block signals or ASTs; if interrupted in midstream
1767 * may leave file with an altered ACL.
1770 /*{{{int mp_do_kill_file(const char *name, int dirflag)*/
1772 mp_do_kill_file(pTHX_ const char *name, int dirflag)
1774 char *vmsname, *rspec;
1776 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
1777 unsigned long int cxt = 0, aclsts, fndsts, rmsts = -1;
1778 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1780 unsigned char myace$b_length;
1781 unsigned char myace$b_type;
1782 unsigned short int myace$w_flags;
1783 unsigned long int myace$l_access;
1784 unsigned long int myace$l_ident;
1785 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
1786 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0},
1787 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
1789 findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0},
1790 {sizeof oldace, ACL$C_READACE, &oldace, 0},{0,0,0,0}},
1791 addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}},
1792 dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}},
1793 lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}},
1794 ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}};
1796 /* Expand the input spec using RMS, since the CRTL remove() and
1797 * system services won't do this by themselves, so we may miss
1798 * a file "hiding" behind a logical name or search list. */
1799 vmsname = PerlMem_malloc(NAM$C_MAXRSS+1);
1800 if (vmsname == NULL) _ckvmssts(SS$_INSFMEM);
1802 if (do_rmsexpand(name, vmsname, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL) == NULL) {
1803 PerlMem_free(vmsname);
1807 if (decc_posix_compliant_pathnames) {
1808 /* In POSIX mode, we prefer to remove the UNIX name */
1810 remove_name = (char *)name;
1813 rspec = PerlMem_malloc(NAM$C_MAXRSS+1);
1814 if (rspec == NULL) _ckvmssts(SS$_INSFMEM);
1815 if (do_rmsexpand(vmsname, rspec, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL) == NULL) {
1816 PerlMem_free(rspec);
1817 PerlMem_free(vmsname);
1820 PerlMem_free(vmsname);
1821 remove_name = rspec;
1824 #if defined(__CRTL_VER) && __CRTL_VER >= 70000000
1826 if (decc_dir_barename && decc_posix_compliant_pathnames) {
1827 remove_name = PerlMem_malloc(NAM$C_MAXRSS+1);
1828 if (remove_name == NULL) _ckvmssts(SS$_INSFMEM);
1830 do_pathify_dirspec(name, remove_name, 0, NULL);
1831 if (!rmdir(remove_name)) {
1833 PerlMem_free(remove_name);
1834 PerlMem_free(rspec);
1835 return 0; /* Can we just get rid of it? */
1839 if (!rmdir(remove_name)) {
1840 PerlMem_free(rspec);
1841 return 0; /* Can we just get rid of it? */
1847 if (!remove(remove_name)) {
1848 PerlMem_free(rspec);
1849 return 0; /* Can we just get rid of it? */
1852 /* If not, can changing protections help? */
1853 if (vaxc$errno != RMS$_PRV) {
1854 PerlMem_free(rspec);
1858 /* No, so we get our own UIC to use as a rights identifier,
1859 * and the insert an ACE at the head of the ACL which allows us
1860 * to delete the file.
1862 _ckvmssts(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
1863 fildsc.dsc$w_length = strlen(rspec);
1864 fildsc.dsc$a_pointer = rspec;
1866 newace.myace$l_ident = oldace.myace$l_ident;
1867 if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) {
1869 case RMS$_FNF: case RMS$_DNF: case SS$_NOSUCHOBJECT:
1870 set_errno(ENOENT); break;
1872 set_errno(ENOTDIR); break;
1874 set_errno(ENODEV); break;
1875 case RMS$_SYN: case SS$_INVFILFOROP:
1876 set_errno(EINVAL); break;
1878 set_errno(EACCES); break;
1882 set_vaxc_errno(aclsts);
1883 PerlMem_free(rspec);
1886 /* Grab any existing ACEs with this identifier in case we fail */
1887 aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt);
1888 if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY
1889 || fndsts == SS$_NOMOREACE ) {
1890 /* Add the new ACE . . . */
1891 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1))
1894 #if defined(__CRTL_VER) && __CRTL_VER >= 70000000
1896 if (decc_dir_barename && decc_posix_compliant_pathnames) {
1897 remove_name = PerlMem_malloc(NAM$C_MAXRSS+1);
1898 if (remove_name == NULL) _ckvmssts(SS$_INSFMEM);
1900 do_pathify_dirspec(name, remove_name, 0, NULL);
1901 rmsts = rmdir(remove_name);
1902 PerlMem_free(remove_name);
1905 rmsts = rmdir(remove_name);
1909 rmsts = remove(remove_name);
1911 /* We blew it - dir with files in it, no write priv for
1912 * parent directory, etc. Put things back the way they were. */
1913 if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1))
1916 addlst[0].bufadr = &oldace;
1917 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1))
1924 fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0);
1925 /* We just deleted it, so of course it's not there. Some versions of
1926 * VMS seem to return success on the unlock operation anyhow (after all
1927 * the unlock is successful), but others don't.
1929 if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL;
1930 if (aclsts & 1) aclsts = fndsts;
1931 if (!(aclsts & 1)) {
1933 set_vaxc_errno(aclsts);
1934 PerlMem_free(rspec);
1938 PerlMem_free(rspec);
1941 } /* end of kill_file() */
1945 /*{{{int do_rmdir(char *name)*/
1947 Perl_do_rmdir(pTHX_ const char *name)
1949 char dirfile[NAM$C_MAXRSS+1];
1953 if (do_fileify_dirspec(name,dirfile,0,NULL) == NULL) return -1;
1954 if (flex_stat(dirfile,&st) || !S_ISDIR(st.st_mode)) retval = -1;
1955 else retval = mp_do_kill_file(aTHX_ dirfile, 1);
1958 } /* end of do_rmdir */
1962 * Delete any file to which user has control access, regardless of whether
1963 * delete access is explicitly allowed.
1964 * Limitations: User must have write access to parent directory.
1965 * Does not block signals or ASTs; if interrupted in midstream
1966 * may leave file with an altered ACL.
1969 /*{{{int kill_file(char *name)*/
1971 Perl_kill_file(pTHX_ const char *name)
1973 char rspec[NAM$C_MAXRSS+1];
1975 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
1976 unsigned long int cxt = 0, aclsts, fndsts, rmsts = -1;
1977 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1979 unsigned char myace$b_length;
1980 unsigned char myace$b_type;
1981 unsigned short int myace$w_flags;
1982 unsigned long int myace$l_access;
1983 unsigned long int myace$l_ident;
1984 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
1985 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0},
1986 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
1988 findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0},
1989 {sizeof oldace, ACL$C_READACE, &oldace, 0},{0,0,0,0}},
1990 addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}},
1991 dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}},
1992 lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}},
1993 ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}};
1995 /* Expand the input spec using RMS, since the CRTL remove() and
1996 * system services won't do this by themselves, so we may miss
1997 * a file "hiding" behind a logical name or search list. */
1998 tspec = do_rmsexpand(name, rspec, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL);
1999 if (tspec == NULL) return -1;
2000 if (!remove(rspec)) return 0; /* Can we just get rid of it? */
2001 /* If not, can changing protections help? */
2002 if (vaxc$errno != RMS$_PRV) return -1;
2004 /* No, so we get our own UIC to use as a rights identifier,
2005 * and the insert an ACE at the head of the ACL which allows us
2006 * to delete the file.
2008 _ckvmssts(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
2009 fildsc.dsc$w_length = strlen(rspec);
2010 fildsc.dsc$a_pointer = rspec;
2012 newace.myace$l_ident = oldace.myace$l_ident;
2013 if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) {
2015 case RMS$_FNF: case RMS$_DNF: case SS$_NOSUCHOBJECT:
2016 set_errno(ENOENT); break;
2018 set_errno(ENOTDIR); break;
2020 set_errno(ENODEV); break;
2021 case RMS$_SYN: case SS$_INVFILFOROP:
2022 set_errno(EINVAL); break;
2024 set_errno(EACCES); break;
2028 set_vaxc_errno(aclsts);
2031 /* Grab any existing ACEs with this identifier in case we fail */
2032 aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt);
2033 if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY
2034 || fndsts == SS$_NOMOREACE ) {
2035 /* Add the new ACE . . . */
2036 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1))
2038 if ((rmsts = remove(name))) {
2039 /* We blew it - dir with files in it, no write priv for
2040 * parent directory, etc. Put things back the way they were. */
2041 if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1))
2044 addlst[0].bufadr = &oldace;
2045 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1))
2052 fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0);
2053 /* We just deleted it, so of course it's not there. Some versions of
2054 * VMS seem to return success on the unlock operation anyhow (after all
2055 * the unlock is successful), but others don't.
2057 if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL;
2058 if (aclsts & 1) aclsts = fndsts;
2059 if (!(aclsts & 1)) {
2061 set_vaxc_errno(aclsts);
2067 } /* end of kill_file() */
2071 /*{{{int my_mkdir(char *,Mode_t)*/
2073 Perl_my_mkdir(pTHX_ const char *dir, Mode_t mode)
2075 STRLEN dirlen = strlen(dir);
2077 /* zero length string sometimes gives ACCVIO */
2078 if (dirlen == 0) return -1;
2080 /* CRTL mkdir() doesn't tolerate trailing /, since that implies
2081 * null file name/type. However, it's commonplace under Unix,
2082 * so we'll allow it for a gain in portability.
2084 if (dir[dirlen-1] == '/') {
2085 char *newdir = savepvn(dir,dirlen-1);
2086 int ret = mkdir(newdir,mode);
2090 else return mkdir(dir,mode);
2091 } /* end of my_mkdir */
2094 /*{{{int my_chdir(char *)*/
2096 Perl_my_chdir(pTHX_ const char *dir)
2098 STRLEN dirlen = strlen(dir);
2100 /* zero length string sometimes gives ACCVIO */
2101 if (dirlen == 0) return -1;
2104 /* Perl is passing the output of the DCL SHOW DEFAULT with leading spaces.
2105 * This does not work if DECC$EFS_CHARSET is active. Hack it here
2106 * so that existing scripts do not need to be changed.
2109 while ((dirlen > 0) && (*dir1 == ' ')) {
2114 /* some versions of CRTL chdir() doesn't tolerate trailing /, since
2116 * null file name/type. However, it's commonplace under Unix,
2117 * so we'll allow it for a gain in portability.
2119 * - Preview- '/' will be valid soon on VMS
2121 if ((dirlen > 1) && (dir1[dirlen-1] == '/')) {
2122 char *newdir = savepvn(dir1,dirlen-1);
2123 int ret = chdir(newdir);
2127 else return chdir(dir1);
2128 } /* end of my_chdir */
2132 /*{{{FILE *my_tmpfile()*/
2139 if ((fp = tmpfile())) return fp;
2141 cp = PerlMem_malloc(L_tmpnam+24);
2142 if (cp == NULL) _ckvmssts_noperl(SS$_INSFMEM);
2144 if (decc_filename_unix_only == 0)
2145 strcpy(cp,"Sys$Scratch:");
2148 tmpnam(cp+strlen(cp));
2149 strcat(cp,".Perltmp");
2150 fp = fopen(cp,"w+","fop=dlt");
2157 #ifndef HOMEGROWN_POSIX_SIGNALS
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);
2177 #ifdef KILL_BY_SIGPRC
2178 #include <errnodef.h>
2180 /* We implement our own kill() using the undocumented system service
2181 sys$sigprc for one of two reasons:
2183 1.) If the kill() in an older CRTL uses sys$forcex, causing the
2184 target process to do a sys$exit, which usually can't be handled
2185 gracefully...certainly not by Perl and the %SIG{} mechanism.
2187 2.) If the kill() in the CRTL can't be called from a signal
2188 handler without disappearing into the ether, i.e., the signal
2189 it purportedly sends is never trapped. Still true as of VMS 7.3.
2191 sys$sigprc has the same parameters as sys$forcex, but throws an exception
2192 in the target process rather than calling sys$exit.
2194 Note that distinguishing SIGSEGV from SIGBUS requires an extra arg
2195 on the ACCVIO condition, which sys$sigprc (and sys$forcex) don't
2196 provide. On VMS 7.0+ this is taken care of by doing sys$sigprc
2197 with condition codes C$_SIG0+nsig*8, catching the exception on the
2198 target process and resignaling with appropriate arguments.
2200 But we don't have that VMS 7.0+ exception handler, so if you
2201 Perl_my_kill(.., SIGSEGV) it will show up as a SIGBUS. Oh well.
2203 Also note that SIGTERM is listed in the docs as being "unimplemented",
2204 yet always seems to be signaled with a VMS condition code of 4 (and
2205 correctly handled for that code). So we hardwire it in.
2207 Unlike the VMS 7.0+ CRTL kill() function, we actually check the signal
2208 number to see if it's valid. So Perl_my_kill(pid,0) returns -1 rather
2209 than signalling with an unrecognized (and unhandled by CRTL) code.
2212 #define _MY_SIG_MAX 28
2215 Perl_sig_to_vmscondition_int(int sig)
2217 static unsigned int sig_code[_MY_SIG_MAX+1] =
2220 SS$_HANGUP, /* 1 SIGHUP */
2221 SS$_CONTROLC, /* 2 SIGINT */
2222 SS$_CONTROLY, /* 3 SIGQUIT */
2223 SS$_RADRMOD, /* 4 SIGILL */
2224 SS$_BREAK, /* 5 SIGTRAP */
2225 SS$_OPCCUS, /* 6 SIGABRT */
2226 SS$_COMPAT, /* 7 SIGEMT */
2228 SS$_FLTOVF, /* 8 SIGFPE VAX */
2230 SS$_HPARITH, /* 8 SIGFPE AXP */
2232 SS$_ABORT, /* 9 SIGKILL */
2233 SS$_ACCVIO, /* 10 SIGBUS */
2234 SS$_ACCVIO, /* 11 SIGSEGV */
2235 SS$_BADPARAM, /* 12 SIGSYS */
2236 SS$_NOMBX, /* 13 SIGPIPE */
2237 SS$_ASTFLT, /* 14 SIGALRM */
2254 #if __VMS_VER >= 60200000
2255 static int initted = 0;
2258 sig_code[16] = C$_SIGUSR1;
2259 sig_code[17] = C$_SIGUSR2;
2260 #if __CRTL_VER >= 70000000
2261 sig_code[20] = C$_SIGCHLD;
2263 #if __CRTL_VER >= 70300000
2264 sig_code[28] = C$_SIGWINCH;
2269 if (sig < _SIG_MIN) return 0;
2270 if (sig > _MY_SIG_MAX) return 0;
2271 return sig_code[sig];
2275 Perl_sig_to_vmscondition(int sig)
2278 if (vms_debug_on_exception != 0)
2279 lib$signal(SS$_DEBUG);
2281 return Perl_sig_to_vmscondition_int(sig);
2286 Perl_my_kill(int pid, int sig)
2291 int sys$sigprc(unsigned int *pidadr,
2292 struct dsc$descriptor_s *prcname,
2295 /* sig 0 means validate the PID */
2296 /*------------------------------*/
2298 const unsigned long int jpicode = JPI$_PID;
2301 status = lib$getjpi(&jpicode, &pid, NULL, &ret_pid, NULL, NULL);
2302 if ($VMS_STATUS_SUCCESS(status))
2305 case SS$_NOSUCHNODE:
2306 case SS$_UNREACHABLE:
2320 code = Perl_sig_to_vmscondition_int(sig);
2323 SETERRNO(EINVAL, SS$_BADPARAM);
2327 /* Fixme: Per official UNIX specification: If pid = 0, or negative then
2328 * signals are to be sent to multiple processes.
2329 * pid = 0 - all processes in group except ones that the system exempts
2330 * pid = -1 - all processes except ones that the system exempts
2331 * pid = -n - all processes in group (abs(n)) except ...
2332 * For now, just report as not supported.
2336 SETERRNO(ENOTSUP, SS$_UNSUPPORTED);
2340 iss = sys$sigprc((unsigned int *)&pid,0,code);
2341 if (iss&1) return 0;
2345 set_errno(EPERM); break;
2347 case SS$_NOSUCHNODE:
2348 case SS$_UNREACHABLE:
2349 set_errno(ESRCH); break;
2351 set_errno(ENOMEM); break;
2356 set_vaxc_errno(iss);
2362 /* Routine to convert a VMS status code to a UNIX status code.
2363 ** More tricky than it appears because of conflicting conventions with
2366 ** VMS status codes are a bit mask, with the least significant bit set for
2369 ** Special UNIX status of EVMSERR indicates that no translation is currently
2370 ** available, and programs should check the VMS status code.
2372 ** Programs compiled with _POSIX_EXIT have a special encoding that requires
2376 #ifndef C_FACILITY_NO
2377 #define C_FACILITY_NO 0x350000
2380 #define DCL_IVVERB 0x38090
2383 int Perl_vms_status_to_unix(int vms_status, int child_flag)
2391 /* Assume the best or the worst */
2392 if (vms_status & STS$M_SUCCESS)
2395 unix_status = EVMSERR;
2397 msg_status = vms_status & ~STS$M_CONTROL;
2399 facility = vms_status & STS$M_FAC_NO;
2400 fac_sp = vms_status & STS$M_FAC_SP;
2401 msg_no = vms_status & (STS$M_MSG_NO | STS$M_SEVERITY);
2403 if (((facility == 0) || (fac_sp == 0)) && (child_flag == 0)) {
2409 unix_status = EFAULT;
2411 case SS$_DEVOFFLINE:
2412 unix_status = EBUSY;
2415 unix_status = ENOTCONN;
2423 case SS$_INVFILFOROP:
2427 unix_status = EINVAL;
2429 case SS$_UNSUPPORTED:
2430 unix_status = ENOTSUP;
2435 unix_status = EACCES;
2437 case SS$_DEVICEFULL:
2438 unix_status = ENOSPC;
2441 unix_status = ENODEV;
2443 case SS$_NOSUCHFILE:
2444 case SS$_NOSUCHOBJECT:
2445 unix_status = ENOENT;
2447 case SS$_ABORT: /* Fatal case */
2448 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_ERROR): /* Error case */
2449 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_WARNING): /* Warning case */
2450 unix_status = EINTR;
2453 unix_status = E2BIG;
2456 unix_status = ENOMEM;
2459 unix_status = EPERM;
2461 case SS$_NOSUCHNODE:
2462 case SS$_UNREACHABLE:
2463 unix_status = ESRCH;
2466 unix_status = ECHILD;
2469 if ((facility == 0) && (msg_no < 8)) {
2470 /* These are not real VMS status codes so assume that they are
2471 ** already UNIX status codes
2473 unix_status = msg_no;
2479 /* Translate a POSIX exit code to a UNIX exit code */
2480 if ((facility == C_FACILITY_NO) && ((msg_no & 0xA000) == 0xA000)) {
2481 unix_status = (msg_no & 0x07F8) >> 3;
2485 /* Documented traditional behavior for handling VMS child exits */
2486 /*--------------------------------------------------------------*/
2487 if (child_flag != 0) {
2489 /* Success / Informational return 0 */
2490 /*----------------------------------*/
2491 if (msg_no & STS$K_SUCCESS)
2494 /* Warning returns 1 */
2495 /*-------------------*/
2496 if ((msg_no & (STS$K_ERROR | STS$K_SEVERE)) == 0)
2499 /* Everything else pass through the severity bits */
2500 /*------------------------------------------------*/
2501 return (msg_no & STS$M_SEVERITY);
2504 /* Normal VMS status to ERRNO mapping attempt */
2505 /*--------------------------------------------*/
2506 switch(msg_status) {
2507 /* case RMS$_EOF: */ /* End of File */
2508 case RMS$_FNF: /* File Not Found */
2509 case RMS$_DNF: /* Dir Not Found */
2510 unix_status = ENOENT;
2512 case RMS$_RNF: /* Record Not Found */
2513 unix_status = ESRCH;
2516 unix_status = ENOTDIR;
2519 unix_status = ENODEV;
2524 unix_status = EBADF;
2527 unix_status = EEXIST;
2531 case LIB$_INVSTRDES:
2533 case LIB$_NOSUCHSYM:
2534 case LIB$_INVSYMNAM:
2536 unix_status = EINVAL;
2542 unix_status = E2BIG;
2544 case RMS$_PRV: /* No privilege */
2545 case RMS$_ACC: /* ACP file access failed */
2546 case RMS$_WLK: /* Device write locked */
2547 unix_status = EACCES;
2549 /* case RMS$_NMF: */ /* No more files */
2557 /* Try to guess at what VMS error status should go with a UNIX errno
2558 * value. This is hard to do as there could be many possible VMS
2559 * error statuses that caused the errno value to be set.
2562 int Perl_unix_status_to_vms(int unix_status)
2564 int test_unix_status;
2566 /* Trivial cases first */
2567 /*---------------------*/
2568 if (unix_status == EVMSERR)
2571 /* Is vaxc$errno sane? */
2572 /*---------------------*/
2573 test_unix_status = Perl_vms_status_to_unix(vaxc$errno, 0);
2574 if (test_unix_status == unix_status)
2577 /* If way out of range, must be VMS code already */
2578 /*-----------------------------------------------*/
2579 if (unix_status > EVMSERR)
2582 /* If out of range, punt */
2583 /*-----------------------*/
2584 if (unix_status > __ERRNO_MAX)
2588 /* Ok, now we have to do it the hard way. */
2589 /*----------------------------------------*/
2590 switch(unix_status) {
2591 case 0: return SS$_NORMAL;
2592 case EPERM: return SS$_NOPRIV;
2593 case ENOENT: return SS$_NOSUCHOBJECT;
2594 case ESRCH: return SS$_UNREACHABLE;
2595 case EINTR: return SS$_ABORT;
2598 case E2BIG: return SS$_BUFFEROVF;
2600 case EBADF: return RMS$_IFI;
2601 case ECHILD: return SS$_NONEXPR;
2603 case ENOMEM: return SS$_INSFMEM;
2604 case EACCES: return SS$_FILACCERR;
2605 case EFAULT: return SS$_ACCVIO;
2607 case EBUSY: return SS$_DEVOFFLINE;
2608 case EEXIST: return RMS$_FEX;
2610 case ENODEV: return SS$_NOSUCHDEV;
2611 case ENOTDIR: return RMS$_DIR;
2613 case EINVAL: return SS$_INVARG;
2619 case ENOSPC: return SS$_DEVICEFULL;
2620 case ESPIPE: return LIB$_INVARG;
2625 case ERANGE: return LIB$_INVARG;
2626 /* case EWOULDBLOCK */
2627 /* case EINPROGRESS */
2630 /* case EDESTADDRREQ */
2632 /* case EPROTOTYPE */
2633 /* case ENOPROTOOPT */
2634 /* case EPROTONOSUPPORT */
2635 /* case ESOCKTNOSUPPORT */
2636 /* case EOPNOTSUPP */
2637 /* case EPFNOSUPPORT */
2638 /* case EAFNOSUPPORT */
2639 /* case EADDRINUSE */
2640 /* case EADDRNOTAVAIL */
2642 /* case ENETUNREACH */
2643 /* case ENETRESET */
2644 /* case ECONNABORTED */
2645 /* case ECONNRESET */
2648 case ENOTCONN: return SS$_CLEARED;
2649 /* case ESHUTDOWN */
2650 /* case ETOOMANYREFS */
2651 /* case ETIMEDOUT */
2652 /* case ECONNREFUSED */
2654 /* case ENAMETOOLONG */
2655 /* case EHOSTDOWN */
2656 /* case EHOSTUNREACH */
2657 /* case ENOTEMPTY */
2669 /* case ECANCELED */
2673 return SS$_UNSUPPORTED;
2679 /* case EABANDONED */
2681 return SS$_ABORT; /* punt */
2684 return SS$_ABORT; /* Should not get here */
2688 /* default piping mailbox size */
2689 #define PERL_BUFSIZ 512
2693 create_mbx(pTHX_ unsigned short int *chan, struct dsc$descriptor_s *namdsc)
2695 unsigned long int mbxbufsiz;
2696 static unsigned long int syssize = 0;
2697 unsigned long int dviitm = DVI$_DEVNAM;
2698 char csize[LNM$C_NAMLENGTH+1];
2702 unsigned long syiitm = SYI$_MAXBUF;
2704 * Get the SYSGEN parameter MAXBUF
2706 * If the logical 'PERL_MBX_SIZE' is defined
2707 * use the value of the logical instead of PERL_BUFSIZ, but
2708 * keep the size between 128 and MAXBUF.
2711 _ckvmssts(lib$getsyi(&syiitm, &syssize, 0, 0, 0, 0));
2714 if (vmstrnenv("PERL_MBX_SIZE", csize, 0, fildev, 0)) {
2715 mbxbufsiz = atoi(csize);
2717 mbxbufsiz = PERL_BUFSIZ;
2719 if (mbxbufsiz < 128) mbxbufsiz = 128;
2720 if (mbxbufsiz > syssize) mbxbufsiz = syssize;
2722 _ckvmssts(sts = sys$crembx(0,chan,mbxbufsiz,mbxbufsiz,0,0,0));
2724 _ckvmssts(sts = lib$getdvi(&dviitm, chan, NULL, NULL, namdsc, &namdsc->dsc$w_length));
2725 namdsc->dsc$a_pointer[namdsc->dsc$w_length] = '\0';
2727 } /* end of create_mbx() */
2730 /*{{{ my_popen and my_pclose*/
2732 typedef struct _iosb IOSB;
2733 typedef struct _iosb* pIOSB;
2734 typedef struct _pipe Pipe;
2735 typedef struct _pipe* pPipe;
2736 typedef struct pipe_details Info;
2737 typedef struct pipe_details* pInfo;
2738 typedef struct _srqp RQE;
2739 typedef struct _srqp* pRQE;
2740 typedef struct _tochildbuf CBuf;
2741 typedef struct _tochildbuf* pCBuf;
2744 unsigned short status;
2745 unsigned short count;
2746 unsigned long dvispec;
2749 #pragma member_alignment save
2750 #pragma nomember_alignment quadword
2751 struct _srqp { /* VMS self-relative queue entry */
2752 unsigned long qptr[2];
2754 #pragma member_alignment restore
2755 static RQE RQE_ZERO = {0,0};
2757 struct _tochildbuf {
2760 unsigned short size;
2768 unsigned short chan_in;
2769 unsigned short chan_out;
2771 unsigned int bufsize;
2783 #if defined(PERL_IMPLICIT_CONTEXT)
2784 void *thx; /* Either a thread or an interpreter */
2785 /* pointer, depending on how we're built */
2793 PerlIO *fp; /* file pointer to pipe mailbox */
2794 int useFILE; /* using stdio, not perlio */
2795 int pid; /* PID of subprocess */
2796 int mode; /* == 'r' if pipe open for reading */
2797 int done; /* subprocess has completed */
2798 int waiting; /* waiting for completion/closure */
2799 int closing; /* my_pclose is closing this pipe */
2800 unsigned long completion; /* termination status of subprocess */
2801 pPipe in; /* pipe in to sub */
2802 pPipe out; /* pipe out of sub */
2803 pPipe err; /* pipe of sub's sys$error */
2804 int in_done; /* true when in pipe finished */
2807 unsigned short xchan; /* channel to debug xterm */
2808 unsigned short xchan_valid; /* channel is assigned */
2811 struct exit_control_block
2813 struct exit_control_block *flink;
2814 unsigned long int (*exit_routine)();
2815 unsigned long int arg_count;
2816 unsigned long int *status_address;
2817 unsigned long int exit_status;
2820 typedef struct _closed_pipes Xpipe;
2821 typedef struct _closed_pipes* pXpipe;
2823 struct _closed_pipes {
2824 int pid; /* PID of subprocess */
2825 unsigned long completion; /* termination status of subprocess */
2827 #define NKEEPCLOSED 50
2828 static Xpipe closed_list[NKEEPCLOSED];
2829 static int closed_index = 0;
2830 static int closed_num = 0;
2832 #define RETRY_DELAY "0 ::0.20"
2833 #define MAX_RETRY 50
2835 static int pipe_ef = 0; /* first call to safe_popen inits these*/
2836 static unsigned long mypid;
2837 static unsigned long delaytime[2];
2839 static pInfo open_pipes = NULL;
2840 static $DESCRIPTOR(nl_desc, "NL:");
2842 #define PIPE_COMPLETION_WAIT 30 /* seconds, for EOF/FORCEX wait */
2846 static unsigned long int
2847 pipe_exit_routine(pTHX)
2850 unsigned long int retsts = SS$_NORMAL, abort = SS$_TIMEOUT;
2851 int sts, did_stuff, need_eof, j;
2854 * Flush any pending i/o, but since we are in process run-down, be
2855 * careful about referencing PerlIO structures that may already have
2856 * been deallocated. We may not even have an interpreter anymore.
2862 #if defined(USE_ITHREADS)
2865 && PL_perlio_fd_refcnt)
2866 PerlIO_flush(info->fp);
2868 fflush((FILE *)info->fp);
2874 next we try sending an EOF...ignore if doesn't work, make sure we
2882 _ckvmssts_noperl(sys$setast(0));
2883 if (info->in && !info->in->shut_on_empty) {
2884 _ckvmssts_noperl(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
2889 _ckvmssts_noperl(sys$setast(1));
2893 /* wait for EOF to have effect, up to ~ 30 sec [default] */
2895 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
2900 _ckvmssts_noperl(sys$setast(0));
2901 if (info->waiting && info->done)
2903 nwait += info->waiting;
2904 _ckvmssts_noperl(sys$setast(1));
2914 _ckvmssts_noperl(sys$setast(0));
2915 if (!info->done) { /* Tap them gently on the shoulder . . .*/
2916 sts = sys$forcex(&info->pid,0,&abort);
2917 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
2920 _ckvmssts_noperl(sys$setast(1));
2924 /* again, wait for effect */
2926 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
2931 _ckvmssts_noperl(sys$setast(0));
2932 if (info->waiting && info->done)
2934 nwait += info->waiting;
2935 _ckvmssts_noperl(sys$setast(1));
2944 _ckvmssts_noperl(sys$setast(0));
2945 if (!info->done) { /* We tried to be nice . . . */
2946 sts = sys$delprc(&info->pid,0);
2947 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
2948 info->done = 1; /* sys$delprc is as done as we're going to get. */
2950 _ckvmssts_noperl(sys$setast(1));
2955 if ((sts = my_pclose(open_pipes->fp)) == -1) retsts = vaxc$errno;
2956 else if (!(sts & 1)) retsts = sts;
2961 static struct exit_control_block pipe_exitblock =
2962 {(struct exit_control_block *) 0,
2963 pipe_exit_routine, 0, &pipe_exitblock.exit_status, 0};
2965 static void pipe_mbxtofd_ast(pPipe p);
2966 static void pipe_tochild1_ast(pPipe p);
2967 static void pipe_tochild2_ast(pPipe p);
2970 popen_completion_ast(pInfo info)
2972 pInfo i = open_pipes;
2977 info->completion &= 0x0FFFFFFF; /* strip off "control" field */
2978 closed_list[closed_index].pid = info->pid;
2979 closed_list[closed_index].completion = info->completion;
2981 if (closed_index == NKEEPCLOSED)
2986 if (i == info) break;
2989 if (!i) return; /* unlinked, probably freed too */
2994 Writing to subprocess ...
2995 if my_pclose'd: EOF already sent, should shutdown chan_in part of pipe
2997 chan_out may be waiting for "done" flag, or hung waiting
2998 for i/o completion to child...cancel the i/o. This will
2999 put it into "snarf mode" (done but no EOF yet) that discards
3002 Output from subprocess (stdout, stderr) needs to be flushed and
3003 shut down. We try sending an EOF, but if the mbx is full the pipe
3004 routine should still catch the "shut_on_empty" flag, telling it to
3005 use immediate-style reads so that "mbx empty" -> EOF.
3009 if (info->in && !info->in_done) { /* only for mode=w */
3010 if (info->in->shut_on_empty && info->in->need_wake) {
3011 info->in->need_wake = FALSE;
3012 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,info->in,0));
3014 _ckvmssts_noperl(sys$cancel(info->in->chan_out));
3018 if (info->out && !info->out_done) { /* were we also piping output? */
3019 info->out->shut_on_empty = TRUE;
3020 iss = sys$qio(0,info->out->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
3021 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
3022 _ckvmssts_noperl(iss);
3025 if (info->err && !info->err_done) { /* we were piping stderr */
3026 info->err->shut_on_empty = TRUE;
3027 iss = sys$qio(0,info->err->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
3028 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
3029 _ckvmssts_noperl(iss);
3031 _ckvmssts_noperl(sys$setef(pipe_ef));
3035 static unsigned long int setup_cmddsc(pTHX_ const char *cmd, int check_img, int *suggest_quote, struct dsc$descriptor_s **pvmscmd);
3036 static void vms_execfree(struct dsc$descriptor_s *vmscmd);
3039 we actually differ from vmstrnenv since we use this to
3040 get the RMS IFI to check if SYS$OUTPUT and SYS$ERROR *really*
3041 are pointing to the same thing
3044 static unsigned short
3045 popen_translate(pTHX_ char *logical, char *result)
3048 $DESCRIPTOR(d_table,"LNM$PROCESS_TABLE");
3049 $DESCRIPTOR(d_log,"");
3051 unsigned short length;
3052 unsigned short code;
3054 unsigned short *retlenaddr;
3056 unsigned short l, ifi;
3058 d_log.dsc$a_pointer = logical;
3059 d_log.dsc$w_length = strlen(logical);
3061 itmlst[0].code = LNM$_STRING;
3062 itmlst[0].length = 255;
3063 itmlst[0].buffer_addr = result;
3064 itmlst[0].retlenaddr = &l;
3067 itmlst[1].length = 0;
3068 itmlst[1].buffer_addr = 0;
3069 itmlst[1].retlenaddr = 0;
3071 iss = sys$trnlnm(0, &d_table, &d_log, 0, itmlst);
3072 if (iss == SS$_NOLOGNAM) {
3076 if (!(iss&1)) lib$signal(iss);
3079 logicals for PPFs have a 4 byte prefix ESC+NUL+(RMS IFI)
3080 strip it off and return the ifi, if any
3083 if (result[0] == 0x1b && result[1] == 0x00) {
3084 memmove(&ifi,result+2,2);
3085 strcpy(result,result+4);
3087 return ifi; /* this is the RMS internal file id */
3090 static void pipe_infromchild_ast(pPipe p);
3093 I'm using LIB$(GET|FREE)_VM here so that we can allocate and deallocate
3094 inside an AST routine without worrying about reentrancy and which Perl
3095 memory allocator is being used.
3097 We read data and queue up the buffers, then spit them out one at a
3098 time to the output mailbox when the output mailbox is ready for one.
3101 #define INITIAL_TOCHILDQUEUE 2
3104 pipe_tochild_setup(pTHX_ char *rmbx, char *wmbx)
3108 char mbx1[64], mbx2[64];
3109 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3110 DSC$K_CLASS_S, mbx1},
3111 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
3112 DSC$K_CLASS_S, mbx2};
3113 unsigned int dviitm = DVI$_DEVBUFSIZ;
3117 _ckvmssts(lib$get_vm(&n, &p));
3119 create_mbx(aTHX_ &p->chan_in , &d_mbx1);
3120 create_mbx(aTHX_ &p->chan_out, &d_mbx2);
3121 _ckvmssts(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3124 p->shut_on_empty = FALSE;
3125 p->need_wake = FALSE;
3128 p->iosb.status = SS$_NORMAL;
3129 p->iosb2.status = SS$_NORMAL;
3135 #ifdef PERL_IMPLICIT_CONTEXT
3139 n = sizeof(CBuf) + p->bufsize;
3141 for (j = 0; j < INITIAL_TOCHILDQUEUE; j++) {
3142 _ckvmssts(lib$get_vm(&n, &b));
3143 b->buf = (char *) b + sizeof(CBuf);
3144 _ckvmssts(lib$insqhi(b, &p->free));
3147 pipe_tochild2_ast(p);
3148 pipe_tochild1_ast(p);
3154 /* reads the MBX Perl is writing, and queues */
3157 pipe_tochild1_ast(pPipe p)
3160 int iss = p->iosb.status;
3161 int eof = (iss == SS$_ENDOFFILE);
3163 #ifdef PERL_IMPLICIT_CONTEXT
3169 p->shut_on_empty = TRUE;
3171 _ckvmssts(sys$dassgn(p->chan_in));
3177 b->size = p->iosb.count;
3178 _ckvmssts(sts = lib$insqhi(b, &p->wait));
3180 p->need_wake = FALSE;
3181 _ckvmssts(sys$dclast(pipe_tochild2_ast,p,0));
3184 p->retry = 1; /* initial call */
3187 if (eof) { /* flush the free queue, return when done */
3188 int n = sizeof(CBuf) + p->bufsize;
3190 iss = lib$remqti(&p->free, &b);
3191 if (iss == LIB$_QUEWASEMP) return;
3193 _ckvmssts(lib$free_vm(&n, &b));
3197 iss = lib$remqti(&p->free, &b);
3198 if (iss == LIB$_QUEWASEMP) {
3199 int n = sizeof(CBuf) + p->bufsize;
3200 _ckvmssts(lib$get_vm(&n, &b));
3201 b->buf = (char *) b + sizeof(CBuf);
3207 iss = sys$qio(0,p->chan_in,
3208 IO$_READVBLK|(p->shut_on_empty ? IO$M_NOWAIT : 0),
3210 pipe_tochild1_ast, p, b->buf, p->bufsize, 0, 0, 0, 0);
3211 if (iss == SS$_ENDOFFILE && p->shut_on_empty) iss = SS$_NORMAL;
3216 /* writes queued buffers to output, waits for each to complete before
3220 pipe_tochild2_ast(pPipe p)
3223 int iss = p->iosb2.status;
3224 int n = sizeof(CBuf) + p->bufsize;
3225 int done = (p->info && p->info->done) ||
3226 iss == SS$_CANCEL || iss == SS$_ABORT;
3227 #if defined(PERL_IMPLICIT_CONTEXT)
3232 if (p->type) { /* type=1 has old buffer, dispose */
3233 if (p->shut_on_empty) {
3234 _ckvmssts(lib$free_vm(&n, &b));
3236 _ckvmssts(lib$insqhi(b, &p->free));
3241 iss = lib$remqti(&p->wait, &b);
3242 if (iss == LIB$_QUEWASEMP) {
3243 if (p->shut_on_empty) {
3245 _ckvmssts(sys$dassgn(p->chan_out));
3246 *p->pipe_done = TRUE;
3247 _ckvmssts(sys$setef(pipe_ef));
3249 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEOF,
3250 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3254 p->need_wake = TRUE;
3264 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEOF,
3265 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3267 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEVBLK,
3268 &p->iosb2, pipe_tochild2_ast, p, b->buf, b->size, 0, 0, 0, 0));
3277 pipe_infromchild_setup(pTHX_ char *rmbx, char *wmbx)
3280 char mbx1[64], mbx2[64];
3281 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3282 DSC$K_CLASS_S, mbx1},
3283 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
3284 DSC$K_CLASS_S, mbx2};
3285 unsigned int dviitm = DVI$_DEVBUFSIZ;
3287 int n = sizeof(Pipe);
3288 _ckvmssts(lib$get_vm(&n, &p));
3289 create_mbx(aTHX_ &p->chan_in , &d_mbx1);
3290 create_mbx(aTHX_ &p->chan_out, &d_mbx2);
3292 _ckvmssts(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3293 n = p->bufsize * sizeof(char);
3294 _ckvmssts(lib$get_vm(&n, &p->buf));
3295 p->shut_on_empty = FALSE;
3298 p->iosb.status = SS$_NORMAL;
3299 #if defined(PERL_IMPLICIT_CONTEXT)
3302 pipe_infromchild_ast(p);
3310 pipe_infromchild_ast(pPipe p)
3312 int iss = p->iosb.status;
3313 int eof = (iss == SS$_ENDOFFILE);
3314 int myeof = (eof && (p->iosb.dvispec == mypid || p->iosb.dvispec == 0));
3315 int kideof = (eof && (p->iosb.dvispec == p->info->pid));
3316 #if defined(PERL_IMPLICIT_CONTEXT)
3320 if (p->info && p->info->closing && p->chan_out) { /* output shutdown */
3321 _ckvmssts(sys$dassgn(p->chan_out));
3326 input shutdown if EOF from self (done or shut_on_empty)
3327 output shutdown if closing flag set (my_pclose)
3328 send data/eof from child or eof from self
3329 otherwise, re-read (snarf of data from child)
3334 if (myeof && p->chan_in) { /* input shutdown */
3335 _ckvmssts(sys$dassgn(p->chan_in));
3340 if (myeof || kideof) { /* pass EOF to parent */
3341 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEOF, &p->iosb,
3342 pipe_infromchild_ast, p,
3345 } else if (eof) { /* eat EOF --- fall through to read*/
3347 } else { /* transmit data */
3348 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEVBLK,&p->iosb,
3349 pipe_infromchild_ast,p,
3350 p->buf, p->iosb.count, 0, 0, 0, 0));
3356 /* everything shut? flag as done */
3358 if (!p->chan_in && !p->chan_out) {
3359 *p->pipe_done = TRUE;
3360 _ckvmssts(sys$setef(pipe_ef));
3364 /* write completed (or read, if snarfing from child)
3365 if still have input active,
3366 queue read...immediate mode if shut_on_empty so we get EOF if empty
3368 check if Perl reading, generate EOFs as needed
3374 iss = sys$qio(0,p->chan_in,IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0),&p->iosb,
3375 pipe_infromchild_ast,p,
3376 p->buf, p->bufsize, 0, 0, 0, 0);
3377 if (p->shut_on_empty && iss == SS$_ENDOFFILE) iss = SS$_NORMAL;
3379 } else { /* send EOFs for extra reads */
3380 p->iosb.status = SS$_ENDOFFILE;
3381 p->iosb.dvispec = 0;
3382 _ckvmssts(sys$qio(0,p->chan_out,IO$_SETMODE|IO$M_READATTN,
3384 pipe_infromchild_ast, p, 0, 0, 0, 0));
3390 pipe_mbxtofd_setup(pTHX_ int fd, char *out)
3394 unsigned long dviitm = DVI$_DEVBUFSIZ;
3396 struct dsc$descriptor_s d_mbx = {sizeof mbx, DSC$K_DTYPE_T,
3397 DSC$K_CLASS_S, mbx};
3398 int n = sizeof(Pipe);
3400 /* things like terminals and mbx's don't need this filter */
3401 if (fd && fstat(fd,&s) == 0) {
3402 unsigned long dviitm = DVI$_DEVCHAR, devchar;
3404 unsigned short dev_len;
3405 struct dsc$descriptor_s d_dev;
3407 struct item_list_3 items[3];
3409 unsigned short dvi_iosb[4];
3411 cptr = getname(fd, out, 1);
3412 if (cptr == NULL) _ckvmssts(SS$_NOSUCHDEV);
3413 d_dev.dsc$a_pointer = out;
3414 d_dev.dsc$w_length = strlen(out);
3415 d_dev.dsc$b_dtype = DSC$K_DTYPE_T;
3416 d_dev.dsc$b_class = DSC$K_CLASS_S;
3419 items[0].code = DVI$_DEVCHAR;
3420 items[0].bufadr = &devchar;
3421 items[0].retadr = NULL;
3423 items[1].code = DVI$_FULLDEVNAM;
3424 items[1].bufadr = device;
3425 items[1].retadr = &dev_len;
3429 status = sys$getdviw
3430 (NO_EFN, 0, &d_dev, items, dvi_iosb, NULL, NULL, NULL);
3432 if ($VMS_STATUS_SUCCESS(dvi_iosb[0])) {
3433 device[dev_len] = 0;
3435 if (!(devchar & DEV$M_DIR)) {
3436 strcpy(out, device);
3442 _ckvmssts(lib$get_vm(&n, &p));
3443 p->fd_out = dup(fd);
3444 create_mbx(aTHX_ &p->chan_in, &d_mbx);
3445 _ckvmssts(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3446 n = (p->bufsize+1) * sizeof(char);
3447 _ckvmssts(lib$get_vm(&n, &p->buf));
3448 p->shut_on_empty = FALSE;
3453 _ckvmssts(sys$qio(0, p->chan_in, IO$_READVBLK, &p->iosb,
3454 pipe_mbxtofd_ast, p,
3455 p->buf, p->bufsize, 0, 0, 0, 0));
3461 pipe_mbxtofd_ast(pPipe p)
3463 int iss = p->iosb.status;
3464 int done = p->info->done;
3466 int eof = (iss == SS$_ENDOFFILE);
3467 int myeof = eof && ((p->iosb.dvispec == mypid)||(p->iosb.dvispec == 0));
3468 int err = !(iss&1) && !eof;
3469 #if defined(PERL_IMPLICIT_CONTEXT)
3473 if (done && myeof) { /* end piping */
3475 sys$dassgn(p->chan_in);
3476 *p->pipe_done = TRUE;
3477 _ckvmssts(sys$setef(pipe_ef));
3481 if (!err && !eof) { /* good data to send to file */
3482 p->buf[p->iosb.count] = '\n';
3483 iss2 = write(p->fd_out, p->buf, p->iosb.count+1);
3486 if (p->retry < MAX_RETRY) {
3487 _ckvmssts(sys$setimr(0,delaytime,pipe_mbxtofd_ast,p));
3497 iss = sys$qio(0, p->chan_in, IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0), &p->iosb,
3498 pipe_mbxtofd_ast, p,
3499 p->buf, p->bufsize, 0, 0, 0, 0);
3500 if (p->shut_on_empty && (iss == SS$_ENDOFFILE)) iss = SS$_NORMAL;
3505 typedef struct _pipeloc PLOC;
3506 typedef struct _pipeloc* pPLOC;
3510 char dir[NAM$C_MAXRSS+1];
3512 static pPLOC head_PLOC = 0;
3515 free_pipelocs(pTHX_ void *head)
3518 pPLOC *pHead = (pPLOC *)head;
3530 store_pipelocs(pTHX)
3539 char temp[NAM$C_MAXRSS+1];
3543 free_pipelocs(aTHX_ &head_PLOC);
3545 /* the . directory from @INC comes last */
3547 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3548 if (p == NULL) _ckvmssts(SS$_INSFMEM);
3549 p->next = head_PLOC;
3551 strcpy(p->dir,"./");
3553 /* get the directory from $^X */
3555 unixdir = PerlMem_malloc(VMS_MAXRSS);
3556 if (unixdir == NULL) _ckvmssts(SS$_INSFMEM);
3558 #ifdef PERL_IMPLICIT_CONTEXT
3559 if (aTHX && PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
3561 if (PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
3563 strcpy(temp, PL_origargv[0]);
3564 x = strrchr(temp,']');
3566 x = strrchr(temp,'>');
3568 /* It could be a UNIX path */
3569 x = strrchr(temp,'/');
3575 /* Got a bare name, so use default directory */
3580 if ((tounixpath_utf8(temp, unixdir, NULL)) != Nullch) {
3581 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3582 if (p == NULL) _ckvmssts(SS$_INSFMEM);
3583 p->next = head_PLOC;
3585 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3586 p->dir[NAM$C_MAXRSS] = '\0';
3590 /* reverse order of @INC entries, skip "." since entered above */
3592 #ifdef PERL_IMPLICIT_CONTEXT
3595 if (PL_incgv) av = GvAVn(PL_incgv);
3597 for (i = 0; av && i <= AvFILL(av); i++) {
3598 dirsv = *av_fetch(av,i,TRUE);
3600 if (SvROK(dirsv)) continue;
3601 dir = SvPVx(dirsv,n_a);
3602 if (strcmp(dir,".") == 0) continue;
3603 if ((tounixpath_utf8(dir, unixdir, NULL)) == Nullch)
3606 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3607 p->next = head_PLOC;
3609 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3610 p->dir[NAM$C_MAXRSS] = '\0';
3613 /* most likely spot (ARCHLIB) put first in the list */
3616 if ((tounixpath_utf8(ARCHLIB_EXP, unixdir, NULL)) != Nullch) {
3617 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3618 if (p == NULL) _ckvmssts(SS$_INSFMEM);
3619 p->next = head_PLOC;
3621 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3622 p->dir[NAM$C_MAXRSS] = '\0';
3625 PerlMem_free(unixdir);
3629 Perl_cando_by_name_int
3630 (pTHX_ I32 bit, bool effective, const char *fname, int opts);
3631 #if !defined(PERL_IMPLICIT_CONTEXT)
3632 #define cando_by_name_int Perl_cando_by_name_int
3634 #define cando_by_name_int(a,b,c,d) Perl_cando_by_name_int(aTHX_ a,b,c,d)
3640 static int vmspipe_file_status = 0;
3641 static char vmspipe_file[NAM$C_MAXRSS+1];
3643 /* already found? Check and use ... need read+execute permission */
3645 if (vmspipe_file_status == 1) {
3646 if (cando_by_name_int(S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)
3647 && cando_by_name_int
3648 (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) {
3649 return vmspipe_file;
3651 vmspipe_file_status = 0;
3654 /* scan through stored @INC, $^X */
3656 if (vmspipe_file_status == 0) {
3657 char file[NAM$C_MAXRSS+1];
3658 pPLOC p = head_PLOC;
3663 strcpy(file, p->dir);
3664 dirlen = strlen(file);
3665 strncat(file, "vmspipe.com",NAM$C_MAXRSS - dirlen);
3666 file[NAM$C_MAXRSS] = '\0';
3669 exp_res = do_rmsexpand
3670 (file, vmspipe_file, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL);
3671 if (!exp_res) continue;
3673 if (cando_by_name_int
3674 (S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)
3675 && cando_by_name_int
3676 (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) {
3677 vmspipe_file_status = 1;
3678 return vmspipe_file;
3681 vmspipe_file_status = -1; /* failed, use tempfiles */
3688 vmspipe_tempfile(pTHX)
3690 char file[NAM$C_MAXRSS+1];
3692 static int index = 0;
3696 /* create a tempfile */
3698 /* we can't go from W, shr=get to R, shr=get without
3699 an intermediate vulnerable state, so don't bother trying...
3701 and lib$spawn doesn't shr=put, so have to close the write
3703 So... match up the creation date/time and the FID to
3704 make sure we're dealing with the same file
3709 if (!decc_filename_unix_only) {
3710 sprintf(file,"sys$scratch:perlpipe_%08.8x_%d.com",mypid,index);
3711 fp = fopen(file,"w");
3713 sprintf(file,"sys$login:perlpipe_%08.8x_%d.com",mypid,index);
3714 fp = fopen(file,"w");
3716 sprintf(file,"sys$disk:[]perlpipe_%08.8x_%d.com",mypid,index);
3717 fp = fopen(file,"w");
3722 sprintf(file,"/tmp/perlpipe_%08.8x_%d.com",mypid,index);
3723 fp = fopen(file,"w");
3725 sprintf(file,"/sys$login/perlpipe_%08.8x_%d.com",mypid,index);
3726 fp = fopen(file,"w");
3728 sprintf(file,"./perlpipe_%08.8x_%d.com",mypid,index);
3729 fp = fopen(file,"w");
3733 if (!fp) return 0; /* we're hosed */
3735 fprintf(fp,"$! 'f$verify(0)'\n");
3736 fprintf(fp,"$! --- protect against nonstandard definitions ---\n");
3737 fprintf(fp,"$ perl_cfile = f$environment(\"procedure\")\n");
3738 fprintf(fp,"$ perl_define = \"define/nolog\"\n");
3739 fprintf(fp,"$ perl_on = \"set noon\"\n");
3740 fprintf(fp,"$ perl_exit = \"exit\"\n");
3741 fprintf(fp,"$ perl_del = \"delete\"\n");
3742 fprintf(fp,"$ pif = \"if\"\n");
3743 fprintf(fp,"$! --- define i/o redirection (sys$output set by lib$spawn)\n");
3744 fprintf(fp,"$ pif perl_popen_in .nes. \"\" then perl_define/user/name_attributes=confine sys$input 'perl_popen_in'\n");
3745 fprintf(fp,"$ pif perl_popen_err .nes. \"\" then perl_define/user/name_attributes=confine sys$error 'perl_popen_err'\n");
3746 fprintf(fp,"$ pif perl_popen_out .nes. \"\" then perl_define sys$output 'perl_popen_out'\n");
3747 fprintf(fp,"$! --- build command line to get max possible length\n");
3748 fprintf(fp,"$c=perl_popen_cmd0\n");
3749 fprintf(fp,"$c=c+perl_popen_cmd1\n");
3750 fprintf(fp,"$c=c+perl_popen_cmd2\n");
3751 fprintf(fp,"$x=perl_popen_cmd3\n");
3752 fprintf(fp,"$c=c+x\n");
3753 fprintf(fp,"$ perl_on\n");
3754 fprintf(fp,"$ 'c'\n");
3755 fprintf(fp,"$ perl_status = $STATUS\n");
3756 fprintf(fp,"$ perl_del 'perl_cfile'\n");
3757 fprintf(fp,"$ perl_exit 'perl_status'\n");
3760 fgetname(fp, file, 1);
3761 fstat(fileno(fp), (struct stat *)&s0);
3764 if (decc_filename_unix_only)
3765 do_tounixspec(file, file, 0, NULL);
3766 fp = fopen(file,"r","shr=get");
3768 fstat(fileno(fp), (struct stat *)&s1);
3770 cmp_result = VMS_INO_T_COMPARE(s0.crtl_stat.st_ino, s1.crtl_stat.st_ino);
3771 if ((cmp_result != 0) && (s0.st_ctime != s1.st_ctime)) {
3780 static int vms_is_syscommand_xterm(void)
3782 const static struct dsc$descriptor_s syscommand_dsc =
3783 { 11, DSC$K_DTYPE_T, DSC$K_CLASS_S, "SYS$COMMAND" };
3785 const static struct dsc$descriptor_s decwdisplay_dsc =
3786 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "DECW$DISPLAY" };
3788 struct item_list_3 items[2];
3789 unsigned short dvi_iosb[4];
3790 unsigned long devchar;
3791 unsigned long devclass;
3794 /* Very simple check to guess if sys$command is a decterm? */
3795 /* First see if the DECW$DISPLAY: device exists */
3797 items[0].code = DVI$_DEVCHAR;
3798 items[0].bufadr = &devchar;
3799 items[0].retadr = NULL;
3803 status = sys$getdviw
3804 (NO_EFN, 0, &decwdisplay_dsc, items, dvi_iosb, NULL, NULL, NULL);
3806 if ($VMS_STATUS_SUCCESS(status)) {
3807 status = dvi_iosb[0];
3810 if (!$VMS_STATUS_SUCCESS(status)) {
3811 SETERRNO(EVMSERR, status);
3815 /* If it does, then for now assume that we are on a workstation */
3816 /* Now verify that SYS$COMMAND is a terminal */
3817 /* for creating the debugger DECTerm */
3820 items[0].code = DVI$_DEVCLASS;
3821 items[0].bufadr = &devclass;
3822 items[0].retadr = NULL;
3826 status = sys$getdviw
3827 (NO_EFN, 0, &syscommand_dsc, items, dvi_iosb, NULL, NULL, NULL);
3829 if ($VMS_STATUS_SUCCESS(status)) {
3830 status = dvi_iosb[0];
3833 if (!$VMS_STATUS_SUCCESS(status)) {
3834 SETERRNO(EVMSERR, status);
3838 if (devclass == DC$_TERM) {
3845 /* If we are on a DECTerm, we can pretend to fork xterms when requested */
3846 static PerlIO * create_forked_xterm(pTHX_ const char *cmd, const char *mode)
3851 char device_name[65];
3852 unsigned short device_name_len;
3853 struct dsc$descriptor_s customization_dsc;
3854 struct dsc$descriptor_s device_name_dsc;
3857 char customization[200];
3861 unsigned short p_chan;
3863 unsigned short iosb[4];
3864 struct item_list_3 items[2];
3865 const char * cust_str =
3866 "DECW$TERMINAL.iconName:\tPerl Dbg\nDECW$TERMINAL.title:\t%40s\n";
3867 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3868 DSC$K_CLASS_S, mbx1};
3870 /* LIB$FIND_IMAGE_SIGNAL needs a handler */
3871 /*---------------------------------------*/
3872 VAXC$ESTABLISH((__vms_handler)LIB$SIG_TO_RET);
3875 /* Make sure that this is from the Perl debugger */
3876 ret_char = strstr(cmd," xterm ");
3877 if (ret_char == NULL)
3879 cptr = ret_char + 7;
3880 ret_char = strstr(cmd,"tty");
3881 if (ret_char == NULL)
3883 ret_char = strstr(cmd,"sleep");
3884 if (ret_char == NULL)
3887 if (decw_term_port == 0) {
3888 $DESCRIPTOR(filename1_dsc, "DECW$TERMINALSHR12");
3889 $DESCRIPTOR(filename2_dsc, "DECW$TERMINALSHR");
3890 $DESCRIPTOR(decw_term_port_dsc, "DECW$TERM_PORT");
3892 status = LIB$FIND_IMAGE_SYMBOL
3894 &decw_term_port_dsc,
3895 (void *)&decw_term_port,
3899 /* Try again with the other image name */
3900 if (!$VMS_STATUS_SUCCESS(status)) {
3902 status = LIB$FIND_IMAGE_SYMBOL
3904 &decw_term_port_dsc,
3905 (void *)&decw_term_port,
3914 /* No decw$term_port, give it up */
3915 if (!$VMS_STATUS_SUCCESS(status))
3918 /* Are we on a workstation? */
3919 /* to do: capture the rows / columns and pass their properties */
3920 ret_stat = vms_is_syscommand_xterm();
3924 /* Make the title: */
3925 ret_char = strstr(cptr,"-title");
3926 if (ret_char != NULL) {
3927 while ((*cptr != 0) && (*cptr != '\"')) {
3933 while ((*cptr != 0) && (*cptr != '\"')) {
3946 strcpy(title,"Perl Debug DECTerm");
3948 sprintf(customization, cust_str, title);
3950 customization_dsc.dsc$a_pointer = customization;
3951 customization_dsc.dsc$w_length = strlen(customization);
3952 customization_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
3953 customization_dsc.dsc$b_class = DSC$K_CLASS_S;
3955 device_name_dsc.dsc$a_pointer = device_name;
3956 device_name_dsc.dsc$w_length = sizeof device_name -1;
3957 device_name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
3958 device_name_dsc.dsc$b_class = DSC$K_CLASS_S;
3960 device_name_len = 0;
3962 /* Try to create the window */
3963 status = (*decw_term_port)
3972 if (!$VMS_STATUS_SUCCESS(status)) {
3973 SETERRNO(EVMSERR, status);
3977 device_name[device_name_len] = '\0';
3979 /* Need to set this up to look like a pipe for cleanup */
3981 status = lib$get_vm(&n, &info);
3982 if (!$VMS_STATUS_SUCCESS(status)) {
3983 SETERRNO(ENOMEM, status);
3989 info->completion = 0;
3990 info->closing = FALSE;
3997 info->in_done = TRUE;
3998 info->out_done = TRUE;
3999 info->err_done = TRUE;
4001 /* Assign a channel on this so that it will persist, and not login */
4002 /* We stash this channel in the info structure for reference. */
4003 /* The created xterm self destructs when the last channel is removed */
4004 /* and it appears that perl5db.pl (perl debugger) does this routinely */
4005 /* So leave this assigned. */
4006 device_name_dsc.dsc$w_length = device_name_len;
4007 status = sys$assign(&device_name_dsc,&info->xchan,0,0);
4008 if (!$VMS_STATUS_SUCCESS(status)) {
4009 SETERRNO(EVMSERR, status);
4012 info->xchan_valid = 1;
4014 /* Now create a mailbox to be read by the application */
4016 create_mbx(aTHX_ &p_chan, &d_mbx1);
4018 /* write the name of the created terminal to the mailbox */
4019 status = sys$qiow(NO_EFN, p_chan, IO$_WRITEVBLK|IO$M_NOW,
4020 iosb, NULL, NULL, device_name, device_name_len, 0, 0, 0, 0);
4022 if (!$VMS_STATUS_SUCCESS(status)) {
4023 SETERRNO(EVMSERR, status);
4027 info->fp = PerlIO_open(mbx1, mode);
4029 /* Done with this channel */
4032 /* If any errors, then clean up */
4035 _ckvmssts(lib$free_vm(&n, &info));
4044 safe_popen(pTHX_ const char *cmd, const char *in_mode, int *psts)
4046 static int handler_set_up = FALSE;
4047 unsigned long int sts, flags = CLI$M_NOWAIT;
4048 /* The use of a GLOBAL table (as was done previously) rendered
4049 * Perl's qx() or `` unusable from a C<$ SET SYMBOL/SCOPE=NOGLOBAL> DCL
4050 * environment. Hence we've switched to LOCAL symbol table.
4052 unsigned int table = LIB$K_CLI_LOCAL_SYM;
4054 char *p, mode[10], symbol[MAX_DCL_SYMBOL+1], *vmspipe;
4055 char *in, *out, *err, mbx[512];
4057 char tfilebuf[NAM$C_MAXRSS+1];
4059 char cmd_sym_name[20];
4060 struct dsc$descriptor_s d_symbol= {0, DSC$K_DTYPE_T,
4061 DSC$K_CLASS_S, symbol};
4062 struct dsc$descriptor_s vmspipedsc = {0, DSC$K_DTYPE_T,
4064 struct dsc$descriptor_s d_sym_cmd = {0, DSC$K_DTYPE_T,
4065 DSC$K_CLASS_S, cmd_sym_name};
4066 struct dsc$descriptor_s *vmscmd;
4067 $DESCRIPTOR(d_sym_in ,"PERL_POPEN_IN");
4068 $DESCRIPTOR(d_sym_out,"PERL_POPEN_OUT");
4069 $DESCRIPTOR(d_sym_err,"PERL_POPEN_ERR");
4071 /* Check here for Xterm create request. This means looking for
4072 * "3>&1 xterm\b" and "\btty 1>&3\b$" in the command, and that it
4073 * is possible to create an xterm.
4075 if (*in_mode == 'r') {
4078 xterm_fd = create_forked_xterm(aTHX_ cmd, in_mode);
4079 if (xterm_fd != Nullfp)
4083 if (!head_PLOC) store_pipelocs(aTHX); /* at least TRY to use a static vmspipe file */
4085 /* once-per-program initialization...
4086 note that the SETAST calls and the dual test of pipe_ef
4087 makes sure that only the FIRST thread through here does
4088 the initialization...all other threads wait until it's
4091 Yeah, uglier than a pthread call, it's got all the stuff inline
4092 rather than in a separate routine.
4096 _ckvmssts(sys$setast(0));
4098 unsigned long int pidcode = JPI$_PID;
4099 $DESCRIPTOR(d_delay, RETRY_DELAY);
4100 _ckvmssts(lib$get_ef(&pipe_ef));
4101 _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
4102 _ckvmssts(sys$bintim(&d_delay, delaytime));
4104 if (!handler_set_up) {
4105 _ckvmssts(sys$dclexh(&pipe_exitblock));
4106 handler_set_up = TRUE;
4108 _ckvmssts(sys$setast(1));
4111 /* see if we can find a VMSPIPE.COM */
4114 vmspipe = find_vmspipe(aTHX);
4116 strcpy(tfilebuf+1,vmspipe);
4117 } else { /* uh, oh...we're in tempfile hell */
4118 tpipe = vmspipe_tempfile(aTHX);
4119 if (!tpipe) { /* a fish popular in Boston */
4120 if (ckWARN(WARN_PIPE)) {
4121 Perl_warner(aTHX_ packWARN(WARN_PIPE),"unable to find VMSPIPE.COM for i/o piping");
4125 fgetname(tpipe,tfilebuf+1,1);
4127 vmspipedsc.dsc$a_pointer = tfilebuf;
4128 vmspipedsc.dsc$w_length = strlen(tfilebuf);
4130 sts = setup_cmddsc(aTHX_ cmd,0,0,&vmscmd);
4133 case RMS$_FNF: case RMS$_DNF:
4134 set_errno(ENOENT); break;
4136 set_errno(ENOTDIR); break;
4138 set_errno(ENODEV); break;
4140 set_errno(EACCES); break;
4142 set_errno(EINVAL); break;
4143 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
4144 set_errno(E2BIG); break;
4145 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
4146 _ckvmssts(sts); /* fall through */
4147 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
4150 set_vaxc_errno(sts);
4151 if (*in_mode != 'n' && ckWARN(WARN_PIPE)) {
4152 Perl_warner(aTHX_ packWARN(WARN_PIPE),"Can't pipe \"%*s\": %s", strlen(cmd), cmd, Strerror(errno));
4158 _ckvmssts(lib$get_vm(&n, &info));
4160 strcpy(mode,in_mode);
4163 info->completion = 0;
4164 info->closing = FALSE;
4171 info->in_done = TRUE;
4172 info->out_done = TRUE;
4173 info->err_done = TRUE;
4175 info->xchan_valid = 0;
4177 in = PerlMem_malloc(VMS_MAXRSS);
4178 if (in == NULL) _ckvmssts(SS$_INSFMEM);
4179 out = PerlMem_malloc(VMS_MAXRSS);
4180 if (out == NULL) _ckvmssts(SS$_INSFMEM);
4181 err = PerlMem_malloc(VMS_MAXRSS);
4182 if (err == NULL) _ckvmssts(SS$_INSFMEM);
4184 in[0] = out[0] = err[0] = '\0';
4186 if ((p = strchr(mode,'F')) != NULL) { /* F -> use FILE* */
4190 if ((p = strchr(mode,'W')) != NULL) { /* W -> wait for completion */
4195 if (*mode == 'r') { /* piping from subroutine */
4197 info->out = pipe_infromchild_setup(aTHX_ mbx,out);
4199 info->out->pipe_done = &info->out_done;
4200 info->out_done = FALSE;
4201 info->out->info = info;
4203 if (!info->useFILE) {
4204 info->fp = PerlIO_open(mbx, mode);
4206 info->fp = (PerlIO *) freopen(mbx, mode, stdin);
4207 Perl_vmssetuserlnm(aTHX_ "SYS$INPUT",mbx);
4210 if (!info->fp && info->out) {
4211 sys$cancel(info->out->chan_out);
4213 while (!info->out_done) {
4215 _ckvmssts(sys$setast(0));
4216 done = info->out_done;
4217 if (!done) _ckvmssts(sys$clref(pipe_ef));
4218 _ckvmssts(sys$setast(1));
4219 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4222 if (info->out->buf) {
4223 n = info->out->bufsize * sizeof(char);
4224 _ckvmssts(lib$free_vm(&n, &info->out->buf));
4227 _ckvmssts(lib$free_vm(&n, &info->out));
4229 _ckvmssts(lib$free_vm(&n, &info));
4234 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4236 info->err->pipe_done = &info->err_done;
4237 info->err_done = FALSE;
4238 info->err->info = info;
4241 } else if (*mode == 'w') { /* piping to subroutine */
4243 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
4245 info->out->pipe_done = &info->out_done;
4246 info->out_done = FALSE;
4247 info->out->info = info;
4250 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4252 info->err->pipe_done = &info->err_done;
4253 info->err_done = FALSE;
4254 info->err->info = info;
4257 info->in = pipe_tochild_setup(aTHX_ in,mbx);
4258 if (!info->useFILE) {
4259 info->fp = PerlIO_open(mbx, mode);
4261 info->fp = (PerlIO *) freopen(mbx, mode, stdout);
4262 Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",mbx);
4266 info->in->pipe_done = &info->in_done;
4267 info->in_done = FALSE;
4268 info->in->info = info;
4272 if (!info->fp && info->in) {
4274 _ckvmssts(sys$qiow(0,info->in->chan_in, IO$_WRITEOF, 0,
4275 0, 0, 0, 0, 0, 0, 0, 0));
4277 while (!info->in_done) {
4279 _ckvmssts(sys$setast(0));
4280 done = info->in_done;
4281 if (!done) _ckvmssts(sys$clref(pipe_ef));
4282 _ckvmssts(sys$setast(1));
4283 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4286 if (info->in->buf) {
4287 n = info->in->bufsize * sizeof(char);
4288 _ckvmssts(lib$free_vm(&n, &info->in->buf));
4291 _ckvmssts(lib$free_vm(&n, &info->in));
4293 _ckvmssts(lib$free_vm(&n, &info));
4299 } else if (*mode == 'n') { /* separate subprocess, no Perl i/o */
4300 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
4302 info->out->pipe_done = &info->out_done;
4303 info->out_done = FALSE;
4304 info->out->info = info;
4307 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4309 info->err->pipe_done = &info->err_done;
4310 info->err_done = FALSE;
4311 info->err->info = info;
4315 symbol[MAX_DCL_SYMBOL] = '\0';
4317 strncpy(symbol, in, MAX_DCL_SYMBOL);
4318 d_symbol.dsc$w_length = strlen(symbol);
4319 _ckvmssts(lib$set_symbol(&d_sym_in, &d_symbol, &table));
4321 strncpy(symbol, err, MAX_DCL_SYMBOL);
4322 d_symbol.dsc$w_length = strlen(symbol);
4323 _ckvmssts(lib$set_symbol(&d_sym_err, &d_symbol, &table));
4325 strncpy(symbol, out, MAX_DCL_SYMBOL);
4326 d_symbol.dsc$w_length = strlen(symbol);
4327 _ckvmssts(lib$set_symbol(&d_sym_out, &d_symbol, &table));
4329 /* Done with the names for the pipes */
4334 p = vmscmd->dsc$a_pointer;
4335 while (*p == ' ' || *p == '\t') p++; /* remove leading whitespace */
4336 if (*p == '$') p++; /* remove leading $ */
4337 while (*p == ' ' || *p == '\t') p++;
4339 for (j = 0; j < 4; j++) {
4340 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
4341 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
4343 strncpy(symbol, p, MAX_DCL_SYMBOL);
4344 d_symbol.dsc$w_length = strlen(symbol);
4345 _ckvmssts(lib$set_symbol(&d_sym_cmd, &d_symbol, &table));
4347 if (strlen(p) > MAX_DCL_SYMBOL) {
4348 p += MAX_DCL_SYMBOL;
4353 _ckvmssts(sys$setast(0));
4354 info->next=open_pipes; /* prepend to list */
4356 _ckvmssts(sys$setast(1));
4357 /* Omit arg 2 (input file) so the child will get the parent's SYS$INPUT
4358 * and SYS$COMMAND. vmspipe.com will redefine SYS$INPUT, but we'll still
4359 * have SYS$COMMAND if we need it.
4361 _ckvmssts(lib$spawn(&vmspipedsc, 0, &nl_desc, &flags,
4362 0, &info->pid, &info->completion,
4363 0, popen_completion_ast,info,0,0,0));
4365 /* if we were using a tempfile, close it now */
4367 if (tpipe) fclose(tpipe);
4369 /* once the subprocess is spawned, it has copied the symbols and
4370 we can get rid of ours */
4372 for (j = 0; j < 4; j++) {
4373 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
4374 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
4375 _ckvmssts(lib$delete_symbol(&d_sym_cmd, &table));
4377 _ckvmssts(lib$delete_symbol(&d_sym_in, &table));
4378 _ckvmssts(lib$delete_symbol(&d_sym_err, &table));
4379 _ckvmssts(lib$delete_symbol(&d_sym_out, &table));
4380 vms_execfree(vmscmd);
4382 #ifdef PERL_IMPLICIT_CONTEXT
4385 PL_forkprocess = info->pid;
4390 _ckvmssts(sys$setast(0));
4392 if (!done) _ckvmssts(sys$clref(pipe_ef));
4393 _ckvmssts(sys$setast(1));
4394 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4396 *psts = info->completion;
4397 /* Caller thinks it is open and tries to close it. */
4398 /* This causes some problems, as it changes the error status */
4399 /* my_pclose(info->fp); */
4404 } /* end of safe_popen */
4407 /*{{{ PerlIO *my_popen(char *cmd, char *mode)*/
4409 Perl_my_popen(pTHX_ const char *cmd, const char *mode)
4413 TAINT_PROPER("popen");
4414 PERL_FLUSHALL_FOR_CHILD;
4415 return safe_popen(aTHX_ cmd,mode,&sts);
4420 /*{{{ I32 my_pclose(PerlIO *fp)*/
4421 I32 Perl_my_pclose(pTHX_ PerlIO *fp)
4423 pInfo info, last = NULL;
4424 unsigned long int retsts;
4428 for (info = open_pipes; info != NULL; last = info, info = info->next)
4429 if (info->fp == fp) break;
4431 if (info == NULL) { /* no such pipe open */
4432 set_errno(ECHILD); /* quoth POSIX */
4433 set_vaxc_errno(SS$_NONEXPR);
4437 /* If we were writing to a subprocess, insure that someone reading from
4438 * the mailbox gets an EOF. It looks like a simple fclose() doesn't
4439 * produce an EOF record in the mailbox.
4441 * well, at least sometimes it *does*, so we have to watch out for
4442 * the first EOF closing the pipe (and DASSGN'ing the channel)...
4446 #if defined(USE_ITHREADS)
4449 && PL_perlio_fd_refcnt)
4450 PerlIO_flush(info->fp);
4452 fflush((FILE *)info->fp);
4455 _ckvmssts(sys$setast(0));
4456 info->closing = TRUE;
4457 done = info->done && info->in_done && info->out_done && info->err_done;
4458 /* hanging on write to Perl's input? cancel it */
4459 if (info->mode == 'r' && info->out && !info->out_done) {
4460 if (info->out->chan_out) {
4461 _ckvmssts(sys$cancel(info->out->chan_out));
4462 if (!info->out->chan_in) { /* EOF generation, need AST */
4463 _ckvmssts(sys$dclast(pipe_infromchild_ast,info->out,0));
4467 if (info->in && !info->in_done && !info->in->shut_on_empty) /* EOF if hasn't had one yet */
4468 _ckvmssts(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
4470 _ckvmssts(sys$setast(1));
4473 #if defined(USE_ITHREADS)
4476 && PL_perlio_fd_refcnt)
4477 PerlIO_close(info->fp);
4479 fclose((FILE *)info->fp);
4482 we have to wait until subprocess completes, but ALSO wait until all
4483 the i/o completes...otherwise we'll be freeing the "info" structure
4484 that the i/o ASTs could still be using...
4488 _ckvmssts(sys$setast(0));
4489 done = info->done && info->in_done && info->out_done && info->err_done;
4490 if (!done) _ckvmssts(sys$clref(pipe_ef));
4491 _ckvmssts(sys$setast(1));
4492 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4494 retsts = info->completion;
4496 /* remove from list of open pipes */
4497 _ckvmssts(sys$setast(0));
4498 if (last) last->next = info->next;
4499 else open_pipes = info->next;
4500 _ckvmssts(sys$setast(1));
4502 /* free buffers and structures */
4505 if (info->in->buf) {
4506 n = info->in->bufsize * sizeof(char);
4507 _ckvmssts(lib$free_vm(&n, &info->in->buf));
4510 _ckvmssts(lib$free_vm(&n, &info->in));
4513 if (info->out->buf) {
4514 n = info->out->bufsize * sizeof(char);
4515 _ckvmssts(lib$free_vm(&n, &info->out->buf));
4518 _ckvmssts(lib$free_vm(&n, &info->out));
4521 if (info->err->buf) {
4522 n = info->err->bufsize * sizeof(char);
4523 _ckvmssts(lib$free_vm(&n, &info->err->buf));
4526 _ckvmssts(lib$free_vm(&n, &info->err));
4529 _ckvmssts(lib$free_vm(&n, &info));
4533 } /* end of my_pclose() */
4535 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
4536 /* Roll our own prototype because we want this regardless of whether
4537 * _VMS_WAIT is defined.
4539 __pid_t __vms_waitpid( __pid_t __pid, int *__stat_loc, int __options );
4541 /* sort-of waitpid; special handling of pipe clean-up for subprocesses
4542 created with popen(); otherwise partially emulate waitpid() unless
4543 we have a suitable one from the CRTL that came with VMS 7.2 and later.
4544 Also check processes not considered by the CRTL waitpid().
4546 /*{{{Pid_t my_waitpid(Pid_t pid, int *statusp, int flags)*/
4548 Perl_my_waitpid(pTHX_ Pid_t pid, int *statusp, int flags)
4555 if (statusp) *statusp = 0;
4557 for (info = open_pipes; info != NULL; info = info->next)
4558 if (info->pid == pid) break;
4560 if (info != NULL) { /* we know about this child */
4561 while (!info->done) {
4562 _ckvmssts(sys$setast(0));
4564 if (!done) _ckvmssts(sys$clref(pipe_ef));
4565 _ckvmssts(sys$setast(1));
4566 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4569 if (statusp) *statusp = info->completion;
4573 /* child that already terminated? */
4575 for (j = 0; j < NKEEPCLOSED && j < closed_num; j++) {
4576 if (closed_list[j].pid == pid) {
4577 if (statusp) *statusp = closed_list[j].completion;
4582 /* fall through if this child is not one of our own pipe children */
4584 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
4586 /* waitpid() became available in the CRTL as of VMS 7.0, but only
4587 * in 7.2 did we get a version that fills in the VMS completion
4588 * status as Perl has always tried to do.
4591 sts = __vms_waitpid( pid, statusp, flags );
4593 if ( sts == 0 || !(sts == -1 && errno == ECHILD) )
4596 /* If the real waitpid tells us the child does not exist, we
4597 * fall through here to implement waiting for a child that
4598 * was created by some means other than exec() (say, spawned
4599 * from DCL) or to wait for a process that is not a subprocess
4600 * of the current process.
4603 #endif /* defined(__CRTL_VER) && __CRTL_VER >= 70200000 */
4606 $DESCRIPTOR(intdsc,"0 00:00:01");
4607 unsigned long int ownercode = JPI$_OWNER, ownerpid;
4608 unsigned long int pidcode = JPI$_PID, mypid;
4609 unsigned long int interval[2];
4610 unsigned int jpi_iosb[2];
4611 struct itmlst_3 jpilist[2] = {
4612 {sizeof(ownerpid), JPI$_OWNER, &ownerpid, 0},
4617 /* Sorry folks, we don't presently implement rooting around for
4618 the first child we can find, and we definitely don't want to
4619 pass a pid of -1 to $getjpi, where it is a wildcard operation.
4625 /* Get the owner of the child so I can warn if it's not mine. If the
4626 * process doesn't exist or I don't have the privs to look at it,
4627 * I can go home early.
4629 sts = sys$getjpiw(0,&pid,NULL,&jpilist,&jpi_iosb,NULL,NULL);
4630 if (sts & 1) sts = jpi_iosb[0];
4642 set_vaxc_errno(sts);
4646 if (ckWARN(WARN_EXEC)) {
4647 /* remind folks they are asking for non-standard waitpid behavior */
4648 _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
4649 if (ownerpid != mypid)
4650 Perl_warner(aTHX_ packWARN(WARN_EXEC),
4651 "waitpid: process %x is not a child of process %x",
4655 /* simply check on it once a second until it's not there anymore. */
4657 _ckvmssts(sys$bintim(&intdsc,interval));
4658 while ((sts=lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0)) & 1) {
4659 _ckvmssts(sys$schdwk(0,0,interval,0));
4660 _ckvmssts(sys$hiber());
4662 if (sts == SS$_NONEXPR) sts = SS$_NORMAL;
4667 } /* end of waitpid() */
4672 /*{{{ char *my_gconvert(double val, int ndig, int trail, char *buf) */
4674 my_gconvert(double val, int ndig, int trail, char *buf)
4676 static char __gcvtbuf[DBL_DIG+1];
4679 loc = buf ? buf : __gcvtbuf;
4681 #ifndef __DECC /* VAXCRTL gcvt uses E format for numbers < 1 */
4683 sprintf(loc,"%.*g",ndig,val);
4689 if (!buf && ndig > DBL_DIG) ndig = DBL_DIG;
4690 return gcvt(val,ndig,loc);
4693 loc[0] = '0'; loc[1] = '\0';
4700 #if defined(__VAX) || !defined(NAML$C_MAXRSS)
4701 static int rms_free_search_context(struct FAB * fab)
4705 nam = fab->fab$l_nam;
4706 nam->nam$b_nop |= NAM$M_SYNCHK;
4707 nam->nam$l_rlf = NULL;
4709 return sys$parse(fab, NULL, NULL);
4712 #define rms_setup_nam(nam) struct NAM nam = cc$rms_nam
4713 #define rms_clear_nam_nop(nam) nam.nam$b_nop = 0;
4714 #define rms_set_nam_nop(nam, opt) nam.nam$b_nop |= (opt)
4715 #define rms_set_nam_fnb(nam, opt) nam.nam$l_fnb |= (opt)
4716 #define rms_is_nam_fnb(nam, opt) (nam.nam$l_fnb & (opt))
4717 #define rms_nam_esll(nam) nam.nam$b_esl
4718 #define rms_nam_esl(nam) nam.nam$b_esl
4719 #define rms_nam_name(nam) nam.nam$l_name
4720 #define rms_nam_namel(nam) nam.nam$l_name
4721 #define rms_nam_type(nam) nam.nam$l_type
4722 #define rms_nam_typel(nam) nam.nam$l_type
4723 #define rms_nam_ver(nam) nam.nam$l_ver
4724 #define rms_nam_verl(nam) nam.nam$l_ver
4725 #define rms_nam_rsll(nam) nam.nam$b_rsl
4726 #define rms_nam_rsl(nam) nam.nam$b_rsl
4727 #define rms_bind_fab_nam(fab, nam) fab.fab$l_nam = &nam
4728 #define rms_set_fna(fab, nam, name, size) \
4729 { fab.fab$b_fns = size; fab.fab$l_fna = name; }
4730 #define rms_get_fna(fab, nam) fab.fab$l_fna
4731 #define rms_set_dna(fab, nam, name, size) \
4732 { fab.fab$b_dns = size; fab.fab$l_dna = name; }
4733 #define rms_nam_dns(fab, nam) fab.fab$b_dns
4734 #define rms_set_esa(fab, nam, name, size) \
4735 { nam.nam$b_ess = size; nam.nam$l_esa = name; }
4736 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
4737 { nam.nam$l_esa = s_name; nam.nam$b_ess = s_size;}
4738 #define rms_set_rsa(nam, name, size) \
4739 { nam.nam$l_rsa = name; nam.nam$b_rss = size; }
4740 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
4741 { nam.nam$l_rsa = s_name; nam.nam$b_rss = s_size; }
4742 #define rms_nam_name_type_l_size(nam) \
4743 (nam.nam$b_name + nam.nam$b_type)
4745 static int rms_free_search_context(struct FAB * fab)
4749 nam = fab->fab$l_naml;
4750 nam->naml$b_nop |= NAM$M_SYNCHK;
4751 nam->naml$l_rlf = NULL;
4752 nam->naml$l_long_defname_size = 0;
4755 return sys$parse(fab, NULL, NULL);
4758 #define rms_setup_nam(nam) struct NAML nam = cc$rms_naml
4759 #define rms_clear_nam_nop(nam) nam.naml$b_nop = 0;
4760 #define rms_set_nam_nop(nam, opt) nam.naml$b_nop |= (opt)
4761 #define rms_set_nam_fnb(nam, opt) nam.naml$l_fnb |= (opt)
4762 #define rms_is_nam_fnb(nam, opt) (nam.naml$l_fnb & (opt))
4763 #define rms_nam_esll(nam) nam.naml$l_long_expand_size
4764 #define rms_nam_esl(nam) nam.naml$b_esl
4765 #define rms_nam_name(nam) nam.naml$l_name
4766 #define rms_nam_namel(nam) nam.naml$l_long_name
4767 #define rms_nam_type(nam) nam.naml$l_type
4768 #define rms_nam_typel(nam) nam.naml$l_long_type
4769 #define rms_nam_ver(nam) nam.naml$l_ver
4770 #define rms_nam_verl(nam) nam.naml$l_long_ver
4771 #define rms_nam_rsll(nam) nam.naml$l_long_result_size
4772 #define rms_nam_rsl(nam) nam.naml$b_rsl
4773 #define rms_bind_fab_nam(fab, nam) fab.fab$l_naml = &nam
4774 #define rms_set_fna(fab, nam, name, size) \
4775 { fab.fab$b_fns = 0; fab.fab$l_fna = (char *) -1; \
4776 nam.naml$l_long_filename_size = size; \
4777 nam.naml$l_long_filename = name;}
4778 #define rms_get_fna(fab, nam) nam.naml$l_long_filename
4779 #define rms_set_dna(fab, nam, name, size) \
4780 { fab.fab$b_dns = 0; fab.fab$l_dna = (char *) -1; \
4781 nam.naml$l_long_defname_size = size; \
4782 nam.naml$l_long_defname = name; }
4783 #define rms_nam_dns(fab, nam) nam.naml$l_long_defname_size
4784 #define rms_set_esa(fab, nam, name, size) \
4785 { nam.naml$b_ess = 0; nam.naml$l_esa = (char *) -1; \
4786 nam.naml$l_long_expand_alloc = size; \
4787 nam.naml$l_long_expand = name; }
4788 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
4789 { nam.naml$l_esa = s_name; nam.naml$b_ess = s_size; \
4790 nam.naml$l_long_expand = l_name; \
4791 nam.naml$l_long_expand_alloc = l_size; }
4792 #define rms_set_rsa(nam, name, size) \
4793 { nam.naml$l_rsa = NULL; nam.naml$b_rss = 0; \
4794 nam.naml$l_long_result = name; \
4795 nam.naml$l_long_result_alloc = size; }
4796 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
4797 { nam.naml$l_rsa = s_name; nam.naml$b_rss = s_size; \
4798 nam.naml$l_long_result = l_name; \
4799 nam.naml$l_long_result_alloc = l_size; }
4800 #define rms_nam_name_type_l_size(nam) \
4801 (nam.naml$l_long_name_size + nam.naml$l_long_type_size)
4805 /*{{{char *do_rmsexpand(char *fspec, char *out, int ts, char *def, unsigned opts)*/
4806 /* Shortcut for common case of simple calls to $PARSE and $SEARCH
4807 * to expand file specification. Allows for a single default file
4808 * specification and a simple mask of options. If outbuf is non-NULL,
4809 * it must point to a buffer at least NAM$C_MAXRSS bytes long, into which
4810 * the resultant file specification is placed. If outbuf is NULL, the
4811 * resultant file specification is placed into a static buffer.
4812 * The third argument, if non-NULL, is taken to be a default file
4813 * specification string. The fourth argument is unused at present.
4814 * rmesexpand() returns the address of the resultant string if
4815 * successful, and NULL on error.
4817 * New functionality for previously unused opts value:
4818 * PERL_RMSEXPAND_M_VMS - Force output file specification to VMS format.
4819 * PERL_RMSEXPAND_M_LONG - Want output in long formst
4820 * PERL_RMSEXPAND_M_VMS_IN - Input is already in VMS, so do not vmsify
4822 static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *);
4826 (pTHX_ const char *filespec,
4829 const char *defspec,
4834 static char __rmsexpand_retbuf[VMS_MAXRSS];
4835 char * vmsfspec, *tmpfspec;
4836 char * esa, *cp, *out = NULL;
4840 struct FAB myfab = cc$rms_fab;
4841 rms_setup_nam(mynam);
4843 unsigned long int retsts, trimver, trimtype, haslower = 0, isunix = 0;
4846 /* temp hack until UTF8 is actually implemented */
4847 if (fs_utf8 != NULL)
4850 if (!filespec || !*filespec) {
4851 set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL);
4855 if (ts) out = Newx(outbuf,VMS_MAXRSS,char);
4856 else outbuf = __rmsexpand_retbuf;
4864 if ((opts & PERL_RMSEXPAND_M_VMS_IN) == 0) {
4865 isunix = is_unix_filespec(filespec);
4867 vmsfspec = PerlMem_malloc(VMS_MAXRSS);
4868 if (vmsfspec == NULL) _ckvmssts(SS$_INSFMEM);
4869 if (do_tovmsspec(filespec,vmsfspec,0,fs_utf8) == NULL) {
4870 PerlMem_free(vmsfspec);
4875 filespec = vmsfspec;
4877 /* Unless we are forcing to VMS format, a UNIX input means
4878 * UNIX output, and that requires long names to be used
4880 if ((opts & PERL_RMSEXPAND_M_VMS) == 0)
4881 opts |= PERL_RMSEXPAND_M_LONG;
4888 rms_set_fna(myfab, mynam, (char *)filespec, strlen(filespec)); /* cast ok */
4889 rms_bind_fab_nam(myfab, mynam);
4891 if (defspec && *defspec) {
4893 t_isunix = is_unix_filespec(defspec);
4895 tmpfspec = PerlMem_malloc(VMS_MAXRSS);
4896 if (tmpfspec == NULL) _ckvmssts(SS$_INSFMEM);
4897 if (do_tovmsspec(defspec,tmpfspec,0,dfs_utf8) == NULL) {
4898 PerlMem_free(tmpfspec);
4899 if (vmsfspec != NULL)
4900 PerlMem_free(vmsfspec);
4907 rms_set_dna(myfab, mynam, (char *)defspec, strlen(defspec)); /* cast ok */
4910 esa = PerlMem_malloc(NAM$C_MAXRSS + 1);
4911 if (esa == NULL) _ckvmssts(SS$_INSFMEM);
4912 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
4913 esal = PerlMem_malloc(VMS_MAXRSS);
4914 if (esal == NULL) _ckvmssts(SS$_INSFMEM);
4916 rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS-1);
4918 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
4919 rms_set_rsa(mynam, outbuf, (VMS_MAXRSS - 1));
4922 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
4923 outbufl = PerlMem_malloc(VMS_MAXRSS);
4924 if (outbufl == NULL) _ckvmssts(SS$_INSFMEM);
4925 rms_set_rsal(mynam, outbuf, NAM$C_MAXRSS, outbufl, (VMS_MAXRSS - 1));
4927 rms_set_rsa(mynam, outbuf, NAM$C_MAXRSS);
4931 #ifdef NAM$M_NO_SHORT_UPCASE
4932 if (decc_efs_case_preserve)
4933 rms_set_nam_nop(mynam, NAM$M_NO_SHORT_UPCASE);
4936 /* First attempt to parse as an existing file */
4937 retsts = sys$parse(&myfab,0,0);
4938 if (!(retsts & STS$K_SUCCESS)) {
4940 /* Could not find the file, try as syntax only if error is not fatal */
4941 rms_set_nam_nop(mynam, NAM$M_SYNCHK);
4942 if (retsts == RMS$_DNF || retsts == RMS$_DIR || retsts == RMS$_DEV) {
4943 retsts = sys$parse(&myfab,0,0);
4944 if (retsts & STS$K_SUCCESS) goto expanded;
4947 /* Still could not parse the file specification */
4948 /*----------------------------------------------*/
4949 sts = rms_free_search_context(&myfab); /* Free search context */
4950 if (out) Safefree(out);
4951 if (tmpfspec != NULL)
4952 PerlMem_free(tmpfspec);
4953 if (vmsfspec != NULL)
4954 PerlMem_free(vmsfspec);
4955 if (outbufl != NULL)
4956 PerlMem_free(outbufl);
4960 set_vaxc_errno(retsts);
4961 if (retsts == RMS$_PRV) set_errno(EACCES);
4962 else if (retsts == RMS$_DEV) set_errno(ENODEV);
4963 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
4964 else set_errno(EVMSERR);
4967 retsts = sys$search(&myfab,0,0);
4968 if (!(retsts & STS$K_SUCCESS) && retsts != RMS$_FNF) {
4969 sts = rms_free_search_context(&myfab); /* Free search context */
4970 if (out) Safefree(out);
4971 if (tmpfspec != NULL)
4972 PerlMem_free(tmpfspec);
4973 if (vmsfspec != NULL)
4974 PerlMem_free(vmsfspec);
4975 if (outbufl != NULL)
4976 PerlMem_free(outbufl);
4980 set_vaxc_errno(retsts);
4981 if (retsts == RMS$_PRV) set_errno(EACCES);
4982 else set_errno(EVMSERR);
4986 /* If the input filespec contained any lowercase characters,
4987 * downcase the result for compatibility with Unix-minded code. */
4989 if (!decc_efs_case_preserve) {
4990 for (tbuf = rms_get_fna(myfab, mynam); *tbuf; tbuf++)
4991 if (islower(*tbuf)) { haslower = 1; break; }
4994 /* Is a long or a short name expected */
4995 /*------------------------------------*/
4996 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
4997 if (rms_nam_rsll(mynam)) {
4999 speclen = rms_nam_rsll(mynam);
5002 tbuf = esal; /* Not esa */
5003 speclen = rms_nam_esll(mynam);
5007 if (rms_nam_rsl(mynam)) {
5009 speclen = rms_nam_rsl(mynam);
5012 tbuf = esa; /* Not esal */
5013 speclen = rms_nam_esl(mynam);
5016 tbuf[speclen] = '\0';
5018 /* Trim off null fields added by $PARSE
5019 * If type > 1 char, must have been specified in original or default spec
5020 * (not true for version; $SEARCH may have added version of existing file).
5022 trimver = !rms_is_nam_fnb(mynam, NAM$M_EXP_VER);
5023 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5024 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
5025 ((rms_nam_verl(mynam) - rms_nam_typel(mynam)) == 1);
5028 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
5029 ((rms_nam_ver(mynam) - rms_nam_type(mynam)) == 1);
5031 if (trimver || trimtype) {
5032 if (defspec && *defspec) {
5033 char *defesal = NULL;
5034 defesal = PerlMem_malloc(VMS_MAXRSS + 1);
5035 if (defesal != NULL) {
5036 struct FAB deffab = cc$rms_fab;
5037 rms_setup_nam(defnam);
5039 rms_bind_fab_nam(deffab, defnam);
5043 (deffab, defnam, (char *)defspec, rms_nam_dns(myfab, mynam));
5045 rms_set_esa(deffab, defnam, defesal, VMS_MAXRSS - 1);
5047 rms_clear_nam_nop(defnam);
5048 rms_set_nam_nop(defnam, NAM$M_SYNCHK);
5049 #ifdef NAM$M_NO_SHORT_UPCASE
5050 if (decc_efs_case_preserve)
5051 rms_set_nam_nop(defnam, NAM$M_NO_SHORT_UPCASE);
5053 if (sys$parse(&deffab,0,0) & STS$K_SUCCESS) {
5055 trimver = !rms_is_nam_fnb(defnam, NAM$M_EXP_VER);
5058 trimtype = !rms_is_nam_fnb(defnam, NAM$M_EXP_TYPE);
5061 PerlMem_free(defesal);
5065 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5066 if (*(rms_nam_verl(mynam)) != '\"')
5067 speclen = rms_nam_verl(mynam) - tbuf;
5070 if (*(rms_nam_ver(mynam)) != '\"')
5071 speclen = rms_nam_ver(mynam) - tbuf;
5075 /* If we didn't already trim version, copy down */
5076 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5077 if (speclen > rms_nam_verl(mynam) - tbuf)
5079 (rms_nam_typel(mynam),
5080 rms_nam_verl(mynam),
5081 speclen - (rms_nam_verl(mynam) - tbuf));
5082 speclen -= rms_nam_verl(mynam) - rms_nam_typel(mynam);
5085 if (speclen > rms_nam_ver(mynam) - tbuf)
5087 (rms_nam_type(mynam),
5089 speclen - (rms_nam_ver(mynam) - tbuf));
5090 speclen -= rms_nam_ver(mynam) - rms_nam_type(mynam);
5095 /* Done with these copies of the input files */
5096 /*-------------------------------------------*/
5097 if (vmsfspec != NULL)
5098 PerlMem_free(vmsfspec);
5099 if (tmpfspec != NULL)
5100 PerlMem_free(tmpfspec);
5102 /* If we just had a directory spec on input, $PARSE "helpfully"
5103 * adds an empty name and type for us */
5104 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5105 if (rms_nam_namel(mynam) == rms_nam_typel(mynam) &&
5106 rms_nam_verl(mynam) == rms_nam_typel(mynam) + 1 &&
5107 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
5108 speclen = rms_nam_namel(mynam) - tbuf;
5111 if (rms_nam_name(mynam) == rms_nam_type(mynam) &&
5112 rms_nam_ver(mynam) == rms_nam_ver(mynam) + 1 &&
5113 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
5114 speclen = rms_nam_name(mynam) - tbuf;
5117 /* Posix format specifications must have matching quotes */
5118 if (speclen < (VMS_MAXRSS - 1)) {
5119 if (decc_posix_compliant_pathnames && (tbuf[0] == '\"')) {
5120 if ((speclen > 1) && (tbuf[speclen-1] != '\"')) {
5121 tbuf[speclen] = '\"';
5126 tbuf[speclen] = '\0';
5127 if (haslower && !decc_efs_case_preserve) __mystrtolower(tbuf);
5129 /* Have we been working with an expanded, but not resultant, spec? */
5130 /* Also, convert back to Unix syntax if necessary. */
5132 if (!rms_nam_rsll(mynam)) {
5134 if (do_tounixspec(esa,outbuf,0,fs_utf8) == NULL) {
5135 if (out) Safefree(out);
5139 if (outbufl != NULL)
5140 PerlMem_free(outbufl);
5144 else strcpy(outbuf,esa);
5147 tmpfspec = PerlMem_malloc(VMS_MAXRSS);
5148 if (tmpfspec == NULL) _ckvmssts(SS$_INSFMEM);
5149 if (do_tounixspec(outbuf,tmpfspec,0,fs_utf8) == NULL) {
5150 if (out) Safefree(out);
5154 PerlMem_free(tmpfspec);
5155 if (outbufl != NULL)
5156 PerlMem_free(outbufl);
5159 strcpy(outbuf,tmpfspec);
5160 PerlMem_free(tmpfspec);
5163 rms_set_rsal(mynam, NULL, 0, NULL, 0);
5164 sts = rms_free_search_context(&myfab); /* Free search context */
5168 if (outbufl != NULL)
5169 PerlMem_free(outbufl);
5173 /* External entry points */
5174 char *Perl_rmsexpand(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
5175 { return do_rmsexpand(spec,buf,0,def,opt,NULL,NULL); }
5176 char *Perl_rmsexpand_ts(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
5177 { return do_rmsexpand(spec,buf,1,def,opt,NULL,NULL); }
5178 char *Perl_rmsexpand_utf8
5179 (pTHX_ const char *spec, char *buf, const char *def,
5180 unsigned opt, int * fs_utf8, int * dfs_utf8)
5181 { return do_rmsexpand(spec,buf,0,def,opt, fs_utf8, dfs_utf8); }
5182 char *Perl_rmsexpand_utf8_ts
5183 (pTHX_ const char *spec, char *buf, const char *def,
5184 unsigned opt, int * fs_utf8, int * dfs_utf8)
5185 { return do_rmsexpand(spec,buf,1,def,opt, fs_utf8, dfs_utf8); }
5189 ** The following routines are provided to make life easier when
5190 ** converting among VMS-style and Unix-style directory specifications.
5191 ** All will take input specifications in either VMS or Unix syntax. On
5192 ** failure, all return NULL. If successful, the routines listed below
5193 ** return a pointer to a buffer containing the appropriately
5194 ** reformatted spec (and, therefore, subsequent calls to that routine
5195 ** will clobber the result), while the routines of the same names with
5196 ** a _ts suffix appended will return a pointer to a mallocd string
5197 ** containing the appropriately reformatted spec.
5198 ** In all cases, only explicit syntax is altered; no check is made that
5199 ** the resulting string is valid or that the directory in question
5202 ** fileify_dirspec() - convert a directory spec into the name of the
5203 ** directory file (i.e. what you can stat() to see if it's a dir).
5204 ** The style (VMS or Unix) of the result is the same as the style
5205 ** of the parameter passed in.
5206 ** pathify_dirspec() - convert a directory spec into a path (i.e.
5207 ** what you prepend to a filename to indicate what directory it's in).
5208 ** The style (VMS or Unix) of the result is the same as the style
5209 ** of the parameter passed in.
5210 ** tounixpath() - convert a directory spec into a Unix-style path.
5211 ** tovmspath() - convert a directory spec into a VMS-style path.
5212 ** tounixspec() - convert any file spec into a Unix-style file spec.
5213 ** tovmsspec() - convert any file spec into a VMS-style spec.
5214 ** xxxxx_utf8() - Variants that support UTF8 encoding of Unix-Style file spec.
5216 ** Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>
5217 ** Permission is given to distribute this code as part of the Perl
5218 ** standard distribution under the terms of the GNU General Public
5219 ** License or the Perl Artistic License. Copies of each may be
5220 ** found in the Perl standard distribution.
5223 /*{{{ char *fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/
5224 static char *mp_do_fileify_dirspec(pTHX_ const char *dir,char *buf,int ts, int *utf8_fl)
5226 static char __fileify_retbuf[VMS_MAXRSS];
5227 unsigned long int dirlen, retlen, addmfd = 0, hasfilename = 0;
5228 char *retspec, *cp1, *cp2, *lastdir;
5229 char *trndir, *vmsdir;
5230 unsigned short int trnlnm_iter_count;
5232 if (utf8_fl != NULL)
5235 if (!dir || !*dir) {
5236 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
5238 dirlen = strlen(dir);
5239 while (dirlen && dir[dirlen-1] == '/') --dirlen;
5240 if (!dirlen) { /* We had Unixish '/' -- substitute top of current tree */
5241 if (!decc_posix_compliant_pathnames && decc_disable_posix_root) {
5248 if (dirlen > (VMS_MAXRSS - 1)) {
5249 set_errno(ENAMETOOLONG); set_vaxc_errno(RMS$_SYN);
5252 trndir = PerlMem_malloc(VMS_MAXRSS + 1);
5253 if (trndir == NULL) _ckvmssts(SS$_INSFMEM);
5254 if (!strpbrk(dir+1,"/]>:") &&
5255 (!decc_posix_compliant_pathnames && decc_disable_posix_root)) {
5256 strcpy(trndir,*dir == '/' ? dir + 1: dir);
5257 trnlnm_iter_count = 0;
5258 while (!strpbrk(trndir,"/]>:") && my_trnlnm(trndir,trndir,0)) {
5259 trnlnm_iter_count++;
5260 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
5262 dirlen = strlen(trndir);
5265 strncpy(trndir,dir,dirlen);
5266 trndir[dirlen] = '\0';
5269 /* At this point we are done with *dir and use *trndir which is a
5270 * copy that can be modified. *dir must not be modified.
5273 /* If we were handed a rooted logical name or spec, treat it like a
5274 * simple directory, so that
5275 * $ Define myroot dev:[dir.]
5276 * ... do_fileify_dirspec("myroot",buf,1) ...
5277 * does something useful.
5279 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".]")) {
5280 trndir[--dirlen] = '\0';
5281 trndir[dirlen-1] = ']';
5283 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".>")) {
5284 trndir[--dirlen] = '\0';
5285 trndir[dirlen-1] = '>';
5288 if ((cp1 = strrchr(trndir,']')) != NULL || (cp1 = strrchr(trndir,'>')) != NULL) {
5289 /* If we've got an explicit filename, we can just shuffle the string. */
5290 if (*(cp1+1)) hasfilename = 1;
5291 /* Similarly, we can just back up a level if we've got multiple levels
5292 of explicit directories in a VMS spec which ends with directories. */
5294 for (cp2 = cp1; cp2 > trndir; cp2--) {
5296 if ((cp2 - 1 > trndir) && (*(cp2 - 1) != '^')) {
5297 /* fix-me, can not scan EFS file specs backward like this */
5298 *cp2 = *cp1; *cp1 = '\0';
5303 if (*cp2 == '[' || *cp2 == '<') break;
5308 vmsdir = PerlMem_malloc(VMS_MAXRSS + 1);
5309 if (vmsdir == NULL) _ckvmssts(SS$_INSFMEM);
5310 cp1 = strpbrk(trndir,"]:>");
5311 if (hasfilename || !cp1) { /* Unix-style path or filename */
5312 if (trndir[0] == '.') {
5313 if (trndir[1] == '\0' || (trndir[1] == '/' && trndir[2] == '\0')) {
5314 PerlMem_free(trndir);
5315 PerlMem_free(vmsdir);
5316 return do_fileify_dirspec("[]",buf,ts,NULL);
5318 else if (trndir[1] == '.' &&
5319 (trndir[2] == '\0' || (trndir[2] == '/' && trndir[3] == '\0'))) {
5320 PerlMem_free(trndir);
5321 PerlMem_free(vmsdir);
5322 return do_fileify_dirspec("[-]",buf,ts,NULL);
5325 if (dirlen && trndir[dirlen-1] == '/') { /* path ends with '/'; just add .dir;1 */
5326 dirlen -= 1; /* to last element */
5327 lastdir = strrchr(trndir,'/');
5329 else if ((cp1 = strstr(trndir,"/.")) != NULL) {
5330 /* If we have "/." or "/..", VMSify it and let the VMS code
5331 * below expand it, rather than repeating the code to handle
5332 * relative components of a filespec here */
5334 if (*(cp1+2) == '.') cp1++;
5335 if (*(cp1+2) == '/' || *(cp1+2) == '\0') {
5337 if (do_tovmsspec(trndir,vmsdir,0,NULL) == NULL) {
5338 PerlMem_free(trndir);
5339 PerlMem_free(vmsdir);
5342 if (strchr(vmsdir,'/') != NULL) {
5343 /* If do_tovmsspec() returned it, it must have VMS syntax
5344 * delimiters in it, so it's a mixed VMS/Unix spec. We take
5345 * the time to check this here only so we avoid a recursion
5346 * loop; otherwise, gigo.
5348 PerlMem_free(trndir);
5349 PerlMem_free(vmsdir);
5350 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN);
5353 if (do_fileify_dirspec(vmsdir,trndir,0,NULL) == NULL) {
5354 PerlMem_free(trndir);
5355 PerlMem_free(vmsdir);
5358 ret_chr = do_tounixspec(trndir,buf,ts,NULL);
5359 PerlMem_free(trndir);
5360 PerlMem_free(vmsdir);
5364 } while ((cp1 = strstr(cp1,"/.")) != NULL);
5365 lastdir = strrchr(trndir,'/');
5367 else if (dirlen >= 7 && !strcmp(&trndir[dirlen-7],"/000000")) {
5369 /* Ditto for specs that end in an MFD -- let the VMS code
5370 * figure out whether it's a real device or a rooted logical. */
5372 /* This should not happen any more. Allowing the fake /000000
5373 * in a UNIX pathname causes all sorts of problems when trying
5374 * to run in UNIX emulation. So the VMS to UNIX conversions
5375 * now remove the fake /000000 directories.
5378 trndir[dirlen] = '/'; trndir[dirlen+1] = '\0';
5379 if (do_tovmsspec(trndir,vmsdir,0,NULL) == NULL) {
5380 PerlMem_free(trndir);
5381 PerlMem_free(vmsdir);
5384 if (do_fileify_dirspec(vmsdir,trndir,0,NULL) == NULL) {
5385 PerlMem_free(trndir);
5386 PerlMem_free(vmsdir);
5389 ret_chr = do_tounixspec(trndir,buf,ts,NULL);
5390 PerlMem_free(trndir);
5391 PerlMem_free(vmsdir);
5396 if ( !(lastdir = cp1 = strrchr(trndir,'/')) &&
5397 !(lastdir = cp1 = strrchr(trndir,']')) &&
5398 !(lastdir = cp1 = strrchr(trndir,'>'))) cp1 = trndir;
5399 if ((cp2 = strchr(cp1,'.'))) { /* look for explicit type */
5402 /* For EFS or ODS-5 look for the last dot */
5403 if (decc_efs_charset) {
5404 cp2 = strrchr(cp1,'.');
5406 if (vms_process_case_tolerant) {
5407 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
5408 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
5409 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
5410 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
5411 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
5412 (ver || *cp3)))))) {
5413 PerlMem_free(trndir);
5414 PerlMem_free(vmsdir);
5416 set_vaxc_errno(RMS$_DIR);
5421 if (!*(cp2+1) || *(cp2+1) != 'D' || /* Wrong type. */
5422 !*(cp2+2) || *(cp2+2) != 'I' || /* Bzzt. */
5423 !*(cp2+3) || *(cp2+3) != 'R' ||
5424 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
5425 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
5426 (ver || *cp3)))))) {
5427 PerlMem_free(trndir);
5428 PerlMem_free(vmsdir);
5430 set_vaxc_errno(RMS$_DIR);
5434 dirlen = cp2 - trndir;
5438 retlen = dirlen + 6;
5439 if (buf) retspec = buf;
5440 else if (ts) Newx(retspec,retlen+1,char);
5441 else retspec = __fileify_retbuf;
5442 memcpy(retspec,trndir,dirlen);
5443 retspec[dirlen] = '\0';
5445 /* We've picked up everything up to the directory file name.
5446 Now just add the type and version, and we're set. */
5447 if ((!decc_efs_case_preserve) && vms_process_case_tolerant)
5448 strcat(retspec,".dir;1");
5450 strcat(retspec,".DIR;1");
5451 PerlMem_free(trndir);
5452 PerlMem_free(vmsdir);
5455 else { /* VMS-style directory spec */
5457 char *esa, term, *cp;
5458 unsigned long int sts, cmplen, haslower = 0;
5459 unsigned int nam_fnb;
5461 struct FAB dirfab = cc$rms_fab;
5462 rms_setup_nam(savnam);
5463 rms_setup_nam(dirnam);
5465 esa = PerlMem_malloc(VMS_MAXRSS + 1);
5466 if (esa == NULL) _ckvmssts(SS$_INSFMEM);
5467 rms_set_fna(dirfab, dirnam, trndir, strlen(trndir));
5468 rms_bind_fab_nam(dirfab, dirnam);
5469 rms_set_dna(dirfab, dirnam, ".DIR;1", 6);
5470 rms_set_esa(dirfab, dirnam, esa, (VMS_MAXRSS - 1));
5471 #ifdef NAM$M_NO_SHORT_UPCASE
5472 if (decc_efs_case_preserve)
5473 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
5476 for (cp = trndir; *cp; cp++)
5477 if (islower(*cp)) { haslower = 1; break; }
5478 if (!((sts = sys$parse(&dirfab)) & STS$K_SUCCESS)) {
5479 if ((dirfab.fab$l_sts == RMS$_DIR) || (dirfab.fab$l_sts == RMS$_DNF)) {
5480 rms_set_nam_nop(dirnam, NAM$M_SYNCHK);
5481 sts = sys$parse(&dirfab) & STS$K_SUCCESS;
5485 PerlMem_free(trndir);
5486 PerlMem_free(vmsdir);
5488 set_vaxc_errno(dirfab.fab$l_sts);
5494 /* Does the file really exist? */
5495 if (sys$search(&dirfab)& STS$K_SUCCESS) {
5496 /* Yes; fake the fnb bits so we'll check type below */
5497 rms_set_nam_fnb(dirnam, (NAM$M_EXP_TYPE | NAM$M_EXP_VER));
5499 else { /* No; just work with potential name */
5500 if (dirfab.fab$l_sts == RMS$_FNF) dirnam = savnam;
5503 fab_sts = dirfab.fab$l_sts;
5504 sts = rms_free_search_context(&dirfab);
5506 PerlMem_free(trndir);
5507 PerlMem_free(vmsdir);
5508 set_errno(EVMSERR); set_vaxc_errno(fab_sts);
5513 esa[rms_nam_esll(dirnam)] = '\0';
5514 if (!rms_is_nam_fnb(dirnam, (NAM$M_EXP_DEV | NAM$M_EXP_DIR))) {
5515 cp1 = strchr(esa,']');
5516 if (!cp1) cp1 = strchr(esa,'>');
5517 if (cp1) { /* Should always be true */
5518 rms_nam_esll(dirnam) -= cp1 - esa - 1;
5519 memmove(esa,cp1 + 1, rms_nam_esll(dirnam));
5522 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_TYPE)) { /* Was type specified? */
5523 /* Yep; check version while we're at it, if it's there. */
5524 cmplen = rms_is_nam_fnb(dirnam, NAM$M_EXP_VER) ? 6 : 4;
5525 if (strncmp(rms_nam_typel(dirnam), ".DIR;1", cmplen)) {
5526 /* Something other than .DIR[;1]. Bzzt. */
5527 sts = rms_free_search_context(&dirfab);
5529 PerlMem_free(trndir);
5530 PerlMem_free(vmsdir);
5532 set_vaxc_errno(RMS$_DIR);
5537 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_NAME)) {
5538 /* They provided at least the name; we added the type, if necessary, */
5539 if (buf) retspec = buf; /* in sys$parse() */
5540 else if (ts) Newx(retspec, rms_nam_esll(dirnam)+1, char);
5541 else retspec = __fileify_retbuf;
5542 strcpy(retspec,esa);
5543 sts = rms_free_search_context(&dirfab);
5544 PerlMem_free(trndir);
5546 PerlMem_free(vmsdir);
5549 if ((cp1 = strstr(esa,".][000000]")) != NULL) {
5550 for (cp2 = cp1 + 9; *cp2; cp1++,cp2++) *cp1 = *cp2;
5552 rms_nam_esll(dirnam) -= 9;
5554 if ((cp1 = strrchr(esa,']')) == NULL) cp1 = strrchr(esa,'>');
5555 if (cp1 == NULL) { /* should never happen */
5556 sts = rms_free_search_context(&dirfab);
5557 PerlMem_free(trndir);
5559 PerlMem_free(vmsdir);
5564 retlen = strlen(esa);
5565 cp1 = strrchr(esa,'.');
5566 /* ODS-5 directory specifications can have extra "." in them. */
5567 /* Fix-me, can not scan EFS file specifications backwards */
5568 while (cp1 != NULL) {
5569 if ((cp1-1 == esa) || (*(cp1-1) != '^'))
5573 while ((cp1 > esa) && (*cp1 != '.'))
5580 if ((cp1) != NULL) {
5581 /* There's more than one directory in the path. Just roll back. */
5583 if (buf) retspec = buf;
5584 else if (ts) Newx(retspec,retlen+7,char);
5585 else retspec = __fileify_retbuf;
5586 strcpy(retspec,esa);
5589 if (rms_is_nam_fnb(dirnam, NAM$M_ROOT_DIR)) {
5590 /* Go back and expand rooted logical name */
5591 rms_set_nam_nop(dirnam, NAM$M_SYNCHK | NAM$M_NOCONCEAL);
5592 #ifdef NAM$M_NO_SHORT_UPCASE
5593 if (decc_efs_case_preserve)
5594 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
5596 if (!(sys$parse(&dirfab) & STS$K_SUCCESS)) {
5597 sts = rms_free_search_context(&dirfab);
5599 PerlMem_free(trndir);
5600 PerlMem_free(vmsdir);
5602 set_vaxc_errno(dirfab.fab$l_sts);
5605 retlen = rms_nam_esll(dirnam) - 9; /* esa - '][' - '].DIR;1' */
5606 if (buf) retspec = buf;
5607 else if (ts) Newx(retspec,retlen+16,char);
5608 else retspec = __fileify_retbuf;
5609 cp1 = strstr(esa,"][");
5610 if (!cp1) cp1 = strstr(esa,"]<");
5612 memcpy(retspec,esa,dirlen);
5613 if (!strncmp(cp1+2,"000000]",7)) {
5614 retspec[dirlen-1] = '\0';
5615 /* fix-me Not full ODS-5, just extra dots in directories for now */
5616 cp1 = retspec + dirlen - 1;
5617 while (cp1 > retspec)
5622 if (*(cp1-1) != '^')
5627 if (*cp1 == '.') *cp1 = ']';
5629 memmove(cp1+8,cp1+1,retspec+dirlen-cp1);
5630 memmove(cp1+1,"000000]",7);
5634 memmove(retspec+dirlen,cp1+2,retlen-dirlen);
5635 retspec[retlen] = '\0';
5636 /* Convert last '.' to ']' */
5637 cp1 = retspec+retlen-1;
5638 while (*cp != '[') {
5641 /* Do not trip on extra dots in ODS-5 directories */
5642 if ((cp1 == retspec) || (*(cp1-1) != '^'))
5646 if (*cp1 == '.') *cp1 = ']';
5648 memmove(cp1+8,cp1+1,retspec+dirlen-cp1);
5649 memmove(cp1+1,"000000]",7);
5653 else { /* This is a top-level dir. Add the MFD to the path. */
5654 if (buf) retspec = buf;
5655 else if (ts) Newx(retspec,retlen+16,char);
5656 else retspec = __fileify_retbuf;
5659 while ((*cp1 != ':') && (*cp1 != '\0')) *(cp2++) = *(cp1++);
5660 strcpy(cp2,":[000000]");
5665 sts = rms_free_search_context(&dirfab);
5666 /* We've set up the string up through the filename. Add the
5667 type and version, and we're done. */
5668 strcat(retspec,".DIR;1");
5670 /* $PARSE may have upcased filespec, so convert output to lower
5671 * case if input contained any lowercase characters. */
5672 if (haslower && !decc_efs_case_preserve) __mystrtolower(retspec);
5673 PerlMem_free(trndir);
5675 PerlMem_free(vmsdir);
5678 } /* end of do_fileify_dirspec() */
5680 /* External entry points */
5681 char *Perl_fileify_dirspec(pTHX_ const char *dir, char *buf)
5682 { return do_fileify_dirspec(dir,buf,0,NULL); }
5683 char *Perl_fileify_dirspec_ts(pTHX_ const char *dir, char *buf)
5684 { return do_fileify_dirspec(dir,buf,1,NULL); }
5685 char *Perl_fileify_dirspec_utf8(pTHX_ const char *dir, char *buf, int * utf8_fl)
5686 { return do_fileify_dirspec(dir,buf,0,utf8_fl); }
5687 char *Perl_fileify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int * utf8_fl)
5688 { return do_fileify_dirspec(dir,buf,1,utf8_fl); }
5690 /*{{{ char *pathify_dirspec[_ts](char *path, char *buf)*/
5691 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int * utf8_fl)
5693 static char __pathify_retbuf[VMS_MAXRSS];
5694 unsigned long int retlen;
5695 char *retpath, *cp1, *cp2, *trndir;
5696 unsigned short int trnlnm_iter_count;
5699 if (utf8_fl != NULL)
5702 if (!dir || !*dir) {
5703 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
5706 trndir = PerlMem_malloc(VMS_MAXRSS);
5707 if (trndir == NULL) _ckvmssts(SS$_INSFMEM);
5708 if (*dir) strcpy(trndir,dir);
5709 else getcwd(trndir,VMS_MAXRSS - 1);
5711 trnlnm_iter_count = 0;
5712 while (!strpbrk(trndir,"/]:>") && !no_translate_barewords
5713 && my_trnlnm(trndir,trndir,0)) {
5714 trnlnm_iter_count++;
5715 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
5716 trnlen = strlen(trndir);
5718 /* Trap simple rooted lnms, and return lnm:[000000] */
5719 if (!strcmp(trndir+trnlen-2,".]")) {
5720 if (buf) retpath = buf;
5721 else if (ts) Newx(retpath,strlen(dir)+10,char);
5722 else retpath = __pathify_retbuf;
5723 strcpy(retpath,dir);
5724 strcat(retpath,":[000000]");
5725 PerlMem_free(trndir);
5730 /* At this point we do not work with *dir, but the copy in
5731 * *trndir that is modifiable.
5734 if (!strpbrk(trndir,"]:>")) { /* Unix-style path or plain name */
5735 if (*trndir == '.' && (*(trndir+1) == '\0' ||
5736 (*(trndir+1) == '.' && *(trndir+2) == '\0')))
5737 retlen = 2 + (*(trndir+1) != '\0');
5739 if ( !(cp1 = strrchr(trndir,'/')) &&
5740 !(cp1 = strrchr(trndir,']')) &&
5741 !(cp1 = strrchr(trndir,'>')) ) cp1 = trndir;
5742 if ((cp2 = strchr(cp1,'.')) != NULL &&
5743 (*(cp2-1) != '/' || /* Trailing '.', '..', */
5744 !(*(cp2+1) == '\0' || /* or '...' are dirs. */
5745 (*(cp2+1) == '.' && *(cp2+2) == '\0') ||
5746 (*(cp2+1) == '.' && *(cp2+2) == '.' && *(cp2+3) == '\0')))) {
5749 /* For EFS or ODS-5 look for the last dot */
5750 if (decc_efs_charset) {
5751 cp2 = strrchr(cp1,'.');
5753 if (vms_process_case_tolerant) {
5754 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
5755 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
5756 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
5757 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
5758 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
5759 (ver || *cp3)))))) {
5760 PerlMem_free(trndir);
5762 set_vaxc_errno(RMS$_DIR);
5767 if (!*(cp2+1) || *(cp2+1) != 'D' || /* Wrong type. */
5768 !*(cp2+2) || *(cp2+2) != 'I' || /* Bzzt. */
5769 !*(cp2+3) || *(cp2+3) != 'R' ||
5770 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
5771 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
5772 (ver || *cp3)))))) {
5773 PerlMem_free(trndir);
5775 set_vaxc_errno(RMS$_DIR);
5779 retlen = cp2 - trndir + 1;
5781 else { /* No file type present. Treat the filename as a directory. */
5782 retlen = strlen(trndir) + 1;
5785 if (buf) retpath = buf;
5786 else if (ts) Newx(retpath,retlen+1,char);
5787 else retpath = __pathify_retbuf;
5788 strncpy(retpath, trndir, retlen-1);
5789 if (retpath[retlen-2] != '/') { /* If the path doesn't already end */
5790 retpath[retlen-1] = '/'; /* with '/', add it. */
5791 retpath[retlen] = '\0';
5793 else retpath[retlen-1] = '\0';
5795 else { /* VMS-style directory spec */
5797 unsigned long int sts, cmplen, haslower;
5798 struct FAB dirfab = cc$rms_fab;
5800 rms_setup_nam(savnam);
5801 rms_setup_nam(dirnam);
5803 /* If we've got an explicit filename, we can just shuffle the string. */
5804 if ( ( (cp1 = strrchr(trndir,']')) != NULL ||
5805 (cp1 = strrchr(trndir,'>')) != NULL ) && *(cp1+1)) {
5806 if ((cp2 = strchr(cp1,'.')) != NULL) {
5808 if (vms_process_case_tolerant) {
5809 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
5810 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
5811 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
5812 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
5813 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
5814 (ver || *cp3)))))) {
5815 PerlMem_free(trndir);
5817 set_vaxc_errno(RMS$_DIR);
5822 if (!*(cp2+1) || *(cp2+1) != 'D' || /* Wrong type. */
5823 !*(cp2+2) || *(cp2+2) != 'I' || /* Bzzt. */
5824 !*(cp2+3) || *(cp2+3) != 'R' ||
5825 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
5826 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
5827 (ver || *cp3)))))) {
5828 PerlMem_free(trndir);
5830 set_vaxc_errno(RMS$_DIR);
5835 else { /* No file type, so just draw name into directory part */
5836 for (cp2 = cp1; *cp2; cp2++) ;
5839 *(cp2+1) = '\0'; /* OK; trndir is guaranteed to be long enough */
5841 /* We've now got a VMS 'path'; fall through */
5844 dirlen = strlen(trndir);
5845 if (trndir[dirlen-1] == ']' ||
5846 trndir[dirlen-1] == '>' ||
5847 trndir[dirlen-1] == ':') { /* It's already a VMS 'path' */
5848 if (buf) retpath = buf;
5849 else if (ts) Newx(retpath,strlen(trndir)+1,char);
5850 else retpath = __pathify_retbuf;
5851 strcpy(retpath,trndir);
5852 PerlMem_free(trndir);
5855 rms_set_fna(dirfab, dirnam, trndir, dirlen);
5856 esa = PerlMem_malloc(VMS_MAXRSS);
5857 if (esa == NULL) _ckvmssts(SS$_INSFMEM);
5858 rms_set_dna(dirfab, dirnam, ".DIR;1", 6);
5859 rms_bind_fab_nam(dirfab, dirnam);
5860 rms_set_esa(dirfab, dirnam, esa, VMS_MAXRSS - 1);
5861 #ifdef NAM$M_NO_SHORT_UPCASE
5862 if (decc_efs_case_preserve)
5863 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
5866 for (cp = trndir; *cp; cp++)
5867 if (islower(*cp)) { haslower = 1; break; }
5869 if (!(sts = (sys$parse(&dirfab)& STS$K_SUCCESS))) {
5870 if ((dirfab.fab$l_sts == RMS$_DIR) || (dirfab.fab$l_sts == RMS$_DNF)) {
5871 rms_set_nam_nop(dirnam, NAM$M_SYNCHK);
5872 sts = sys$parse(&dirfab) & STS$K_SUCCESS;
5875 PerlMem_free(trndir);
5878 set_vaxc_errno(dirfab.fab$l_sts);
5884 /* Does the file really exist? */
5885 if (!(sys$search(&dirfab)&STS$K_SUCCESS)) {
5886 if (dirfab.fab$l_sts != RMS$_FNF) {
5888 sts1 = rms_free_search_context(&dirfab);
5889 PerlMem_free(trndir);
5892 set_vaxc_errno(dirfab.fab$l_sts);
5895 dirnam = savnam; /* No; just work with potential name */
5898 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_TYPE)) { /* Was type specified? */
5899 /* Yep; check version while we're at it, if it's there. */
5900 cmplen = rms_is_nam_fnb(dirnam, NAM$M_EXP_VER) ? 6 : 4;
5901 if (strncmp(rms_nam_typel(dirnam),".DIR;1",cmplen)) {
5903 /* Something other than .DIR[;1]. Bzzt. */
5904 sts2 = rms_free_search_context(&dirfab);
5905 PerlMem_free(trndir);
5908 set_vaxc_errno(RMS$_DIR);
5912 /* OK, the type was fine. Now pull any file name into the
5914 if ((cp1 = strrchr(esa,']'))) *(rms_nam_typel(dirnam)) = ']';
5916 cp1 = strrchr(esa,'>');
5917 *(rms_nam_typel(dirnam)) = '>';
5920 *(rms_nam_typel(dirnam) + 1) = '\0';
5921 retlen = (rms_nam_typel(dirnam)) - esa + 2;
5922 if (buf) retpath = buf;
5923 else if (ts) Newx(retpath,retlen,char);
5924 else retpath = __pathify_retbuf;
5925 strcpy(retpath,esa);
5927 sts = rms_free_search_context(&dirfab);
5928 /* $PARSE may have upcased filespec, so convert output to lower
5929 * case if input contained any lowercase characters. */
5930 if (haslower && !decc_efs_case_preserve) __mystrtolower(retpath);
5933 PerlMem_free(trndir);
5935 } /* end of do_pathify_dirspec() */
5937 /* External entry points */
5938 char *Perl_pathify_dirspec(pTHX_ const char *dir, char *buf)
5939 { return do_pathify_dirspec(dir,buf,0,NULL); }
5940 char *Perl_pathify_dirspec_ts(pTHX_ const char *dir, char *buf)
5941 { return do_pathify_dirspec(dir,buf,1,NULL); }
5942 char *Perl_pathify_dirspec_utf8(pTHX_ const char *dir, char *buf, int *utf8_fl)
5943 { return do_pathify_dirspec(dir,buf,0,utf8_fl); }
5944 char *Perl_pathify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int *utf8_fl)
5945 { return do_pathify_dirspec(dir,buf,1,utf8_fl); }
5947 /*{{{ char *tounixspec[_ts](char *spec, char *buf, int *)*/
5948 static char *mp_do_tounixspec(pTHX_ const char *spec, char *buf, int ts, int * utf8_fl)
5950 static char __tounixspec_retbuf[VMS_MAXRSS];
5951 char *dirend, *rslt, *cp1, *cp3, *tmp;
5953 int devlen, dirlen, retlen = VMS_MAXRSS;
5954 int expand = 1; /* guarantee room for leading and trailing slashes */
5955 unsigned short int trnlnm_iter_count;
5957 if (utf8_fl != NULL)
5960 if (spec == NULL) return NULL;
5961 if (strlen(spec) > (VMS_MAXRSS-1)) return NULL;
5962 if (buf) rslt = buf;
5964 Newx(rslt, VMS_MAXRSS, char);
5966 else rslt = __tounixspec_retbuf;
5968 /* New VMS specific format needs translation
5969 * glob passes filenames with trailing '\n' and expects this preserved.
5971 if (decc_posix_compliant_pathnames) {
5972 if (strncmp(spec, "\"^UP^", 5) == 0) {
5978 tunix = PerlMem_malloc(VMS_MAXRSS);
5979 if (tunix == NULL) _ckvmssts(SS$_INSFMEM);
5980 strcpy(tunix, spec);
5981 tunix_len = strlen(tunix);
5983 if (tunix[tunix_len - 1] == '\n') {
5984 tunix[tunix_len - 1] = '\"';
5985 tunix[tunix_len] = '\0';
5989 uspec = decc$translate_vms(tunix);
5990 PerlMem_free(tunix);
5991 if ((int)uspec > 0) {
5997 /* If we can not translate it, makemaker wants as-is */
6005 cmp_rslt = 0; /* Presume VMS */
6006 cp1 = strchr(spec, '/');
6010 /* Look for EFS ^/ */
6011 if (decc_efs_charset) {
6012 while (cp1 != NULL) {
6015 /* Found illegal VMS, assume UNIX */
6020 cp1 = strchr(cp1, '/');
6024 /* Look for "." and ".." */
6025 if (decc_filename_unix_report) {
6026 if (spec[0] == '.') {
6027 if ((spec[1] == '\0') || (spec[1] == '\n')) {
6031 if ((spec[1] == '.') && ((spec[2] == '\0') || (spec[2] == '\n'))) {
6037 /* This is already UNIX or at least nothing VMS understands */
6045 dirend = strrchr(spec,']');
6046 if (dirend == NULL) dirend = strrchr(spec,'>');
6047 if (dirend == NULL) dirend = strchr(spec,':');
6048 if (dirend == NULL) {
6053 /* Special case 1 - sys$posix_root = / */
6054 #if __CRTL_VER >= 70000000
6055 if (!decc_disable_posix_root) {
6056 if (strncasecmp(spec, "SYS$POSIX_ROOT:", 15) == 0) {
6064 /* Special case 2 - Convert NLA0: to /dev/null */
6065 #if __CRTL_VER < 70000000
6066 cmp_rslt = strncmp(spec,"NLA0:", 5);
6068 cmp_rslt = strncmp(spec,"nla0:", 5);
6070 cmp_rslt = strncasecmp(spec,"NLA0:", 5);
6072 if (cmp_rslt == 0) {
6073 strcpy(rslt, "/dev/null");
6076 if (spec[6] != '\0') {
6083 /* Also handle special case "SYS$SCRATCH:" */
6084 #if __CRTL_VER < 70000000
6085 cmp_rslt = strncmp(spec,"SYS$SCRATCH:", 12);
6087 cmp_rslt = strncmp(spec,"sys$scratch:", 12);
6089 cmp_rslt = strncasecmp(spec,"SYS$SCRATCH:", 12);
6091 tmp = PerlMem_malloc(VMS_MAXRSS);
6092 if (tmp == NULL) _ckvmssts(SS$_INSFMEM);
6093 if (cmp_rslt == 0) {
6096 islnm = my_trnlnm(tmp, "TMP", 0);
6098 strcpy(rslt, "/tmp");
6101 if (spec[12] != '\0') {
6109 if (*cp2 != '[' && *cp2 != '<') {
6112 else { /* the VMS spec begins with directories */
6114 if (*cp2 == ']' || *cp2 == '>') {
6115 *(cp1++) = '.'; *(cp1++) = '/'; *(cp1++) = '\0';
6119 else if ( *cp2 != '^' && *cp2 != '.' && *cp2 != '-') { /* add the implied device */
6120 if (getcwd(tmp, VMS_MAXRSS-1 ,1) == NULL) {
6121 if (ts) Safefree(rslt);
6125 trnlnm_iter_count = 0;
6128 while (*cp3 != ':' && *cp3) cp3++;
6130 if (strchr(cp3,']') != NULL) break;
6131 trnlnm_iter_count++;
6132 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER+1) break;
6133 } while (vmstrnenv(tmp,tmp,0,fildev,0));
6135 ((devlen = strlen(tmp)) + (dirlen = strlen(cp2)) + 1 > retlen)) {
6136 retlen = devlen + dirlen;
6137 Renew(rslt,retlen+1+2*expand,char);
6143 *(cp1++) = *(cp3++);
6144 if (cp1 - rslt > (VMS_MAXRSS - 1) && !ts && !buf) {
6146 return NULL; /* No room */
6151 if ((*cp2 == '^')) {
6152 /* EFS file escape, pass the next character as is */
6153 /* Fix me: HEX encoding for Unicode not implemented */
6156 else if ( *cp2 == '.') {
6157 if (*(cp2+1) == '.' && *(cp2+2) == '.') {
6158 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
6165 for (; cp2 <= dirend; cp2++) {
6166 if ((*cp2 == '^')) {
6167 /* EFS file escape, pass the next character as is */
6168 /* Fix me: HEX encoding for Unicode not implemented */
6169 *(cp1++) = *(++cp2);
6170 /* An escaped dot stays as is -- don't convert to slash */
6171 if (*cp2 == '.') cp2++;
6175 if (*(cp2+1) == '[') cp2++;
6177 else if (*cp2 == ']' || *cp2 == '>') {
6178 if (*(cp1-1) != '/') *(cp1++) = '/'; /* Don't double after ellipsis */
6180 else if ((*cp2 == '.') && (*cp2-1 != '^')) {
6182 if (*(cp2+1) == ']' || *(cp2+1) == '>') {
6183 while (*(cp2+1) == ']' || *(cp2+1) == '>' ||
6184 *(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
6185 if (!strncmp(cp2,"[000000",7) && (*(cp2+7) == ']' ||
6186 *(cp2+7) == '>' || *(cp2+7) == '.')) cp2 += 7;
6188 else if ( *(cp2+1) == '.' && *(cp2+2) == '.') {
6189 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) ='/';
6193 else if (*cp2 == '-') {
6194 if (*(cp2-1) == '[' || *(cp2-1) == '<' || *(cp2-1) == '.') {
6195 while (*cp2 == '-') {
6197 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
6199 if (*cp2 != '.' && *cp2 != ']' && *cp2 != '>') { /* we don't allow */
6200 if (ts) Safefree(rslt); /* filespecs like */
6201 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); /* [fred.--foo.bar] */
6205 else *(cp1++) = *cp2;
6207 else *(cp1++) = *cp2;
6210 if ((*cp2 == '^') && (*(cp2+1) == '.')) cp2++; /* '^.' --> '.' */
6211 *(cp1++) = *(cp2++);
6215 /* This still leaves /000000/ when working with a
6216 * VMS device root or concealed root.
6222 ulen = strlen(rslt);
6224 /* Get rid of "000000/ in rooted filespecs */
6226 zeros = strstr(rslt, "/000000/");
6227 if (zeros != NULL) {
6229 mlen = ulen - (zeros - rslt) - 7;
6230 memmove(zeros, &zeros[7], mlen);
6239 } /* end of do_tounixspec() */
6241 /* External entry points */
6242 char *Perl_tounixspec(pTHX_ const char *spec, char *buf)
6243 { return do_tounixspec(spec,buf,0, NULL); }
6244 char *Perl_tounixspec_ts(pTHX_ const char *spec, char *buf)
6245 { return do_tounixspec(spec,buf,1, NULL); }
6246 char *Perl_tounixspec_utf8(pTHX_ const char *spec, char *buf, int * utf8_fl)
6247 { return do_tounixspec(spec,buf,0, utf8_fl); }
6248 char *Perl_tounixspec_utf8_ts(pTHX_ const char *spec, char *buf, int * utf8_fl)
6249 { return do_tounixspec(spec,buf,1, utf8_fl); }
6251 #if __CRTL_VER >= 70200000 && !defined(__VAX)
6254 This procedure is used to identify if a path is based in either
6255 the old SYS$POSIX_ROOT: or the new 8.3 RMS based POSIX root, and
6256 it returns the OpenVMS format directory for it.
6258 It is expecting specifications of only '/' or '/xxxx/'
6260 If a posix root does not exist, or 'xxxx' is not a directory
6261 in the posix root, it returns a failure.
6263 FIX-ME: xxxx could be in UTF-8 and needs to be returned in VTF-7.
6265 It is used only internally by posix_to_vmsspec_hardway().
6268 static int posix_root_to_vms
6269 (char *vmspath, int vmspath_len,
6270 const char *unixpath,
6271 const int * utf8_fl) {
6273 struct FAB myfab = cc$rms_fab;
6274 struct NAML mynam = cc$rms_naml;
6275 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
6276 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
6283 unixlen = strlen(unixpath);
6289 #if __CRTL_VER >= 80200000
6290 /* If not a posix spec already, convert it */
6291 if (decc_posix_compliant_pathnames) {
6292 if (strncmp(unixpath,"\"^UP^",5) != 0) {
6293 sprintf(vmspath,"\"^UP^%s\"",unixpath);
6296 /* This is already a VMS specification, no conversion */
6298 strncpy(vmspath,unixpath, vmspath_len);
6307 /* Check to see if this is under the POSIX root */
6308 if (decc_disable_posix_root) {
6312 /* Skip leading / */
6313 if (unixpath[0] == '/') {
6319 strcpy(vmspath,"SYS$POSIX_ROOT:");
6321 /* If this is only the / , or blank, then... */
6322 if (unixpath[0] == '\0') {
6323 /* by definition, this is the answer */
6327 /* Need to look up a directory */
6331 /* Copy and add '^' escape characters as needed */
6334 while (unixpath[i] != 0) {
6337 j += copy_expand_unix_filename_escape
6338 (&vmspath[j], &unixpath[i], &k, utf8_fl);
6342 path_len = strlen(vmspath);
6343 if (vmspath[path_len - 1] == '/')
6345 vmspath[path_len] = ']';
6347 vmspath[path_len] = '\0';
6350 vmspath[vmspath_len] = 0;
6351 if (unixpath[unixlen - 1] == '/')
6353 esa = PerlMem_malloc(VMS_MAXRSS);
6354 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6355 myfab.fab$l_fna = vmspath;
6356 myfab.fab$b_fns = strlen(vmspath);
6357 myfab.fab$l_naml = &mynam;
6358 mynam.naml$l_esa = NULL;
6359 mynam.naml$b_ess = 0;
6360 mynam.naml$l_long_expand = esa;
6361 mynam.naml$l_long_expand_alloc = (unsigned char) VMS_MAXRSS - 1;
6362 mynam.naml$l_rsa = NULL;
6363 mynam.naml$b_rss = 0;
6364 if (decc_efs_case_preserve)
6365 mynam.naml$b_nop |= NAM$M_NO_SHORT_UPCASE;
6366 #ifdef NAML$M_OPEN_SPECIAL
6367 mynam.naml$l_input_flags |= NAML$M_OPEN_SPECIAL;
6370 /* Set up the remaining naml fields */
6371 sts = sys$parse(&myfab);
6373 /* It failed! Try again as a UNIX filespec */
6379 /* get the Device ID and the FID */
6380 sts = sys$search(&myfab);
6381 /* on any failure, returned the POSIX ^UP^ filespec */
6386 specdsc.dsc$a_pointer = vmspath;
6387 specdsc.dsc$w_length = vmspath_len;
6389 dvidsc.dsc$a_pointer = &mynam.naml$t_dvi[1];
6390 dvidsc.dsc$w_length = mynam.naml$t_dvi[0];
6391 sts = lib$fid_to_name
6392 (&dvidsc, mynam.naml$w_fid, &specdsc, &specdsc.dsc$w_length);
6394 /* on any failure, returned the POSIX ^UP^ filespec */
6396 /* This can happen if user does not have permission to read directories */
6397 if (strncmp(unixpath,"\"^UP^",5) != 0)
6398 sprintf(vmspath,"\"^UP^%s\"",unixpath);
6400 strcpy(vmspath, unixpath);
6403 vmspath[specdsc.dsc$w_length] = 0;
6405 /* Are we expecting a directory? */
6406 if (dir_flag != 0) {
6412 i = specdsc.dsc$w_length - 1;
6416 /* Version must be '1' */
6417 if (vmspath[i--] != '1')
6419 /* Version delimiter is one of ".;" */
6420 if ((vmspath[i] != '.') && (vmspath[i] != ';'))
6423 if (vmspath[i--] != 'R')
6425 if (vmspath[i--] != 'I')
6427 if (vmspath[i--] != 'D')
6429 if (vmspath[i--] != '.')
6431 eptr = &vmspath[i+1];
6433 if ((vmspath[i] == ']') || (vmspath[i] == '>')) {
6434 if (vmspath[i-1] != '^') {
6442 /* Get rid of 6 imaginary zero directory filename */
6443 vmspath[i+1] = '\0';
6447 if (vmspath[i] == '0')
6461 /* /dev/mumble needs to be handled special.
6462 /dev/null becomes NLA0:, And there is the potential for other stuff
6463 like /dev/tty which may need to be mapped to something.
6467 slash_dev_special_to_vms
6468 (const char * unixptr,
6478 nextslash = strchr(unixptr, '/');
6479 len = strlen(unixptr);
6480 if (nextslash != NULL)
6481 len = nextslash - unixptr;
6482 cmp = strncmp("null", unixptr, 5);
6484 if (vmspath_len >= 6) {
6485 strcpy(vmspath, "_NLA0:");
6492 /* The built in routines do not understand perl's special needs, so
6493 doing a manual conversion from UNIX to VMS
6495 If the utf8_fl is not null and points to a non-zero value, then
6496 treat 8 bit characters as UTF-8.
6498 The sequence starting with '$(' and ending with ')' will be passed
6499 through with out interpretation instead of being escaped.
6502 static int posix_to_vmsspec_hardway
6503 (char *vmspath, int vmspath_len,
6504 const char *unixpath,
6509 const char *unixptr;
6510 const char *unixend;
6512 const char *lastslash;
6513 const char *lastdot;
6519 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
6520 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
6522 if (utf8_fl != NULL)
6528 /* Ignore leading "/" characters */
6529 while((unixptr[0] == '/') && (unixptr[1] == '/')) {
6532 unixlen = strlen(unixptr);
6534 /* Do nothing with blank paths */
6541 /* This could have a "^UP^ on the front */
6542 if (strncmp(unixptr,"\"^UP^",5) == 0) {
6548 lastslash = strrchr(unixptr,'/');
6549 lastdot = strrchr(unixptr,'.');
6550 unixend = strrchr(unixptr,'\"');
6551 if (!quoted || !((unixend != NULL) && (unixend[1] == '\0'))) {
6552 unixend = unixptr + unixlen;
6555 /* last dot is last dot or past end of string */
6556 if (lastdot == NULL)
6557 lastdot = unixptr + unixlen;
6559 /* if no directories, set last slash to beginning of string */
6560 if (lastslash == NULL) {
6561 lastslash = unixptr;
6564 /* Watch out for trailing "." after last slash, still a directory */
6565 if ((lastslash[1] == '.') && (lastslash[2] == '\0')) {
6566 lastslash = unixptr + unixlen;
6569 /* Watch out for traiing ".." after last slash, still a directory */
6570 if ((lastslash[1] == '.')&&(lastslash[2] == '.')&&(lastslash[3] == '\0')) {
6571 lastslash = unixptr + unixlen;
6574 /* dots in directories are aways escaped */
6575 if (lastdot < lastslash)
6576 lastdot = unixptr + unixlen;
6579 /* if (unixptr < lastslash) then we are in a directory */
6586 /* Start with the UNIX path */
6587 if (*unixptr != '/') {
6588 /* relative paths */
6590 /* If allowing logical names on relative pathnames, then handle here */
6591 if ((unixptr[0] != '.') && !decc_disable_to_vms_logname_translation &&
6592 !decc_posix_compliant_pathnames) {
6598 /* Find the next slash */
6599 nextslash = strchr(unixptr,'/');
6601 esa = PerlMem_malloc(vmspath_len);
6602 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6604 trn = PerlMem_malloc(VMS_MAXRSS);
6605 if (trn == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6607 if (nextslash != NULL) {
6609 seg_len = nextslash - unixptr;
6610 strncpy(esa, unixptr, seg_len);
6614 strcpy(esa, unixptr);
6615 seg_len = strlen(unixptr);
6617 /* trnlnm(section) */
6618 islnm = vmstrnenv(esa, trn, 0, fildev, 0);
6621 /* Now fix up the directory */
6623 /* Split up the path to find the components */
6624 sts = vms_split_path
6643 /* A logical name must be a directory or the full
6644 specification. It is only a full specification if
6645 it is the only component */
6646 if ((unixptr[seg_len] == '\0') ||
6647 (unixptr[seg_len+1] == '\0')) {
6649 /* Is a directory being required? */
6650 if (((n_len + e_len) != 0) && (dir_flag !=0)) {
6651 /* Not a logical name */
6656 if ((unixptr[seg_len] == '/') || (dir_flag != 0)) {
6657 /* This must be a directory */
6658 if (((n_len + e_len) == 0)&&(seg_len <= vmspath_len)) {
6659 strcpy(vmsptr, esa);
6660 vmslen=strlen(vmsptr);
6661 vmsptr[vmslen] = ':';
6663 vmsptr[vmslen] = '\0';
6671 /* must be dev/directory - ignore version */
6672 if ((n_len + e_len) != 0)
6675 /* transfer the volume */
6676 if (v_len > 0 && ((v_len + vmslen) < vmspath_len)) {
6677 strncpy(vmsptr, v_spec, v_len);
6683 /* unroot the rooted directory */
6684 if ((r_len > 0) && ((r_len + d_len + vmslen) < vmspath_len)) {
6686 r_spec[r_len - 1] = ']';
6688 /* This should not be there, but nothing is perfect */
6690 cmp = strcmp(&r_spec[1], "000000.");
6700 strncpy(vmsptr, r_spec, r_len);
6706 /* Bring over the directory. */
6708 ((d_len + vmslen) < vmspath_len)) {
6710 d_spec[d_len - 1] = ']';
6712 cmp = strcmp(&d_spec[1], "000000.");
6723 /* Remove the redundant root */
6731 strncpy(vmsptr, d_spec, d_len);
6745 if (lastslash > unixptr) {
6748 /* skip leading ./ */
6750 while ((unixptr[0] == '.') && (unixptr[1] == '/')) {
6756 /* Are we still in a directory? */
6757 if (unixptr <= lastslash) {
6762 /* if not backing up, then it is relative forward. */
6763 if (!((*unixptr == '.') && (unixptr[1] == '.') &&
6764 ((unixptr[2] == '/') || (&unixptr[2] == unixend)))) {
6772 /* Perl wants an empty directory here to tell the difference
6773 * between a DCL commmand and a filename
6782 /* Handle two special files . and .. */
6783 if (unixptr[0] == '.') {
6784 if (&unixptr[1] == unixend) {
6791 if ((unixptr[1] == '.') && (&unixptr[2] == unixend)) {
6802 else { /* Absolute PATH handling */
6806 /* Need to find out where root is */
6808 /* In theory, this procedure should never get an absolute POSIX pathname
6809 * that can not be found on the POSIX root.
6810 * In practice, that can not be relied on, and things will show up
6811 * here that are a VMS device name or concealed logical name instead.
6812 * So to make things work, this procedure must be tolerant.
6814 esa = PerlMem_malloc(vmspath_len);
6815 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6818 nextslash = strchr(&unixptr[1],'/');
6820 if (nextslash != NULL) {
6822 seg_len = nextslash - &unixptr[1];
6823 strncpy(vmspath, unixptr, seg_len + 1);
6824 vmspath[seg_len+1] = 0;
6827 cmp = strncmp(vmspath, "dev", 4);
6829 sts = slash_dev_special_to_vms(unixptr, vmspath, vmspath_len);
6830 if (sts = SS$_NORMAL)
6834 sts = posix_root_to_vms(esa, vmspath_len, vmspath, utf8_fl);
6837 if ($VMS_STATUS_SUCCESS(sts)) {
6838 /* This is verified to be a real path */
6840 sts = posix_root_to_vms(esa, vmspath_len, "/", NULL);
6841 if ($VMS_STATUS_SUCCESS(sts)) {
6842 strcpy(vmspath, esa);
6843 vmslen = strlen(vmspath);
6844 vmsptr = vmspath + vmslen;
6846 if (unixptr < lastslash) {
6855 cmp = strcmp(rptr,"000000.");
6860 } /* removing 6 zeros */
6861 } /* vmslen < 7, no 6 zeros possible */
6862 } /* Not in a directory */
6863 } /* Posix root found */
6865 /* No posix root, fall back to default directory */
6866 strcpy(vmspath, "SYS$DISK:[");
6867 vmsptr = &vmspath[10];
6869 if (unixptr > lastslash) {
6878 } /* end of verified real path handling */
6883 /* Ok, we have a device or a concealed root that is not in POSIX
6884 * or we have garbage. Make the best of it.
6887 /* Posix to VMS destroyed this, so copy it again */
6888 strncpy(vmspath, &unixptr[1], seg_len);
6889 vmspath[seg_len] = 0;
6891 vmsptr = &vmsptr[vmslen];
6894 /* Now do we need to add the fake 6 zero directory to it? */
6896 if ((*lastslash == '/') && (nextslash < lastslash)) {
6897 /* No there is another directory */
6904 /* now we have foo:bar or foo:[000000]bar to decide from */
6905 islnm = vmstrnenv(vmspath, esa, 0, fildev, 0);
6907 if (!islnm && !decc_posix_compliant_pathnames) {
6909 cmp = strncmp("bin", vmspath, 4);
6911 /* bin => SYS$SYSTEM: */
6912 islnm = vmstrnenv("SYS$SYSTEM:", esa, 0, fildev, 0);
6915 /* tmp => SYS$SCRATCH: */
6916 cmp = strncmp("tmp", vmspath, 4);
6918 islnm = vmstrnenv("SYS$SCRATCH:", esa, 0, fildev, 0);
6923 trnend = islnm ? islnm - 1 : 0;
6925 /* if this was a logical name, ']' or '>' must be present */
6926 /* if not a logical name, then assume a device and hope. */
6927 islnm = trnend ? (esa[trnend] == ']' || esa[trnend] == '>') : 0;
6929 /* if log name and trailing '.' then rooted - treat as device */
6930 add_6zero = islnm ? (esa[trnend-1] == '.') : 0;
6932 /* Fix me, if not a logical name, a device lookup should be
6933 * done to see if the device is file structured. If the device
6934 * is not file structured, the 6 zeros should not be put on.
6936 * As it is, perl is occasionally looking for dev:[000000]tty.
6937 * which looks a little strange.
6939 * Not that easy to detect as "/dev" may be file structured with
6940 * special device files.
6943 if ((add_6zero == 0) && (*nextslash == '/') &&
6944 (&nextslash[1] == unixend)) {
6945 /* No real directory present */
6950 /* Put the device delimiter on */
6953 unixptr = nextslash;
6956 /* Start directory if needed */
6957 if (!islnm || add_6zero) {
6963 /* add fake 000000] if needed */
6976 } /* non-POSIX translation */
6978 } /* End of relative/absolute path handling */
6980 while ((unixptr <= unixend) && (vmslen < vmspath_len)){
6987 if (dir_start != 0) {
6989 /* First characters in a directory are handled special */
6990 while ((*unixptr == '/') ||
6991 ((*unixptr == '.') &&
6992 ((unixptr[1]=='.') || (unixptr[1]=='/') ||
6993 (&unixptr[1]==unixend)))) {
6998 /* Skip redundant / in specification */
6999 while ((*unixptr == '/') && (dir_start != 0)) {
7002 if (unixptr == lastslash)
7005 if (unixptr == lastslash)
7008 /* Skip redundant ./ characters */
7009 while ((*unixptr == '.') &&
7010 ((unixptr[1] == '/')||(&unixptr[1] == unixend))) {
7013 if (unixptr == lastslash)
7015 if (*unixptr == '/')
7018 if (unixptr == lastslash)
7021 /* Skip redundant ../ characters */
7022 while ((*unixptr == '.') && (unixptr[1] == '.') &&
7023 ((unixptr[2] == '/') || (&unixptr[2] == unixend))) {
7024 /* Set the backing up flag */
7030 unixptr++; /* first . */
7031 unixptr++; /* second . */
7032 if (unixptr == lastslash)
7034 if (*unixptr == '/') /* The slash */
7037 if (unixptr == lastslash)
7040 /* To do: Perl expects /.../ to be translated to [...] on VMS */
7041 /* Not needed when VMS is pretending to be UNIX. */
7043 /* Is this loop stuck because of too many dots? */
7044 if (loop_flag == 0) {
7045 /* Exit the loop and pass the rest through */
7050 /* Are we done with directories yet? */
7051 if (unixptr >= lastslash) {
7053 /* Watch out for trailing dots */
7062 if (*unixptr == '/')
7066 /* Have we stopped backing up? */
7071 /* dir_start continues to be = 1 */
7073 if (*unixptr == '-') {
7075 *vmsptr++ = *unixptr++;
7079 /* Now are we done with directories yet? */
7080 if (unixptr >= lastslash) {
7082 /* Watch out for trailing dots */
7098 if (unixptr >= unixend)
7101 /* Normal characters - More EFS work probably needed */
7107 /* remove multiple / */
7108 while (unixptr[1] == '/') {
7111 if (unixptr == lastslash) {
7112 /* Watch out for trailing dots */
7124 /* To do: Perl expects /.../ to be translated to [...] on VMS */
7125 /* Not needed when VMS is pretending to be UNIX. */
7129 if (unixptr != unixend)
7134 if ((unixptr < lastdot) || (unixptr < lastslash) ||
7135 (&unixptr[1] == unixend)) {
7141 /* trailing dot ==> '^..' on VMS */
7142 if (unixptr == unixend) {
7150 *vmsptr++ = *unixptr++;
7154 if (quoted && (&unixptr[1] == unixend)) {
7158 in_cnt = copy_expand_unix_filename_escape
7159 (vmsptr, unixptr, &out_cnt, utf8_fl);
7169 in_cnt = copy_expand_unix_filename_escape
7170 (vmsptr, unixptr, &out_cnt, utf8_fl);
7177 /* Make sure directory is closed */
7178 if (unixptr == lastslash) {
7180 vmsptr2 = vmsptr - 1;
7182 if (*vmsptr2 != ']') {
7185 /* directories do not end in a dot bracket */
7186 if (*vmsptr2 == '.') {
7190 if (*vmsptr2 != '^') {
7191 vmsptr--; /* back up over the dot */
7199 /* Add a trailing dot if a file with no extension */
7200 vmsptr2 = vmsptr - 1;
7202 (*vmsptr2 != ']') && (*vmsptr2 != '*') && (*vmsptr2 != '%') &&
7203 (*vmsptr2 != ')') && (*lastdot != '.')) {
7214 /* Eventual routine to convert a UTF-8 specification to VTF-7. */
7215 static char * utf8_to_vtf7(char * rslt, const char * path, int *utf8_fl)
7220 /* If a UTF8 flag is being passed, honor it */
7222 if (utf8_fl != NULL) {
7223 utf8_flag = *utf8_fl;
7228 /* If there is a possibility of UTF8, then if any UTF8 characters
7229 are present, then they must be converted to VTF-7
7231 result = strcpy(rslt, path); /* FIX-ME */
7234 result = strcpy(rslt, path);
7240 /*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/
7241 static char *mp_do_tovmsspec
7242 (pTHX_ const char *path, char *buf, int ts, int dir_flag, int * utf8_flag) {
7243 static char __tovmsspec_retbuf[VMS_MAXRSS];
7244 char *rslt, *dirend;
7249 unsigned long int infront = 0, hasdir = 1;
7252 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
7253 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
7255 if (path == NULL) return NULL;
7256 rslt_len = VMS_MAXRSS-1;
7257 if (buf) rslt = buf;
7258 else if (ts) Newx(rslt, VMS_MAXRSS, char);
7259 else rslt = __tovmsspec_retbuf;
7261 /* '.' and '..' are "[]" and "[-]" for a quick check */
7262 if (path[0] == '.') {
7263 if (path[1] == '\0') {
7265 if (utf8_flag != NULL)
7270 if (path[1] == '.' && path[2] == '\0') {
7272 if (utf8_flag != NULL)
7279 /* Posix specifications are now a native VMS format */
7280 /*--------------------------------------------------*/
7281 #if __CRTL_VER >= 80200000 && !defined(__VAX)
7282 if (decc_posix_compliant_pathnames) {
7283 if (strncmp(path,"\"^UP^",5) == 0) {
7284 posix_to_vmsspec_hardway(rslt, rslt_len, path, dir_flag, utf8_flag);
7290 /* This is really the only way to see if this is already in VMS format */
7291 sts = vms_split_path
7306 /* FIX-ME - If dir_flag is non-zero, then this is a mp_do_vmspath()
7307 replacement, because the above parse just took care of most of
7308 what is needed to do vmspath when the specification is already
7311 And if it is not already, it is easier to do the conversion as
7312 part of this routine than to call this routine and then work on
7316 /* If VMS punctuation was found, it is already VMS format */
7317 if ((v_len != 0) || (r_len != 0) || (d_len != 0) || (vs_len != 0)) {
7318 if (utf8_flag != NULL)
7323 /* Now, what to do with trailing "." cases where there is no
7324 extension? If this is a UNIX specification, and EFS characters
7325 are enabled, then the trailing "." should be converted to a "^.".
7326 But if this was already a VMS specification, then it should be
7329 So in the case of ambiguity, leave the specification alone.
7333 /* If there is a possibility of UTF8, then if any UTF8 characters
7334 are present, then they must be converted to VTF-7
7336 if (utf8_flag != NULL)
7342 dirend = strrchr(path,'/');
7344 if (dirend == NULL) {
7345 /* If we get here with no UNIX directory delimiters, then this is
7346 not a complete file specification, either garbage a UNIX glob
7347 specification that can not be converted to a VMS wildcard, or
7348 it a UNIX shell macro. MakeMaker wants these passed through AS-IS,
7349 so apparently other programs expect this also.
7351 utf8 flag setting needs to be preserved.
7357 /* If POSIX mode active, handle the conversion */
7358 #if __CRTL_VER >= 80200000 && !defined(__VAX)
7359 if (decc_efs_charset) {
7360 posix_to_vmsspec_hardway(rslt, rslt_len, path, dir_flag, utf8_flag);
7365 if (*(dirend+1) == '.') { /* do we have trailing "/." or "/.." or "/..."? */
7366 if (!*(dirend+2)) dirend +=2;
7367 if (*(dirend+2) == '.' && !*(dirend+3)) dirend += 3;
7368 if (decc_efs_charset == 0) {
7369 if (*(dirend+2) == '.' && *(dirend+3) == '.' && !*(dirend+4)) dirend += 4;
7375 lastdot = strrchr(cp2,'.');
7381 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
7383 if (decc_disable_posix_root) {
7384 strcpy(rslt,"sys$disk:[000000]");
7387 strcpy(rslt,"sys$posix_root:[000000]");
7389 if (utf8_flag != NULL)
7393 while (*(++cp2) != '/' && *cp2) *(cp1++) = *cp2;
7395 trndev = PerlMem_malloc(VMS_MAXRSS);
7396 if (trndev == NULL) _ckvmssts(SS$_INSFMEM);
7397 islnm = my_trnlnm(rslt,trndev,0);
7399 /* DECC special handling */
7401 if (strcmp(rslt,"bin") == 0) {
7402 strcpy(rslt,"sys$system");
7405 islnm = my_trnlnm(rslt,trndev,0);
7407 else if (strcmp(rslt,"tmp") == 0) {
7408 strcpy(rslt,"sys$scratch");
7411 islnm = my_trnlnm(rslt,trndev,0);
7413 else if (!decc_disable_posix_root) {
7414 strcpy(rslt, "sys$posix_root");
7418 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
7419 islnm = my_trnlnm(rslt,trndev,0);
7421 else if (strcmp(rslt,"dev") == 0) {
7422 if (strncmp(cp2,"/null", 5) == 0) {
7423 if ((cp2[5] == 0) || (cp2[5] == '/')) {
7424 strcpy(rslt,"NLA0");
7428 islnm = my_trnlnm(rslt,trndev,0);
7434 trnend = islnm ? strlen(trndev) - 1 : 0;
7435 islnm = trnend ? (trndev[trnend] == ']' || trndev[trnend] == '>') : 0;
7436 rooted = islnm ? (trndev[trnend-1] == '.') : 0;
7437 /* If the first element of the path is a logical name, determine
7438 * whether it has to be translated so we can add more directories. */
7439 if (!islnm || rooted) {
7442 if (cp2 == dirend) while (infront++ < 6) *(cp1++) = '0';
7446 if (cp2 != dirend) {
7447 strcpy(rslt,trndev);
7448 cp1 = rslt + trnend;
7455 if (decc_disable_posix_root) {
7461 PerlMem_free(trndev);
7466 if (*(cp2+1) == '/' || *(cp2+1) == '\0') {
7467 cp2 += 2; /* skip over "./" - it's redundant */
7468 *(cp1++) = '.'; /* but it does indicate a relative dirspec */
7470 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
7471 *(cp1++) = '-'; /* "../" --> "-" */
7474 else if (*(cp2+1) == '.' && *(cp2+2) == '.' &&
7475 (*(cp2+3) == '/' || *(cp2+3) == '\0')) {
7476 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
7477 if (!*(cp2+4)) *(cp1++) = '.'; /* Simulate trailing '/' for later */
7480 else if ((cp2 != lastdot) || (lastdot < dirend)) {
7481 /* Escape the extra dots in EFS file specifications */
7484 if (cp2 > dirend) cp2 = dirend;
7486 else *(cp1++) = '.';
7488 for (; cp2 < dirend; cp2++) {
7490 if (*(cp2-1) == '/') continue;
7491 if (*(cp1-1) != '.') *(cp1++) = '.';
7494 else if (!infront && *cp2 == '.') {
7495 if (cp2+1 == dirend || *(cp2+1) == '\0') { cp2++; break; }
7496 else if (*(cp2+1) == '/') cp2++; /* skip over "./" - it's redundant */
7497 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
7498 if (*(cp1-1) == '-' || *(cp1-1) == '[') *(cp1++) = '-'; /* handle "../" */
7499 else if (*(cp1-2) == '[') *(cp1-1) = '-';
7500 else { /* back up over previous directory name */
7502 while (*(cp1-1) != '.' && *(cp1-1) != '[') cp1--;
7503 if (*(cp1-1) == '[') {
7504 memcpy(cp1,"000000.",7);
7509 if (cp2 == dirend) break;
7511 else if ( *(cp2+1) == '.' && *(cp2+2) == '.' &&
7512 (*(cp2+3) == '/' || *(cp2+3) == '\0') ) {
7513 if (*(cp1-1) != '.') *(cp1++) = '.'; /* May already have 1 from '/' */
7514 *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
7516 *(cp1++) = '.'; /* Simulate trailing '/' */
7517 cp2 += 2; /* for loop will incr this to == dirend */
7519 else cp2 += 3; /* Trailing '/' was there, so skip it, too */
7522 if (decc_efs_charset == 0)
7523 *(cp1++) = '_'; /* fix up syntax - '.' in name not allowed */
7525 *(cp1++) = '^'; /* fix up syntax - '.' in name is allowed */
7531 if (!infront && *(cp1-1) == '-') *(cp1++) = '.';
7533 if (decc_efs_charset == 0)
7540 else *(cp1++) = *cp2;
7544 if (*(cp1-1) == '.') cp1--; /* Unix spec ending in '/' ==> trailing '.' */
7545 if (hasdir) *(cp1++) = ']';
7546 if (*cp2) cp2++; /* check in case we ended with trailing '..' */
7547 /* fixme for ODS5 */
7554 if (decc_efs_charset == 0)
7565 if (((cp2 < lastdot) || (cp2[1] == '\0')) &&
7566 decc_readdir_dropdotnotype) {
7571 /* trailing dot ==> '^..' on VMS */
7578 *(cp1++) = *(cp2++);
7583 /* This could be a macro to be passed through */
7584 *(cp1++) = *(cp2++);
7586 const char * save_cp2;
7590 /* paranoid check */
7596 *(cp1++) = *(cp2++);
7597 if (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
7598 *(cp1++) = *(cp2++);
7599 while (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
7600 *(cp1++) = *(cp2++);
7603 *(cp1++) = *(cp2++);
7607 if (is_macro == 0) {
7608 /* Not really a macro - never mind */
7621 /* Don't escape again if following character is
7622 * already something we escape.
7624 if (strchr("\"~`!#%^&()=+\'@[]{}:\\|<>_.", *(cp2+1))) {
7625 *(cp1++) = *(cp2++);
7628 /* But otherwise fall through and escape it. */
7646 *(cp1++) = *(cp2++);
7649 /* FIXME: This needs fixing as Perl is putting ".dir;" on UNIX filespecs
7650 * which is wrong. UNIX notation should be ".dir." unless
7651 * the DECC$FILENAME_UNIX_NO_VERSION is enabled.
7652 * changing this behavior could break more things at this time.
7653 * efs character set effectively does not allow "." to be a version
7654 * delimiter as a further complication about changing this.
7656 if (decc_filename_unix_report != 0) {
7659 *(cp1++) = *(cp2++);
7662 *(cp1++) = *(cp2++);
7665 if ((no_type_seen == 1) && decc_readdir_dropdotnotype) {
7669 /* Fix me for "^]", but that requires making sure that you do
7670 * not back up past the start of the filename
7672 if ((*lcp1 != ']') && (*lcp1 != '*') && (*lcp1 != '%'))
7677 if (utf8_flag != NULL)
7681 } /* end of do_tovmsspec() */
7683 /* External entry points */
7684 char *Perl_tovmsspec(pTHX_ const char *path, char *buf)
7685 { return do_tovmsspec(path,buf,0,NULL); }
7686 char *Perl_tovmsspec_ts(pTHX_ const char *path, char *buf)
7687 { return do_tovmsspec(path,buf,1,NULL); }
7688 char *Perl_tovmsspec_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
7689 { return do_tovmsspec(path,buf,0,utf8_fl); }
7690 char *Perl_tovmsspec_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
7691 { return do_tovmsspec(path,buf,1,utf8_fl); }
7693 /*{{{ char *tovmspath[_ts](char *path, char *buf, const int *)*/
7694 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) {
7695 static char __tovmspath_retbuf[VMS_MAXRSS];
7697 char *pathified, *vmsified, *cp;
7699 if (path == NULL) return NULL;
7700 pathified = PerlMem_malloc(VMS_MAXRSS);
7701 if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
7702 if (do_pathify_dirspec(path,pathified,0,NULL) == NULL) {
7703 PerlMem_free(pathified);
7709 Newx(vmsified, VMS_MAXRSS, char);
7710 if (do_tovmsspec(pathified, buf ? buf : vmsified, 0, NULL) == NULL) {
7711 PerlMem_free(pathified);
7712 if (vmsified) Safefree(vmsified);
7715 PerlMem_free(pathified);
7720 vmslen = strlen(vmsified);
7721 Newx(cp,vmslen+1,char);
7722 memcpy(cp,vmsified,vmslen);
7728 strcpy(__tovmspath_retbuf,vmsified);
7730 return __tovmspath_retbuf;
7733 } /* end of do_tovmspath() */
7735 /* External entry points */
7736 char *Perl_tovmspath(pTHX_ const char *path, char *buf)
7737 { return do_tovmspath(path,buf,0, NULL); }
7738 char *Perl_tovmspath_ts(pTHX_ const char *path, char *buf)
7739 { return do_tovmspath(path,buf,1, NULL); }
7740 char *Perl_tovmspath_utf8(pTHX_ const char *path, char *buf, int *utf8_fl)
7741 { return do_tovmspath(path,buf,0,utf8_fl); }
7742 char *Perl_tovmspath_utf8_ts(pTHX_ const char *path, char *buf, int *utf8_fl)
7743 { return do_tovmspath(path,buf,1,utf8_fl); }
7746 /*{{{ char *tounixpath[_ts](char *path, char *buf, int * utf8_fl)*/
7747 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) {
7748 static char __tounixpath_retbuf[VMS_MAXRSS];
7750 char *pathified, *unixified, *cp;
7752 if (path == NULL) return NULL;
7753 pathified = PerlMem_malloc(VMS_MAXRSS);
7754 if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
7755 if (do_pathify_dirspec(path,pathified,0,NULL) == NULL) {
7756 PerlMem_free(pathified);
7762 Newx(unixified, VMS_MAXRSS, char);
7764 if (do_tounixspec(pathified,buf ? buf : unixified,0,NULL) == NULL) {
7765 PerlMem_free(pathified);
7766 if (unixified) Safefree(unixified);
7769 PerlMem_free(pathified);
7774 unixlen = strlen(unixified);
7775 Newx(cp,unixlen+1,char);
7776 memcpy(cp,unixified,unixlen);
7778 Safefree(unixified);
7782 strcpy(__tounixpath_retbuf,unixified);
7783 Safefree(unixified);
7784 return __tounixpath_retbuf;
7787 } /* end of do_tounixpath() */
7789 /* External entry points */
7790 char *Perl_tounixpath(pTHX_ const char *path, char *buf)
7791 { return do_tounixpath(path,buf,0,NULL); }
7792 char *Perl_tounixpath_ts(pTHX_ const char *path, char *buf)
7793 { return do_tounixpath(path,buf,1,NULL); }
7794 char *Perl_tounixpath_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
7795 { return do_tounixpath(path,buf,0,utf8_fl); }
7796 char *Perl_tounixpath_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
7797 { return do_tounixpath(path,buf,1,utf8_fl); }
7800 * @(#)argproc.c 2.2 94/08/16 Mark Pizzolato (mark AT infocomm DOT com)
7802 *****************************************************************************
7804 * Copyright (C) 1989-1994, 2007 by *
7805 * Mark Pizzolato - INFO COMM, Danville, California (510) 837-5600 *
7807 * Permission is hereby granted for the reproduction of this software *
7808 * on condition that this copyright notice is included in source *
7809 * distributions of the software. The code may be modified and *
7810 * distributed under the same terms as Perl itself. *
7812 * 27-Aug-1994 Modified for inclusion in perl5 *
7813 * by Charles Bailey (bailey AT newman DOT upenn DOT edu) *
7814 *****************************************************************************
7818 * getredirection() is intended to aid in porting C programs
7819 * to VMS (Vax-11 C). The native VMS environment does not support
7820 * '>' and '<' I/O redirection, or command line wild card expansion,
7821 * or a command line pipe mechanism using the '|' AND background
7822 * command execution '&'. All of these capabilities are provided to any
7823 * C program which calls this procedure as the first thing in the
7825 * The piping mechanism will probably work with almost any 'filter' type
7826 * of program. With suitable modification, it may useful for other
7827 * portability problems as well.
7829 * Author: Mark Pizzolato (mark AT infocomm DOT com)
7833 struct list_item *next;
7837 static void add_item(struct list_item **head,
7838 struct list_item **tail,
7842 static void mp_expand_wild_cards(pTHX_ char *item,
7843 struct list_item **head,
7844 struct list_item **tail,
7847 static int background_process(pTHX_ int argc, char **argv);
7849 static void pipe_and_fork(pTHX_ char **cmargv);
7851 /*{{{ void getredirection(int *ac, char ***av)*/
7853 mp_getredirection(pTHX_ int *ac, char ***av)
7855 * Process vms redirection arg's. Exit if any error is seen.
7856 * If getredirection() processes an argument, it is erased
7857 * from the vector. getredirection() returns a new argc and argv value.
7858 * In the event that a background command is requested (by a trailing "&"),
7859 * this routine creates a background subprocess, and simply exits the program.
7861 * Warning: do not try to simplify the code for vms. The code
7862 * presupposes that getredirection() is called before any data is
7863 * read from stdin or written to stdout.
7865 * Normal usage is as follows:
7871 * getredirection(&argc, &argv);
7875 int argc = *ac; /* Argument Count */
7876 char **argv = *av; /* Argument Vector */
7877 char *ap; /* Argument pointer */
7878 int j; /* argv[] index */
7879 int item_count = 0; /* Count of Items in List */
7880 struct list_item *list_head = 0; /* First Item in List */
7881 struct list_item *list_tail; /* Last Item in List */
7882 char *in = NULL; /* Input File Name */
7883 char *out = NULL; /* Output File Name */
7884 char *outmode = "w"; /* Mode to Open Output File */
7885 char *err = NULL; /* Error File Name */
7886 char *errmode = "w"; /* Mode to Open Error File */
7887 int cmargc = 0; /* Piped Command Arg Count */
7888 char **cmargv = NULL;/* Piped Command Arg Vector */
7891 * First handle the case where the last thing on the line ends with
7892 * a '&'. This indicates the desire for the command to be run in a
7893 * subprocess, so we satisfy that desire.
7896 if (0 == strcmp("&", ap))
7897 exit(background_process(aTHX_ --argc, argv));
7898 if (*ap && '&' == ap[strlen(ap)-1])
7900 ap[strlen(ap)-1] = '\0';
7901 exit(background_process(aTHX_ argc, argv));
7904 * Now we handle the general redirection cases that involve '>', '>>',
7905 * '<', and pipes '|'.
7907 for (j = 0; j < argc; ++j)
7909 if (0 == strcmp("<", argv[j]))
7913 fprintf(stderr,"No input file after < on command line");
7914 exit(LIB$_WRONUMARG);
7919 if ('<' == *(ap = argv[j]))
7924 if (0 == strcmp(">", ap))
7928 fprintf(stderr,"No output file after > on command line");
7929 exit(LIB$_WRONUMARG);
7948 fprintf(stderr,"No output file after > or >> on command line");
7949 exit(LIB$_WRONUMARG);
7953 if (('2' == *ap) && ('>' == ap[1]))
7970 fprintf(stderr,"No output file after 2> or 2>> on command line");
7971 exit(LIB$_WRONUMARG);
7975 if (0 == strcmp("|", argv[j]))
7979 fprintf(stderr,"No command into which to pipe on command line");
7980 exit(LIB$_WRONUMARG);
7982 cmargc = argc-(j+1);
7983 cmargv = &argv[j+1];
7987 if ('|' == *(ap = argv[j]))
7995 expand_wild_cards(ap, &list_head, &list_tail, &item_count);
7998 * Allocate and fill in the new argument vector, Some Unix's terminate
7999 * the list with an extra null pointer.
8001 argv = (char **) PerlMem_malloc((item_count+1) * sizeof(char *));
8002 if (argv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8004 for (j = 0; j < item_count; ++j, list_head = list_head->next)
8005 argv[j] = list_head->value;
8011 fprintf(stderr,"'|' and '>' may not both be specified on command line");
8012 exit(LIB$_INVARGORD);
8014 pipe_and_fork(aTHX_ cmargv);
8017 /* Check for input from a pipe (mailbox) */
8019 if (in == NULL && 1 == isapipe(0))
8021 char mbxname[L_tmpnam];
8023 long int dvi_item = DVI$_DEVBUFSIZ;
8024 $DESCRIPTOR(mbxnam, "");
8025 $DESCRIPTOR(mbxdevnam, "");
8027 /* Input from a pipe, reopen it in binary mode to disable */
8028 /* carriage control processing. */
8030 fgetname(stdin, mbxname);
8031 mbxnam.dsc$a_pointer = mbxname;
8032 mbxnam.dsc$w_length = strlen(mbxnam.dsc$a_pointer);
8033 lib$getdvi(&dvi_item, 0, &mbxnam, &bufsize, 0, 0);
8034 mbxdevnam.dsc$a_pointer = mbxname;
8035 mbxdevnam.dsc$w_length = sizeof(mbxname);
8036 dvi_item = DVI$_DEVNAM;
8037 lib$getdvi(&dvi_item, 0, &mbxnam, 0, &mbxdevnam, &mbxdevnam.dsc$w_length);
8038 mbxdevnam.dsc$a_pointer[mbxdevnam.dsc$w_length] = '\0';
8041 freopen(mbxname, "rb", stdin);
8044 fprintf(stderr,"Can't reopen input pipe (name: %s) in binary mode",mbxname);
8048 if ((in != NULL) && (NULL == freopen(in, "r", stdin, "mbc=32", "mbf=2")))
8050 fprintf(stderr,"Can't open input file %s as stdin",in);
8053 if ((out != NULL) && (NULL == freopen(out, outmode, stdout, "mbc=32", "mbf=2")))
8055 fprintf(stderr,"Can't open output file %s as stdout",out);
8058 if (out != NULL) Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",out);
8061 if (strcmp(err,"&1") == 0) {
8062 dup2(fileno(stdout), fileno(stderr));
8063 Perl_vmssetuserlnm(aTHX_ "SYS$ERROR","SYS$OUTPUT");
8066 if (NULL == (tmperr = fopen(err, errmode, "mbc=32", "mbf=2")))
8068 fprintf(stderr,"Can't open error file %s as stderr",err);
8072 if (NULL == freopen(err, "a", stderr, "mbc=32", "mbf=2"))
8076 Perl_vmssetuserlnm(aTHX_ "SYS$ERROR",err);
8079 #ifdef ARGPROC_DEBUG
8080 PerlIO_printf(Perl_debug_log, "Arglist:\n");
8081 for (j = 0; j < *ac; ++j)
8082 PerlIO_printf(Perl_debug_log, "argv[%d] = '%s'\n", j, argv[j]);
8084 /* Clear errors we may have hit expanding wildcards, so they don't
8085 show up in Perl's $! later */
8086 set_errno(0); set_vaxc_errno(1);
8087 } /* end of getredirection() */
8090 static void add_item(struct list_item **head,
8091 struct list_item **tail,
8097 *head = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
8098 if (head == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8102 (*tail)->next = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
8103 if ((*tail)->next == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8104 *tail = (*tail)->next;
8106 (*tail)->value = value;
8110 static void mp_expand_wild_cards(pTHX_ char *item,
8111 struct list_item **head,
8112 struct list_item **tail,
8116 unsigned long int context = 0;
8124 $DESCRIPTOR(filespec, "");
8125 $DESCRIPTOR(defaultspec, "SYS$DISK:[]");
8126 $DESCRIPTOR(resultspec, "");
8127 unsigned long int lff_flags = 0;
8131 #ifdef VMS_LONGNAME_SUPPORT
8132 lff_flags = LIB$M_FIL_LONG_NAMES;
8135 for (cp = item; *cp; cp++) {
8136 if (*cp == '*' || *cp == '%' || isspace(*cp)) break;
8137 if (*cp == '.' && *(cp-1) == '.' && *(cp-2) =='.') break;
8139 if (!*cp || isspace(*cp))
8141 add_item(head, tail, item, count);
8146 /* "double quoted" wild card expressions pass as is */
8147 /* From DCL that means using e.g.: */
8148 /* perl program """perl.*""" */
8149 item_len = strlen(item);
8150 if ( '"' == *item && '"' == item[item_len-1] )
8153 item[item_len-2] = '\0';
8154 add_item(head, tail, item, count);
8158 resultspec.dsc$b_dtype = DSC$K_DTYPE_T;
8159 resultspec.dsc$b_class = DSC$K_CLASS_D;
8160 resultspec.dsc$a_pointer = NULL;
8161 vmsspec = PerlMem_malloc(VMS_MAXRSS);
8162 if (vmsspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8163 if ((isunix = (int) strchr(item,'/')) != (int) NULL)
8164 filespec.dsc$a_pointer = do_tovmsspec(item,vmsspec,0,NULL);
8165 if (!isunix || !filespec.dsc$a_pointer)
8166 filespec.dsc$a_pointer = item;
8167 filespec.dsc$w_length = strlen(filespec.dsc$a_pointer);
8169 * Only return version specs, if the caller specified a version
8171 had_version = strchr(item, ';');
8173 * Only return device and directory specs, if the caller specifed either.
8175 had_device = strchr(item, ':');
8176 had_directory = (isunix || NULL != strchr(item, '[')) || (NULL != strchr(item, '<'));
8178 while ($VMS_STATUS_SUCCESS(sts = lib$find_file
8179 (&filespec, &resultspec, &context,
8180 &defaultspec, 0, &rms_sts, &lff_flags)))
8185 string = PerlMem_malloc(resultspec.dsc$w_length+1);
8186 if (string == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8187 strncpy(string, resultspec.dsc$a_pointer, resultspec.dsc$w_length);
8188 string[resultspec.dsc$w_length] = '\0';
8189 if (NULL == had_version)
8190 *(strrchr(string, ';')) = '\0';
8191 if ((!had_directory) && (had_device == NULL))
8193 if (NULL == (devdir = strrchr(string, ']')))
8194 devdir = strrchr(string, '>');
8195 strcpy(string, devdir + 1);
8198 * Be consistent with what the C RTL has already done to the rest of
8199 * the argv items and lowercase all of these names.
8201 if (!decc_efs_case_preserve) {
8202 for (c = string; *c; ++c)
8206 if (isunix) trim_unixpath(string,item,1);
8207 add_item(head, tail, string, count);
8210 PerlMem_free(vmsspec);
8211 if (sts != RMS$_NMF)
8213 set_vaxc_errno(sts);
8216 case RMS$_FNF: case RMS$_DNF:
8217 set_errno(ENOENT); break;
8219 set_errno(ENOTDIR); break;
8221 set_errno(ENODEV); break;
8222 case RMS$_FNM: case RMS$_SYN:
8223 set_errno(EINVAL); break;
8225 set_errno(EACCES); break;
8227 _ckvmssts_noperl(sts);
8231 add_item(head, tail, item, count);
8232 _ckvmssts_noperl(lib$sfree1_dd(&resultspec));
8233 _ckvmssts_noperl(lib$find_file_end(&context));
8236 static int child_st[2];/* Event Flag set when child process completes */
8238 static unsigned short child_chan;/* I/O Channel for Pipe Mailbox */
8240 static unsigned long int exit_handler(int *status)
8244 if (0 == child_st[0])
8246 #ifdef ARGPROC_DEBUG
8247 PerlIO_printf(Perl_debug_log, "Waiting for Child Process to Finish . . .\n");
8249 fflush(stdout); /* Have to flush pipe for binary data to */
8250 /* terminate properly -- <tp@mccall.com> */
8251 sys$qiow(0, child_chan, IO$_WRITEOF, iosb, 0, 0, 0, 0, 0, 0, 0, 0);
8252 sys$dassgn(child_chan);
8254 sys$synch(0, child_st);
8259 static void sig_child(int chan)
8261 #ifdef ARGPROC_DEBUG
8262 PerlIO_printf(Perl_debug_log, "Child Completion AST\n");
8264 if (child_st[0] == 0)
8268 static struct exit_control_block exit_block =
8273 &exit_block.exit_status,
8278 pipe_and_fork(pTHX_ char **cmargv)
8281 struct dsc$descriptor_s *vmscmd;
8282 char subcmd[2*MAX_DCL_LINE_LENGTH], *p, *q;
8283 int sts, j, l, ismcr, quote, tquote = 0;
8285 sts = setup_cmddsc(aTHX_ cmargv[0],0,"e,&vmscmd);
8286 vms_execfree(vmscmd);
8291 ismcr = q && toupper(*q) == 'M' && toupper(*(q+1)) == 'C'
8292 && toupper(*(q+2)) == 'R' && !*(q+3);
8294 while (q && l < MAX_DCL_LINE_LENGTH) {
8296 if (j > 0 && quote) {
8302 if (ismcr && j > 1) quote = 1;
8303 tquote = (strchr(q,' ')) != NULL || *q == '\0';
8306 if (quote || tquote) {
8312 if ((quote||tquote) && *q == '"') {
8322 fp = safe_popen(aTHX_ subcmd,"wbF",&sts);
8324 PerlIO_printf(Perl_debug_log,"Can't open output pipe (status %d)",sts);
8328 static int background_process(pTHX_ int argc, char **argv)
8330 char command[MAX_DCL_SYMBOL + 1] = "$";
8331 $DESCRIPTOR(value, "");
8332 static $DESCRIPTOR(cmd, "BACKGROUND$COMMAND");
8333 static $DESCRIPTOR(null, "NLA0:");
8334 static $DESCRIPTOR(pidsymbol, "SHELL_BACKGROUND_PID");
8336 $DESCRIPTOR(pidstr, "");
8338 unsigned long int flags = 17, one = 1, retsts;
8341 strcat(command, argv[0]);
8342 len = strlen(command);
8343 while (--argc && (len < MAX_DCL_SYMBOL))
8345 strcat(command, " \"");
8346 strcat(command, *(++argv));
8347 strcat(command, "\"");
8348 len = strlen(command);
8350 value.dsc$a_pointer = command;
8351 value.dsc$w_length = strlen(value.dsc$a_pointer);
8352 _ckvmssts_noperl(lib$set_symbol(&cmd, &value));
8353 retsts = lib$spawn(&cmd, &null, 0, &flags, 0, &pid);
8354 if (retsts == 0x38250) { /* DCL-W-NOTIFY - We must be BATCH, so retry */
8355 _ckvmssts_noperl(lib$spawn(&cmd, &null, 0, &one, 0, &pid));
8358 _ckvmssts_noperl(retsts);
8360 #ifdef ARGPROC_DEBUG
8361 PerlIO_printf(Perl_debug_log, "%s\n", command);
8363 sprintf(pidstring, "%08X", pid);
8364 PerlIO_printf(Perl_debug_log, "%s\n", pidstring);
8365 pidstr.dsc$a_pointer = pidstring;
8366 pidstr.dsc$w_length = strlen(pidstr.dsc$a_pointer);
8367 lib$set_symbol(&pidsymbol, &pidstr);
8371 /***** End of code taken from Mark Pizzolato's argproc.c package *****/
8374 /* OS-specific initialization at image activation (not thread startup) */
8375 /* Older VAXC header files lack these constants */
8376 #ifndef JPI$_RIGHTS_SIZE
8377 # define JPI$_RIGHTS_SIZE 817
8379 #ifndef KGB$M_SUBSYSTEM
8380 # define KGB$M_SUBSYSTEM 0x8
8383 /* Avoid Newx() in vms_image_init as thread context has not been initialized. */
8385 /*{{{void vms_image_init(int *, char ***)*/
8387 vms_image_init(int *argcp, char ***argvp)
8389 char eqv[LNM$C_NAMLENGTH+1] = "";
8390 unsigned int len, tabct = 8, tabidx = 0;
8391 unsigned long int *mask, iosb[2], i, rlst[128], rsz;
8392 unsigned long int iprv[(sizeof(union prvdef) + sizeof(unsigned long int) - 1) / sizeof(unsigned long int)];
8393 unsigned short int dummy, rlen;
8394 struct dsc$descriptor_s **tabvec;
8395 #if defined(PERL_IMPLICIT_CONTEXT)
8398 struct itmlst_3 jpilist[4] = { {sizeof iprv, JPI$_IMAGPRIV, iprv, &dummy},
8399 {sizeof rlst, JPI$_RIGHTSLIST, rlst, &rlen},
8400 { sizeof rsz, JPI$_RIGHTS_SIZE, &rsz, &dummy},
8403 #ifdef KILL_BY_SIGPRC
8404 Perl_csighandler_init();
8407 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,jpilist,iosb,NULL,NULL));
8408 _ckvmssts_noperl(iosb[0]);
8409 for (i = 0; i < sizeof iprv / sizeof(unsigned long int); i++) {
8410 if (iprv[i]) { /* Running image installed with privs? */
8411 _ckvmssts_noperl(sys$setprv(0,iprv,0,NULL)); /* Turn 'em off. */
8416 /* Rights identifiers might trigger tainting as well. */
8417 if (!will_taint && (rlen || rsz)) {
8418 while (rlen < rsz) {
8419 /* We didn't get all the identifiers on the first pass. Allocate a
8420 * buffer much larger than $GETJPI wants (rsz is size in bytes that
8421 * were needed to hold all identifiers at time of last call; we'll
8422 * allocate that many unsigned long ints), and go back and get 'em.
8423 * If it gave us less than it wanted to despite ample buffer space,
8424 * something's broken. Is your system missing a system identifier?
8426 if (rsz <= jpilist[1].buflen) {
8427 /* Perl_croak accvios when used this early in startup. */
8428 fprintf(stderr, "vms_image_init: $getjpiw refuses to store RIGHTSLIST of %u bytes in buffer of %u bytes.\n%s",
8429 rsz, (unsigned long) jpilist[1].buflen,
8430 "Check your rights database for corruption.\n");
8433 if (jpilist[1].bufadr != rlst) PerlMem_free(jpilist[1].bufadr);
8434 jpilist[1].bufadr = mask = (unsigned long int *) PerlMem_malloc(rsz * sizeof(unsigned long int));
8435 if (mask == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8436 jpilist[1].buflen = rsz * sizeof(unsigned long int);
8437 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,&jpilist[1],iosb,NULL,NULL));
8438 _ckvmssts_noperl(iosb[0]);
8440 mask = jpilist[1].bufadr;
8441 /* Check attribute flags for each identifier (2nd longword); protected
8442 * subsystem identifiers trigger tainting.
8444 for (i = 1; i < (rlen + sizeof(unsigned long int) - 1) / sizeof(unsigned long int); i += 2) {
8445 if (mask[i] & KGB$M_SUBSYSTEM) {
8450 if (mask != rlst) PerlMem_free(mask);
8453 /* When Perl is in decc_filename_unix_report mode and is run from a concealed
8454 * logical, some versions of the CRTL will add a phanthom /000000/
8455 * directory. This needs to be removed.
8457 if (decc_filename_unix_report) {
8460 ulen = strlen(argvp[0][0]);
8462 zeros = strstr(argvp[0][0], "/000000/");
8463 if (zeros != NULL) {
8465 mlen = ulen - (zeros - argvp[0][0]) - 7;
8466 memmove(zeros, &zeros[7], mlen);
8468 argvp[0][0][ulen] = '\0';
8471 /* It also may have a trailing dot that needs to be removed otherwise
8472 * it will be converted to VMS mode incorrectly.
8475 if ((argvp[0][0][ulen] == '.') && (decc_readdir_dropdotnotype))
8476 argvp[0][0][ulen] = '\0';
8479 /* We need to use this hack to tell Perl it should run with tainting,
8480 * since its tainting flag may be part of the PL_curinterp struct, which
8481 * hasn't been allocated when vms_image_init() is called.
8484 char **newargv, **oldargv;
8486 newargv = (char **) PerlMem_malloc(((*argcp)+2) * sizeof(char *));
8487 if (newargv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8488 newargv[0] = oldargv[0];
8489 newargv[1] = PerlMem_malloc(3 * sizeof(char));
8490 if (newargv[1] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8491 strcpy(newargv[1], "-T");
8492 Copy(&oldargv[1],&newargv[2],(*argcp)-1,char **);
8494 newargv[*argcp] = NULL;
8495 /* We orphan the old argv, since we don't know where it's come from,
8496 * so we don't know how to free it.
8500 else { /* Did user explicitly request tainting? */
8502 char *cp, **av = *argvp;
8503 for (i = 1; i < *argcp; i++) {
8504 if (*av[i] != '-') break;
8505 for (cp = av[i]+1; *cp; cp++) {
8506 if (*cp == 'T') { will_taint = 1; break; }
8507 else if ( (*cp == 'd' || *cp == 'V') && *(cp+1) == ':' ||
8508 strchr("DFIiMmx",*cp)) break;
8510 if (will_taint) break;
8515 len = my_trnlnm("PERL_ENV_TABLES",eqv,tabidx);
8518 tabvec = (struct dsc$descriptor_s **)
8519 PerlMem_malloc(tabct * sizeof(struct dsc$descriptor_s *));
8520 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8522 else if (tabidx >= tabct) {
8524 tabvec = (struct dsc$descriptor_s **) PerlMem_realloc(tabvec, tabct * sizeof(struct dsc$descriptor_s *));
8525 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8527 tabvec[tabidx] = (struct dsc$descriptor_s *) PerlMem_malloc(sizeof(struct dsc$descriptor_s));
8528 if (tabvec[tabidx] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8529 tabvec[tabidx]->dsc$w_length = 0;
8530 tabvec[tabidx]->dsc$b_dtype = DSC$K_DTYPE_T;
8531 tabvec[tabidx]->dsc$b_class = DSC$K_CLASS_D;
8532 tabvec[tabidx]->dsc$a_pointer = NULL;
8533 _ckvmssts_noperl(lib$scopy_r_dx(&len,eqv,tabvec[tabidx]));
8535 if (tabidx) { tabvec[tabidx] = NULL; env_tables = tabvec; }
8537 getredirection(argcp,argvp);
8538 #if defined(USE_ITHREADS) && ( defined(__DECC) || defined(__DECCXX) )
8540 # include <reentrancy.h>
8541 decc$set_reentrancy(C$C_MULTITHREAD);
8550 * Trim Unix-style prefix off filespec, so it looks like what a shell
8551 * glob expansion would return (i.e. from specified prefix on, not
8552 * full path). Note that returned filespec is Unix-style, regardless
8553 * of whether input filespec was VMS-style or Unix-style.
8555 * fspec is filespec to be trimmed, and wildspec is wildcard spec used to
8556 * determine prefix (both may be in VMS or Unix syntax). opts is a bit
8557 * vector of options; at present, only bit 0 is used, and if set tells
8558 * trim unixpath to try the current default directory as a prefix when
8559 * presented with a possibly ambiguous ... wildcard.
8561 * Returns !=0 on success, with trimmed filespec replacing contents of
8562 * fspec, and 0 on failure, with contents of fpsec unchanged.
8564 /*{{{int trim_unixpath(char *fspec, char *wildspec, int opts)*/
8566 Perl_trim_unixpath(pTHX_ char *fspec, const char *wildspec, int opts)
8568 char *unixified, *unixwild,
8569 *template, *base, *end, *cp1, *cp2;
8570 register int tmplen, reslen = 0, dirs = 0;
8572 unixwild = PerlMem_malloc(VMS_MAXRSS);
8573 if (unixwild == NULL) _ckvmssts(SS$_INSFMEM);
8574 if (!wildspec || !fspec) return 0;
8575 template = unixwild;
8576 if (strpbrk(wildspec,"]>:") != NULL) {
8577 if (do_tounixspec(wildspec,unixwild,0,NULL) == NULL) {
8578 PerlMem_free(unixwild);
8583 strncpy(unixwild, wildspec, VMS_MAXRSS-1);
8584 unixwild[VMS_MAXRSS-1] = 0;
8586 unixified = PerlMem_malloc(VMS_MAXRSS);
8587 if (unixified == NULL) _ckvmssts(SS$_INSFMEM);
8588 if (strpbrk(fspec,"]>:") != NULL) {
8589 if (do_tounixspec(fspec,unixified,0,NULL) == NULL) {
8590 PerlMem_free(unixwild);
8591 PerlMem_free(unixified);
8594 else base = unixified;
8595 /* reslen != 0 ==> we had to unixify resultant filespec, so we must
8596 * check to see that final result fits into (isn't longer than) fspec */
8597 reslen = strlen(fspec);
8601 /* No prefix or absolute path on wildcard, so nothing to remove */
8602 if (!*template || *template == '/') {
8603 PerlMem_free(unixwild);
8604 if (base == fspec) {
8605 PerlMem_free(unixified);
8608 tmplen = strlen(unixified);
8609 if (tmplen > reslen) {
8610 PerlMem_free(unixified);
8611 return 0; /* not enough space */
8613 /* Copy unixified resultant, including trailing NUL */
8614 memmove(fspec,unixified,tmplen+1);
8615 PerlMem_free(unixified);
8619 for (end = base; *end; end++) ; /* Find end of resultant filespec */
8620 if ((cp1 = strstr(template,".../")) == NULL) { /* No ...; just count elts */
8621 for (cp1 = template; *cp1; cp1++) if (*cp1 == '/') dirs++;
8622 for (cp1 = end ;cp1 >= base; cp1--)
8623 if ((*cp1 == '/') && !dirs--) /* postdec so we get front of rel path */
8625 if (cp1 != fspec) memmove(fspec,cp1, end - cp1 + 1);
8626 PerlMem_free(unixified);
8627 PerlMem_free(unixwild);
8632 char *front, *nextell, *lcend, *lcfront, *ellipsis = cp1;
8633 int ells = 1, totells, segdirs, match;
8634 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL},
8635 resdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
8637 while ((cp1 = strstr(ellipsis+4,".../")) != NULL) {ellipsis = cp1; ells++;}
8639 for (cp1 = ellipsis+4; *cp1; cp1++) if (*cp1 == '/') dirs++;
8640 tpl = PerlMem_malloc(VMS_MAXRSS);
8641 if (tpl == NULL) _ckvmssts(SS$_INSFMEM);
8642 if (ellipsis == template && opts & 1) {
8643 /* Template begins with an ellipsis. Since we can't tell how many
8644 * directory names at the front of the resultant to keep for an
8645 * arbitrary starting point, we arbitrarily choose the current
8646 * default directory as a starting point. If it's there as a prefix,
8647 * clip it off. If not, fall through and act as if the leading
8648 * ellipsis weren't there (i.e. return shortest possible path that
8649 * could match template).
8651 if (getcwd(tpl, (VMS_MAXRSS - 1),0) == NULL) {
8653 PerlMem_free(unixified);
8654 PerlMem_free(unixwild);
8657 if (!decc_efs_case_preserve) {
8658 for (cp1 = tpl, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
8659 if (_tolower(*cp1) != _tolower(*cp2)) break;
8661 segdirs = dirs - totells; /* Min # of dirs we must have left */
8662 for (front = cp2+1; *front; front++) if (*front == '/') segdirs--;
8663 if (*cp1 == '\0' && *cp2 == '/' && segdirs < 1) {
8664 memmove(fspec,cp2+1,end - cp2);
8666 PerlMem_free(unixified);
8667 PerlMem_free(unixwild);
8671 /* First off, back up over constant elements at end of path */
8673 for (front = end ; front >= base; front--)
8674 if (*front == '/' && !dirs--) { front++; break; }
8676 lcres = PerlMem_malloc(VMS_MAXRSS);
8677 if (lcres == NULL) _ckvmssts(SS$_INSFMEM);
8678 for (cp1=template,cp2=lcres; *cp1 && cp2 <= lcres + (VMS_MAXRSS - 1);
8680 if (!decc_efs_case_preserve) {
8681 *cp2 = _tolower(*cp1); /* Make lc copy for match */
8689 PerlMem_free(unixified);
8690 PerlMem_free(unixwild);
8691 PerlMem_free(lcres);
8692 return 0; /* Path too long. */
8695 *cp2 = '\0'; /* Pick up with memcpy later */
8696 lcfront = lcres + (front - base);
8697 /* Now skip over each ellipsis and try to match the path in front of it. */
8699 for (cp1 = ellipsis - 2; cp1 >= template; cp1--)
8700 if (*(cp1) == '.' && *(cp1+1) == '.' &&
8701 *(cp1+2) == '.' && *(cp1+3) == '/' ) break;
8702 if (cp1 < template) break; /* template started with an ellipsis */
8703 if (cp1 + 4 == ellipsis) { /* Consecutive ellipses */
8704 ellipsis = cp1; continue;
8706 wilddsc.dsc$a_pointer = tpl;
8707 wilddsc.dsc$w_length = resdsc.dsc$w_length = ellipsis - 1 - cp1;
8709 for (segdirs = 0, cp2 = tpl;
8710 cp1 <= ellipsis - 1 && cp2 <= tpl + (VMS_MAXRSS-1);
8712 if (*cp1 == '?') *cp2 = '%'; /* Substitute VMS' wildcard for Unix' */
8714 if (!decc_efs_case_preserve) {
8715 *cp2 = _tolower(*cp1); /* else lowercase for match */
8718 *cp2 = *cp1; /* else preserve case for match */
8721 if (*cp2 == '/') segdirs++;
8723 if (cp1 != ellipsis - 1) {
8725 PerlMem_free(unixified);
8726 PerlMem_free(unixwild);
8727 PerlMem_free(lcres);
8728 return 0; /* Path too long */
8730 /* Back up at least as many dirs as in template before matching */
8731 for (cp1 = lcfront - 1; segdirs && cp1 >= lcres; cp1--)
8732 if (*cp1 == '/' && !segdirs--) { cp1++; break; }
8733 for (match = 0; cp1 > lcres;) {
8734 resdsc.dsc$a_pointer = cp1;
8735 if (str$match_wild(&wilddsc,&resdsc) == STR$_MATCH) {
8737 if (match == 1) lcfront = cp1;
8739 for ( ; cp1 >= lcres; cp1--) if (*cp1 == '/') { cp1++; break; }
8743 PerlMem_free(unixified);
8744 PerlMem_free(unixwild);
8745 PerlMem_free(lcres);
8746 return 0; /* Can't find prefix ??? */
8748 if (match > 1 && opts & 1) {
8749 /* This ... wildcard could cover more than one set of dirs (i.e.
8750 * a set of similar dir names is repeated). If the template
8751 * contains more than 1 ..., upstream elements could resolve the
8752 * ambiguity, but it's not worth a full backtracking setup here.
8753 * As a quick heuristic, clip off the current default directory
8754 * if it's present to find the trimmed spec, else use the
8755 * shortest string that this ... could cover.
8757 char def[NAM$C_MAXRSS+1], *st;
8759 if (getcwd(def, sizeof def,0) == NULL) {
8760 Safefree(unixified);
8766 if (!decc_efs_case_preserve) {
8767 for (cp1 = def, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
8768 if (_tolower(*cp1) != _tolower(*cp2)) break;
8770 segdirs = dirs - totells; /* Min # of dirs we must have left */
8771 for (st = cp2+1; *st; st++) if (*st == '/') segdirs--;
8772 if (*cp1 == '\0' && *cp2 == '/') {
8773 memmove(fspec,cp2+1,end - cp2);
8775 PerlMem_free(unixified);
8776 PerlMem_free(unixwild);
8777 PerlMem_free(lcres);
8780 /* Nope -- stick with lcfront from above and keep going. */
8783 memmove(fspec,base + (lcfront - lcres), lcend - lcfront + 1);
8785 PerlMem_free(unixified);
8786 PerlMem_free(unixwild);
8787 PerlMem_free(lcres);
8792 } /* end of trim_unixpath() */
8797 * VMS readdir() routines.
8798 * Written by Rich $alz, <rsalz@bbn.com> in August, 1990.
8800 * 21-Jul-1994 Charles Bailey bailey@newman.upenn.edu
8801 * Minor modifications to original routines.
8804 /* readdir may have been redefined by reentr.h, so make sure we get
8805 * the local version for what we do here.
8810 #if !defined(PERL_IMPLICIT_CONTEXT)
8811 # define readdir Perl_readdir
8813 # define readdir(a) Perl_readdir(aTHX_ a)
8816 /* Number of elements in vms_versions array */
8817 #define VERSIZE(e) (sizeof e->vms_versions / sizeof e->vms_versions[0])
8820 * Open a directory, return a handle for later use.
8822 /*{{{ DIR *opendir(char*name) */
8824 Perl_opendir(pTHX_ const char *name)
8830 Newx(dir, VMS_MAXRSS, char);
8831 if (do_tovmspath(name,dir,0,NULL) == NULL) {
8835 /* Check access before stat; otherwise stat does not
8836 * accurately report whether it's a directory.
8838 if (!cando_by_name_int(S_IRUSR,0,dir,PERL_RMSEXPAND_M_VMS_IN)) {
8839 /* cando_by_name has already set errno */
8843 if (flex_stat(dir,&sb) == -1) return NULL;
8844 if (!S_ISDIR(sb.st_mode)) {
8846 set_errno(ENOTDIR); set_vaxc_errno(RMS$_DIR);
8849 /* Get memory for the handle, and the pattern. */
8851 Newx(dd->pattern,strlen(dir)+sizeof "*.*" + 1,char);
8853 /* Fill in the fields; mainly playing with the descriptor. */
8854 sprintf(dd->pattern, "%s*.*",dir);
8859 /* By saying we always want the result of readdir() in unix format, we
8860 * are really saying we want all the escapes removed. Otherwise the caller,
8861 * having no way to know whether it's already in VMS format, might send it
8862 * through tovmsspec again, thus double escaping.
8864 dd->flags = PERL_VMSDIR_M_UNIXSPECS;
8865 dd->pat.dsc$a_pointer = dd->pattern;
8866 dd->pat.dsc$w_length = strlen(dd->pattern);
8867 dd->pat.dsc$b_dtype = DSC$K_DTYPE_T;
8868 dd->pat.dsc$b_class = DSC$K_CLASS_S;
8869 #if defined(USE_ITHREADS)
8870 Newx(dd->mutex,1,perl_mutex);
8871 MUTEX_INIT( (perl_mutex *) dd->mutex );
8877 } /* end of opendir() */
8881 * Set the flag to indicate we want versions or not.
8883 /*{{{ void vmsreaddirversions(DIR *dd, int flag)*/
8885 vmsreaddirversions(DIR *dd, int flag)
8888 dd->flags |= PERL_VMSDIR_M_VERSIONS;
8890 dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
8895 * Free up an opened directory.
8897 /*{{{ void closedir(DIR *dd)*/
8899 Perl_closedir(DIR *dd)
8903 sts = lib$find_file_end(&dd->context);
8904 Safefree(dd->pattern);
8905 #if defined(USE_ITHREADS)
8906 MUTEX_DESTROY( (perl_mutex *) dd->mutex );
8907 Safefree(dd->mutex);
8914 * Collect all the version numbers for the current file.
8917 collectversions(pTHX_ DIR *dd)
8919 struct dsc$descriptor_s pat;
8920 struct dsc$descriptor_s res;
8922 char *p, *text, *buff;
8924 unsigned long context, tmpsts;
8926 /* Convenient shorthand. */
8929 /* Add the version wildcard, ignoring the "*.*" put on before */
8930 i = strlen(dd->pattern);
8931 Newx(text,i + e->d_namlen + 3,char);
8932 strcpy(text, dd->pattern);
8933 sprintf(&text[i - 3], "%s;*", e->d_name);
8935 /* Set up the pattern descriptor. */
8936 pat.dsc$a_pointer = text;
8937 pat.dsc$w_length = i + e->d_namlen - 1;
8938 pat.dsc$b_dtype = DSC$K_DTYPE_T;
8939 pat.dsc$b_class = DSC$K_CLASS_S;
8941 /* Set up result descriptor. */
8942 Newx(buff, VMS_MAXRSS, char);
8943 res.dsc$a_pointer = buff;
8944 res.dsc$w_length = VMS_MAXRSS - 1;
8945 res.dsc$b_dtype = DSC$K_DTYPE_T;
8946 res.dsc$b_class = DSC$K_CLASS_S;
8948 /* Read files, collecting versions. */
8949 for (context = 0, e->vms_verscount = 0;
8950 e->vms_verscount < VERSIZE(e);
8951 e->vms_verscount++) {
8953 unsigned long flags = 0;
8955 #ifdef VMS_LONGNAME_SUPPORT
8956 flags = LIB$M_FIL_LONG_NAMES;
8958 tmpsts = lib$find_file(&pat, &res, &context, NULL, NULL, &rsts, &flags);
8959 if (tmpsts == RMS$_NMF || context == 0) break;
8961 buff[VMS_MAXRSS - 1] = '\0';
8962 if ((p = strchr(buff, ';')))
8963 e->vms_versions[e->vms_verscount] = atoi(p + 1);
8965 e->vms_versions[e->vms_verscount] = -1;
8968 _ckvmssts(lib$find_file_end(&context));
8972 } /* end of collectversions() */
8975 * Read the next entry from the directory.
8977 /*{{{ struct dirent *readdir(DIR *dd)*/
8979 Perl_readdir(pTHX_ DIR *dd)
8981 struct dsc$descriptor_s res;
8983 unsigned long int tmpsts;
8985 unsigned long flags = 0;
8986 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
8987 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
8989 /* Set up result descriptor, and get next file. */
8990 Newx(buff, VMS_MAXRSS, char);
8991 res.dsc$a_pointer = buff;
8992 res.dsc$w_length = VMS_MAXRSS - 1;
8993 res.dsc$b_dtype = DSC$K_DTYPE_T;
8994 res.dsc$b_class = DSC$K_CLASS_S;
8996 #ifdef VMS_LONGNAME_SUPPORT
8997 flags = LIB$M_FIL_LONG_NAMES;
9000 tmpsts = lib$find_file
9001 (&dd->pat, &res, &dd->context, NULL, NULL, &rsts, &flags);
9002 if ( tmpsts == RMS$_NMF || dd->context == 0) return NULL; /* None left. */
9003 if (!(tmpsts & 1)) {
9004 set_vaxc_errno(tmpsts);
9007 set_errno(EACCES); break;
9009 set_errno(ENODEV); break;
9011 set_errno(ENOTDIR); break;
9012 case RMS$_FNF: case RMS$_DNF:
9013 set_errno(ENOENT); break;
9021 /* Force the buffer to end with a NUL, and downcase name to match C convention. */
9022 if (!decc_efs_case_preserve) {
9023 buff[VMS_MAXRSS - 1] = '\0';
9024 for (p = buff; *p; p++) *p = _tolower(*p);
9027 /* we don't want to force to lowercase, just null terminate */
9028 buff[res.dsc$w_length] = '\0';
9030 while (--p >= buff) if (!isspace(*p)) break; /* Do we really need this? */
9033 /* Skip any directory component and just copy the name. */
9034 sts = vms_split_path
9049 /* Drop NULL extensions on UNIX file specification */
9050 if ((dd->flags & PERL_VMSDIR_M_UNIXSPECS &&
9051 (e_len == 1) && decc_readdir_dropdotnotype)) {
9056 strncpy(dd->entry.d_name, n_spec, n_len + e_len);
9057 dd->entry.d_name[n_len + e_len] = '\0';
9058 dd->entry.d_namlen = strlen(dd->entry.d_name);
9060 /* Convert the filename to UNIX format if needed */
9061 if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
9063 /* Translate the encoded characters. */
9064 /* Fixme: Unicode handling could result in embedded 0 characters */
9065 if (strchr(dd->entry.d_name, '^') != NULL) {
9068 p = dd->entry.d_name;
9071 int inchars_read, outchars_added;
9072 inchars_read = copy_expand_vms_filename_escape(q, p, &outchars_added);
9074 q += outchars_added;
9076 /* if outchars_added > 1, then this is a wide file specification */
9077 /* Wide file specifications need to be passed in Perl */
9078 /* counted strings apparently with a Unicode flag */
9081 strcpy(dd->entry.d_name, new_name);
9082 dd->entry.d_namlen = strlen(dd->entry.d_name);
9086 dd->entry.vms_verscount = 0;
9087 if (dd->flags & PERL_VMSDIR_M_VERSIONS) collectversions(aTHX_ dd);
9091 } /* end of readdir() */
9095 * Read the next entry from the directory -- thread-safe version.
9097 /*{{{ int readdir_r(DIR *dd, struct dirent *entry, struct dirent **result)*/
9099 Perl_readdir_r(pTHX_ DIR *dd, struct dirent *entry, struct dirent **result)
9103 MUTEX_LOCK( (perl_mutex *) dd->mutex );
9105 entry = readdir(dd);
9107 retval = ( *result == NULL ? errno : 0 );
9109 MUTEX_UNLOCK( (perl_mutex *) dd->mutex );
9113 } /* end of readdir_r() */
9117 * Return something that can be used in a seekdir later.
9119 /*{{{ long telldir(DIR *dd)*/
9121 Perl_telldir(DIR *dd)
9128 * Return to a spot where we used to be. Brute force.
9130 /*{{{ void seekdir(DIR *dd,long count)*/
9132 Perl_seekdir(pTHX_ DIR *dd, long count)
9136 /* If we haven't done anything yet... */
9140 /* Remember some state, and clear it. */
9141 old_flags = dd->flags;
9142 dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
9143 _ckvmssts(lib$find_file_end(&dd->context));
9146 /* The increment is in readdir(). */
9147 for (dd->count = 0; dd->count < count; )
9150 dd->flags = old_flags;
9152 } /* end of seekdir() */
9155 /* VMS subprocess management
9157 * my_vfork() - just a vfork(), after setting a flag to record that
9158 * the current script is trying a Unix-style fork/exec.
9160 * vms_do_aexec() and vms_do_exec() are called in response to the
9161 * perl 'exec' function. If this follows a vfork call, then they
9162 * call out the regular perl routines in doio.c which do an
9163 * execvp (for those who really want to try this under VMS).
9164 * Otherwise, they do exactly what the perl docs say exec should
9165 * do - terminate the current script and invoke a new command
9166 * (See below for notes on command syntax.)
9168 * do_aspawn() and do_spawn() implement the VMS side of the perl
9169 * 'system' function.
9171 * Note on command arguments to perl 'exec' and 'system': When handled
9172 * in 'VMSish fashion' (i.e. not after a call to vfork) The args
9173 * are concatenated to form a DCL command string. If the first arg
9174 * begins with '$' (i.e. the perl script had "\$ Type" or some such),
9175 * the command string is handed off to DCL directly. Otherwise,
9176 * the first token of the command is taken as the filespec of an image
9177 * to run. The filespec is expanded using a default type of '.EXE' and
9178 * the process defaults for device, directory, etc., and if found, the resultant
9179 * filespec is invoked using the DCL verb 'MCR', and passed the rest of
9180 * the command string as parameters. This is perhaps a bit complicated,
9181 * but I hope it will form a happy medium between what VMS folks expect
9182 * from lib$spawn and what Unix folks expect from exec.
9185 static int vfork_called;
9187 /*{{{int my_vfork()*/
9198 vms_execfree(struct dsc$descriptor_s *vmscmd)
9201 if (vmscmd->dsc$a_pointer) {
9202 PerlMem_free(vmscmd->dsc$a_pointer);
9204 PerlMem_free(vmscmd);
9209 setup_argstr(pTHX_ SV *really, SV **mark, SV **sp)
9211 char *junk, *tmps = Nullch;
9212 register size_t cmdlen = 0;
9219 tmps = SvPV(really,rlen);
9226 for (idx++; idx <= sp; idx++) {
9228 junk = SvPVx(*idx,rlen);
9229 cmdlen += rlen ? rlen + 1 : 0;
9232 Newx(PL_Cmd, cmdlen+1, char);
9234 if (tmps && *tmps) {
9235 strcpy(PL_Cmd,tmps);
9238 else *PL_Cmd = '\0';
9239 while (++mark <= sp) {
9241 char *s = SvPVx(*mark,n_a);
9243 if (*PL_Cmd) strcat(PL_Cmd," ");
9249 } /* end of setup_argstr() */
9252 static unsigned long int
9253 setup_cmddsc(pTHX_ const char *incmd, int check_img, int *suggest_quote,
9254 struct dsc$descriptor_s **pvmscmd)
9256 char vmsspec[NAM$C_MAXRSS+1], resspec[NAM$C_MAXRSS+1];
9257 char image_name[NAM$C_MAXRSS+1];
9258 char image_argv[NAM$C_MAXRSS+1];
9259 $DESCRIPTOR(defdsc,".EXE");
9260 $DESCRIPTOR(defdsc2,".");
9261 $DESCRIPTOR(resdsc,resspec);
9262 struct dsc$descriptor_s *vmscmd;
9263 struct dsc$descriptor_s imgdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
9264 unsigned long int cxt = 0, flags = 1, retsts = SS$_NORMAL;
9265 register char *s, *rest, *cp, *wordbreak;
9270 vmscmd = PerlMem_malloc(sizeof(struct dsc$descriptor_s));
9271 if (vmscmd == NULL) _ckvmssts(SS$_INSFMEM);
9273 /* Make a copy for modification */
9274 cmdlen = strlen(incmd);
9275 cmd = PerlMem_malloc(cmdlen+1);
9276 if (cmd == NULL) _ckvmssts(SS$_INSFMEM);
9277 strncpy(cmd, incmd, cmdlen);
9282 vmscmd->dsc$a_pointer = NULL;
9283 vmscmd->dsc$b_dtype = DSC$K_DTYPE_T;
9284 vmscmd->dsc$b_class = DSC$K_CLASS_S;
9285 vmscmd->dsc$w_length = 0;
9286 if (pvmscmd) *pvmscmd = vmscmd;
9288 if (suggest_quote) *suggest_quote = 0;
9290 if (strlen(cmd) > MAX_DCL_LINE_LENGTH) {
9292 return CLI$_BUFOVF; /* continuation lines currently unsupported */
9297 while (*s && isspace(*s)) s++;
9299 if (*s == '@' || *s == '$') {
9300 vmsspec[0] = *s; rest = s + 1;
9301 for (cp = &vmsspec[1]; *rest && isspace(*rest); rest++,cp++) *cp = *rest;
9303 else { cp = vmsspec; rest = s; }
9304 if (*rest == '.' || *rest == '/') {
9307 *rest && !isspace(*rest) && cp2 - resspec < sizeof resspec;
9308 rest++, cp2++) *cp2 = *rest;
9310 if (do_tovmsspec(resspec,cp,0,NULL)) {
9313 for (cp2 = vmsspec + strlen(vmsspec);
9314 *rest && cp2 - vmsspec < sizeof vmsspec;
9315 rest++, cp2++) *cp2 = *rest;
9320 /* Intuit whether verb (first word of cmd) is a DCL command:
9321 * - if first nonspace char is '@', it's a DCL indirection
9323 * - if verb contains a filespec separator, it's not a DCL command
9324 * - if it doesn't, caller tells us whether to default to a DCL
9325 * command, or to a local image unless told it's DCL (by leading '$')
9329 if (suggest_quote) *suggest_quote = 1;
9331 register char *filespec = strpbrk(s,":<[.;");
9332 rest = wordbreak = strpbrk(s," \"\t/");
9333 if (!wordbreak) wordbreak = s + strlen(s);
9334 if (*s == '$') check_img = 0;
9335 if (filespec && (filespec < wordbreak)) isdcl = 0;
9336 else isdcl = !check_img;
9341 imgdsc.dsc$a_pointer = s;
9342 imgdsc.dsc$w_length = wordbreak - s;
9343 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
9345 _ckvmssts(lib$find_file_end(&cxt));
9346 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
9347 if (!(retsts & 1) && *s == '$') {
9348 _ckvmssts(lib$find_file_end(&cxt));
9349 imgdsc.dsc$a_pointer++; imgdsc.dsc$w_length--;
9350 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
9352 _ckvmssts(lib$find_file_end(&cxt));
9353 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
9357 _ckvmssts(lib$find_file_end(&cxt));
9362 while (*s && !isspace(*s)) s++;
9365 /* check that it's really not DCL with no file extension */
9366 fp = fopen(resspec,"r","ctx=bin","ctx=rec","shr=get");
9368 char b[256] = {0,0,0,0};
9369 read(fileno(fp), b, 256);
9370 isdcl = isprint(b[0]) && isprint(b[1]) && isprint(b[2]) && isprint(b[3]);
9374 /* Check for script */
9376 if ((b[0] == '#') && (b[1] == '!'))
9378 #ifdef ALTERNATE_SHEBANG
9380 shebang_len = strlen(ALTERNATE_SHEBANG);
9381 if (strncmp(b, ALTERNATE_SHEBANG, shebang_len) == 0) {
9383 perlstr = strstr("perl",b);
9384 if (perlstr == NULL)
9392 if (shebang_len > 0) {
9395 char tmpspec[NAM$C_MAXRSS + 1];
9398 /* Image is following after white space */
9399 /*--------------------------------------*/
9400 while (isprint(b[i]) && isspace(b[i]))
9404 while (isprint(b[i]) && !isspace(b[i])) {
9405 tmpspec[j++] = b[i++];
9406 if (j >= NAM$C_MAXRSS)
9411 /* There may be some default parameters to the image */
9412 /*---------------------------------------------------*/
9414 while (isprint(b[i])) {
9415 image_argv[j++] = b[i++];
9416 if (j >= NAM$C_MAXRSS)
9419 while ((j > 0) && !isprint(image_argv[j-1]))
9423 /* It will need to be converted to VMS format and validated */
9424 if (tmpspec[0] != '\0') {
9427 /* Try to find the exact program requested to be run */
9428 /*---------------------------------------------------*/
9429 iname = do_rmsexpand
9430 (tmpspec, image_name, 0, ".exe",
9431 PERL_RMSEXPAND_M_VMS, NULL, NULL);
9432 if (iname != NULL) {
9433 if (cando_by_name_int
9434 (S_IXUSR,0,image_name,PERL_RMSEXPAND_M_VMS_IN)) {
9435 /* MCR prefix needed */
9439 /* Try again with a null type */
9440 /*----------------------------*/
9441 iname = do_rmsexpand
9442 (tmpspec, image_name, 0, ".",
9443 PERL_RMSEXPAND_M_VMS, NULL, NULL);
9444 if (iname != NULL) {
9445 if (cando_by_name_int
9446 (S_IXUSR,0,image_name, PERL_RMSEXPAND_M_VMS_IN)) {
9447 /* MCR prefix needed */
9453 /* Did we find the image to run the script? */
9454 /*------------------------------------------*/
9458 /* Assume DCL or foreign command exists */
9459 /*--------------------------------------*/
9460 tchr = strrchr(tmpspec, '/');
9467 strcpy(image_name, tchr);
9475 if (check_img && isdcl) return RMS$_FNF;
9477 if (cando_by_name(S_IXUSR,0,resspec)) {
9478 vmscmd->dsc$a_pointer = PerlMem_malloc(MAX_DCL_LINE_LENGTH);
9479 if (vmscmd->dsc$a_pointer == NULL) _ckvmssts(SS$_INSFMEM);
9481 strcpy(vmscmd->dsc$a_pointer,"$ MCR ");
9482 if (image_name[0] != 0) {
9483 strcat(vmscmd->dsc$a_pointer, image_name);
9484 strcat(vmscmd->dsc$a_pointer, " ");
9486 } else if (image_name[0] != 0) {
9487 strcpy(vmscmd->dsc$a_pointer, image_name);
9488 strcat(vmscmd->dsc$a_pointer, " ");
9490 strcpy(vmscmd->dsc$a_pointer,"@");
9492 if (suggest_quote) *suggest_quote = 1;
9494 /* If there is an image name, use original command */
9495 if (image_name[0] == 0)
9496 strcat(vmscmd->dsc$a_pointer,resspec);
9499 while (*rest && isspace(*rest)) rest++;
9502 if (image_argv[0] != 0) {
9503 strcat(vmscmd->dsc$a_pointer,image_argv);
9504 strcat(vmscmd->dsc$a_pointer, " ");
9510 rest_len = strlen(rest);
9511 vmscmd_len = strlen(vmscmd->dsc$a_pointer);
9512 if ((rest_len + vmscmd_len) < MAX_DCL_LINE_LENGTH)
9513 strcat(vmscmd->dsc$a_pointer,rest);
9515 retsts = CLI$_BUFOVF;
9517 vmscmd->dsc$w_length = strlen(vmscmd->dsc$a_pointer);
9519 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
9525 /* It's either a DCL command or we couldn't find a suitable image */
9526 vmscmd->dsc$w_length = strlen(cmd);
9528 vmscmd->dsc$a_pointer = PerlMem_malloc(vmscmd->dsc$w_length + 1);
9529 strncpy(vmscmd->dsc$a_pointer,cmd,vmscmd->dsc$w_length);
9530 vmscmd->dsc$a_pointer[vmscmd->dsc$w_length] = 0;
9534 /* check if it's a symbol (for quoting purposes) */
9535 if (suggest_quote && !*suggest_quote) {
9537 char equiv[LNM$C_NAMLENGTH];
9538 struct dsc$descriptor_s eqvdsc = {sizeof(equiv), DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
9539 eqvdsc.dsc$a_pointer = equiv;
9541 iss = lib$get_symbol(vmscmd,&eqvdsc);
9542 if (iss&1 && (*equiv == '$' || *equiv == '@')) *suggest_quote = 1;
9544 if (!(retsts & 1)) {
9545 /* just hand off status values likely to be due to user error */
9546 if (retsts == RMS$_FNF || retsts == RMS$_DNF || retsts == RMS$_PRV ||
9547 retsts == RMS$_DEV || retsts == RMS$_DIR || retsts == RMS$_SYN ||
9548 (retsts & STS$M_CODE) == (SHR$_NOWILD & STS$M_CODE)) return retsts;
9549 else { _ckvmssts(retsts); }
9552 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
9554 } /* end of setup_cmddsc() */
9557 /* {{{ bool vms_do_aexec(SV *really,SV **mark,SV **sp) */
9559 Perl_vms_do_aexec(pTHX_ SV *really,SV **mark,SV **sp)
9565 if (vfork_called) { /* this follows a vfork - act Unixish */
9567 if (vfork_called < 0) {
9568 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
9571 else return do_aexec(really,mark,sp);
9573 /* no vfork - act VMSish */
9574 cmd = setup_argstr(aTHX_ really,mark,sp);
9575 exec_sts = vms_do_exec(cmd);
9576 Safefree(cmd); /* Clean up from setup_argstr() */
9581 } /* end of vms_do_aexec() */
9584 /* {{{bool vms_do_exec(char *cmd) */
9586 Perl_vms_do_exec(pTHX_ const char *cmd)
9588 struct dsc$descriptor_s *vmscmd;
9590 if (vfork_called) { /* this follows a vfork - act Unixish */
9592 if (vfork_called < 0) {
9593 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
9596 else return do_exec(cmd);
9599 { /* no vfork - act VMSish */
9600 unsigned long int retsts;
9603 TAINT_PROPER("exec");
9604 if ((retsts = setup_cmddsc(aTHX_ cmd,1,0,&vmscmd)) & 1)
9605 retsts = lib$do_command(vmscmd);
9608 case RMS$_FNF: case RMS$_DNF:
9609 set_errno(ENOENT); break;
9611 set_errno(ENOTDIR); break;
9613 set_errno(ENODEV); break;
9615 set_errno(EACCES); break;
9617 set_errno(EINVAL); break;
9618 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
9619 set_errno(E2BIG); break;
9620 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
9621 _ckvmssts(retsts); /* fall through */
9622 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
9625 set_vaxc_errno(retsts);
9626 if (ckWARN(WARN_EXEC)) {
9627 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't exec \"%*s\": %s",
9628 vmscmd->dsc$w_length, vmscmd->dsc$a_pointer, Strerror(errno));
9630 vms_execfree(vmscmd);
9635 } /* end of vms_do_exec() */
9638 unsigned long int Perl_do_spawn(pTHX_ const char *);
9640 /* {{{ unsigned long int do_aspawn(void *really,void **mark,void **sp) */
9642 Perl_do_aspawn(pTHX_ void *really,void **mark,void **sp)
9644 unsigned long int sts;
9648 cmd = setup_argstr(aTHX_ (SV *)really,(SV **)mark,(SV **)sp);
9649 sts = do_spawn(cmd);
9650 /* pp_sys will clean up cmd */
9654 } /* end of do_aspawn() */
9657 /* {{{unsigned long int do_spawn(char *cmd) */
9659 Perl_do_spawn(pTHX_ const char *cmd)
9661 unsigned long int sts, substs;
9663 /* The caller of this routine expects to Safefree(PL_Cmd) */
9664 Newx(PL_Cmd,10,char);
9667 TAINT_PROPER("spawn");
9668 if (!cmd || !*cmd) {
9669 sts = lib$spawn(0,0,0,0,0,0,&substs,0,0,0,0,0,0);
9672 case RMS$_FNF: case RMS$_DNF:
9673 set_errno(ENOENT); break;
9675 set_errno(ENOTDIR); break;
9677 set_errno(ENODEV); break;
9679 set_errno(EACCES); break;
9681 set_errno(EINVAL); break;
9682 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
9683 set_errno(E2BIG); break;
9684 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
9685 _ckvmssts(sts); /* fall through */
9686 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
9689 set_vaxc_errno(sts);
9690 if (ckWARN(WARN_EXEC)) {
9691 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't spawn: %s",
9699 fp = safe_popen(aTHX_ cmd, "nW", (int *)&sts);
9704 } /* end of do_spawn() */
9708 static unsigned int *sockflags, sockflagsize;
9711 * Shim fdopen to identify sockets for my_fwrite later, since the stdio
9712 * routines found in some versions of the CRTL can't deal with sockets.
9713 * We don't shim the other file open routines since a socket isn't
9714 * likely to be opened by a name.
9716 /*{{{ FILE *my_fdopen(int fd, const char *mode)*/
9717 FILE *my_fdopen(int fd, const char *mode)
9719 FILE *fp = fdopen(fd, mode);
9722 unsigned int fdoff = fd / sizeof(unsigned int);
9723 Stat_t sbuf; /* native stat; we don't need flex_stat */
9724 if (!sockflagsize || fdoff > sockflagsize) {
9725 if (sockflags) Renew( sockflags,fdoff+2,unsigned int);
9726 else Newx (sockflags,fdoff+2,unsigned int);
9727 memset(sockflags+sockflagsize,0,fdoff + 2 - sockflagsize);
9728 sockflagsize = fdoff + 2;
9730 if (fstat(fd, (struct stat *)&sbuf) == 0 && S_ISSOCK(sbuf.st_mode))
9731 sockflags[fdoff] |= 1 << (fd % sizeof(unsigned int));
9740 * Clear the corresponding bit when the (possibly) socket stream is closed.
9741 * There still a small hole: we miss an implicit close which might occur
9742 * via freopen(). >> Todo
9744 /*{{{ int my_fclose(FILE *fp)*/
9745 int my_fclose(FILE *fp) {
9747 unsigned int fd = fileno(fp);
9748 unsigned int fdoff = fd / sizeof(unsigned int);
9750 if (sockflagsize && fdoff <= sockflagsize)
9751 sockflags[fdoff] &= ~(1 << fd % sizeof(unsigned int));
9759 * A simple fwrite replacement which outputs itmsz*nitm chars without
9760 * introducing record boundaries every itmsz chars.
9761 * We are using fputs, which depends on a terminating null. We may
9762 * well be writing binary data, so we need to accommodate not only
9763 * data with nulls sprinkled in the middle but also data with no null
9766 /*{{{ int my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)*/
9768 my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)
9770 register char *cp, *end, *cpd, *data;
9771 register unsigned int fd = fileno(dest);
9772 register unsigned int fdoff = fd / sizeof(unsigned int);
9774 int bufsize = itmsz * nitm + 1;
9776 if (fdoff < sockflagsize &&
9777 (sockflags[fdoff] | 1 << (fd % sizeof(unsigned int)))) {
9778 if (write(fd, src, itmsz * nitm) == EOF) return EOF;
9782 _ckvmssts_noperl(lib$get_vm(&bufsize, &data));
9783 memcpy( data, src, itmsz*nitm );
9784 data[itmsz*nitm] = '\0';
9786 end = data + itmsz * nitm;
9787 retval = (int) nitm; /* on success return # items written */
9790 while (cpd <= end) {
9791 for (cp = cpd; cp <= end; cp++) if (!*cp) break;
9792 if (fputs(cpd,dest) == EOF) { retval = EOF; break; }
9794 if (fputc('\0',dest) == EOF) { retval = EOF; break; }
9798 if (data) _ckvmssts_noperl(lib$free_vm(&bufsize, &data));
9801 } /* end of my_fwrite() */
9804 /*{{{ int my_flush(FILE *fp)*/
9806 Perl_my_flush(pTHX_ FILE *fp)
9809 if ((res = fflush(fp)) == 0 && fp) {
9810 #ifdef VMS_DO_SOCKETS
9812 if (Fstat(fileno(fp), &s) == 0 && !S_ISSOCK(s.st_mode))
9814 res = fsync(fileno(fp));
9817 * If the flush succeeded but set end-of-file, we need to clear
9818 * the error because our caller may check ferror(). BTW, this
9819 * probably means we just flushed an empty file.
9821 if (res == 0 && vaxc$errno == RMS$_EOF) clearerr(fp);
9828 * Here are replacements for the following Unix routines in the VMS environment:
9829 * getpwuid Get information for a particular UIC or UID
9830 * getpwnam Get information for a named user
9831 * getpwent Get information for each user in the rights database
9832 * setpwent Reset search to the start of the rights database
9833 * endpwent Finish searching for users in the rights database
9835 * getpwuid, getpwnam, and getpwent return a pointer to the passwd structure
9836 * (defined in pwd.h), which contains the following fields:-
9838 * char *pw_name; Username (in lower case)
9839 * char *pw_passwd; Hashed password
9840 * unsigned int pw_uid; UIC
9841 * unsigned int pw_gid; UIC group number
9842 * char *pw_unixdir; Default device/directory (VMS-style)
9843 * char *pw_gecos; Owner name
9844 * char *pw_dir; Default device/directory (Unix-style)
9845 * char *pw_shell; Default CLI name (eg. DCL)
9847 * If the specified user does not exist, getpwuid and getpwnam return NULL.
9849 * pw_uid is the full UIC (eg. what's returned by stat() in st_uid).
9850 * not the UIC member number (eg. what's returned by getuid()),
9851 * getpwuid() can accept either as input (if uid is specified, the caller's
9852 * UIC group is used), though it won't recognise gid=0.
9854 * Note that in VMS it is necessary to have GRPPRV or SYSPRV to return
9855 * information about other users in your group or in other groups, respectively.
9856 * If the required privilege is not available, then these routines fill only
9857 * the pw_name, pw_uid, and pw_gid fields (the others point to an empty
9860 * By Tim Adye (T.J.Adye@rl.ac.uk), 10th February 1995.
9863 /* sizes of various UAF record fields */
9864 #define UAI$S_USERNAME 12
9865 #define UAI$S_IDENT 31
9866 #define UAI$S_OWNER 31
9867 #define UAI$S_DEFDEV 31
9868 #define UAI$S_DEFDIR 63
9869 #define UAI$S_DEFCLI 31
9872 #define valid_uic(uic) ((uic).uic$v_format == UIC$K_UIC_FORMAT && \
9873 (uic).uic$v_member != UIC$K_WILD_MEMBER && \
9874 (uic).uic$v_group != UIC$K_WILD_GROUP)
9876 static char __empty[]= "";
9877 static struct passwd __passwd_empty=
9878 {(char *) __empty, (char *) __empty, 0, 0,
9879 (char *) __empty, (char *) __empty, (char *) __empty, (char *) __empty};
9880 static int contxt= 0;
9881 static struct passwd __pwdcache;
9882 static char __pw_namecache[UAI$S_IDENT+1];
9885 * This routine does most of the work extracting the user information.
9887 static int fillpasswd (pTHX_ const char *name, struct passwd *pwd)
9890 unsigned char length;
9891 char pw_gecos[UAI$S_OWNER+1];
9893 static union uicdef uic;
9895 unsigned char length;
9896 char pw_dir[UAI$S_DEFDEV+UAI$S_DEFDIR+1];
9899 unsigned char length;
9900 char unixdir[UAI$_DEFDEV+UAI$S_DEFDIR+1];
9903 unsigned char length;
9904 char pw_shell[UAI$S_DEFCLI+1];
9906 static char pw_passwd[UAI$S_PWD+1];
9908 static unsigned short lowner, luic, ldefdev, ldefdir, ldefcli, lpwd;
9909 struct dsc$descriptor_s name_desc;
9910 unsigned long int sts;
9912 static struct itmlst_3 itmlst[]= {
9913 {UAI$S_OWNER+1, UAI$_OWNER, &owner, &lowner},
9914 {sizeof(uic), UAI$_UIC, &uic, &luic},
9915 {UAI$S_DEFDEV+1, UAI$_DEFDEV, &defdev, &ldefdev},
9916 {UAI$S_DEFDIR+1, UAI$_DEFDIR, &defdir, &ldefdir},
9917 {UAI$S_DEFCLI+1, UAI$_DEFCLI, &defcli, &ldefcli},
9918 {UAI$S_PWD, UAI$_PWD, pw_passwd, &lpwd},
9919 {0, 0, NULL, NULL}};
9921 name_desc.dsc$w_length= strlen(name);
9922 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
9923 name_desc.dsc$b_class= DSC$K_CLASS_S;
9924 name_desc.dsc$a_pointer= (char *) name; /* read only pointer */
9926 /* Note that sys$getuai returns many fields as counted strings. */
9927 sts= sys$getuai(0, 0, &name_desc, &itmlst, 0, 0, 0);
9928 if (sts == SS$_NOSYSPRV || sts == SS$_NOGRPPRV || sts == RMS$_RNF) {
9929 set_vaxc_errno(sts); set_errno(sts == RMS$_RNF ? EINVAL : EACCES);
9931 else { _ckvmssts(sts); }
9932 if (!(sts & 1)) return 0; /* out here in case _ckvmssts() doesn't abort */
9934 if ((int) owner.length < lowner) lowner= (int) owner.length;
9935 if ((int) defdev.length < ldefdev) ldefdev= (int) defdev.length;
9936 if ((int) defdir.length < ldefdir) ldefdir= (int) defdir.length;
9937 if ((int) defcli.length < ldefcli) ldefcli= (int) defcli.length;
9938 memcpy(&defdev.pw_dir[ldefdev], &defdir.unixdir[0], ldefdir);
9939 owner.pw_gecos[lowner]= '\0';
9940 defdev.pw_dir[ldefdev+ldefdir]= '\0';
9941 defcli.pw_shell[ldefcli]= '\0';
9942 if (valid_uic(uic)) {
9943 pwd->pw_uid= uic.uic$l_uic;
9944 pwd->pw_gid= uic.uic$v_group;
9947 Perl_warn(aTHX_ "getpwnam returned invalid UIC %#o for user \"%s\"");
9948 pwd->pw_passwd= pw_passwd;
9949 pwd->pw_gecos= owner.pw_gecos;
9950 pwd->pw_dir= defdev.pw_dir;
9951 pwd->pw_unixdir= do_tounixpath(defdev.pw_dir, defdir.unixdir,1,NULL);
9952 pwd->pw_shell= defcli.pw_shell;
9953 if (pwd->pw_unixdir && pwd->pw_unixdir[0]) {
9955 ldir= strlen(pwd->pw_unixdir) - 1;
9956 if (pwd->pw_unixdir[ldir]=='/') pwd->pw_unixdir[ldir]= '\0';
9959 strcpy(pwd->pw_unixdir, pwd->pw_dir);
9960 if (!decc_efs_case_preserve)
9961 __mystrtolower(pwd->pw_unixdir);
9966 * Get information for a named user.
9968 /*{{{struct passwd *getpwnam(char *name)*/
9969 struct passwd *Perl_my_getpwnam(pTHX_ const char *name)
9971 struct dsc$descriptor_s name_desc;
9973 unsigned long int status, sts;
9975 __pwdcache = __passwd_empty;
9976 if (!fillpasswd(aTHX_ name, &__pwdcache)) {
9977 /* We still may be able to determine pw_uid and pw_gid */
9978 name_desc.dsc$w_length= strlen(name);
9979 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
9980 name_desc.dsc$b_class= DSC$K_CLASS_S;
9981 name_desc.dsc$a_pointer= (char *) name;
9982 if ((sts = sys$asctoid(&name_desc, &uic, 0)) == SS$_NORMAL) {
9983 __pwdcache.pw_uid= uic.uic$l_uic;
9984 __pwdcache.pw_gid= uic.uic$v_group;
9987 if (sts == SS$_NOSUCHID || sts == SS$_IVIDENT || sts == RMS$_PRV) {
9988 set_vaxc_errno(sts);
9989 set_errno(sts == RMS$_PRV ? EACCES : EINVAL);
9992 else { _ckvmssts(sts); }
9995 strncpy(__pw_namecache, name, sizeof(__pw_namecache));
9996 __pw_namecache[sizeof __pw_namecache - 1] = '\0';
9997 __pwdcache.pw_name= __pw_namecache;
9999 } /* end of my_getpwnam() */
10003 * Get information for a particular UIC or UID.
10004 * Called by my_getpwent with uid=-1 to list all users.
10006 /*{{{struct passwd *my_getpwuid(Uid_t uid)*/
10007 struct passwd *Perl_my_getpwuid(pTHX_ Uid_t uid)
10009 const $DESCRIPTOR(name_desc,__pw_namecache);
10010 unsigned short lname;
10012 unsigned long int status;
10014 if (uid == (unsigned int) -1) {
10016 status = sys$idtoasc(-1, &lname, &name_desc, &uic, 0, &contxt);
10017 if (status == SS$_NOSUCHID || status == RMS$_PRV) {
10018 set_vaxc_errno(status);
10019 set_errno(status == RMS$_PRV ? EACCES : EINVAL);
10023 else { _ckvmssts(status); }
10024 } while (!valid_uic (uic));
10027 uic.uic$l_uic= uid;
10028 if (!uic.uic$v_group)
10029 uic.uic$v_group= PerlProc_getgid();
10030 if (valid_uic(uic))
10031 status = sys$idtoasc(uic.uic$l_uic, &lname, &name_desc, 0, 0, 0);
10032 else status = SS$_IVIDENT;
10033 if (status == SS$_IVIDENT || status == SS$_NOSUCHID ||
10034 status == RMS$_PRV) {
10035 set_vaxc_errno(status); set_errno(status == RMS$_PRV ? EACCES : EINVAL);
10038 else { _ckvmssts(status); }
10040 __pw_namecache[lname]= '\0';
10041 __mystrtolower(__pw_namecache);
10043 __pwdcache = __passwd_empty;
10044 __pwdcache.pw_name = __pw_namecache;
10046 /* Fill in the uid and gid in case fillpasswd can't (eg. no privilege).
10047 The identifier's value is usually the UIC, but it doesn't have to be,
10048 so if we can, we let fillpasswd update this. */
10049 __pwdcache.pw_uid = uic.uic$l_uic;
10050 __pwdcache.pw_gid = uic.uic$v_group;
10052 fillpasswd(aTHX_ __pw_namecache, &__pwdcache);
10053 return &__pwdcache;
10055 } /* end of my_getpwuid() */
10059 * Get information for next user.
10061 /*{{{struct passwd *my_getpwent()*/
10062 struct passwd *Perl_my_getpwent(pTHX)
10064 return (my_getpwuid((unsigned int) -1));
10069 * Finish searching rights database for users.
10071 /*{{{void my_endpwent()*/
10072 void Perl_my_endpwent(pTHX)
10075 _ckvmssts(sys$finish_rdb(&contxt));
10081 #ifdef HOMEGROWN_POSIX_SIGNALS
10082 /* Signal handling routines, pulled into the core from POSIX.xs.
10084 * We need these for threads, so they've been rolled into the core,
10085 * rather than left in POSIX.xs.
10087 * (DRS, Oct 23, 1997)
10090 /* sigset_t is atomic under VMS, so these routines are easy */
10091 /*{{{int my_sigemptyset(sigset_t *) */
10092 int my_sigemptyset(sigset_t *set) {
10093 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
10094 *set = 0; return 0;
10099 /*{{{int my_sigfillset(sigset_t *)*/
10100 int my_sigfillset(sigset_t *set) {
10102 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
10103 for (i = 0; i < NSIG; i++) *set |= (1 << i);
10109 /*{{{int my_sigaddset(sigset_t *set, int sig)*/
10110 int my_sigaddset(sigset_t *set, int sig) {
10111 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
10112 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
10113 *set |= (1 << (sig - 1));
10119 /*{{{int my_sigdelset(sigset_t *set, int sig)*/
10120 int my_sigdelset(sigset_t *set, int sig) {
10121 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
10122 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
10123 *set &= ~(1 << (sig - 1));
10129 /*{{{int my_sigismember(sigset_t *set, int sig)*/
10130 int my_sigismember(sigset_t *set, int sig) {
10131 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
10132 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
10133 return *set & (1 << (sig - 1));
10138 /*{{{int my_sigprocmask(int how, sigset_t *set, sigset_t *oset)*/
10139 int my_sigprocmask(int how, sigset_t *set, sigset_t *oset) {
10142 /* If set and oset are both null, then things are badly wrong. Bail out. */
10143 if ((oset == NULL) && (set == NULL)) {
10144 set_errno(EFAULT); set_vaxc_errno(SS$_ACCVIO);
10148 /* If set's null, then we're just handling a fetch. */
10150 tempmask = sigblock(0);
10155 tempmask = sigsetmask(*set);
10158 tempmask = sigblock(*set);
10161 tempmask = sigblock(0);
10162 sigsetmask(*oset & ~tempmask);
10165 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
10170 /* Did they pass us an oset? If so, stick our holding mask into it */
10177 #endif /* HOMEGROWN_POSIX_SIGNALS */
10180 /* Used for UTC calculation in my_gmtime(), my_localtime(), my_time(),
10181 * my_utime(), and flex_stat(), all of which operate on UTC unless
10182 * VMSISH_TIMES is true.
10184 /* method used to handle UTC conversions:
10185 * 1 == CRTL gmtime(); 2 == SYS$TIMEZONE_DIFFERENTIAL; 3 == no correction
10187 static int gmtime_emulation_type;
10188 /* number of secs to add to UTC POSIX-style time to get local time */
10189 static long int utc_offset_secs;
10191 /* We #defined 'gmtime', 'localtime', and 'time' as 'my_gmtime' etc.
10192 * in vmsish.h. #undef them here so we can call the CRTL routines
10201 * DEC C previous to 6.0 corrupts the behavior of the /prefix
10202 * qualifier with the extern prefix pragma. This provisional
10203 * hack circumvents this prefix pragma problem in previous
10206 #if defined(__VMS_VER) && __VMS_VER >= 70000000
10207 # if defined(VMS_WE_ARE_CASE_SENSITIVE) && (__DECC_VER < 60000000)
10208 # pragma __extern_prefix save
10209 # pragma __extern_prefix "" /* set to empty to prevent prefixing */
10210 # define gmtime decc$__utctz_gmtime
10211 # define localtime decc$__utctz_localtime
10212 # define time decc$__utc_time
10213 # pragma __extern_prefix restore
10215 struct tm *gmtime(), *localtime();
10221 static time_t toutc_dst(time_t loc) {
10224 if ((rsltmp = localtime(&loc)) == NULL) return -1;
10225 loc -= utc_offset_secs;
10226 if (rsltmp->tm_isdst) loc -= 3600;
10229 #define _toutc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
10230 ((gmtime_emulation_type || my_time(NULL)), \
10231 (gmtime_emulation_type == 1 ? toutc_dst(secs) : \
10232 ((secs) - utc_offset_secs))))
10234 static time_t toloc_dst(time_t utc) {
10237 utc += utc_offset_secs;
10238 if ((rsltmp = localtime(&utc)) == NULL) return -1;
10239 if (rsltmp->tm_isdst) utc += 3600;
10242 #define _toloc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
10243 ((gmtime_emulation_type || my_time(NULL)), \
10244 (gmtime_emulation_type == 1 ? toloc_dst(secs) : \
10245 ((secs) + utc_offset_secs))))
10247 #ifndef RTL_USES_UTC
10250 ucx$tz = "EST5EDT4,M4.1.0,M10.5.0" typical
10251 DST starts on 1st sun of april at 02:00 std time
10252 ends on last sun of october at 02:00 dst time
10253 see the UCX management command reference, SET CONFIG TIMEZONE
10254 for formatting info.
10256 No, it's not as general as it should be, but then again, NOTHING
10257 will handle UK times in a sensible way.
10262 parse the DST start/end info:
10263 (Jddd|ddd|Mmon.nth.dow)[/hh:mm:ss]
10267 tz_parse_startend(char *s, struct tm *w, int *past)
10269 int dinm[] = {31,28,31,30,31,30,31,31,30,31,30,31};
10270 int ly, dozjd, d, m, n, hour, min, sec, j, k;
10275 if (!past) return 0;
10278 if (w->tm_year % 4 == 0) ly = 1;
10279 if (w->tm_year % 100 == 0) ly = 0;
10280 if (w->tm_year+1900 % 400 == 0) ly = 1;
10283 dozjd = isdigit(*s);
10284 if (*s == 'J' || *s == 'j' || dozjd) {
10285 if (!dozjd && !isdigit(*++s)) return 0;
10288 d = d*10 + *s++ - '0';
10290 d = d*10 + *s++ - '0';
10293 if (d == 0) return 0;
10294 if (d > 366) return 0;
10296 if (!dozjd && d > 58 && ly) d++; /* after 28 feb */
10299 } else if (*s == 'M' || *s == 'm') {
10300 if (!isdigit(*++s)) return 0;
10302 if (isdigit(*s)) m = 10*m + *s++ - '0';
10303 if (*s != '.') return 0;
10304 if (!isdigit(*++s)) return 0;
10306 if (n < 1 || n > 5) return 0;
10307 if (*s != '.') return 0;
10308 if (!isdigit(*++s)) return 0;
10310 if (d > 6) return 0;
10314 if (!isdigit(*++s)) return 0;
10316 if (isdigit(*s)) hour = 10*hour + *s++ - '0';
10318 if (!isdigit(*++s)) return 0;
10320 if (isdigit(*s)) min = 10*min + *s++ - '0';
10322 if (!isdigit(*++s)) return 0;
10324 if (isdigit(*s)) sec = 10*sec + *s++ - '0';
10334 if (w->tm_yday < d) goto before;
10335 if (w->tm_yday > d) goto after;
10337 if (w->tm_mon+1 < m) goto before;
10338 if (w->tm_mon+1 > m) goto after;
10340 j = (42 + w->tm_wday - w->tm_mday)%7; /*dow of mday 0 */
10341 k = d - j; /* mday of first d */
10342 if (k <= 0) k += 7;
10343 k += 7 * ((n>4?4:n)-1); /* mday of n'th d */
10344 if (n == 5 && k+7 <= dinm[w->tm_mon]) k += 7;
10345 if (w->tm_mday < k) goto before;
10346 if (w->tm_mday > k) goto after;
10349 if (w->tm_hour < hour) goto before;
10350 if (w->tm_hour > hour) goto after;
10351 if (w->tm_min < min) goto before;
10352 if (w->tm_min > min) goto after;
10353 if (w->tm_sec < sec) goto before;
10367 /* parse the offset: (+|-)hh[:mm[:ss]] */
10370 tz_parse_offset(char *s, int *offset)
10372 int hour = 0, min = 0, sec = 0;
10375 if (!offset) return 0;
10377 if (*s == '-') {neg++; s++;}
10378 if (*s == '+') s++;
10379 if (!isdigit(*s)) return 0;
10381 if (isdigit(*s)) hour = hour*10+(*s++ - '0');
10382 if (hour > 24) return 0;
10384 if (!isdigit(*++s)) return 0;
10386 if (isdigit(*s)) min = min*10 + (*s++ - '0');
10387 if (min > 59) return 0;
10389 if (!isdigit(*++s)) return 0;
10391 if (isdigit(*s)) sec = sec*10 + (*s++ - '0');
10392 if (sec > 59) return 0;
10396 *offset = (hour*60+min)*60 + sec;
10397 if (neg) *offset = -*offset;
10402 input time is w, whatever type of time the CRTL localtime() uses.
10403 sets dst, the zone, and the gmtoff (seconds)
10405 caches the value of TZ and UCX$TZ env variables; note that
10406 my_setenv looks for these and sets a flag if they're changed
10409 We have to watch out for the "australian" case (dst starts in
10410 october, ends in april)...flagged by "reverse" and checked by
10411 scanning through the months of the previous year.
10416 tz_parse(pTHX_ time_t *w, int *dst, char *zone, int *gmtoff)
10421 char *dstzone, *tz, *s_start, *s_end;
10422 int std_off, dst_off, isdst;
10423 int y, dststart, dstend;
10424 static char envtz[1025]; /* longer than any logical, symbol, ... */
10425 static char ucxtz[1025];
10426 static char reversed = 0;
10432 reversed = -1; /* flag need to check */
10433 envtz[0] = ucxtz[0] = '\0';
10434 tz = my_getenv("TZ",0);
10435 if (tz) strcpy(envtz, tz);
10436 tz = my_getenv("UCX$TZ",0);
10437 if (tz) strcpy(ucxtz, tz);
10438 if (!envtz[0] && !ucxtz[0]) return 0; /* we give up */
10441 if (!*tz) tz = ucxtz;
10444 while (isalpha(*s)) s++;
10445 s = tz_parse_offset(s, &std_off);
10447 if (!*s) { /* no DST, hurray we're done! */
10453 while (isalpha(*s)) s++;
10454 s2 = tz_parse_offset(s, &dst_off);
10458 dst_off = std_off - 3600;
10461 if (!*s) { /* default dst start/end?? */
10462 if (tz != ucxtz) { /* if TZ tells zone only, UCX$TZ tells rule */
10463 s = strchr(ucxtz,',');
10465 if (!s || !*s) s = ",M4.1.0,M10.5.0"; /* we know we do dst, default rule */
10467 if (*s != ',') return 0;
10470 when = _toutc(when); /* convert to utc */
10471 when = when - std_off; /* convert to pseudolocal time*/
10473 w2 = localtime(&when);
10476 s = tz_parse_startend(s_start,w2,&dststart);
10478 if (*s != ',') return 0;
10481 when = _toutc(when); /* convert to utc */
10482 when = when - dst_off; /* convert to pseudolocal time*/
10483 w2 = localtime(&when);
10484 if (w2->tm_year != y) { /* spans a year, just check one time */
10485 when += dst_off - std_off;
10486 w2 = localtime(&when);
10489 s = tz_parse_startend(s_end,w2,&dstend);
10492 if (reversed == -1) { /* need to check if start later than end */
10496 if (when < 2*365*86400) {
10497 when += 2*365*86400;
10501 w2 =localtime(&when);
10502 when = when + (15 - w2->tm_yday) * 86400; /* jan 15 */
10504 for (j = 0; j < 12; j++) {
10505 w2 =localtime(&when);
10506 tz_parse_startend(s_start,w2,&ds);
10507 tz_parse_startend(s_end,w2,&de);
10508 if (ds != de) break;
10512 if (de && !ds) reversed = 1;
10515 isdst = dststart && !dstend;
10516 if (reversed) isdst = dststart || !dstend;
10519 if (dst) *dst = isdst;
10520 if (gmtoff) *gmtoff = isdst ? dst_off : std_off;
10521 if (isdst) tz = dstzone;
10523 while(isalpha(*tz)) *zone++ = *tz++;
10529 #endif /* !RTL_USES_UTC */
10531 /* my_time(), my_localtime(), my_gmtime()
10532 * By default traffic in UTC time values, using CRTL gmtime() or
10533 * SYS$TIMEZONE_DIFFERENTIAL to determine offset from local time zone.
10534 * Note: We need to use these functions even when the CRTL has working
10535 * UTC support, since they also handle C<use vmsish qw(times);>
10537 * Contributed by Chuck Lane <lane@duphy4.physics.drexel.edu>
10538 * Modified by Charles Bailey <bailey@newman.upenn.edu>
10541 /*{{{time_t my_time(time_t *timep)*/
10542 time_t Perl_my_time(pTHX_ time_t *timep)
10547 if (gmtime_emulation_type == 0) {
10549 time_t base = 15 * 86400; /* 15jan71; to avoid month/year ends between */
10550 /* results of calls to gmtime() and localtime() */
10551 /* for same &base */
10553 gmtime_emulation_type++;
10554 if ((tm_p = gmtime(&base)) == NULL) { /* CRTL gmtime() is a fake */
10555 char off[LNM$C_NAMLENGTH+1];;
10557 gmtime_emulation_type++;
10558 if (!vmstrnenv("SYS$TIMEZONE_DIFFERENTIAL",off,0,fildev,0)) {
10559 gmtime_emulation_type++;
10560 utc_offset_secs = 0;
10561 Perl_warn(aTHX_ "no UTC offset information; assuming local time is UTC");
10563 else { utc_offset_secs = atol(off); }
10565 else { /* We've got a working gmtime() */
10566 struct tm gmt, local;
10569 tm_p = localtime(&base);
10571 utc_offset_secs = (local.tm_mday - gmt.tm_mday) * 86400;
10572 utc_offset_secs += (local.tm_hour - gmt.tm_hour) * 3600;
10573 utc_offset_secs += (local.tm_min - gmt.tm_min) * 60;
10574 utc_offset_secs += (local.tm_sec - gmt.tm_sec);
10579 # ifdef VMSISH_TIME
10580 # ifdef RTL_USES_UTC
10581 if (VMSISH_TIME) when = _toloc(when);
10583 if (!VMSISH_TIME) when = _toutc(when);
10586 if (timep != NULL) *timep = when;
10589 } /* end of my_time() */
10593 /*{{{struct tm *my_gmtime(const time_t *timep)*/
10595 Perl_my_gmtime(pTHX_ const time_t *timep)
10601 if (timep == NULL) {
10602 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
10605 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
10608 # ifdef VMSISH_TIME
10609 if (VMSISH_TIME) when = _toutc(when); /* Input was local time */
10611 # ifdef RTL_USES_UTC /* this implies that the CRTL has a working gmtime() */
10612 return gmtime(&when);
10614 /* CRTL localtime() wants local time as input, so does no tz correction */
10615 rsltmp = localtime(&when);
10616 if (rsltmp) rsltmp->tm_isdst = 0; /* We already took DST into account */
10619 } /* end of my_gmtime() */
10623 /*{{{struct tm *my_localtime(const time_t *timep)*/
10625 Perl_my_localtime(pTHX_ const time_t *timep)
10627 time_t when, whenutc;
10631 if (timep == NULL) {
10632 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
10635 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
10636 if (gmtime_emulation_type == 0) my_time(NULL); /* Init UTC */
10639 # ifdef RTL_USES_UTC
10640 # ifdef VMSISH_TIME
10641 if (VMSISH_TIME) when = _toutc(when);
10643 /* CRTL localtime() wants UTC as input, does tz correction itself */
10644 return localtime(&when);
10646 # else /* !RTL_USES_UTC */
10648 # ifdef VMSISH_TIME
10649 if (!VMSISH_TIME) when = _toloc(whenutc); /* input was UTC */
10650 if (VMSISH_TIME) whenutc = _toutc(when); /* input was truelocal */
10653 #ifndef RTL_USES_UTC
10654 if (tz_parse(aTHX_ &when, &dst, 0, &offset)) { /* truelocal determines DST*/
10655 when = whenutc - offset; /* pseudolocal time*/
10658 /* CRTL localtime() wants local time as input, so does no tz correction */
10659 rsltmp = localtime(&when);
10660 if (rsltmp && gmtime_emulation_type != 1) rsltmp->tm_isdst = dst;
10664 } /* end of my_localtime() */
10667 /* Reset definitions for later calls */
10668 #define gmtime(t) my_gmtime(t)
10669 #define localtime(t) my_localtime(t)
10670 #define time(t) my_time(t)
10673 /* my_utime - update modification/access time of a file
10675 * VMS 7.3 and later implementation
10676 * Only the UTC translation is home-grown. The rest is handled by the
10677 * CRTL utime(), which will take into account the relevant feature
10678 * logicals and ODS-5 volume characteristics for true access times.
10680 * pre VMS 7.3 implementation:
10681 * The calling sequence is identical to POSIX utime(), but under
10682 * VMS with ODS-2, only the modification time is changed; ODS-2 does
10683 * not maintain access times. Restrictions differ from the POSIX
10684 * definition in that the time can be changed as long as the
10685 * caller has permission to execute the necessary IO$_MODIFY $QIO;
10686 * no separate checks are made to insure that the caller is the
10687 * owner of the file or has special privs enabled.
10688 * Code here is based on Joe Meadows' FILE utility.
10692 /* Adjustment from Unix epoch (01-JAN-1970 00:00:00.00)
10693 * to VMS epoch (01-JAN-1858 00:00:00.00)
10694 * in 100 ns intervals.
10696 static const long int utime_baseadjust[2] = { 0x4beb4000, 0x7c9567 };
10698 /*{{{int my_utime(const char *path, const struct utimbuf *utimes)*/
10699 int Perl_my_utime(pTHX_ const char *file, const struct utimbuf *utimes)
10701 #if __CRTL_VER >= 70300000
10702 struct utimbuf utc_utimes, *utc_utimesp;
10704 if (utimes != NULL) {
10705 utc_utimes.actime = utimes->actime;
10706 utc_utimes.modtime = utimes->modtime;
10707 # ifdef VMSISH_TIME
10708 /* If input was local; convert to UTC for sys svc */
10710 utc_utimes.actime = _toutc(utimes->actime);
10711 utc_utimes.modtime = _toutc(utimes->modtime);
10714 utc_utimesp = &utc_utimes;
10717 utc_utimesp = NULL;
10720 return utime(file, utc_utimesp);
10722 #else /* __CRTL_VER < 70300000 */
10726 long int bintime[2], len = 2, lowbit, unixtime,
10727 secscale = 10000000; /* seconds --> 100 ns intervals */
10728 unsigned long int chan, iosb[2], retsts;
10729 char vmsspec[NAM$C_MAXRSS+1], rsa[NAM$C_MAXRSS], esa[NAM$C_MAXRSS];
10730 struct FAB myfab = cc$rms_fab;
10731 struct NAM mynam = cc$rms_nam;
10732 #if defined (__DECC) && defined (__VAX)
10733 /* VAX DEC C atrdef.h has unsigned type for pointer member atr$l_addr,
10734 * at least through VMS V6.1, which causes a type-conversion warning.
10736 # pragma message save
10737 # pragma message disable cvtdiftypes
10739 struct atrdef myatr[2] = {{sizeof bintime, ATR$C_REVDATE, bintime}, {0,0,0}};
10740 struct fibdef myfib;
10741 #if defined (__DECC) && defined (__VAX)
10742 /* This should be right after the declaration of myatr, but due
10743 * to a bug in VAX DEC C, this takes effect a statement early.
10745 # pragma message restore
10747 /* cast ok for read only parameter */
10748 struct dsc$descriptor fibdsc = {sizeof(myfib), DSC$K_DTYPE_Z, DSC$K_CLASS_S,(char *) &myfib},
10749 devdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0},
10750 fnmdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0};
10752 if (file == NULL || *file == '\0') {
10753 SETERRNO(ENOENT, LIB$_INVARG);
10757 /* Convert to VMS format ensuring that it will fit in 255 characters */
10758 if (do_rmsexpand(file, vmsspec, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL) == NULL) {
10759 SETERRNO(ENOENT, LIB$_INVARG);
10762 if (utimes != NULL) {
10763 /* Convert Unix time (seconds since 01-JAN-1970 00:00:00.00)
10764 * to VMS quadword time (100 nsec intervals since 01-JAN-1858 00:00:00.00).
10765 * Since time_t is unsigned long int, and lib$emul takes a signed long int
10766 * as input, we force the sign bit to be clear by shifting unixtime right
10767 * one bit, then multiplying by an extra factor of 2 in lib$emul().
10769 lowbit = (utimes->modtime & 1) ? secscale : 0;
10770 unixtime = (long int) utimes->modtime;
10771 # ifdef VMSISH_TIME
10772 /* If input was UTC; convert to local for sys svc */
10773 if (!VMSISH_TIME) unixtime = _toloc(unixtime);
10775 unixtime >>= 1; secscale <<= 1;
10776 retsts = lib$emul(&secscale, &unixtime, &lowbit, bintime);
10777 if (!(retsts & 1)) {
10778 SETERRNO(EVMSERR, retsts);
10781 retsts = lib$addx(bintime,utime_baseadjust,bintime,&len);
10782 if (!(retsts & 1)) {
10783 SETERRNO(EVMSERR, retsts);
10788 /* Just get the current time in VMS format directly */
10789 retsts = sys$gettim(bintime);
10790 if (!(retsts & 1)) {
10791 SETERRNO(EVMSERR, retsts);
10796 myfab.fab$l_fna = vmsspec;
10797 myfab.fab$b_fns = (unsigned char) strlen(vmsspec);
10798 myfab.fab$l_nam = &mynam;
10799 mynam.nam$l_esa = esa;
10800 mynam.nam$b_ess = (unsigned char) sizeof esa;
10801 mynam.nam$l_rsa = rsa;
10802 mynam.nam$b_rss = (unsigned char) sizeof rsa;
10803 if (decc_efs_case_preserve)
10804 mynam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
10806 /* Look for the file to be affected, letting RMS parse the file
10807 * specification for us as well. I have set errno using only
10808 * values documented in the utime() man page for VMS POSIX.
10810 retsts = sys$parse(&myfab,0,0);
10811 if (!(retsts & 1)) {
10812 set_vaxc_errno(retsts);
10813 if (retsts == RMS$_PRV) set_errno(EACCES);
10814 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
10815 else set_errno(EVMSERR);
10818 retsts = sys$search(&myfab,0,0);
10819 if (!(retsts & 1)) {
10820 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
10821 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
10822 set_vaxc_errno(retsts);
10823 if (retsts == RMS$_PRV) set_errno(EACCES);
10824 else if (retsts == RMS$_FNF) set_errno(ENOENT);
10825 else set_errno(EVMSERR);
10829 devdsc.dsc$w_length = mynam.nam$b_dev;
10830 /* cast ok for read only parameter */
10831 devdsc.dsc$a_pointer = (char *) mynam.nam$l_dev;
10833 retsts = sys$assign(&devdsc,&chan,0,0);
10834 if (!(retsts & 1)) {
10835 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
10836 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
10837 set_vaxc_errno(retsts);
10838 if (retsts == SS$_IVDEVNAM) set_errno(ENOTDIR);
10839 else if (retsts == SS$_NOPRIV) set_errno(EACCES);
10840 else if (retsts == SS$_NOSUCHDEV) set_errno(ENOTDIR);
10841 else set_errno(EVMSERR);
10845 fnmdsc.dsc$a_pointer = mynam.nam$l_name;
10846 fnmdsc.dsc$w_length = mynam.nam$b_name + mynam.nam$b_type + mynam.nam$b_ver;
10848 memset((void *) &myfib, 0, sizeof myfib);
10849 #if defined(__DECC) || defined(__DECCXX)
10850 for (i=0;i<3;i++) myfib.fib$w_fid[i] = mynam.nam$w_fid[i];
10851 for (i=0;i<3;i++) myfib.fib$w_did[i] = mynam.nam$w_did[i];
10852 /* This prevents the revision time of the file being reset to the current
10853 * time as a result of our IO$_MODIFY $QIO. */
10854 myfib.fib$l_acctl = FIB$M_NORECORD;
10856 for (i=0;i<3;i++) myfib.fib$r_fid_overlay.fib$w_fid[i] = mynam.nam$w_fid[i];
10857 for (i=0;i<3;i++) myfib.fib$r_did_overlay.fib$w_did[i] = mynam.nam$w_did[i];
10858 myfib.fib$r_acctl_overlay.fib$l_acctl = FIB$M_NORECORD;
10860 retsts = sys$qiow(0,chan,IO$_MODIFY,iosb,0,0,&fibdsc,&fnmdsc,0,0,myatr,0);
10861 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
10862 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
10863 _ckvmssts(sys$dassgn(chan));
10864 if (retsts & 1) retsts = iosb[0];
10865 if (!(retsts & 1)) {
10866 set_vaxc_errno(retsts);
10867 if (retsts == SS$_NOPRIV) set_errno(EACCES);
10868 else set_errno(EVMSERR);
10874 #endif /* #if __CRTL_VER >= 70300000 */
10876 } /* end of my_utime() */
10880 * flex_stat, flex_lstat, flex_fstat
10881 * basic stat, but gets it right when asked to stat
10882 * a Unix-style path ending in a directory name (e.g. dir1/dir2/dir3)
10885 #ifndef _USE_STD_STAT
10886 /* encode_dev packs a VMS device name string into an integer to allow
10887 * simple comparisons. This can be used, for example, to check whether two
10888 * files are located on the same device, by comparing their encoded device
10889 * names. Even a string comparison would not do, because stat() reuses the
10890 * device name buffer for each call; so without encode_dev, it would be
10891 * necessary to save the buffer and use strcmp (this would mean a number of
10892 * changes to the standard Perl code, to say nothing of what a Perl script
10893 * would have to do.
10895 * The device lock id, if it exists, should be unique (unless perhaps compared
10896 * with lock ids transferred from other nodes). We have a lock id if the disk is
10897 * mounted cluster-wide, which is when we tend to get long (host-qualified)
10898 * device names. Thus we use the lock id in preference, and only if that isn't
10899 * available, do we try to pack the device name into an integer (flagged by
10900 * the sign bit (LOCKID_MASK) being set).
10902 * Note that encode_dev cannot guarantee an 1-to-1 correspondence twixt device
10903 * name and its encoded form, but it seems very unlikely that we will find
10904 * two files on different disks that share the same encoded device names,
10905 * and even more remote that they will share the same file id (if the test
10906 * is to check for the same file).
10908 * A better method might be to use sys$device_scan on the first call, and to
10909 * search for the device, returning an index into the cached array.
10910 * The number returned would be more intelligible.
10911 * This is probably not worth it, and anyway would take quite a bit longer
10912 * on the first call.
10914 #define LOCKID_MASK 0x80000000 /* Use 0 to force device name use only */
10915 static mydev_t encode_dev (pTHX_ const char *dev)
10918 unsigned long int f;
10923 if (!dev || !dev[0]) return 0;
10927 struct dsc$descriptor_s dev_desc;
10928 unsigned long int status, lockid = 0, item = DVI$_LOCKID;
10930 /* For cluster-mounted disks, the disk lock identifier is unique, so we
10931 can try that first. */
10932 dev_desc.dsc$w_length = strlen (dev);
10933 dev_desc.dsc$b_dtype = DSC$K_DTYPE_T;
10934 dev_desc.dsc$b_class = DSC$K_CLASS_S;
10935 dev_desc.dsc$a_pointer = (char *) dev; /* Read only parameter */
10936 status = lib$getdvi(&item, 0, &dev_desc, &lockid, 0, 0);
10937 if (!$VMS_STATUS_SUCCESS(status)) {
10939 case SS$_NOSUCHDEV:
10940 SETERRNO(ENODEV, status);
10946 if (lockid) return (lockid & ~LOCKID_MASK);
10950 /* Otherwise we try to encode the device name */
10954 for (q = dev + strlen(dev); q--; q >= dev) {
10959 else if (isalpha (toupper (*q)))
10960 c= toupper (*q) - 'A' + (char)10;
10962 continue; /* Skip '$'s */
10964 if (i>6) break; /* 36^7 is too large to fit in an unsigned long int */
10966 enc += f * (unsigned long int) c;
10968 return (enc | LOCKID_MASK); /* May have already overflowed into bit 31 */
10970 } /* end of encode_dev() */
10971 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
10972 device_no = encode_dev(aTHX_ devname)
10974 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
10975 device_no = new_dev_no
10979 is_null_device(name)
10982 if (decc_bug_devnull != 0) {
10983 if (strncmp("/dev/null", name, 9) == 0)
10986 /* The VMS null device is named "_NLA0:", usually abbreviated as "NL:".
10987 The underscore prefix, controller letter, and unit number are
10988 independently optional; for our purposes, the colon punctuation
10989 is not. The colon can be trailed by optional directory and/or
10990 filename, but two consecutive colons indicates a nodename rather
10991 than a device. [pr] */
10992 if (*name == '_') ++name;
10993 if (tolower(*name++) != 'n') return 0;
10994 if (tolower(*name++) != 'l') return 0;
10995 if (tolower(*name) == 'a') ++name;
10996 if (*name == '0') ++name;
10997 return (*name++ == ':') && (*name != ':');
11002 Perl_cando_by_name_int
11003 (pTHX_ I32 bit, bool effective, const char *fname, int opts)
11005 char usrname[L_cuserid];
11006 struct dsc$descriptor_s usrdsc =
11007 {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, usrname};
11008 char *vmsname = NULL, *fileified = NULL;
11009 unsigned long int objtyp = ACL$C_FILE, access, retsts, privused, iosb[2], flags;
11010 unsigned short int retlen, trnlnm_iter_count;
11011 struct dsc$descriptor_s namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
11012 union prvdef curprv;
11013 struct itmlst_3 armlst[4] = {{sizeof access, CHP$_ACCESS, &access, &retlen},
11014 {sizeof privused, CHP$_PRIVUSED, &privused, &retlen},
11015 {sizeof flags, CHP$_FLAGS, &flags, &retlen},{0,0,0,0}};
11016 struct itmlst_3 jpilst[3] = {{sizeof curprv, JPI$_CURPRIV, &curprv, &retlen},
11017 {sizeof usrname, JPI$_USERNAME, &usrname, &usrdsc.dsc$w_length},
11019 struct itmlst_3 usrprolst[2] = {{sizeof curprv, CHP$_PRIV, &curprv, &retlen},
11021 struct dsc$descriptor_s usrprodsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
11023 static int profile_context = -1;
11025 if (!fname || !*fname) return FALSE;
11027 /* Make sure we expand logical names, since sys$check_access doesn't */
11028 fileified = PerlMem_malloc(VMS_MAXRSS);
11029 if (fileified == NULL) _ckvmssts(SS$_INSFMEM);
11030 if (!strpbrk(fname,"/]>:")) {
11031 strcpy(fileified,fname);
11032 trnlnm_iter_count = 0;
11033 while (!strpbrk(fileified,"/]>:") && my_trnlnm(fileified,fileified,0)) {
11034 trnlnm_iter_count++;
11035 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
11040 vmsname = PerlMem_malloc(VMS_MAXRSS);
11041 if (vmsname == NULL) _ckvmssts(SS$_INSFMEM);
11042 if ( !(opts & PERL_RMSEXPAND_M_VMS_IN) ) {
11043 /* Don't know if already in VMS format, so make sure */
11044 if (!do_rmsexpand(fname, vmsname, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL)) {
11045 PerlMem_free(fileified);
11046 PerlMem_free(vmsname);
11051 strcpy(vmsname,fname);
11054 /* sys$check_access needs a file spec, not a directory spec.
11055 * Don't use flex_stat here, as that depends on thread context
11056 * having been initialized, and we may get here during startup.
11059 retlen = namdsc.dsc$w_length = strlen(vmsname);
11060 if (vmsname[retlen-1] == ']'
11061 || vmsname[retlen-1] == '>'
11062 || vmsname[retlen-1] == ':'
11063 || (!stat(vmsname, (stat_t *)&st) && S_ISDIR(st.st_mode))) {
11065 if (!do_fileify_dirspec(vmsname,fileified,1,NULL)) {
11066 PerlMem_free(fileified);
11067 PerlMem_free(vmsname);
11076 retlen = namdsc.dsc$w_length = strlen(fname);
11077 namdsc.dsc$a_pointer = (char *)fname;
11080 case S_IXUSR: case S_IXGRP: case S_IXOTH:
11081 access = ARM$M_EXECUTE;
11082 flags = CHP$M_READ;
11084 case S_IRUSR: case S_IRGRP: case S_IROTH:
11085 access = ARM$M_READ;
11086 flags = CHP$M_READ | CHP$M_USEREADALL;
11088 case S_IWUSR: case S_IWGRP: case S_IWOTH:
11089 access = ARM$M_WRITE;
11090 flags = CHP$M_READ | CHP$M_WRITE;
11092 case S_IDUSR: case S_IDGRP: case S_IDOTH:
11093 access = ARM$M_DELETE;
11094 flags = CHP$M_READ | CHP$M_WRITE;
11097 if (fileified != NULL)
11098 PerlMem_free(fileified);
11099 if (vmsname != NULL)
11100 PerlMem_free(vmsname);
11104 /* Before we call $check_access, create a user profile with the current
11105 * process privs since otherwise it just uses the default privs from the
11106 * UAF and might give false positives or negatives. This only works on
11107 * VMS versions v6.0 and later since that's when sys$create_user_profile
11108 * became available.
11111 /* get current process privs and username */
11112 _ckvmssts(sys$getjpiw(0,0,0,jpilst,iosb,0,0));
11113 _ckvmssts(iosb[0]);
11115 #if defined(__VMS_VER) && __VMS_VER >= 60000000
11117 /* find out the space required for the profile */
11118 _ckvmssts(sys$create_user_profile(&usrdsc,&usrprolst,0,0,
11119 &usrprodsc.dsc$w_length,&profile_context));
11121 /* allocate space for the profile and get it filled in */
11122 usrprodsc.dsc$a_pointer = PerlMem_malloc(usrprodsc.dsc$w_length);
11123 if (usrprodsc.dsc$a_pointer == NULL) _ckvmssts(SS$_INSFMEM);
11124 _ckvmssts(sys$create_user_profile(&usrdsc,&usrprolst,0,usrprodsc.dsc$a_pointer,
11125 &usrprodsc.dsc$w_length,&profile_context));
11127 /* use the profile to check access to the file; free profile & analyze results */
11128 retsts = sys$check_access(&objtyp,&namdsc,0,armlst,&profile_context,0,0,&usrprodsc);
11129 PerlMem_free(usrprodsc.dsc$a_pointer);
11130 if (retsts == SS$_NOCALLPRIV) retsts = SS$_NOPRIV; /* not really 3rd party */
11134 retsts = sys$check_access(&objtyp,&namdsc,&usrdsc,armlst);
11138 if (retsts == SS$_NOPRIV || retsts == SS$_NOSUCHOBJECT ||
11139 retsts == SS$_INVFILFOROP || retsts == RMS$_FNF || retsts == RMS$_SYN ||
11140 retsts == RMS$_DIR || retsts == RMS$_DEV || retsts == RMS$_DNF) {
11141 set_vaxc_errno(retsts);
11142 if (retsts == SS$_NOPRIV) set_errno(EACCES);
11143 else if (retsts == SS$_INVFILFOROP) set_errno(EINVAL);
11144 else set_errno(ENOENT);
11145 if (fileified != NULL)
11146 PerlMem_free(fileified);
11147 if (vmsname != NULL)
11148 PerlMem_free(vmsname);
11151 if (retsts == SS$_NORMAL || retsts == SS$_ACCONFLICT) {
11152 if (fileified != NULL)
11153 PerlMem_free(fileified);
11154 if (vmsname != NULL)
11155 PerlMem_free(vmsname);
11160 if (fileified != NULL)
11161 PerlMem_free(fileified);
11162 if (vmsname != NULL)
11163 PerlMem_free(vmsname);
11164 return FALSE; /* Should never get here */
11168 /* Do the permissions allow some operation? Assumes PL_statcache already set. */
11169 /* Do this via $Check_Access on VMS, since the CRTL stat() returns only a
11170 * subset of the applicable information.
11173 Perl_cando(pTHX_ Mode_t bit, bool effective, const Stat_t *statbufp)
11175 return cando_by_name_int
11176 (bit, effective, statbufp->st_devnam, PERL_RMSEXPAND_M_VMS_IN);
11177 } /* end of cando() */
11181 /*{{{I32 cando_by_name(I32 bit, bool effective, char *fname)*/
11183 Perl_cando_by_name(pTHX_ I32 bit, bool effective, const char *fname)
11185 return cando_by_name_int(bit, effective, fname, 0);
11187 } /* end of cando_by_name() */
11191 /*{{{ int flex_fstat(int fd, Stat_t *statbuf)*/
11193 Perl_flex_fstat(pTHX_ int fd, Stat_t *statbufp)
11195 if (!fstat(fd,(stat_t *) statbufp)) {
11197 char *vms_filename;
11198 vms_filename = PerlMem_malloc(VMS_MAXRSS);
11199 if (vms_filename == NULL) _ckvmssts(SS$_INSFMEM);
11201 /* Save name for cando by name in VMS format */
11202 cptr = getname(fd, vms_filename, 1);
11204 /* This should not happen, but just in case */
11205 if (cptr == NULL) {
11206 statbufp->st_devnam[0] = 0;
11209 /* Make sure that the saved name fits in 255 characters */
11210 cptr = do_rmsexpand
11212 statbufp->st_devnam,
11215 PERL_RMSEXPAND_M_VMS | PERL_RMSEXPAND_M_VMS_IN,
11219 statbufp->st_devnam[0] = 0;
11221 PerlMem_free(vms_filename);
11223 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
11225 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
11227 # ifdef RTL_USES_UTC
11228 # ifdef VMSISH_TIME
11230 statbufp->st_mtime = _toloc(statbufp->st_mtime);
11231 statbufp->st_atime = _toloc(statbufp->st_atime);
11232 statbufp->st_ctime = _toloc(statbufp->st_ctime);
11236 # ifdef VMSISH_TIME
11237 if (!VMSISH_TIME) { /* Return UTC instead of local time */
11241 statbufp->st_mtime = _toutc(statbufp->st_mtime);
11242 statbufp->st_atime = _toutc(statbufp->st_atime);
11243 statbufp->st_ctime = _toutc(statbufp->st_ctime);
11250 } /* end of flex_fstat() */
11253 #if !defined(__VAX) && __CRTL_VER >= 80200000
11261 #define lstat(_x, _y) stat(_x, _y)
11264 #define flex_stat_int(a,b,c) Perl_flex_stat_int(aTHX_ a,b,c)
11267 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag)
11269 char fileified[VMS_MAXRSS];
11270 char temp_fspec[VMS_MAXRSS];
11273 int saved_errno, saved_vaxc_errno;
11275 if (!fspec) return retval;
11276 saved_errno = errno; saved_vaxc_errno = vaxc$errno;
11277 strcpy(temp_fspec, fspec);
11279 if (decc_bug_devnull != 0) {
11280 if (is_null_device(temp_fspec)) { /* Fake a stat() for the null device */
11281 memset(statbufp,0,sizeof *statbufp);
11282 VMS_DEVICE_ENCODE(statbufp->st_dev, "_NLA0:", 0);
11283 statbufp->st_mode = S_IFBLK | S_IREAD | S_IWRITE | S_IEXEC;
11284 statbufp->st_uid = 0x00010001;
11285 statbufp->st_gid = 0x0001;
11286 time((time_t *)&statbufp->st_mtime);
11287 statbufp->st_atime = statbufp->st_ctime = statbufp->st_mtime;
11292 /* Try for a directory name first. If fspec contains a filename without
11293 * a type (e.g. sea:[wine.dark]water), and both sea:[wine.dark]water.dir
11294 * and sea:[wine.dark]water. exist, we prefer the directory here.
11295 * Similarly, sea:[wine.dark] returns the result for sea:[wine]dark.dir,
11296 * not sea:[wine.dark]., if the latter exists. If the intended target is
11297 * the file with null type, specify this by calling flex_stat() with
11298 * a '.' at the end of fspec.
11300 * If we are in Posix filespec mode, accept the filename as is.
11304 #if __CRTL_VER >= 70300000 && !defined(__VAX)
11305 /* The CRTL stat() falls down hard on multi-dot filenames in unix format unless
11306 * DECC$EFS_CHARSET is in effect, so temporarily enable it if it isn't already.
11308 if (!decc_efs_charset)
11309 decc$feature_set_value(decc$feature_get_index("DECC$EFS_CHARSET"),1,1);
11312 #if __CRTL_VER >= 80200000 && !defined(__VAX)
11313 if (decc_posix_compliant_pathnames == 0) {
11315 if (do_fileify_dirspec(temp_fspec,fileified,0,NULL) != NULL) {
11316 if (lstat_flag == 0)
11317 retval = stat(fileified,(stat_t *) statbufp);
11319 retval = lstat(fileified,(stat_t *) statbufp);
11320 save_spec = fileified;
11323 if (lstat_flag == 0)
11324 retval = stat(temp_fspec,(stat_t *) statbufp);
11326 retval = lstat(temp_fspec,(stat_t *) statbufp);
11327 save_spec = temp_fspec;
11329 #if __CRTL_VER >= 80200000 && !defined(__VAX)
11331 if (lstat_flag == 0)
11332 retval = stat(temp_fspec,(stat_t *) statbufp);
11334 retval = lstat(temp_fspec,(stat_t *) statbufp);
11335 save_spec = temp_fspec;
11339 #if __CRTL_VER >= 70300000 && !defined(__VAX)
11340 /* As you were... */
11341 if (!decc_efs_charset)
11342 decc$feature_set_value(decc$feature_get_index("DECC$EFS_CHARSET"),1,0);
11347 cptr = do_rmsexpand
11348 (save_spec, statbufp->st_devnam, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL);
11350 statbufp->st_devnam[0] = 0;
11352 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
11354 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
11355 # ifdef RTL_USES_UTC
11356 # ifdef VMSISH_TIME
11358 statbufp->st_mtime = _toloc(statbufp->st_mtime);
11359 statbufp->st_atime = _toloc(statbufp->st_atime);
11360 statbufp->st_ctime = _toloc(statbufp->st_ctime);
11364 # ifdef VMSISH_TIME
11365 if (!VMSISH_TIME) { /* Return UTC instead of local time */
11369 statbufp->st_mtime = _toutc(statbufp->st_mtime);
11370 statbufp->st_atime = _toutc(statbufp->st_atime);
11371 statbufp->st_ctime = _toutc(statbufp->st_ctime);
11375 /* If we were successful, leave errno where we found it */
11376 if (retval == 0) { errno = saved_errno; vaxc$errno = saved_vaxc_errno; }
11379 } /* end of flex_stat_int() */
11382 /*{{{ int flex_stat(const char *fspec, Stat_t *statbufp)*/
11384 Perl_flex_stat(pTHX_ const char *fspec, Stat_t *statbufp)
11386 return flex_stat_int(fspec, statbufp, 0);
11390 /*{{{ int flex_lstat(const char *fspec, Stat_t *statbufp)*/
11392 Perl_flex_lstat(pTHX_ const char *fspec, Stat_t *statbufp)
11394 return flex_stat_int(fspec, statbufp, 1);
11399 /*{{{char *my_getlogin()*/
11400 /* VMS cuserid == Unix getlogin, except calling sequence */
11404 static char user[L_cuserid];
11405 return cuserid(user);
11410 /* rmscopy - copy a file using VMS RMS routines
11412 * Copies contents and attributes of spec_in to spec_out, except owner
11413 * and protection information. Name and type of spec_in are used as
11414 * defaults for spec_out. The third parameter specifies whether rmscopy()
11415 * should try to propagate timestamps from the input file to the output file.
11416 * If it is less than 0, no timestamps are preserved. If it is 0, then
11417 * rmscopy() will behave similarly to the DCL COPY command: timestamps are
11418 * propagated to the output file at creation iff the output file specification
11419 * did not contain an explicit name or type, and the revision date is always
11420 * updated at the end of the copy operation. If it is greater than 0, then
11421 * it is interpreted as a bitmask, in which bit 0 indicates that timestamps
11422 * other than the revision date should be propagated, and bit 1 indicates
11423 * that the revision date should be propagated.
11425 * Returns 1 on success; returns 0 and sets errno and vaxc$errno on failure.
11427 * Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>.
11428 * Incorporates, with permission, some code from EZCOPY by Tim Adye
11429 * <T.J.Adye@rl.ac.uk>. Permission is given to distribute this code
11430 * as part of the Perl standard distribution under the terms of the
11431 * GNU General Public License or the Perl Artistic License. Copies
11432 * of each may be found in the Perl standard distribution.
11434 /*{{{int rmscopy(char *src, char *dst, int preserve_dates)*/
11436 Perl_rmscopy(pTHX_ const char *spec_in, const char *spec_out, int preserve_dates)
11438 char *vmsin, * vmsout, *esa, *esa_out,
11440 unsigned long int i, sts, sts2;
11442 struct FAB fab_in, fab_out;
11443 struct RAB rab_in, rab_out;
11444 rms_setup_nam(nam);
11445 rms_setup_nam(nam_out);
11446 struct XABDAT xabdat;
11447 struct XABFHC xabfhc;
11448 struct XABRDT xabrdt;
11449 struct XABSUM xabsum;
11451 vmsin = PerlMem_malloc(VMS_MAXRSS);
11452 if (vmsin == NULL) _ckvmssts(SS$_INSFMEM);
11453 vmsout = PerlMem_malloc(VMS_MAXRSS);
11454 if (vmsout == NULL) _ckvmssts(SS$_INSFMEM);
11455 if (!spec_in || !*spec_in || !do_tovmsspec(spec_in,vmsin,1,NULL) ||
11456 !spec_out || !*spec_out || !do_tovmsspec(spec_out,vmsout,1,NULL)) {
11457 PerlMem_free(vmsin);
11458 PerlMem_free(vmsout);
11459 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11463 esa = PerlMem_malloc(VMS_MAXRSS);
11464 if (esa == NULL) _ckvmssts(SS$_INSFMEM);
11465 fab_in = cc$rms_fab;
11466 rms_set_fna(fab_in, nam, vmsin, strlen(vmsin));
11467 fab_in.fab$b_shr = FAB$M_SHRPUT | FAB$M_UPI;
11468 fab_in.fab$b_fac = FAB$M_BIO | FAB$M_GET;
11469 fab_in.fab$l_fop = FAB$M_SQO;
11470 rms_bind_fab_nam(fab_in, nam);
11471 fab_in.fab$l_xab = (void *) &xabdat;
11473 rsa = PerlMem_malloc(VMS_MAXRSS);
11474 if (rsa == NULL) _ckvmssts(SS$_INSFMEM);
11475 rms_set_rsa(nam, rsa, (VMS_MAXRSS-1));
11476 rms_set_esa(fab_in, nam, esa, (VMS_MAXRSS-1));
11477 rms_nam_esl(nam) = 0;
11478 rms_nam_rsl(nam) = 0;
11479 rms_nam_esll(nam) = 0;
11480 rms_nam_rsll(nam) = 0;
11481 #ifdef NAM$M_NO_SHORT_UPCASE
11482 if (decc_efs_case_preserve)
11483 rms_set_nam_nop(nam, NAM$M_NO_SHORT_UPCASE);
11486 xabdat = cc$rms_xabdat; /* To get creation date */
11487 xabdat.xab$l_nxt = (void *) &xabfhc;
11489 xabfhc = cc$rms_xabfhc; /* To get record length */
11490 xabfhc.xab$l_nxt = (void *) &xabsum;
11492 xabsum = cc$rms_xabsum; /* To get key and area information */
11494 if (!((sts = sys$open(&fab_in)) & 1)) {
11495 PerlMem_free(vmsin);
11496 PerlMem_free(vmsout);
11499 set_vaxc_errno(sts);
11501 case RMS$_FNF: case RMS$_DNF:
11502 set_errno(ENOENT); break;
11504 set_errno(ENOTDIR); break;
11506 set_errno(ENODEV); break;
11508 set_errno(EINVAL); break;
11510 set_errno(EACCES); break;
11512 set_errno(EVMSERR);
11519 fab_out.fab$w_ifi = 0;
11520 fab_out.fab$b_fac = FAB$M_BIO | FAB$M_PUT;
11521 fab_out.fab$b_shr = FAB$M_SHRGET | FAB$M_UPI;
11522 fab_out.fab$l_fop = FAB$M_SQO;
11523 rms_bind_fab_nam(fab_out, nam_out);
11524 rms_set_fna(fab_out, nam_out, vmsout, strlen(vmsout));
11525 dna_len = rms_nam_namel(nam) ? rms_nam_name_type_l_size(nam) : 0;
11526 rms_set_dna(fab_out, nam_out, rms_nam_namel(nam), dna_len);
11527 esa_out = PerlMem_malloc(VMS_MAXRSS);
11528 if (esa_out == NULL) _ckvmssts(SS$_INSFMEM);
11529 rms_set_rsa(nam_out, NULL, 0);
11530 rms_set_esa(fab_out, nam_out, esa_out, (VMS_MAXRSS - 1));
11532 if (preserve_dates == 0) { /* Act like DCL COPY */
11533 rms_set_nam_nop(nam_out, NAM$M_SYNCHK);
11534 fab_out.fab$l_xab = NULL; /* Don't disturb data from input file */
11535 if (!((sts = sys$parse(&fab_out)) & STS$K_SUCCESS)) {
11536 PerlMem_free(vmsin);
11537 PerlMem_free(vmsout);
11540 PerlMem_free(esa_out);
11541 set_errno(sts == RMS$_SYN ? EINVAL : EVMSERR);
11542 set_vaxc_errno(sts);
11545 fab_out.fab$l_xab = (void *) &xabdat;
11546 if (rms_is_nam_fnb(nam, NAM$M_EXP_NAME | NAM$M_EXP_TYPE))
11547 preserve_dates = 1;
11549 if (preserve_dates < 0) /* Clear all bits; we'll use it as a */
11550 preserve_dates =0; /* bitmask from this point forward */
11552 if (!(preserve_dates & 1)) fab_out.fab$l_xab = (void *) &xabfhc;
11553 if (!((sts = sys$create(&fab_out)) & STS$K_SUCCESS)) {
11554 PerlMem_free(vmsin);
11555 PerlMem_free(vmsout);
11558 PerlMem_free(esa_out);
11559 set_vaxc_errno(sts);
11562 set_errno(ENOENT); break;
11564 set_errno(ENOTDIR); break;
11566 set_errno(ENODEV); break;
11568 set_errno(EINVAL); break;
11570 set_errno(EACCES); break;
11572 set_errno(EVMSERR);
11576 fab_out.fab$l_fop |= FAB$M_DLT; /* in case we have to bail out */
11577 if (preserve_dates & 2) {
11578 /* sys$close() will process xabrdt, not xabdat */
11579 xabrdt = cc$rms_xabrdt;
11581 xabrdt.xab$q_rdt = xabdat.xab$q_rdt;
11583 /* gcc doesn't like the assignment, since its prototype for xab$q_rdt
11584 * is unsigned long[2], while DECC & VAXC use a struct */
11585 memcpy(xabrdt.xab$q_rdt,xabdat.xab$q_rdt,sizeof xabrdt.xab$q_rdt);
11587 fab_out.fab$l_xab = (void *) &xabrdt;
11590 ubf = PerlMem_malloc(32256);
11591 if (ubf == NULL) _ckvmssts(SS$_INSFMEM);
11592 rab_in = cc$rms_rab;
11593 rab_in.rab$l_fab = &fab_in;
11594 rab_in.rab$l_rop = RAB$M_BIO;
11595 rab_in.rab$l_ubf = ubf;
11596 rab_in.rab$w_usz = 32256;
11597 if (!((sts = sys$connect(&rab_in)) & 1)) {
11598 sys$close(&fab_in); sys$close(&fab_out);
11599 PerlMem_free(vmsin);
11600 PerlMem_free(vmsout);
11604 PerlMem_free(esa_out);
11605 set_errno(EVMSERR); set_vaxc_errno(sts);
11609 rab_out = cc$rms_rab;
11610 rab_out.rab$l_fab = &fab_out;
11611 rab_out.rab$l_rbf = ubf;
11612 if (!((sts = sys$connect(&rab_out)) & 1)) {
11613 sys$close(&fab_in); sys$close(&fab_out);
11614 PerlMem_free(vmsin);
11615 PerlMem_free(vmsout);
11619 PerlMem_free(esa_out);
11620 set_errno(EVMSERR); set_vaxc_errno(sts);
11624 while ((sts = sys$read(&rab_in))) { /* always true */
11625 if (sts == RMS$_EOF) break;
11626 rab_out.rab$w_rsz = rab_in.rab$w_rsz;
11627 if (!(sts & 1) || !((sts = sys$write(&rab_out)) & 1)) {
11628 sys$close(&fab_in); sys$close(&fab_out);
11629 PerlMem_free(vmsin);
11630 PerlMem_free(vmsout);
11634 PerlMem_free(esa_out);
11635 set_errno(EVMSERR); set_vaxc_errno(sts);
11641 fab_out.fab$l_fop &= ~FAB$M_DLT; /* We got this far; keep the output */
11642 sys$close(&fab_in); sys$close(&fab_out);
11643 sts = (fab_in.fab$l_sts & 1) ? fab_out.fab$l_sts : fab_in.fab$l_sts;
11645 PerlMem_free(vmsin);
11646 PerlMem_free(vmsout);
11650 PerlMem_free(esa_out);
11651 set_errno(EVMSERR); set_vaxc_errno(sts);
11655 PerlMem_free(vmsin);
11656 PerlMem_free(vmsout);
11660 PerlMem_free(esa_out);
11663 } /* end of rmscopy() */
11667 /*** The following glue provides 'hooks' to make some of the routines
11668 * from this file available from Perl. These routines are sufficiently
11669 * basic, and are required sufficiently early in the build process,
11670 * that's it's nice to have them available to miniperl as well as the
11671 * full Perl, so they're set up here instead of in an extension. The
11672 * Perl code which handles importation of these names into a given
11673 * package lives in [.VMS]Filespec.pm in @INC.
11677 rmsexpand_fromperl(pTHX_ CV *cv)
11680 char *fspec, *defspec = NULL, *rslt;
11682 int fs_utf8, dfs_utf8;
11686 if (!items || items > 2)
11687 Perl_croak(aTHX_ "Usage: VMS::Filespec::rmsexpand(spec[,defspec])");
11688 fspec = SvPV(ST(0),n_a);
11689 fs_utf8 = SvUTF8(ST(0));
11690 if (!fspec || !*fspec) XSRETURN_UNDEF;
11692 defspec = SvPV(ST(1),n_a);
11693 dfs_utf8 = SvUTF8(ST(1));
11695 rslt = do_rmsexpand(fspec,NULL,1,defspec,0,&fs_utf8,&dfs_utf8);
11696 ST(0) = sv_newmortal();
11697 if (rslt != NULL) {
11698 sv_usepvn(ST(0),rslt,strlen(rslt));
11707 vmsify_fromperl(pTHX_ CV *cv)
11714 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsify(spec)");
11715 utf8_fl = SvUTF8(ST(0));
11716 vmsified = do_tovmsspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
11717 ST(0) = sv_newmortal();
11718 if (vmsified != NULL) {
11719 sv_usepvn(ST(0),vmsified,strlen(vmsified));
11728 unixify_fromperl(pTHX_ CV *cv)
11735 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixify(spec)");
11736 utf8_fl = SvUTF8(ST(0));
11737 unixified = do_tounixspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
11738 ST(0) = sv_newmortal();
11739 if (unixified != NULL) {
11740 sv_usepvn(ST(0),unixified,strlen(unixified));
11749 fileify_fromperl(pTHX_ CV *cv)
11756 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::fileify(spec)");
11757 utf8_fl = SvUTF8(ST(0));
11758 fileified = do_fileify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
11759 ST(0) = sv_newmortal();
11760 if (fileified != NULL) {
11761 sv_usepvn(ST(0),fileified,strlen(fileified));
11770 pathify_fromperl(pTHX_ CV *cv)
11777 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::pathify(spec)");
11778 utf8_fl = SvUTF8(ST(0));
11779 pathified = do_pathify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
11780 ST(0) = sv_newmortal();
11781 if (pathified != NULL) {
11782 sv_usepvn(ST(0),pathified,strlen(pathified));
11791 vmspath_fromperl(pTHX_ CV *cv)
11798 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmspath(spec)");
11799 utf8_fl = SvUTF8(ST(0));
11800 vmspath = do_tovmspath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
11801 ST(0) = sv_newmortal();
11802 if (vmspath != NULL) {
11803 sv_usepvn(ST(0),vmspath,strlen(vmspath));
11812 unixpath_fromperl(pTHX_ CV *cv)
11819 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixpath(spec)");
11820 utf8_fl = SvUTF8(ST(0));
11821 unixpath = do_tounixpath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
11822 ST(0) = sv_newmortal();
11823 if (unixpath != NULL) {
11824 sv_usepvn(ST(0),unixpath,strlen(unixpath));
11833 candelete_fromperl(pTHX_ CV *cv)
11841 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::candelete(spec)");
11843 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
11844 Newx(fspec, VMS_MAXRSS, char);
11845 if (fspec == NULL) _ckvmssts(SS$_INSFMEM);
11846 if (SvTYPE(mysv) == SVt_PVGV) {
11847 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),fspec)) {
11848 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11856 if (mysv != ST(0) || !(fsp = SvPV(mysv,n_a)) || !*fsp) {
11857 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11864 ST(0) = boolSV(cando_by_name(S_IDUSR,0,fsp));
11870 rmscopy_fromperl(pTHX_ CV *cv)
11873 char *inspec, *outspec, *inp, *outp;
11875 struct dsc$descriptor indsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
11876 outdsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
11877 unsigned long int sts;
11882 if (items < 2 || items > 3)
11883 Perl_croak(aTHX_ "Usage: File::Copy::rmscopy(from,to[,date_flag])");
11885 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
11886 Newx(inspec, VMS_MAXRSS, char);
11887 if (SvTYPE(mysv) == SVt_PVGV) {
11888 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),inspec)) {
11889 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11897 if (mysv != ST(0) || !(inp = SvPV(mysv,n_a)) || !*inp) {
11898 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11904 mysv = SvROK(ST(1)) ? SvRV(ST(1)) : ST(1);
11905 Newx(outspec, VMS_MAXRSS, char);
11906 if (SvTYPE(mysv) == SVt_PVGV) {
11907 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),outspec)) {
11908 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11917 if (mysv != ST(1) || !(outp = SvPV(mysv,n_a)) || !*outp) {
11918 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11925 date_flag = (items == 3) ? SvIV(ST(2)) : 0;
11927 ST(0) = boolSV(rmscopy(inp,outp,date_flag));
11933 /* The mod2fname is limited to shorter filenames by design, so it should
11934 * not be modified to support longer EFS pathnames
11937 mod2fname(pTHX_ CV *cv)
11940 char ultimate_name[NAM$C_MAXRSS+1], work_name[NAM$C_MAXRSS*8 + 1],
11941 workbuff[NAM$C_MAXRSS*1 + 1];
11942 int total_namelen = 3, counter, num_entries;
11943 /* ODS-5 ups this, but we want to be consistent, so... */
11944 int max_name_len = 39;
11945 AV *in_array = (AV *)SvRV(ST(0));
11947 num_entries = av_len(in_array);
11949 /* All the names start with PL_. */
11950 strcpy(ultimate_name, "PL_");
11952 /* Clean up our working buffer */
11953 Zero(work_name, sizeof(work_name), char);
11955 /* Run through the entries and build up a working name */
11956 for(counter = 0; counter <= num_entries; counter++) {
11957 /* If it's not the first name then tack on a __ */
11959 strcat(work_name, "__");
11961 strcat(work_name, SvPV(*av_fetch(in_array, counter, FALSE),
11965 /* Check to see if we actually have to bother...*/
11966 if (strlen(work_name) + 3 <= max_name_len) {
11967 strcat(ultimate_name, work_name);
11969 /* It's too darned big, so we need to go strip. We use the same */
11970 /* algorithm as xsubpp does. First, strip out doubled __ */
11971 char *source, *dest, last;
11974 for (source = work_name; *source; source++) {
11975 if (last == *source && last == '_') {
11981 /* Go put it back */
11982 strcpy(work_name, workbuff);
11983 /* Is it still too big? */
11984 if (strlen(work_name) + 3 > max_name_len) {
11985 /* Strip duplicate letters */
11988 for (source = work_name; *source; source++) {
11989 if (last == toupper(*source)) {
11993 last = toupper(*source);
11995 strcpy(work_name, workbuff);
11998 /* Is it *still* too big? */
11999 if (strlen(work_name) + 3 > max_name_len) {
12000 /* Too bad, we truncate */
12001 work_name[max_name_len - 2] = 0;
12003 strcat(ultimate_name, work_name);
12006 /* Okay, return it */
12007 ST(0) = sv_2mortal(newSVpv(ultimate_name, 0));
12012 hushexit_fromperl(pTHX_ CV *cv)
12017 VMSISH_HUSHED = SvTRUE(ST(0));
12019 ST(0) = boolSV(VMSISH_HUSHED);
12025 Perl_vms_start_glob
12026 (pTHX_ SV *tmpglob,
12030 struct vs_str_st *rslt;
12034 $DESCRIPTOR(dfltdsc,"SYS$DISK:[]*.*;");
12037 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
12038 struct dsc$descriptor_vs rsdsc;
12039 unsigned long int cxt = 0, sts = 0, ok = 1, hasdir = 0;
12040 unsigned long hasver = 0, isunix = 0;
12041 unsigned long int lff_flags = 0;
12044 #ifdef VMS_LONGNAME_SUPPORT
12045 lff_flags = LIB$M_FIL_LONG_NAMES;
12047 /* The Newx macro will not allow me to assign a smaller array
12048 * to the rslt pointer, so we will assign it to the begin char pointer
12049 * and then copy the value into the rslt pointer.
12051 Newx(begin, VMS_MAXRSS + sizeof(unsigned short int), char);
12052 rslt = (struct vs_str_st *)begin;
12054 rstr = &rslt->str[0];
12055 rsdsc.dsc$a_pointer = (char *) rslt; /* cast required */
12056 rsdsc.dsc$w_maxstrlen = VMS_MAXRSS + sizeof(unsigned short int);
12057 rsdsc.dsc$b_dtype = DSC$K_DTYPE_VT;
12058 rsdsc.dsc$b_class = DSC$K_CLASS_VS;
12060 Newx(vmsspec, VMS_MAXRSS, char);
12062 /* We could find out if there's an explicit dev/dir or version
12063 by peeking into lib$find_file's internal context at
12064 ((struct NAM *)((struct FAB *)cxt)->fab$l_nam)->nam$l_fnb
12065 but that's unsupported, so I don't want to do it now and
12066 have it bite someone in the future. */
12067 /* Fix-me: vms_split_path() is the only way to do this, the
12068 existing method will fail with many legal EFS or UNIX specifications
12071 cp = SvPV(tmpglob,i);
12074 if (cp[i] == ';') hasver = 1;
12075 if (cp[i] == '.') {
12076 if (sts) hasver = 1;
12079 if (cp[i] == '/') {
12080 hasdir = isunix = 1;
12083 if (cp[i] == ']' || cp[i] == '>' || cp[i] == ':') {
12088 if ((tmpfp = PerlIO_tmpfile()) != NULL) {
12092 stat_sts = PerlLIO_stat(SvPVX_const(tmpglob),&st);
12093 if (!stat_sts && S_ISDIR(st.st_mode)) {
12094 wilddsc.dsc$a_pointer = tovmspath_utf8(SvPVX(tmpglob),vmsspec,NULL);
12095 ok = (wilddsc.dsc$a_pointer != NULL);
12096 /* maybe passed 'foo' rather than '[.foo]', thus not detected above */
12100 wilddsc.dsc$a_pointer = tovmsspec_utf8(SvPVX(tmpglob),vmsspec,NULL);
12101 ok = (wilddsc.dsc$a_pointer != NULL);
12104 wilddsc.dsc$w_length = strlen(wilddsc.dsc$a_pointer);
12106 /* If not extended character set, replace ? with % */
12107 /* With extended character set, ? is a wildcard single character */
12108 if (!decc_efs_case_preserve) {
12109 for (cp=wilddsc.dsc$a_pointer; ok && cp && *cp; cp++)
12110 if (*cp == '?') *cp = '%';
12113 while (ok && $VMS_STATUS_SUCCESS(sts)) {
12114 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
12115 int v_sts, v_len, r_len, d_len, n_len, e_len, vs_len;
12117 sts = lib$find_file(&wilddsc,&rsdsc,&cxt,
12118 &dfltdsc,NULL,&rms_sts,&lff_flags);
12119 if (!$VMS_STATUS_SUCCESS(sts))
12124 /* with varying string, 1st word of buffer contains result length */
12125 rstr[rslt->length] = '\0';
12127 /* Find where all the components are */
12128 v_sts = vms_split_path
12143 /* If no version on input, truncate the version on output */
12144 if (!hasver && (vs_len > 0)) {
12148 /* No version & a null extension on UNIX handling */
12149 if (isunix && (e_len == 1) && decc_readdir_dropdotnotype) {
12155 if (!decc_efs_case_preserve) {
12156 for (cp = rstr; *cp; cp++) *cp = _tolower(*cp);
12160 if (isunix) trim_unixpath(rstr,SvPVX(tmpglob),1);
12164 /* Start with the name */
12167 strcat(begin,"\n");
12168 ok = (PerlIO_puts(tmpfp,begin) != EOF);
12170 if (cxt) (void)lib$find_file_end(&cxt);
12173 /* Be POSIXish: return the input pattern when no matches */
12174 begin = SvPVX(tmpglob);
12175 strcat(begin,"\n");
12176 ok = (PerlIO_puts(tmpfp,begin) != EOF);
12179 if (ok && sts != RMS$_NMF &&
12180 sts != RMS$_DNF && sts != RMS_FNF) ok = 0;
12183 SETERRNO((sts == RMS$_SYN ? EINVAL : EVMSERR),sts);
12185 PerlIO_close(tmpfp);
12189 PerlIO_rewind(tmpfp);
12190 IoTYPE(io) = IoTYPE_RDONLY;
12191 IoIFP(io) = fp = tmpfp;
12192 IoFLAGS(io) &= ~IOf_UNTAINT; /* maybe redundant */
12203 mp_do_vms_realpath(pTHX_ const char *filespec, char * rslt_spec,
12204 const int *utf8_fl);
12207 vms_realpath_fromperl(pTHX_ CV *cv)
12210 char *fspec, *rslt_spec, *rslt;
12213 if (!items || items != 1)
12214 Perl_croak(aTHX_ "Usage: VMS::Filespec::vms_realpath(spec)");
12216 fspec = SvPV(ST(0),n_a);
12217 if (!fspec || !*fspec) XSRETURN_UNDEF;
12219 Newx(rslt_spec, VMS_MAXRSS + 1, char);
12220 rslt = do_vms_realpath(fspec, rslt_spec, NULL);
12221 ST(0) = sv_newmortal();
12223 sv_usepvn(ST(0),rslt,strlen(rslt));
12225 Safefree(rslt_spec);
12230 #if __CRTL_VER >= 70301000 && !defined(__VAX)
12231 int do_vms_case_tolerant(void);
12234 vms_case_tolerant_fromperl(pTHX_ CV *cv)
12237 ST(0) = boolSV(do_vms_case_tolerant());
12243 Perl_sys_intern_dup(pTHX_ struct interp_intern *src,
12244 struct interp_intern *dst)
12246 memcpy(dst,src,sizeof(struct interp_intern));
12250 Perl_sys_intern_clear(pTHX)
12255 Perl_sys_intern_init(pTHX)
12257 unsigned int ix = RAND_MAX;
12262 /* fix me later to track running under GNV */
12263 /* this allows some limited testing */
12264 MY_POSIX_EXIT = decc_filename_unix_report;
12267 MY_INV_RAND_MAX = 1./x;
12271 init_os_extras(void)
12274 char* file = __FILE__;
12275 if (decc_disable_to_vms_logname_translation) {
12276 no_translate_barewords = TRUE;
12278 no_translate_barewords = FALSE;
12281 newXSproto("VMS::Filespec::rmsexpand",rmsexpand_fromperl,file,"$;$");
12282 newXSproto("VMS::Filespec::vmsify",vmsify_fromperl,file,"$");
12283 newXSproto("VMS::Filespec::unixify",unixify_fromperl,file,"$");
12284 newXSproto("VMS::Filespec::pathify",pathify_fromperl,file,"$");
12285 newXSproto("VMS::Filespec::fileify",fileify_fromperl,file,"$");
12286 newXSproto("VMS::Filespec::vmspath",vmspath_fromperl,file,"$");
12287 newXSproto("VMS::Filespec::unixpath",unixpath_fromperl,file,"$");
12288 newXSproto("VMS::Filespec::candelete",candelete_fromperl,file,"$");
12289 newXSproto("DynaLoader::mod2fname", mod2fname, file, "$");
12290 newXS("File::Copy::rmscopy",rmscopy_fromperl,file);
12291 newXSproto("vmsish::hushed",hushexit_fromperl,file,";$");
12293 newXSproto("VMS::Filespec::vms_realpath",vms_realpath_fromperl,file,"$;$");
12295 #if __CRTL_VER >= 70301000 && !defined(__VAX)
12296 newXSproto("VMS::Filespec::case_tolerant",vms_case_tolerant_fromperl,file,"$;$");
12299 store_pipelocs(aTHX); /* will redo any earlier attempts */
12306 #if __CRTL_VER == 80200000
12307 /* This missed getting in to the DECC SDK for 8.2 */
12308 char *realpath(const char *file_name, char * resolved_name, ...);
12311 /*{{{char *do_vms_realpath(const char *file_name, char *resolved_name)*/
12312 /* wrapper for the realpath() function added with 8.2 RMS SYMLINK SDK.
12313 * The perl fallback routine to provide realpath() is not as efficient
12317 mp_do_vms_realpath(pTHX_ const char *filespec, char *outbuf,
12318 const int *utf8_fl)
12320 return realpath(filespec, outbuf);
12324 /* External entry points */
12325 char *Perl_vms_realpath(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
12326 { return do_vms_realpath(filespec, outbuf, utf8_fl); }
12328 char *Perl_vms_realpath(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
12333 #if __CRTL_VER >= 70301000 && !defined(__VAX)
12334 /* case_tolerant */
12336 /*{{{int do_vms_case_tolerant(void)*/
12337 /* OpenVMS provides a case sensitive implementation of ODS-5 and this is
12338 * controlled by a process setting.
12340 int do_vms_case_tolerant(void)
12342 return vms_process_case_tolerant;
12345 /* External entry points */
12346 int Perl_vms_case_tolerant(void)
12347 { return do_vms_case_tolerant(); }
12349 int Perl_vms_case_tolerant(void)
12350 { return vms_process_case_tolerant; }
12354 /* Start of DECC RTL Feature handling */
12356 static int sys_trnlnm
12357 (const char * logname,
12361 const $DESCRIPTOR(table_dsc, "LNM$FILE_DEV");
12362 const unsigned long attr = LNM$M_CASE_BLIND;
12363 struct dsc$descriptor_s name_dsc;
12365 unsigned short result;
12366 struct itmlst_3 itlst[2] = {{value_len, LNM$_STRING, value, &result},
12369 name_dsc.dsc$w_length = strlen(logname);
12370 name_dsc.dsc$a_pointer = (char *)logname;
12371 name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
12372 name_dsc.dsc$b_class = DSC$K_CLASS_S;
12374 status = sys$trnlnm(&attr, &table_dsc, &name_dsc, 0, itlst);
12376 if ($VMS_STATUS_SUCCESS(status)) {
12378 /* Null terminate and return the string */
12379 /*--------------------------------------*/
12386 static int sys_crelnm
12387 (const char * logname,
12388 const char * value)
12391 const char * proc_table = "LNM$PROCESS_TABLE";
12392 struct dsc$descriptor_s proc_table_dsc;
12393 struct dsc$descriptor_s logname_dsc;
12394 struct itmlst_3 item_list[2];
12396 proc_table_dsc.dsc$a_pointer = (char *) proc_table;
12397 proc_table_dsc.dsc$w_length = strlen(proc_table);
12398 proc_table_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
12399 proc_table_dsc.dsc$b_class = DSC$K_CLASS_S;
12401 logname_dsc.dsc$a_pointer = (char *) logname;
12402 logname_dsc.dsc$w_length = strlen(logname);
12403 logname_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
12404 logname_dsc.dsc$b_class = DSC$K_CLASS_S;
12406 item_list[0].buflen = strlen(value);
12407 item_list[0].itmcode = LNM$_STRING;
12408 item_list[0].bufadr = (char *)value;
12409 item_list[0].retlen = NULL;
12411 item_list[1].buflen = 0;
12412 item_list[1].itmcode = 0;
12414 ret_val = sys$crelnm
12416 (const struct dsc$descriptor_s *)&proc_table_dsc,
12417 (const struct dsc$descriptor_s *)&logname_dsc,
12419 (const struct item_list_3 *) item_list);
12424 /* C RTL Feature settings */
12426 static int set_features
12427 (int (* init_coroutine)(int *, int *, void *), /* Needs casts if used */
12428 int (* cli_routine)(void), /* Not documented */
12429 void *image_info) /* Not documented */
12436 #if defined(JPI$_CASE_LOOKUP_PERM) && !defined(__VAX)
12437 const unsigned long int jpicode1 = JPI$_CASE_LOOKUP_PERM;
12438 const unsigned long int jpicode2 = JPI$_CASE_LOOKUP_IMAGE;
12439 unsigned long case_perm;
12440 unsigned long case_image;
12443 /* Allow an exception to bring Perl into the VMS debugger */
12444 vms_debug_on_exception = 0;
12445 status = sys_trnlnm("PERL_VMS_EXCEPTION_DEBUG", val_str, sizeof(val_str));
12446 if ($VMS_STATUS_SUCCESS(status)) {
12447 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
12448 vms_debug_on_exception = 1;
12450 vms_debug_on_exception = 0;
12453 /* Create VTF-7 filenames from Unicode instead of UTF-8 */
12454 vms_vtf7_filenames = 0;
12455 status = sys_trnlnm("PERL_VMS_VTF7_FILENAMES", val_str, sizeof(val_str));
12456 if ($VMS_STATUS_SUCCESS(status)) {
12457 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
12458 vms_vtf7_filenames = 1;
12460 vms_vtf7_filenames = 0;
12463 /* Dectect running under GNV Bash or other UNIX like shell */
12464 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12465 gnv_unix_shell = 0;
12466 status = sys_trnlnm("GNV$UNIX_SHELL", val_str, sizeof(val_str));
12467 if ($VMS_STATUS_SUCCESS(status)) {
12468 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
12469 gnv_unix_shell = 1;
12470 set_feature_default("DECC$EFS_CASE_PRESERVE", 1);
12471 set_feature_default("DECC$EFS_CHARSET", 1);
12472 set_feature_default("DECC$FILENAME_UNIX_NO_VERSION", 1);
12473 set_feature_default("DECC$FILENAME_UNIX_REPORT", 1);
12474 set_feature_default("DECC$READDIR_DROPDOTNOTYPE", 1);
12475 set_feature_default("DECC$DISABLE_POSIX_ROOT", 0);
12478 gnv_unix_shell = 0;
12482 /* hacks to see if known bugs are still present for testing */
12484 /* Readdir is returning filenames in VMS syntax always */
12485 decc_bug_readdir_efs1 = 1;
12486 status = sys_trnlnm("DECC_BUG_READDIR_EFS1", val_str, sizeof(val_str));
12487 if ($VMS_STATUS_SUCCESS(status)) {
12488 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
12489 decc_bug_readdir_efs1 = 1;
12491 decc_bug_readdir_efs1 = 0;
12494 /* PCP mode requires creating /dev/null special device file */
12495 decc_bug_devnull = 0;
12496 status = sys_trnlnm("DECC_BUG_DEVNULL", val_str, sizeof(val_str));
12497 if ($VMS_STATUS_SUCCESS(status)) {
12498 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
12499 decc_bug_devnull = 1;
12501 decc_bug_devnull = 0;
12504 /* fgetname returning a VMS name in UNIX mode */
12505 decc_bug_fgetname = 1;
12506 status = sys_trnlnm("DECC_BUG_FGETNAME", val_str, sizeof(val_str));
12507 if ($VMS_STATUS_SUCCESS(status)) {
12508 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
12509 decc_bug_fgetname = 1;
12511 decc_bug_fgetname = 0;
12514 /* UNIX directory names with no paths are broken in a lot of places */
12515 decc_dir_barename = 1;
12516 status = sys_trnlnm("DECC_DIR_BARENAME", val_str, sizeof(val_str));
12517 if ($VMS_STATUS_SUCCESS(status)) {
12518 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
12519 decc_dir_barename = 1;
12521 decc_dir_barename = 0;
12524 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12525 s = decc$feature_get_index("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION");
12527 decc_disable_to_vms_logname_translation = decc$feature_get_value(s, 1);
12528 if (decc_disable_to_vms_logname_translation < 0)
12529 decc_disable_to_vms_logname_translation = 0;
12532 s = decc$feature_get_index("DECC$EFS_CASE_PRESERVE");
12534 decc_efs_case_preserve = decc$feature_get_value(s, 1);
12535 if (decc_efs_case_preserve < 0)
12536 decc_efs_case_preserve = 0;
12539 s = decc$feature_get_index("DECC$EFS_CHARSET");
12541 decc_efs_charset = decc$feature_get_value(s, 1);
12542 if (decc_efs_charset < 0)
12543 decc_efs_charset = 0;
12546 s = decc$feature_get_index("DECC$FILENAME_UNIX_REPORT");
12548 decc_filename_unix_report = decc$feature_get_value(s, 1);
12549 if (decc_filename_unix_report > 0)
12550 decc_filename_unix_report = 1;
12552 decc_filename_unix_report = 0;
12555 s = decc$feature_get_index("DECC$FILENAME_UNIX_ONLY");
12557 decc_filename_unix_only = decc$feature_get_value(s, 1);
12558 if (decc_filename_unix_only > 0) {
12559 decc_filename_unix_only = 1;
12562 decc_filename_unix_only = 0;
12566 s = decc$feature_get_index("DECC$FILENAME_UNIX_NO_VERSION");
12568 decc_filename_unix_no_version = decc$feature_get_value(s, 1);
12569 if (decc_filename_unix_no_version < 0)
12570 decc_filename_unix_no_version = 0;
12573 s = decc$feature_get_index("DECC$READDIR_DROPDOTNOTYPE");
12575 decc_readdir_dropdotnotype = decc$feature_get_value(s, 1);
12576 if (decc_readdir_dropdotnotype < 0)
12577 decc_readdir_dropdotnotype = 0;
12580 status = sys_trnlnm("SYS$POSIX_ROOT", val_str, sizeof(val_str));
12581 if ($VMS_STATUS_SUCCESS(status)) {
12582 s = decc$feature_get_index("DECC$DISABLE_POSIX_ROOT");
12584 dflt = decc$feature_get_value(s, 4);
12586 decc_disable_posix_root = decc$feature_get_value(s, 1);
12587 if (decc_disable_posix_root <= 0) {
12588 decc$feature_set_value(s, 1, 1);
12589 decc_disable_posix_root = 1;
12593 /* Traditionally Perl assumes this is off */
12594 decc_disable_posix_root = 1;
12595 decc$feature_set_value(s, 1, 1);
12600 #if __CRTL_VER >= 80200000
12601 s = decc$feature_get_index("DECC$POSIX_COMPLIANT_PATHNAMES");
12603 decc_posix_compliant_pathnames = decc$feature_get_value(s, 1);
12604 if (decc_posix_compliant_pathnames < 0)
12605 decc_posix_compliant_pathnames = 0;
12606 if (decc_posix_compliant_pathnames > 4)
12607 decc_posix_compliant_pathnames = 0;
12612 status = sys_trnlnm
12613 ("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION", val_str, sizeof(val_str));
12614 if ($VMS_STATUS_SUCCESS(status)) {
12615 val_str[0] = _toupper(val_str[0]);
12616 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
12617 decc_disable_to_vms_logname_translation = 1;
12622 status = sys_trnlnm("DECC$EFS_CASE_PRESERVE", val_str, sizeof(val_str));
12623 if ($VMS_STATUS_SUCCESS(status)) {
12624 val_str[0] = _toupper(val_str[0]);
12625 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
12626 decc_efs_case_preserve = 1;
12631 status = sys_trnlnm("DECC$FILENAME_UNIX_REPORT", val_str, sizeof(val_str));
12632 if ($VMS_STATUS_SUCCESS(status)) {
12633 val_str[0] = _toupper(val_str[0]);
12634 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
12635 decc_filename_unix_report = 1;
12638 status = sys_trnlnm("DECC$FILENAME_UNIX_ONLY", val_str, sizeof(val_str));
12639 if ($VMS_STATUS_SUCCESS(status)) {
12640 val_str[0] = _toupper(val_str[0]);
12641 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
12642 decc_filename_unix_only = 1;
12643 decc_filename_unix_report = 1;
12646 status = sys_trnlnm("DECC$FILENAME_UNIX_NO_VERSION", val_str, sizeof(val_str));
12647 if ($VMS_STATUS_SUCCESS(status)) {
12648 val_str[0] = _toupper(val_str[0]);
12649 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
12650 decc_filename_unix_no_version = 1;
12653 status = sys_trnlnm("DECC$READDIR_DROPDOTNOTYPE", val_str, sizeof(val_str));
12654 if ($VMS_STATUS_SUCCESS(status)) {
12655 val_str[0] = _toupper(val_str[0]);
12656 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
12657 decc_readdir_dropdotnotype = 1;
12662 #if defined(JPI$_CASE_LOOKUP_PERM) && !defined(__VAX)
12664 /* Report true case tolerance */
12665 /*----------------------------*/
12666 status = lib$getjpi(&jpicode1, 0, 0, &case_perm, 0, 0);
12667 if (!$VMS_STATUS_SUCCESS(status))
12668 case_perm = PPROP$K_CASE_BLIND;
12669 status = lib$getjpi(&jpicode2, 0, 0, &case_image, 0, 0);
12670 if (!$VMS_STATUS_SUCCESS(status))
12671 case_image = PPROP$K_CASE_BLIND;
12672 if ((case_perm == PPROP$K_CASE_SENSITIVE) ||
12673 (case_image == PPROP$K_CASE_SENSITIVE))
12674 vms_process_case_tolerant = 0;
12679 /* CRTL can be initialized past this point, but not before. */
12680 /* DECC$CRTL_INIT(); */
12687 #pragma extern_model save
12688 #pragma extern_model strict_refdef "LIB$INITIALIZ" nowrt
12689 const __align (LONGWORD) int spare[8] = {0};
12691 /* .psect LIB$INITIALIZE, NOPIC, USR, CON, REL, GBL, NOSHR, NOEXE, RD, NOWRT, LONG */
12692 #if __DECC_VER >= 60560002
12693 #pragma extern_model strict_refdef "LIB$INITIALIZE" nopic, con, rel, gbl, noshr, noexe, nowrt, long
12695 #pragma extern_model strict_refdef "LIB$INITIALIZE" nopic, con, gbl, noshr, nowrt, long
12697 #endif /* __DECC */
12699 const long vms_cc_features = (const long)set_features;
12702 ** Force a reference to LIB$INITIALIZE to ensure it
12703 ** exists in the image.
12705 int lib$initialize(void);
12707 #pragma extern_model strict_refdef
12709 int lib_init_ref = (int) lib$initialize;
12712 #pragma extern_model restore