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 #if __CRTL_VER >= 70300000 && !defined(__VAX)
96 static int set_feature_default(const char *name, int value)
101 index = decc$feature_get_index(name);
103 status = decc$feature_set_value(index, 1, value);
104 if (index == -1 || (status == -1)) {
108 status = decc$feature_get_value(index, 1);
109 if (status != value) {
117 /* Older versions of ssdef.h don't have these */
118 #ifndef SS$_INVFILFOROP
119 # define SS$_INVFILFOROP 3930
121 #ifndef SS$_NOSUCHOBJECT
122 # define SS$_NOSUCHOBJECT 2696
125 /* We implement I/O here, so we will be mixing PerlIO and stdio calls. */
126 #define PERLIO_NOT_STDIO 0
128 /* Don't replace system definitions of vfork, getenv, lstat, and stat,
129 * code below needs to get to the underlying CRTL routines. */
130 #define DONT_MASK_RTL_CALLS
134 /* Anticipating future expansion in lexical warnings . . . */
135 #ifndef WARN_INTERNAL
136 # define WARN_INTERNAL WARN_MISC
139 #ifdef VMS_LONGNAME_SUPPORT
140 #include <libfildef.h>
143 #if defined(__VMS_VER) && __VMS_VER >= 70000000 && __DECC_VER >= 50200000
144 # define RTL_USES_UTC 1
147 #ifdef USE_VMS_DECTERM
149 /* Routine to create a decterm for use with the Perl debugger */
150 /* No headers, this information was found in the Programming Concepts Manual */
153 (const struct dsc$descriptor_s * display,
154 const struct dsc$descriptor_s * setup_file,
155 const struct dsc$descriptor_s * customization,
156 struct dsc$descriptor_s * result_device_name,
157 unsigned short * result_device_name_length,
160 void * char_change_buffer);
163 /* gcc's header files don't #define direct access macros
164 * corresponding to VAXC's variant structs */
166 # define uic$v_format uic$r_uic_form.uic$v_format
167 # define uic$v_group uic$r_uic_form.uic$v_group
168 # define uic$v_member uic$r_uic_form.uic$v_member
169 # define prv$v_bypass prv$r_prvdef_bits0.prv$v_bypass
170 # define prv$v_grpprv prv$r_prvdef_bits0.prv$v_grpprv
171 # define prv$v_readall prv$r_prvdef_bits0.prv$v_readall
172 # define prv$v_sysprv prv$r_prvdef_bits0.prv$v_sysprv
175 #if defined(NEED_AN_H_ERRNO)
180 #pragma message disable pragma
181 #pragma member_alignment save
182 #pragma nomember_alignment longword
184 #pragma message disable misalgndmem
187 unsigned short int buflen;
188 unsigned short int itmcode;
190 unsigned short int *retlen;
193 struct filescan_itmlst_2 {
194 unsigned short length;
195 unsigned short itmcode;
200 unsigned short length;
205 #pragma message restore
206 #pragma member_alignment restore
209 #define do_fileify_dirspec(a,b,c,d) mp_do_fileify_dirspec(aTHX_ a,b,c,d)
210 #define do_pathify_dirspec(a,b,c,d) mp_do_pathify_dirspec(aTHX_ a,b,c,d)
211 #define do_tovmsspec(a,b,c,d) mp_do_tovmsspec(aTHX_ a,b,c,0,d)
212 #define do_tovmspath(a,b,c,d) mp_do_tovmspath(aTHX_ a,b,c,d)
213 #define do_rmsexpand(a,b,c,d,e,f,g) mp_do_rmsexpand(aTHX_ a,b,c,d,e,f,g)
214 #define do_vms_realpath(a,b,c) mp_do_vms_realpath(aTHX_ a,b,c)
215 #define do_tounixspec(a,b,c,d) mp_do_tounixspec(aTHX_ a,b,c,d)
216 #define do_tounixpath(a,b,c,d) mp_do_tounixpath(aTHX_ a,b,c,d)
217 #define do_vms_case_tolerant(a) mp_do_vms_case_tolerant(a)
218 #define expand_wild_cards(a,b,c,d) mp_expand_wild_cards(aTHX_ a,b,c,d)
219 #define getredirection(a,b) mp_getredirection(aTHX_ a,b)
221 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int *);
222 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int *);
223 static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *);
224 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int *);
226 /* see system service docs for $TRNLNM -- NOT the same as LNM$_MAX_INDEX */
227 #define PERL_LNM_MAX_ALLOWED_INDEX 127
229 /* OpenVMS User's Guide says at least 9 iterative translations will be performed,
230 * depending on the facility. SHOW LOGICAL does 10, so we'll imitate that for
233 #define PERL_LNM_MAX_ITER 10
235 /* New values with 7.3-2*/ /* why does max DCL have 4 byte subtracted from it? */
236 #if __CRTL_VER >= 70302000 && !defined(__VAX)
237 #define MAX_DCL_SYMBOL (8192)
238 #define MAX_DCL_LINE_LENGTH (4096 - 4)
240 #define MAX_DCL_SYMBOL (1024)
241 #define MAX_DCL_LINE_LENGTH (1024 - 4)
244 static char *__mystrtolower(char *str)
246 if (str) for (; *str; ++str) *str= tolower(*str);
250 static struct dsc$descriptor_s fildevdsc =
251 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "LNM$FILE_DEV" };
252 static struct dsc$descriptor_s crtlenvdsc =
253 { 8, DSC$K_DTYPE_T, DSC$K_CLASS_S, "CRTL_ENV" };
254 static struct dsc$descriptor_s *fildev[] = { &fildevdsc, NULL };
255 static struct dsc$descriptor_s *defenv[] = { &fildevdsc, &crtlenvdsc, NULL };
256 static struct dsc$descriptor_s **env_tables = defenv;
257 static bool will_taint = FALSE; /* tainting active, but no PL_curinterp yet */
259 /* True if we shouldn't treat barewords as logicals during directory */
261 static int no_translate_barewords;
264 static int tz_updated = 1;
267 /* DECC Features that may need to affect how Perl interprets
268 * displays filename information
270 static int decc_disable_to_vms_logname_translation = 1;
271 static int decc_disable_posix_root = 1;
272 int decc_efs_case_preserve = 0;
273 static int decc_efs_charset = 0;
274 static int decc_filename_unix_no_version = 0;
275 static int decc_filename_unix_only = 0;
276 int decc_filename_unix_report = 0;
277 int decc_posix_compliant_pathnames = 0;
278 int decc_readdir_dropdotnotype = 0;
279 static int vms_process_case_tolerant = 1;
280 int vms_vtf7_filenames = 0;
281 int gnv_unix_shell = 0;
283 /* bug workarounds if needed */
284 int decc_bug_readdir_efs1 = 0;
285 int decc_bug_devnull = 1;
286 int decc_bug_fgetname = 0;
287 int decc_dir_barename = 0;
289 static int vms_debug_on_exception = 0;
291 /* Is this a UNIX file specification?
292 * No longer a simple check with EFS file specs
293 * For now, not a full check, but need to
294 * handle POSIX ^UP^ specifications
295 * Fixing to handle ^/ cases would require
296 * changes to many other conversion routines.
299 static int is_unix_filespec(const char *path)
305 if (strncmp(path,"\"^UP^",5) != 0) {
306 pch1 = strchr(path, '/');
311 /* If the user wants UNIX files, "." needs to be treated as in UNIX */
312 if (decc_filename_unix_report || decc_filename_unix_only) {
313 if (strcmp(path,".") == 0)
321 /* This routine converts a UCS-2 character to be VTF-7 encoded.
324 static void ucs2_to_vtf7
326 unsigned long ucs2_char,
329 unsigned char * ucs_ptr;
332 ucs_ptr = (unsigned char *)&ucs2_char;
336 hex = (ucs_ptr[1] >> 4) & 0xf;
338 outspec[2] = hex + '0';
340 outspec[2] = (hex - 9) + 'A';
341 hex = ucs_ptr[1] & 0xF;
343 outspec[3] = hex + '0';
345 outspec[3] = (hex - 9) + 'A';
347 hex = (ucs_ptr[0] >> 4) & 0xf;
349 outspec[4] = hex + '0';
351 outspec[4] = (hex - 9) + 'A';
352 hex = ucs_ptr[1] & 0xF;
354 outspec[5] = hex + '0';
356 outspec[5] = (hex - 9) + 'A';
362 /* This handles the conversion of a UNIX extended character set to a ^
363 * escaped VMS character.
364 * in a UNIX file specification.
366 * The output count variable contains the number of characters added
367 * to the output string.
369 * The return value is the number of characters read from the input string
371 static int copy_expand_unix_filename_escape
372 (char *outspec, const char *inspec, int *output_cnt, const int * utf8_fl)
380 utf8_flag = *utf8_fl;
384 if (*inspec >= 0x80) {
385 if (utf8_fl && vms_vtf7_filenames) {
386 unsigned long ucs_char;
390 if ((*inspec & 0xE0) == 0xC0) {
392 ucs_char = ((inspec[0] & 0x1F) << 6) + (inspec[1] & 0x3f);
393 if (ucs_char >= 0x80) {
394 ucs2_to_vtf7(outspec, ucs_char, output_cnt);
397 } else if ((*inspec & 0xF0) == 0xE0) {
399 ucs_char = ((inspec[0] & 0xF) << 12) +
400 ((inspec[1] & 0x3f) << 6) +
402 if (ucs_char >= 0x800) {
403 ucs2_to_vtf7(outspec, ucs_char, output_cnt);
407 #if 0 /* I do not see longer sequences supported by OpenVMS */
408 /* Maybe some one can fix this later */
409 } else if ((*inspec & 0xF8) == 0xF0) {
412 } else if ((*inspec & 0xFC) == 0xF8) {
415 } else if ((*inspec & 0xFE) == 0xFC) {
422 /* High bit set, but not a unicode character! */
424 /* Non printing DECMCS or ISO Latin-1 character? */
425 if (*inspec <= 0x9F) {
429 hex = (*inspec >> 4) & 0xF;
431 outspec[1] = hex + '0';
433 outspec[1] = (hex - 9) + 'A';
437 outspec[2] = hex + '0';
439 outspec[2] = (hex - 9) + 'A';
443 } else if (*inspec == 0xA0) {
449 } else if (*inspec == 0xFF) {
461 /* Is this a macro that needs to be passed through?
462 * Macros start with $( and an alpha character, followed
463 * by a string of alpha numeric characters ending with a )
464 * If this does not match, then encode it as ODS-5.
466 if ((inspec[0] == '$') && (inspec[1] == '(')) {
469 if (isalnum(inspec[2]) || (inspec[2] == '.') || (inspec[2] == '_')) {
471 outspec[0] = inspec[0];
472 outspec[1] = inspec[1];
473 outspec[2] = inspec[2];
475 while(isalnum(inspec[tcnt]) ||
476 (inspec[2] == '.') || (inspec[2] == '_')) {
477 outspec[tcnt] = inspec[tcnt];
480 if (inspec[tcnt] == ')') {
481 outspec[tcnt] = inspec[tcnt];
498 if (decc_efs_charset == 0)
525 /* Assume that this is to be escaped */
527 outspec[1] = *inspec;
531 case ' ': /* space */
532 /* Assume that this is to be escaped */
547 /* This handles the expansion of a '^' prefix to the proper character
548 * in a UNIX file specification.
550 * The output count variable contains the number of characters added
551 * to the output string.
553 * The return value is the number of characters read from the input
556 static int copy_expand_vms_filename_escape
557 (char *outspec, const char *inspec, int *output_cnt)
564 if (*inspec == '^') {
568 /* Non trailing dots should just be passed through, but eat the escape */
572 case '_': /* space */
578 case 'U': /* Unicode - FIX-ME this is wrong. */
581 scnt = strspn(inspec, "0123456789ABCDEFabcdef");
584 scnt = sscanf(inspec, "%2x%2x", &c1, &c2);
585 outspec[0] == c1 & 0xff;
586 outspec[1] == c2 & 0xff;
593 /* Error - do best we can to continue */
603 scnt = strspn(inspec, "0123456789ABCDEFabcdef");
607 scnt = sscanf(inspec, "%2x", &c1);
608 outspec[0] = c1 & 0xff;
632 (const struct dsc$descriptor_s * srcstr,
633 struct filescan_itmlst_2 * valuelist,
634 unsigned long * fldflags,
635 struct dsc$descriptor_s *auxout,
636 unsigned short * retlen);
639 /* vms_split_path - Verify that the input file specification is a
640 * VMS format file specification, and provide pointers to the components of
641 * it. With EFS format filenames, this is virtually the only way to
642 * parse a VMS path specification into components.
644 * If the sum of the components do not add up to the length of the
645 * string, then the passed file specification is probably a UNIX style
648 static int vms_split_path
663 struct dsc$descriptor path_desc;
667 struct filescan_itmlst_2 item_list[9];
668 const int filespec = 0;
669 const int nodespec = 1;
670 const int devspec = 2;
671 const int rootspec = 3;
672 const int dirspec = 4;
673 const int namespec = 5;
674 const int typespec = 6;
675 const int verspec = 7;
677 /* Assume the worst for an easy exit */
692 path_desc.dsc$a_pointer = (char *)path; /* cast ok */
693 path_desc.dsc$w_length = strlen(path);
694 path_desc.dsc$b_dtype = DSC$K_DTYPE_T;
695 path_desc.dsc$b_class = DSC$K_CLASS_S;
697 /* Get the total length, if it is shorter than the string passed
698 * then this was probably not a VMS formatted file specification
700 item_list[filespec].itmcode = FSCN$_FILESPEC;
701 item_list[filespec].length = 0;
702 item_list[filespec].component = NULL;
704 /* If the node is present, then it gets considered as part of the
705 * volume name to hopefully make things simple.
707 item_list[nodespec].itmcode = FSCN$_NODE;
708 item_list[nodespec].length = 0;
709 item_list[nodespec].component = NULL;
711 item_list[devspec].itmcode = FSCN$_DEVICE;
712 item_list[devspec].length = 0;
713 item_list[devspec].component = NULL;
715 /* root is a special case, adding it to either the directory or
716 * the device components will probalby complicate things for the
717 * callers of this routine, so leave it separate.
719 item_list[rootspec].itmcode = FSCN$_ROOT;
720 item_list[rootspec].length = 0;
721 item_list[rootspec].component = NULL;
723 item_list[dirspec].itmcode = FSCN$_DIRECTORY;
724 item_list[dirspec].length = 0;
725 item_list[dirspec].component = NULL;
727 item_list[namespec].itmcode = FSCN$_NAME;
728 item_list[namespec].length = 0;
729 item_list[namespec].component = NULL;
731 item_list[typespec].itmcode = FSCN$_TYPE;
732 item_list[typespec].length = 0;
733 item_list[typespec].component = NULL;
735 item_list[verspec].itmcode = FSCN$_VERSION;
736 item_list[verspec].length = 0;
737 item_list[verspec].component = NULL;
739 item_list[8].itmcode = 0;
740 item_list[8].length = 0;
741 item_list[8].component = NULL;
743 status = sys$filescan
744 ((const struct dsc$descriptor_s *)&path_desc, item_list,
746 _ckvmssts_noperl(status); /* All failure status values indicate a coding error */
748 /* If we parsed it successfully these two lengths should be the same */
749 if (path_desc.dsc$w_length != item_list[filespec].length)
752 /* If we got here, then it is a VMS file specification */
755 /* set the volume name */
756 if (item_list[nodespec].length > 0) {
757 *volume = item_list[nodespec].component;
758 *vol_len = item_list[nodespec].length + item_list[devspec].length;
761 *volume = item_list[devspec].component;
762 *vol_len = item_list[devspec].length;
765 *root = item_list[rootspec].component;
766 *root_len = item_list[rootspec].length;
768 *dir = item_list[dirspec].component;
769 *dir_len = item_list[dirspec].length;
771 /* Now fun with versions and EFS file specifications
772 * The parser can not tell the difference when a "." is a version
773 * delimiter or a part of the file specification.
775 if ((decc_efs_charset) &&
776 (item_list[verspec].length > 0) &&
777 (item_list[verspec].component[0] == '.')) {
778 *name = item_list[namespec].component;
779 *name_len = item_list[namespec].length + item_list[typespec].length;
780 *ext = item_list[verspec].component;
781 *ext_len = item_list[verspec].length;
786 *name = item_list[namespec].component;
787 *name_len = item_list[namespec].length;
788 *ext = item_list[typespec].component;
789 *ext_len = item_list[typespec].length;
790 *version = item_list[verspec].component;
791 *ver_len = item_list[verspec].length;
798 * Routine to retrieve the maximum equivalence index for an input
799 * logical name. Some calls to this routine have no knowledge if
800 * the variable is a logical or not. So on error we return a max
803 /*{{{int my_maxidx(const char *lnm) */
805 my_maxidx(const char *lnm)
809 int attr = LNM$M_CASE_BLIND;
810 struct dsc$descriptor lnmdsc;
811 struct itmlst_3 itlst[2] = {{sizeof(midx), LNM$_MAX_INDEX, &midx, 0},
814 lnmdsc.dsc$w_length = strlen(lnm);
815 lnmdsc.dsc$b_dtype = DSC$K_DTYPE_T;
816 lnmdsc.dsc$b_class = DSC$K_CLASS_S;
817 lnmdsc.dsc$a_pointer = (char *) lnm; /* Cast ok for read only parameter */
819 status = sys$trnlnm(&attr, &fildevdsc, &lnmdsc, 0, itlst);
820 if ((status & 1) == 0)
827 /*{{{int vmstrnenv(const char *lnm, char *eqv, unsigned long int idx, struct dsc$descriptor_s **tabvec, unsigned long int flags) */
829 Perl_vmstrnenv(const char *lnm, char *eqv, unsigned long int idx,
830 struct dsc$descriptor_s **tabvec, unsigned long int flags)
833 char uplnm[LNM$C_NAMLENGTH+1], *cp2;
834 unsigned short int eqvlen, curtab, ivlnm = 0, ivsym = 0, ivenv = 0, secure;
835 unsigned long int retsts, attr = LNM$M_CASE_BLIND;
837 unsigned char acmode;
838 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
839 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
840 struct itmlst_3 lnmlst[3] = {{sizeof idx, LNM$_INDEX, &idx, 0},
841 {LNM$C_NAMLENGTH, LNM$_STRING, eqv, &eqvlen},
843 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
844 #if defined(PERL_IMPLICIT_CONTEXT)
847 aTHX = PERL_GET_INTERP;
853 if (!lnm || !eqv || ((idx != 0) && ((idx-1) > PERL_LNM_MAX_ALLOWED_INDEX))) {
854 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return 0;
856 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
857 *cp2 = _toupper(*cp1);
858 if (cp1 - lnm > LNM$C_NAMLENGTH) {
859 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
863 lnmdsc.dsc$w_length = cp1 - lnm;
864 lnmdsc.dsc$a_pointer = uplnm;
865 uplnm[lnmdsc.dsc$w_length] = '\0';
866 secure = flags & PERL__TRNENV_SECURE;
867 acmode = secure ? PSL$C_EXEC : PSL$C_USER;
868 if (!tabvec || !*tabvec) tabvec = env_tables;
870 for (curtab = 0; tabvec[curtab]; curtab++) {
871 if (!str$case_blind_compare(tabvec[curtab],&crtlenv)) {
872 if (!ivenv && !secure) {
877 Perl_warn(aTHX_ "Can't read CRTL environ\n");
880 retsts = SS$_NOLOGNAM;
881 for (i = 0; environ[i]; i++) {
882 if ((eq = strchr(environ[i],'=')) &&
883 lnmdsc.dsc$w_length == (eq - environ[i]) &&
884 !strncmp(environ[i],uplnm,eq - environ[i])) {
886 for (eqvlen = 0; eq[eqvlen]; eqvlen++) eqv[eqvlen] = eq[eqvlen];
887 if (!eqvlen) continue;
892 if (retsts != SS$_NOLOGNAM) break;
895 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
896 !str$case_blind_compare(&tmpdsc,&clisym)) {
897 if (!ivsym && !secure) {
898 unsigned short int deflen = LNM$C_NAMLENGTH;
899 struct dsc$descriptor_d eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
900 /* dynamic dsc to accomodate possible long value */
901 _ckvmssts(lib$sget1_dd(&deflen,&eqvdsc));
902 retsts = lib$get_symbol(&lnmdsc,&eqvdsc,&eqvlen,0);
904 if (eqvlen > MAX_DCL_SYMBOL) {
905 set_errno(EVMSERR); set_vaxc_errno(LIB$_STRTRU);
906 eqvlen = MAX_DCL_SYMBOL;
907 /* Special hack--we might be called before the interpreter's */
908 /* fully initialized, in which case either thr or PL_curcop */
909 /* might be bogus. We have to check, since ckWARN needs them */
910 /* both to be valid if running threaded */
911 if (ckWARN(WARN_MISC)) {
912 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of CLI symbol \"%s\" too long",lnm);
915 strncpy(eqv,eqvdsc.dsc$a_pointer,eqvlen);
917 _ckvmssts(lib$sfree1_dd(&eqvdsc));
918 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
919 if (retsts == LIB$_NOSUCHSYM) continue;
924 if ( (idx == 0) && (flags & PERL__TRNENV_JOIN_SEARCHLIST) ) {
925 midx = my_maxidx(lnm);
926 for (idx = 0, cp2 = eqv; idx <= midx; idx++) {
927 lnmlst[1].bufadr = cp2;
929 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
930 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; break; }
931 if (retsts == SS$_NOLOGNAM) break;
932 /* PPFs have a prefix */
935 *((int *)uplnm) == *((int *)"SYS$") &&
937 eqvlen >= 4 && eqv[0] == 0x1b && eqv[1] == 0x00 &&
938 ( (uplnm[4] == 'O' && !strcmp(uplnm,"SYS$OUTPUT")) ||
939 (uplnm[4] == 'I' && !strcmp(uplnm,"SYS$INPUT")) ||
940 (uplnm[4] == 'E' && !strcmp(uplnm,"SYS$ERROR")) ||
941 (uplnm[4] == 'C' && !strcmp(uplnm,"SYS$COMMAND")) ) ) {
942 memmove(eqv,eqv+4,eqvlen-4);
948 if ((retsts == SS$_IVLOGNAM) ||
949 (retsts == SS$_NOLOGNAM)) { continue; }
952 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
953 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
954 if (retsts == SS$_NOLOGNAM) continue;
957 eqvlen = strlen(eqv);
961 if (retsts & 1) { eqv[eqvlen] = '\0'; return eqvlen; }
962 else if (retsts == LIB$_NOSUCHSYM || retsts == LIB$_INVSYMNAM ||
963 retsts == SS$_IVLOGNAM || retsts == SS$_IVLOGTAB ||
964 retsts == SS$_NOLOGNAM) {
965 set_errno(EINVAL); set_vaxc_errno(retsts);
967 else _ckvmssts(retsts);
969 } /* end of vmstrnenv */
972 /*{{{ int my_trnlnm(const char *lnm, char *eqv, unsigned long int idx)*/
973 /* Define as a function so we can access statics. */
974 int Perl_my_trnlnm(pTHX_ const char *lnm, char *eqv, unsigned long int idx)
976 return vmstrnenv(lnm,eqv,idx,fildev,
977 #ifdef SECURE_INTERNAL_GETENV
978 (PL_curinterp ? PL_tainting : will_taint) ? PERL__TRNENV_SECURE : 0
987 * Note: Uses Perl temp to store result so char * can be returned to
988 * caller; this pointer will be invalidated at next Perl statement
990 * We define this as a function rather than a macro in terms of my_getenv_len()
991 * so that it'll work when PL_curinterp is undefined (and we therefore can't
994 /*{{{ char *my_getenv(const char *lnm, bool sys)*/
996 Perl_my_getenv(pTHX_ const char *lnm, bool sys)
999 static char *__my_getenv_eqv = NULL;
1000 char uplnm[LNM$C_NAMLENGTH+1], *cp2, *eqv;
1001 unsigned long int idx = 0;
1002 int trnsuccess, success, secure, saverr, savvmserr;
1006 midx = my_maxidx(lnm) + 1;
1008 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
1009 /* Set up a temporary buffer for the return value; Perl will
1010 * clean it up at the next statement transition */
1011 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
1012 if (!tmpsv) return NULL;
1016 /* Assume no interpreter ==> single thread */
1017 if (__my_getenv_eqv != NULL) {
1018 Renew(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
1021 Newx(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
1023 eqv = __my_getenv_eqv;
1026 for (cp1 = lnm, cp2 = eqv; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
1027 if (cp1 - lnm == 7 && !strncmp(eqv,"DEFAULT",7)) {
1029 getcwd(eqv,LNM$C_NAMLENGTH);
1033 /* Get rid of "000000/ in rooted filespecs */
1036 zeros = strstr(eqv, "/000000/");
1037 if (zeros != NULL) {
1039 mlen = len - (zeros - eqv) - 7;
1040 memmove(zeros, &zeros[7], mlen);
1048 /* Impose security constraints only if tainting */
1050 /* Impose security constraints only if tainting */
1051 secure = PL_curinterp ? PL_tainting : will_taint;
1052 saverr = errno; savvmserr = vaxc$errno;
1059 #ifdef SECURE_INTERNAL_GETENV
1060 secure ? PERL__TRNENV_SECURE : 0
1066 /* For the getenv interface we combine all the equivalence names
1067 * of a search list logical into one value to acquire a maximum
1068 * value length of 255*128 (assuming %ENV is using logicals).
1070 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
1072 /* If the name contains a semicolon-delimited index, parse it
1073 * off and make sure we only retrieve the equivalence name for
1075 if ((cp2 = strchr(lnm,';')) != NULL) {
1077 uplnm[cp2-lnm] = '\0';
1078 idx = strtoul(cp2+1,NULL,0);
1080 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
1083 success = vmstrnenv(lnm,eqv,idx,secure ? fildev : NULL,flags);
1085 /* Discard NOLOGNAM on internal calls since we're often looking
1086 * for an optional name, and this "error" often shows up as the
1087 * (bogus) exit status for a die() call later on. */
1088 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
1089 return success ? eqv : Nullch;
1092 } /* end of my_getenv() */
1096 /*{{{ SV *my_getenv_len(const char *lnm, bool sys)*/
1098 Perl_my_getenv_len(pTHX_ const char *lnm, unsigned long *len, bool sys)
1102 unsigned long idx = 0;
1104 static char *__my_getenv_len_eqv = NULL;
1105 int secure, saverr, savvmserr;
1108 midx = my_maxidx(lnm) + 1;
1110 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
1111 /* Set up a temporary buffer for the return value; Perl will
1112 * clean it up at the next statement transition */
1113 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
1114 if (!tmpsv) return NULL;
1118 /* Assume no interpreter ==> single thread */
1119 if (__my_getenv_len_eqv != NULL) {
1120 Renew(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
1123 Newx(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
1125 buf = __my_getenv_len_eqv;
1128 for (cp1 = lnm, cp2 = buf; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
1129 if (cp1 - lnm == 7 && !strncmp(buf,"DEFAULT",7)) {
1132 getcwd(buf,LNM$C_NAMLENGTH);
1135 /* Get rid of "000000/ in rooted filespecs */
1137 zeros = strstr(buf, "/000000/");
1138 if (zeros != NULL) {
1140 mlen = *len - (zeros - buf) - 7;
1141 memmove(zeros, &zeros[7], mlen);
1150 /* Impose security constraints only if tainting */
1151 secure = PL_curinterp ? PL_tainting : will_taint;
1152 saverr = errno; savvmserr = vaxc$errno;
1159 #ifdef SECURE_INTERNAL_GETENV
1160 secure ? PERL__TRNENV_SECURE : 0
1166 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
1168 if ((cp2 = strchr(lnm,';')) != NULL) {
1170 buf[cp2-lnm] = '\0';
1171 idx = strtoul(cp2+1,NULL,0);
1173 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
1176 *len = vmstrnenv(lnm,buf,idx,secure ? fildev : NULL,flags);
1178 /* Get rid of "000000/ in rooted filespecs */
1181 zeros = strstr(buf, "/000000/");
1182 if (zeros != NULL) {
1184 mlen = *len - (zeros - buf) - 7;
1185 memmove(zeros, &zeros[7], mlen);
1191 /* Discard NOLOGNAM on internal calls since we're often looking
1192 * for an optional name, and this "error" often shows up as the
1193 * (bogus) exit status for a die() call later on. */
1194 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
1195 return *len ? buf : Nullch;
1198 } /* end of my_getenv_len() */
1201 static void create_mbx(pTHX_ unsigned short int *, struct dsc$descriptor_s *);
1203 static void riseandshine(unsigned long int dummy) { sys$wake(0,0); }
1205 /*{{{ void prime_env_iter() */
1207 prime_env_iter(void)
1208 /* Fill the %ENV associative array with all logical names we can
1209 * find, in preparation for iterating over it.
1212 static int primed = 0;
1213 HV *seenhv = NULL, *envhv;
1215 char cmd[LNM$C_NAMLENGTH+24], mbxnam[LNM$C_NAMLENGTH], *buf = Nullch;
1216 unsigned short int chan;
1217 #ifndef CLI$M_TRUSTED
1218 # define CLI$M_TRUSTED 0x40 /* Missing from VAXC headers */
1220 unsigned long int defflags = CLI$M_NOWAIT | CLI$M_NOKEYPAD | CLI$M_TRUSTED;
1221 unsigned long int mbxbufsiz, flags, retsts, subpid = 0, substs = 0, wakect = 0;
1223 bool have_sym = FALSE, have_lnm = FALSE;
1224 struct dsc$descriptor_s tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1225 $DESCRIPTOR(cmddsc,cmd); $DESCRIPTOR(nldsc,"_NLA0:");
1226 $DESCRIPTOR(clidsc,"DCL"); $DESCRIPTOR(clitabdsc,"DCLTABLES");
1227 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
1228 $DESCRIPTOR(local,"_LOCAL"); $DESCRIPTOR(mbxdsc,mbxnam);
1229 #if defined(PERL_IMPLICIT_CONTEXT)
1232 #if defined(USE_ITHREADS)
1233 static perl_mutex primenv_mutex;
1234 MUTEX_INIT(&primenv_mutex);
1237 #if defined(PERL_IMPLICIT_CONTEXT)
1238 /* We jump through these hoops because we can be called at */
1239 /* platform-specific initialization time, which is before anything is */
1240 /* set up--we can't even do a plain dTHX since that relies on the */
1241 /* interpreter structure to be initialized */
1243 aTHX = PERL_GET_INTERP;
1249 if (primed || !PL_envgv) return;
1250 MUTEX_LOCK(&primenv_mutex);
1251 if (primed) { MUTEX_UNLOCK(&primenv_mutex); return; }
1252 envhv = GvHVn(PL_envgv);
1253 /* Perform a dummy fetch as an lval to insure that the hash table is
1254 * set up. Otherwise, the hv_store() will turn into a nullop. */
1255 (void) hv_fetch(envhv,"DEFAULT",7,TRUE);
1257 for (i = 0; env_tables[i]; i++) {
1258 if (!have_sym && (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
1259 !str$case_blind_compare(&tmpdsc,&clisym)) have_sym = 1;
1260 if (!have_lnm && str$case_blind_compare(env_tables[i],&crtlenv)) have_lnm = 1;
1262 if (have_sym || have_lnm) {
1263 long int syiitm = SYI$_MAXBUF, dviitm = DVI$_DEVNAM;
1264 _ckvmssts(lib$getsyi(&syiitm, &mbxbufsiz, 0, 0, 0, 0));
1265 _ckvmssts(sys$crembx(0,&chan,mbxbufsiz,mbxbufsiz,0xff0f,0,0));
1266 _ckvmssts(lib$getdvi(&dviitm, &chan, NULL, NULL, &mbxdsc, &mbxdsc.dsc$w_length));
1269 for (i--; i >= 0; i--) {
1270 if (!str$case_blind_compare(env_tables[i],&crtlenv)) {
1273 for (j = 0; environ[j]; j++) {
1274 if (!(start = strchr(environ[j],'='))) {
1275 if (ckWARN(WARN_INTERNAL))
1276 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed CRTL environ value \"%s\"\n",environ[j]);
1280 sv = newSVpv(start,0);
1282 (void) hv_store(envhv,environ[j],start - environ[j] - 1,sv,0);
1287 else if ((tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
1288 !str$case_blind_compare(&tmpdsc,&clisym)) {
1289 strcpy(cmd,"Show Symbol/Global *");
1290 cmddsc.dsc$w_length = 20;
1291 if (env_tables[i]->dsc$w_length == 12 &&
1292 (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer + 6) &&
1293 !str$case_blind_compare(&tmpdsc,&local)) strcpy(cmd+12,"Local *");
1294 flags = defflags | CLI$M_NOLOGNAM;
1297 strcpy(cmd,"Show Logical *");
1298 if (str$case_blind_compare(env_tables[i],&fildevdsc)) {
1299 strcat(cmd," /Table=");
1300 strncat(cmd,env_tables[i]->dsc$a_pointer,env_tables[i]->dsc$w_length);
1301 cmddsc.dsc$w_length = strlen(cmd);
1303 else cmddsc.dsc$w_length = 14; /* N.B. We test this below */
1304 flags = defflags | CLI$M_NOCLISYM;
1307 /* Create a new subprocess to execute each command, to exclude the
1308 * remote possibility that someone could subvert a mbx or file used
1309 * to write multiple commands to a single subprocess.
1312 retsts = lib$spawn(&cmddsc,&nldsc,&mbxdsc,&flags,0,&subpid,&substs,
1313 0,&riseandshine,0,0,&clidsc,&clitabdsc);
1314 flags &= ~CLI$M_TRUSTED; /* Just in case we hit a really old version */
1315 defflags &= ~CLI$M_TRUSTED;
1316 } while (retsts == LIB$_INVARG && (flags | CLI$M_TRUSTED));
1318 if (!buf) Newx(buf,mbxbufsiz + 1,char);
1319 if (seenhv) SvREFCNT_dec(seenhv);
1322 char *cp1, *cp2, *key;
1323 unsigned long int sts, iosb[2], retlen, keylen;
1326 sts = sys$qiow(0,chan,IO$_READVBLK,iosb,0,0,buf,mbxbufsiz,0,0,0,0);
1327 if (sts & 1) sts = iosb[0] & 0xffff;
1328 if (sts == SS$_ENDOFFILE) {
1330 while (substs == 0) { sys$hiber(); wakect++;}
1331 if (wakect > 1) sys$wake(0,0); /* Stole someone else's wake */
1336 retlen = iosb[0] >> 16;
1337 if (!retlen) continue; /* blank line */
1339 if (iosb[1] != subpid) {
1341 Perl_croak(aTHX_ "Unknown process %x sent message to prime_env_iter: %s",buf);
1345 if (sts == SS$_BUFFEROVF && ckWARN(WARN_INTERNAL))
1346 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Buffer overflow in prime_env_iter: %s",buf);
1348 for (cp1 = buf; *cp1 && isspace(*cp1); cp1++) ;
1349 if (*cp1 == '(' || /* Logical name table name */
1350 *cp1 == '=' /* Next eqv of searchlist */) continue;
1351 if (*cp1 == '"') cp1++;
1352 for (cp2 = cp1; *cp2 && *cp2 != '"' && *cp2 != ' '; cp2++) ;
1353 key = cp1; keylen = cp2 - cp1;
1354 if (keylen && hv_exists(seenhv,key,keylen)) continue;
1355 while (*cp2 && *cp2 != '=') cp2++;
1356 while (*cp2 && *cp2 == '=') cp2++;
1357 while (*cp2 && *cp2 == ' ') cp2++;
1358 if (*cp2 == '"') { /* String translation; may embed "" */
1359 for (cp1 = buf + retlen; *cp1 != '"'; cp1--) ;
1360 cp2++; cp1--; /* Skip "" surrounding translation */
1362 else { /* Numeric translation */
1363 for (cp1 = cp2; *cp1 && *cp1 != ' '; cp1++) ;
1364 cp1--; /* stop on last non-space char */
1366 if ((!keylen || (cp1 - cp2 < -1)) && ckWARN(WARN_INTERNAL)) {
1367 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed message in prime_env_iter: |%s|",buf);
1370 PERL_HASH(hash,key,keylen);
1372 if (cp1 == cp2 && *cp2 == '.') {
1373 /* A single dot usually means an unprintable character, such as a null
1374 * to indicate a zero-length value. Get the actual value to make sure.
1376 char lnm[LNM$C_NAMLENGTH+1];
1377 char eqv[MAX_DCL_SYMBOL+1];
1379 strncpy(lnm, key, keylen);
1380 trnlen = vmstrnenv(lnm, eqv, 0, fildev, 0);
1381 sv = newSVpvn(eqv, strlen(eqv));
1384 sv = newSVpvn(cp2,cp1 - cp2 + 1);
1388 hv_store(envhv,key,keylen,sv,hash);
1389 hv_store(seenhv,key,keylen,&PL_sv_yes,hash);
1391 if (cmddsc.dsc$w_length == 14) { /* We just read LNM$FILE_DEV */
1392 /* get the PPFs for this process, not the subprocess */
1393 const char *ppfs[] = {"SYS$COMMAND", "SYS$INPUT", "SYS$OUTPUT", "SYS$ERROR", NULL};
1394 char eqv[LNM$C_NAMLENGTH+1];
1396 for (i = 0; ppfs[i]; i++) {
1397 trnlen = vmstrnenv(ppfs[i],eqv,0,fildev,0);
1398 sv = newSVpv(eqv,trnlen);
1400 hv_store(envhv,ppfs[i],strlen(ppfs[i]),sv,0);
1405 if (have_sym || have_lnm) _ckvmssts(sys$dassgn(chan));
1406 if (buf) Safefree(buf);
1407 if (seenhv) SvREFCNT_dec(seenhv);
1408 MUTEX_UNLOCK(&primenv_mutex);
1411 } /* end of prime_env_iter */
1415 /*{{{ int vmssetenv(const char *lnm, const char *eqv)*/
1416 /* Define or delete an element in the same "environment" as
1417 * vmstrnenv(). If an element is to be deleted, it's removed from
1418 * the first place it's found. If it's to be set, it's set in the
1419 * place designated by the first element of the table vector.
1420 * Like setenv() returns 0 for success, non-zero on error.
1423 Perl_vmssetenv(pTHX_ const char *lnm, const char *eqv, struct dsc$descriptor_s **tabvec)
1426 char uplnm[LNM$C_NAMLENGTH], *cp2, *c;
1427 unsigned short int curtab, ivlnm = 0, ivsym = 0, ivenv = 0;
1429 unsigned long int retsts, usermode = PSL$C_USER;
1430 struct itmlst_3 *ile, *ilist;
1431 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,uplnm},
1432 eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
1433 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1434 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
1435 $DESCRIPTOR(local,"_LOCAL");
1438 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1439 return SS$_IVLOGNAM;
1442 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
1443 *cp2 = _toupper(*cp1);
1444 if (cp1 - lnm > LNM$C_NAMLENGTH) {
1445 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1446 return SS$_IVLOGNAM;
1449 lnmdsc.dsc$w_length = cp1 - lnm;
1450 if (!tabvec || !*tabvec) tabvec = env_tables;
1452 if (!eqv) { /* we're deleting n element */
1453 for (curtab = 0; tabvec[curtab]; curtab++) {
1454 if (!ivenv && !str$case_blind_compare(tabvec[curtab],&crtlenv)) {
1456 for (i = 0; environ[i]; i++) { /* If it's an environ elt, reset */
1457 if ((cp1 = strchr(environ[i],'=')) &&
1458 lnmdsc.dsc$w_length == (cp1 - environ[i]) &&
1459 !strncmp(environ[i],lnm,cp1 - environ[i])) {
1461 return setenv(lnm,"",1) ? vaxc$errno : 0;
1464 ivenv = 1; retsts = SS$_NOLOGNAM;
1466 if (ckWARN(WARN_INTERNAL))
1467 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't reset CRTL environ elements (%s)",lnm);
1468 ivenv = 1; retsts = SS$_NOSUCHPGM;
1474 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
1475 !str$case_blind_compare(&tmpdsc,&clisym)) {
1476 unsigned int symtype;
1477 if (tabvec[curtab]->dsc$w_length == 12 &&
1478 (tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer + 6) &&
1479 !str$case_blind_compare(&tmpdsc,&local))
1480 symtype = LIB$K_CLI_LOCAL_SYM;
1481 else symtype = LIB$K_CLI_GLOBAL_SYM;
1482 retsts = lib$delete_symbol(&lnmdsc,&symtype);
1483 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
1484 if (retsts == LIB$_NOSUCHSYM) continue;
1488 retsts = sys$dellnm(tabvec[curtab],&lnmdsc,&usermode); /* try user mode first */
1489 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
1490 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
1491 retsts = lib$delete_logical(&lnmdsc,tabvec[curtab]); /* then supervisor mode */
1492 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
1496 else { /* we're defining a value */
1497 if (!ivenv && !str$case_blind_compare(tabvec[0],&crtlenv)) {
1499 return setenv(lnm,eqv,1) ? vaxc$errno : 0;
1501 if (ckWARN(WARN_INTERNAL))
1502 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't set CRTL environ elements (%s=%s)",lnm,eqv);
1503 retsts = SS$_NOSUCHPGM;
1507 eqvdsc.dsc$a_pointer = (char *) eqv; /* cast ok to readonly parameter */
1508 eqvdsc.dsc$w_length = strlen(eqv);
1509 if ((tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer) &&
1510 !str$case_blind_compare(&tmpdsc,&clisym)) {
1511 unsigned int symtype;
1512 if (tabvec[0]->dsc$w_length == 12 &&
1513 (tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer + 6) &&
1514 !str$case_blind_compare(&tmpdsc,&local))
1515 symtype = LIB$K_CLI_LOCAL_SYM;
1516 else symtype = LIB$K_CLI_GLOBAL_SYM;
1517 retsts = lib$set_symbol(&lnmdsc,&eqvdsc,&symtype);
1520 if (!*eqv) eqvdsc.dsc$w_length = 1;
1521 if (eqvdsc.dsc$w_length > LNM$C_NAMLENGTH) {
1523 nseg = (eqvdsc.dsc$w_length + LNM$C_NAMLENGTH - 1) / LNM$C_NAMLENGTH;
1524 if (nseg > PERL_LNM_MAX_ALLOWED_INDEX + 1) {
1525 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of logical \"%s\" too long. Truncating to %i bytes",
1526 lnm, LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1));
1527 eqvdsc.dsc$w_length = LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1);
1528 nseg = PERL_LNM_MAX_ALLOWED_INDEX + 1;
1531 Newx(ilist,nseg+1,struct itmlst_3);
1534 set_errno(ENOMEM); set_vaxc_errno(SS$_INSFMEM);
1537 memset(ilist, 0, (sizeof(struct itmlst_3) * (nseg+1)));
1539 for (j = 0, c = eqvdsc.dsc$a_pointer; j < nseg; j++, ile++, c += LNM$C_NAMLENGTH) {
1540 ile->itmcode = LNM$_STRING;
1542 if ((j+1) == nseg) {
1543 ile->buflen = strlen(c);
1544 /* in case we are truncating one that's too long */
1545 if (ile->buflen > LNM$C_NAMLENGTH) ile->buflen = LNM$C_NAMLENGTH;
1548 ile->buflen = LNM$C_NAMLENGTH;
1552 retsts = lib$set_logical(&lnmdsc,0,tabvec[0],0,ilist);
1556 retsts = lib$set_logical(&lnmdsc,&eqvdsc,tabvec[0],0,0);
1561 if (!(retsts & 1)) {
1563 case LIB$_AMBSYMDEF: case LIB$_INSCLIMEM:
1564 case SS$_NOLOGTAB: case SS$_TOOMANYLNAM: case SS$_IVLOGTAB:
1565 set_errno(EVMSERR); break;
1566 case LIB$_INVARG: case LIB$_INVSYMNAM: case SS$_IVLOGNAM:
1567 case LIB$_NOSUCHSYM: case SS$_NOLOGNAM:
1568 set_errno(EINVAL); break;
1570 set_errno(EACCES); break;
1575 set_vaxc_errno(retsts);
1576 return (int) retsts || 44; /* retsts should never be 0, but just in case */
1579 /* We reset error values on success because Perl does an hv_fetch()
1580 * before each hv_store(), and if the thing we're setting didn't
1581 * previously exist, we've got a leftover error message. (Of course,
1582 * this fails in the face of
1583 * $foo = $ENV{nonexistent}; $ENV{existent} = 'foo';
1584 * in that the error reported in $! isn't spurious,
1585 * but it's right more often than not.)
1587 set_errno(0); set_vaxc_errno(retsts);
1591 } /* end of vmssetenv() */
1594 /*{{{ void my_setenv(const char *lnm, const char *eqv)*/
1595 /* This has to be a function since there's a prototype for it in proto.h */
1597 Perl_my_setenv(pTHX_ const char *lnm, const char *eqv)
1600 int len = strlen(lnm);
1604 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
1605 if (!strcmp(uplnm,"DEFAULT")) {
1606 if (eqv && *eqv) my_chdir(eqv);
1610 #ifndef RTL_USES_UTC
1611 if (len == 6 || len == 2) {
1614 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
1616 if (!strcmp(uplnm,"UCX$TZ")) tz_updated = 1;
1617 if (!strcmp(uplnm,"TZ")) tz_updated = 1;
1621 (void) vmssetenv(lnm,eqv,NULL);
1625 /*{{{static void vmssetuserlnm(char *name, char *eqv); */
1627 * sets a user-mode logical in the process logical name table
1628 * used for redirection of sys$error
1631 Perl_vmssetuserlnm(pTHX_ const char *name, const char *eqv)
1633 $DESCRIPTOR(d_tab, "LNM$PROCESS");
1634 struct dsc$descriptor_d d_name = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
1635 unsigned long int iss, attr = LNM$M_CONFINE;
1636 unsigned char acmode = PSL$C_USER;
1637 struct itmlst_3 lnmlst[2] = {{0, LNM$_STRING, 0, 0},
1639 d_name.dsc$a_pointer = (char *)name; /* Cast OK for read only parameter */
1640 d_name.dsc$w_length = strlen(name);
1642 lnmlst[0].buflen = strlen(eqv);
1643 lnmlst[0].bufadr = (char *)eqv; /* Cast OK for read only parameter */
1645 iss = sys$crelnm(&attr,&d_tab,&d_name,&acmode,lnmlst);
1646 if (!(iss&1)) lib$signal(iss);
1651 /*{{{ char *my_crypt(const char *textpasswd, const char *usrname)*/
1652 /* my_crypt - VMS password hashing
1653 * my_crypt() provides an interface compatible with the Unix crypt()
1654 * C library function, and uses sys$hash_password() to perform VMS
1655 * password hashing. The quadword hashed password value is returned
1656 * as a NUL-terminated 8 character string. my_crypt() does not change
1657 * the case of its string arguments; in order to match the behavior
1658 * of LOGINOUT et al., alphabetic characters in both arguments must
1659 * be upcased by the caller.
1661 * - fix me to call ACM services when available
1664 Perl_my_crypt(pTHX_ const char *textpasswd, const char *usrname)
1666 # ifndef UAI$C_PREFERRED_ALGORITHM
1667 # define UAI$C_PREFERRED_ALGORITHM 127
1669 unsigned char alg = UAI$C_PREFERRED_ALGORITHM;
1670 unsigned short int salt = 0;
1671 unsigned long int sts;
1673 unsigned short int dsc$w_length;
1674 unsigned char dsc$b_type;
1675 unsigned char dsc$b_class;
1676 const char * dsc$a_pointer;
1677 } usrdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
1678 txtdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1679 struct itmlst_3 uailst[3] = {
1680 { sizeof alg, UAI$_ENCRYPT, &alg, 0},
1681 { sizeof salt, UAI$_SALT, &salt, 0},
1682 { 0, 0, NULL, NULL}};
1683 static char hash[9];
1685 usrdsc.dsc$w_length = strlen(usrname);
1686 usrdsc.dsc$a_pointer = usrname;
1687 if (!((sts = sys$getuai(0, 0, &usrdsc, uailst, 0, 0, 0)) & 1)) {
1689 case SS$_NOGRPPRV: case SS$_NOSYSPRV:
1693 set_errno(ESRCH); /* There isn't a Unix no-such-user error */
1698 set_vaxc_errno(sts);
1699 if (sts != RMS$_RNF) return NULL;
1702 txtdsc.dsc$w_length = strlen(textpasswd);
1703 txtdsc.dsc$a_pointer = textpasswd;
1704 if (!((sts = sys$hash_password(&txtdsc, alg, salt, &usrdsc, &hash)) & 1)) {
1705 set_errno(EVMSERR); set_vaxc_errno(sts); return NULL;
1708 return (char *) hash;
1710 } /* end of my_crypt() */
1714 static char *mp_do_rmsexpand(pTHX_ const char *, char *, int, const char *, unsigned, int *, int *);
1715 static char *mp_do_fileify_dirspec(pTHX_ const char *, char *, int, int *);
1716 static char *mp_do_tovmsspec(pTHX_ const char *, char *, int, int, int *);
1718 /* fixup barenames that are directories for internal use.
1719 * There have been problems with the consistent handling of UNIX
1720 * style directory names when routines are presented with a name that
1721 * has no directory delimitors at all. So this routine will eventually
1724 static char * fixup_bare_dirnames(const char * name)
1726 if (decc_disable_to_vms_logname_translation) {
1733 * A little hack to get around a bug in some implemenation of remove()
1734 * that do not know how to delete a directory
1736 * Delete any file to which user has control access, regardless of whether
1737 * delete access is explicitly allowed.
1738 * Limitations: User must have write access to parent directory.
1739 * Does not block signals or ASTs; if interrupted in midstream
1740 * may leave file with an altered ACL.
1743 /*{{{int mp_do_kill_file(const char *name, int dirflag)*/
1745 mp_do_kill_file(pTHX_ const char *name, int dirflag)
1747 char *vmsname, *rspec;
1749 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
1750 unsigned long int cxt = 0, aclsts, fndsts, rmsts = -1;
1751 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1753 unsigned char myace$b_length;
1754 unsigned char myace$b_type;
1755 unsigned short int myace$w_flags;
1756 unsigned long int myace$l_access;
1757 unsigned long int myace$l_ident;
1758 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
1759 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0},
1760 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
1762 findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0},
1763 {sizeof oldace, ACL$C_READACE, &oldace, 0},{0,0,0,0}},
1764 addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}},
1765 dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}},
1766 lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}},
1767 ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}};
1769 /* Expand the input spec using RMS, since the CRTL remove() and
1770 * system services won't do this by themselves, so we may miss
1771 * a file "hiding" behind a logical name or search list. */
1772 vmsname = PerlMem_malloc(NAM$C_MAXRSS+1);
1773 if (vmsname == NULL) _ckvmssts(SS$_INSFMEM);
1775 if (do_rmsexpand(name, vmsname, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL) == NULL) {
1776 PerlMem_free(vmsname);
1780 if (decc_posix_compliant_pathnames) {
1781 /* In POSIX mode, we prefer to remove the UNIX name */
1783 remove_name = (char *)name;
1786 rspec = PerlMem_malloc(NAM$C_MAXRSS+1);
1787 if (rspec == NULL) _ckvmssts(SS$_INSFMEM);
1788 if (do_rmsexpand(vmsname, rspec, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL) == NULL) {
1789 PerlMem_free(rspec);
1790 PerlMem_free(vmsname);
1793 PerlMem_free(vmsname);
1794 remove_name = rspec;
1797 #if defined(__CRTL_VER) && __CRTL_VER >= 70000000
1799 if (decc_dir_barename && decc_posix_compliant_pathnames) {
1800 remove_name = PerlMem_malloc(NAM$C_MAXRSS+1);
1801 if (remove_name == NULL) _ckvmssts(SS$_INSFMEM);
1803 do_pathify_dirspec(name, remove_name, 0, NULL);
1804 if (!rmdir(remove_name)) {
1806 PerlMem_free(remove_name);
1807 PerlMem_free(rspec);
1808 return 0; /* Can we just get rid of it? */
1812 if (!rmdir(remove_name)) {
1813 PerlMem_free(rspec);
1814 return 0; /* Can we just get rid of it? */
1820 if (!remove(remove_name)) {
1821 PerlMem_free(rspec);
1822 return 0; /* Can we just get rid of it? */
1825 /* If not, can changing protections help? */
1826 if (vaxc$errno != RMS$_PRV) {
1827 PerlMem_free(rspec);
1831 /* No, so we get our own UIC to use as a rights identifier,
1832 * and the insert an ACE at the head of the ACL which allows us
1833 * to delete the file.
1835 _ckvmssts(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
1836 fildsc.dsc$w_length = strlen(rspec);
1837 fildsc.dsc$a_pointer = rspec;
1839 newace.myace$l_ident = oldace.myace$l_ident;
1840 if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) {
1842 case RMS$_FNF: case RMS$_DNF: case SS$_NOSUCHOBJECT:
1843 set_errno(ENOENT); break;
1845 set_errno(ENOTDIR); break;
1847 set_errno(ENODEV); break;
1848 case RMS$_SYN: case SS$_INVFILFOROP:
1849 set_errno(EINVAL); break;
1851 set_errno(EACCES); break;
1855 set_vaxc_errno(aclsts);
1856 PerlMem_free(rspec);
1859 /* Grab any existing ACEs with this identifier in case we fail */
1860 aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt);
1861 if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY
1862 || fndsts == SS$_NOMOREACE ) {
1863 /* Add the new ACE . . . */
1864 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1))
1867 #if defined(__CRTL_VER) && __CRTL_VER >= 70000000
1869 if (decc_dir_barename && decc_posix_compliant_pathnames) {
1870 remove_name = PerlMem_malloc(NAM$C_MAXRSS+1);
1871 if (remove_name == NULL) _ckvmssts(SS$_INSFMEM);
1873 do_pathify_dirspec(name, remove_name, 0, NULL);
1874 rmsts = rmdir(remove_name);
1875 PerlMem_free(remove_name);
1878 rmsts = rmdir(remove_name);
1882 rmsts = remove(remove_name);
1884 /* We blew it - dir with files in it, no write priv for
1885 * parent directory, etc. Put things back the way they were. */
1886 if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1))
1889 addlst[0].bufadr = &oldace;
1890 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1))
1897 fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0);
1898 /* We just deleted it, so of course it's not there. Some versions of
1899 * VMS seem to return success on the unlock operation anyhow (after all
1900 * the unlock is successful), but others don't.
1902 if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL;
1903 if (aclsts & 1) aclsts = fndsts;
1904 if (!(aclsts & 1)) {
1906 set_vaxc_errno(aclsts);
1907 PerlMem_free(rspec);
1911 PerlMem_free(rspec);
1914 } /* end of kill_file() */
1918 /*{{{int do_rmdir(char *name)*/
1920 Perl_do_rmdir(pTHX_ const char *name)
1922 char dirfile[NAM$C_MAXRSS+1];
1926 if (do_fileify_dirspec(name,dirfile,0,NULL) == NULL) return -1;
1927 if (flex_stat(dirfile,&st) || !S_ISDIR(st.st_mode)) retval = -1;
1928 else retval = mp_do_kill_file(aTHX_ dirfile, 1);
1931 } /* end of do_rmdir */
1935 * Delete any file to which user has control access, regardless of whether
1936 * delete access is explicitly allowed.
1937 * Limitations: User must have write access to parent directory.
1938 * Does not block signals or ASTs; if interrupted in midstream
1939 * may leave file with an altered ACL.
1942 /*{{{int kill_file(char *name)*/
1944 Perl_kill_file(pTHX_ const char *name)
1946 char rspec[NAM$C_MAXRSS+1];
1948 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
1949 unsigned long int cxt = 0, aclsts, fndsts, rmsts = -1;
1950 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1952 unsigned char myace$b_length;
1953 unsigned char myace$b_type;
1954 unsigned short int myace$w_flags;
1955 unsigned long int myace$l_access;
1956 unsigned long int myace$l_ident;
1957 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
1958 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0},
1959 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
1961 findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0},
1962 {sizeof oldace, ACL$C_READACE, &oldace, 0},{0,0,0,0}},
1963 addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}},
1964 dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}},
1965 lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}},
1966 ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}};
1968 /* Expand the input spec using RMS, since the CRTL remove() and
1969 * system services won't do this by themselves, so we may miss
1970 * a file "hiding" behind a logical name or search list. */
1971 tspec = do_rmsexpand(name, rspec, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL);
1972 if (tspec == NULL) return -1;
1973 if (!remove(rspec)) return 0; /* Can we just get rid of it? */
1974 /* If not, can changing protections help? */
1975 if (vaxc$errno != RMS$_PRV) return -1;
1977 /* No, so we get our own UIC to use as a rights identifier,
1978 * and the insert an ACE at the head of the ACL which allows us
1979 * to delete the file.
1981 _ckvmssts(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
1982 fildsc.dsc$w_length = strlen(rspec);
1983 fildsc.dsc$a_pointer = rspec;
1985 newace.myace$l_ident = oldace.myace$l_ident;
1986 if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) {
1988 case RMS$_FNF: case RMS$_DNF: case SS$_NOSUCHOBJECT:
1989 set_errno(ENOENT); break;
1991 set_errno(ENOTDIR); break;
1993 set_errno(ENODEV); break;
1994 case RMS$_SYN: case SS$_INVFILFOROP:
1995 set_errno(EINVAL); break;
1997 set_errno(EACCES); break;
2001 set_vaxc_errno(aclsts);
2004 /* Grab any existing ACEs with this identifier in case we fail */
2005 aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt);
2006 if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY
2007 || fndsts == SS$_NOMOREACE ) {
2008 /* Add the new ACE . . . */
2009 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1))
2011 if ((rmsts = remove(name))) {
2012 /* We blew it - dir with files in it, no write priv for
2013 * parent directory, etc. Put things back the way they were. */
2014 if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1))
2017 addlst[0].bufadr = &oldace;
2018 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1))
2025 fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0);
2026 /* We just deleted it, so of course it's not there. Some versions of
2027 * VMS seem to return success on the unlock operation anyhow (after all
2028 * the unlock is successful), but others don't.
2030 if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL;
2031 if (aclsts & 1) aclsts = fndsts;
2032 if (!(aclsts & 1)) {
2034 set_vaxc_errno(aclsts);
2040 } /* end of kill_file() */
2044 /*{{{int my_mkdir(char *,Mode_t)*/
2046 Perl_my_mkdir(pTHX_ const char *dir, Mode_t mode)
2048 STRLEN dirlen = strlen(dir);
2050 /* zero length string sometimes gives ACCVIO */
2051 if (dirlen == 0) return -1;
2053 /* CRTL mkdir() doesn't tolerate trailing /, since that implies
2054 * null file name/type. However, it's commonplace under Unix,
2055 * so we'll allow it for a gain in portability.
2057 if (dir[dirlen-1] == '/') {
2058 char *newdir = savepvn(dir,dirlen-1);
2059 int ret = mkdir(newdir,mode);
2063 else return mkdir(dir,mode);
2064 } /* end of my_mkdir */
2067 /*{{{int my_chdir(char *)*/
2069 Perl_my_chdir(pTHX_ const char *dir)
2071 STRLEN dirlen = strlen(dir);
2073 /* zero length string sometimes gives ACCVIO */
2074 if (dirlen == 0) return -1;
2077 /* Perl is passing the output of the DCL SHOW DEFAULT with leading spaces.
2078 * This does not work if DECC$EFS_CHARSET is active. Hack it here
2079 * so that existing scripts do not need to be changed.
2082 while ((dirlen > 0) && (*dir1 == ' ')) {
2087 /* some versions of CRTL chdir() doesn't tolerate trailing /, since
2089 * null file name/type. However, it's commonplace under Unix,
2090 * so we'll allow it for a gain in portability.
2092 * - Preview- '/' will be valid soon on VMS
2094 if ((dirlen > 1) && (dir1[dirlen-1] == '/')) {
2095 char *newdir = savepvn(dir1,dirlen-1);
2096 int ret = chdir(newdir);
2100 else return chdir(dir1);
2101 } /* end of my_chdir */
2105 /*{{{FILE *my_tmpfile()*/
2112 if ((fp = tmpfile())) return fp;
2114 cp = PerlMem_malloc(L_tmpnam+24);
2115 if (cp == NULL) _ckvmssts_noperl(SS$_INSFMEM);
2117 if (decc_filename_unix_only == 0)
2118 strcpy(cp,"Sys$Scratch:");
2121 tmpnam(cp+strlen(cp));
2122 strcat(cp,".Perltmp");
2123 fp = fopen(cp,"w+","fop=dlt");
2130 #ifndef HOMEGROWN_POSIX_SIGNALS
2132 * The C RTL's sigaction fails to check for invalid signal numbers so we
2133 * help it out a bit. The docs are correct, but the actual routine doesn't
2134 * do what the docs say it will.
2136 /*{{{int Perl_my_sigaction (pTHX_ int, const struct sigaction*, struct sigaction*);*/
2138 Perl_my_sigaction (pTHX_ int sig, const struct sigaction* act,
2139 struct sigaction* oact)
2141 if (sig == SIGKILL || sig == SIGSTOP || sig == SIGCONT) {
2142 SETERRNO(EINVAL, SS$_INVARG);
2145 return sigaction(sig, act, oact);
2150 #ifdef KILL_BY_SIGPRC
2151 #include <errnodef.h>
2153 /* We implement our own kill() using the undocumented system service
2154 sys$sigprc for one of two reasons:
2156 1.) If the kill() in an older CRTL uses sys$forcex, causing the
2157 target process to do a sys$exit, which usually can't be handled
2158 gracefully...certainly not by Perl and the %SIG{} mechanism.
2160 2.) If the kill() in the CRTL can't be called from a signal
2161 handler without disappearing into the ether, i.e., the signal
2162 it purportedly sends is never trapped. Still true as of VMS 7.3.
2164 sys$sigprc has the same parameters as sys$forcex, but throws an exception
2165 in the target process rather than calling sys$exit.
2167 Note that distinguishing SIGSEGV from SIGBUS requires an extra arg
2168 on the ACCVIO condition, which sys$sigprc (and sys$forcex) don't
2169 provide. On VMS 7.0+ this is taken care of by doing sys$sigprc
2170 with condition codes C$_SIG0+nsig*8, catching the exception on the
2171 target process and resignaling with appropriate arguments.
2173 But we don't have that VMS 7.0+ exception handler, so if you
2174 Perl_my_kill(.., SIGSEGV) it will show up as a SIGBUS. Oh well.
2176 Also note that SIGTERM is listed in the docs as being "unimplemented",
2177 yet always seems to be signaled with a VMS condition code of 4 (and
2178 correctly handled for that code). So we hardwire it in.
2180 Unlike the VMS 7.0+ CRTL kill() function, we actually check the signal
2181 number to see if it's valid. So Perl_my_kill(pid,0) returns -1 rather
2182 than signalling with an unrecognized (and unhandled by CRTL) code.
2185 #define _MY_SIG_MAX 28
2188 Perl_sig_to_vmscondition_int(int sig)
2190 static unsigned int sig_code[_MY_SIG_MAX+1] =
2193 SS$_HANGUP, /* 1 SIGHUP */
2194 SS$_CONTROLC, /* 2 SIGINT */
2195 SS$_CONTROLY, /* 3 SIGQUIT */
2196 SS$_RADRMOD, /* 4 SIGILL */
2197 SS$_BREAK, /* 5 SIGTRAP */
2198 SS$_OPCCUS, /* 6 SIGABRT */
2199 SS$_COMPAT, /* 7 SIGEMT */
2201 SS$_FLTOVF, /* 8 SIGFPE VAX */
2203 SS$_HPARITH, /* 8 SIGFPE AXP */
2205 SS$_ABORT, /* 9 SIGKILL */
2206 SS$_ACCVIO, /* 10 SIGBUS */
2207 SS$_ACCVIO, /* 11 SIGSEGV */
2208 SS$_BADPARAM, /* 12 SIGSYS */
2209 SS$_NOMBX, /* 13 SIGPIPE */
2210 SS$_ASTFLT, /* 14 SIGALRM */
2227 #if __VMS_VER >= 60200000
2228 static int initted = 0;
2231 sig_code[16] = C$_SIGUSR1;
2232 sig_code[17] = C$_SIGUSR2;
2233 #if __CRTL_VER >= 70000000
2234 sig_code[20] = C$_SIGCHLD;
2236 #if __CRTL_VER >= 70300000
2237 sig_code[28] = C$_SIGWINCH;
2242 if (sig < _SIG_MIN) return 0;
2243 if (sig > _MY_SIG_MAX) return 0;
2244 return sig_code[sig];
2248 Perl_sig_to_vmscondition(int sig)
2251 if (vms_debug_on_exception != 0)
2252 lib$signal(SS$_DEBUG);
2254 return Perl_sig_to_vmscondition_int(sig);
2259 Perl_my_kill(int pid, int sig)
2264 int sys$sigprc(unsigned int *pidadr,
2265 struct dsc$descriptor_s *prcname,
2268 /* sig 0 means validate the PID */
2269 /*------------------------------*/
2271 const unsigned long int jpicode = JPI$_PID;
2274 status = lib$getjpi(&jpicode, &pid, NULL, &ret_pid, NULL, NULL);
2275 if ($VMS_STATUS_SUCCESS(status))
2278 case SS$_NOSUCHNODE:
2279 case SS$_UNREACHABLE:
2293 code = Perl_sig_to_vmscondition_int(sig);
2296 SETERRNO(EINVAL, SS$_BADPARAM);
2300 /* Fixme: Per official UNIX specification: If pid = 0, or negative then
2301 * signals are to be sent to multiple processes.
2302 * pid = 0 - all processes in group except ones that the system exempts
2303 * pid = -1 - all processes except ones that the system exempts
2304 * pid = -n - all processes in group (abs(n)) except ...
2305 * For now, just report as not supported.
2309 SETERRNO(ENOTSUP, SS$_UNSUPPORTED);
2313 iss = sys$sigprc((unsigned int *)&pid,0,code);
2314 if (iss&1) return 0;
2318 set_errno(EPERM); break;
2320 case SS$_NOSUCHNODE:
2321 case SS$_UNREACHABLE:
2322 set_errno(ESRCH); break;
2324 set_errno(ENOMEM); break;
2329 set_vaxc_errno(iss);
2335 /* Routine to convert a VMS status code to a UNIX status code.
2336 ** More tricky than it appears because of conflicting conventions with
2339 ** VMS status codes are a bit mask, with the least significant bit set for
2342 ** Special UNIX status of EVMSERR indicates that no translation is currently
2343 ** available, and programs should check the VMS status code.
2345 ** Programs compiled with _POSIX_EXIT have a special encoding that requires
2349 #ifndef C_FACILITY_NO
2350 #define C_FACILITY_NO 0x350000
2353 #define DCL_IVVERB 0x38090
2356 int Perl_vms_status_to_unix(int vms_status, int child_flag)
2364 /* Assume the best or the worst */
2365 if (vms_status & STS$M_SUCCESS)
2368 unix_status = EVMSERR;
2370 msg_status = vms_status & ~STS$M_CONTROL;
2372 facility = vms_status & STS$M_FAC_NO;
2373 fac_sp = vms_status & STS$M_FAC_SP;
2374 msg_no = vms_status & (STS$M_MSG_NO | STS$M_SEVERITY);
2376 if (((facility == 0) || (fac_sp == 0)) && (child_flag == 0)) {
2382 unix_status = EFAULT;
2384 case SS$_DEVOFFLINE:
2385 unix_status = EBUSY;
2388 unix_status = ENOTCONN;
2396 case SS$_INVFILFOROP:
2400 unix_status = EINVAL;
2402 case SS$_UNSUPPORTED:
2403 unix_status = ENOTSUP;
2408 unix_status = EACCES;
2410 case SS$_DEVICEFULL:
2411 unix_status = ENOSPC;
2414 unix_status = ENODEV;
2416 case SS$_NOSUCHFILE:
2417 case SS$_NOSUCHOBJECT:
2418 unix_status = ENOENT;
2420 case SS$_ABORT: /* Fatal case */
2421 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_ERROR): /* Error case */
2422 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_WARNING): /* Warning case */
2423 unix_status = EINTR;
2426 unix_status = E2BIG;
2429 unix_status = ENOMEM;
2432 unix_status = EPERM;
2434 case SS$_NOSUCHNODE:
2435 case SS$_UNREACHABLE:
2436 unix_status = ESRCH;
2439 unix_status = ECHILD;
2442 if ((facility == 0) && (msg_no < 8)) {
2443 /* These are not real VMS status codes so assume that they are
2444 ** already UNIX status codes
2446 unix_status = msg_no;
2452 /* Translate a POSIX exit code to a UNIX exit code */
2453 if ((facility == C_FACILITY_NO) && ((msg_no & 0xA000) == 0xA000)) {
2454 unix_status = (msg_no & 0x07F8) >> 3;
2458 /* Documented traditional behavior for handling VMS child exits */
2459 /*--------------------------------------------------------------*/
2460 if (child_flag != 0) {
2462 /* Success / Informational return 0 */
2463 /*----------------------------------*/
2464 if (msg_no & STS$K_SUCCESS)
2467 /* Warning returns 1 */
2468 /*-------------------*/
2469 if ((msg_no & (STS$K_ERROR | STS$K_SEVERE)) == 0)
2472 /* Everything else pass through the severity bits */
2473 /*------------------------------------------------*/
2474 return (msg_no & STS$M_SEVERITY);
2477 /* Normal VMS status to ERRNO mapping attempt */
2478 /*--------------------------------------------*/
2479 switch(msg_status) {
2480 /* case RMS$_EOF: */ /* End of File */
2481 case RMS$_FNF: /* File Not Found */
2482 case RMS$_DNF: /* Dir Not Found */
2483 unix_status = ENOENT;
2485 case RMS$_RNF: /* Record Not Found */
2486 unix_status = ESRCH;
2489 unix_status = ENOTDIR;
2492 unix_status = ENODEV;
2497 unix_status = EBADF;
2500 unix_status = EEXIST;
2504 case LIB$_INVSTRDES:
2506 case LIB$_NOSUCHSYM:
2507 case LIB$_INVSYMNAM:
2509 unix_status = EINVAL;
2515 unix_status = E2BIG;
2517 case RMS$_PRV: /* No privilege */
2518 case RMS$_ACC: /* ACP file access failed */
2519 case RMS$_WLK: /* Device write locked */
2520 unix_status = EACCES;
2522 /* case RMS$_NMF: */ /* No more files */
2530 /* Try to guess at what VMS error status should go with a UNIX errno
2531 * value. This is hard to do as there could be many possible VMS
2532 * error statuses that caused the errno value to be set.
2535 int Perl_unix_status_to_vms(int unix_status)
2537 int test_unix_status;
2539 /* Trivial cases first */
2540 /*---------------------*/
2541 if (unix_status == EVMSERR)
2544 /* Is vaxc$errno sane? */
2545 /*---------------------*/
2546 test_unix_status = Perl_vms_status_to_unix(vaxc$errno, 0);
2547 if (test_unix_status == unix_status)
2550 /* If way out of range, must be VMS code already */
2551 /*-----------------------------------------------*/
2552 if (unix_status > EVMSERR)
2555 /* If out of range, punt */
2556 /*-----------------------*/
2557 if (unix_status > __ERRNO_MAX)
2561 /* Ok, now we have to do it the hard way. */
2562 /*----------------------------------------*/
2563 switch(unix_status) {
2564 case 0: return SS$_NORMAL;
2565 case EPERM: return SS$_NOPRIV;
2566 case ENOENT: return SS$_NOSUCHOBJECT;
2567 case ESRCH: return SS$_UNREACHABLE;
2568 case EINTR: return SS$_ABORT;
2571 case E2BIG: return SS$_BUFFEROVF;
2573 case EBADF: return RMS$_IFI;
2574 case ECHILD: return SS$_NONEXPR;
2576 case ENOMEM: return SS$_INSFMEM;
2577 case EACCES: return SS$_FILACCERR;
2578 case EFAULT: return SS$_ACCVIO;
2580 case EBUSY: return SS$_DEVOFFLINE;
2581 case EEXIST: return RMS$_FEX;
2583 case ENODEV: return SS$_NOSUCHDEV;
2584 case ENOTDIR: return RMS$_DIR;
2586 case EINVAL: return SS$_INVARG;
2592 case ENOSPC: return SS$_DEVICEFULL;
2593 case ESPIPE: return LIB$_INVARG;
2598 case ERANGE: return LIB$_INVARG;
2599 /* case EWOULDBLOCK */
2600 /* case EINPROGRESS */
2603 /* case EDESTADDRREQ */
2605 /* case EPROTOTYPE */
2606 /* case ENOPROTOOPT */
2607 /* case EPROTONOSUPPORT */
2608 /* case ESOCKTNOSUPPORT */
2609 /* case EOPNOTSUPP */
2610 /* case EPFNOSUPPORT */
2611 /* case EAFNOSUPPORT */
2612 /* case EADDRINUSE */
2613 /* case EADDRNOTAVAIL */
2615 /* case ENETUNREACH */
2616 /* case ENETRESET */
2617 /* case ECONNABORTED */
2618 /* case ECONNRESET */
2621 case ENOTCONN: return SS$_CLEARED;
2622 /* case ESHUTDOWN */
2623 /* case ETOOMANYREFS */
2624 /* case ETIMEDOUT */
2625 /* case ECONNREFUSED */
2627 /* case ENAMETOOLONG */
2628 /* case EHOSTDOWN */
2629 /* case EHOSTUNREACH */
2630 /* case ENOTEMPTY */
2642 /* case ECANCELED */
2646 return SS$_UNSUPPORTED;
2652 /* case EABANDONED */
2654 return SS$_ABORT; /* punt */
2657 return SS$_ABORT; /* Should not get here */
2661 /* default piping mailbox size */
2662 #define PERL_BUFSIZ 512
2666 create_mbx(pTHX_ unsigned short int *chan, struct dsc$descriptor_s *namdsc)
2668 unsigned long int mbxbufsiz;
2669 static unsigned long int syssize = 0;
2670 unsigned long int dviitm = DVI$_DEVNAM;
2671 char csize[LNM$C_NAMLENGTH+1];
2675 unsigned long syiitm = SYI$_MAXBUF;
2677 * Get the SYSGEN parameter MAXBUF
2679 * If the logical 'PERL_MBX_SIZE' is defined
2680 * use the value of the logical instead of PERL_BUFSIZ, but
2681 * keep the size between 128 and MAXBUF.
2684 _ckvmssts(lib$getsyi(&syiitm, &syssize, 0, 0, 0, 0));
2687 if (vmstrnenv("PERL_MBX_SIZE", csize, 0, fildev, 0)) {
2688 mbxbufsiz = atoi(csize);
2690 mbxbufsiz = PERL_BUFSIZ;
2692 if (mbxbufsiz < 128) mbxbufsiz = 128;
2693 if (mbxbufsiz > syssize) mbxbufsiz = syssize;
2695 _ckvmssts(sts = sys$crembx(0,chan,mbxbufsiz,mbxbufsiz,0,0,0));
2697 _ckvmssts(sts = lib$getdvi(&dviitm, chan, NULL, NULL, namdsc, &namdsc->dsc$w_length));
2698 namdsc->dsc$a_pointer[namdsc->dsc$w_length] = '\0';
2700 } /* end of create_mbx() */
2703 /*{{{ my_popen and my_pclose*/
2705 typedef struct _iosb IOSB;
2706 typedef struct _iosb* pIOSB;
2707 typedef struct _pipe Pipe;
2708 typedef struct _pipe* pPipe;
2709 typedef struct pipe_details Info;
2710 typedef struct pipe_details* pInfo;
2711 typedef struct _srqp RQE;
2712 typedef struct _srqp* pRQE;
2713 typedef struct _tochildbuf CBuf;
2714 typedef struct _tochildbuf* pCBuf;
2717 unsigned short status;
2718 unsigned short count;
2719 unsigned long dvispec;
2722 #pragma member_alignment save
2723 #pragma nomember_alignment quadword
2724 struct _srqp { /* VMS self-relative queue entry */
2725 unsigned long qptr[2];
2727 #pragma member_alignment restore
2728 static RQE RQE_ZERO = {0,0};
2730 struct _tochildbuf {
2733 unsigned short size;
2741 unsigned short chan_in;
2742 unsigned short chan_out;
2744 unsigned int bufsize;
2756 #if defined(PERL_IMPLICIT_CONTEXT)
2757 void *thx; /* Either a thread or an interpreter */
2758 /* pointer, depending on how we're built */
2766 PerlIO *fp; /* file pointer to pipe mailbox */
2767 int useFILE; /* using stdio, not perlio */
2768 int pid; /* PID of subprocess */
2769 int mode; /* == 'r' if pipe open for reading */
2770 int done; /* subprocess has completed */
2771 int waiting; /* waiting for completion/closure */
2772 int closing; /* my_pclose is closing this pipe */
2773 unsigned long completion; /* termination status of subprocess */
2774 pPipe in; /* pipe in to sub */
2775 pPipe out; /* pipe out of sub */
2776 pPipe err; /* pipe of sub's sys$error */
2777 int in_done; /* true when in pipe finished */
2780 unsigned short xchan; /* channel to debug xterm */
2781 unsigned short xchan_valid; /* channel is assigned */
2784 struct exit_control_block
2786 struct exit_control_block *flink;
2787 unsigned long int (*exit_routine)();
2788 unsigned long int arg_count;
2789 unsigned long int *status_address;
2790 unsigned long int exit_status;
2793 typedef struct _closed_pipes Xpipe;
2794 typedef struct _closed_pipes* pXpipe;
2796 struct _closed_pipes {
2797 int pid; /* PID of subprocess */
2798 unsigned long completion; /* termination status of subprocess */
2800 #define NKEEPCLOSED 50
2801 static Xpipe closed_list[NKEEPCLOSED];
2802 static int closed_index = 0;
2803 static int closed_num = 0;
2805 #define RETRY_DELAY "0 ::0.20"
2806 #define MAX_RETRY 50
2808 static int pipe_ef = 0; /* first call to safe_popen inits these*/
2809 static unsigned long mypid;
2810 static unsigned long delaytime[2];
2812 static pInfo open_pipes = NULL;
2813 static $DESCRIPTOR(nl_desc, "NL:");
2815 #define PIPE_COMPLETION_WAIT 30 /* seconds, for EOF/FORCEX wait */
2819 static unsigned long int
2820 pipe_exit_routine(pTHX)
2823 unsigned long int retsts = SS$_NORMAL, abort = SS$_TIMEOUT;
2824 int sts, did_stuff, need_eof, j;
2827 * Flush any pending i/o, but since we are in process run-down, be
2828 * careful about referencing PerlIO structures that may already have
2829 * been deallocated. We may not even have an interpreter anymore.
2835 #if defined(USE_ITHREADS)
2838 && PL_perlio_fd_refcnt)
2839 PerlIO_flush(info->fp);
2841 fflush((FILE *)info->fp);
2847 next we try sending an EOF...ignore if doesn't work, make sure we
2855 _ckvmssts_noperl(sys$setast(0));
2856 if (info->in && !info->in->shut_on_empty) {
2857 _ckvmssts_noperl(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
2862 _ckvmssts_noperl(sys$setast(1));
2866 /* wait for EOF to have effect, up to ~ 30 sec [default] */
2868 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
2873 _ckvmssts_noperl(sys$setast(0));
2874 if (info->waiting && info->done)
2876 nwait += info->waiting;
2877 _ckvmssts_noperl(sys$setast(1));
2887 _ckvmssts_noperl(sys$setast(0));
2888 if (!info->done) { /* Tap them gently on the shoulder . . .*/
2889 sts = sys$forcex(&info->pid,0,&abort);
2890 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
2893 _ckvmssts_noperl(sys$setast(1));
2897 /* again, wait for effect */
2899 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
2904 _ckvmssts_noperl(sys$setast(0));
2905 if (info->waiting && info->done)
2907 nwait += info->waiting;
2908 _ckvmssts_noperl(sys$setast(1));
2917 _ckvmssts_noperl(sys$setast(0));
2918 if (!info->done) { /* We tried to be nice . . . */
2919 sts = sys$delprc(&info->pid,0);
2920 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
2921 info->done = 1; /* sys$delprc is as done as we're going to get. */
2923 _ckvmssts_noperl(sys$setast(1));
2928 if ((sts = my_pclose(open_pipes->fp)) == -1) retsts = vaxc$errno;
2929 else if (!(sts & 1)) retsts = sts;
2934 static struct exit_control_block pipe_exitblock =
2935 {(struct exit_control_block *) 0,
2936 pipe_exit_routine, 0, &pipe_exitblock.exit_status, 0};
2938 static void pipe_mbxtofd_ast(pPipe p);
2939 static void pipe_tochild1_ast(pPipe p);
2940 static void pipe_tochild2_ast(pPipe p);
2943 popen_completion_ast(pInfo info)
2945 pInfo i = open_pipes;
2950 info->completion &= 0x0FFFFFFF; /* strip off "control" field */
2951 closed_list[closed_index].pid = info->pid;
2952 closed_list[closed_index].completion = info->completion;
2954 if (closed_index == NKEEPCLOSED)
2959 if (i == info) break;
2962 if (!i) return; /* unlinked, probably freed too */
2967 Writing to subprocess ...
2968 if my_pclose'd: EOF already sent, should shutdown chan_in part of pipe
2970 chan_out may be waiting for "done" flag, or hung waiting
2971 for i/o completion to child...cancel the i/o. This will
2972 put it into "snarf mode" (done but no EOF yet) that discards
2975 Output from subprocess (stdout, stderr) needs to be flushed and
2976 shut down. We try sending an EOF, but if the mbx is full the pipe
2977 routine should still catch the "shut_on_empty" flag, telling it to
2978 use immediate-style reads so that "mbx empty" -> EOF.
2982 if (info->in && !info->in_done) { /* only for mode=w */
2983 if (info->in->shut_on_empty && info->in->need_wake) {
2984 info->in->need_wake = FALSE;
2985 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,info->in,0));
2987 _ckvmssts_noperl(sys$cancel(info->in->chan_out));
2991 if (info->out && !info->out_done) { /* were we also piping output? */
2992 info->out->shut_on_empty = TRUE;
2993 iss = sys$qio(0,info->out->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2994 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
2995 _ckvmssts_noperl(iss);
2998 if (info->err && !info->err_done) { /* we were piping stderr */
2999 info->err->shut_on_empty = TRUE;
3000 iss = sys$qio(0,info->err->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
3001 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
3002 _ckvmssts_noperl(iss);
3004 _ckvmssts_noperl(sys$setef(pipe_ef));
3008 static unsigned long int setup_cmddsc(pTHX_ const char *cmd, int check_img, int *suggest_quote, struct dsc$descriptor_s **pvmscmd);
3009 static void vms_execfree(struct dsc$descriptor_s *vmscmd);
3012 we actually differ from vmstrnenv since we use this to
3013 get the RMS IFI to check if SYS$OUTPUT and SYS$ERROR *really*
3014 are pointing to the same thing
3017 static unsigned short
3018 popen_translate(pTHX_ char *logical, char *result)
3021 $DESCRIPTOR(d_table,"LNM$PROCESS_TABLE");
3022 $DESCRIPTOR(d_log,"");
3024 unsigned short length;
3025 unsigned short code;
3027 unsigned short *retlenaddr;
3029 unsigned short l, ifi;
3031 d_log.dsc$a_pointer = logical;
3032 d_log.dsc$w_length = strlen(logical);
3034 itmlst[0].code = LNM$_STRING;
3035 itmlst[0].length = 255;
3036 itmlst[0].buffer_addr = result;
3037 itmlst[0].retlenaddr = &l;
3040 itmlst[1].length = 0;
3041 itmlst[1].buffer_addr = 0;
3042 itmlst[1].retlenaddr = 0;
3044 iss = sys$trnlnm(0, &d_table, &d_log, 0, itmlst);
3045 if (iss == SS$_NOLOGNAM) {
3049 if (!(iss&1)) lib$signal(iss);
3052 logicals for PPFs have a 4 byte prefix ESC+NUL+(RMS IFI)
3053 strip it off and return the ifi, if any
3056 if (result[0] == 0x1b && result[1] == 0x00) {
3057 memmove(&ifi,result+2,2);
3058 strcpy(result,result+4);
3060 return ifi; /* this is the RMS internal file id */
3063 static void pipe_infromchild_ast(pPipe p);
3066 I'm using LIB$(GET|FREE)_VM here so that we can allocate and deallocate
3067 inside an AST routine without worrying about reentrancy and which Perl
3068 memory allocator is being used.
3070 We read data and queue up the buffers, then spit them out one at a
3071 time to the output mailbox when the output mailbox is ready for one.
3074 #define INITIAL_TOCHILDQUEUE 2
3077 pipe_tochild_setup(pTHX_ char *rmbx, char *wmbx)
3081 char mbx1[64], mbx2[64];
3082 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3083 DSC$K_CLASS_S, mbx1},
3084 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
3085 DSC$K_CLASS_S, mbx2};
3086 unsigned int dviitm = DVI$_DEVBUFSIZ;
3090 _ckvmssts(lib$get_vm(&n, &p));
3092 create_mbx(aTHX_ &p->chan_in , &d_mbx1);
3093 create_mbx(aTHX_ &p->chan_out, &d_mbx2);
3094 _ckvmssts(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3097 p->shut_on_empty = FALSE;
3098 p->need_wake = FALSE;
3101 p->iosb.status = SS$_NORMAL;
3102 p->iosb2.status = SS$_NORMAL;
3108 #ifdef PERL_IMPLICIT_CONTEXT
3112 n = sizeof(CBuf) + p->bufsize;
3114 for (j = 0; j < INITIAL_TOCHILDQUEUE; j++) {
3115 _ckvmssts(lib$get_vm(&n, &b));
3116 b->buf = (char *) b + sizeof(CBuf);
3117 _ckvmssts(lib$insqhi(b, &p->free));
3120 pipe_tochild2_ast(p);
3121 pipe_tochild1_ast(p);
3127 /* reads the MBX Perl is writing, and queues */
3130 pipe_tochild1_ast(pPipe p)
3133 int iss = p->iosb.status;
3134 int eof = (iss == SS$_ENDOFFILE);
3136 #ifdef PERL_IMPLICIT_CONTEXT
3142 p->shut_on_empty = TRUE;
3144 _ckvmssts(sys$dassgn(p->chan_in));
3150 b->size = p->iosb.count;
3151 _ckvmssts(sts = lib$insqhi(b, &p->wait));
3153 p->need_wake = FALSE;
3154 _ckvmssts(sys$dclast(pipe_tochild2_ast,p,0));
3157 p->retry = 1; /* initial call */
3160 if (eof) { /* flush the free queue, return when done */
3161 int n = sizeof(CBuf) + p->bufsize;
3163 iss = lib$remqti(&p->free, &b);
3164 if (iss == LIB$_QUEWASEMP) return;
3166 _ckvmssts(lib$free_vm(&n, &b));
3170 iss = lib$remqti(&p->free, &b);
3171 if (iss == LIB$_QUEWASEMP) {
3172 int n = sizeof(CBuf) + p->bufsize;
3173 _ckvmssts(lib$get_vm(&n, &b));
3174 b->buf = (char *) b + sizeof(CBuf);
3180 iss = sys$qio(0,p->chan_in,
3181 IO$_READVBLK|(p->shut_on_empty ? IO$M_NOWAIT : 0),
3183 pipe_tochild1_ast, p, b->buf, p->bufsize, 0, 0, 0, 0);
3184 if (iss == SS$_ENDOFFILE && p->shut_on_empty) iss = SS$_NORMAL;
3189 /* writes queued buffers to output, waits for each to complete before
3193 pipe_tochild2_ast(pPipe p)
3196 int iss = p->iosb2.status;
3197 int n = sizeof(CBuf) + p->bufsize;
3198 int done = (p->info && p->info->done) ||
3199 iss == SS$_CANCEL || iss == SS$_ABORT;
3200 #if defined(PERL_IMPLICIT_CONTEXT)
3205 if (p->type) { /* type=1 has old buffer, dispose */
3206 if (p->shut_on_empty) {
3207 _ckvmssts(lib$free_vm(&n, &b));
3209 _ckvmssts(lib$insqhi(b, &p->free));
3214 iss = lib$remqti(&p->wait, &b);
3215 if (iss == LIB$_QUEWASEMP) {
3216 if (p->shut_on_empty) {
3218 _ckvmssts(sys$dassgn(p->chan_out));
3219 *p->pipe_done = TRUE;
3220 _ckvmssts(sys$setef(pipe_ef));
3222 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEOF,
3223 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3227 p->need_wake = TRUE;
3237 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEOF,
3238 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3240 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEVBLK,
3241 &p->iosb2, pipe_tochild2_ast, p, b->buf, b->size, 0, 0, 0, 0));
3250 pipe_infromchild_setup(pTHX_ char *rmbx, char *wmbx)
3253 char mbx1[64], mbx2[64];
3254 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3255 DSC$K_CLASS_S, mbx1},
3256 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
3257 DSC$K_CLASS_S, mbx2};
3258 unsigned int dviitm = DVI$_DEVBUFSIZ;
3260 int n = sizeof(Pipe);
3261 _ckvmssts(lib$get_vm(&n, &p));
3262 create_mbx(aTHX_ &p->chan_in , &d_mbx1);
3263 create_mbx(aTHX_ &p->chan_out, &d_mbx2);
3265 _ckvmssts(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3266 n = p->bufsize * sizeof(char);
3267 _ckvmssts(lib$get_vm(&n, &p->buf));
3268 p->shut_on_empty = FALSE;
3271 p->iosb.status = SS$_NORMAL;
3272 #if defined(PERL_IMPLICIT_CONTEXT)
3275 pipe_infromchild_ast(p);
3283 pipe_infromchild_ast(pPipe p)
3285 int iss = p->iosb.status;
3286 int eof = (iss == SS$_ENDOFFILE);
3287 int myeof = (eof && (p->iosb.dvispec == mypid || p->iosb.dvispec == 0));
3288 int kideof = (eof && (p->iosb.dvispec == p->info->pid));
3289 #if defined(PERL_IMPLICIT_CONTEXT)
3293 if (p->info && p->info->closing && p->chan_out) { /* output shutdown */
3294 _ckvmssts(sys$dassgn(p->chan_out));
3299 input shutdown if EOF from self (done or shut_on_empty)
3300 output shutdown if closing flag set (my_pclose)
3301 send data/eof from child or eof from self
3302 otherwise, re-read (snarf of data from child)
3307 if (myeof && p->chan_in) { /* input shutdown */
3308 _ckvmssts(sys$dassgn(p->chan_in));
3313 if (myeof || kideof) { /* pass EOF to parent */
3314 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEOF, &p->iosb,
3315 pipe_infromchild_ast, p,
3318 } else if (eof) { /* eat EOF --- fall through to read*/
3320 } else { /* transmit data */
3321 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEVBLK,&p->iosb,
3322 pipe_infromchild_ast,p,
3323 p->buf, p->iosb.count, 0, 0, 0, 0));
3329 /* everything shut? flag as done */
3331 if (!p->chan_in && !p->chan_out) {
3332 *p->pipe_done = TRUE;
3333 _ckvmssts(sys$setef(pipe_ef));
3337 /* write completed (or read, if snarfing from child)
3338 if still have input active,
3339 queue read...immediate mode if shut_on_empty so we get EOF if empty
3341 check if Perl reading, generate EOFs as needed
3347 iss = sys$qio(0,p->chan_in,IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0),&p->iosb,
3348 pipe_infromchild_ast,p,
3349 p->buf, p->bufsize, 0, 0, 0, 0);
3350 if (p->shut_on_empty && iss == SS$_ENDOFFILE) iss = SS$_NORMAL;
3352 } else { /* send EOFs for extra reads */
3353 p->iosb.status = SS$_ENDOFFILE;
3354 p->iosb.dvispec = 0;
3355 _ckvmssts(sys$qio(0,p->chan_out,IO$_SETMODE|IO$M_READATTN,
3357 pipe_infromchild_ast, p, 0, 0, 0, 0));
3363 pipe_mbxtofd_setup(pTHX_ int fd, char *out)
3367 unsigned long dviitm = DVI$_DEVBUFSIZ;
3369 struct dsc$descriptor_s d_mbx = {sizeof mbx, DSC$K_DTYPE_T,
3370 DSC$K_CLASS_S, mbx};
3371 int n = sizeof(Pipe);
3373 /* things like terminals and mbx's don't need this filter */
3374 if (fd && fstat(fd,&s) == 0) {
3375 unsigned long dviitm = DVI$_DEVCHAR, devchar;
3377 unsigned short dev_len;
3378 struct dsc$descriptor_s d_dev;
3380 struct item_list_3 items[3];
3382 unsigned short dvi_iosb[4];
3384 cptr = getname(fd, out, 1);
3385 if (cptr == NULL) _ckvmssts(SS$_NOSUCHDEV);
3386 d_dev.dsc$a_pointer = out;
3387 d_dev.dsc$w_length = strlen(out);
3388 d_dev.dsc$b_dtype = DSC$K_DTYPE_T;
3389 d_dev.dsc$b_class = DSC$K_CLASS_S;
3392 items[0].code = DVI$_DEVCHAR;
3393 items[0].bufadr = &devchar;
3394 items[0].retadr = NULL;
3396 items[1].code = DVI$_FULLDEVNAM;
3397 items[1].bufadr = device;
3398 items[1].retadr = &dev_len;
3402 status = sys$getdviw
3403 (NO_EFN, 0, &d_dev, items, dvi_iosb, NULL, NULL, NULL);
3405 if ($VMS_STATUS_SUCCESS(dvi_iosb[0])) {
3406 device[dev_len] = 0;
3408 if (!(devchar & DEV$M_DIR)) {
3409 strcpy(out, device);
3415 _ckvmssts(lib$get_vm(&n, &p));
3416 p->fd_out = dup(fd);
3417 create_mbx(aTHX_ &p->chan_in, &d_mbx);
3418 _ckvmssts(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3419 n = (p->bufsize+1) * sizeof(char);
3420 _ckvmssts(lib$get_vm(&n, &p->buf));
3421 p->shut_on_empty = FALSE;
3426 _ckvmssts(sys$qio(0, p->chan_in, IO$_READVBLK, &p->iosb,
3427 pipe_mbxtofd_ast, p,
3428 p->buf, p->bufsize, 0, 0, 0, 0));
3434 pipe_mbxtofd_ast(pPipe p)
3436 int iss = p->iosb.status;
3437 int done = p->info->done;
3439 int eof = (iss == SS$_ENDOFFILE);
3440 int myeof = eof && ((p->iosb.dvispec == mypid)||(p->iosb.dvispec == 0));
3441 int err = !(iss&1) && !eof;
3442 #if defined(PERL_IMPLICIT_CONTEXT)
3446 if (done && myeof) { /* end piping */
3448 sys$dassgn(p->chan_in);
3449 *p->pipe_done = TRUE;
3450 _ckvmssts(sys$setef(pipe_ef));
3454 if (!err && !eof) { /* good data to send to file */
3455 p->buf[p->iosb.count] = '\n';
3456 iss2 = write(p->fd_out, p->buf, p->iosb.count+1);
3459 if (p->retry < MAX_RETRY) {
3460 _ckvmssts(sys$setimr(0,delaytime,pipe_mbxtofd_ast,p));
3470 iss = sys$qio(0, p->chan_in, IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0), &p->iosb,
3471 pipe_mbxtofd_ast, p,
3472 p->buf, p->bufsize, 0, 0, 0, 0);
3473 if (p->shut_on_empty && (iss == SS$_ENDOFFILE)) iss = SS$_NORMAL;
3478 typedef struct _pipeloc PLOC;
3479 typedef struct _pipeloc* pPLOC;
3483 char dir[NAM$C_MAXRSS+1];
3485 static pPLOC head_PLOC = 0;
3488 free_pipelocs(pTHX_ void *head)
3491 pPLOC *pHead = (pPLOC *)head;
3503 store_pipelocs(pTHX)
3512 char temp[NAM$C_MAXRSS+1];
3516 free_pipelocs(aTHX_ &head_PLOC);
3518 /* the . directory from @INC comes last */
3520 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3521 if (p == NULL) _ckvmssts(SS$_INSFMEM);
3522 p->next = head_PLOC;
3524 strcpy(p->dir,"./");
3526 /* get the directory from $^X */
3528 unixdir = PerlMem_malloc(VMS_MAXRSS);
3529 if (unixdir == NULL) _ckvmssts(SS$_INSFMEM);
3531 #ifdef PERL_IMPLICIT_CONTEXT
3532 if (aTHX && PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
3534 if (PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
3536 strcpy(temp, PL_origargv[0]);
3537 x = strrchr(temp,']');
3539 x = strrchr(temp,'>');
3541 /* It could be a UNIX path */
3542 x = strrchr(temp,'/');
3548 /* Got a bare name, so use default directory */
3553 if ((tounixpath_utf8(temp, unixdir, NULL)) != Nullch) {
3554 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3555 if (p == NULL) _ckvmssts(SS$_INSFMEM);
3556 p->next = head_PLOC;
3558 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3559 p->dir[NAM$C_MAXRSS] = '\0';
3563 /* reverse order of @INC entries, skip "." since entered above */
3565 #ifdef PERL_IMPLICIT_CONTEXT
3568 if (PL_incgv) av = GvAVn(PL_incgv);
3570 for (i = 0; av && i <= AvFILL(av); i++) {
3571 dirsv = *av_fetch(av,i,TRUE);
3573 if (SvROK(dirsv)) continue;
3574 dir = SvPVx(dirsv,n_a);
3575 if (strcmp(dir,".") == 0) continue;
3576 if ((tounixpath_utf8(dir, unixdir, NULL)) == Nullch)
3579 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3580 p->next = head_PLOC;
3582 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3583 p->dir[NAM$C_MAXRSS] = '\0';
3586 /* most likely spot (ARCHLIB) put first in the list */
3589 if ((tounixpath_utf8(ARCHLIB_EXP, unixdir, NULL)) != Nullch) {
3590 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3591 if (p == NULL) _ckvmssts(SS$_INSFMEM);
3592 p->next = head_PLOC;
3594 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3595 p->dir[NAM$C_MAXRSS] = '\0';
3598 PerlMem_free(unixdir);
3602 Perl_cando_by_name_int
3603 (pTHX_ I32 bit, bool effective, const char *fname, int opts);
3604 #if !defined(PERL_IMPLICIT_CONTEXT)
3605 #define cando_by_name_int Perl_cando_by_name_int
3607 #define cando_by_name_int(a,b,c,d) Perl_cando_by_name_int(aTHX_ a,b,c,d)
3613 static int vmspipe_file_status = 0;
3614 static char vmspipe_file[NAM$C_MAXRSS+1];
3616 /* already found? Check and use ... need read+execute permission */
3618 if (vmspipe_file_status == 1) {
3619 if (cando_by_name_int(S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)
3620 && cando_by_name_int
3621 (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) {
3622 return vmspipe_file;
3624 vmspipe_file_status = 0;
3627 /* scan through stored @INC, $^X */
3629 if (vmspipe_file_status == 0) {
3630 char file[NAM$C_MAXRSS+1];
3631 pPLOC p = head_PLOC;
3636 strcpy(file, p->dir);
3637 dirlen = strlen(file);
3638 strncat(file, "vmspipe.com",NAM$C_MAXRSS - dirlen);
3639 file[NAM$C_MAXRSS] = '\0';
3642 exp_res = do_rmsexpand
3643 (file, vmspipe_file, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL);
3644 if (!exp_res) continue;
3646 if (cando_by_name_int
3647 (S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)
3648 && cando_by_name_int
3649 (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) {
3650 vmspipe_file_status = 1;
3651 return vmspipe_file;
3654 vmspipe_file_status = -1; /* failed, use tempfiles */
3661 vmspipe_tempfile(pTHX)
3663 char file[NAM$C_MAXRSS+1];
3665 static int index = 0;
3669 /* create a tempfile */
3671 /* we can't go from W, shr=get to R, shr=get without
3672 an intermediate vulnerable state, so don't bother trying...
3674 and lib$spawn doesn't shr=put, so have to close the write
3676 So... match up the creation date/time and the FID to
3677 make sure we're dealing with the same file
3682 if (!decc_filename_unix_only) {
3683 sprintf(file,"sys$scratch:perlpipe_%08.8x_%d.com",mypid,index);
3684 fp = fopen(file,"w");
3686 sprintf(file,"sys$login:perlpipe_%08.8x_%d.com",mypid,index);
3687 fp = fopen(file,"w");
3689 sprintf(file,"sys$disk:[]perlpipe_%08.8x_%d.com",mypid,index);
3690 fp = fopen(file,"w");
3695 sprintf(file,"/tmp/perlpipe_%08.8x_%d.com",mypid,index);
3696 fp = fopen(file,"w");
3698 sprintf(file,"/sys$login/perlpipe_%08.8x_%d.com",mypid,index);
3699 fp = fopen(file,"w");
3701 sprintf(file,"./perlpipe_%08.8x_%d.com",mypid,index);
3702 fp = fopen(file,"w");
3706 if (!fp) return 0; /* we're hosed */
3708 fprintf(fp,"$! 'f$verify(0)'\n");
3709 fprintf(fp,"$! --- protect against nonstandard definitions ---\n");
3710 fprintf(fp,"$ perl_cfile = f$environment(\"procedure\")\n");
3711 fprintf(fp,"$ perl_define = \"define/nolog\"\n");
3712 fprintf(fp,"$ perl_on = \"set noon\"\n");
3713 fprintf(fp,"$ perl_exit = \"exit\"\n");
3714 fprintf(fp,"$ perl_del = \"delete\"\n");
3715 fprintf(fp,"$ pif = \"if\"\n");
3716 fprintf(fp,"$! --- define i/o redirection (sys$output set by lib$spawn)\n");
3717 fprintf(fp,"$ pif perl_popen_in .nes. \"\" then perl_define/user/name_attributes=confine sys$input 'perl_popen_in'\n");
3718 fprintf(fp,"$ pif perl_popen_err .nes. \"\" then perl_define/user/name_attributes=confine sys$error 'perl_popen_err'\n");
3719 fprintf(fp,"$ pif perl_popen_out .nes. \"\" then perl_define sys$output 'perl_popen_out'\n");
3720 fprintf(fp,"$! --- build command line to get max possible length\n");
3721 fprintf(fp,"$c=perl_popen_cmd0\n");
3722 fprintf(fp,"$c=c+perl_popen_cmd1\n");
3723 fprintf(fp,"$c=c+perl_popen_cmd2\n");
3724 fprintf(fp,"$x=perl_popen_cmd3\n");
3725 fprintf(fp,"$c=c+x\n");
3726 fprintf(fp,"$ perl_on\n");
3727 fprintf(fp,"$ 'c'\n");
3728 fprintf(fp,"$ perl_status = $STATUS\n");
3729 fprintf(fp,"$ perl_del 'perl_cfile'\n");
3730 fprintf(fp,"$ perl_exit 'perl_status'\n");
3733 fgetname(fp, file, 1);
3734 fstat(fileno(fp), (struct stat *)&s0);
3737 if (decc_filename_unix_only)
3738 do_tounixspec(file, file, 0, NULL);
3739 fp = fopen(file,"r","shr=get");
3741 fstat(fileno(fp), (struct stat *)&s1);
3743 cmp_result = VMS_INO_T_COMPARE(s0.crtl_stat.st_ino, s1.crtl_stat.st_ino);
3744 if ((cmp_result != 0) && (s0.st_ctime != s1.st_ctime)) {
3753 #ifdef USE_VMS_DECTERM
3755 static int vms_is_syscommand_xterm(void)
3757 const static struct dsc$descriptor_s syscommand_dsc =
3758 { 11, DSC$K_DTYPE_T, DSC$K_CLASS_S, "SYS$COMMAND" };
3760 const static struct dsc$descriptor_s decwdisplay_dsc =
3761 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "DECW$DISPLAY" };
3763 struct item_list_3 items[2];
3764 unsigned short dvi_iosb[4];
3765 unsigned long devchar;
3766 unsigned long devclass;
3769 /* Very simple check to guess if sys$command is a decterm? */
3770 /* First see if the DECW$DISPLAY: device exists */
3772 items[0].code = DVI$_DEVCHAR;
3773 items[0].bufadr = &devchar;
3774 items[0].retadr = NULL;
3778 status = sys$getdviw
3779 (NO_EFN, 0, &decwdisplay_dsc, items, dvi_iosb, NULL, NULL, NULL);
3781 if ($VMS_STATUS_SUCCESS(status)) {
3782 status = dvi_iosb[0];
3785 if (!$VMS_STATUS_SUCCESS(status)) {
3786 SETERRNO(EVMSERR, status);
3790 /* If it does, then for now assume that we are on a workstation */
3791 /* Now verify that SYS$COMMAND is a terminal */
3792 /* for creating the debugger DECTerm */
3795 items[0].code = DVI$_DEVCLASS;
3796 items[0].bufadr = &devclass;
3797 items[0].retadr = NULL;
3801 status = sys$getdviw
3802 (NO_EFN, 0, &syscommand_dsc, items, dvi_iosb, NULL, NULL, NULL);
3804 if ($VMS_STATUS_SUCCESS(status)) {
3805 status = dvi_iosb[0];
3808 if (!$VMS_STATUS_SUCCESS(status)) {
3809 SETERRNO(EVMSERR, status);
3813 if (devclass == DC$_TERM) {
3820 /* If we are on a DECTerm, we can pretend to fork xterms when requested */
3821 static PerlIO * create_forked_xterm(pTHX_ const char *cmd, const char *mode)
3826 char device_name[65];
3827 unsigned short device_name_len;
3828 struct dsc$descriptor_s customization_dsc;
3829 struct dsc$descriptor_s device_name_dsc;
3832 char customization[200];
3836 unsigned short p_chan;
3838 unsigned short iosb[4];
3839 struct item_list_3 items[2];
3840 const char * cust_str =
3841 "DECW$TERMINAL.iconName:\tPerl Dbg\nDECW$TERMINAL.title:\t%40s\n";
3842 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3843 DSC$K_CLASS_S, mbx1};
3845 ret_char = strstr(cmd," xterm ");
3846 if (ret_char == NULL)
3848 cptr = ret_char + 7;
3849 ret_char = strstr(cmd,"tty");
3850 if (ret_char == NULL)
3852 ret_char = strstr(cmd,"sleep");
3853 if (ret_char == NULL)
3856 /* Are we on a workstation? */
3857 /* to do: capture the rows / columns and pass their properties */
3858 ret_stat = vms_is_syscommand_xterm();
3862 /* Make the title: */
3863 ret_char = strstr(cptr,"-title");
3864 if (ret_char != NULL) {
3865 while ((*cptr != 0) && (*cptr != '\"')) {
3871 while ((*cptr != 0) && (*cptr != '\"')) {
3884 strcpy(title,"Perl Debug DECTerm");
3886 sprintf(customization, cust_str, title);
3888 customization_dsc.dsc$a_pointer = customization;
3889 customization_dsc.dsc$w_length = strlen(customization);
3890 customization_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
3891 customization_dsc.dsc$b_class = DSC$K_CLASS_S;
3893 device_name_dsc.dsc$a_pointer = device_name;
3894 device_name_dsc.dsc$w_length = sizeof device_name -1;
3895 device_name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
3896 device_name_dsc.dsc$b_class = DSC$K_CLASS_S;
3898 device_name_len = 0;
3900 /* Try to create the window */
3901 status = decw$term_port
3910 if (!$VMS_STATUS_SUCCESS(status)) {
3911 SETERRNO(EVMSERR, status);
3915 device_name[device_name_len] = '\0';
3917 /* Need to set this up to look like a pipe for cleanup */
3919 status = lib$get_vm(&n, &info);
3920 if (!$VMS_STATUS_SUCCESS(status)) {
3921 SETERRNO(ENOMEM, status);
3927 info->completion = 0;
3928 info->closing = FALSE;
3935 info->in_done = TRUE;
3936 info->out_done = TRUE;
3937 info->err_done = TRUE;
3939 /* Assign a channel on this so that it will persist, and not login */
3940 /* We stash this channel in the info structure for reference. */
3941 /* The created xterm self destructs when the last channel is removed */
3942 /* and it appears that perl5db.pl (perl debugger) does this routinely */
3943 /* So leave this assigned. */
3944 device_name_dsc.dsc$w_length = device_name_len;
3945 status = sys$assign(&device_name_dsc,&info->xchan,0,0);
3946 if (!$VMS_STATUS_SUCCESS(status)) {
3947 SETERRNO(EVMSERR, status);
3950 info->xchan_valid = 1;
3952 /* Now create a mailbox to be read by the application */
3954 create_mbx(aTHX_ &p_chan, &d_mbx1);
3956 /* write the name of the created terminal to the mailbox */
3957 status = sys$qiow(NO_EFN, p_chan, IO$_WRITEVBLK|IO$M_NOW,
3958 iosb, NULL, NULL, device_name, device_name_len, 0, 0, 0, 0);
3960 if (!$VMS_STATUS_SUCCESS(status)) {
3961 SETERRNO(EVMSERR, status);
3965 info->fp = PerlIO_open(mbx1, mode);
3967 /* Done with this channel */
3970 /* If any errors, then clean up */
3973 _ckvmssts(lib$free_vm(&n, &info));
3983 safe_popen(pTHX_ const char *cmd, const char *in_mode, int *psts)
3985 static int handler_set_up = FALSE;
3986 unsigned long int sts, flags = CLI$M_NOWAIT;
3987 /* The use of a GLOBAL table (as was done previously) rendered
3988 * Perl's qx() or `` unusable from a C<$ SET SYMBOL/SCOPE=NOGLOBAL> DCL
3989 * environment. Hence we've switched to LOCAL symbol table.
3991 unsigned int table = LIB$K_CLI_LOCAL_SYM;
3993 char *p, mode[10], symbol[MAX_DCL_SYMBOL+1], *vmspipe;
3994 char *in, *out, *err, mbx[512];
3996 char tfilebuf[NAM$C_MAXRSS+1];
3998 char cmd_sym_name[20];
3999 struct dsc$descriptor_s d_symbol= {0, DSC$K_DTYPE_T,
4000 DSC$K_CLASS_S, symbol};
4001 struct dsc$descriptor_s vmspipedsc = {0, DSC$K_DTYPE_T,
4003 struct dsc$descriptor_s d_sym_cmd = {0, DSC$K_DTYPE_T,
4004 DSC$K_CLASS_S, cmd_sym_name};
4005 struct dsc$descriptor_s *vmscmd;
4006 $DESCRIPTOR(d_sym_in ,"PERL_POPEN_IN");
4007 $DESCRIPTOR(d_sym_out,"PERL_POPEN_OUT");
4008 $DESCRIPTOR(d_sym_err,"PERL_POPEN_ERR");
4010 #ifdef USE_VMS_DECTERM
4011 /* Check here for Xterm create request. This means looking for
4012 * "3>&1 xterm\b" and "\btty 1>&3\b$" in the command, and that it
4013 * is possible to create an xterm.
4015 if (*in_mode == 'r') {
4018 xterm_fd = create_forked_xterm(aTHX_ cmd, in_mode);
4019 if (xterm_fd != Nullfp)
4024 if (!head_PLOC) store_pipelocs(aTHX); /* at least TRY to use a static vmspipe file */
4026 /* once-per-program initialization...
4027 note that the SETAST calls and the dual test of pipe_ef
4028 makes sure that only the FIRST thread through here does
4029 the initialization...all other threads wait until it's
4032 Yeah, uglier than a pthread call, it's got all the stuff inline
4033 rather than in a separate routine.
4037 _ckvmssts(sys$setast(0));
4039 unsigned long int pidcode = JPI$_PID;
4040 $DESCRIPTOR(d_delay, RETRY_DELAY);
4041 _ckvmssts(lib$get_ef(&pipe_ef));
4042 _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
4043 _ckvmssts(sys$bintim(&d_delay, delaytime));
4045 if (!handler_set_up) {
4046 _ckvmssts(sys$dclexh(&pipe_exitblock));
4047 handler_set_up = TRUE;
4049 _ckvmssts(sys$setast(1));
4052 /* see if we can find a VMSPIPE.COM */
4055 vmspipe = find_vmspipe(aTHX);
4057 strcpy(tfilebuf+1,vmspipe);
4058 } else { /* uh, oh...we're in tempfile hell */
4059 tpipe = vmspipe_tempfile(aTHX);
4060 if (!tpipe) { /* a fish popular in Boston */
4061 if (ckWARN(WARN_PIPE)) {
4062 Perl_warner(aTHX_ packWARN(WARN_PIPE),"unable to find VMSPIPE.COM for i/o piping");
4066 fgetname(tpipe,tfilebuf+1,1);
4068 vmspipedsc.dsc$a_pointer = tfilebuf;
4069 vmspipedsc.dsc$w_length = strlen(tfilebuf);
4071 sts = setup_cmddsc(aTHX_ cmd,0,0,&vmscmd);
4074 case RMS$_FNF: case RMS$_DNF:
4075 set_errno(ENOENT); break;
4077 set_errno(ENOTDIR); break;
4079 set_errno(ENODEV); break;
4081 set_errno(EACCES); break;
4083 set_errno(EINVAL); break;
4084 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
4085 set_errno(E2BIG); break;
4086 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
4087 _ckvmssts(sts); /* fall through */
4088 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
4091 set_vaxc_errno(sts);
4092 if (*in_mode != 'n' && ckWARN(WARN_PIPE)) {
4093 Perl_warner(aTHX_ packWARN(WARN_PIPE),"Can't pipe \"%*s\": %s", strlen(cmd), cmd, Strerror(errno));
4099 _ckvmssts(lib$get_vm(&n, &info));
4101 strcpy(mode,in_mode);
4104 info->completion = 0;
4105 info->closing = FALSE;
4112 info->in_done = TRUE;
4113 info->out_done = TRUE;
4114 info->err_done = TRUE;
4116 info->xchan_valid = 0;
4118 in = PerlMem_malloc(VMS_MAXRSS);
4119 if (in == NULL) _ckvmssts(SS$_INSFMEM);
4120 out = PerlMem_malloc(VMS_MAXRSS);
4121 if (out == NULL) _ckvmssts(SS$_INSFMEM);
4122 err = PerlMem_malloc(VMS_MAXRSS);
4123 if (err == NULL) _ckvmssts(SS$_INSFMEM);
4125 in[0] = out[0] = err[0] = '\0';
4127 if ((p = strchr(mode,'F')) != NULL) { /* F -> use FILE* */
4131 if ((p = strchr(mode,'W')) != NULL) { /* W -> wait for completion */
4136 if (*mode == 'r') { /* piping from subroutine */
4138 info->out = pipe_infromchild_setup(aTHX_ mbx,out);
4140 info->out->pipe_done = &info->out_done;
4141 info->out_done = FALSE;
4142 info->out->info = info;
4144 if (!info->useFILE) {
4145 info->fp = PerlIO_open(mbx, mode);
4147 info->fp = (PerlIO *) freopen(mbx, mode, stdin);
4148 Perl_vmssetuserlnm(aTHX_ "SYS$INPUT",mbx);
4151 if (!info->fp && info->out) {
4152 sys$cancel(info->out->chan_out);
4154 while (!info->out_done) {
4156 _ckvmssts(sys$setast(0));
4157 done = info->out_done;
4158 if (!done) _ckvmssts(sys$clref(pipe_ef));
4159 _ckvmssts(sys$setast(1));
4160 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4163 if (info->out->buf) {
4164 n = info->out->bufsize * sizeof(char);
4165 _ckvmssts(lib$free_vm(&n, &info->out->buf));
4168 _ckvmssts(lib$free_vm(&n, &info->out));
4170 _ckvmssts(lib$free_vm(&n, &info));
4175 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4177 info->err->pipe_done = &info->err_done;
4178 info->err_done = FALSE;
4179 info->err->info = info;
4182 } else if (*mode == 'w') { /* piping to subroutine */
4184 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
4186 info->out->pipe_done = &info->out_done;
4187 info->out_done = FALSE;
4188 info->out->info = info;
4191 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4193 info->err->pipe_done = &info->err_done;
4194 info->err_done = FALSE;
4195 info->err->info = info;
4198 info->in = pipe_tochild_setup(aTHX_ in,mbx);
4199 if (!info->useFILE) {
4200 info->fp = PerlIO_open(mbx, mode);
4202 info->fp = (PerlIO *) freopen(mbx, mode, stdout);
4203 Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",mbx);
4207 info->in->pipe_done = &info->in_done;
4208 info->in_done = FALSE;
4209 info->in->info = info;
4213 if (!info->fp && info->in) {
4215 _ckvmssts(sys$qiow(0,info->in->chan_in, IO$_WRITEOF, 0,
4216 0, 0, 0, 0, 0, 0, 0, 0));
4218 while (!info->in_done) {
4220 _ckvmssts(sys$setast(0));
4221 done = info->in_done;
4222 if (!done) _ckvmssts(sys$clref(pipe_ef));
4223 _ckvmssts(sys$setast(1));
4224 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4227 if (info->in->buf) {
4228 n = info->in->bufsize * sizeof(char);
4229 _ckvmssts(lib$free_vm(&n, &info->in->buf));
4232 _ckvmssts(lib$free_vm(&n, &info->in));
4234 _ckvmssts(lib$free_vm(&n, &info));
4240 } else if (*mode == 'n') { /* separate subprocess, no Perl i/o */
4241 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
4243 info->out->pipe_done = &info->out_done;
4244 info->out_done = FALSE;
4245 info->out->info = info;
4248 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4250 info->err->pipe_done = &info->err_done;
4251 info->err_done = FALSE;
4252 info->err->info = info;
4256 symbol[MAX_DCL_SYMBOL] = '\0';
4258 strncpy(symbol, in, MAX_DCL_SYMBOL);
4259 d_symbol.dsc$w_length = strlen(symbol);
4260 _ckvmssts(lib$set_symbol(&d_sym_in, &d_symbol, &table));
4262 strncpy(symbol, err, MAX_DCL_SYMBOL);
4263 d_symbol.dsc$w_length = strlen(symbol);
4264 _ckvmssts(lib$set_symbol(&d_sym_err, &d_symbol, &table));
4266 strncpy(symbol, out, MAX_DCL_SYMBOL);
4267 d_symbol.dsc$w_length = strlen(symbol);
4268 _ckvmssts(lib$set_symbol(&d_sym_out, &d_symbol, &table));
4270 /* Done with the names for the pipes */
4275 p = vmscmd->dsc$a_pointer;
4276 while (*p == ' ' || *p == '\t') p++; /* remove leading whitespace */
4277 if (*p == '$') p++; /* remove leading $ */
4278 while (*p == ' ' || *p == '\t') p++;
4280 for (j = 0; j < 4; j++) {
4281 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
4282 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
4284 strncpy(symbol, p, MAX_DCL_SYMBOL);
4285 d_symbol.dsc$w_length = strlen(symbol);
4286 _ckvmssts(lib$set_symbol(&d_sym_cmd, &d_symbol, &table));
4288 if (strlen(p) > MAX_DCL_SYMBOL) {
4289 p += MAX_DCL_SYMBOL;
4294 _ckvmssts(sys$setast(0));
4295 info->next=open_pipes; /* prepend to list */
4297 _ckvmssts(sys$setast(1));
4298 /* Omit arg 2 (input file) so the child will get the parent's SYS$INPUT
4299 * and SYS$COMMAND. vmspipe.com will redefine SYS$INPUT, but we'll still
4300 * have SYS$COMMAND if we need it.
4302 _ckvmssts(lib$spawn(&vmspipedsc, 0, &nl_desc, &flags,
4303 0, &info->pid, &info->completion,
4304 0, popen_completion_ast,info,0,0,0));
4306 /* if we were using a tempfile, close it now */
4308 if (tpipe) fclose(tpipe);
4310 /* once the subprocess is spawned, it has copied the symbols and
4311 we can get rid of ours */
4313 for (j = 0; j < 4; j++) {
4314 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
4315 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
4316 _ckvmssts(lib$delete_symbol(&d_sym_cmd, &table));
4318 _ckvmssts(lib$delete_symbol(&d_sym_in, &table));
4319 _ckvmssts(lib$delete_symbol(&d_sym_err, &table));
4320 _ckvmssts(lib$delete_symbol(&d_sym_out, &table));
4321 vms_execfree(vmscmd);
4323 #ifdef PERL_IMPLICIT_CONTEXT
4326 PL_forkprocess = info->pid;
4331 _ckvmssts(sys$setast(0));
4333 if (!done) _ckvmssts(sys$clref(pipe_ef));
4334 _ckvmssts(sys$setast(1));
4335 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4337 *psts = info->completion;
4338 /* Caller thinks it is open and tries to close it. */
4339 /* This causes some problems, as it changes the error status */
4340 /* my_pclose(info->fp); */
4345 } /* end of safe_popen */
4348 /*{{{ PerlIO *my_popen(char *cmd, char *mode)*/
4350 Perl_my_popen(pTHX_ const char *cmd, const char *mode)
4354 TAINT_PROPER("popen");
4355 PERL_FLUSHALL_FOR_CHILD;
4356 return safe_popen(aTHX_ cmd,mode,&sts);
4361 /*{{{ I32 my_pclose(PerlIO *fp)*/
4362 I32 Perl_my_pclose(pTHX_ PerlIO *fp)
4364 pInfo info, last = NULL;
4365 unsigned long int retsts;
4369 for (info = open_pipes; info != NULL; last = info, info = info->next)
4370 if (info->fp == fp) break;
4372 if (info == NULL) { /* no such pipe open */
4373 set_errno(ECHILD); /* quoth POSIX */
4374 set_vaxc_errno(SS$_NONEXPR);
4378 /* If we were writing to a subprocess, insure that someone reading from
4379 * the mailbox gets an EOF. It looks like a simple fclose() doesn't
4380 * produce an EOF record in the mailbox.
4382 * well, at least sometimes it *does*, so we have to watch out for
4383 * the first EOF closing the pipe (and DASSGN'ing the channel)...
4387 #if defined(USE_ITHREADS)
4390 && PL_perlio_fd_refcnt)
4391 PerlIO_flush(info->fp);
4393 fflush((FILE *)info->fp);
4396 _ckvmssts(sys$setast(0));
4397 info->closing = TRUE;
4398 done = info->done && info->in_done && info->out_done && info->err_done;
4399 /* hanging on write to Perl's input? cancel it */
4400 if (info->mode == 'r' && info->out && !info->out_done) {
4401 if (info->out->chan_out) {
4402 _ckvmssts(sys$cancel(info->out->chan_out));
4403 if (!info->out->chan_in) { /* EOF generation, need AST */
4404 _ckvmssts(sys$dclast(pipe_infromchild_ast,info->out,0));
4408 if (info->in && !info->in_done && !info->in->shut_on_empty) /* EOF if hasn't had one yet */
4409 _ckvmssts(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
4411 _ckvmssts(sys$setast(1));
4414 #if defined(USE_ITHREADS)
4417 && PL_perlio_fd_refcnt)
4418 PerlIO_close(info->fp);
4420 fclose((FILE *)info->fp);
4423 we have to wait until subprocess completes, but ALSO wait until all
4424 the i/o completes...otherwise we'll be freeing the "info" structure
4425 that the i/o ASTs could still be using...
4429 _ckvmssts(sys$setast(0));
4430 done = info->done && info->in_done && info->out_done && info->err_done;
4431 if (!done) _ckvmssts(sys$clref(pipe_ef));
4432 _ckvmssts(sys$setast(1));
4433 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4435 retsts = info->completion;
4437 /* remove from list of open pipes */
4438 _ckvmssts(sys$setast(0));
4439 if (last) last->next = info->next;
4440 else open_pipes = info->next;
4441 _ckvmssts(sys$setast(1));
4443 /* free buffers and structures */
4446 if (info->in->buf) {
4447 n = info->in->bufsize * sizeof(char);
4448 _ckvmssts(lib$free_vm(&n, &info->in->buf));
4451 _ckvmssts(lib$free_vm(&n, &info->in));
4454 if (info->out->buf) {
4455 n = info->out->bufsize * sizeof(char);
4456 _ckvmssts(lib$free_vm(&n, &info->out->buf));
4459 _ckvmssts(lib$free_vm(&n, &info->out));
4462 if (info->err->buf) {
4463 n = info->err->bufsize * sizeof(char);
4464 _ckvmssts(lib$free_vm(&n, &info->err->buf));
4467 _ckvmssts(lib$free_vm(&n, &info->err));
4470 _ckvmssts(lib$free_vm(&n, &info));
4474 } /* end of my_pclose() */
4476 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
4477 /* Roll our own prototype because we want this regardless of whether
4478 * _VMS_WAIT is defined.
4480 __pid_t __vms_waitpid( __pid_t __pid, int *__stat_loc, int __options );
4482 /* sort-of waitpid; special handling of pipe clean-up for subprocesses
4483 created with popen(); otherwise partially emulate waitpid() unless
4484 we have a suitable one from the CRTL that came with VMS 7.2 and later.
4485 Also check processes not considered by the CRTL waitpid().
4487 /*{{{Pid_t my_waitpid(Pid_t pid, int *statusp, int flags)*/
4489 Perl_my_waitpid(pTHX_ Pid_t pid, int *statusp, int flags)
4496 if (statusp) *statusp = 0;
4498 for (info = open_pipes; info != NULL; info = info->next)
4499 if (info->pid == pid) break;
4501 if (info != NULL) { /* we know about this child */
4502 while (!info->done) {
4503 _ckvmssts(sys$setast(0));
4505 if (!done) _ckvmssts(sys$clref(pipe_ef));
4506 _ckvmssts(sys$setast(1));
4507 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4510 if (statusp) *statusp = info->completion;
4514 /* child that already terminated? */
4516 for (j = 0; j < NKEEPCLOSED && j < closed_num; j++) {
4517 if (closed_list[j].pid == pid) {
4518 if (statusp) *statusp = closed_list[j].completion;
4523 /* fall through if this child is not one of our own pipe children */
4525 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
4527 /* waitpid() became available in the CRTL as of VMS 7.0, but only
4528 * in 7.2 did we get a version that fills in the VMS completion
4529 * status as Perl has always tried to do.
4532 sts = __vms_waitpid( pid, statusp, flags );
4534 if ( sts == 0 || !(sts == -1 && errno == ECHILD) )
4537 /* If the real waitpid tells us the child does not exist, we
4538 * fall through here to implement waiting for a child that
4539 * was created by some means other than exec() (say, spawned
4540 * from DCL) or to wait for a process that is not a subprocess
4541 * of the current process.
4544 #endif /* defined(__CRTL_VER) && __CRTL_VER >= 70200000 */
4547 $DESCRIPTOR(intdsc,"0 00:00:01");
4548 unsigned long int ownercode = JPI$_OWNER, ownerpid;
4549 unsigned long int pidcode = JPI$_PID, mypid;
4550 unsigned long int interval[2];
4551 unsigned int jpi_iosb[2];
4552 struct itmlst_3 jpilist[2] = {
4553 {sizeof(ownerpid), JPI$_OWNER, &ownerpid, 0},
4558 /* Sorry folks, we don't presently implement rooting around for
4559 the first child we can find, and we definitely don't want to
4560 pass a pid of -1 to $getjpi, where it is a wildcard operation.
4566 /* Get the owner of the child so I can warn if it's not mine. If the
4567 * process doesn't exist or I don't have the privs to look at it,
4568 * I can go home early.
4570 sts = sys$getjpiw(0,&pid,NULL,&jpilist,&jpi_iosb,NULL,NULL);
4571 if (sts & 1) sts = jpi_iosb[0];
4583 set_vaxc_errno(sts);
4587 if (ckWARN(WARN_EXEC)) {
4588 /* remind folks they are asking for non-standard waitpid behavior */
4589 _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
4590 if (ownerpid != mypid)
4591 Perl_warner(aTHX_ packWARN(WARN_EXEC),
4592 "waitpid: process %x is not a child of process %x",
4596 /* simply check on it once a second until it's not there anymore. */
4598 _ckvmssts(sys$bintim(&intdsc,interval));
4599 while ((sts=lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0)) & 1) {
4600 _ckvmssts(sys$schdwk(0,0,interval,0));
4601 _ckvmssts(sys$hiber());
4603 if (sts == SS$_NONEXPR) sts = SS$_NORMAL;
4608 } /* end of waitpid() */
4613 /*{{{ char *my_gconvert(double val, int ndig, int trail, char *buf) */
4615 my_gconvert(double val, int ndig, int trail, char *buf)
4617 static char __gcvtbuf[DBL_DIG+1];
4620 loc = buf ? buf : __gcvtbuf;
4622 #ifndef __DECC /* VAXCRTL gcvt uses E format for numbers < 1 */
4624 sprintf(loc,"%.*g",ndig,val);
4630 if (!buf && ndig > DBL_DIG) ndig = DBL_DIG;
4631 return gcvt(val,ndig,loc);
4634 loc[0] = '0'; loc[1] = '\0';
4641 #if defined(__VAX) || !defined(NAML$C_MAXRSS)
4642 static int rms_free_search_context(struct FAB * fab)
4646 nam = fab->fab$l_nam;
4647 nam->nam$b_nop |= NAM$M_SYNCHK;
4648 nam->nam$l_rlf = NULL;
4650 return sys$parse(fab, NULL, NULL);
4653 #define rms_setup_nam(nam) struct NAM nam = cc$rms_nam
4654 #define rms_clear_nam_nop(nam) nam.nam$b_nop = 0;
4655 #define rms_set_nam_nop(nam, opt) nam.nam$b_nop |= (opt)
4656 #define rms_set_nam_fnb(nam, opt) nam.nam$l_fnb |= (opt)
4657 #define rms_is_nam_fnb(nam, opt) (nam.nam$l_fnb & (opt))
4658 #define rms_nam_esll(nam) nam.nam$b_esl
4659 #define rms_nam_esl(nam) nam.nam$b_esl
4660 #define rms_nam_name(nam) nam.nam$l_name
4661 #define rms_nam_namel(nam) nam.nam$l_name
4662 #define rms_nam_type(nam) nam.nam$l_type
4663 #define rms_nam_typel(nam) nam.nam$l_type
4664 #define rms_nam_ver(nam) nam.nam$l_ver
4665 #define rms_nam_verl(nam) nam.nam$l_ver
4666 #define rms_nam_rsll(nam) nam.nam$b_rsl
4667 #define rms_nam_rsl(nam) nam.nam$b_rsl
4668 #define rms_bind_fab_nam(fab, nam) fab.fab$l_nam = &nam
4669 #define rms_set_fna(fab, nam, name, size) \
4670 { fab.fab$b_fns = size; fab.fab$l_fna = name; }
4671 #define rms_get_fna(fab, nam) fab.fab$l_fna
4672 #define rms_set_dna(fab, nam, name, size) \
4673 { fab.fab$b_dns = size; fab.fab$l_dna = name; }
4674 #define rms_nam_dns(fab, nam) fab.fab$b_dns
4675 #define rms_set_esa(fab, nam, name, size) \
4676 { nam.nam$b_ess = size; nam.nam$l_esa = name; }
4677 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
4678 { nam.nam$l_esa = s_name; nam.nam$b_ess = s_size;}
4679 #define rms_set_rsa(nam, name, size) \
4680 { nam.nam$l_rsa = name; nam.nam$b_rss = size; }
4681 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
4682 { nam.nam$l_rsa = s_name; nam.nam$b_rss = s_size; }
4683 #define rms_nam_name_type_l_size(nam) \
4684 (nam.nam$b_name + nam.nam$b_type)
4686 static int rms_free_search_context(struct FAB * fab)
4690 nam = fab->fab$l_naml;
4691 nam->naml$b_nop |= NAM$M_SYNCHK;
4692 nam->naml$l_rlf = NULL;
4693 nam->naml$l_long_defname_size = 0;
4696 return sys$parse(fab, NULL, NULL);
4699 #define rms_setup_nam(nam) struct NAML nam = cc$rms_naml
4700 #define rms_clear_nam_nop(nam) nam.naml$b_nop = 0;
4701 #define rms_set_nam_nop(nam, opt) nam.naml$b_nop |= (opt)
4702 #define rms_set_nam_fnb(nam, opt) nam.naml$l_fnb |= (opt)
4703 #define rms_is_nam_fnb(nam, opt) (nam.naml$l_fnb & (opt))
4704 #define rms_nam_esll(nam) nam.naml$l_long_expand_size
4705 #define rms_nam_esl(nam) nam.naml$b_esl
4706 #define rms_nam_name(nam) nam.naml$l_name
4707 #define rms_nam_namel(nam) nam.naml$l_long_name
4708 #define rms_nam_type(nam) nam.naml$l_type
4709 #define rms_nam_typel(nam) nam.naml$l_long_type
4710 #define rms_nam_ver(nam) nam.naml$l_ver
4711 #define rms_nam_verl(nam) nam.naml$l_long_ver
4712 #define rms_nam_rsll(nam) nam.naml$l_long_result_size
4713 #define rms_nam_rsl(nam) nam.naml$b_rsl
4714 #define rms_bind_fab_nam(fab, nam) fab.fab$l_naml = &nam
4715 #define rms_set_fna(fab, nam, name, size) \
4716 { fab.fab$b_fns = 0; fab.fab$l_fna = (char *) -1; \
4717 nam.naml$l_long_filename_size = size; \
4718 nam.naml$l_long_filename = name;}
4719 #define rms_get_fna(fab, nam) nam.naml$l_long_filename
4720 #define rms_set_dna(fab, nam, name, size) \
4721 { fab.fab$b_dns = 0; fab.fab$l_dna = (char *) -1; \
4722 nam.naml$l_long_defname_size = size; \
4723 nam.naml$l_long_defname = name; }
4724 #define rms_nam_dns(fab, nam) nam.naml$l_long_defname_size
4725 #define rms_set_esa(fab, nam, name, size) \
4726 { nam.naml$b_ess = 0; nam.naml$l_esa = (char *) -1; \
4727 nam.naml$l_long_expand_alloc = size; \
4728 nam.naml$l_long_expand = name; }
4729 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
4730 { nam.naml$l_esa = s_name; nam.naml$b_ess = s_size; \
4731 nam.naml$l_long_expand = l_name; \
4732 nam.naml$l_long_expand_alloc = l_size; }
4733 #define rms_set_rsa(nam, name, size) \
4734 { nam.naml$l_rsa = NULL; nam.naml$b_rss = 0; \
4735 nam.naml$l_long_result = name; \
4736 nam.naml$l_long_result_alloc = size; }
4737 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
4738 { nam.naml$l_rsa = s_name; nam.naml$b_rss = s_size; \
4739 nam.naml$l_long_result = l_name; \
4740 nam.naml$l_long_result_alloc = l_size; }
4741 #define rms_nam_name_type_l_size(nam) \
4742 (nam.naml$l_long_name_size + nam.naml$l_long_type_size)
4746 /*{{{char *do_rmsexpand(char *fspec, char *out, int ts, char *def, unsigned opts)*/
4747 /* Shortcut for common case of simple calls to $PARSE and $SEARCH
4748 * to expand file specification. Allows for a single default file
4749 * specification and a simple mask of options. If outbuf is non-NULL,
4750 * it must point to a buffer at least NAM$C_MAXRSS bytes long, into which
4751 * the resultant file specification is placed. If outbuf is NULL, the
4752 * resultant file specification is placed into a static buffer.
4753 * The third argument, if non-NULL, is taken to be a default file
4754 * specification string. The fourth argument is unused at present.
4755 * rmesexpand() returns the address of the resultant string if
4756 * successful, and NULL on error.
4758 * New functionality for previously unused opts value:
4759 * PERL_RMSEXPAND_M_VMS - Force output file specification to VMS format.
4760 * PERL_RMSEXPAND_M_LONG - Want output in long formst
4761 * PERL_RMSEXPAND_M_VMS_IN - Input is already in VMS, so do not vmsify
4763 static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *);
4767 (pTHX_ const char *filespec,
4770 const char *defspec,
4775 static char __rmsexpand_retbuf[VMS_MAXRSS];
4776 char * vmsfspec, *tmpfspec;
4777 char * esa, *cp, *out = NULL;
4781 struct FAB myfab = cc$rms_fab;
4782 rms_setup_nam(mynam);
4784 unsigned long int retsts, trimver, trimtype, haslower = 0, isunix = 0;
4787 /* temp hack until UTF8 is actually implemented */
4788 if (fs_utf8 != NULL)
4791 if (!filespec || !*filespec) {
4792 set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL);
4796 if (ts) out = Newx(outbuf,VMS_MAXRSS,char);
4797 else outbuf = __rmsexpand_retbuf;
4805 if ((opts & PERL_RMSEXPAND_M_VMS_IN) == 0) {
4806 isunix = is_unix_filespec(filespec);
4808 vmsfspec = PerlMem_malloc(VMS_MAXRSS);
4809 if (vmsfspec == NULL) _ckvmssts(SS$_INSFMEM);
4810 if (do_tovmsspec(filespec,vmsfspec,0,fs_utf8) == NULL) {
4811 PerlMem_free(vmsfspec);
4816 filespec = vmsfspec;
4818 /* Unless we are forcing to VMS format, a UNIX input means
4819 * UNIX output, and that requires long names to be used
4821 if ((opts & PERL_RMSEXPAND_M_VMS) == 0)
4822 opts |= PERL_RMSEXPAND_M_LONG;
4829 rms_set_fna(myfab, mynam, (char *)filespec, strlen(filespec)); /* cast ok */
4830 rms_bind_fab_nam(myfab, mynam);
4832 if (defspec && *defspec) {
4834 t_isunix = is_unix_filespec(defspec);
4836 tmpfspec = PerlMem_malloc(VMS_MAXRSS);
4837 if (tmpfspec == NULL) _ckvmssts(SS$_INSFMEM);
4838 if (do_tovmsspec(defspec,tmpfspec,0,dfs_utf8) == NULL) {
4839 PerlMem_free(tmpfspec);
4840 if (vmsfspec != NULL)
4841 PerlMem_free(vmsfspec);
4848 rms_set_dna(myfab, mynam, (char *)defspec, strlen(defspec)); /* cast ok */
4851 esa = PerlMem_malloc(NAM$C_MAXRSS + 1);
4852 if (esa == NULL) _ckvmssts(SS$_INSFMEM);
4853 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
4854 esal = PerlMem_malloc(VMS_MAXRSS);
4855 if (esal == NULL) _ckvmssts(SS$_INSFMEM);
4857 rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS-1);
4859 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
4860 rms_set_rsa(mynam, outbuf, (VMS_MAXRSS - 1));
4863 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
4864 outbufl = PerlMem_malloc(VMS_MAXRSS);
4865 if (outbufl == NULL) _ckvmssts(SS$_INSFMEM);
4866 rms_set_rsal(mynam, outbuf, NAM$C_MAXRSS, outbufl, (VMS_MAXRSS - 1));
4868 rms_set_rsa(mynam, outbuf, NAM$C_MAXRSS);
4872 #ifdef NAM$M_NO_SHORT_UPCASE
4873 if (decc_efs_case_preserve)
4874 rms_set_nam_nop(mynam, NAM$M_NO_SHORT_UPCASE);
4877 /* First attempt to parse as an existing file */
4878 retsts = sys$parse(&myfab,0,0);
4879 if (!(retsts & STS$K_SUCCESS)) {
4881 /* Could not find the file, try as syntax only if error is not fatal */
4882 rms_set_nam_nop(mynam, NAM$M_SYNCHK);
4883 if (retsts == RMS$_DNF || retsts == RMS$_DIR || retsts == RMS$_DEV) {
4884 retsts = sys$parse(&myfab,0,0);
4885 if (retsts & STS$K_SUCCESS) goto expanded;
4888 /* Still could not parse the file specification */
4889 /*----------------------------------------------*/
4890 sts = rms_free_search_context(&myfab); /* Free search context */
4891 if (out) Safefree(out);
4892 if (tmpfspec != NULL)
4893 PerlMem_free(tmpfspec);
4894 if (vmsfspec != NULL)
4895 PerlMem_free(vmsfspec);
4896 if (outbufl != NULL)
4897 PerlMem_free(outbufl);
4901 set_vaxc_errno(retsts);
4902 if (retsts == RMS$_PRV) set_errno(EACCES);
4903 else if (retsts == RMS$_DEV) set_errno(ENODEV);
4904 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
4905 else set_errno(EVMSERR);
4908 retsts = sys$search(&myfab,0,0);
4909 if (!(retsts & STS$K_SUCCESS) && retsts != RMS$_FNF) {
4910 sts = rms_free_search_context(&myfab); /* Free search context */
4911 if (out) Safefree(out);
4912 if (tmpfspec != NULL)
4913 PerlMem_free(tmpfspec);
4914 if (vmsfspec != NULL)
4915 PerlMem_free(vmsfspec);
4916 if (outbufl != NULL)
4917 PerlMem_free(outbufl);
4921 set_vaxc_errno(retsts);
4922 if (retsts == RMS$_PRV) set_errno(EACCES);
4923 else set_errno(EVMSERR);
4927 /* If the input filespec contained any lowercase characters,
4928 * downcase the result for compatibility with Unix-minded code. */
4930 if (!decc_efs_case_preserve) {
4931 for (tbuf = rms_get_fna(myfab, mynam); *tbuf; tbuf++)
4932 if (islower(*tbuf)) { haslower = 1; break; }
4935 /* Is a long or a short name expected */
4936 /*------------------------------------*/
4937 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
4938 if (rms_nam_rsll(mynam)) {
4940 speclen = rms_nam_rsll(mynam);
4943 tbuf = esal; /* Not esa */
4944 speclen = rms_nam_esll(mynam);
4948 if (rms_nam_rsl(mynam)) {
4950 speclen = rms_nam_rsl(mynam);
4953 tbuf = esa; /* Not esal */
4954 speclen = rms_nam_esl(mynam);
4957 tbuf[speclen] = '\0';
4959 /* Trim off null fields added by $PARSE
4960 * If type > 1 char, must have been specified in original or default spec
4961 * (not true for version; $SEARCH may have added version of existing file).
4963 trimver = !rms_is_nam_fnb(mynam, NAM$M_EXP_VER);
4964 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
4965 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
4966 ((rms_nam_verl(mynam) - rms_nam_typel(mynam)) == 1);
4969 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
4970 ((rms_nam_ver(mynam) - rms_nam_type(mynam)) == 1);
4972 if (trimver || trimtype) {
4973 if (defspec && *defspec) {
4974 char *defesal = NULL;
4975 defesal = PerlMem_malloc(VMS_MAXRSS + 1);
4976 if (defesal != NULL) {
4977 struct FAB deffab = cc$rms_fab;
4978 rms_setup_nam(defnam);
4980 rms_bind_fab_nam(deffab, defnam);
4984 (deffab, defnam, (char *)defspec, rms_nam_dns(myfab, mynam));
4986 rms_set_esa(deffab, defnam, defesal, VMS_MAXRSS - 1);
4988 rms_clear_nam_nop(defnam);
4989 rms_set_nam_nop(defnam, NAM$M_SYNCHK);
4990 #ifdef NAM$M_NO_SHORT_UPCASE
4991 if (decc_efs_case_preserve)
4992 rms_set_nam_nop(defnam, NAM$M_NO_SHORT_UPCASE);
4994 if (sys$parse(&deffab,0,0) & STS$K_SUCCESS) {
4996 trimver = !rms_is_nam_fnb(defnam, NAM$M_EXP_VER);
4999 trimtype = !rms_is_nam_fnb(defnam, NAM$M_EXP_TYPE);
5002 PerlMem_free(defesal);
5006 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5007 if (*(rms_nam_verl(mynam)) != '\"')
5008 speclen = rms_nam_verl(mynam) - tbuf;
5011 if (*(rms_nam_ver(mynam)) != '\"')
5012 speclen = rms_nam_ver(mynam) - tbuf;
5016 /* If we didn't already trim version, copy down */
5017 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5018 if (speclen > rms_nam_verl(mynam) - tbuf)
5020 (rms_nam_typel(mynam),
5021 rms_nam_verl(mynam),
5022 speclen - (rms_nam_verl(mynam) - tbuf));
5023 speclen -= rms_nam_verl(mynam) - rms_nam_typel(mynam);
5026 if (speclen > rms_nam_ver(mynam) - tbuf)
5028 (rms_nam_type(mynam),
5030 speclen - (rms_nam_ver(mynam) - tbuf));
5031 speclen -= rms_nam_ver(mynam) - rms_nam_type(mynam);
5036 /* Done with these copies of the input files */
5037 /*-------------------------------------------*/
5038 if (vmsfspec != NULL)
5039 PerlMem_free(vmsfspec);
5040 if (tmpfspec != NULL)
5041 PerlMem_free(tmpfspec);
5043 /* If we just had a directory spec on input, $PARSE "helpfully"
5044 * adds an empty name and type for us */
5045 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5046 if (rms_nam_namel(mynam) == rms_nam_typel(mynam) &&
5047 rms_nam_verl(mynam) == rms_nam_typel(mynam) + 1 &&
5048 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
5049 speclen = rms_nam_namel(mynam) - tbuf;
5052 if (rms_nam_name(mynam) == rms_nam_type(mynam) &&
5053 rms_nam_ver(mynam) == rms_nam_ver(mynam) + 1 &&
5054 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
5055 speclen = rms_nam_name(mynam) - tbuf;
5058 /* Posix format specifications must have matching quotes */
5059 if (speclen < (VMS_MAXRSS - 1)) {
5060 if (decc_posix_compliant_pathnames && (tbuf[0] == '\"')) {
5061 if ((speclen > 1) && (tbuf[speclen-1] != '\"')) {
5062 tbuf[speclen] = '\"';
5067 tbuf[speclen] = '\0';
5068 if (haslower && !decc_efs_case_preserve) __mystrtolower(tbuf);
5070 /* Have we been working with an expanded, but not resultant, spec? */
5071 /* Also, convert back to Unix syntax if necessary. */
5073 if (!rms_nam_rsll(mynam)) {
5075 if (do_tounixspec(esa,outbuf,0,fs_utf8) == NULL) {
5076 if (out) Safefree(out);
5080 if (outbufl != NULL)
5081 PerlMem_free(outbufl);
5085 else strcpy(outbuf,esa);
5088 tmpfspec = PerlMem_malloc(VMS_MAXRSS);
5089 if (tmpfspec == NULL) _ckvmssts(SS$_INSFMEM);
5090 if (do_tounixspec(outbuf,tmpfspec,0,fs_utf8) == NULL) {
5091 if (out) Safefree(out);
5095 PerlMem_free(tmpfspec);
5096 if (outbufl != NULL)
5097 PerlMem_free(outbufl);
5100 strcpy(outbuf,tmpfspec);
5101 PerlMem_free(tmpfspec);
5104 rms_set_rsal(mynam, NULL, 0, NULL, 0);
5105 sts = rms_free_search_context(&myfab); /* Free search context */
5109 if (outbufl != NULL)
5110 PerlMem_free(outbufl);
5114 /* External entry points */
5115 char *Perl_rmsexpand(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
5116 { return do_rmsexpand(spec,buf,0,def,opt,NULL,NULL); }
5117 char *Perl_rmsexpand_ts(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
5118 { return do_rmsexpand(spec,buf,1,def,opt,NULL,NULL); }
5119 char *Perl_rmsexpand_utf8
5120 (pTHX_ const char *spec, char *buf, const char *def,
5121 unsigned opt, int * fs_utf8, int * dfs_utf8)
5122 { return do_rmsexpand(spec,buf,0,def,opt, fs_utf8, dfs_utf8); }
5123 char *Perl_rmsexpand_utf8_ts
5124 (pTHX_ const char *spec, char *buf, const char *def,
5125 unsigned opt, int * fs_utf8, int * dfs_utf8)
5126 { return do_rmsexpand(spec,buf,1,def,opt, fs_utf8, dfs_utf8); }
5130 ** The following routines are provided to make life easier when
5131 ** converting among VMS-style and Unix-style directory specifications.
5132 ** All will take input specifications in either VMS or Unix syntax. On
5133 ** failure, all return NULL. If successful, the routines listed below
5134 ** return a pointer to a buffer containing the appropriately
5135 ** reformatted spec (and, therefore, subsequent calls to that routine
5136 ** will clobber the result), while the routines of the same names with
5137 ** a _ts suffix appended will return a pointer to a mallocd string
5138 ** containing the appropriately reformatted spec.
5139 ** In all cases, only explicit syntax is altered; no check is made that
5140 ** the resulting string is valid or that the directory in question
5143 ** fileify_dirspec() - convert a directory spec into the name of the
5144 ** directory file (i.e. what you can stat() to see if it's a dir).
5145 ** The style (VMS or Unix) of the result is the same as the style
5146 ** of the parameter passed in.
5147 ** pathify_dirspec() - convert a directory spec into a path (i.e.
5148 ** what you prepend to a filename to indicate what directory it's in).
5149 ** The style (VMS or Unix) of the result is the same as the style
5150 ** of the parameter passed in.
5151 ** tounixpath() - convert a directory spec into a Unix-style path.
5152 ** tovmspath() - convert a directory spec into a VMS-style path.
5153 ** tounixspec() - convert any file spec into a Unix-style file spec.
5154 ** tovmsspec() - convert any file spec into a VMS-style spec.
5155 ** xxxxx_utf8() - Variants that support UTF8 encoding of Unix-Style file spec.
5157 ** Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>
5158 ** Permission is given to distribute this code as part of the Perl
5159 ** standard distribution under the terms of the GNU General Public
5160 ** License or the Perl Artistic License. Copies of each may be
5161 ** found in the Perl standard distribution.
5164 /*{{{ char *fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/
5165 static char *mp_do_fileify_dirspec(pTHX_ const char *dir,char *buf,int ts, int *utf8_fl)
5167 static char __fileify_retbuf[VMS_MAXRSS];
5168 unsigned long int dirlen, retlen, addmfd = 0, hasfilename = 0;
5169 char *retspec, *cp1, *cp2, *lastdir;
5170 char *trndir, *vmsdir;
5171 unsigned short int trnlnm_iter_count;
5173 if (utf8_fl != NULL)
5176 if (!dir || !*dir) {
5177 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
5179 dirlen = strlen(dir);
5180 while (dirlen && dir[dirlen-1] == '/') --dirlen;
5181 if (!dirlen) { /* We had Unixish '/' -- substitute top of current tree */
5182 if (!decc_posix_compliant_pathnames && decc_disable_posix_root) {
5189 if (dirlen > (VMS_MAXRSS - 1)) {
5190 set_errno(ENAMETOOLONG); set_vaxc_errno(RMS$_SYN);
5193 trndir = PerlMem_malloc(VMS_MAXRSS + 1);
5194 if (trndir == NULL) _ckvmssts(SS$_INSFMEM);
5195 if (!strpbrk(dir+1,"/]>:") &&
5196 (!decc_posix_compliant_pathnames && decc_disable_posix_root)) {
5197 strcpy(trndir,*dir == '/' ? dir + 1: dir);
5198 trnlnm_iter_count = 0;
5199 while (!strpbrk(trndir,"/]>:") && my_trnlnm(trndir,trndir,0)) {
5200 trnlnm_iter_count++;
5201 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
5203 dirlen = strlen(trndir);
5206 strncpy(trndir,dir,dirlen);
5207 trndir[dirlen] = '\0';
5210 /* At this point we are done with *dir and use *trndir which is a
5211 * copy that can be modified. *dir must not be modified.
5214 /* If we were handed a rooted logical name or spec, treat it like a
5215 * simple directory, so that
5216 * $ Define myroot dev:[dir.]
5217 * ... do_fileify_dirspec("myroot",buf,1) ...
5218 * does something useful.
5220 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".]")) {
5221 trndir[--dirlen] = '\0';
5222 trndir[dirlen-1] = ']';
5224 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".>")) {
5225 trndir[--dirlen] = '\0';
5226 trndir[dirlen-1] = '>';
5229 if ((cp1 = strrchr(trndir,']')) != NULL || (cp1 = strrchr(trndir,'>')) != NULL) {
5230 /* If we've got an explicit filename, we can just shuffle the string. */
5231 if (*(cp1+1)) hasfilename = 1;
5232 /* Similarly, we can just back up a level if we've got multiple levels
5233 of explicit directories in a VMS spec which ends with directories. */
5235 for (cp2 = cp1; cp2 > trndir; cp2--) {
5237 if ((cp2 - 1 > trndir) && (*(cp2 - 1) != '^')) {
5238 /* fix-me, can not scan EFS file specs backward like this */
5239 *cp2 = *cp1; *cp1 = '\0';
5244 if (*cp2 == '[' || *cp2 == '<') break;
5249 vmsdir = PerlMem_malloc(VMS_MAXRSS + 1);
5250 if (vmsdir == NULL) _ckvmssts(SS$_INSFMEM);
5251 cp1 = strpbrk(trndir,"]:>");
5252 if (hasfilename || !cp1) { /* Unix-style path or filename */
5253 if (trndir[0] == '.') {
5254 if (trndir[1] == '\0' || (trndir[1] == '/' && trndir[2] == '\0')) {
5255 PerlMem_free(trndir);
5256 PerlMem_free(vmsdir);
5257 return do_fileify_dirspec("[]",buf,ts,NULL);
5259 else if (trndir[1] == '.' &&
5260 (trndir[2] == '\0' || (trndir[2] == '/' && trndir[3] == '\0'))) {
5261 PerlMem_free(trndir);
5262 PerlMem_free(vmsdir);
5263 return do_fileify_dirspec("[-]",buf,ts,NULL);
5266 if (dirlen && trndir[dirlen-1] == '/') { /* path ends with '/'; just add .dir;1 */
5267 dirlen -= 1; /* to last element */
5268 lastdir = strrchr(trndir,'/');
5270 else if ((cp1 = strstr(trndir,"/.")) != NULL) {
5271 /* If we have "/." or "/..", VMSify it and let the VMS code
5272 * below expand it, rather than repeating the code to handle
5273 * relative components of a filespec here */
5275 if (*(cp1+2) == '.') cp1++;
5276 if (*(cp1+2) == '/' || *(cp1+2) == '\0') {
5278 if (do_tovmsspec(trndir,vmsdir,0,NULL) == NULL) {
5279 PerlMem_free(trndir);
5280 PerlMem_free(vmsdir);
5283 if (strchr(vmsdir,'/') != NULL) {
5284 /* If do_tovmsspec() returned it, it must have VMS syntax
5285 * delimiters in it, so it's a mixed VMS/Unix spec. We take
5286 * the time to check this here only so we avoid a recursion
5287 * loop; otherwise, gigo.
5289 PerlMem_free(trndir);
5290 PerlMem_free(vmsdir);
5291 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN);
5294 if (do_fileify_dirspec(vmsdir,trndir,0,NULL) == NULL) {
5295 PerlMem_free(trndir);
5296 PerlMem_free(vmsdir);
5299 ret_chr = do_tounixspec(trndir,buf,ts,NULL);
5300 PerlMem_free(trndir);
5301 PerlMem_free(vmsdir);
5305 } while ((cp1 = strstr(cp1,"/.")) != NULL);
5306 lastdir = strrchr(trndir,'/');
5308 else if (dirlen >= 7 && !strcmp(&trndir[dirlen-7],"/000000")) {
5310 /* Ditto for specs that end in an MFD -- let the VMS code
5311 * figure out whether it's a real device or a rooted logical. */
5313 /* This should not happen any more. Allowing the fake /000000
5314 * in a UNIX pathname causes all sorts of problems when trying
5315 * to run in UNIX emulation. So the VMS to UNIX conversions
5316 * now remove the fake /000000 directories.
5319 trndir[dirlen] = '/'; trndir[dirlen+1] = '\0';
5320 if (do_tovmsspec(trndir,vmsdir,0,NULL) == NULL) {
5321 PerlMem_free(trndir);
5322 PerlMem_free(vmsdir);
5325 if (do_fileify_dirspec(vmsdir,trndir,0,NULL) == NULL) {
5326 PerlMem_free(trndir);
5327 PerlMem_free(vmsdir);
5330 ret_chr = do_tounixspec(trndir,buf,ts,NULL);
5331 PerlMem_free(trndir);
5332 PerlMem_free(vmsdir);
5337 if ( !(lastdir = cp1 = strrchr(trndir,'/')) &&
5338 !(lastdir = cp1 = strrchr(trndir,']')) &&
5339 !(lastdir = cp1 = strrchr(trndir,'>'))) cp1 = trndir;
5340 if ((cp2 = strchr(cp1,'.'))) { /* look for explicit type */
5343 /* For EFS or ODS-5 look for the last dot */
5344 if (decc_efs_charset) {
5345 cp2 = strrchr(cp1,'.');
5347 if (vms_process_case_tolerant) {
5348 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
5349 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
5350 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
5351 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
5352 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
5353 (ver || *cp3)))))) {
5354 PerlMem_free(trndir);
5355 PerlMem_free(vmsdir);
5357 set_vaxc_errno(RMS$_DIR);
5362 if (!*(cp2+1) || *(cp2+1) != 'D' || /* Wrong type. */
5363 !*(cp2+2) || *(cp2+2) != 'I' || /* Bzzt. */
5364 !*(cp2+3) || *(cp2+3) != 'R' ||
5365 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
5366 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
5367 (ver || *cp3)))))) {
5368 PerlMem_free(trndir);
5369 PerlMem_free(vmsdir);
5371 set_vaxc_errno(RMS$_DIR);
5375 dirlen = cp2 - trndir;
5379 retlen = dirlen + 6;
5380 if (buf) retspec = buf;
5381 else if (ts) Newx(retspec,retlen+1,char);
5382 else retspec = __fileify_retbuf;
5383 memcpy(retspec,trndir,dirlen);
5384 retspec[dirlen] = '\0';
5386 /* We've picked up everything up to the directory file name.
5387 Now just add the type and version, and we're set. */
5388 if ((!decc_efs_case_preserve) && vms_process_case_tolerant)
5389 strcat(retspec,".dir;1");
5391 strcat(retspec,".DIR;1");
5392 PerlMem_free(trndir);
5393 PerlMem_free(vmsdir);
5396 else { /* VMS-style directory spec */
5398 char *esa, term, *cp;
5399 unsigned long int sts, cmplen, haslower = 0;
5400 unsigned int nam_fnb;
5402 struct FAB dirfab = cc$rms_fab;
5403 rms_setup_nam(savnam);
5404 rms_setup_nam(dirnam);
5406 esa = PerlMem_malloc(VMS_MAXRSS + 1);
5407 if (esa == NULL) _ckvmssts(SS$_INSFMEM);
5408 rms_set_fna(dirfab, dirnam, trndir, strlen(trndir));
5409 rms_bind_fab_nam(dirfab, dirnam);
5410 rms_set_dna(dirfab, dirnam, ".DIR;1", 6);
5411 rms_set_esa(dirfab, dirnam, esa, (VMS_MAXRSS - 1));
5412 #ifdef NAM$M_NO_SHORT_UPCASE
5413 if (decc_efs_case_preserve)
5414 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
5417 for (cp = trndir; *cp; cp++)
5418 if (islower(*cp)) { haslower = 1; break; }
5419 if (!((sts = sys$parse(&dirfab)) & STS$K_SUCCESS)) {
5420 if ((dirfab.fab$l_sts == RMS$_DIR) || (dirfab.fab$l_sts == RMS$_DNF)) {
5421 rms_set_nam_nop(dirnam, NAM$M_SYNCHK);
5422 sts = sys$parse(&dirfab) & STS$K_SUCCESS;
5426 PerlMem_free(trndir);
5427 PerlMem_free(vmsdir);
5429 set_vaxc_errno(dirfab.fab$l_sts);
5435 /* Does the file really exist? */
5436 if (sys$search(&dirfab)& STS$K_SUCCESS) {
5437 /* Yes; fake the fnb bits so we'll check type below */
5438 rms_set_nam_fnb(dirnam, (NAM$M_EXP_TYPE | NAM$M_EXP_VER));
5440 else { /* No; just work with potential name */
5441 if (dirfab.fab$l_sts == RMS$_FNF) dirnam = savnam;
5444 fab_sts = dirfab.fab$l_sts;
5445 sts = rms_free_search_context(&dirfab);
5447 PerlMem_free(trndir);
5448 PerlMem_free(vmsdir);
5449 set_errno(EVMSERR); set_vaxc_errno(fab_sts);
5454 esa[rms_nam_esll(dirnam)] = '\0';
5455 if (!rms_is_nam_fnb(dirnam, (NAM$M_EXP_DEV | NAM$M_EXP_DIR))) {
5456 cp1 = strchr(esa,']');
5457 if (!cp1) cp1 = strchr(esa,'>');
5458 if (cp1) { /* Should always be true */
5459 rms_nam_esll(dirnam) -= cp1 - esa - 1;
5460 memmove(esa,cp1 + 1, rms_nam_esll(dirnam));
5463 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_TYPE)) { /* Was type specified? */
5464 /* Yep; check version while we're at it, if it's there. */
5465 cmplen = rms_is_nam_fnb(dirnam, NAM$M_EXP_VER) ? 6 : 4;
5466 if (strncmp(rms_nam_typel(dirnam), ".DIR;1", cmplen)) {
5467 /* Something other than .DIR[;1]. Bzzt. */
5468 sts = rms_free_search_context(&dirfab);
5470 PerlMem_free(trndir);
5471 PerlMem_free(vmsdir);
5473 set_vaxc_errno(RMS$_DIR);
5478 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_NAME)) {
5479 /* They provided at least the name; we added the type, if necessary, */
5480 if (buf) retspec = buf; /* in sys$parse() */
5481 else if (ts) Newx(retspec, rms_nam_esll(dirnam)+1, char);
5482 else retspec = __fileify_retbuf;
5483 strcpy(retspec,esa);
5484 sts = rms_free_search_context(&dirfab);
5485 PerlMem_free(trndir);
5487 PerlMem_free(vmsdir);
5490 if ((cp1 = strstr(esa,".][000000]")) != NULL) {
5491 for (cp2 = cp1 + 9; *cp2; cp1++,cp2++) *cp1 = *cp2;
5493 rms_nam_esll(dirnam) -= 9;
5495 if ((cp1 = strrchr(esa,']')) == NULL) cp1 = strrchr(esa,'>');
5496 if (cp1 == NULL) { /* should never happen */
5497 sts = rms_free_search_context(&dirfab);
5498 PerlMem_free(trndir);
5500 PerlMem_free(vmsdir);
5505 retlen = strlen(esa);
5506 cp1 = strrchr(esa,'.');
5507 /* ODS-5 directory specifications can have extra "." in them. */
5508 /* Fix-me, can not scan EFS file specifications backwards */
5509 while (cp1 != NULL) {
5510 if ((cp1-1 == esa) || (*(cp1-1) != '^'))
5514 while ((cp1 > esa) && (*cp1 != '.'))
5521 if ((cp1) != NULL) {
5522 /* There's more than one directory in the path. Just roll back. */
5524 if (buf) retspec = buf;
5525 else if (ts) Newx(retspec,retlen+7,char);
5526 else retspec = __fileify_retbuf;
5527 strcpy(retspec,esa);
5530 if (rms_is_nam_fnb(dirnam, NAM$M_ROOT_DIR)) {
5531 /* Go back and expand rooted logical name */
5532 rms_set_nam_nop(dirnam, NAM$M_SYNCHK | NAM$M_NOCONCEAL);
5533 #ifdef NAM$M_NO_SHORT_UPCASE
5534 if (decc_efs_case_preserve)
5535 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
5537 if (!(sys$parse(&dirfab) & STS$K_SUCCESS)) {
5538 sts = rms_free_search_context(&dirfab);
5540 PerlMem_free(trndir);
5541 PerlMem_free(vmsdir);
5543 set_vaxc_errno(dirfab.fab$l_sts);
5546 retlen = rms_nam_esll(dirnam) - 9; /* esa - '][' - '].DIR;1' */
5547 if (buf) retspec = buf;
5548 else if (ts) Newx(retspec,retlen+16,char);
5549 else retspec = __fileify_retbuf;
5550 cp1 = strstr(esa,"][");
5551 if (!cp1) cp1 = strstr(esa,"]<");
5553 memcpy(retspec,esa,dirlen);
5554 if (!strncmp(cp1+2,"000000]",7)) {
5555 retspec[dirlen-1] = '\0';
5556 /* fix-me Not full ODS-5, just extra dots in directories for now */
5557 cp1 = retspec + dirlen - 1;
5558 while (cp1 > retspec)
5563 if (*(cp1-1) != '^')
5568 if (*cp1 == '.') *cp1 = ']';
5570 memmove(cp1+8,cp1+1,retspec+dirlen-cp1);
5571 memmove(cp1+1,"000000]",7);
5575 memmove(retspec+dirlen,cp1+2,retlen-dirlen);
5576 retspec[retlen] = '\0';
5577 /* Convert last '.' to ']' */
5578 cp1 = retspec+retlen-1;
5579 while (*cp != '[') {
5582 /* Do not trip on extra dots in ODS-5 directories */
5583 if ((cp1 == retspec) || (*(cp1-1) != '^'))
5587 if (*cp1 == '.') *cp1 = ']';
5589 memmove(cp1+8,cp1+1,retspec+dirlen-cp1);
5590 memmove(cp1+1,"000000]",7);
5594 else { /* This is a top-level dir. Add the MFD to the path. */
5595 if (buf) retspec = buf;
5596 else if (ts) Newx(retspec,retlen+16,char);
5597 else retspec = __fileify_retbuf;
5600 while ((*cp1 != ':') && (*cp1 != '\0')) *(cp2++) = *(cp1++);
5601 strcpy(cp2,":[000000]");
5606 sts = rms_free_search_context(&dirfab);
5607 /* We've set up the string up through the filename. Add the
5608 type and version, and we're done. */
5609 strcat(retspec,".DIR;1");
5611 /* $PARSE may have upcased filespec, so convert output to lower
5612 * case if input contained any lowercase characters. */
5613 if (haslower && !decc_efs_case_preserve) __mystrtolower(retspec);
5614 PerlMem_free(trndir);
5616 PerlMem_free(vmsdir);
5619 } /* end of do_fileify_dirspec() */
5621 /* External entry points */
5622 char *Perl_fileify_dirspec(pTHX_ const char *dir, char *buf)
5623 { return do_fileify_dirspec(dir,buf,0,NULL); }
5624 char *Perl_fileify_dirspec_ts(pTHX_ const char *dir, char *buf)
5625 { return do_fileify_dirspec(dir,buf,1,NULL); }
5626 char *Perl_fileify_dirspec_utf8(pTHX_ const char *dir, char *buf, int * utf8_fl)
5627 { return do_fileify_dirspec(dir,buf,0,utf8_fl); }
5628 char *Perl_fileify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int * utf8_fl)
5629 { return do_fileify_dirspec(dir,buf,1,utf8_fl); }
5631 /*{{{ char *pathify_dirspec[_ts](char *path, char *buf)*/
5632 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int * utf8_fl)
5634 static char __pathify_retbuf[VMS_MAXRSS];
5635 unsigned long int retlen;
5636 char *retpath, *cp1, *cp2, *trndir;
5637 unsigned short int trnlnm_iter_count;
5640 if (utf8_fl != NULL)
5643 if (!dir || !*dir) {
5644 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
5647 trndir = PerlMem_malloc(VMS_MAXRSS);
5648 if (trndir == NULL) _ckvmssts(SS$_INSFMEM);
5649 if (*dir) strcpy(trndir,dir);
5650 else getcwd(trndir,VMS_MAXRSS - 1);
5652 trnlnm_iter_count = 0;
5653 while (!strpbrk(trndir,"/]:>") && !no_translate_barewords
5654 && my_trnlnm(trndir,trndir,0)) {
5655 trnlnm_iter_count++;
5656 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
5657 trnlen = strlen(trndir);
5659 /* Trap simple rooted lnms, and return lnm:[000000] */
5660 if (!strcmp(trndir+trnlen-2,".]")) {
5661 if (buf) retpath = buf;
5662 else if (ts) Newx(retpath,strlen(dir)+10,char);
5663 else retpath = __pathify_retbuf;
5664 strcpy(retpath,dir);
5665 strcat(retpath,":[000000]");
5666 PerlMem_free(trndir);
5671 /* At this point we do not work with *dir, but the copy in
5672 * *trndir that is modifiable.
5675 if (!strpbrk(trndir,"]:>")) { /* Unix-style path or plain name */
5676 if (*trndir == '.' && (*(trndir+1) == '\0' ||
5677 (*(trndir+1) == '.' && *(trndir+2) == '\0')))
5678 retlen = 2 + (*(trndir+1) != '\0');
5680 if ( !(cp1 = strrchr(trndir,'/')) &&
5681 !(cp1 = strrchr(trndir,']')) &&
5682 !(cp1 = strrchr(trndir,'>')) ) cp1 = trndir;
5683 if ((cp2 = strchr(cp1,'.')) != NULL &&
5684 (*(cp2-1) != '/' || /* Trailing '.', '..', */
5685 !(*(cp2+1) == '\0' || /* or '...' are dirs. */
5686 (*(cp2+1) == '.' && *(cp2+2) == '\0') ||
5687 (*(cp2+1) == '.' && *(cp2+2) == '.' && *(cp2+3) == '\0')))) {
5690 /* For EFS or ODS-5 look for the last dot */
5691 if (decc_efs_charset) {
5692 cp2 = strrchr(cp1,'.');
5694 if (vms_process_case_tolerant) {
5695 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
5696 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
5697 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
5698 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
5699 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
5700 (ver || *cp3)))))) {
5701 PerlMem_free(trndir);
5703 set_vaxc_errno(RMS$_DIR);
5708 if (!*(cp2+1) || *(cp2+1) != 'D' || /* Wrong type. */
5709 !*(cp2+2) || *(cp2+2) != 'I' || /* Bzzt. */
5710 !*(cp2+3) || *(cp2+3) != 'R' ||
5711 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
5712 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
5713 (ver || *cp3)))))) {
5714 PerlMem_free(trndir);
5716 set_vaxc_errno(RMS$_DIR);
5720 retlen = cp2 - trndir + 1;
5722 else { /* No file type present. Treat the filename as a directory. */
5723 retlen = strlen(trndir) + 1;
5726 if (buf) retpath = buf;
5727 else if (ts) Newx(retpath,retlen+1,char);
5728 else retpath = __pathify_retbuf;
5729 strncpy(retpath, trndir, retlen-1);
5730 if (retpath[retlen-2] != '/') { /* If the path doesn't already end */
5731 retpath[retlen-1] = '/'; /* with '/', add it. */
5732 retpath[retlen] = '\0';
5734 else retpath[retlen-1] = '\0';
5736 else { /* VMS-style directory spec */
5738 unsigned long int sts, cmplen, haslower;
5739 struct FAB dirfab = cc$rms_fab;
5741 rms_setup_nam(savnam);
5742 rms_setup_nam(dirnam);
5744 /* If we've got an explicit filename, we can just shuffle the string. */
5745 if ( ( (cp1 = strrchr(trndir,']')) != NULL ||
5746 (cp1 = strrchr(trndir,'>')) != NULL ) && *(cp1+1)) {
5747 if ((cp2 = strchr(cp1,'.')) != NULL) {
5749 if (vms_process_case_tolerant) {
5750 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
5751 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
5752 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
5753 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
5754 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
5755 (ver || *cp3)))))) {
5756 PerlMem_free(trndir);
5758 set_vaxc_errno(RMS$_DIR);
5763 if (!*(cp2+1) || *(cp2+1) != 'D' || /* Wrong type. */
5764 !*(cp2+2) || *(cp2+2) != 'I' || /* Bzzt. */
5765 !*(cp2+3) || *(cp2+3) != 'R' ||
5766 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
5767 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
5768 (ver || *cp3)))))) {
5769 PerlMem_free(trndir);
5771 set_vaxc_errno(RMS$_DIR);
5776 else { /* No file type, so just draw name into directory part */
5777 for (cp2 = cp1; *cp2; cp2++) ;
5780 *(cp2+1) = '\0'; /* OK; trndir is guaranteed to be long enough */
5782 /* We've now got a VMS 'path'; fall through */
5785 dirlen = strlen(trndir);
5786 if (trndir[dirlen-1] == ']' ||
5787 trndir[dirlen-1] == '>' ||
5788 trndir[dirlen-1] == ':') { /* It's already a VMS 'path' */
5789 if (buf) retpath = buf;
5790 else if (ts) Newx(retpath,strlen(trndir)+1,char);
5791 else retpath = __pathify_retbuf;
5792 strcpy(retpath,trndir);
5793 PerlMem_free(trndir);
5796 rms_set_fna(dirfab, dirnam, trndir, dirlen);
5797 esa = PerlMem_malloc(VMS_MAXRSS);
5798 if (esa == NULL) _ckvmssts(SS$_INSFMEM);
5799 rms_set_dna(dirfab, dirnam, ".DIR;1", 6);
5800 rms_bind_fab_nam(dirfab, dirnam);
5801 rms_set_esa(dirfab, dirnam, esa, VMS_MAXRSS - 1);
5802 #ifdef NAM$M_NO_SHORT_UPCASE
5803 if (decc_efs_case_preserve)
5804 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
5807 for (cp = trndir; *cp; cp++)
5808 if (islower(*cp)) { haslower = 1; break; }
5810 if (!(sts = (sys$parse(&dirfab)& STS$K_SUCCESS))) {
5811 if ((dirfab.fab$l_sts == RMS$_DIR) || (dirfab.fab$l_sts == RMS$_DNF)) {
5812 rms_set_nam_nop(dirnam, NAM$M_SYNCHK);
5813 sts = sys$parse(&dirfab) & STS$K_SUCCESS;
5816 PerlMem_free(trndir);
5819 set_vaxc_errno(dirfab.fab$l_sts);
5825 /* Does the file really exist? */
5826 if (!(sys$search(&dirfab)&STS$K_SUCCESS)) {
5827 if (dirfab.fab$l_sts != RMS$_FNF) {
5829 sts1 = rms_free_search_context(&dirfab);
5830 PerlMem_free(trndir);
5833 set_vaxc_errno(dirfab.fab$l_sts);
5836 dirnam = savnam; /* No; just work with potential name */
5839 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_TYPE)) { /* Was type specified? */
5840 /* Yep; check version while we're at it, if it's there. */
5841 cmplen = rms_is_nam_fnb(dirnam, NAM$M_EXP_VER) ? 6 : 4;
5842 if (strncmp(rms_nam_typel(dirnam),".DIR;1",cmplen)) {
5844 /* Something other than .DIR[;1]. Bzzt. */
5845 sts2 = rms_free_search_context(&dirfab);
5846 PerlMem_free(trndir);
5849 set_vaxc_errno(RMS$_DIR);
5853 /* OK, the type was fine. Now pull any file name into the
5855 if ((cp1 = strrchr(esa,']'))) *(rms_nam_typel(dirnam)) = ']';
5857 cp1 = strrchr(esa,'>');
5858 *(rms_nam_typel(dirnam)) = '>';
5861 *(rms_nam_typel(dirnam) + 1) = '\0';
5862 retlen = (rms_nam_typel(dirnam)) - esa + 2;
5863 if (buf) retpath = buf;
5864 else if (ts) Newx(retpath,retlen,char);
5865 else retpath = __pathify_retbuf;
5866 strcpy(retpath,esa);
5868 sts = rms_free_search_context(&dirfab);
5869 /* $PARSE may have upcased filespec, so convert output to lower
5870 * case if input contained any lowercase characters. */
5871 if (haslower && !decc_efs_case_preserve) __mystrtolower(retpath);
5874 PerlMem_free(trndir);
5876 } /* end of do_pathify_dirspec() */
5878 /* External entry points */
5879 char *Perl_pathify_dirspec(pTHX_ const char *dir, char *buf)
5880 { return do_pathify_dirspec(dir,buf,0,NULL); }
5881 char *Perl_pathify_dirspec_ts(pTHX_ const char *dir, char *buf)
5882 { return do_pathify_dirspec(dir,buf,1,NULL); }
5883 char *Perl_pathify_dirspec_utf8(pTHX_ const char *dir, char *buf, int *utf8_fl)
5884 { return do_pathify_dirspec(dir,buf,0,utf8_fl); }
5885 char *Perl_pathify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int *utf8_fl)
5886 { return do_pathify_dirspec(dir,buf,1,utf8_fl); }
5888 /*{{{ char *tounixspec[_ts](char *spec, char *buf, int *)*/
5889 static char *mp_do_tounixspec(pTHX_ const char *spec, char *buf, int ts, int * utf8_fl)
5891 static char __tounixspec_retbuf[VMS_MAXRSS];
5892 char *dirend, *rslt, *cp1, *cp3, *tmp;
5894 int devlen, dirlen, retlen = VMS_MAXRSS;
5895 int expand = 1; /* guarantee room for leading and trailing slashes */
5896 unsigned short int trnlnm_iter_count;
5898 if (utf8_fl != NULL)
5901 if (spec == NULL) return NULL;
5902 if (strlen(spec) > (VMS_MAXRSS-1)) return NULL;
5903 if (buf) rslt = buf;
5905 Newx(rslt, VMS_MAXRSS, char);
5907 else rslt = __tounixspec_retbuf;
5909 /* New VMS specific format needs translation
5910 * glob passes filenames with trailing '\n' and expects this preserved.
5912 if (decc_posix_compliant_pathnames) {
5913 if (strncmp(spec, "\"^UP^", 5) == 0) {
5919 tunix = PerlMem_malloc(VMS_MAXRSS);
5920 if (tunix == NULL) _ckvmssts(SS$_INSFMEM);
5921 strcpy(tunix, spec);
5922 tunix_len = strlen(tunix);
5924 if (tunix[tunix_len - 1] == '\n') {
5925 tunix[tunix_len - 1] = '\"';
5926 tunix[tunix_len] = '\0';
5930 uspec = decc$translate_vms(tunix);
5931 PerlMem_free(tunix);
5932 if ((int)uspec > 0) {
5938 /* If we can not translate it, makemaker wants as-is */
5946 cmp_rslt = 0; /* Presume VMS */
5947 cp1 = strchr(spec, '/');
5951 /* Look for EFS ^/ */
5952 if (decc_efs_charset) {
5953 while (cp1 != NULL) {
5956 /* Found illegal VMS, assume UNIX */
5961 cp1 = strchr(cp1, '/');
5965 /* Look for "." and ".." */
5966 if (decc_filename_unix_report) {
5967 if (spec[0] == '.') {
5968 if ((spec[1] == '\0') || (spec[1] == '\n')) {
5972 if ((spec[1] == '.') && ((spec[2] == '\0') || (spec[2] == '\n'))) {
5978 /* This is already UNIX or at least nothing VMS understands */
5986 dirend = strrchr(spec,']');
5987 if (dirend == NULL) dirend = strrchr(spec,'>');
5988 if (dirend == NULL) dirend = strchr(spec,':');
5989 if (dirend == NULL) {
5994 /* Special case 1 - sys$posix_root = / */
5995 #if __CRTL_VER >= 70000000
5996 if (!decc_disable_posix_root) {
5997 if (strncasecmp(spec, "SYS$POSIX_ROOT:", 15) == 0) {
6005 /* Special case 2 - Convert NLA0: to /dev/null */
6006 #if __CRTL_VER < 70000000
6007 cmp_rslt = strncmp(spec,"NLA0:", 5);
6009 cmp_rslt = strncmp(spec,"nla0:", 5);
6011 cmp_rslt = strncasecmp(spec,"NLA0:", 5);
6013 if (cmp_rslt == 0) {
6014 strcpy(rslt, "/dev/null");
6017 if (spec[6] != '\0') {
6024 /* Also handle special case "SYS$SCRATCH:" */
6025 #if __CRTL_VER < 70000000
6026 cmp_rslt = strncmp(spec,"SYS$SCRATCH:", 12);
6028 cmp_rslt = strncmp(spec,"sys$scratch:", 12);
6030 cmp_rslt = strncasecmp(spec,"SYS$SCRATCH:", 12);
6032 tmp = PerlMem_malloc(VMS_MAXRSS);
6033 if (tmp == NULL) _ckvmssts(SS$_INSFMEM);
6034 if (cmp_rslt == 0) {
6037 islnm = my_trnlnm(tmp, "TMP", 0);
6039 strcpy(rslt, "/tmp");
6042 if (spec[12] != '\0') {
6050 if (*cp2 != '[' && *cp2 != '<') {
6053 else { /* the VMS spec begins with directories */
6055 if (*cp2 == ']' || *cp2 == '>') {
6056 *(cp1++) = '.'; *(cp1++) = '/'; *(cp1++) = '\0';
6060 else if ( *cp2 != '^' && *cp2 != '.' && *cp2 != '-') { /* add the implied device */
6061 if (getcwd(tmp, VMS_MAXRSS-1 ,1) == NULL) {
6062 if (ts) Safefree(rslt);
6066 trnlnm_iter_count = 0;
6069 while (*cp3 != ':' && *cp3) cp3++;
6071 if (strchr(cp3,']') != NULL) break;
6072 trnlnm_iter_count++;
6073 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER+1) break;
6074 } while (vmstrnenv(tmp,tmp,0,fildev,0));
6076 ((devlen = strlen(tmp)) + (dirlen = strlen(cp2)) + 1 > retlen)) {
6077 retlen = devlen + dirlen;
6078 Renew(rslt,retlen+1+2*expand,char);
6084 *(cp1++) = *(cp3++);
6085 if (cp1 - rslt > (VMS_MAXRSS - 1) && !ts && !buf) {
6087 return NULL; /* No room */
6092 if ((*cp2 == '^')) {
6093 /* EFS file escape, pass the next character as is */
6094 /* Fix me: HEX encoding for UNICODE not implemented */
6097 else if ( *cp2 == '.') {
6098 if (*(cp2+1) == '.' && *(cp2+2) == '.') {
6099 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
6106 for (; cp2 <= dirend; cp2++) {
6107 if ((*cp2 == '^')) {
6108 /* EFS file escape, pass the next character as is */
6109 /* Fix me: HEX encoding for UNICODE not implemented */
6110 *(cp1++) = *(++cp2);
6111 /* An escaped dot stays as is -- don't convert to slash */
6112 if (*cp2 == '.') cp2++;
6116 if (*(cp2+1) == '[') cp2++;
6118 else if (*cp2 == ']' || *cp2 == '>') {
6119 if (*(cp1-1) != '/') *(cp1++) = '/'; /* Don't double after ellipsis */
6121 else if ((*cp2 == '.') && (*cp2-1 != '^')) {
6123 if (*(cp2+1) == ']' || *(cp2+1) == '>') {
6124 while (*(cp2+1) == ']' || *(cp2+1) == '>' ||
6125 *(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
6126 if (!strncmp(cp2,"[000000",7) && (*(cp2+7) == ']' ||
6127 *(cp2+7) == '>' || *(cp2+7) == '.')) cp2 += 7;
6129 else if ( *(cp2+1) == '.' && *(cp2+2) == '.') {
6130 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) ='/';
6134 else if (*cp2 == '-') {
6135 if (*(cp2-1) == '[' || *(cp2-1) == '<' || *(cp2-1) == '.') {
6136 while (*cp2 == '-') {
6138 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
6140 if (*cp2 != '.' && *cp2 != ']' && *cp2 != '>') { /* we don't allow */
6141 if (ts) Safefree(rslt); /* filespecs like */
6142 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); /* [fred.--foo.bar] */
6146 else *(cp1++) = *cp2;
6148 else *(cp1++) = *cp2;
6151 if ((*cp2 == '^') && (*(cp2+1) == '.')) cp2++; /* '^.' --> '.' */
6152 *(cp1++) = *(cp2++);
6156 /* This still leaves /000000/ when working with a
6157 * VMS device root or concealed root.
6163 ulen = strlen(rslt);
6165 /* Get rid of "000000/ in rooted filespecs */
6167 zeros = strstr(rslt, "/000000/");
6168 if (zeros != NULL) {
6170 mlen = ulen - (zeros - rslt) - 7;
6171 memmove(zeros, &zeros[7], mlen);
6180 } /* end of do_tounixspec() */
6182 /* External entry points */
6183 char *Perl_tounixspec(pTHX_ const char *spec, char *buf)
6184 { return do_tounixspec(spec,buf,0, NULL); }
6185 char *Perl_tounixspec_ts(pTHX_ const char *spec, char *buf)
6186 { return do_tounixspec(spec,buf,1, NULL); }
6187 char *Perl_tounixspec_utf8(pTHX_ const char *spec, char *buf, int * utf8_fl)
6188 { return do_tounixspec(spec,buf,0, utf8_fl); }
6189 char *Perl_tounixspec_utf8_ts(pTHX_ const char *spec, char *buf, int * utf8_fl)
6190 { return do_tounixspec(spec,buf,1, utf8_fl); }
6192 #if __CRTL_VER >= 70200000 && !defined(__VAX)
6195 This procedure is used to identify if a path is based in either
6196 the old SYS$POSIX_ROOT: or the new 8.3 RMS based POSIX root, and
6197 it returns the OpenVMS format directory for it.
6199 It is expecting specifications of only '/' or '/xxxx/'
6201 If a posix root does not exist, or 'xxxx' is not a directory
6202 in the posix root, it returns a failure.
6204 FIX-ME: xxxx could be in UTF-8 and needs to be returned in VTF-7.
6206 It is used only internally by posix_to_vmsspec_hardway().
6209 static int posix_root_to_vms
6210 (char *vmspath, int vmspath_len,
6211 const char *unixpath,
6212 const int * utf8_fl) {
6214 struct FAB myfab = cc$rms_fab;
6215 struct NAML mynam = cc$rms_naml;
6216 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
6217 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
6224 unixlen = strlen(unixpath);
6230 #if __CRTL_VER >= 80200000
6231 /* If not a posix spec already, convert it */
6232 if (decc_posix_compliant_pathnames) {
6233 if (strncmp(unixpath,"\"^UP^",5) != 0) {
6234 sprintf(vmspath,"\"^UP^%s\"",unixpath);
6237 /* This is already a VMS specification, no conversion */
6239 strncpy(vmspath,unixpath, vmspath_len);
6248 /* Check to see if this is under the POSIX root */
6249 if (decc_disable_posix_root) {
6253 /* Skip leading / */
6254 if (unixpath[0] == '/') {
6260 strcpy(vmspath,"SYS$POSIX_ROOT:");
6262 /* If this is only the / , or blank, then... */
6263 if (unixpath[0] == '\0') {
6264 /* by definition, this is the answer */
6268 /* Need to look up a directory */
6272 /* Copy and add '^' escape characters as needed */
6275 while (unixpath[i] != 0) {
6278 j += copy_expand_unix_filename_escape
6279 (&vmspath[j], &unixpath[i], &k, utf8_fl);
6283 path_len = strlen(vmspath);
6284 if (vmspath[path_len - 1] == '/')
6286 vmspath[path_len] = ']';
6288 vmspath[path_len] = '\0';
6291 vmspath[vmspath_len] = 0;
6292 if (unixpath[unixlen - 1] == '/')
6294 esa = PerlMem_malloc(VMS_MAXRSS);
6295 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6296 myfab.fab$l_fna = vmspath;
6297 myfab.fab$b_fns = strlen(vmspath);
6298 myfab.fab$l_naml = &mynam;
6299 mynam.naml$l_esa = NULL;
6300 mynam.naml$b_ess = 0;
6301 mynam.naml$l_long_expand = esa;
6302 mynam.naml$l_long_expand_alloc = (unsigned char) VMS_MAXRSS - 1;
6303 mynam.naml$l_rsa = NULL;
6304 mynam.naml$b_rss = 0;
6305 if (decc_efs_case_preserve)
6306 mynam.naml$b_nop |= NAM$M_NO_SHORT_UPCASE;
6307 #ifdef NAML$M_OPEN_SPECIAL
6308 mynam.naml$l_input_flags |= NAML$M_OPEN_SPECIAL;
6311 /* Set up the remaining naml fields */
6312 sts = sys$parse(&myfab);
6314 /* It failed! Try again as a UNIX filespec */
6320 /* get the Device ID and the FID */
6321 sts = sys$search(&myfab);
6322 /* on any failure, returned the POSIX ^UP^ filespec */
6327 specdsc.dsc$a_pointer = vmspath;
6328 specdsc.dsc$w_length = vmspath_len;
6330 dvidsc.dsc$a_pointer = &mynam.naml$t_dvi[1];
6331 dvidsc.dsc$w_length = mynam.naml$t_dvi[0];
6332 sts = lib$fid_to_name
6333 (&dvidsc, mynam.naml$w_fid, &specdsc, &specdsc.dsc$w_length);
6335 /* on any failure, returned the POSIX ^UP^ filespec */
6337 /* This can happen if user does not have permission to read directories */
6338 if (strncmp(unixpath,"\"^UP^",5) != 0)
6339 sprintf(vmspath,"\"^UP^%s\"",unixpath);
6341 strcpy(vmspath, unixpath);
6344 vmspath[specdsc.dsc$w_length] = 0;
6346 /* Are we expecting a directory? */
6347 if (dir_flag != 0) {
6353 i = specdsc.dsc$w_length - 1;
6357 /* Version must be '1' */
6358 if (vmspath[i--] != '1')
6360 /* Version delimiter is one of ".;" */
6361 if ((vmspath[i] != '.') && (vmspath[i] != ';'))
6364 if (vmspath[i--] != 'R')
6366 if (vmspath[i--] != 'I')
6368 if (vmspath[i--] != 'D')
6370 if (vmspath[i--] != '.')
6372 eptr = &vmspath[i+1];
6374 if ((vmspath[i] == ']') || (vmspath[i] == '>')) {
6375 if (vmspath[i-1] != '^') {
6383 /* Get rid of 6 imaginary zero directory filename */
6384 vmspath[i+1] = '\0';
6388 if (vmspath[i] == '0')
6402 /* /dev/mumble needs to be handled special.
6403 /dev/null becomes NLA0:, And there is the potential for other stuff
6404 like /dev/tty which may need to be mapped to something.
6408 slash_dev_special_to_vms
6409 (const char * unixptr,
6419 nextslash = strchr(unixptr, '/');
6420 len = strlen(unixptr);
6421 if (nextslash != NULL)
6422 len = nextslash - unixptr;
6423 cmp = strncmp("null", unixptr, 5);
6425 if (vmspath_len >= 6) {
6426 strcpy(vmspath, "_NLA0:");
6433 /* The built in routines do not understand perl's special needs, so
6434 doing a manual conversion from UNIX to VMS
6436 If the utf8_fl is not null and points to a non-zero value, then
6437 treat 8 bit characters as UTF-8.
6439 The sequence starting with '$(' and ending with ')' will be passed
6440 through with out interpretation instead of being escaped.
6443 static int posix_to_vmsspec_hardway
6444 (char *vmspath, int vmspath_len,
6445 const char *unixpath,
6450 const char *unixptr;
6451 const char *unixend;
6453 const char *lastslash;
6454 const char *lastdot;
6460 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
6461 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
6463 if (utf8_fl != NULL)
6469 /* Ignore leading "/" characters */
6470 while((unixptr[0] == '/') && (unixptr[1] == '/')) {
6473 unixlen = strlen(unixptr);
6475 /* Do nothing with blank paths */
6482 /* This could have a "^UP^ on the front */
6483 if (strncmp(unixptr,"\"^UP^",5) == 0) {
6489 lastslash = strrchr(unixptr,'/');
6490 lastdot = strrchr(unixptr,'.');
6491 unixend = strrchr(unixptr,'\"');
6492 if (!quoted || !((unixend != NULL) && (unixend[1] == '\0'))) {
6493 unixend = unixptr + unixlen;
6496 /* last dot is last dot or past end of string */
6497 if (lastdot == NULL)
6498 lastdot = unixptr + unixlen;
6500 /* if no directories, set last slash to beginning of string */
6501 if (lastslash == NULL) {
6502 lastslash = unixptr;
6505 /* Watch out for trailing "." after last slash, still a directory */
6506 if ((lastslash[1] == '.') && (lastslash[2] == '\0')) {
6507 lastslash = unixptr + unixlen;
6510 /* Watch out for traiing ".." after last slash, still a directory */
6511 if ((lastslash[1] == '.')&&(lastslash[2] == '.')&&(lastslash[3] == '\0')) {
6512 lastslash = unixptr + unixlen;
6515 /* dots in directories are aways escaped */
6516 if (lastdot < lastslash)
6517 lastdot = unixptr + unixlen;
6520 /* if (unixptr < lastslash) then we are in a directory */
6527 /* Start with the UNIX path */
6528 if (*unixptr != '/') {
6529 /* relative paths */
6531 /* If allowing logical names on relative pathnames, then handle here */
6532 if ((unixptr[0] != '.') && !decc_disable_to_vms_logname_translation &&
6533 !decc_posix_compliant_pathnames) {
6539 /* Find the next slash */
6540 nextslash = strchr(unixptr,'/');
6542 esa = PerlMem_malloc(vmspath_len);
6543 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6545 trn = PerlMem_malloc(VMS_MAXRSS);
6546 if (trn == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6548 if (nextslash != NULL) {
6550 seg_len = nextslash - unixptr;
6551 strncpy(esa, unixptr, seg_len);
6555 strcpy(esa, unixptr);
6556 seg_len = strlen(unixptr);
6558 /* trnlnm(section) */
6559 islnm = vmstrnenv(esa, trn, 0, fildev, 0);
6562 /* Now fix up the directory */
6564 /* Split up the path to find the components */
6565 sts = vms_split_path
6584 /* A logical name must be a directory or the full
6585 specification. It is only a full specification if
6586 it is the only component */
6587 if ((unixptr[seg_len] == '\0') ||
6588 (unixptr[seg_len+1] == '\0')) {
6590 /* Is a directory being required? */
6591 if (((n_len + e_len) != 0) && (dir_flag !=0)) {
6592 /* Not a logical name */
6597 if ((unixptr[seg_len] == '/') || (dir_flag != 0)) {
6598 /* This must be a directory */
6599 if (((n_len + e_len) == 0)&&(seg_len <= vmspath_len)) {
6600 strcpy(vmsptr, esa);
6601 vmslen=strlen(vmsptr);
6602 vmsptr[vmslen] = ':';
6604 vmsptr[vmslen] = '\0';
6612 /* must be dev/directory - ignore version */
6613 if ((n_len + e_len) != 0)
6616 /* transfer the volume */
6617 if (v_len > 0 && ((v_len + vmslen) < vmspath_len)) {
6618 strncpy(vmsptr, v_spec, v_len);
6624 /* unroot the rooted directory */
6625 if ((r_len > 0) && ((r_len + d_len + vmslen) < vmspath_len)) {
6627 r_spec[r_len - 1] = ']';
6629 /* This should not be there, but nothing is perfect */
6631 cmp = strcmp(&r_spec[1], "000000.");
6641 strncpy(vmsptr, r_spec, r_len);
6647 /* Bring over the directory. */
6649 ((d_len + vmslen) < vmspath_len)) {
6651 d_spec[d_len - 1] = ']';
6653 cmp = strcmp(&d_spec[1], "000000.");
6664 /* Remove the redundant root */
6672 strncpy(vmsptr, d_spec, d_len);
6686 if (lastslash > unixptr) {
6689 /* skip leading ./ */
6691 while ((unixptr[0] == '.') && (unixptr[1] == '/')) {
6697 /* Are we still in a directory? */
6698 if (unixptr <= lastslash) {
6703 /* if not backing up, then it is relative forward. */
6704 if (!((*unixptr == '.') && (unixptr[1] == '.') &&
6705 ((unixptr[2] == '/') || (&unixptr[2] == unixend)))) {
6713 /* Perl wants an empty directory here to tell the difference
6714 * between a DCL commmand and a filename
6723 /* Handle two special files . and .. */
6724 if (unixptr[0] == '.') {
6725 if (&unixptr[1] == unixend) {
6732 if ((unixptr[1] == '.') && (&unixptr[2] == unixend)) {
6743 else { /* Absolute PATH handling */
6747 /* Need to find out where root is */
6749 /* In theory, this procedure should never get an absolute POSIX pathname
6750 * that can not be found on the POSIX root.
6751 * In practice, that can not be relied on, and things will show up
6752 * here that are a VMS device name or concealed logical name instead.
6753 * So to make things work, this procedure must be tolerant.
6755 esa = PerlMem_malloc(vmspath_len);
6756 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6759 nextslash = strchr(&unixptr[1],'/');
6761 if (nextslash != NULL) {
6763 seg_len = nextslash - &unixptr[1];
6764 strncpy(vmspath, unixptr, seg_len + 1);
6765 vmspath[seg_len+1] = 0;
6768 cmp = strncmp(vmspath, "dev", 4);
6770 sts = slash_dev_special_to_vms(unixptr, vmspath, vmspath_len);
6771 if (sts = SS$_NORMAL)
6775 sts = posix_root_to_vms(esa, vmspath_len, vmspath, utf8_fl);
6778 if ($VMS_STATUS_SUCCESS(sts)) {
6779 /* This is verified to be a real path */
6781 sts = posix_root_to_vms(esa, vmspath_len, "/", NULL);
6782 if ($VMS_STATUS_SUCCESS(sts)) {
6783 strcpy(vmspath, esa);
6784 vmslen = strlen(vmspath);
6785 vmsptr = vmspath + vmslen;
6787 if (unixptr < lastslash) {
6796 cmp = strcmp(rptr,"000000.");
6801 } /* removing 6 zeros */
6802 } /* vmslen < 7, no 6 zeros possible */
6803 } /* Not in a directory */
6804 } /* Posix root found */
6806 /* No posix root, fall back to default directory */
6807 strcpy(vmspath, "SYS$DISK:[");
6808 vmsptr = &vmspath[10];
6810 if (unixptr > lastslash) {
6819 } /* end of verified real path handling */
6824 /* Ok, we have a device or a concealed root that is not in POSIX
6825 * or we have garbage. Make the best of it.
6828 /* Posix to VMS destroyed this, so copy it again */
6829 strncpy(vmspath, &unixptr[1], seg_len);
6830 vmspath[seg_len] = 0;
6832 vmsptr = &vmsptr[vmslen];
6835 /* Now do we need to add the fake 6 zero directory to it? */
6837 if ((*lastslash == '/') && (nextslash < lastslash)) {
6838 /* No there is another directory */
6845 /* now we have foo:bar or foo:[000000]bar to decide from */
6846 islnm = vmstrnenv(vmspath, esa, 0, fildev, 0);
6848 if (!islnm && !decc_posix_compliant_pathnames) {
6850 cmp = strncmp("bin", vmspath, 4);
6852 /* bin => SYS$SYSTEM: */
6853 islnm = vmstrnenv("SYS$SYSTEM:", esa, 0, fildev, 0);
6856 /* tmp => SYS$SCRATCH: */
6857 cmp = strncmp("tmp", vmspath, 4);
6859 islnm = vmstrnenv("SYS$SCRATCH:", esa, 0, fildev, 0);
6864 trnend = islnm ? islnm - 1 : 0;
6866 /* if this was a logical name, ']' or '>' must be present */
6867 /* if not a logical name, then assume a device and hope. */
6868 islnm = trnend ? (esa[trnend] == ']' || esa[trnend] == '>') : 0;
6870 /* if log name and trailing '.' then rooted - treat as device */
6871 add_6zero = islnm ? (esa[trnend-1] == '.') : 0;
6873 /* Fix me, if not a logical name, a device lookup should be
6874 * done to see if the device is file structured. If the device
6875 * is not file structured, the 6 zeros should not be put on.
6877 * As it is, perl is occasionally looking for dev:[000000]tty.
6878 * which looks a little strange.
6880 * Not that easy to detect as "/dev" may be file structured with
6881 * special device files.
6884 if ((add_6zero == 0) && (*nextslash == '/') &&
6885 (&nextslash[1] == unixend)) {
6886 /* No real directory present */
6891 /* Put the device delimiter on */
6894 unixptr = nextslash;
6897 /* Start directory if needed */
6898 if (!islnm || add_6zero) {
6904 /* add fake 000000] if needed */
6917 } /* non-POSIX translation */
6919 } /* End of relative/absolute path handling */
6921 while ((unixptr <= unixend) && (vmslen < vmspath_len)){
6928 if (dir_start != 0) {
6930 /* First characters in a directory are handled special */
6931 while ((*unixptr == '/') ||
6932 ((*unixptr == '.') &&
6933 ((unixptr[1]=='.') || (unixptr[1]=='/') ||
6934 (&unixptr[1]==unixend)))) {
6939 /* Skip redundant / in specification */
6940 while ((*unixptr == '/') && (dir_start != 0)) {
6943 if (unixptr == lastslash)
6946 if (unixptr == lastslash)
6949 /* Skip redundant ./ characters */
6950 while ((*unixptr == '.') &&
6951 ((unixptr[1] == '/')||(&unixptr[1] == unixend))) {
6954 if (unixptr == lastslash)
6956 if (*unixptr == '/')
6959 if (unixptr == lastslash)
6962 /* Skip redundant ../ characters */
6963 while ((*unixptr == '.') && (unixptr[1] == '.') &&
6964 ((unixptr[2] == '/') || (&unixptr[2] == unixend))) {
6965 /* Set the backing up flag */
6971 unixptr++; /* first . */
6972 unixptr++; /* second . */
6973 if (unixptr == lastslash)
6975 if (*unixptr == '/') /* The slash */
6978 if (unixptr == lastslash)
6981 /* To do: Perl expects /.../ to be translated to [...] on VMS */
6982 /* Not needed when VMS is pretending to be UNIX. */
6984 /* Is this loop stuck because of too many dots? */
6985 if (loop_flag == 0) {
6986 /* Exit the loop and pass the rest through */
6991 /* Are we done with directories yet? */
6992 if (unixptr >= lastslash) {
6994 /* Watch out for trailing dots */
7003 if (*unixptr == '/')
7007 /* Have we stopped backing up? */
7012 /* dir_start continues to be = 1 */
7014 if (*unixptr == '-') {
7016 *vmsptr++ = *unixptr++;
7020 /* Now are we done with directories yet? */
7021 if (unixptr >= lastslash) {
7023 /* Watch out for trailing dots */
7039 if (unixptr >= unixend)
7042 /* Normal characters - More EFS work probably needed */
7048 /* remove multiple / */
7049 while (unixptr[1] == '/') {
7052 if (unixptr == lastslash) {
7053 /* Watch out for trailing dots */
7065 /* To do: Perl expects /.../ to be translated to [...] on VMS */
7066 /* Not needed when VMS is pretending to be UNIX. */
7070 if (unixptr != unixend)
7075 if ((unixptr < lastdot) || (unixptr < lastslash) ||
7076 (&unixptr[1] == unixend)) {
7082 /* trailing dot ==> '^..' on VMS */
7083 if (unixptr == unixend) {
7091 *vmsptr++ = *unixptr++;
7095 if (quoted && (&unixptr[1] == unixend)) {
7099 in_cnt = copy_expand_unix_filename_escape
7100 (vmsptr, unixptr, &out_cnt, utf8_fl);
7110 in_cnt = copy_expand_unix_filename_escape
7111 (vmsptr, unixptr, &out_cnt, utf8_fl);
7118 /* Make sure directory is closed */
7119 if (unixptr == lastslash) {
7121 vmsptr2 = vmsptr - 1;
7123 if (*vmsptr2 != ']') {
7126 /* directories do not end in a dot bracket */
7127 if (*vmsptr2 == '.') {
7131 if (*vmsptr2 != '^') {
7132 vmsptr--; /* back up over the dot */
7140 /* Add a trailing dot if a file with no extension */
7141 vmsptr2 = vmsptr - 1;
7143 (*vmsptr2 != ']') && (*vmsptr2 != '*') && (*vmsptr2 != '%') &&
7144 (*vmsptr2 != ')') && (*lastdot != '.')) {
7155 /* Eventual routine to convert a UTF-8 specification to VTF-7. */
7156 static char * utf8_to_vtf7(char * rslt, const char * path, int *utf8_fl)
7161 /* If a UTF8 flag is being passed, honor it */
7163 if (utf8_fl != NULL) {
7164 utf8_flag = *utf8_fl;
7169 /* If there is a possibility of UTF8, then if any UTF8 characters
7170 are present, then they must be converted to VTF-7
7172 result = strcpy(rslt, path); /* FIX-ME */
7175 result = strcpy(rslt, path);
7181 /*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/
7182 static char *mp_do_tovmsspec
7183 (pTHX_ const char *path, char *buf, int ts, int dir_flag, int * utf8_flag) {
7184 static char __tovmsspec_retbuf[VMS_MAXRSS];
7185 char *rslt, *dirend;
7190 unsigned long int infront = 0, hasdir = 1;
7193 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
7194 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
7196 if (path == NULL) return NULL;
7197 rslt_len = VMS_MAXRSS-1;
7198 if (buf) rslt = buf;
7199 else if (ts) Newx(rslt, VMS_MAXRSS, char);
7200 else rslt = __tovmsspec_retbuf;
7202 /* '.' and '..' are "[]" and "[-]" for a quick check */
7203 if (path[0] == '.') {
7204 if (path[1] == '\0') {
7206 if (utf8_flag != NULL)
7211 if (path[1] == '.' && path[2] == '\0') {
7213 if (utf8_flag != NULL)
7220 /* Posix specifications are now a native VMS format */
7221 /*--------------------------------------------------*/
7222 #if __CRTL_VER >= 80200000 && !defined(__VAX)
7223 if (decc_posix_compliant_pathnames) {
7224 if (strncmp(path,"\"^UP^",5) == 0) {
7225 posix_to_vmsspec_hardway(rslt, rslt_len, path, dir_flag, utf8_flag);
7231 /* This is really the only way to see if this is already in VMS format */
7232 sts = vms_split_path
7247 /* FIX-ME - If dir_flag is non-zero, then this is a mp_do_vmspath()
7248 replacement, because the above parse just took care of most of
7249 what is needed to do vmspath when the specification is already
7252 And if it is not already, it is easier to do the conversion as
7253 part of this routine than to call this routine and then work on
7257 /* If VMS punctuation was found, it is already VMS format */
7258 if ((v_len != 0) || (r_len != 0) || (d_len != 0) || (vs_len != 0)) {
7259 if (utf8_flag != NULL)
7264 /* Now, what to do with trailing "." cases where there is no
7265 extension? If this is a UNIX specification, and EFS characters
7266 are enabled, then the trailing "." should be converted to a "^.".
7267 But if this was already a VMS specification, then it should be
7270 So in the case of ambiguity, leave the specification alone.
7274 /* If there is a possibility of UTF8, then if any UTF8 characters
7275 are present, then they must be converted to VTF-7
7277 if (utf8_flag != NULL)
7283 dirend = strrchr(path,'/');
7285 if (dirend == NULL) {
7286 /* If we get here with no UNIX directory delimiters, then this is
7287 not a complete file specification, either garbage a UNIX glob
7288 specification that can not be converted to a VMS wildcard, or
7289 it a UNIX shell macro. MakeMaker wants these passed through AS-IS,
7290 so apparently other programs expect this also.
7292 utf8 flag setting needs to be preserved.
7298 /* If POSIX mode active, handle the conversion */
7299 #if __CRTL_VER >= 80200000 && !defined(__VAX)
7300 if (decc_efs_charset) {
7301 posix_to_vmsspec_hardway(rslt, rslt_len, path, dir_flag, utf8_flag);
7306 if (*(dirend+1) == '.') { /* do we have trailing "/." or "/.." or "/..."? */
7307 if (!*(dirend+2)) dirend +=2;
7308 if (*(dirend+2) == '.' && !*(dirend+3)) dirend += 3;
7309 if (decc_efs_charset == 0) {
7310 if (*(dirend+2) == '.' && *(dirend+3) == '.' && !*(dirend+4)) dirend += 4;
7316 lastdot = strrchr(cp2,'.');
7322 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
7324 if (decc_disable_posix_root) {
7325 strcpy(rslt,"sys$disk:[000000]");
7328 strcpy(rslt,"sys$posix_root:[000000]");
7330 if (utf8_flag != NULL)
7334 while (*(++cp2) != '/' && *cp2) *(cp1++) = *cp2;
7336 trndev = PerlMem_malloc(VMS_MAXRSS);
7337 if (trndev == NULL) _ckvmssts(SS$_INSFMEM);
7338 islnm = my_trnlnm(rslt,trndev,0);
7340 /* DECC special handling */
7342 if (strcmp(rslt,"bin") == 0) {
7343 strcpy(rslt,"sys$system");
7346 islnm = my_trnlnm(rslt,trndev,0);
7348 else if (strcmp(rslt,"tmp") == 0) {
7349 strcpy(rslt,"sys$scratch");
7352 islnm = my_trnlnm(rslt,trndev,0);
7354 else if (!decc_disable_posix_root) {
7355 strcpy(rslt, "sys$posix_root");
7359 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
7360 islnm = my_trnlnm(rslt,trndev,0);
7362 else if (strcmp(rslt,"dev") == 0) {
7363 if (strncmp(cp2,"/null", 5) == 0) {
7364 if ((cp2[5] == 0) || (cp2[5] == '/')) {
7365 strcpy(rslt,"NLA0");
7369 islnm = my_trnlnm(rslt,trndev,0);
7375 trnend = islnm ? strlen(trndev) - 1 : 0;
7376 islnm = trnend ? (trndev[trnend] == ']' || trndev[trnend] == '>') : 0;
7377 rooted = islnm ? (trndev[trnend-1] == '.') : 0;
7378 /* If the first element of the path is a logical name, determine
7379 * whether it has to be translated so we can add more directories. */
7380 if (!islnm || rooted) {
7383 if (cp2 == dirend) while (infront++ < 6) *(cp1++) = '0';
7387 if (cp2 != dirend) {
7388 strcpy(rslt,trndev);
7389 cp1 = rslt + trnend;
7396 if (decc_disable_posix_root) {
7402 PerlMem_free(trndev);
7407 if (*(cp2+1) == '/' || *(cp2+1) == '\0') {
7408 cp2 += 2; /* skip over "./" - it's redundant */
7409 *(cp1++) = '.'; /* but it does indicate a relative dirspec */
7411 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
7412 *(cp1++) = '-'; /* "../" --> "-" */
7415 else if (*(cp2+1) == '.' && *(cp2+2) == '.' &&
7416 (*(cp2+3) == '/' || *(cp2+3) == '\0')) {
7417 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
7418 if (!*(cp2+4)) *(cp1++) = '.'; /* Simulate trailing '/' for later */
7421 else if ((cp2 != lastdot) || (lastdot < dirend)) {
7422 /* Escape the extra dots in EFS file specifications */
7425 if (cp2 > dirend) cp2 = dirend;
7427 else *(cp1++) = '.';
7429 for (; cp2 < dirend; cp2++) {
7431 if (*(cp2-1) == '/') continue;
7432 if (*(cp1-1) != '.') *(cp1++) = '.';
7435 else if (!infront && *cp2 == '.') {
7436 if (cp2+1 == dirend || *(cp2+1) == '\0') { cp2++; break; }
7437 else if (*(cp2+1) == '/') cp2++; /* skip over "./" - it's redundant */
7438 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
7439 if (*(cp1-1) == '-' || *(cp1-1) == '[') *(cp1++) = '-'; /* handle "../" */
7440 else if (*(cp1-2) == '[') *(cp1-1) = '-';
7441 else { /* back up over previous directory name */
7443 while (*(cp1-1) != '.' && *(cp1-1) != '[') cp1--;
7444 if (*(cp1-1) == '[') {
7445 memcpy(cp1,"000000.",7);
7450 if (cp2 == dirend) break;
7452 else if ( *(cp2+1) == '.' && *(cp2+2) == '.' &&
7453 (*(cp2+3) == '/' || *(cp2+3) == '\0') ) {
7454 if (*(cp1-1) != '.') *(cp1++) = '.'; /* May already have 1 from '/' */
7455 *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
7457 *(cp1++) = '.'; /* Simulate trailing '/' */
7458 cp2 += 2; /* for loop will incr this to == dirend */
7460 else cp2 += 3; /* Trailing '/' was there, so skip it, too */
7463 if (decc_efs_charset == 0)
7464 *(cp1++) = '_'; /* fix up syntax - '.' in name not allowed */
7466 *(cp1++) = '^'; /* fix up syntax - '.' in name is allowed */
7472 if (!infront && *(cp1-1) == '-') *(cp1++) = '.';
7474 if (decc_efs_charset == 0)
7481 else *(cp1++) = *cp2;
7485 if (*(cp1-1) == '.') cp1--; /* Unix spec ending in '/' ==> trailing '.' */
7486 if (hasdir) *(cp1++) = ']';
7487 if (*cp2) cp2++; /* check in case we ended with trailing '..' */
7488 /* fixme for ODS5 */
7495 if (decc_efs_charset == 0)
7506 if (((cp2 < lastdot) || (cp2[1] == '\0')) &&
7507 decc_readdir_dropdotnotype) {
7512 /* trailing dot ==> '^..' on VMS */
7519 *(cp1++) = *(cp2++);
7524 /* This could be a macro to be passed through */
7525 *(cp1++) = *(cp2++);
7527 const char * save_cp2;
7531 /* paranoid check */
7537 *(cp1++) = *(cp2++);
7538 if (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
7539 *(cp1++) = *(cp2++);
7540 while (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
7541 *(cp1++) = *(cp2++);
7544 *(cp1++) = *(cp2++);
7548 if (is_macro == 0) {
7549 /* Not really a macro - never mind */
7579 *(cp1++) = *(cp2++);
7582 /* FIXME: This needs fixing as Perl is putting ".dir;" on UNIX filespecs
7583 * which is wrong. UNIX notation should be ".dir." unless
7584 * the DECC$FILENAME_UNIX_NO_VERSION is enabled.
7585 * changing this behavior could break more things at this time.
7586 * efs character set effectively does not allow "." to be a version
7587 * delimiter as a further complication about changing this.
7589 if (decc_filename_unix_report != 0) {
7592 *(cp1++) = *(cp2++);
7595 *(cp1++) = *(cp2++);
7598 if ((no_type_seen == 1) && decc_readdir_dropdotnotype) {
7602 /* Fix me for "^]", but that requires making sure that you do
7603 * not back up past the start of the filename
7605 if ((*lcp1 != ']') && (*lcp1 != '*') && (*lcp1 != '%'))
7610 if (utf8_flag != NULL)
7614 } /* end of do_tovmsspec() */
7616 /* External entry points */
7617 char *Perl_tovmsspec(pTHX_ const char *path, char *buf)
7618 { return do_tovmsspec(path,buf,0,NULL); }
7619 char *Perl_tovmsspec_ts(pTHX_ const char *path, char *buf)
7620 { return do_tovmsspec(path,buf,1,NULL); }
7621 char *Perl_tovmsspec_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
7622 { return do_tovmsspec(path,buf,0,utf8_fl); }
7623 char *Perl_tovmsspec_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
7624 { return do_tovmsspec(path,buf,1,utf8_fl); }
7626 /*{{{ char *tovmspath[_ts](char *path, char *buf, const int *)*/
7627 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) {
7628 static char __tovmspath_retbuf[VMS_MAXRSS];
7630 char *pathified, *vmsified, *cp;
7632 if (path == NULL) return NULL;
7633 pathified = PerlMem_malloc(VMS_MAXRSS);
7634 if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
7635 if (do_pathify_dirspec(path,pathified,0,NULL) == NULL) {
7636 PerlMem_free(pathified);
7642 Newx(vmsified, VMS_MAXRSS, char);
7643 if (do_tovmsspec(pathified, buf ? buf : vmsified, 0, NULL) == NULL) {
7644 PerlMem_free(pathified);
7645 if (vmsified) Safefree(vmsified);
7648 PerlMem_free(pathified);
7653 vmslen = strlen(vmsified);
7654 Newx(cp,vmslen+1,char);
7655 memcpy(cp,vmsified,vmslen);
7661 strcpy(__tovmspath_retbuf,vmsified);
7663 return __tovmspath_retbuf;
7666 } /* end of do_tovmspath() */
7668 /* External entry points */
7669 char *Perl_tovmspath(pTHX_ const char *path, char *buf)
7670 { return do_tovmspath(path,buf,0, NULL); }
7671 char *Perl_tovmspath_ts(pTHX_ const char *path, char *buf)
7672 { return do_tovmspath(path,buf,1, NULL); }
7673 char *Perl_tovmspath_utf8(pTHX_ const char *path, char *buf, int *utf8_fl)
7674 { return do_tovmspath(path,buf,0,utf8_fl); }
7675 char *Perl_tovmspath_utf8_ts(pTHX_ const char *path, char *buf, int *utf8_fl)
7676 { return do_tovmspath(path,buf,1,utf8_fl); }
7679 /*{{{ char *tounixpath[_ts](char *path, char *buf, int * utf8_fl)*/
7680 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) {
7681 static char __tounixpath_retbuf[VMS_MAXRSS];
7683 char *pathified, *unixified, *cp;
7685 if (path == NULL) return NULL;
7686 pathified = PerlMem_malloc(VMS_MAXRSS);
7687 if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
7688 if (do_pathify_dirspec(path,pathified,0,NULL) == NULL) {
7689 PerlMem_free(pathified);
7695 Newx(unixified, VMS_MAXRSS, char);
7697 if (do_tounixspec(pathified,buf ? buf : unixified,0,NULL) == NULL) {
7698 PerlMem_free(pathified);
7699 if (unixified) Safefree(unixified);
7702 PerlMem_free(pathified);
7707 unixlen = strlen(unixified);
7708 Newx(cp,unixlen+1,char);
7709 memcpy(cp,unixified,unixlen);
7711 Safefree(unixified);
7715 strcpy(__tounixpath_retbuf,unixified);
7716 Safefree(unixified);
7717 return __tounixpath_retbuf;
7720 } /* end of do_tounixpath() */
7722 /* External entry points */
7723 char *Perl_tounixpath(pTHX_ const char *path, char *buf)
7724 { return do_tounixpath(path,buf,0,NULL); }
7725 char *Perl_tounixpath_ts(pTHX_ const char *path, char *buf)
7726 { return do_tounixpath(path,buf,1,NULL); }
7727 char *Perl_tounixpath_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
7728 { return do_tounixpath(path,buf,0,utf8_fl); }
7729 char *Perl_tounixpath_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
7730 { return do_tounixpath(path,buf,1,utf8_fl); }
7733 * @(#)argproc.c 2.2 94/08/16 Mark Pizzolato (mark AT infocomm DOT com)
7735 *****************************************************************************
7737 * Copyright (C) 1989-1994, 2007 by *
7738 * Mark Pizzolato - INFO COMM, Danville, California (510) 837-5600 *
7740 * Permission is hereby granted for the reproduction of this software *
7741 * on condition that this copyright notice is included in source *
7742 * distributions of the software. The code may be modified and *
7743 * distributed under the same terms as Perl itself. *
7745 * 27-Aug-1994 Modified for inclusion in perl5 *
7746 * by Charles Bailey (bailey AT newman DOT upenn DOT edu) *
7747 *****************************************************************************
7751 * getredirection() is intended to aid in porting C programs
7752 * to VMS (Vax-11 C). The native VMS environment does not support
7753 * '>' and '<' I/O redirection, or command line wild card expansion,
7754 * or a command line pipe mechanism using the '|' AND background
7755 * command execution '&'. All of these capabilities are provided to any
7756 * C program which calls this procedure as the first thing in the
7758 * The piping mechanism will probably work with almost any 'filter' type
7759 * of program. With suitable modification, it may useful for other
7760 * portability problems as well.
7762 * Author: Mark Pizzolato (mark AT infocomm DOT com)
7766 struct list_item *next;
7770 static void add_item(struct list_item **head,
7771 struct list_item **tail,
7775 static void mp_expand_wild_cards(pTHX_ char *item,
7776 struct list_item **head,
7777 struct list_item **tail,
7780 static int background_process(pTHX_ int argc, char **argv);
7782 static void pipe_and_fork(pTHX_ char **cmargv);
7784 /*{{{ void getredirection(int *ac, char ***av)*/
7786 mp_getredirection(pTHX_ int *ac, char ***av)
7788 * Process vms redirection arg's. Exit if any error is seen.
7789 * If getredirection() processes an argument, it is erased
7790 * from the vector. getredirection() returns a new argc and argv value.
7791 * In the event that a background command is requested (by a trailing "&"),
7792 * this routine creates a background subprocess, and simply exits the program.
7794 * Warning: do not try to simplify the code for vms. The code
7795 * presupposes that getredirection() is called before any data is
7796 * read from stdin or written to stdout.
7798 * Normal usage is as follows:
7804 * getredirection(&argc, &argv);
7808 int argc = *ac; /* Argument Count */
7809 char **argv = *av; /* Argument Vector */
7810 char *ap; /* Argument pointer */
7811 int j; /* argv[] index */
7812 int item_count = 0; /* Count of Items in List */
7813 struct list_item *list_head = 0; /* First Item in List */
7814 struct list_item *list_tail; /* Last Item in List */
7815 char *in = NULL; /* Input File Name */
7816 char *out = NULL; /* Output File Name */
7817 char *outmode = "w"; /* Mode to Open Output File */
7818 char *err = NULL; /* Error File Name */
7819 char *errmode = "w"; /* Mode to Open Error File */
7820 int cmargc = 0; /* Piped Command Arg Count */
7821 char **cmargv = NULL;/* Piped Command Arg Vector */
7824 * First handle the case where the last thing on the line ends with
7825 * a '&'. This indicates the desire for the command to be run in a
7826 * subprocess, so we satisfy that desire.
7829 if (0 == strcmp("&", ap))
7830 exit(background_process(aTHX_ --argc, argv));
7831 if (*ap && '&' == ap[strlen(ap)-1])
7833 ap[strlen(ap)-1] = '\0';
7834 exit(background_process(aTHX_ argc, argv));
7837 * Now we handle the general redirection cases that involve '>', '>>',
7838 * '<', and pipes '|'.
7840 for (j = 0; j < argc; ++j)
7842 if (0 == strcmp("<", argv[j]))
7846 fprintf(stderr,"No input file after < on command line");
7847 exit(LIB$_WRONUMARG);
7852 if ('<' == *(ap = argv[j]))
7857 if (0 == strcmp(">", ap))
7861 fprintf(stderr,"No output file after > on command line");
7862 exit(LIB$_WRONUMARG);
7881 fprintf(stderr,"No output file after > or >> on command line");
7882 exit(LIB$_WRONUMARG);
7886 if (('2' == *ap) && ('>' == ap[1]))
7903 fprintf(stderr,"No output file after 2> or 2>> on command line");
7904 exit(LIB$_WRONUMARG);
7908 if (0 == strcmp("|", argv[j]))
7912 fprintf(stderr,"No command into which to pipe on command line");
7913 exit(LIB$_WRONUMARG);
7915 cmargc = argc-(j+1);
7916 cmargv = &argv[j+1];
7920 if ('|' == *(ap = argv[j]))
7928 expand_wild_cards(ap, &list_head, &list_tail, &item_count);
7931 * Allocate and fill in the new argument vector, Some Unix's terminate
7932 * the list with an extra null pointer.
7934 argv = (char **) PerlMem_malloc((item_count+1) * sizeof(char *));
7935 if (argv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7937 for (j = 0; j < item_count; ++j, list_head = list_head->next)
7938 argv[j] = list_head->value;
7944 fprintf(stderr,"'|' and '>' may not both be specified on command line");
7945 exit(LIB$_INVARGORD);
7947 pipe_and_fork(aTHX_ cmargv);
7950 /* Check for input from a pipe (mailbox) */
7952 if (in == NULL && 1 == isapipe(0))
7954 char mbxname[L_tmpnam];
7956 long int dvi_item = DVI$_DEVBUFSIZ;
7957 $DESCRIPTOR(mbxnam, "");
7958 $DESCRIPTOR(mbxdevnam, "");
7960 /* Input from a pipe, reopen it in binary mode to disable */
7961 /* carriage control processing. */
7963 fgetname(stdin, mbxname);
7964 mbxnam.dsc$a_pointer = mbxname;
7965 mbxnam.dsc$w_length = strlen(mbxnam.dsc$a_pointer);
7966 lib$getdvi(&dvi_item, 0, &mbxnam, &bufsize, 0, 0);
7967 mbxdevnam.dsc$a_pointer = mbxname;
7968 mbxdevnam.dsc$w_length = sizeof(mbxname);
7969 dvi_item = DVI$_DEVNAM;
7970 lib$getdvi(&dvi_item, 0, &mbxnam, 0, &mbxdevnam, &mbxdevnam.dsc$w_length);
7971 mbxdevnam.dsc$a_pointer[mbxdevnam.dsc$w_length] = '\0';
7974 freopen(mbxname, "rb", stdin);
7977 fprintf(stderr,"Can't reopen input pipe (name: %s) in binary mode",mbxname);
7981 if ((in != NULL) && (NULL == freopen(in, "r", stdin, "mbc=32", "mbf=2")))
7983 fprintf(stderr,"Can't open input file %s as stdin",in);
7986 if ((out != NULL) && (NULL == freopen(out, outmode, stdout, "mbc=32", "mbf=2")))
7988 fprintf(stderr,"Can't open output file %s as stdout",out);
7991 if (out != NULL) Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",out);
7994 if (strcmp(err,"&1") == 0) {
7995 dup2(fileno(stdout), fileno(stderr));
7996 Perl_vmssetuserlnm(aTHX_ "SYS$ERROR","SYS$OUTPUT");
7999 if (NULL == (tmperr = fopen(err, errmode, "mbc=32", "mbf=2")))
8001 fprintf(stderr,"Can't open error file %s as stderr",err);
8005 if (NULL == freopen(err, "a", stderr, "mbc=32", "mbf=2"))
8009 Perl_vmssetuserlnm(aTHX_ "SYS$ERROR",err);
8012 #ifdef ARGPROC_DEBUG
8013 PerlIO_printf(Perl_debug_log, "Arglist:\n");
8014 for (j = 0; j < *ac; ++j)
8015 PerlIO_printf(Perl_debug_log, "argv[%d] = '%s'\n", j, argv[j]);
8017 /* Clear errors we may have hit expanding wildcards, so they don't
8018 show up in Perl's $! later */
8019 set_errno(0); set_vaxc_errno(1);
8020 } /* end of getredirection() */
8023 static void add_item(struct list_item **head,
8024 struct list_item **tail,
8030 *head = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
8031 if (head == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8035 (*tail)->next = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
8036 if ((*tail)->next == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8037 *tail = (*tail)->next;
8039 (*tail)->value = value;
8043 static void mp_expand_wild_cards(pTHX_ char *item,
8044 struct list_item **head,
8045 struct list_item **tail,
8049 unsigned long int context = 0;
8057 $DESCRIPTOR(filespec, "");
8058 $DESCRIPTOR(defaultspec, "SYS$DISK:[]");
8059 $DESCRIPTOR(resultspec, "");
8060 unsigned long int lff_flags = 0;
8064 #ifdef VMS_LONGNAME_SUPPORT
8065 lff_flags = LIB$M_FIL_LONG_NAMES;
8068 for (cp = item; *cp; cp++) {
8069 if (*cp == '*' || *cp == '%' || isspace(*cp)) break;
8070 if (*cp == '.' && *(cp-1) == '.' && *(cp-2) =='.') break;
8072 if (!*cp || isspace(*cp))
8074 add_item(head, tail, item, count);
8079 /* "double quoted" wild card expressions pass as is */
8080 /* From DCL that means using e.g.: */
8081 /* perl program """perl.*""" */
8082 item_len = strlen(item);
8083 if ( '"' == *item && '"' == item[item_len-1] )
8086 item[item_len-2] = '\0';
8087 add_item(head, tail, item, count);
8091 resultspec.dsc$b_dtype = DSC$K_DTYPE_T;
8092 resultspec.dsc$b_class = DSC$K_CLASS_D;
8093 resultspec.dsc$a_pointer = NULL;
8094 vmsspec = PerlMem_malloc(VMS_MAXRSS);
8095 if (vmsspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8096 if ((isunix = (int) strchr(item,'/')) != (int) NULL)
8097 filespec.dsc$a_pointer = do_tovmsspec(item,vmsspec,0,NULL);
8098 if (!isunix || !filespec.dsc$a_pointer)
8099 filespec.dsc$a_pointer = item;
8100 filespec.dsc$w_length = strlen(filespec.dsc$a_pointer);
8102 * Only return version specs, if the caller specified a version
8104 had_version = strchr(item, ';');
8106 * Only return device and directory specs, if the caller specifed either.
8108 had_device = strchr(item, ':');
8109 had_directory = (isunix || NULL != strchr(item, '[')) || (NULL != strchr(item, '<'));
8111 while ($VMS_STATUS_SUCCESS(sts = lib$find_file
8112 (&filespec, &resultspec, &context,
8113 &defaultspec, 0, &rms_sts, &lff_flags)))
8118 string = PerlMem_malloc(resultspec.dsc$w_length+1);
8119 if (string == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8120 strncpy(string, resultspec.dsc$a_pointer, resultspec.dsc$w_length);
8121 string[resultspec.dsc$w_length] = '\0';
8122 if (NULL == had_version)
8123 *(strrchr(string, ';')) = '\0';
8124 if ((!had_directory) && (had_device == NULL))
8126 if (NULL == (devdir = strrchr(string, ']')))
8127 devdir = strrchr(string, '>');
8128 strcpy(string, devdir + 1);
8131 * Be consistent with what the C RTL has already done to the rest of
8132 * the argv items and lowercase all of these names.
8134 if (!decc_efs_case_preserve) {
8135 for (c = string; *c; ++c)
8139 if (isunix) trim_unixpath(string,item,1);
8140 add_item(head, tail, string, count);
8143 PerlMem_free(vmsspec);
8144 if (sts != RMS$_NMF)
8146 set_vaxc_errno(sts);
8149 case RMS$_FNF: case RMS$_DNF:
8150 set_errno(ENOENT); break;
8152 set_errno(ENOTDIR); break;
8154 set_errno(ENODEV); break;
8155 case RMS$_FNM: case RMS$_SYN:
8156 set_errno(EINVAL); break;
8158 set_errno(EACCES); break;
8160 _ckvmssts_noperl(sts);
8164 add_item(head, tail, item, count);
8165 _ckvmssts_noperl(lib$sfree1_dd(&resultspec));
8166 _ckvmssts_noperl(lib$find_file_end(&context));
8169 static int child_st[2];/* Event Flag set when child process completes */
8171 static unsigned short child_chan;/* I/O Channel for Pipe Mailbox */
8173 static unsigned long int exit_handler(int *status)
8177 if (0 == child_st[0])
8179 #ifdef ARGPROC_DEBUG
8180 PerlIO_printf(Perl_debug_log, "Waiting for Child Process to Finish . . .\n");
8182 fflush(stdout); /* Have to flush pipe for binary data to */
8183 /* terminate properly -- <tp@mccall.com> */
8184 sys$qiow(0, child_chan, IO$_WRITEOF, iosb, 0, 0, 0, 0, 0, 0, 0, 0);
8185 sys$dassgn(child_chan);
8187 sys$synch(0, child_st);
8192 static void sig_child(int chan)
8194 #ifdef ARGPROC_DEBUG
8195 PerlIO_printf(Perl_debug_log, "Child Completion AST\n");
8197 if (child_st[0] == 0)
8201 static struct exit_control_block exit_block =
8206 &exit_block.exit_status,
8211 pipe_and_fork(pTHX_ char **cmargv)
8214 struct dsc$descriptor_s *vmscmd;
8215 char subcmd[2*MAX_DCL_LINE_LENGTH], *p, *q;
8216 int sts, j, l, ismcr, quote, tquote = 0;
8218 sts = setup_cmddsc(aTHX_ cmargv[0],0,"e,&vmscmd);
8219 vms_execfree(vmscmd);
8224 ismcr = q && toupper(*q) == 'M' && toupper(*(q+1)) == 'C'
8225 && toupper(*(q+2)) == 'R' && !*(q+3);
8227 while (q && l < MAX_DCL_LINE_LENGTH) {
8229 if (j > 0 && quote) {
8235 if (ismcr && j > 1) quote = 1;
8236 tquote = (strchr(q,' ')) != NULL || *q == '\0';
8239 if (quote || tquote) {
8245 if ((quote||tquote) && *q == '"') {
8255 fp = safe_popen(aTHX_ subcmd,"wbF",&sts);
8257 PerlIO_printf(Perl_debug_log,"Can't open output pipe (status %d)",sts);
8261 static int background_process(pTHX_ int argc, char **argv)
8263 char command[MAX_DCL_SYMBOL + 1] = "$";
8264 $DESCRIPTOR(value, "");
8265 static $DESCRIPTOR(cmd, "BACKGROUND$COMMAND");
8266 static $DESCRIPTOR(null, "NLA0:");
8267 static $DESCRIPTOR(pidsymbol, "SHELL_BACKGROUND_PID");
8269 $DESCRIPTOR(pidstr, "");
8271 unsigned long int flags = 17, one = 1, retsts;
8274 strcat(command, argv[0]);
8275 len = strlen(command);
8276 while (--argc && (len < MAX_DCL_SYMBOL))
8278 strcat(command, " \"");
8279 strcat(command, *(++argv));
8280 strcat(command, "\"");
8281 len = strlen(command);
8283 value.dsc$a_pointer = command;
8284 value.dsc$w_length = strlen(value.dsc$a_pointer);
8285 _ckvmssts_noperl(lib$set_symbol(&cmd, &value));
8286 retsts = lib$spawn(&cmd, &null, 0, &flags, 0, &pid);
8287 if (retsts == 0x38250) { /* DCL-W-NOTIFY - We must be BATCH, so retry */
8288 _ckvmssts_noperl(lib$spawn(&cmd, &null, 0, &one, 0, &pid));
8291 _ckvmssts_noperl(retsts);
8293 #ifdef ARGPROC_DEBUG
8294 PerlIO_printf(Perl_debug_log, "%s\n", command);
8296 sprintf(pidstring, "%08X", pid);
8297 PerlIO_printf(Perl_debug_log, "%s\n", pidstring);
8298 pidstr.dsc$a_pointer = pidstring;
8299 pidstr.dsc$w_length = strlen(pidstr.dsc$a_pointer);
8300 lib$set_symbol(&pidsymbol, &pidstr);
8304 /***** End of code taken from Mark Pizzolato's argproc.c package *****/
8307 /* OS-specific initialization at image activation (not thread startup) */
8308 /* Older VAXC header files lack these constants */
8309 #ifndef JPI$_RIGHTS_SIZE
8310 # define JPI$_RIGHTS_SIZE 817
8312 #ifndef KGB$M_SUBSYSTEM
8313 # define KGB$M_SUBSYSTEM 0x8
8316 /* Avoid Newx() in vms_image_init as thread context has not been initialized. */
8318 /*{{{void vms_image_init(int *, char ***)*/
8320 vms_image_init(int *argcp, char ***argvp)
8322 char eqv[LNM$C_NAMLENGTH+1] = "";
8323 unsigned int len, tabct = 8, tabidx = 0;
8324 unsigned long int *mask, iosb[2], i, rlst[128], rsz;
8325 unsigned long int iprv[(sizeof(union prvdef) + sizeof(unsigned long int) - 1) / sizeof(unsigned long int)];
8326 unsigned short int dummy, rlen;
8327 struct dsc$descriptor_s **tabvec;
8328 #if defined(PERL_IMPLICIT_CONTEXT)
8331 struct itmlst_3 jpilist[4] = { {sizeof iprv, JPI$_IMAGPRIV, iprv, &dummy},
8332 {sizeof rlst, JPI$_RIGHTSLIST, rlst, &rlen},
8333 { sizeof rsz, JPI$_RIGHTS_SIZE, &rsz, &dummy},
8336 #ifdef KILL_BY_SIGPRC
8337 Perl_csighandler_init();
8340 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,jpilist,iosb,NULL,NULL));
8341 _ckvmssts_noperl(iosb[0]);
8342 for (i = 0; i < sizeof iprv / sizeof(unsigned long int); i++) {
8343 if (iprv[i]) { /* Running image installed with privs? */
8344 _ckvmssts_noperl(sys$setprv(0,iprv,0,NULL)); /* Turn 'em off. */
8349 /* Rights identifiers might trigger tainting as well. */
8350 if (!will_taint && (rlen || rsz)) {
8351 while (rlen < rsz) {
8352 /* We didn't get all the identifiers on the first pass. Allocate a
8353 * buffer much larger than $GETJPI wants (rsz is size in bytes that
8354 * were needed to hold all identifiers at time of last call; we'll
8355 * allocate that many unsigned long ints), and go back and get 'em.
8356 * If it gave us less than it wanted to despite ample buffer space,
8357 * something's broken. Is your system missing a system identifier?
8359 if (rsz <= jpilist[1].buflen) {
8360 /* Perl_croak accvios when used this early in startup. */
8361 fprintf(stderr, "vms_image_init: $getjpiw refuses to store RIGHTSLIST of %u bytes in buffer of %u bytes.\n%s",
8362 rsz, (unsigned long) jpilist[1].buflen,
8363 "Check your rights database for corruption.\n");
8366 if (jpilist[1].bufadr != rlst) PerlMem_free(jpilist[1].bufadr);
8367 jpilist[1].bufadr = mask = (unsigned long int *) PerlMem_malloc(rsz * sizeof(unsigned long int));
8368 if (mask == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8369 jpilist[1].buflen = rsz * sizeof(unsigned long int);
8370 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,&jpilist[1],iosb,NULL,NULL));
8371 _ckvmssts_noperl(iosb[0]);
8373 mask = jpilist[1].bufadr;
8374 /* Check attribute flags for each identifier (2nd longword); protected
8375 * subsystem identifiers trigger tainting.
8377 for (i = 1; i < (rlen + sizeof(unsigned long int) - 1) / sizeof(unsigned long int); i += 2) {
8378 if (mask[i] & KGB$M_SUBSYSTEM) {
8383 if (mask != rlst) PerlMem_free(mask);
8386 /* When Perl is in decc_filename_unix_report mode and is run from a concealed
8387 * logical, some versions of the CRTL will add a phanthom /000000/
8388 * directory. This needs to be removed.
8390 if (decc_filename_unix_report) {
8393 ulen = strlen(argvp[0][0]);
8395 zeros = strstr(argvp[0][0], "/000000/");
8396 if (zeros != NULL) {
8398 mlen = ulen - (zeros - argvp[0][0]) - 7;
8399 memmove(zeros, &zeros[7], mlen);
8401 argvp[0][0][ulen] = '\0';
8404 /* It also may have a trailing dot that needs to be removed otherwise
8405 * it will be converted to VMS mode incorrectly.
8408 if ((argvp[0][0][ulen] == '.') && (decc_readdir_dropdotnotype))
8409 argvp[0][0][ulen] = '\0';
8412 /* We need to use this hack to tell Perl it should run with tainting,
8413 * since its tainting flag may be part of the PL_curinterp struct, which
8414 * hasn't been allocated when vms_image_init() is called.
8417 char **newargv, **oldargv;
8419 newargv = (char **) PerlMem_malloc(((*argcp)+2) * sizeof(char *));
8420 if (newargv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8421 newargv[0] = oldargv[0];
8422 newargv[1] = PerlMem_malloc(3 * sizeof(char));
8423 if (newargv[1] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8424 strcpy(newargv[1], "-T");
8425 Copy(&oldargv[1],&newargv[2],(*argcp)-1,char **);
8427 newargv[*argcp] = NULL;
8428 /* We orphan the old argv, since we don't know where it's come from,
8429 * so we don't know how to free it.
8433 else { /* Did user explicitly request tainting? */
8435 char *cp, **av = *argvp;
8436 for (i = 1; i < *argcp; i++) {
8437 if (*av[i] != '-') break;
8438 for (cp = av[i]+1; *cp; cp++) {
8439 if (*cp == 'T') { will_taint = 1; break; }
8440 else if ( (*cp == 'd' || *cp == 'V') && *(cp+1) == ':' ||
8441 strchr("DFIiMmx",*cp)) break;
8443 if (will_taint) break;
8448 len = my_trnlnm("PERL_ENV_TABLES",eqv,tabidx);
8451 tabvec = (struct dsc$descriptor_s **)
8452 PerlMem_malloc(tabct * sizeof(struct dsc$descriptor_s *));
8453 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8455 else if (tabidx >= tabct) {
8457 tabvec = (struct dsc$descriptor_s **) PerlMem_realloc(tabvec, tabct * sizeof(struct dsc$descriptor_s *));
8458 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8460 tabvec[tabidx] = (struct dsc$descriptor_s *) PerlMem_malloc(sizeof(struct dsc$descriptor_s));
8461 if (tabvec[tabidx] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8462 tabvec[tabidx]->dsc$w_length = 0;
8463 tabvec[tabidx]->dsc$b_dtype = DSC$K_DTYPE_T;
8464 tabvec[tabidx]->dsc$b_class = DSC$K_CLASS_D;
8465 tabvec[tabidx]->dsc$a_pointer = NULL;
8466 _ckvmssts_noperl(lib$scopy_r_dx(&len,eqv,tabvec[tabidx]));
8468 if (tabidx) { tabvec[tabidx] = NULL; env_tables = tabvec; }
8470 getredirection(argcp,argvp);
8471 #if defined(USE_ITHREADS) && ( defined(__DECC) || defined(__DECCXX) )
8473 # include <reentrancy.h>
8474 decc$set_reentrancy(C$C_MULTITHREAD);
8483 * Trim Unix-style prefix off filespec, so it looks like what a shell
8484 * glob expansion would return (i.e. from specified prefix on, not
8485 * full path). Note that returned filespec is Unix-style, regardless
8486 * of whether input filespec was VMS-style or Unix-style.
8488 * fspec is filespec to be trimmed, and wildspec is wildcard spec used to
8489 * determine prefix (both may be in VMS or Unix syntax). opts is a bit
8490 * vector of options; at present, only bit 0 is used, and if set tells
8491 * trim unixpath to try the current default directory as a prefix when
8492 * presented with a possibly ambiguous ... wildcard.
8494 * Returns !=0 on success, with trimmed filespec replacing contents of
8495 * fspec, and 0 on failure, with contents of fpsec unchanged.
8497 /*{{{int trim_unixpath(char *fspec, char *wildspec, int opts)*/
8499 Perl_trim_unixpath(pTHX_ char *fspec, const char *wildspec, int opts)
8501 char *unixified, *unixwild,
8502 *template, *base, *end, *cp1, *cp2;
8503 register int tmplen, reslen = 0, dirs = 0;
8505 unixwild = PerlMem_malloc(VMS_MAXRSS);
8506 if (unixwild == NULL) _ckvmssts(SS$_INSFMEM);
8507 if (!wildspec || !fspec) return 0;
8508 template = unixwild;
8509 if (strpbrk(wildspec,"]>:") != NULL) {
8510 if (do_tounixspec(wildspec,unixwild,0,NULL) == NULL) {
8511 PerlMem_free(unixwild);
8516 strncpy(unixwild, wildspec, VMS_MAXRSS-1);
8517 unixwild[VMS_MAXRSS-1] = 0;
8519 unixified = PerlMem_malloc(VMS_MAXRSS);
8520 if (unixified == NULL) _ckvmssts(SS$_INSFMEM);
8521 if (strpbrk(fspec,"]>:") != NULL) {
8522 if (do_tounixspec(fspec,unixified,0,NULL) == NULL) {
8523 PerlMem_free(unixwild);
8524 PerlMem_free(unixified);
8527 else base = unixified;
8528 /* reslen != 0 ==> we had to unixify resultant filespec, so we must
8529 * check to see that final result fits into (isn't longer than) fspec */
8530 reslen = strlen(fspec);
8534 /* No prefix or absolute path on wildcard, so nothing to remove */
8535 if (!*template || *template == '/') {
8536 PerlMem_free(unixwild);
8537 if (base == fspec) {
8538 PerlMem_free(unixified);
8541 tmplen = strlen(unixified);
8542 if (tmplen > reslen) {
8543 PerlMem_free(unixified);
8544 return 0; /* not enough space */
8546 /* Copy unixified resultant, including trailing NUL */
8547 memmove(fspec,unixified,tmplen+1);
8548 PerlMem_free(unixified);
8552 for (end = base; *end; end++) ; /* Find end of resultant filespec */
8553 if ((cp1 = strstr(template,".../")) == NULL) { /* No ...; just count elts */
8554 for (cp1 = template; *cp1; cp1++) if (*cp1 == '/') dirs++;
8555 for (cp1 = end ;cp1 >= base; cp1--)
8556 if ((*cp1 == '/') && !dirs--) /* postdec so we get front of rel path */
8558 if (cp1 != fspec) memmove(fspec,cp1, end - cp1 + 1);
8559 PerlMem_free(unixified);
8560 PerlMem_free(unixwild);
8565 char *front, *nextell, *lcend, *lcfront, *ellipsis = cp1;
8566 int ells = 1, totells, segdirs, match;
8567 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL},
8568 resdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
8570 while ((cp1 = strstr(ellipsis+4,".../")) != NULL) {ellipsis = cp1; ells++;}
8572 for (cp1 = ellipsis+4; *cp1; cp1++) if (*cp1 == '/') dirs++;
8573 tpl = PerlMem_malloc(VMS_MAXRSS);
8574 if (tpl == NULL) _ckvmssts(SS$_INSFMEM);
8575 if (ellipsis == template && opts & 1) {
8576 /* Template begins with an ellipsis. Since we can't tell how many
8577 * directory names at the front of the resultant to keep for an
8578 * arbitrary starting point, we arbitrarily choose the current
8579 * default directory as a starting point. If it's there as a prefix,
8580 * clip it off. If not, fall through and act as if the leading
8581 * ellipsis weren't there (i.e. return shortest possible path that
8582 * could match template).
8584 if (getcwd(tpl, (VMS_MAXRSS - 1),0) == NULL) {
8586 PerlMem_free(unixified);
8587 PerlMem_free(unixwild);
8590 if (!decc_efs_case_preserve) {
8591 for (cp1 = tpl, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
8592 if (_tolower(*cp1) != _tolower(*cp2)) break;
8594 segdirs = dirs - totells; /* Min # of dirs we must have left */
8595 for (front = cp2+1; *front; front++) if (*front == '/') segdirs--;
8596 if (*cp1 == '\0' && *cp2 == '/' && segdirs < 1) {
8597 memmove(fspec,cp2+1,end - cp2);
8599 PerlMem_free(unixified);
8600 PerlMem_free(unixwild);
8604 /* First off, back up over constant elements at end of path */
8606 for (front = end ; front >= base; front--)
8607 if (*front == '/' && !dirs--) { front++; break; }
8609 lcres = PerlMem_malloc(VMS_MAXRSS);
8610 if (lcres == NULL) _ckvmssts(SS$_INSFMEM);
8611 for (cp1=template,cp2=lcres; *cp1 && cp2 <= lcres + (VMS_MAXRSS - 1);
8613 if (!decc_efs_case_preserve) {
8614 *cp2 = _tolower(*cp1); /* Make lc copy for match */
8622 PerlMem_free(unixified);
8623 PerlMem_free(unixwild);
8624 PerlMem_free(lcres);
8625 return 0; /* Path too long. */
8628 *cp2 = '\0'; /* Pick up with memcpy later */
8629 lcfront = lcres + (front - base);
8630 /* Now skip over each ellipsis and try to match the path in front of it. */
8632 for (cp1 = ellipsis - 2; cp1 >= template; cp1--)
8633 if (*(cp1) == '.' && *(cp1+1) == '.' &&
8634 *(cp1+2) == '.' && *(cp1+3) == '/' ) break;
8635 if (cp1 < template) break; /* template started with an ellipsis */
8636 if (cp1 + 4 == ellipsis) { /* Consecutive ellipses */
8637 ellipsis = cp1; continue;
8639 wilddsc.dsc$a_pointer = tpl;
8640 wilddsc.dsc$w_length = resdsc.dsc$w_length = ellipsis - 1 - cp1;
8642 for (segdirs = 0, cp2 = tpl;
8643 cp1 <= ellipsis - 1 && cp2 <= tpl + (VMS_MAXRSS-1);
8645 if (*cp1 == '?') *cp2 = '%'; /* Substitute VMS' wildcard for Unix' */
8647 if (!decc_efs_case_preserve) {
8648 *cp2 = _tolower(*cp1); /* else lowercase for match */
8651 *cp2 = *cp1; /* else preserve case for match */
8654 if (*cp2 == '/') segdirs++;
8656 if (cp1 != ellipsis - 1) {
8658 PerlMem_free(unixified);
8659 PerlMem_free(unixwild);
8660 PerlMem_free(lcres);
8661 return 0; /* Path too long */
8663 /* Back up at least as many dirs as in template before matching */
8664 for (cp1 = lcfront - 1; segdirs && cp1 >= lcres; cp1--)
8665 if (*cp1 == '/' && !segdirs--) { cp1++; break; }
8666 for (match = 0; cp1 > lcres;) {
8667 resdsc.dsc$a_pointer = cp1;
8668 if (str$match_wild(&wilddsc,&resdsc) == STR$_MATCH) {
8670 if (match == 1) lcfront = cp1;
8672 for ( ; cp1 >= lcres; cp1--) if (*cp1 == '/') { cp1++; break; }
8676 PerlMem_free(unixified);
8677 PerlMem_free(unixwild);
8678 PerlMem_free(lcres);
8679 return 0; /* Can't find prefix ??? */
8681 if (match > 1 && opts & 1) {
8682 /* This ... wildcard could cover more than one set of dirs (i.e.
8683 * a set of similar dir names is repeated). If the template
8684 * contains more than 1 ..., upstream elements could resolve the
8685 * ambiguity, but it's not worth a full backtracking setup here.
8686 * As a quick heuristic, clip off the current default directory
8687 * if it's present to find the trimmed spec, else use the
8688 * shortest string that this ... could cover.
8690 char def[NAM$C_MAXRSS+1], *st;
8692 if (getcwd(def, sizeof def,0) == NULL) {
8693 Safefree(unixified);
8699 if (!decc_efs_case_preserve) {
8700 for (cp1 = def, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
8701 if (_tolower(*cp1) != _tolower(*cp2)) break;
8703 segdirs = dirs - totells; /* Min # of dirs we must have left */
8704 for (st = cp2+1; *st; st++) if (*st == '/') segdirs--;
8705 if (*cp1 == '\0' && *cp2 == '/') {
8706 memmove(fspec,cp2+1,end - cp2);
8708 PerlMem_free(unixified);
8709 PerlMem_free(unixwild);
8710 PerlMem_free(lcres);
8713 /* Nope -- stick with lcfront from above and keep going. */
8716 memmove(fspec,base + (lcfront - lcres), lcend - lcfront + 1);
8718 PerlMem_free(unixified);
8719 PerlMem_free(unixwild);
8720 PerlMem_free(lcres);
8725 } /* end of trim_unixpath() */
8730 * VMS readdir() routines.
8731 * Written by Rich $alz, <rsalz@bbn.com> in August, 1990.
8733 * 21-Jul-1994 Charles Bailey bailey@newman.upenn.edu
8734 * Minor modifications to original routines.
8737 /* readdir may have been redefined by reentr.h, so make sure we get
8738 * the local version for what we do here.
8743 #if !defined(PERL_IMPLICIT_CONTEXT)
8744 # define readdir Perl_readdir
8746 # define readdir(a) Perl_readdir(aTHX_ a)
8749 /* Number of elements in vms_versions array */
8750 #define VERSIZE(e) (sizeof e->vms_versions / sizeof e->vms_versions[0])
8753 * Open a directory, return a handle for later use.
8755 /*{{{ DIR *opendir(char*name) */
8757 Perl_opendir(pTHX_ const char *name)
8763 Newx(dir, VMS_MAXRSS, char);
8764 if (do_tovmspath(name,dir,0,NULL) == NULL) {
8768 /* Check access before stat; otherwise stat does not
8769 * accurately report whether it's a directory.
8771 if (!cando_by_name_int(S_IRUSR,0,dir,PERL_RMSEXPAND_M_VMS_IN)) {
8772 /* cando_by_name has already set errno */
8776 if (flex_stat(dir,&sb) == -1) return NULL;
8777 if (!S_ISDIR(sb.st_mode)) {
8779 set_errno(ENOTDIR); set_vaxc_errno(RMS$_DIR);
8782 /* Get memory for the handle, and the pattern. */
8784 Newx(dd->pattern,strlen(dir)+sizeof "*.*" + 1,char);
8786 /* Fill in the fields; mainly playing with the descriptor. */
8787 sprintf(dd->pattern, "%s*.*",dir);
8792 /* By saying we always want the result of readdir() in unix format, we
8793 * are really saying we want all the escapes removed. Otherwise the caller,
8794 * having no way to know whether it's already in VMS format, might send it
8795 * through tovmsspec again, thus double escaping.
8797 dd->flags = PERL_VMSDIR_M_UNIXSPECS;
8798 dd->pat.dsc$a_pointer = dd->pattern;
8799 dd->pat.dsc$w_length = strlen(dd->pattern);
8800 dd->pat.dsc$b_dtype = DSC$K_DTYPE_T;
8801 dd->pat.dsc$b_class = DSC$K_CLASS_S;
8802 #if defined(USE_ITHREADS)
8803 Newx(dd->mutex,1,perl_mutex);
8804 MUTEX_INIT( (perl_mutex *) dd->mutex );
8810 } /* end of opendir() */
8814 * Set the flag to indicate we want versions or not.
8816 /*{{{ void vmsreaddirversions(DIR *dd, int flag)*/
8818 vmsreaddirversions(DIR *dd, int flag)
8821 dd->flags |= PERL_VMSDIR_M_VERSIONS;
8823 dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
8828 * Free up an opened directory.
8830 /*{{{ void closedir(DIR *dd)*/
8832 Perl_closedir(DIR *dd)
8836 sts = lib$find_file_end(&dd->context);
8837 Safefree(dd->pattern);
8838 #if defined(USE_ITHREADS)
8839 MUTEX_DESTROY( (perl_mutex *) dd->mutex );
8840 Safefree(dd->mutex);
8847 * Collect all the version numbers for the current file.
8850 collectversions(pTHX_ DIR *dd)
8852 struct dsc$descriptor_s pat;
8853 struct dsc$descriptor_s res;
8855 char *p, *text, *buff;
8857 unsigned long context, tmpsts;
8859 /* Convenient shorthand. */
8862 /* Add the version wildcard, ignoring the "*.*" put on before */
8863 i = strlen(dd->pattern);
8864 Newx(text,i + e->d_namlen + 3,char);
8865 strcpy(text, dd->pattern);
8866 sprintf(&text[i - 3], "%s;*", e->d_name);
8868 /* Set up the pattern descriptor. */
8869 pat.dsc$a_pointer = text;
8870 pat.dsc$w_length = i + e->d_namlen - 1;
8871 pat.dsc$b_dtype = DSC$K_DTYPE_T;
8872 pat.dsc$b_class = DSC$K_CLASS_S;
8874 /* Set up result descriptor. */
8875 Newx(buff, VMS_MAXRSS, char);
8876 res.dsc$a_pointer = buff;
8877 res.dsc$w_length = VMS_MAXRSS - 1;
8878 res.dsc$b_dtype = DSC$K_DTYPE_T;
8879 res.dsc$b_class = DSC$K_CLASS_S;
8881 /* Read files, collecting versions. */
8882 for (context = 0, e->vms_verscount = 0;
8883 e->vms_verscount < VERSIZE(e);
8884 e->vms_verscount++) {
8886 unsigned long flags = 0;
8888 #ifdef VMS_LONGNAME_SUPPORT
8889 flags = LIB$M_FIL_LONG_NAMES;
8891 tmpsts = lib$find_file(&pat, &res, &context, NULL, NULL, &rsts, &flags);
8892 if (tmpsts == RMS$_NMF || context == 0) break;
8894 buff[VMS_MAXRSS - 1] = '\0';
8895 if ((p = strchr(buff, ';')))
8896 e->vms_versions[e->vms_verscount] = atoi(p + 1);
8898 e->vms_versions[e->vms_verscount] = -1;
8901 _ckvmssts(lib$find_file_end(&context));
8905 } /* end of collectversions() */
8908 * Read the next entry from the directory.
8910 /*{{{ struct dirent *readdir(DIR *dd)*/
8912 Perl_readdir(pTHX_ DIR *dd)
8914 struct dsc$descriptor_s res;
8916 unsigned long int tmpsts;
8918 unsigned long flags = 0;
8919 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
8920 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
8922 /* Set up result descriptor, and get next file. */
8923 Newx(buff, VMS_MAXRSS, char);
8924 res.dsc$a_pointer = buff;
8925 res.dsc$w_length = VMS_MAXRSS - 1;
8926 res.dsc$b_dtype = DSC$K_DTYPE_T;
8927 res.dsc$b_class = DSC$K_CLASS_S;
8929 #ifdef VMS_LONGNAME_SUPPORT
8930 flags = LIB$M_FIL_LONG_NAMES;
8933 tmpsts = lib$find_file
8934 (&dd->pat, &res, &dd->context, NULL, NULL, &rsts, &flags);
8935 if ( tmpsts == RMS$_NMF || dd->context == 0) return NULL; /* None left. */
8936 if (!(tmpsts & 1)) {
8937 set_vaxc_errno(tmpsts);
8940 set_errno(EACCES); break;
8942 set_errno(ENODEV); break;
8944 set_errno(ENOTDIR); break;
8945 case RMS$_FNF: case RMS$_DNF:
8946 set_errno(ENOENT); break;
8954 /* Force the buffer to end with a NUL, and downcase name to match C convention. */
8955 if (!decc_efs_case_preserve) {
8956 buff[VMS_MAXRSS - 1] = '\0';
8957 for (p = buff; *p; p++) *p = _tolower(*p);
8960 /* we don't want to force to lowercase, just null terminate */
8961 buff[res.dsc$w_length] = '\0';
8963 while (--p >= buff) if (!isspace(*p)) break; /* Do we really need this? */
8966 /* Skip any directory component and just copy the name. */
8967 sts = vms_split_path
8982 /* Drop NULL extensions on UNIX file specification */
8983 if ((dd->flags & PERL_VMSDIR_M_UNIXSPECS &&
8984 (e_len == 1) && decc_readdir_dropdotnotype)) {
8989 strncpy(dd->entry.d_name, n_spec, n_len + e_len);
8990 dd->entry.d_name[n_len + e_len] = '\0';
8991 dd->entry.d_namlen = strlen(dd->entry.d_name);
8993 /* Convert the filename to UNIX format if needed */
8994 if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
8996 /* Translate the encoded characters. */
8997 /* Fixme: unicode handling could result in embedded 0 characters */
8998 if (strchr(dd->entry.d_name, '^') != NULL) {
9001 p = dd->entry.d_name;
9004 int inchars_read, outchars_added;
9005 inchars_read = copy_expand_vms_filename_escape(q, p, &outchars_added);
9007 q += outchars_added;
9009 /* if outchars_added > 1, then this is a wide file specification */
9010 /* Wide file specifications need to be passed in Perl */
9011 /* counted strings apparently with a unicode flag */
9014 strcpy(dd->entry.d_name, new_name);
9015 dd->entry.d_namlen = strlen(dd->entry.d_name);
9019 dd->entry.vms_verscount = 0;
9020 if (dd->flags & PERL_VMSDIR_M_VERSIONS) collectversions(aTHX_ dd);
9024 } /* end of readdir() */
9028 * Read the next entry from the directory -- thread-safe version.
9030 /*{{{ int readdir_r(DIR *dd, struct dirent *entry, struct dirent **result)*/
9032 Perl_readdir_r(pTHX_ DIR *dd, struct dirent *entry, struct dirent **result)
9036 MUTEX_LOCK( (perl_mutex *) dd->mutex );
9038 entry = readdir(dd);
9040 retval = ( *result == NULL ? errno : 0 );
9042 MUTEX_UNLOCK( (perl_mutex *) dd->mutex );
9046 } /* end of readdir_r() */
9050 * Return something that can be used in a seekdir later.
9052 /*{{{ long telldir(DIR *dd)*/
9054 Perl_telldir(DIR *dd)
9061 * Return to a spot where we used to be. Brute force.
9063 /*{{{ void seekdir(DIR *dd,long count)*/
9065 Perl_seekdir(pTHX_ DIR *dd, long count)
9069 /* If we haven't done anything yet... */
9073 /* Remember some state, and clear it. */
9074 old_flags = dd->flags;
9075 dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
9076 _ckvmssts(lib$find_file_end(&dd->context));
9079 /* The increment is in readdir(). */
9080 for (dd->count = 0; dd->count < count; )
9083 dd->flags = old_flags;
9085 } /* end of seekdir() */
9088 /* VMS subprocess management
9090 * my_vfork() - just a vfork(), after setting a flag to record that
9091 * the current script is trying a Unix-style fork/exec.
9093 * vms_do_aexec() and vms_do_exec() are called in response to the
9094 * perl 'exec' function. If this follows a vfork call, then they
9095 * call out the regular perl routines in doio.c which do an
9096 * execvp (for those who really want to try this under VMS).
9097 * Otherwise, they do exactly what the perl docs say exec should
9098 * do - terminate the current script and invoke a new command
9099 * (See below for notes on command syntax.)
9101 * do_aspawn() and do_spawn() implement the VMS side of the perl
9102 * 'system' function.
9104 * Note on command arguments to perl 'exec' and 'system': When handled
9105 * in 'VMSish fashion' (i.e. not after a call to vfork) The args
9106 * are concatenated to form a DCL command string. If the first arg
9107 * begins with '$' (i.e. the perl script had "\$ Type" or some such),
9108 * the command string is handed off to DCL directly. Otherwise,
9109 * the first token of the command is taken as the filespec of an image
9110 * to run. The filespec is expanded using a default type of '.EXE' and
9111 * the process defaults for device, directory, etc., and if found, the resultant
9112 * filespec is invoked using the DCL verb 'MCR', and passed the rest of
9113 * the command string as parameters. This is perhaps a bit complicated,
9114 * but I hope it will form a happy medium between what VMS folks expect
9115 * from lib$spawn and what Unix folks expect from exec.
9118 static int vfork_called;
9120 /*{{{int my_vfork()*/
9131 vms_execfree(struct dsc$descriptor_s *vmscmd)
9134 if (vmscmd->dsc$a_pointer) {
9135 PerlMem_free(vmscmd->dsc$a_pointer);
9137 PerlMem_free(vmscmd);
9142 setup_argstr(pTHX_ SV *really, SV **mark, SV **sp)
9144 char *junk, *tmps = Nullch;
9145 register size_t cmdlen = 0;
9152 tmps = SvPV(really,rlen);
9159 for (idx++; idx <= sp; idx++) {
9161 junk = SvPVx(*idx,rlen);
9162 cmdlen += rlen ? rlen + 1 : 0;
9165 Newx(PL_Cmd, cmdlen+1, char);
9167 if (tmps && *tmps) {
9168 strcpy(PL_Cmd,tmps);
9171 else *PL_Cmd = '\0';
9172 while (++mark <= sp) {
9174 char *s = SvPVx(*mark,n_a);
9176 if (*PL_Cmd) strcat(PL_Cmd," ");
9182 } /* end of setup_argstr() */
9185 static unsigned long int
9186 setup_cmddsc(pTHX_ const char *incmd, int check_img, int *suggest_quote,
9187 struct dsc$descriptor_s **pvmscmd)
9189 char vmsspec[NAM$C_MAXRSS+1], resspec[NAM$C_MAXRSS+1];
9190 char image_name[NAM$C_MAXRSS+1];
9191 char image_argv[NAM$C_MAXRSS+1];
9192 $DESCRIPTOR(defdsc,".EXE");
9193 $DESCRIPTOR(defdsc2,".");
9194 $DESCRIPTOR(resdsc,resspec);
9195 struct dsc$descriptor_s *vmscmd;
9196 struct dsc$descriptor_s imgdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
9197 unsigned long int cxt = 0, flags = 1, retsts = SS$_NORMAL;
9198 register char *s, *rest, *cp, *wordbreak;
9203 vmscmd = PerlMem_malloc(sizeof(struct dsc$descriptor_s));
9204 if (vmscmd == NULL) _ckvmssts(SS$_INSFMEM);
9206 /* Make a copy for modification */
9207 cmdlen = strlen(incmd);
9208 cmd = PerlMem_malloc(cmdlen+1);
9209 if (cmd == NULL) _ckvmssts(SS$_INSFMEM);
9210 strncpy(cmd, incmd, cmdlen);
9215 vmscmd->dsc$a_pointer = NULL;
9216 vmscmd->dsc$b_dtype = DSC$K_DTYPE_T;
9217 vmscmd->dsc$b_class = DSC$K_CLASS_S;
9218 vmscmd->dsc$w_length = 0;
9219 if (pvmscmd) *pvmscmd = vmscmd;
9221 if (suggest_quote) *suggest_quote = 0;
9223 if (strlen(cmd) > MAX_DCL_LINE_LENGTH) {
9225 return CLI$_BUFOVF; /* continuation lines currently unsupported */
9230 while (*s && isspace(*s)) s++;
9232 if (*s == '@' || *s == '$') {
9233 vmsspec[0] = *s; rest = s + 1;
9234 for (cp = &vmsspec[1]; *rest && isspace(*rest); rest++,cp++) *cp = *rest;
9236 else { cp = vmsspec; rest = s; }
9237 if (*rest == '.' || *rest == '/') {
9240 *rest && !isspace(*rest) && cp2 - resspec < sizeof resspec;
9241 rest++, cp2++) *cp2 = *rest;
9243 if (do_tovmsspec(resspec,cp,0,NULL)) {
9246 for (cp2 = vmsspec + strlen(vmsspec);
9247 *rest && cp2 - vmsspec < sizeof vmsspec;
9248 rest++, cp2++) *cp2 = *rest;
9253 /* Intuit whether verb (first word of cmd) is a DCL command:
9254 * - if first nonspace char is '@', it's a DCL indirection
9256 * - if verb contains a filespec separator, it's not a DCL command
9257 * - if it doesn't, caller tells us whether to default to a DCL
9258 * command, or to a local image unless told it's DCL (by leading '$')
9262 if (suggest_quote) *suggest_quote = 1;
9264 register char *filespec = strpbrk(s,":<[.;");
9265 rest = wordbreak = strpbrk(s," \"\t/");
9266 if (!wordbreak) wordbreak = s + strlen(s);
9267 if (*s == '$') check_img = 0;
9268 if (filespec && (filespec < wordbreak)) isdcl = 0;
9269 else isdcl = !check_img;
9274 imgdsc.dsc$a_pointer = s;
9275 imgdsc.dsc$w_length = wordbreak - s;
9276 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
9278 _ckvmssts(lib$find_file_end(&cxt));
9279 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
9280 if (!(retsts & 1) && *s == '$') {
9281 _ckvmssts(lib$find_file_end(&cxt));
9282 imgdsc.dsc$a_pointer++; imgdsc.dsc$w_length--;
9283 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
9285 _ckvmssts(lib$find_file_end(&cxt));
9286 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
9290 _ckvmssts(lib$find_file_end(&cxt));
9295 while (*s && !isspace(*s)) s++;
9298 /* check that it's really not DCL with no file extension */
9299 fp = fopen(resspec,"r","ctx=bin","ctx=rec","shr=get");
9301 char b[256] = {0,0,0,0};
9302 read(fileno(fp), b, 256);
9303 isdcl = isprint(b[0]) && isprint(b[1]) && isprint(b[2]) && isprint(b[3]);
9307 /* Check for script */
9309 if ((b[0] == '#') && (b[1] == '!'))
9311 #ifdef ALTERNATE_SHEBANG
9313 shebang_len = strlen(ALTERNATE_SHEBANG);
9314 if (strncmp(b, ALTERNATE_SHEBANG, shebang_len) == 0) {
9316 perlstr = strstr("perl",b);
9317 if (perlstr == NULL)
9325 if (shebang_len > 0) {
9328 char tmpspec[NAM$C_MAXRSS + 1];
9331 /* Image is following after white space */
9332 /*--------------------------------------*/
9333 while (isprint(b[i]) && isspace(b[i]))
9337 while (isprint(b[i]) && !isspace(b[i])) {
9338 tmpspec[j++] = b[i++];
9339 if (j >= NAM$C_MAXRSS)
9344 /* There may be some default parameters to the image */
9345 /*---------------------------------------------------*/
9347 while (isprint(b[i])) {
9348 image_argv[j++] = b[i++];
9349 if (j >= NAM$C_MAXRSS)
9352 while ((j > 0) && !isprint(image_argv[j-1]))
9356 /* It will need to be converted to VMS format and validated */
9357 if (tmpspec[0] != '\0') {
9360 /* Try to find the exact program requested to be run */
9361 /*---------------------------------------------------*/
9362 iname = do_rmsexpand
9363 (tmpspec, image_name, 0, ".exe",
9364 PERL_RMSEXPAND_M_VMS, NULL, NULL);
9365 if (iname != NULL) {
9366 if (cando_by_name_int
9367 (S_IXUSR,0,image_name,PERL_RMSEXPAND_M_VMS_IN)) {
9368 /* MCR prefix needed */
9372 /* Try again with a null type */
9373 /*----------------------------*/
9374 iname = do_rmsexpand
9375 (tmpspec, image_name, 0, ".",
9376 PERL_RMSEXPAND_M_VMS, NULL, NULL);
9377 if (iname != NULL) {
9378 if (cando_by_name_int
9379 (S_IXUSR,0,image_name, PERL_RMSEXPAND_M_VMS_IN)) {
9380 /* MCR prefix needed */
9386 /* Did we find the image to run the script? */
9387 /*------------------------------------------*/
9391 /* Assume DCL or foreign command exists */
9392 /*--------------------------------------*/
9393 tchr = strrchr(tmpspec, '/');
9400 strcpy(image_name, tchr);
9408 if (check_img && isdcl) return RMS$_FNF;
9410 if (cando_by_name(S_IXUSR,0,resspec)) {
9411 vmscmd->dsc$a_pointer = PerlMem_malloc(MAX_DCL_LINE_LENGTH);
9412 if (vmscmd->dsc$a_pointer == NULL) _ckvmssts(SS$_INSFMEM);
9414 strcpy(vmscmd->dsc$a_pointer,"$ MCR ");
9415 if (image_name[0] != 0) {
9416 strcat(vmscmd->dsc$a_pointer, image_name);
9417 strcat(vmscmd->dsc$a_pointer, " ");
9419 } else if (image_name[0] != 0) {
9420 strcpy(vmscmd->dsc$a_pointer, image_name);
9421 strcat(vmscmd->dsc$a_pointer, " ");
9423 strcpy(vmscmd->dsc$a_pointer,"@");
9425 if (suggest_quote) *suggest_quote = 1;
9427 /* If there is an image name, use original command */
9428 if (image_name[0] == 0)
9429 strcat(vmscmd->dsc$a_pointer,resspec);
9432 while (*rest && isspace(*rest)) rest++;
9435 if (image_argv[0] != 0) {
9436 strcat(vmscmd->dsc$a_pointer,image_argv);
9437 strcat(vmscmd->dsc$a_pointer, " ");
9443 rest_len = strlen(rest);
9444 vmscmd_len = strlen(vmscmd->dsc$a_pointer);
9445 if ((rest_len + vmscmd_len) < MAX_DCL_LINE_LENGTH)
9446 strcat(vmscmd->dsc$a_pointer,rest);
9448 retsts = CLI$_BUFOVF;
9450 vmscmd->dsc$w_length = strlen(vmscmd->dsc$a_pointer);
9452 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
9458 /* It's either a DCL command or we couldn't find a suitable image */
9459 vmscmd->dsc$w_length = strlen(cmd);
9461 vmscmd->dsc$a_pointer = PerlMem_malloc(vmscmd->dsc$w_length + 1);
9462 strncpy(vmscmd->dsc$a_pointer,cmd,vmscmd->dsc$w_length);
9463 vmscmd->dsc$a_pointer[vmscmd->dsc$w_length] = 0;
9467 /* check if it's a symbol (for quoting purposes) */
9468 if (suggest_quote && !*suggest_quote) {
9470 char equiv[LNM$C_NAMLENGTH];
9471 struct dsc$descriptor_s eqvdsc = {sizeof(equiv), DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
9472 eqvdsc.dsc$a_pointer = equiv;
9474 iss = lib$get_symbol(vmscmd,&eqvdsc);
9475 if (iss&1 && (*equiv == '$' || *equiv == '@')) *suggest_quote = 1;
9477 if (!(retsts & 1)) {
9478 /* just hand off status values likely to be due to user error */
9479 if (retsts == RMS$_FNF || retsts == RMS$_DNF || retsts == RMS$_PRV ||
9480 retsts == RMS$_DEV || retsts == RMS$_DIR || retsts == RMS$_SYN ||
9481 (retsts & STS$M_CODE) == (SHR$_NOWILD & STS$M_CODE)) return retsts;
9482 else { _ckvmssts(retsts); }
9485 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
9487 } /* end of setup_cmddsc() */
9490 /* {{{ bool vms_do_aexec(SV *really,SV **mark,SV **sp) */
9492 Perl_vms_do_aexec(pTHX_ SV *really,SV **mark,SV **sp)
9498 if (vfork_called) { /* this follows a vfork - act Unixish */
9500 if (vfork_called < 0) {
9501 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
9504 else return do_aexec(really,mark,sp);
9506 /* no vfork - act VMSish */
9507 cmd = setup_argstr(aTHX_ really,mark,sp);
9508 exec_sts = vms_do_exec(cmd);
9509 Safefree(cmd); /* Clean up from setup_argstr() */
9514 } /* end of vms_do_aexec() */
9517 /* {{{bool vms_do_exec(char *cmd) */
9519 Perl_vms_do_exec(pTHX_ const char *cmd)
9521 struct dsc$descriptor_s *vmscmd;
9523 if (vfork_called) { /* this follows a vfork - act Unixish */
9525 if (vfork_called < 0) {
9526 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
9529 else return do_exec(cmd);
9532 { /* no vfork - act VMSish */
9533 unsigned long int retsts;
9536 TAINT_PROPER("exec");
9537 if ((retsts = setup_cmddsc(aTHX_ cmd,1,0,&vmscmd)) & 1)
9538 retsts = lib$do_command(vmscmd);
9541 case RMS$_FNF: case RMS$_DNF:
9542 set_errno(ENOENT); break;
9544 set_errno(ENOTDIR); break;
9546 set_errno(ENODEV); break;
9548 set_errno(EACCES); break;
9550 set_errno(EINVAL); break;
9551 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
9552 set_errno(E2BIG); break;
9553 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
9554 _ckvmssts(retsts); /* fall through */
9555 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
9558 set_vaxc_errno(retsts);
9559 if (ckWARN(WARN_EXEC)) {
9560 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't exec \"%*s\": %s",
9561 vmscmd->dsc$w_length, vmscmd->dsc$a_pointer, Strerror(errno));
9563 vms_execfree(vmscmd);
9568 } /* end of vms_do_exec() */
9571 unsigned long int Perl_do_spawn(pTHX_ const char *);
9573 /* {{{ unsigned long int do_aspawn(void *really,void **mark,void **sp) */
9575 Perl_do_aspawn(pTHX_ void *really,void **mark,void **sp)
9577 unsigned long int sts;
9581 cmd = setup_argstr(aTHX_ (SV *)really,(SV **)mark,(SV **)sp);
9582 sts = do_spawn(cmd);
9583 /* pp_sys will clean up cmd */
9587 } /* end of do_aspawn() */
9590 /* {{{unsigned long int do_spawn(char *cmd) */
9592 Perl_do_spawn(pTHX_ const char *cmd)
9594 unsigned long int sts, substs;
9596 /* The caller of this routine expects to Safefree(PL_Cmd) */
9597 Newx(PL_Cmd,10,char);
9600 TAINT_PROPER("spawn");
9601 if (!cmd || !*cmd) {
9602 sts = lib$spawn(0,0,0,0,0,0,&substs,0,0,0,0,0,0);
9605 case RMS$_FNF: case RMS$_DNF:
9606 set_errno(ENOENT); break;
9608 set_errno(ENOTDIR); break;
9610 set_errno(ENODEV); break;
9612 set_errno(EACCES); break;
9614 set_errno(EINVAL); break;
9615 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
9616 set_errno(E2BIG); break;
9617 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
9618 _ckvmssts(sts); /* fall through */
9619 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
9622 set_vaxc_errno(sts);
9623 if (ckWARN(WARN_EXEC)) {
9624 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't spawn: %s",
9632 fp = safe_popen(aTHX_ cmd, "nW", (int *)&sts);
9637 } /* end of do_spawn() */
9641 static unsigned int *sockflags, sockflagsize;
9644 * Shim fdopen to identify sockets for my_fwrite later, since the stdio
9645 * routines found in some versions of the CRTL can't deal with sockets.
9646 * We don't shim the other file open routines since a socket isn't
9647 * likely to be opened by a name.
9649 /*{{{ FILE *my_fdopen(int fd, const char *mode)*/
9650 FILE *my_fdopen(int fd, const char *mode)
9652 FILE *fp = fdopen(fd, mode);
9655 unsigned int fdoff = fd / sizeof(unsigned int);
9656 Stat_t sbuf; /* native stat; we don't need flex_stat */
9657 if (!sockflagsize || fdoff > sockflagsize) {
9658 if (sockflags) Renew( sockflags,fdoff+2,unsigned int);
9659 else Newx (sockflags,fdoff+2,unsigned int);
9660 memset(sockflags+sockflagsize,0,fdoff + 2 - sockflagsize);
9661 sockflagsize = fdoff + 2;
9663 if (fstat(fd, (struct stat *)&sbuf) == 0 && S_ISSOCK(sbuf.st_mode))
9664 sockflags[fdoff] |= 1 << (fd % sizeof(unsigned int));
9673 * Clear the corresponding bit when the (possibly) socket stream is closed.
9674 * There still a small hole: we miss an implicit close which might occur
9675 * via freopen(). >> Todo
9677 /*{{{ int my_fclose(FILE *fp)*/
9678 int my_fclose(FILE *fp) {
9680 unsigned int fd = fileno(fp);
9681 unsigned int fdoff = fd / sizeof(unsigned int);
9683 if (sockflagsize && fdoff <= sockflagsize)
9684 sockflags[fdoff] &= ~(1 << fd % sizeof(unsigned int));
9692 * A simple fwrite replacement which outputs itmsz*nitm chars without
9693 * introducing record boundaries every itmsz chars.
9694 * We are using fputs, which depends on a terminating null. We may
9695 * well be writing binary data, so we need to accommodate not only
9696 * data with nulls sprinkled in the middle but also data with no null
9699 /*{{{ int my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)*/
9701 my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)
9703 register char *cp, *end, *cpd, *data;
9704 register unsigned int fd = fileno(dest);
9705 register unsigned int fdoff = fd / sizeof(unsigned int);
9707 int bufsize = itmsz * nitm + 1;
9709 if (fdoff < sockflagsize &&
9710 (sockflags[fdoff] | 1 << (fd % sizeof(unsigned int)))) {
9711 if (write(fd, src, itmsz * nitm) == EOF) return EOF;
9715 _ckvmssts_noperl(lib$get_vm(&bufsize, &data));
9716 memcpy( data, src, itmsz*nitm );
9717 data[itmsz*nitm] = '\0';
9719 end = data + itmsz * nitm;
9720 retval = (int) nitm; /* on success return # items written */
9723 while (cpd <= end) {
9724 for (cp = cpd; cp <= end; cp++) if (!*cp) break;
9725 if (fputs(cpd,dest) == EOF) { retval = EOF; break; }
9727 if (fputc('\0',dest) == EOF) { retval = EOF; break; }
9731 if (data) _ckvmssts_noperl(lib$free_vm(&bufsize, &data));
9734 } /* end of my_fwrite() */
9737 /*{{{ int my_flush(FILE *fp)*/
9739 Perl_my_flush(pTHX_ FILE *fp)
9742 if ((res = fflush(fp)) == 0 && fp) {
9743 #ifdef VMS_DO_SOCKETS
9745 if (Fstat(fileno(fp), &s) == 0 && !S_ISSOCK(s.st_mode))
9747 res = fsync(fileno(fp));
9750 * If the flush succeeded but set end-of-file, we need to clear
9751 * the error because our caller may check ferror(). BTW, this
9752 * probably means we just flushed an empty file.
9754 if (res == 0 && vaxc$errno == RMS$_EOF) clearerr(fp);
9761 * Here are replacements for the following Unix routines in the VMS environment:
9762 * getpwuid Get information for a particular UIC or UID
9763 * getpwnam Get information for a named user
9764 * getpwent Get information for each user in the rights database
9765 * setpwent Reset search to the start of the rights database
9766 * endpwent Finish searching for users in the rights database
9768 * getpwuid, getpwnam, and getpwent return a pointer to the passwd structure
9769 * (defined in pwd.h), which contains the following fields:-
9771 * char *pw_name; Username (in lower case)
9772 * char *pw_passwd; Hashed password
9773 * unsigned int pw_uid; UIC
9774 * unsigned int pw_gid; UIC group number
9775 * char *pw_unixdir; Default device/directory (VMS-style)
9776 * char *pw_gecos; Owner name
9777 * char *pw_dir; Default device/directory (Unix-style)
9778 * char *pw_shell; Default CLI name (eg. DCL)
9780 * If the specified user does not exist, getpwuid and getpwnam return NULL.
9782 * pw_uid is the full UIC (eg. what's returned by stat() in st_uid).
9783 * not the UIC member number (eg. what's returned by getuid()),
9784 * getpwuid() can accept either as input (if uid is specified, the caller's
9785 * UIC group is used), though it won't recognise gid=0.
9787 * Note that in VMS it is necessary to have GRPPRV or SYSPRV to return
9788 * information about other users in your group or in other groups, respectively.
9789 * If the required privilege is not available, then these routines fill only
9790 * the pw_name, pw_uid, and pw_gid fields (the others point to an empty
9793 * By Tim Adye (T.J.Adye@rl.ac.uk), 10th February 1995.
9796 /* sizes of various UAF record fields */
9797 #define UAI$S_USERNAME 12
9798 #define UAI$S_IDENT 31
9799 #define UAI$S_OWNER 31
9800 #define UAI$S_DEFDEV 31
9801 #define UAI$S_DEFDIR 63
9802 #define UAI$S_DEFCLI 31
9805 #define valid_uic(uic) ((uic).uic$v_format == UIC$K_UIC_FORMAT && \
9806 (uic).uic$v_member != UIC$K_WILD_MEMBER && \
9807 (uic).uic$v_group != UIC$K_WILD_GROUP)
9809 static char __empty[]= "";
9810 static struct passwd __passwd_empty=
9811 {(char *) __empty, (char *) __empty, 0, 0,
9812 (char *) __empty, (char *) __empty, (char *) __empty, (char *) __empty};
9813 static int contxt= 0;
9814 static struct passwd __pwdcache;
9815 static char __pw_namecache[UAI$S_IDENT+1];
9818 * This routine does most of the work extracting the user information.
9820 static int fillpasswd (pTHX_ const char *name, struct passwd *pwd)
9823 unsigned char length;
9824 char pw_gecos[UAI$S_OWNER+1];
9826 static union uicdef uic;
9828 unsigned char length;
9829 char pw_dir[UAI$S_DEFDEV+UAI$S_DEFDIR+1];
9832 unsigned char length;
9833 char unixdir[UAI$_DEFDEV+UAI$S_DEFDIR+1];
9836 unsigned char length;
9837 char pw_shell[UAI$S_DEFCLI+1];
9839 static char pw_passwd[UAI$S_PWD+1];
9841 static unsigned short lowner, luic, ldefdev, ldefdir, ldefcli, lpwd;
9842 struct dsc$descriptor_s name_desc;
9843 unsigned long int sts;
9845 static struct itmlst_3 itmlst[]= {
9846 {UAI$S_OWNER+1, UAI$_OWNER, &owner, &lowner},
9847 {sizeof(uic), UAI$_UIC, &uic, &luic},
9848 {UAI$S_DEFDEV+1, UAI$_DEFDEV, &defdev, &ldefdev},
9849 {UAI$S_DEFDIR+1, UAI$_DEFDIR, &defdir, &ldefdir},
9850 {UAI$S_DEFCLI+1, UAI$_DEFCLI, &defcli, &ldefcli},
9851 {UAI$S_PWD, UAI$_PWD, pw_passwd, &lpwd},
9852 {0, 0, NULL, NULL}};
9854 name_desc.dsc$w_length= strlen(name);
9855 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
9856 name_desc.dsc$b_class= DSC$K_CLASS_S;
9857 name_desc.dsc$a_pointer= (char *) name; /* read only pointer */
9859 /* Note that sys$getuai returns many fields as counted strings. */
9860 sts= sys$getuai(0, 0, &name_desc, &itmlst, 0, 0, 0);
9861 if (sts == SS$_NOSYSPRV || sts == SS$_NOGRPPRV || sts == RMS$_RNF) {
9862 set_vaxc_errno(sts); set_errno(sts == RMS$_RNF ? EINVAL : EACCES);
9864 else { _ckvmssts(sts); }
9865 if (!(sts & 1)) return 0; /* out here in case _ckvmssts() doesn't abort */
9867 if ((int) owner.length < lowner) lowner= (int) owner.length;
9868 if ((int) defdev.length < ldefdev) ldefdev= (int) defdev.length;
9869 if ((int) defdir.length < ldefdir) ldefdir= (int) defdir.length;
9870 if ((int) defcli.length < ldefcli) ldefcli= (int) defcli.length;
9871 memcpy(&defdev.pw_dir[ldefdev], &defdir.unixdir[0], ldefdir);
9872 owner.pw_gecos[lowner]= '\0';
9873 defdev.pw_dir[ldefdev+ldefdir]= '\0';
9874 defcli.pw_shell[ldefcli]= '\0';
9875 if (valid_uic(uic)) {
9876 pwd->pw_uid= uic.uic$l_uic;
9877 pwd->pw_gid= uic.uic$v_group;
9880 Perl_warn(aTHX_ "getpwnam returned invalid UIC %#o for user \"%s\"");
9881 pwd->pw_passwd= pw_passwd;
9882 pwd->pw_gecos= owner.pw_gecos;
9883 pwd->pw_dir= defdev.pw_dir;
9884 pwd->pw_unixdir= do_tounixpath(defdev.pw_dir, defdir.unixdir,1,NULL);
9885 pwd->pw_shell= defcli.pw_shell;
9886 if (pwd->pw_unixdir && pwd->pw_unixdir[0]) {
9888 ldir= strlen(pwd->pw_unixdir) - 1;
9889 if (pwd->pw_unixdir[ldir]=='/') pwd->pw_unixdir[ldir]= '\0';
9892 strcpy(pwd->pw_unixdir, pwd->pw_dir);
9893 if (!decc_efs_case_preserve)
9894 __mystrtolower(pwd->pw_unixdir);
9899 * Get information for a named user.
9901 /*{{{struct passwd *getpwnam(char *name)*/
9902 struct passwd *Perl_my_getpwnam(pTHX_ const char *name)
9904 struct dsc$descriptor_s name_desc;
9906 unsigned long int status, sts;
9908 __pwdcache = __passwd_empty;
9909 if (!fillpasswd(aTHX_ name, &__pwdcache)) {
9910 /* We still may be able to determine pw_uid and pw_gid */
9911 name_desc.dsc$w_length= strlen(name);
9912 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
9913 name_desc.dsc$b_class= DSC$K_CLASS_S;
9914 name_desc.dsc$a_pointer= (char *) name;
9915 if ((sts = sys$asctoid(&name_desc, &uic, 0)) == SS$_NORMAL) {
9916 __pwdcache.pw_uid= uic.uic$l_uic;
9917 __pwdcache.pw_gid= uic.uic$v_group;
9920 if (sts == SS$_NOSUCHID || sts == SS$_IVIDENT || sts == RMS$_PRV) {
9921 set_vaxc_errno(sts);
9922 set_errno(sts == RMS$_PRV ? EACCES : EINVAL);
9925 else { _ckvmssts(sts); }
9928 strncpy(__pw_namecache, name, sizeof(__pw_namecache));
9929 __pw_namecache[sizeof __pw_namecache - 1] = '\0';
9930 __pwdcache.pw_name= __pw_namecache;
9932 } /* end of my_getpwnam() */
9936 * Get information for a particular UIC or UID.
9937 * Called by my_getpwent with uid=-1 to list all users.
9939 /*{{{struct passwd *my_getpwuid(Uid_t uid)*/
9940 struct passwd *Perl_my_getpwuid(pTHX_ Uid_t uid)
9942 const $DESCRIPTOR(name_desc,__pw_namecache);
9943 unsigned short lname;
9945 unsigned long int status;
9947 if (uid == (unsigned int) -1) {
9949 status = sys$idtoasc(-1, &lname, &name_desc, &uic, 0, &contxt);
9950 if (status == SS$_NOSUCHID || status == RMS$_PRV) {
9951 set_vaxc_errno(status);
9952 set_errno(status == RMS$_PRV ? EACCES : EINVAL);
9956 else { _ckvmssts(status); }
9957 } while (!valid_uic (uic));
9961 if (!uic.uic$v_group)
9962 uic.uic$v_group= PerlProc_getgid();
9964 status = sys$idtoasc(uic.uic$l_uic, &lname, &name_desc, 0, 0, 0);
9965 else status = SS$_IVIDENT;
9966 if (status == SS$_IVIDENT || status == SS$_NOSUCHID ||
9967 status == RMS$_PRV) {
9968 set_vaxc_errno(status); set_errno(status == RMS$_PRV ? EACCES : EINVAL);
9971 else { _ckvmssts(status); }
9973 __pw_namecache[lname]= '\0';
9974 __mystrtolower(__pw_namecache);
9976 __pwdcache = __passwd_empty;
9977 __pwdcache.pw_name = __pw_namecache;
9979 /* Fill in the uid and gid in case fillpasswd can't (eg. no privilege).
9980 The identifier's value is usually the UIC, but it doesn't have to be,
9981 so if we can, we let fillpasswd update this. */
9982 __pwdcache.pw_uid = uic.uic$l_uic;
9983 __pwdcache.pw_gid = uic.uic$v_group;
9985 fillpasswd(aTHX_ __pw_namecache, &__pwdcache);
9988 } /* end of my_getpwuid() */
9992 * Get information for next user.
9994 /*{{{struct passwd *my_getpwent()*/
9995 struct passwd *Perl_my_getpwent(pTHX)
9997 return (my_getpwuid((unsigned int) -1));
10002 * Finish searching rights database for users.
10004 /*{{{void my_endpwent()*/
10005 void Perl_my_endpwent(pTHX)
10008 _ckvmssts(sys$finish_rdb(&contxt));
10014 #ifdef HOMEGROWN_POSIX_SIGNALS
10015 /* Signal handling routines, pulled into the core from POSIX.xs.
10017 * We need these for threads, so they've been rolled into the core,
10018 * rather than left in POSIX.xs.
10020 * (DRS, Oct 23, 1997)
10023 /* sigset_t is atomic under VMS, so these routines are easy */
10024 /*{{{int my_sigemptyset(sigset_t *) */
10025 int my_sigemptyset(sigset_t *set) {
10026 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
10027 *set = 0; return 0;
10032 /*{{{int my_sigfillset(sigset_t *)*/
10033 int my_sigfillset(sigset_t *set) {
10035 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
10036 for (i = 0; i < NSIG; i++) *set |= (1 << i);
10042 /*{{{int my_sigaddset(sigset_t *set, int sig)*/
10043 int my_sigaddset(sigset_t *set, int sig) {
10044 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
10045 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
10046 *set |= (1 << (sig - 1));
10052 /*{{{int my_sigdelset(sigset_t *set, int sig)*/
10053 int my_sigdelset(sigset_t *set, int sig) {
10054 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
10055 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
10056 *set &= ~(1 << (sig - 1));
10062 /*{{{int my_sigismember(sigset_t *set, int sig)*/
10063 int my_sigismember(sigset_t *set, int sig) {
10064 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
10065 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
10066 return *set & (1 << (sig - 1));
10071 /*{{{int my_sigprocmask(int how, sigset_t *set, sigset_t *oset)*/
10072 int my_sigprocmask(int how, sigset_t *set, sigset_t *oset) {
10075 /* If set and oset are both null, then things are badly wrong. Bail out. */
10076 if ((oset == NULL) && (set == NULL)) {
10077 set_errno(EFAULT); set_vaxc_errno(SS$_ACCVIO);
10081 /* If set's null, then we're just handling a fetch. */
10083 tempmask = sigblock(0);
10088 tempmask = sigsetmask(*set);
10091 tempmask = sigblock(*set);
10094 tempmask = sigblock(0);
10095 sigsetmask(*oset & ~tempmask);
10098 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
10103 /* Did they pass us an oset? If so, stick our holding mask into it */
10110 #endif /* HOMEGROWN_POSIX_SIGNALS */
10113 /* Used for UTC calculation in my_gmtime(), my_localtime(), my_time(),
10114 * my_utime(), and flex_stat(), all of which operate on UTC unless
10115 * VMSISH_TIMES is true.
10117 /* method used to handle UTC conversions:
10118 * 1 == CRTL gmtime(); 2 == SYS$TIMEZONE_DIFFERENTIAL; 3 == no correction
10120 static int gmtime_emulation_type;
10121 /* number of secs to add to UTC POSIX-style time to get local time */
10122 static long int utc_offset_secs;
10124 /* We #defined 'gmtime', 'localtime', and 'time' as 'my_gmtime' etc.
10125 * in vmsish.h. #undef them here so we can call the CRTL routines
10134 * DEC C previous to 6.0 corrupts the behavior of the /prefix
10135 * qualifier with the extern prefix pragma. This provisional
10136 * hack circumvents this prefix pragma problem in previous
10139 #if defined(__VMS_VER) && __VMS_VER >= 70000000
10140 # if defined(VMS_WE_ARE_CASE_SENSITIVE) && (__DECC_VER < 60000000)
10141 # pragma __extern_prefix save
10142 # pragma __extern_prefix "" /* set to empty to prevent prefixing */
10143 # define gmtime decc$__utctz_gmtime
10144 # define localtime decc$__utctz_localtime
10145 # define time decc$__utc_time
10146 # pragma __extern_prefix restore
10148 struct tm *gmtime(), *localtime();
10154 static time_t toutc_dst(time_t loc) {
10157 if ((rsltmp = localtime(&loc)) == NULL) return -1;
10158 loc -= utc_offset_secs;
10159 if (rsltmp->tm_isdst) loc -= 3600;
10162 #define _toutc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
10163 ((gmtime_emulation_type || my_time(NULL)), \
10164 (gmtime_emulation_type == 1 ? toutc_dst(secs) : \
10165 ((secs) - utc_offset_secs))))
10167 static time_t toloc_dst(time_t utc) {
10170 utc += utc_offset_secs;
10171 if ((rsltmp = localtime(&utc)) == NULL) return -1;
10172 if (rsltmp->tm_isdst) utc += 3600;
10175 #define _toloc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
10176 ((gmtime_emulation_type || my_time(NULL)), \
10177 (gmtime_emulation_type == 1 ? toloc_dst(secs) : \
10178 ((secs) + utc_offset_secs))))
10180 #ifndef RTL_USES_UTC
10183 ucx$tz = "EST5EDT4,M4.1.0,M10.5.0" typical
10184 DST starts on 1st sun of april at 02:00 std time
10185 ends on last sun of october at 02:00 dst time
10186 see the UCX management command reference, SET CONFIG TIMEZONE
10187 for formatting info.
10189 No, it's not as general as it should be, but then again, NOTHING
10190 will handle UK times in a sensible way.
10195 parse the DST start/end info:
10196 (Jddd|ddd|Mmon.nth.dow)[/hh:mm:ss]
10200 tz_parse_startend(char *s, struct tm *w, int *past)
10202 int dinm[] = {31,28,31,30,31,30,31,31,30,31,30,31};
10203 int ly, dozjd, d, m, n, hour, min, sec, j, k;
10208 if (!past) return 0;
10211 if (w->tm_year % 4 == 0) ly = 1;
10212 if (w->tm_year % 100 == 0) ly = 0;
10213 if (w->tm_year+1900 % 400 == 0) ly = 1;
10216 dozjd = isdigit(*s);
10217 if (*s == 'J' || *s == 'j' || dozjd) {
10218 if (!dozjd && !isdigit(*++s)) return 0;
10221 d = d*10 + *s++ - '0';
10223 d = d*10 + *s++ - '0';
10226 if (d == 0) return 0;
10227 if (d > 366) return 0;
10229 if (!dozjd && d > 58 && ly) d++; /* after 28 feb */
10232 } else if (*s == 'M' || *s == 'm') {
10233 if (!isdigit(*++s)) return 0;
10235 if (isdigit(*s)) m = 10*m + *s++ - '0';
10236 if (*s != '.') return 0;
10237 if (!isdigit(*++s)) return 0;
10239 if (n < 1 || n > 5) return 0;
10240 if (*s != '.') return 0;
10241 if (!isdigit(*++s)) return 0;
10243 if (d > 6) return 0;
10247 if (!isdigit(*++s)) return 0;
10249 if (isdigit(*s)) hour = 10*hour + *s++ - '0';
10251 if (!isdigit(*++s)) return 0;
10253 if (isdigit(*s)) min = 10*min + *s++ - '0';
10255 if (!isdigit(*++s)) return 0;
10257 if (isdigit(*s)) sec = 10*sec + *s++ - '0';
10267 if (w->tm_yday < d) goto before;
10268 if (w->tm_yday > d) goto after;
10270 if (w->tm_mon+1 < m) goto before;
10271 if (w->tm_mon+1 > m) goto after;
10273 j = (42 + w->tm_wday - w->tm_mday)%7; /*dow of mday 0 */
10274 k = d - j; /* mday of first d */
10275 if (k <= 0) k += 7;
10276 k += 7 * ((n>4?4:n)-1); /* mday of n'th d */
10277 if (n == 5 && k+7 <= dinm[w->tm_mon]) k += 7;
10278 if (w->tm_mday < k) goto before;
10279 if (w->tm_mday > k) goto after;
10282 if (w->tm_hour < hour) goto before;
10283 if (w->tm_hour > hour) goto after;
10284 if (w->tm_min < min) goto before;
10285 if (w->tm_min > min) goto after;
10286 if (w->tm_sec < sec) goto before;
10300 /* parse the offset: (+|-)hh[:mm[:ss]] */
10303 tz_parse_offset(char *s, int *offset)
10305 int hour = 0, min = 0, sec = 0;
10308 if (!offset) return 0;
10310 if (*s == '-') {neg++; s++;}
10311 if (*s == '+') s++;
10312 if (!isdigit(*s)) return 0;
10314 if (isdigit(*s)) hour = hour*10+(*s++ - '0');
10315 if (hour > 24) return 0;
10317 if (!isdigit(*++s)) return 0;
10319 if (isdigit(*s)) min = min*10 + (*s++ - '0');
10320 if (min > 59) return 0;
10322 if (!isdigit(*++s)) return 0;
10324 if (isdigit(*s)) sec = sec*10 + (*s++ - '0');
10325 if (sec > 59) return 0;
10329 *offset = (hour*60+min)*60 + sec;
10330 if (neg) *offset = -*offset;
10335 input time is w, whatever type of time the CRTL localtime() uses.
10336 sets dst, the zone, and the gmtoff (seconds)
10338 caches the value of TZ and UCX$TZ env variables; note that
10339 my_setenv looks for these and sets a flag if they're changed
10342 We have to watch out for the "australian" case (dst starts in
10343 october, ends in april)...flagged by "reverse" and checked by
10344 scanning through the months of the previous year.
10349 tz_parse(pTHX_ time_t *w, int *dst, char *zone, int *gmtoff)
10354 char *dstzone, *tz, *s_start, *s_end;
10355 int std_off, dst_off, isdst;
10356 int y, dststart, dstend;
10357 static char envtz[1025]; /* longer than any logical, symbol, ... */
10358 static char ucxtz[1025];
10359 static char reversed = 0;
10365 reversed = -1; /* flag need to check */
10366 envtz[0] = ucxtz[0] = '\0';
10367 tz = my_getenv("TZ",0);
10368 if (tz) strcpy(envtz, tz);
10369 tz = my_getenv("UCX$TZ",0);
10370 if (tz) strcpy(ucxtz, tz);
10371 if (!envtz[0] && !ucxtz[0]) return 0; /* we give up */
10374 if (!*tz) tz = ucxtz;
10377 while (isalpha(*s)) s++;
10378 s = tz_parse_offset(s, &std_off);
10380 if (!*s) { /* no DST, hurray we're done! */
10386 while (isalpha(*s)) s++;
10387 s2 = tz_parse_offset(s, &dst_off);
10391 dst_off = std_off - 3600;
10394 if (!*s) { /* default dst start/end?? */
10395 if (tz != ucxtz) { /* if TZ tells zone only, UCX$TZ tells rule */
10396 s = strchr(ucxtz,',');
10398 if (!s || !*s) s = ",M4.1.0,M10.5.0"; /* we know we do dst, default rule */
10400 if (*s != ',') return 0;
10403 when = _toutc(when); /* convert to utc */
10404 when = when - std_off; /* convert to pseudolocal time*/
10406 w2 = localtime(&when);
10409 s = tz_parse_startend(s_start,w2,&dststart);
10411 if (*s != ',') return 0;
10414 when = _toutc(when); /* convert to utc */
10415 when = when - dst_off; /* convert to pseudolocal time*/
10416 w2 = localtime(&when);
10417 if (w2->tm_year != y) { /* spans a year, just check one time */
10418 when += dst_off - std_off;
10419 w2 = localtime(&when);
10422 s = tz_parse_startend(s_end,w2,&dstend);
10425 if (reversed == -1) { /* need to check if start later than end */
10429 if (when < 2*365*86400) {
10430 when += 2*365*86400;
10434 w2 =localtime(&when);
10435 when = when + (15 - w2->tm_yday) * 86400; /* jan 15 */
10437 for (j = 0; j < 12; j++) {
10438 w2 =localtime(&when);
10439 tz_parse_startend(s_start,w2,&ds);
10440 tz_parse_startend(s_end,w2,&de);
10441 if (ds != de) break;
10445 if (de && !ds) reversed = 1;
10448 isdst = dststart && !dstend;
10449 if (reversed) isdst = dststart || !dstend;
10452 if (dst) *dst = isdst;
10453 if (gmtoff) *gmtoff = isdst ? dst_off : std_off;
10454 if (isdst) tz = dstzone;
10456 while(isalpha(*tz)) *zone++ = *tz++;
10462 #endif /* !RTL_USES_UTC */
10464 /* my_time(), my_localtime(), my_gmtime()
10465 * By default traffic in UTC time values, using CRTL gmtime() or
10466 * SYS$TIMEZONE_DIFFERENTIAL to determine offset from local time zone.
10467 * Note: We need to use these functions even when the CRTL has working
10468 * UTC support, since they also handle C<use vmsish qw(times);>
10470 * Contributed by Chuck Lane <lane@duphy4.physics.drexel.edu>
10471 * Modified by Charles Bailey <bailey@newman.upenn.edu>
10474 /*{{{time_t my_time(time_t *timep)*/
10475 time_t Perl_my_time(pTHX_ time_t *timep)
10480 if (gmtime_emulation_type == 0) {
10482 time_t base = 15 * 86400; /* 15jan71; to avoid month/year ends between */
10483 /* results of calls to gmtime() and localtime() */
10484 /* for same &base */
10486 gmtime_emulation_type++;
10487 if ((tm_p = gmtime(&base)) == NULL) { /* CRTL gmtime() is a fake */
10488 char off[LNM$C_NAMLENGTH+1];;
10490 gmtime_emulation_type++;
10491 if (!vmstrnenv("SYS$TIMEZONE_DIFFERENTIAL",off,0,fildev,0)) {
10492 gmtime_emulation_type++;
10493 utc_offset_secs = 0;
10494 Perl_warn(aTHX_ "no UTC offset information; assuming local time is UTC");
10496 else { utc_offset_secs = atol(off); }
10498 else { /* We've got a working gmtime() */
10499 struct tm gmt, local;
10502 tm_p = localtime(&base);
10504 utc_offset_secs = (local.tm_mday - gmt.tm_mday) * 86400;
10505 utc_offset_secs += (local.tm_hour - gmt.tm_hour) * 3600;
10506 utc_offset_secs += (local.tm_min - gmt.tm_min) * 60;
10507 utc_offset_secs += (local.tm_sec - gmt.tm_sec);
10512 # ifdef VMSISH_TIME
10513 # ifdef RTL_USES_UTC
10514 if (VMSISH_TIME) when = _toloc(when);
10516 if (!VMSISH_TIME) when = _toutc(when);
10519 if (timep != NULL) *timep = when;
10522 } /* end of my_time() */
10526 /*{{{struct tm *my_gmtime(const time_t *timep)*/
10528 Perl_my_gmtime(pTHX_ const time_t *timep)
10534 if (timep == NULL) {
10535 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
10538 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
10541 # ifdef VMSISH_TIME
10542 if (VMSISH_TIME) when = _toutc(when); /* Input was local time */
10544 # ifdef RTL_USES_UTC /* this implies that the CRTL has a working gmtime() */
10545 return gmtime(&when);
10547 /* CRTL localtime() wants local time as input, so does no tz correction */
10548 rsltmp = localtime(&when);
10549 if (rsltmp) rsltmp->tm_isdst = 0; /* We already took DST into account */
10552 } /* end of my_gmtime() */
10556 /*{{{struct tm *my_localtime(const time_t *timep)*/
10558 Perl_my_localtime(pTHX_ const time_t *timep)
10560 time_t when, whenutc;
10564 if (timep == NULL) {
10565 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
10568 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
10569 if (gmtime_emulation_type == 0) my_time(NULL); /* Init UTC */
10572 # ifdef RTL_USES_UTC
10573 # ifdef VMSISH_TIME
10574 if (VMSISH_TIME) when = _toutc(when);
10576 /* CRTL localtime() wants UTC as input, does tz correction itself */
10577 return localtime(&when);
10579 # else /* !RTL_USES_UTC */
10581 # ifdef VMSISH_TIME
10582 if (!VMSISH_TIME) when = _toloc(whenutc); /* input was UTC */
10583 if (VMSISH_TIME) whenutc = _toutc(when); /* input was truelocal */
10586 #ifndef RTL_USES_UTC
10587 if (tz_parse(aTHX_ &when, &dst, 0, &offset)) { /* truelocal determines DST*/
10588 when = whenutc - offset; /* pseudolocal time*/
10591 /* CRTL localtime() wants local time as input, so does no tz correction */
10592 rsltmp = localtime(&when);
10593 if (rsltmp && gmtime_emulation_type != 1) rsltmp->tm_isdst = dst;
10597 } /* end of my_localtime() */
10600 /* Reset definitions for later calls */
10601 #define gmtime(t) my_gmtime(t)
10602 #define localtime(t) my_localtime(t)
10603 #define time(t) my_time(t)
10606 /* my_utime - update modification/access time of a file
10608 * VMS 7.3 and later implementation
10609 * Only the UTC translation is home-grown. The rest is handled by the
10610 * CRTL utime(), which will take into account the relevant feature
10611 * logicals and ODS-5 volume characteristics for true access times.
10613 * pre VMS 7.3 implementation:
10614 * The calling sequence is identical to POSIX utime(), but under
10615 * VMS with ODS-2, only the modification time is changed; ODS-2 does
10616 * not maintain access times. Restrictions differ from the POSIX
10617 * definition in that the time can be changed as long as the
10618 * caller has permission to execute the necessary IO$_MODIFY $QIO;
10619 * no separate checks are made to insure that the caller is the
10620 * owner of the file or has special privs enabled.
10621 * Code here is based on Joe Meadows' FILE utility.
10625 /* Adjustment from Unix epoch (01-JAN-1970 00:00:00.00)
10626 * to VMS epoch (01-JAN-1858 00:00:00.00)
10627 * in 100 ns intervals.
10629 static const long int utime_baseadjust[2] = { 0x4beb4000, 0x7c9567 };
10631 /*{{{int my_utime(const char *path, const struct utimbuf *utimes)*/
10632 int Perl_my_utime(pTHX_ const char *file, const struct utimbuf *utimes)
10634 #if __CRTL_VER >= 70300000
10635 struct utimbuf utc_utimes, *utc_utimesp;
10637 if (utimes != NULL) {
10638 utc_utimes.actime = utimes->actime;
10639 utc_utimes.modtime = utimes->modtime;
10640 # ifdef VMSISH_TIME
10641 /* If input was local; convert to UTC for sys svc */
10643 utc_utimes.actime = _toutc(utimes->actime);
10644 utc_utimes.modtime = _toutc(utimes->modtime);
10647 utc_utimesp = &utc_utimes;
10650 utc_utimesp = NULL;
10653 return utime(file, utc_utimesp);
10655 #else /* __CRTL_VER < 70300000 */
10659 long int bintime[2], len = 2, lowbit, unixtime,
10660 secscale = 10000000; /* seconds --> 100 ns intervals */
10661 unsigned long int chan, iosb[2], retsts;
10662 char vmsspec[NAM$C_MAXRSS+1], rsa[NAM$C_MAXRSS], esa[NAM$C_MAXRSS];
10663 struct FAB myfab = cc$rms_fab;
10664 struct NAM mynam = cc$rms_nam;
10665 #if defined (__DECC) && defined (__VAX)
10666 /* VAX DEC C atrdef.h has unsigned type for pointer member atr$l_addr,
10667 * at least through VMS V6.1, which causes a type-conversion warning.
10669 # pragma message save
10670 # pragma message disable cvtdiftypes
10672 struct atrdef myatr[2] = {{sizeof bintime, ATR$C_REVDATE, bintime}, {0,0,0}};
10673 struct fibdef myfib;
10674 #if defined (__DECC) && defined (__VAX)
10675 /* This should be right after the declaration of myatr, but due
10676 * to a bug in VAX DEC C, this takes effect a statement early.
10678 # pragma message restore
10680 /* cast ok for read only parameter */
10681 struct dsc$descriptor fibdsc = {sizeof(myfib), DSC$K_DTYPE_Z, DSC$K_CLASS_S,(char *) &myfib},
10682 devdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0},
10683 fnmdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0};
10685 if (file == NULL || *file == '\0') {
10686 SETERRNO(ENOENT, LIB$_INVARG);
10690 /* Convert to VMS format ensuring that it will fit in 255 characters */
10691 if (do_rmsexpand(file, vmsspec, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL) == NULL) {
10692 SETERRNO(ENOENT, LIB$_INVARG);
10695 if (utimes != NULL) {
10696 /* Convert Unix time (seconds since 01-JAN-1970 00:00:00.00)
10697 * to VMS quadword time (100 nsec intervals since 01-JAN-1858 00:00:00.00).
10698 * Since time_t is unsigned long int, and lib$emul takes a signed long int
10699 * as input, we force the sign bit to be clear by shifting unixtime right
10700 * one bit, then multiplying by an extra factor of 2 in lib$emul().
10702 lowbit = (utimes->modtime & 1) ? secscale : 0;
10703 unixtime = (long int) utimes->modtime;
10704 # ifdef VMSISH_TIME
10705 /* If input was UTC; convert to local for sys svc */
10706 if (!VMSISH_TIME) unixtime = _toloc(unixtime);
10708 unixtime >>= 1; secscale <<= 1;
10709 retsts = lib$emul(&secscale, &unixtime, &lowbit, bintime);
10710 if (!(retsts & 1)) {
10711 SETERRNO(EVMSERR, retsts);
10714 retsts = lib$addx(bintime,utime_baseadjust,bintime,&len);
10715 if (!(retsts & 1)) {
10716 SETERRNO(EVMSERR, retsts);
10721 /* Just get the current time in VMS format directly */
10722 retsts = sys$gettim(bintime);
10723 if (!(retsts & 1)) {
10724 SETERRNO(EVMSERR, retsts);
10729 myfab.fab$l_fna = vmsspec;
10730 myfab.fab$b_fns = (unsigned char) strlen(vmsspec);
10731 myfab.fab$l_nam = &mynam;
10732 mynam.nam$l_esa = esa;
10733 mynam.nam$b_ess = (unsigned char) sizeof esa;
10734 mynam.nam$l_rsa = rsa;
10735 mynam.nam$b_rss = (unsigned char) sizeof rsa;
10736 if (decc_efs_case_preserve)
10737 mynam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
10739 /* Look for the file to be affected, letting RMS parse the file
10740 * specification for us as well. I have set errno using only
10741 * values documented in the utime() man page for VMS POSIX.
10743 retsts = sys$parse(&myfab,0,0);
10744 if (!(retsts & 1)) {
10745 set_vaxc_errno(retsts);
10746 if (retsts == RMS$_PRV) set_errno(EACCES);
10747 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
10748 else set_errno(EVMSERR);
10751 retsts = sys$search(&myfab,0,0);
10752 if (!(retsts & 1)) {
10753 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
10754 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
10755 set_vaxc_errno(retsts);
10756 if (retsts == RMS$_PRV) set_errno(EACCES);
10757 else if (retsts == RMS$_FNF) set_errno(ENOENT);
10758 else set_errno(EVMSERR);
10762 devdsc.dsc$w_length = mynam.nam$b_dev;
10763 /* cast ok for read only parameter */
10764 devdsc.dsc$a_pointer = (char *) mynam.nam$l_dev;
10766 retsts = sys$assign(&devdsc,&chan,0,0);
10767 if (!(retsts & 1)) {
10768 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
10769 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
10770 set_vaxc_errno(retsts);
10771 if (retsts == SS$_IVDEVNAM) set_errno(ENOTDIR);
10772 else if (retsts == SS$_NOPRIV) set_errno(EACCES);
10773 else if (retsts == SS$_NOSUCHDEV) set_errno(ENOTDIR);
10774 else set_errno(EVMSERR);
10778 fnmdsc.dsc$a_pointer = mynam.nam$l_name;
10779 fnmdsc.dsc$w_length = mynam.nam$b_name + mynam.nam$b_type + mynam.nam$b_ver;
10781 memset((void *) &myfib, 0, sizeof myfib);
10782 #if defined(__DECC) || defined(__DECCXX)
10783 for (i=0;i<3;i++) myfib.fib$w_fid[i] = mynam.nam$w_fid[i];
10784 for (i=0;i<3;i++) myfib.fib$w_did[i] = mynam.nam$w_did[i];
10785 /* This prevents the revision time of the file being reset to the current
10786 * time as a result of our IO$_MODIFY $QIO. */
10787 myfib.fib$l_acctl = FIB$M_NORECORD;
10789 for (i=0;i<3;i++) myfib.fib$r_fid_overlay.fib$w_fid[i] = mynam.nam$w_fid[i];
10790 for (i=0;i<3;i++) myfib.fib$r_did_overlay.fib$w_did[i] = mynam.nam$w_did[i];
10791 myfib.fib$r_acctl_overlay.fib$l_acctl = FIB$M_NORECORD;
10793 retsts = sys$qiow(0,chan,IO$_MODIFY,iosb,0,0,&fibdsc,&fnmdsc,0,0,myatr,0);
10794 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
10795 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
10796 _ckvmssts(sys$dassgn(chan));
10797 if (retsts & 1) retsts = iosb[0];
10798 if (!(retsts & 1)) {
10799 set_vaxc_errno(retsts);
10800 if (retsts == SS$_NOPRIV) set_errno(EACCES);
10801 else set_errno(EVMSERR);
10807 #endif /* #if __CRTL_VER >= 70300000 */
10809 } /* end of my_utime() */
10813 * flex_stat, flex_lstat, flex_fstat
10814 * basic stat, but gets it right when asked to stat
10815 * a Unix-style path ending in a directory name (e.g. dir1/dir2/dir3)
10818 #ifndef _USE_STD_STAT
10819 /* encode_dev packs a VMS device name string into an integer to allow
10820 * simple comparisons. This can be used, for example, to check whether two
10821 * files are located on the same device, by comparing their encoded device
10822 * names. Even a string comparison would not do, because stat() reuses the
10823 * device name buffer for each call; so without encode_dev, it would be
10824 * necessary to save the buffer and use strcmp (this would mean a number of
10825 * changes to the standard Perl code, to say nothing of what a Perl script
10826 * would have to do.
10828 * The device lock id, if it exists, should be unique (unless perhaps compared
10829 * with lock ids transferred from other nodes). We have a lock id if the disk is
10830 * mounted cluster-wide, which is when we tend to get long (host-qualified)
10831 * device names. Thus we use the lock id in preference, and only if that isn't
10832 * available, do we try to pack the device name into an integer (flagged by
10833 * the sign bit (LOCKID_MASK) being set).
10835 * Note that encode_dev cannot guarantee an 1-to-1 correspondence twixt device
10836 * name and its encoded form, but it seems very unlikely that we will find
10837 * two files on different disks that share the same encoded device names,
10838 * and even more remote that they will share the same file id (if the test
10839 * is to check for the same file).
10841 * A better method might be to use sys$device_scan on the first call, and to
10842 * search for the device, returning an index into the cached array.
10843 * The number returned would be more intelligible.
10844 * This is probably not worth it, and anyway would take quite a bit longer
10845 * on the first call.
10847 #define LOCKID_MASK 0x80000000 /* Use 0 to force device name use only */
10848 static mydev_t encode_dev (pTHX_ const char *dev)
10851 unsigned long int f;
10856 if (!dev || !dev[0]) return 0;
10860 struct dsc$descriptor_s dev_desc;
10861 unsigned long int status, lockid = 0, item = DVI$_LOCKID;
10863 /* For cluster-mounted disks, the disk lock identifier is unique, so we
10864 can try that first. */
10865 dev_desc.dsc$w_length = strlen (dev);
10866 dev_desc.dsc$b_dtype = DSC$K_DTYPE_T;
10867 dev_desc.dsc$b_class = DSC$K_CLASS_S;
10868 dev_desc.dsc$a_pointer = (char *) dev; /* Read only parameter */
10869 status = lib$getdvi(&item, 0, &dev_desc, &lockid, 0, 0);
10870 if (!$VMS_STATUS_SUCCESS(status)) {
10872 case SS$_NOSUCHDEV:
10873 SETERRNO(ENODEV, status);
10879 if (lockid) return (lockid & ~LOCKID_MASK);
10883 /* Otherwise we try to encode the device name */
10887 for (q = dev + strlen(dev); q--; q >= dev) {
10892 else if (isalpha (toupper (*q)))
10893 c= toupper (*q) - 'A' + (char)10;
10895 continue; /* Skip '$'s */
10897 if (i>6) break; /* 36^7 is too large to fit in an unsigned long int */
10899 enc += f * (unsigned long int) c;
10901 return (enc | LOCKID_MASK); /* May have already overflowed into bit 31 */
10903 } /* end of encode_dev() */
10904 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
10905 device_no = encode_dev(aTHX_ devname)
10907 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
10908 device_no = new_dev_no
10912 is_null_device(name)
10915 if (decc_bug_devnull != 0) {
10916 if (strncmp("/dev/null", name, 9) == 0)
10919 /* The VMS null device is named "_NLA0:", usually abbreviated as "NL:".
10920 The underscore prefix, controller letter, and unit number are
10921 independently optional; for our purposes, the colon punctuation
10922 is not. The colon can be trailed by optional directory and/or
10923 filename, but two consecutive colons indicates a nodename rather
10924 than a device. [pr] */
10925 if (*name == '_') ++name;
10926 if (tolower(*name++) != 'n') return 0;
10927 if (tolower(*name++) != 'l') return 0;
10928 if (tolower(*name) == 'a') ++name;
10929 if (*name == '0') ++name;
10930 return (*name++ == ':') && (*name != ':');
10935 Perl_cando_by_name_int
10936 (pTHX_ I32 bit, bool effective, const char *fname, int opts)
10938 char usrname[L_cuserid];
10939 struct dsc$descriptor_s usrdsc =
10940 {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, usrname};
10941 char *vmsname = NULL, *fileified = NULL;
10942 unsigned long int objtyp = ACL$C_FILE, access, retsts, privused, iosb[2], flags;
10943 unsigned short int retlen, trnlnm_iter_count;
10944 struct dsc$descriptor_s namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10945 union prvdef curprv;
10946 struct itmlst_3 armlst[4] = {{sizeof access, CHP$_ACCESS, &access, &retlen},
10947 {sizeof privused, CHP$_PRIVUSED, &privused, &retlen},
10948 {sizeof flags, CHP$_FLAGS, &flags, &retlen},{0,0,0,0}};
10949 struct itmlst_3 jpilst[3] = {{sizeof curprv, JPI$_CURPRIV, &curprv, &retlen},
10950 {sizeof usrname, JPI$_USERNAME, &usrname, &usrdsc.dsc$w_length},
10952 struct itmlst_3 usrprolst[2] = {{sizeof curprv, CHP$_PRIV, &curprv, &retlen},
10954 struct dsc$descriptor_s usrprodsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10956 static int profile_context = -1;
10958 if (!fname || !*fname) return FALSE;
10960 /* Make sure we expand logical names, since sys$check_access doesn't */
10961 fileified = PerlMem_malloc(VMS_MAXRSS);
10962 if (fileified == NULL) _ckvmssts(SS$_INSFMEM);
10963 if (!strpbrk(fname,"/]>:")) {
10964 strcpy(fileified,fname);
10965 trnlnm_iter_count = 0;
10966 while (!strpbrk(fileified,"/]>:") && my_trnlnm(fileified,fileified,0)) {
10967 trnlnm_iter_count++;
10968 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
10973 vmsname = PerlMem_malloc(VMS_MAXRSS);
10974 if (vmsname == NULL) _ckvmssts(SS$_INSFMEM);
10975 if ( !(opts & PERL_RMSEXPAND_M_VMS_IN) ) {
10976 /* Don't know if already in VMS format, so make sure */
10977 if (!do_rmsexpand(fname, vmsname, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL)) {
10978 PerlMem_free(fileified);
10979 PerlMem_free(vmsname);
10984 strcpy(vmsname,fname);
10987 /* sys$check_access needs a file spec, not a directory spec.
10988 * Don't use flex_stat here, as that depends on thread context
10989 * having been initialized, and we may get here during startup.
10992 retlen = namdsc.dsc$w_length = strlen(vmsname);
10993 if (vmsname[retlen-1] == ']'
10994 || vmsname[retlen-1] == '>'
10995 || vmsname[retlen-1] == ':'
10996 || (!stat(vmsname, (stat_t *)&st) && S_ISDIR(st.st_mode))) {
10998 if (!do_fileify_dirspec(vmsname,fileified,1,NULL)) {
10999 PerlMem_free(fileified);
11000 PerlMem_free(vmsname);
11009 retlen = namdsc.dsc$w_length = strlen(fname);
11010 namdsc.dsc$a_pointer = (char *)fname;
11013 case S_IXUSR: case S_IXGRP: case S_IXOTH:
11014 access = ARM$M_EXECUTE;
11015 flags = CHP$M_READ;
11017 case S_IRUSR: case S_IRGRP: case S_IROTH:
11018 access = ARM$M_READ;
11019 flags = CHP$M_READ | CHP$M_USEREADALL;
11021 case S_IWUSR: case S_IWGRP: case S_IWOTH:
11022 access = ARM$M_WRITE;
11023 flags = CHP$M_READ | CHP$M_WRITE;
11025 case S_IDUSR: case S_IDGRP: case S_IDOTH:
11026 access = ARM$M_DELETE;
11027 flags = CHP$M_READ | CHP$M_WRITE;
11030 if (fileified != NULL)
11031 PerlMem_free(fileified);
11032 if (vmsname != NULL)
11033 PerlMem_free(vmsname);
11037 /* Before we call $check_access, create a user profile with the current
11038 * process privs since otherwise it just uses the default privs from the
11039 * UAF and might give false positives or negatives. This only works on
11040 * VMS versions v6.0 and later since that's when sys$create_user_profile
11041 * became available.
11044 /* get current process privs and username */
11045 _ckvmssts(sys$getjpiw(0,0,0,jpilst,iosb,0,0));
11046 _ckvmssts(iosb[0]);
11048 #if defined(__VMS_VER) && __VMS_VER >= 60000000
11050 /* find out the space required for the profile */
11051 _ckvmssts(sys$create_user_profile(&usrdsc,&usrprolst,0,0,
11052 &usrprodsc.dsc$w_length,&profile_context));
11054 /* allocate space for the profile and get it filled in */
11055 usrprodsc.dsc$a_pointer = PerlMem_malloc(usrprodsc.dsc$w_length);
11056 if (usrprodsc.dsc$a_pointer == NULL) _ckvmssts(SS$_INSFMEM);
11057 _ckvmssts(sys$create_user_profile(&usrdsc,&usrprolst,0,usrprodsc.dsc$a_pointer,
11058 &usrprodsc.dsc$w_length,&profile_context));
11060 /* use the profile to check access to the file; free profile & analyze results */
11061 retsts = sys$check_access(&objtyp,&namdsc,0,armlst,&profile_context,0,0,&usrprodsc);
11062 PerlMem_free(usrprodsc.dsc$a_pointer);
11063 if (retsts == SS$_NOCALLPRIV) retsts = SS$_NOPRIV; /* not really 3rd party */
11067 retsts = sys$check_access(&objtyp,&namdsc,&usrdsc,armlst);
11071 if (retsts == SS$_NOPRIV || retsts == SS$_NOSUCHOBJECT ||
11072 retsts == SS$_INVFILFOROP || retsts == RMS$_FNF || retsts == RMS$_SYN ||
11073 retsts == RMS$_DIR || retsts == RMS$_DEV || retsts == RMS$_DNF) {
11074 set_vaxc_errno(retsts);
11075 if (retsts == SS$_NOPRIV) set_errno(EACCES);
11076 else if (retsts == SS$_INVFILFOROP) set_errno(EINVAL);
11077 else set_errno(ENOENT);
11078 if (fileified != NULL)
11079 PerlMem_free(fileified);
11080 if (vmsname != NULL)
11081 PerlMem_free(vmsname);
11084 if (retsts == SS$_NORMAL || retsts == SS$_ACCONFLICT) {
11085 if (fileified != NULL)
11086 PerlMem_free(fileified);
11087 if (vmsname != NULL)
11088 PerlMem_free(vmsname);
11093 if (fileified != NULL)
11094 PerlMem_free(fileified);
11095 if (vmsname != NULL)
11096 PerlMem_free(vmsname);
11097 return FALSE; /* Should never get here */
11101 /* Do the permissions allow some operation? Assumes PL_statcache already set. */
11102 /* Do this via $Check_Access on VMS, since the CRTL stat() returns only a
11103 * subset of the applicable information.
11106 Perl_cando(pTHX_ Mode_t bit, bool effective, const Stat_t *statbufp)
11108 return cando_by_name_int
11109 (bit, effective, statbufp->st_devnam, PERL_RMSEXPAND_M_VMS_IN);
11110 } /* end of cando() */
11114 /*{{{I32 cando_by_name(I32 bit, bool effective, char *fname)*/
11116 Perl_cando_by_name(pTHX_ I32 bit, bool effective, const char *fname)
11118 return cando_by_name_int(bit, effective, fname, 0);
11120 } /* end of cando_by_name() */
11124 /*{{{ int flex_fstat(int fd, Stat_t *statbuf)*/
11126 Perl_flex_fstat(pTHX_ int fd, Stat_t *statbufp)
11128 if (!fstat(fd,(stat_t *) statbufp)) {
11130 char *vms_filename;
11131 vms_filename = PerlMem_malloc(VMS_MAXRSS);
11132 if (vms_filename == NULL) _ckvmssts(SS$_INSFMEM);
11134 /* Save name for cando by name in VMS format */
11135 cptr = getname(fd, vms_filename, 1);
11137 /* This should not happen, but just in case */
11138 if (cptr == NULL) {
11139 statbufp->st_devnam[0] = 0;
11142 /* Make sure that the saved name fits in 255 characters */
11143 cptr = do_rmsexpand
11145 statbufp->st_devnam,
11148 PERL_RMSEXPAND_M_VMS | PERL_RMSEXPAND_M_VMS_IN,
11152 statbufp->st_devnam[0] = 0;
11154 PerlMem_free(vms_filename);
11156 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
11158 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
11160 # ifdef RTL_USES_UTC
11161 # ifdef VMSISH_TIME
11163 statbufp->st_mtime = _toloc(statbufp->st_mtime);
11164 statbufp->st_atime = _toloc(statbufp->st_atime);
11165 statbufp->st_ctime = _toloc(statbufp->st_ctime);
11169 # ifdef VMSISH_TIME
11170 if (!VMSISH_TIME) { /* Return UTC instead of local time */
11174 statbufp->st_mtime = _toutc(statbufp->st_mtime);
11175 statbufp->st_atime = _toutc(statbufp->st_atime);
11176 statbufp->st_ctime = _toutc(statbufp->st_ctime);
11183 } /* end of flex_fstat() */
11186 #if !defined(__VAX) && __CRTL_VER >= 80200000
11194 #define lstat(_x, _y) stat(_x, _y)
11197 #define flex_stat_int(a,b,c) Perl_flex_stat_int(aTHX_ a,b,c)
11200 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag)
11202 char fileified[VMS_MAXRSS];
11203 char temp_fspec[VMS_MAXRSS];
11206 int saved_errno, saved_vaxc_errno;
11208 if (!fspec) return retval;
11209 saved_errno = errno; saved_vaxc_errno = vaxc$errno;
11210 strcpy(temp_fspec, fspec);
11212 if (decc_bug_devnull != 0) {
11213 if (is_null_device(temp_fspec)) { /* Fake a stat() for the null device */
11214 memset(statbufp,0,sizeof *statbufp);
11215 VMS_DEVICE_ENCODE(statbufp->st_dev, "_NLA0:", 0);
11216 statbufp->st_mode = S_IFBLK | S_IREAD | S_IWRITE | S_IEXEC;
11217 statbufp->st_uid = 0x00010001;
11218 statbufp->st_gid = 0x0001;
11219 time((time_t *)&statbufp->st_mtime);
11220 statbufp->st_atime = statbufp->st_ctime = statbufp->st_mtime;
11225 /* Try for a directory name first. If fspec contains a filename without
11226 * a type (e.g. sea:[wine.dark]water), and both sea:[wine.dark]water.dir
11227 * and sea:[wine.dark]water. exist, we prefer the directory here.
11228 * Similarly, sea:[wine.dark] returns the result for sea:[wine]dark.dir,
11229 * not sea:[wine.dark]., if the latter exists. If the intended target is
11230 * the file with null type, specify this by calling flex_stat() with
11231 * a '.' at the end of fspec.
11233 * If we are in Posix filespec mode, accept the filename as is.
11237 #if __CRTL_VER >= 70300000 && !defined(__VAX)
11238 /* The CRTL stat() falls down hard on multi-dot filenames in unix format unless
11239 * DECC$EFS_CHARSET is in effect, so temporarily enable it if it isn't already.
11241 if (!decc_efs_charset)
11242 decc$feature_set_value(decc$feature_get_index("DECC$EFS_CHARSET"),1,1);
11245 #if __CRTL_VER >= 80200000 && !defined(__VAX)
11246 if (decc_posix_compliant_pathnames == 0) {
11248 if (do_fileify_dirspec(temp_fspec,fileified,0,NULL) != NULL) {
11249 if (lstat_flag == 0)
11250 retval = stat(fileified,(stat_t *) statbufp);
11252 retval = lstat(fileified,(stat_t *) statbufp);
11253 save_spec = fileified;
11256 if (lstat_flag == 0)
11257 retval = stat(temp_fspec,(stat_t *) statbufp);
11259 retval = lstat(temp_fspec,(stat_t *) statbufp);
11260 save_spec = temp_fspec;
11262 #if __CRTL_VER >= 80200000 && !defined(__VAX)
11264 if (lstat_flag == 0)
11265 retval = stat(temp_fspec,(stat_t *) statbufp);
11267 retval = lstat(temp_fspec,(stat_t *) statbufp);
11268 save_spec = temp_fspec;
11272 #if __CRTL_VER >= 70300000 && !defined(__VAX)
11273 /* As you were... */
11274 if (!decc_efs_charset)
11275 decc$feature_set_value(decc$feature_get_index("DECC$EFS_CHARSET"),1,0);
11280 cptr = do_rmsexpand
11281 (save_spec, statbufp->st_devnam, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL);
11283 statbufp->st_devnam[0] = 0;
11285 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
11287 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
11288 # ifdef RTL_USES_UTC
11289 # ifdef VMSISH_TIME
11291 statbufp->st_mtime = _toloc(statbufp->st_mtime);
11292 statbufp->st_atime = _toloc(statbufp->st_atime);
11293 statbufp->st_ctime = _toloc(statbufp->st_ctime);
11297 # ifdef VMSISH_TIME
11298 if (!VMSISH_TIME) { /* Return UTC instead of local time */
11302 statbufp->st_mtime = _toutc(statbufp->st_mtime);
11303 statbufp->st_atime = _toutc(statbufp->st_atime);
11304 statbufp->st_ctime = _toutc(statbufp->st_ctime);
11308 /* If we were successful, leave errno where we found it */
11309 if (retval == 0) { errno = saved_errno; vaxc$errno = saved_vaxc_errno; }
11312 } /* end of flex_stat_int() */
11315 /*{{{ int flex_stat(const char *fspec, Stat_t *statbufp)*/
11317 Perl_flex_stat(pTHX_ const char *fspec, Stat_t *statbufp)
11319 return flex_stat_int(fspec, statbufp, 0);
11323 /*{{{ int flex_lstat(const char *fspec, Stat_t *statbufp)*/
11325 Perl_flex_lstat(pTHX_ const char *fspec, Stat_t *statbufp)
11327 return flex_stat_int(fspec, statbufp, 1);
11332 /*{{{char *my_getlogin()*/
11333 /* VMS cuserid == Unix getlogin, except calling sequence */
11337 static char user[L_cuserid];
11338 return cuserid(user);
11343 /* rmscopy - copy a file using VMS RMS routines
11345 * Copies contents and attributes of spec_in to spec_out, except owner
11346 * and protection information. Name and type of spec_in are used as
11347 * defaults for spec_out. The third parameter specifies whether rmscopy()
11348 * should try to propagate timestamps from the input file to the output file.
11349 * If it is less than 0, no timestamps are preserved. If it is 0, then
11350 * rmscopy() will behave similarly to the DCL COPY command: timestamps are
11351 * propagated to the output file at creation iff the output file specification
11352 * did not contain an explicit name or type, and the revision date is always
11353 * updated at the end of the copy operation. If it is greater than 0, then
11354 * it is interpreted as a bitmask, in which bit 0 indicates that timestamps
11355 * other than the revision date should be propagated, and bit 1 indicates
11356 * that the revision date should be propagated.
11358 * Returns 1 on success; returns 0 and sets errno and vaxc$errno on failure.
11360 * Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>.
11361 * Incorporates, with permission, some code from EZCOPY by Tim Adye
11362 * <T.J.Adye@rl.ac.uk>. Permission is given to distribute this code
11363 * as part of the Perl standard distribution under the terms of the
11364 * GNU General Public License or the Perl Artistic License. Copies
11365 * of each may be found in the Perl standard distribution.
11367 /*{{{int rmscopy(char *src, char *dst, int preserve_dates)*/
11369 Perl_rmscopy(pTHX_ const char *spec_in, const char *spec_out, int preserve_dates)
11371 char *vmsin, * vmsout, *esa, *esa_out,
11373 unsigned long int i, sts, sts2;
11375 struct FAB fab_in, fab_out;
11376 struct RAB rab_in, rab_out;
11377 rms_setup_nam(nam);
11378 rms_setup_nam(nam_out);
11379 struct XABDAT xabdat;
11380 struct XABFHC xabfhc;
11381 struct XABRDT xabrdt;
11382 struct XABSUM xabsum;
11384 vmsin = PerlMem_malloc(VMS_MAXRSS);
11385 if (vmsin == NULL) _ckvmssts(SS$_INSFMEM);
11386 vmsout = PerlMem_malloc(VMS_MAXRSS);
11387 if (vmsout == NULL) _ckvmssts(SS$_INSFMEM);
11388 if (!spec_in || !*spec_in || !do_tovmsspec(spec_in,vmsin,1,NULL) ||
11389 !spec_out || !*spec_out || !do_tovmsspec(spec_out,vmsout,1,NULL)) {
11390 PerlMem_free(vmsin);
11391 PerlMem_free(vmsout);
11392 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11396 esa = PerlMem_malloc(VMS_MAXRSS);
11397 if (esa == NULL) _ckvmssts(SS$_INSFMEM);
11398 fab_in = cc$rms_fab;
11399 rms_set_fna(fab_in, nam, vmsin, strlen(vmsin));
11400 fab_in.fab$b_shr = FAB$M_SHRPUT | FAB$M_UPI;
11401 fab_in.fab$b_fac = FAB$M_BIO | FAB$M_GET;
11402 fab_in.fab$l_fop = FAB$M_SQO;
11403 rms_bind_fab_nam(fab_in, nam);
11404 fab_in.fab$l_xab = (void *) &xabdat;
11406 rsa = PerlMem_malloc(VMS_MAXRSS);
11407 if (rsa == NULL) _ckvmssts(SS$_INSFMEM);
11408 rms_set_rsa(nam, rsa, (VMS_MAXRSS-1));
11409 rms_set_esa(fab_in, nam, esa, (VMS_MAXRSS-1));
11410 rms_nam_esl(nam) = 0;
11411 rms_nam_rsl(nam) = 0;
11412 rms_nam_esll(nam) = 0;
11413 rms_nam_rsll(nam) = 0;
11414 #ifdef NAM$M_NO_SHORT_UPCASE
11415 if (decc_efs_case_preserve)
11416 rms_set_nam_nop(nam, NAM$M_NO_SHORT_UPCASE);
11419 xabdat = cc$rms_xabdat; /* To get creation date */
11420 xabdat.xab$l_nxt = (void *) &xabfhc;
11422 xabfhc = cc$rms_xabfhc; /* To get record length */
11423 xabfhc.xab$l_nxt = (void *) &xabsum;
11425 xabsum = cc$rms_xabsum; /* To get key and area information */
11427 if (!((sts = sys$open(&fab_in)) & 1)) {
11428 PerlMem_free(vmsin);
11429 PerlMem_free(vmsout);
11432 set_vaxc_errno(sts);
11434 case RMS$_FNF: case RMS$_DNF:
11435 set_errno(ENOENT); break;
11437 set_errno(ENOTDIR); break;
11439 set_errno(ENODEV); break;
11441 set_errno(EINVAL); break;
11443 set_errno(EACCES); break;
11445 set_errno(EVMSERR);
11452 fab_out.fab$w_ifi = 0;
11453 fab_out.fab$b_fac = FAB$M_BIO | FAB$M_PUT;
11454 fab_out.fab$b_shr = FAB$M_SHRGET | FAB$M_UPI;
11455 fab_out.fab$l_fop = FAB$M_SQO;
11456 rms_bind_fab_nam(fab_out, nam_out);
11457 rms_set_fna(fab_out, nam_out, vmsout, strlen(vmsout));
11458 dna_len = rms_nam_namel(nam) ? rms_nam_name_type_l_size(nam) : 0;
11459 rms_set_dna(fab_out, nam_out, rms_nam_namel(nam), dna_len);
11460 esa_out = PerlMem_malloc(VMS_MAXRSS);
11461 if (esa_out == NULL) _ckvmssts(SS$_INSFMEM);
11462 rms_set_rsa(nam_out, NULL, 0);
11463 rms_set_esa(fab_out, nam_out, esa_out, (VMS_MAXRSS - 1));
11465 if (preserve_dates == 0) { /* Act like DCL COPY */
11466 rms_set_nam_nop(nam_out, NAM$M_SYNCHK);
11467 fab_out.fab$l_xab = NULL; /* Don't disturb data from input file */
11468 if (!((sts = sys$parse(&fab_out)) & STS$K_SUCCESS)) {
11469 PerlMem_free(vmsin);
11470 PerlMem_free(vmsout);
11473 PerlMem_free(esa_out);
11474 set_errno(sts == RMS$_SYN ? EINVAL : EVMSERR);
11475 set_vaxc_errno(sts);
11478 fab_out.fab$l_xab = (void *) &xabdat;
11479 if (rms_is_nam_fnb(nam, NAM$M_EXP_NAME | NAM$M_EXP_TYPE))
11480 preserve_dates = 1;
11482 if (preserve_dates < 0) /* Clear all bits; we'll use it as a */
11483 preserve_dates =0; /* bitmask from this point forward */
11485 if (!(preserve_dates & 1)) fab_out.fab$l_xab = (void *) &xabfhc;
11486 if (!((sts = sys$create(&fab_out)) & STS$K_SUCCESS)) {
11487 PerlMem_free(vmsin);
11488 PerlMem_free(vmsout);
11491 PerlMem_free(esa_out);
11492 set_vaxc_errno(sts);
11495 set_errno(ENOENT); break;
11497 set_errno(ENOTDIR); break;
11499 set_errno(ENODEV); break;
11501 set_errno(EINVAL); break;
11503 set_errno(EACCES); break;
11505 set_errno(EVMSERR);
11509 fab_out.fab$l_fop |= FAB$M_DLT; /* in case we have to bail out */
11510 if (preserve_dates & 2) {
11511 /* sys$close() will process xabrdt, not xabdat */
11512 xabrdt = cc$rms_xabrdt;
11514 xabrdt.xab$q_rdt = xabdat.xab$q_rdt;
11516 /* gcc doesn't like the assignment, since its prototype for xab$q_rdt
11517 * is unsigned long[2], while DECC & VAXC use a struct */
11518 memcpy(xabrdt.xab$q_rdt,xabdat.xab$q_rdt,sizeof xabrdt.xab$q_rdt);
11520 fab_out.fab$l_xab = (void *) &xabrdt;
11523 ubf = PerlMem_malloc(32256);
11524 if (ubf == NULL) _ckvmssts(SS$_INSFMEM);
11525 rab_in = cc$rms_rab;
11526 rab_in.rab$l_fab = &fab_in;
11527 rab_in.rab$l_rop = RAB$M_BIO;
11528 rab_in.rab$l_ubf = ubf;
11529 rab_in.rab$w_usz = 32256;
11530 if (!((sts = sys$connect(&rab_in)) & 1)) {
11531 sys$close(&fab_in); sys$close(&fab_out);
11532 PerlMem_free(vmsin);
11533 PerlMem_free(vmsout);
11537 PerlMem_free(esa_out);
11538 set_errno(EVMSERR); set_vaxc_errno(sts);
11542 rab_out = cc$rms_rab;
11543 rab_out.rab$l_fab = &fab_out;
11544 rab_out.rab$l_rbf = ubf;
11545 if (!((sts = sys$connect(&rab_out)) & 1)) {
11546 sys$close(&fab_in); sys$close(&fab_out);
11547 PerlMem_free(vmsin);
11548 PerlMem_free(vmsout);
11552 PerlMem_free(esa_out);
11553 set_errno(EVMSERR); set_vaxc_errno(sts);
11557 while ((sts = sys$read(&rab_in))) { /* always true */
11558 if (sts == RMS$_EOF) break;
11559 rab_out.rab$w_rsz = rab_in.rab$w_rsz;
11560 if (!(sts & 1) || !((sts = sys$write(&rab_out)) & 1)) {
11561 sys$close(&fab_in); sys$close(&fab_out);
11562 PerlMem_free(vmsin);
11563 PerlMem_free(vmsout);
11567 PerlMem_free(esa_out);
11568 set_errno(EVMSERR); set_vaxc_errno(sts);
11574 fab_out.fab$l_fop &= ~FAB$M_DLT; /* We got this far; keep the output */
11575 sys$close(&fab_in); sys$close(&fab_out);
11576 sts = (fab_in.fab$l_sts & 1) ? fab_out.fab$l_sts : fab_in.fab$l_sts;
11578 PerlMem_free(vmsin);
11579 PerlMem_free(vmsout);
11583 PerlMem_free(esa_out);
11584 set_errno(EVMSERR); set_vaxc_errno(sts);
11588 PerlMem_free(vmsin);
11589 PerlMem_free(vmsout);
11593 PerlMem_free(esa_out);
11596 } /* end of rmscopy() */
11600 /*** The following glue provides 'hooks' to make some of the routines
11601 * from this file available from Perl. These routines are sufficiently
11602 * basic, and are required sufficiently early in the build process,
11603 * that's it's nice to have them available to miniperl as well as the
11604 * full Perl, so they're set up here instead of in an extension. The
11605 * Perl code which handles importation of these names into a given
11606 * package lives in [.VMS]Filespec.pm in @INC.
11610 rmsexpand_fromperl(pTHX_ CV *cv)
11613 char *fspec, *defspec = NULL, *rslt;
11615 int fs_utf8, dfs_utf8;
11619 if (!items || items > 2)
11620 Perl_croak(aTHX_ "Usage: VMS::Filespec::rmsexpand(spec[,defspec])");
11621 fspec = SvPV(ST(0),n_a);
11622 fs_utf8 = SvUTF8(ST(0));
11623 if (!fspec || !*fspec) XSRETURN_UNDEF;
11625 defspec = SvPV(ST(1),n_a);
11626 dfs_utf8 = SvUTF8(ST(1));
11628 rslt = do_rmsexpand(fspec,NULL,1,defspec,0,&fs_utf8,&dfs_utf8);
11629 ST(0) = sv_newmortal();
11630 if (rslt != NULL) {
11631 sv_usepvn(ST(0),rslt,strlen(rslt));
11640 vmsify_fromperl(pTHX_ CV *cv)
11647 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsify(spec)");
11648 utf8_fl = SvUTF8(ST(0));
11649 vmsified = do_tovmsspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
11650 ST(0) = sv_newmortal();
11651 if (vmsified != NULL) {
11652 sv_usepvn(ST(0),vmsified,strlen(vmsified));
11661 unixify_fromperl(pTHX_ CV *cv)
11668 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixify(spec)");
11669 utf8_fl = SvUTF8(ST(0));
11670 unixified = do_tounixspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
11671 ST(0) = sv_newmortal();
11672 if (unixified != NULL) {
11673 sv_usepvn(ST(0),unixified,strlen(unixified));
11682 fileify_fromperl(pTHX_ CV *cv)
11689 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::fileify(spec)");
11690 utf8_fl = SvUTF8(ST(0));
11691 fileified = do_fileify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
11692 ST(0) = sv_newmortal();
11693 if (fileified != NULL) {
11694 sv_usepvn(ST(0),fileified,strlen(fileified));
11703 pathify_fromperl(pTHX_ CV *cv)
11710 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::pathify(spec)");
11711 utf8_fl = SvUTF8(ST(0));
11712 pathified = do_pathify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
11713 ST(0) = sv_newmortal();
11714 if (pathified != NULL) {
11715 sv_usepvn(ST(0),pathified,strlen(pathified));
11724 vmspath_fromperl(pTHX_ CV *cv)
11731 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmspath(spec)");
11732 utf8_fl = SvUTF8(ST(0));
11733 vmspath = do_tovmspath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
11734 ST(0) = sv_newmortal();
11735 if (vmspath != NULL) {
11736 sv_usepvn(ST(0),vmspath,strlen(vmspath));
11745 unixpath_fromperl(pTHX_ CV *cv)
11752 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixpath(spec)");
11753 utf8_fl = SvUTF8(ST(0));
11754 unixpath = do_tounixpath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
11755 ST(0) = sv_newmortal();
11756 if (unixpath != NULL) {
11757 sv_usepvn(ST(0),unixpath,strlen(unixpath));
11766 candelete_fromperl(pTHX_ CV *cv)
11774 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::candelete(spec)");
11776 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
11777 Newx(fspec, VMS_MAXRSS, char);
11778 if (fspec == NULL) _ckvmssts(SS$_INSFMEM);
11779 if (SvTYPE(mysv) == SVt_PVGV) {
11780 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),fspec)) {
11781 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11789 if (mysv != ST(0) || !(fsp = SvPV(mysv,n_a)) || !*fsp) {
11790 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11797 ST(0) = boolSV(cando_by_name(S_IDUSR,0,fsp));
11803 rmscopy_fromperl(pTHX_ CV *cv)
11806 char *inspec, *outspec, *inp, *outp;
11808 struct dsc$descriptor indsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
11809 outdsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
11810 unsigned long int sts;
11815 if (items < 2 || items > 3)
11816 Perl_croak(aTHX_ "Usage: File::Copy::rmscopy(from,to[,date_flag])");
11818 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
11819 Newx(inspec, VMS_MAXRSS, char);
11820 if (SvTYPE(mysv) == SVt_PVGV) {
11821 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),inspec)) {
11822 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11830 if (mysv != ST(0) || !(inp = SvPV(mysv,n_a)) || !*inp) {
11831 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11837 mysv = SvROK(ST(1)) ? SvRV(ST(1)) : ST(1);
11838 Newx(outspec, VMS_MAXRSS, char);
11839 if (SvTYPE(mysv) == SVt_PVGV) {
11840 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),outspec)) {
11841 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11850 if (mysv != ST(1) || !(outp = SvPV(mysv,n_a)) || !*outp) {
11851 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11858 date_flag = (items == 3) ? SvIV(ST(2)) : 0;
11860 ST(0) = boolSV(rmscopy(inp,outp,date_flag));
11866 /* The mod2fname is limited to shorter filenames by design, so it should
11867 * not be modified to support longer EFS pathnames
11870 mod2fname(pTHX_ CV *cv)
11873 char ultimate_name[NAM$C_MAXRSS+1], work_name[NAM$C_MAXRSS*8 + 1],
11874 workbuff[NAM$C_MAXRSS*1 + 1];
11875 int total_namelen = 3, counter, num_entries;
11876 /* ODS-5 ups this, but we want to be consistent, so... */
11877 int max_name_len = 39;
11878 AV *in_array = (AV *)SvRV(ST(0));
11880 num_entries = av_len(in_array);
11882 /* All the names start with PL_. */
11883 strcpy(ultimate_name, "PL_");
11885 /* Clean up our working buffer */
11886 Zero(work_name, sizeof(work_name), char);
11888 /* Run through the entries and build up a working name */
11889 for(counter = 0; counter <= num_entries; counter++) {
11890 /* If it's not the first name then tack on a __ */
11892 strcat(work_name, "__");
11894 strcat(work_name, SvPV(*av_fetch(in_array, counter, FALSE),
11898 /* Check to see if we actually have to bother...*/
11899 if (strlen(work_name) + 3 <= max_name_len) {
11900 strcat(ultimate_name, work_name);
11902 /* It's too darned big, so we need to go strip. We use the same */
11903 /* algorithm as xsubpp does. First, strip out doubled __ */
11904 char *source, *dest, last;
11907 for (source = work_name; *source; source++) {
11908 if (last == *source && last == '_') {
11914 /* Go put it back */
11915 strcpy(work_name, workbuff);
11916 /* Is it still too big? */
11917 if (strlen(work_name) + 3 > max_name_len) {
11918 /* Strip duplicate letters */
11921 for (source = work_name; *source; source++) {
11922 if (last == toupper(*source)) {
11926 last = toupper(*source);
11928 strcpy(work_name, workbuff);
11931 /* Is it *still* too big? */
11932 if (strlen(work_name) + 3 > max_name_len) {
11933 /* Too bad, we truncate */
11934 work_name[max_name_len - 2] = 0;
11936 strcat(ultimate_name, work_name);
11939 /* Okay, return it */
11940 ST(0) = sv_2mortal(newSVpv(ultimate_name, 0));
11945 hushexit_fromperl(pTHX_ CV *cv)
11950 VMSISH_HUSHED = SvTRUE(ST(0));
11952 ST(0) = boolSV(VMSISH_HUSHED);
11958 Perl_vms_start_glob
11959 (pTHX_ SV *tmpglob,
11963 struct vs_str_st *rslt;
11967 $DESCRIPTOR(dfltdsc,"SYS$DISK:[]*.*;");
11970 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
11971 struct dsc$descriptor_vs rsdsc;
11972 unsigned long int cxt = 0, sts = 0, ok = 1, hasdir = 0;
11973 unsigned long hasver = 0, isunix = 0;
11974 unsigned long int lff_flags = 0;
11977 #ifdef VMS_LONGNAME_SUPPORT
11978 lff_flags = LIB$M_FIL_LONG_NAMES;
11980 /* The Newx macro will not allow me to assign a smaller array
11981 * to the rslt pointer, so we will assign it to the begin char pointer
11982 * and then copy the value into the rslt pointer.
11984 Newx(begin, VMS_MAXRSS + sizeof(unsigned short int), char);
11985 rslt = (struct vs_str_st *)begin;
11987 rstr = &rslt->str[0];
11988 rsdsc.dsc$a_pointer = (char *) rslt; /* cast required */
11989 rsdsc.dsc$w_maxstrlen = VMS_MAXRSS + sizeof(unsigned short int);
11990 rsdsc.dsc$b_dtype = DSC$K_DTYPE_VT;
11991 rsdsc.dsc$b_class = DSC$K_CLASS_VS;
11993 Newx(vmsspec, VMS_MAXRSS, char);
11995 /* We could find out if there's an explicit dev/dir or version
11996 by peeking into lib$find_file's internal context at
11997 ((struct NAM *)((struct FAB *)cxt)->fab$l_nam)->nam$l_fnb
11998 but that's unsupported, so I don't want to do it now and
11999 have it bite someone in the future. */
12000 /* Fix-me: vms_split_path() is the only way to do this, the
12001 existing method will fail with many legal EFS or UNIX specifications
12004 cp = SvPV(tmpglob,i);
12007 if (cp[i] == ';') hasver = 1;
12008 if (cp[i] == '.') {
12009 if (sts) hasver = 1;
12012 if (cp[i] == '/') {
12013 hasdir = isunix = 1;
12016 if (cp[i] == ']' || cp[i] == '>' || cp[i] == ':') {
12021 if ((tmpfp = PerlIO_tmpfile()) != NULL) {
12025 stat_sts = PerlLIO_stat(SvPVX_const(tmpglob),&st);
12026 if (!stat_sts && S_ISDIR(st.st_mode)) {
12027 wilddsc.dsc$a_pointer = tovmspath_utf8(SvPVX(tmpglob),vmsspec,NULL);
12028 ok = (wilddsc.dsc$a_pointer != NULL);
12029 /* maybe passed 'foo' rather than '[.foo]', thus not detected above */
12033 wilddsc.dsc$a_pointer = tovmsspec_utf8(SvPVX(tmpglob),vmsspec,NULL);
12034 ok = (wilddsc.dsc$a_pointer != NULL);
12037 wilddsc.dsc$w_length = strlen(wilddsc.dsc$a_pointer);
12039 /* If not extended character set, replace ? with % */
12040 /* With extended character set, ? is a wildcard single character */
12041 if (!decc_efs_case_preserve) {
12042 for (cp=wilddsc.dsc$a_pointer; ok && cp && *cp; cp++)
12043 if (*cp == '?') *cp = '%';
12046 while (ok && $VMS_STATUS_SUCCESS(sts)) {
12047 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
12048 int v_sts, v_len, r_len, d_len, n_len, e_len, vs_len;
12050 sts = lib$find_file(&wilddsc,&rsdsc,&cxt,
12051 &dfltdsc,NULL,&rms_sts,&lff_flags);
12052 if (!$VMS_STATUS_SUCCESS(sts))
12057 /* with varying string, 1st word of buffer contains result length */
12058 rstr[rslt->length] = '\0';
12060 /* Find where all the components are */
12061 v_sts = vms_split_path
12076 /* If no version on input, truncate the version on output */
12077 if (!hasver && (vs_len > 0)) {
12081 /* No version & a null extension on UNIX handling */
12082 if (isunix && (e_len == 1) && decc_readdir_dropdotnotype) {
12088 if (!decc_efs_case_preserve) {
12089 for (cp = rstr; *cp; cp++) *cp = _tolower(*cp);
12093 if (isunix) trim_unixpath(rstr,SvPVX(tmpglob),1);
12097 /* Start with the name */
12100 strcat(begin,"\n");
12101 ok = (PerlIO_puts(tmpfp,begin) != EOF);
12103 if (cxt) (void)lib$find_file_end(&cxt);
12106 /* Be POSIXish: return the input pattern when no matches */
12107 begin = SvPVX(tmpglob);
12108 strcat(begin,"\n");
12109 ok = (PerlIO_puts(tmpfp,begin) != EOF);
12112 if (ok && sts != RMS$_NMF &&
12113 sts != RMS$_DNF && sts != RMS_FNF) ok = 0;
12116 SETERRNO((sts == RMS$_SYN ? EINVAL : EVMSERR),sts);
12118 PerlIO_close(tmpfp);
12122 PerlIO_rewind(tmpfp);
12123 IoTYPE(io) = IoTYPE_RDONLY;
12124 IoIFP(io) = fp = tmpfp;
12125 IoFLAGS(io) &= ~IOf_UNTAINT; /* maybe redundant */
12136 mp_do_vms_realpath(pTHX_ const char *filespec, char * rslt_spec, const int *utf8_fl);
12139 vms_realpath_fromperl(pTHX_ CV *cv)
12142 char *fspec, *rslt_spec, *rslt;
12145 if (!items || items != 1)
12146 Perl_croak(aTHX_ "Usage: VMS::Filespec::vms_realpath(spec)");
12148 fspec = SvPV(ST(0),n_a);
12149 if (!fspec || !*fspec) XSRETURN_UNDEF;
12151 Newx(rslt_spec, VMS_MAXRSS + 1, char);
12152 rslt = do_vms_realpath(fspec, rslt_spec, NULL);
12153 ST(0) = sv_newmortal();
12155 sv_usepvn(ST(0),rslt,strlen(rslt));
12157 Safefree(rslt_spec);
12162 #if __CRTL_VER >= 70301000 && !defined(__VAX)
12163 int do_vms_case_tolerant(void);
12166 vms_case_tolerant_fromperl(pTHX_ CV *cv)
12169 ST(0) = boolSV(do_vms_case_tolerant());
12175 Perl_sys_intern_dup(pTHX_ struct interp_intern *src,
12176 struct interp_intern *dst)
12178 memcpy(dst,src,sizeof(struct interp_intern));
12182 Perl_sys_intern_clear(pTHX)
12187 Perl_sys_intern_init(pTHX)
12189 unsigned int ix = RAND_MAX;
12194 /* fix me later to track running under GNV */
12195 /* this allows some limited testing */
12196 MY_POSIX_EXIT = decc_filename_unix_report;
12199 MY_INV_RAND_MAX = 1./x;
12203 init_os_extras(void)
12206 char* file = __FILE__;
12207 if (decc_disable_to_vms_logname_translation) {
12208 no_translate_barewords = TRUE;
12210 no_translate_barewords = FALSE;
12213 newXSproto("VMS::Filespec::rmsexpand",rmsexpand_fromperl,file,"$;$");
12214 newXSproto("VMS::Filespec::vmsify",vmsify_fromperl,file,"$");
12215 newXSproto("VMS::Filespec::unixify",unixify_fromperl,file,"$");
12216 newXSproto("VMS::Filespec::pathify",pathify_fromperl,file,"$");
12217 newXSproto("VMS::Filespec::fileify",fileify_fromperl,file,"$");
12218 newXSproto("VMS::Filespec::vmspath",vmspath_fromperl,file,"$");
12219 newXSproto("VMS::Filespec::unixpath",unixpath_fromperl,file,"$");
12220 newXSproto("VMS::Filespec::candelete",candelete_fromperl,file,"$");
12221 newXSproto("DynaLoader::mod2fname", mod2fname, file, "$");
12222 newXS("File::Copy::rmscopy",rmscopy_fromperl,file);
12223 newXSproto("vmsish::hushed",hushexit_fromperl,file,";$");
12225 newXSproto("VMS::Filespec::vms_realpath",vms_realpath_fromperl,file,"$;$");
12227 #if __CRTL_VER >= 70301000 && !defined(__VAX)
12228 newXSproto("VMS::Filespec::case_tolerant",vms_case_tolerant_fromperl,file,"$;$");
12231 store_pipelocs(aTHX); /* will redo any earlier attempts */
12238 #if __CRTL_VER == 80200000
12239 /* This missed getting in to the DECC SDK for 8.2 */
12240 char *realpath(const char *file_name, char * resolved_name, ...);
12243 /*{{{char *do_vms_realpath(const char *file_name, char *resolved_name)*/
12244 /* wrapper for the realpath() function added with 8.2 RMS SYMLINK SDK.
12245 * The perl fallback routine to provide realpath() is not as efficient
12249 mp_do_vms_realpath(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
12251 return realpath(filespec, outbuf);
12255 /* External entry points */
12256 char *Perl_vms_realpath(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
12257 { return do_vms_realpath(filespec, outbuf, utf8_fl); }
12259 char *Perl_vms_realpath(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
12264 #if __CRTL_VER >= 70301000 && !defined(__VAX)
12265 /* case_tolerant */
12267 /*{{{int do_vms_case_tolerant(void)*/
12268 /* OpenVMS provides a case sensitive implementation of ODS-5 and this is
12269 * controlled by a process setting.
12271 int do_vms_case_tolerant(void)
12273 return vms_process_case_tolerant;
12276 /* External entry points */
12277 int Perl_vms_case_tolerant(void)
12278 { return do_vms_case_tolerant(); }
12280 int Perl_vms_case_tolerant(void)
12281 { return vms_process_case_tolerant; }
12285 /* Start of DECC RTL Feature handling */
12287 static int sys_trnlnm
12288 (const char * logname,
12292 const $DESCRIPTOR(table_dsc, "LNM$FILE_DEV");
12293 const unsigned long attr = LNM$M_CASE_BLIND;
12294 struct dsc$descriptor_s name_dsc;
12296 unsigned short result;
12297 struct itmlst_3 itlst[2] = {{value_len, LNM$_STRING, value, &result},
12300 name_dsc.dsc$w_length = strlen(logname);
12301 name_dsc.dsc$a_pointer = (char *)logname;
12302 name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
12303 name_dsc.dsc$b_class = DSC$K_CLASS_S;
12305 status = sys$trnlnm(&attr, &table_dsc, &name_dsc, 0, itlst);
12307 if ($VMS_STATUS_SUCCESS(status)) {
12309 /* Null terminate and return the string */
12310 /*--------------------------------------*/
12317 static int sys_crelnm
12318 (const char * logname,
12319 const char * value)
12322 const char * proc_table = "LNM$PROCESS_TABLE";
12323 struct dsc$descriptor_s proc_table_dsc;
12324 struct dsc$descriptor_s logname_dsc;
12325 struct itmlst_3 item_list[2];
12327 proc_table_dsc.dsc$a_pointer = (char *) proc_table;
12328 proc_table_dsc.dsc$w_length = strlen(proc_table);
12329 proc_table_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
12330 proc_table_dsc.dsc$b_class = DSC$K_CLASS_S;
12332 logname_dsc.dsc$a_pointer = (char *) logname;
12333 logname_dsc.dsc$w_length = strlen(logname);
12334 logname_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
12335 logname_dsc.dsc$b_class = DSC$K_CLASS_S;
12337 item_list[0].buflen = strlen(value);
12338 item_list[0].itmcode = LNM$_STRING;
12339 item_list[0].bufadr = (char *)value;
12340 item_list[0].retlen = NULL;
12342 item_list[1].buflen = 0;
12343 item_list[1].itmcode = 0;
12345 ret_val = sys$crelnm
12347 (const struct dsc$descriptor_s *)&proc_table_dsc,
12348 (const struct dsc$descriptor_s *)&logname_dsc,
12350 (const struct item_list_3 *) item_list);
12355 /* C RTL Feature settings */
12357 static int set_features
12358 (int (* init_coroutine)(int *, int *, void *), /* Needs casts if used */
12359 int (* cli_routine)(void), /* Not documented */
12360 void *image_info) /* Not documented */
12367 #if defined(JPI$_CASE_LOOKUP_PERM) && !defined(__VAX)
12368 const unsigned long int jpicode1 = JPI$_CASE_LOOKUP_PERM;
12369 const unsigned long int jpicode2 = JPI$_CASE_LOOKUP_IMAGE;
12370 unsigned long case_perm;
12371 unsigned long case_image;
12374 /* Allow an exception to bring Perl into the VMS debugger */
12375 vms_debug_on_exception = 0;
12376 status = sys_trnlnm("PERL_VMS_EXCEPTION_DEBUG", val_str, sizeof(val_str));
12377 if ($VMS_STATUS_SUCCESS(status)) {
12378 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
12379 vms_debug_on_exception = 1;
12381 vms_debug_on_exception = 0;
12384 /* Create VTF-7 filenames from UNICODE instead of UTF-8 */
12385 vms_vtf7_filenames = 0;
12386 status = sys_trnlnm("PERL_VMS_VTF7_FILENAMES", val_str, sizeof(val_str));
12387 if ($VMS_STATUS_SUCCESS(status)) {
12388 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
12389 vms_vtf7_filenames = 1;
12391 vms_vtf7_filenames = 0;
12394 /* Dectect running under GNV Bash or other UNIX like shell */
12395 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12396 gnv_unix_shell = 0;
12397 status = sys_trnlnm("GNV$UNIX_SHELL", val_str, sizeof(val_str));
12398 if ($VMS_STATUS_SUCCESS(status)) {
12399 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
12400 gnv_unix_shell = 1;
12401 set_feature_default("DECC$EFS_CASE_PRESERVE", 1);
12402 set_feature_default("DECC$EFS_CHARSET", 1);
12403 set_feature_default("DECC$FILENAME_UNIX_NO_VERSION", 1);
12404 set_feature_default("DECC$FILENAME_UNIX_REPORT", 1);
12405 set_feature_default("DECC$READDIR_DROPDOTNOTYPE", 1);
12406 set_feature_default("DECC$DISABLE_POSIX_ROOT", 0);
12409 gnv_unix_shell = 0;
12413 /* hacks to see if known bugs are still present for testing */
12415 /* Readdir is returning filenames in VMS syntax always */
12416 decc_bug_readdir_efs1 = 1;
12417 status = sys_trnlnm("DECC_BUG_READDIR_EFS1", val_str, sizeof(val_str));
12418 if ($VMS_STATUS_SUCCESS(status)) {
12419 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
12420 decc_bug_readdir_efs1 = 1;
12422 decc_bug_readdir_efs1 = 0;
12425 /* PCP mode requires creating /dev/null special device file */
12426 decc_bug_devnull = 0;
12427 status = sys_trnlnm("DECC_BUG_DEVNULL", val_str, sizeof(val_str));
12428 if ($VMS_STATUS_SUCCESS(status)) {
12429 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
12430 decc_bug_devnull = 1;
12432 decc_bug_devnull = 0;
12435 /* fgetname returning a VMS name in UNIX mode */
12436 decc_bug_fgetname = 1;
12437 status = sys_trnlnm("DECC_BUG_FGETNAME", val_str, sizeof(val_str));
12438 if ($VMS_STATUS_SUCCESS(status)) {
12439 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
12440 decc_bug_fgetname = 1;
12442 decc_bug_fgetname = 0;
12445 /* UNIX directory names with no paths are broken in a lot of places */
12446 decc_dir_barename = 1;
12447 status = sys_trnlnm("DECC_DIR_BARENAME", val_str, sizeof(val_str));
12448 if ($VMS_STATUS_SUCCESS(status)) {
12449 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
12450 decc_dir_barename = 1;
12452 decc_dir_barename = 0;
12455 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12456 s = decc$feature_get_index("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION");
12458 decc_disable_to_vms_logname_translation = decc$feature_get_value(s, 1);
12459 if (decc_disable_to_vms_logname_translation < 0)
12460 decc_disable_to_vms_logname_translation = 0;
12463 s = decc$feature_get_index("DECC$EFS_CASE_PRESERVE");
12465 decc_efs_case_preserve = decc$feature_get_value(s, 1);
12466 if (decc_efs_case_preserve < 0)
12467 decc_efs_case_preserve = 0;
12470 s = decc$feature_get_index("DECC$EFS_CHARSET");
12472 decc_efs_charset = decc$feature_get_value(s, 1);
12473 if (decc_efs_charset < 0)
12474 decc_efs_charset = 0;
12477 s = decc$feature_get_index("DECC$FILENAME_UNIX_REPORT");
12479 decc_filename_unix_report = decc$feature_get_value(s, 1);
12480 if (decc_filename_unix_report > 0)
12481 decc_filename_unix_report = 1;
12483 decc_filename_unix_report = 0;
12486 s = decc$feature_get_index("DECC$FILENAME_UNIX_ONLY");
12488 decc_filename_unix_only = decc$feature_get_value(s, 1);
12489 if (decc_filename_unix_only > 0) {
12490 decc_filename_unix_only = 1;
12493 decc_filename_unix_only = 0;
12497 s = decc$feature_get_index("DECC$FILENAME_UNIX_NO_VERSION");
12499 decc_filename_unix_no_version = decc$feature_get_value(s, 1);
12500 if (decc_filename_unix_no_version < 0)
12501 decc_filename_unix_no_version = 0;
12504 s = decc$feature_get_index("DECC$READDIR_DROPDOTNOTYPE");
12506 decc_readdir_dropdotnotype = decc$feature_get_value(s, 1);
12507 if (decc_readdir_dropdotnotype < 0)
12508 decc_readdir_dropdotnotype = 0;
12511 status = sys_trnlnm("SYS$POSIX_ROOT", val_str, sizeof(val_str));
12512 if ($VMS_STATUS_SUCCESS(status)) {
12513 s = decc$feature_get_index("DECC$DISABLE_POSIX_ROOT");
12515 dflt = decc$feature_get_value(s, 4);
12517 decc_disable_posix_root = decc$feature_get_value(s, 1);
12518 if (decc_disable_posix_root <= 0) {
12519 decc$feature_set_value(s, 1, 1);
12520 decc_disable_posix_root = 1;
12524 /* Traditionally Perl assumes this is off */
12525 decc_disable_posix_root = 1;
12526 decc$feature_set_value(s, 1, 1);
12531 #if __CRTL_VER >= 80200000
12532 s = decc$feature_get_index("DECC$POSIX_COMPLIANT_PATHNAMES");
12534 decc_posix_compliant_pathnames = decc$feature_get_value(s, 1);
12535 if (decc_posix_compliant_pathnames < 0)
12536 decc_posix_compliant_pathnames = 0;
12537 if (decc_posix_compliant_pathnames > 4)
12538 decc_posix_compliant_pathnames = 0;
12543 status = sys_trnlnm
12544 ("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION", val_str, sizeof(val_str));
12545 if ($VMS_STATUS_SUCCESS(status)) {
12546 val_str[0] = _toupper(val_str[0]);
12547 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
12548 decc_disable_to_vms_logname_translation = 1;
12553 status = sys_trnlnm("DECC$EFS_CASE_PRESERVE", val_str, sizeof(val_str));
12554 if ($VMS_STATUS_SUCCESS(status)) {
12555 val_str[0] = _toupper(val_str[0]);
12556 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
12557 decc_efs_case_preserve = 1;
12562 status = sys_trnlnm("DECC$FILENAME_UNIX_REPORT", val_str, sizeof(val_str));
12563 if ($VMS_STATUS_SUCCESS(status)) {
12564 val_str[0] = _toupper(val_str[0]);
12565 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
12566 decc_filename_unix_report = 1;
12569 status = sys_trnlnm("DECC$FILENAME_UNIX_ONLY", val_str, sizeof(val_str));
12570 if ($VMS_STATUS_SUCCESS(status)) {
12571 val_str[0] = _toupper(val_str[0]);
12572 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
12573 decc_filename_unix_only = 1;
12574 decc_filename_unix_report = 1;
12577 status = sys_trnlnm("DECC$FILENAME_UNIX_NO_VERSION", val_str, sizeof(val_str));
12578 if ($VMS_STATUS_SUCCESS(status)) {
12579 val_str[0] = _toupper(val_str[0]);
12580 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
12581 decc_filename_unix_no_version = 1;
12584 status = sys_trnlnm("DECC$READDIR_DROPDOTNOTYPE", val_str, sizeof(val_str));
12585 if ($VMS_STATUS_SUCCESS(status)) {
12586 val_str[0] = _toupper(val_str[0]);
12587 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
12588 decc_readdir_dropdotnotype = 1;
12593 #if defined(JPI$_CASE_LOOKUP_PERM) && !defined(__VAX)
12595 /* Report true case tolerance */
12596 /*----------------------------*/
12597 status = lib$getjpi(&jpicode1, 0, 0, &case_perm, 0, 0);
12598 if (!$VMS_STATUS_SUCCESS(status))
12599 case_perm = PPROP$K_CASE_BLIND;
12600 status = lib$getjpi(&jpicode2, 0, 0, &case_image, 0, 0);
12601 if (!$VMS_STATUS_SUCCESS(status))
12602 case_image = PPROP$K_CASE_BLIND;
12603 if ((case_perm == PPROP$K_CASE_SENSITIVE) ||
12604 (case_image == PPROP$K_CASE_SENSITIVE))
12605 vms_process_case_tolerant = 0;
12610 /* CRTL can be initialized past this point, but not before. */
12611 /* DECC$CRTL_INIT(); */
12618 #pragma extern_model save
12619 #pragma extern_model strict_refdef "LIB$INITIALIZ" nowrt
12620 const __align (LONGWORD) int spare[8] = {0};
12622 /* .psect LIB$INITIALIZE, NOPIC, USR, CON, REL, GBL, NOSHR, NOEXE, RD, NOWRT, LONG */
12623 #if __DECC_VER >= 60560002
12624 #pragma extern_model strict_refdef "LIB$INITIALIZE" nopic, con, rel, gbl, noshr, noexe, nowrt, long
12626 #pragma extern_model strict_refdef "LIB$INITIALIZE" nopic, con, gbl, noshr, nowrt, long
12628 #endif /* __DECC */
12630 const long vms_cc_features = (const long)set_features;
12633 ** Force a reference to LIB$INITIALIZE to ensure it
12634 ** exists in the image.
12636 int lib$initialize(void);
12638 #pragma extern_model strict_refdef
12640 int lib_init_ref = (int) lib$initialize;
12643 #pragma extern_model restore