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>
29 #include <libclidef.h>
31 #include <lib$routines.h>
34 #if __CRTL_VER >= 70301000 && !defined(__VAX)
44 #include <str$routines.h>
50 #if __CRTL_VER >= 70000000 /* FIXME to earliest version */
52 #define NO_EFN EFN$C_ENF
57 #if __CRTL_VER < 70301000 && __CRTL_VER >= 70300000
58 int decc$feature_get_index(const char *name);
59 char* decc$feature_get_name(int index);
60 int decc$feature_get_value(int index, int mode);
61 int decc$feature_set_value(int index, int mode, int value);
66 #pragma member_alignment save
67 #pragma nomember_alignment longword
72 unsigned short * retadr;
74 #pragma member_alignment restore
76 /* More specific prototype than in starlet_c.h makes programming errors
85 const struct dsc$descriptor_s * devnam,
86 const struct item_list_3 * itmlst,
88 void * (astadr)(unsigned long),
92 #if __CRTL_VER >= 70300000 && !defined(__VAX)
94 static int set_feature_default(const char *name, int value)
99 index = decc$feature_get_index(name);
101 status = decc$feature_set_value(index, 1, value);
102 if (index == -1 || (status == -1)) {
106 status = decc$feature_get_value(index, 1);
107 if (status != value) {
115 /* Older versions of ssdef.h don't have these */
116 #ifndef SS$_INVFILFOROP
117 # define SS$_INVFILFOROP 3930
119 #ifndef SS$_NOSUCHOBJECT
120 # define SS$_NOSUCHOBJECT 2696
123 /* We implement I/O here, so we will be mixing PerlIO and stdio calls. */
124 #define PERLIO_NOT_STDIO 0
126 /* Don't replace system definitions of vfork, getenv, lstat, and stat,
127 * code below needs to get to the underlying CRTL routines. */
128 #define DONT_MASK_RTL_CALLS
132 /* Anticipating future expansion in lexical warnings . . . */
133 #ifndef WARN_INTERNAL
134 # define WARN_INTERNAL WARN_MISC
137 #ifdef VMS_LONGNAME_SUPPORT
138 #include <libfildef.h>
141 #if defined(__VMS_VER) && __VMS_VER >= 70000000 && __DECC_VER >= 50200000
142 # define RTL_USES_UTC 1
146 /* gcc's header files don't #define direct access macros
147 * corresponding to VAXC's variant structs */
149 # define uic$v_format uic$r_uic_form.uic$v_format
150 # define uic$v_group uic$r_uic_form.uic$v_group
151 # define uic$v_member uic$r_uic_form.uic$v_member
152 # define prv$v_bypass prv$r_prvdef_bits0.prv$v_bypass
153 # define prv$v_grpprv prv$r_prvdef_bits0.prv$v_grpprv
154 # define prv$v_readall prv$r_prvdef_bits0.prv$v_readall
155 # define prv$v_sysprv prv$r_prvdef_bits0.prv$v_sysprv
158 #if defined(NEED_AN_H_ERRNO)
163 #pragma message disable pragma
164 #pragma member_alignment save
165 #pragma nomember_alignment longword
167 #pragma message disable misalgndmem
170 unsigned short int buflen;
171 unsigned short int itmcode;
173 unsigned short int *retlen;
176 struct filescan_itmlst_2 {
177 unsigned short length;
178 unsigned short itmcode;
183 unsigned short length;
188 #pragma message restore
189 #pragma member_alignment restore
192 #define do_fileify_dirspec(a,b,c,d) mp_do_fileify_dirspec(aTHX_ a,b,c,d)
193 #define do_pathify_dirspec(a,b,c,d) mp_do_pathify_dirspec(aTHX_ a,b,c,d)
194 #define do_tovmsspec(a,b,c,d) mp_do_tovmsspec(aTHX_ a,b,c,0,d)
195 #define do_tovmspath(a,b,c,d) mp_do_tovmspath(aTHX_ a,b,c,d)
196 #define do_rmsexpand(a,b,c,d,e,f,g) mp_do_rmsexpand(aTHX_ a,b,c,d,e,f,g)
197 #define do_vms_realpath(a,b,c) mp_do_vms_realpath(aTHX_ a,b,c)
198 #define do_tounixspec(a,b,c,d) mp_do_tounixspec(aTHX_ a,b,c,d)
199 #define do_tounixpath(a,b,c,d) mp_do_tounixpath(aTHX_ a,b,c,d)
200 #define do_vms_case_tolerant(a) mp_do_vms_case_tolerant(a)
201 #define expand_wild_cards(a,b,c,d) mp_expand_wild_cards(aTHX_ a,b,c,d)
202 #define getredirection(a,b) mp_getredirection(aTHX_ a,b)
204 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int *);
205 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int *);
206 static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *);
207 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int *);
209 /* see system service docs for $TRNLNM -- NOT the same as LNM$_MAX_INDEX */
210 #define PERL_LNM_MAX_ALLOWED_INDEX 127
212 /* OpenVMS User's Guide says at least 9 iterative translations will be performed,
213 * depending on the facility. SHOW LOGICAL does 10, so we'll imitate that for
216 #define PERL_LNM_MAX_ITER 10
218 /* New values with 7.3-2*/ /* why does max DCL have 4 byte subtracted from it? */
219 #if __CRTL_VER >= 70302000 && !defined(__VAX)
220 #define MAX_DCL_SYMBOL (8192)
221 #define MAX_DCL_LINE_LENGTH (4096 - 4)
223 #define MAX_DCL_SYMBOL (1024)
224 #define MAX_DCL_LINE_LENGTH (1024 - 4)
227 static char *__mystrtolower(char *str)
229 if (str) for (; *str; ++str) *str= tolower(*str);
233 static struct dsc$descriptor_s fildevdsc =
234 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "LNM$FILE_DEV" };
235 static struct dsc$descriptor_s crtlenvdsc =
236 { 8, DSC$K_DTYPE_T, DSC$K_CLASS_S, "CRTL_ENV" };
237 static struct dsc$descriptor_s *fildev[] = { &fildevdsc, NULL };
238 static struct dsc$descriptor_s *defenv[] = { &fildevdsc, &crtlenvdsc, NULL };
239 static struct dsc$descriptor_s **env_tables = defenv;
240 static bool will_taint = FALSE; /* tainting active, but no PL_curinterp yet */
242 /* True if we shouldn't treat barewords as logicals during directory */
244 static int no_translate_barewords;
247 static int tz_updated = 1;
250 /* DECC Features that may need to affect how Perl interprets
251 * displays filename information
253 static int decc_disable_to_vms_logname_translation = 1;
254 static int decc_disable_posix_root = 1;
255 int decc_efs_case_preserve = 0;
256 static int decc_efs_charset = 0;
257 static int decc_filename_unix_no_version = 0;
258 static int decc_filename_unix_only = 0;
259 int decc_filename_unix_report = 0;
260 int decc_posix_compliant_pathnames = 0;
261 int decc_readdir_dropdotnotype = 0;
262 static int vms_process_case_tolerant = 1;
263 int vms_vtf7_filenames = 0;
264 int gnv_unix_shell = 0;
266 /* bug workarounds if needed */
267 int decc_bug_readdir_efs1 = 0;
268 int decc_bug_devnull = 1;
269 int decc_bug_fgetname = 0;
270 int decc_dir_barename = 0;
272 static int vms_debug_on_exception = 0;
274 /* Is this a UNIX file specification?
275 * No longer a simple check with EFS file specs
276 * For now, not a full check, but need to
277 * handle POSIX ^UP^ specifications
278 * Fixing to handle ^/ cases would require
279 * changes to many other conversion routines.
282 static int is_unix_filespec(const char *path)
288 if (strncmp(path,"\"^UP^",5) != 0) {
289 pch1 = strchr(path, '/');
294 /* If the user wants UNIX files, "." needs to be treated as in UNIX */
295 if (decc_filename_unix_report || decc_filename_unix_only) {
296 if (strcmp(path,".") == 0)
304 /* This routine converts a UCS-2 character to be VTF-7 encoded.
307 static void ucs2_to_vtf7
309 unsigned long ucs2_char,
312 unsigned char * ucs_ptr;
315 ucs_ptr = (unsigned char *)&ucs2_char;
319 hex = (ucs_ptr[1] >> 4) & 0xf;
321 outspec[2] = hex + '0';
323 outspec[2] = (hex - 9) + 'A';
324 hex = ucs_ptr[1] & 0xF;
326 outspec[3] = hex + '0';
328 outspec[3] = (hex - 9) + 'A';
330 hex = (ucs_ptr[0] >> 4) & 0xf;
332 outspec[4] = hex + '0';
334 outspec[4] = (hex - 9) + 'A';
335 hex = ucs_ptr[1] & 0xF;
337 outspec[5] = hex + '0';
339 outspec[5] = (hex - 9) + 'A';
345 /* This handles the conversion of a UNIX extended character set to a ^
346 * escaped VMS character.
347 * in a UNIX file specification.
349 * The output count variable contains the number of characters added
350 * to the output string.
352 * The return value is the number of characters read from the input string
354 static int copy_expand_unix_filename_escape
355 (char *outspec, const char *inspec, int *output_cnt, const int * utf8_fl)
363 utf8_flag = *utf8_fl;
367 if (*inspec >= 0x80) {
368 if (utf8_fl && vms_vtf7_filenames) {
369 unsigned long ucs_char;
373 if ((*inspec & 0xE0) == 0xC0) {
375 ucs_char = ((inspec[0] & 0x1F) << 6) + (inspec[1] & 0x3f);
376 if (ucs_char >= 0x80) {
377 ucs2_to_vtf7(outspec, ucs_char, output_cnt);
380 } else if ((*inspec & 0xF0) == 0xE0) {
382 ucs_char = ((inspec[0] & 0xF) << 12) +
383 ((inspec[1] & 0x3f) << 6) +
385 if (ucs_char >= 0x800) {
386 ucs2_to_vtf7(outspec, ucs_char, output_cnt);
390 #if 0 /* I do not see longer sequences supported by OpenVMS */
391 /* Maybe some one can fix this later */
392 } else if ((*inspec & 0xF8) == 0xF0) {
395 } else if ((*inspec & 0xFC) == 0xF8) {
398 } else if ((*inspec & 0xFE) == 0xFC) {
405 /* High bit set, but not a unicode character! */
407 /* Non printing DECMCS or ISO Latin-1 character? */
408 if (*inspec <= 0x9F) {
412 hex = (*inspec >> 4) & 0xF;
414 outspec[1] = hex + '0';
416 outspec[1] = (hex - 9) + 'A';
420 outspec[2] = hex + '0';
422 outspec[2] = (hex - 9) + 'A';
426 } else if (*inspec == 0xA0) {
432 } else if (*inspec == 0xFF) {
444 /* Is this a macro that needs to be passed through?
445 * Macros start with $( and an alpha character, followed
446 * by a string of alpha numeric characters ending with a )
447 * If this does not match, then encode it as ODS-5.
449 if ((inspec[0] == '$') && (inspec[1] == '(')) {
452 if (isalnum(inspec[2]) || (inspec[2] == '.') || (inspec[2] == '_')) {
454 outspec[0] = inspec[0];
455 outspec[1] = inspec[1];
456 outspec[2] = inspec[2];
458 while(isalnum(inspec[tcnt]) ||
459 (inspec[2] == '.') || (inspec[2] == '_')) {
460 outspec[tcnt] = inspec[tcnt];
463 if (inspec[tcnt] == ')') {
464 outspec[tcnt] = inspec[tcnt];
481 if (decc_efs_charset == 0)
508 /* Assume that this is to be escaped */
510 outspec[1] = *inspec;
514 case ' ': /* space */
515 /* Assume that this is to be escaped */
530 /* This handles the expansion of a '^' prefix to the proper character
531 * in a UNIX file specification.
533 * The output count variable contains the number of characters added
534 * to the output string.
536 * The return value is the number of characters read from the input
539 static int copy_expand_vms_filename_escape
540 (char *outspec, const char *inspec, int *output_cnt)
547 if (*inspec == '^') {
551 /* Non trailing dots should just be passed through */
556 case '_': /* space */
562 case 'U': /* Unicode - FIX-ME this is wrong. */
565 scnt = strspn(inspec, "0123456789ABCDEFabcdef");
568 scnt = sscanf(inspec, "%2x%2x", &c1, &c2);
569 outspec[0] == c1 & 0xff;
570 outspec[1] == c2 & 0xff;
577 /* Error - do best we can to continue */
587 scnt = strspn(inspec, "0123456789ABCDEFabcdef");
591 scnt = sscanf(inspec, "%2x", &c1);
592 outspec[0] = c1 & 0xff;
615 (const struct dsc$descriptor_s * srcstr,
616 struct filescan_itmlst_2 * valuelist,
617 unsigned long * fldflags,
618 struct dsc$descriptor_s *auxout,
619 unsigned short * retlen);
621 /* vms_split_path - Verify that the input file specification is a
622 * VMS format file specification, and provide pointers to the components of
623 * it. With EFS format filenames, this is virtually the only way to
624 * parse a VMS path specification into components.
626 * If the sum of the components do not add up to the length of the
627 * string, then the passed file specification is probably a UNIX style
630 static int vms_split_path
645 struct dsc$descriptor path_desc;
649 struct filescan_itmlst_2 item_list[9];
650 const int filespec = 0;
651 const int nodespec = 1;
652 const int devspec = 2;
653 const int rootspec = 3;
654 const int dirspec = 4;
655 const int namespec = 5;
656 const int typespec = 6;
657 const int verspec = 7;
659 /* Assume the worst for an easy exit */
674 path_desc.dsc$a_pointer = (char *)path; /* cast ok */
675 path_desc.dsc$w_length = strlen(path);
676 path_desc.dsc$b_dtype = DSC$K_DTYPE_T;
677 path_desc.dsc$b_class = DSC$K_CLASS_S;
679 /* Get the total length, if it is shorter than the string passed
680 * then this was probably not a VMS formatted file specification
682 item_list[filespec].itmcode = FSCN$_FILESPEC;
683 item_list[filespec].length = 0;
684 item_list[filespec].component = NULL;
686 /* If the node is present, then it gets considered as part of the
687 * volume name to hopefully make things simple.
689 item_list[nodespec].itmcode = FSCN$_NODE;
690 item_list[nodespec].length = 0;
691 item_list[nodespec].component = NULL;
693 item_list[devspec].itmcode = FSCN$_DEVICE;
694 item_list[devspec].length = 0;
695 item_list[devspec].component = NULL;
697 /* root is a special case, adding it to either the directory or
698 * the device components will probalby complicate things for the
699 * callers of this routine, so leave it separate.
701 item_list[rootspec].itmcode = FSCN$_ROOT;
702 item_list[rootspec].length = 0;
703 item_list[rootspec].component = NULL;
705 item_list[dirspec].itmcode = FSCN$_DIRECTORY;
706 item_list[dirspec].length = 0;
707 item_list[dirspec].component = NULL;
709 item_list[namespec].itmcode = FSCN$_NAME;
710 item_list[namespec].length = 0;
711 item_list[namespec].component = NULL;
713 item_list[typespec].itmcode = FSCN$_TYPE;
714 item_list[typespec].length = 0;
715 item_list[typespec].component = NULL;
717 item_list[verspec].itmcode = FSCN$_VERSION;
718 item_list[verspec].length = 0;
719 item_list[verspec].component = NULL;
721 item_list[8].itmcode = 0;
722 item_list[8].length = 0;
723 item_list[8].component = NULL;
725 status = SYS$FILESCAN
726 ((const struct dsc$descriptor_s *)&path_desc, item_list,
728 _ckvmssts_noperl(status); /* All failure status values indicate a coding error */
730 /* If we parsed it successfully these two lengths should be the same */
731 if (path_desc.dsc$w_length != item_list[filespec].length)
734 /* If we got here, then it is a VMS file specification */
737 /* set the volume name */
738 if (item_list[nodespec].length > 0) {
739 *volume = item_list[nodespec].component;
740 *vol_len = item_list[nodespec].length + item_list[devspec].length;
743 *volume = item_list[devspec].component;
744 *vol_len = item_list[devspec].length;
747 *root = item_list[rootspec].component;
748 *root_len = item_list[rootspec].length;
750 *dir = item_list[dirspec].component;
751 *dir_len = item_list[dirspec].length;
753 /* Now fun with versions and EFS file specifications
754 * The parser can not tell the difference when a "." is a version
755 * delimiter or a part of the file specification.
757 if ((decc_efs_charset) &&
758 (item_list[verspec].length > 0) &&
759 (item_list[verspec].component[0] == '.')) {
760 *name = item_list[namespec].component;
761 *name_len = item_list[namespec].length + item_list[typespec].length;
762 *ext = item_list[verspec].component;
763 *ext_len = item_list[verspec].length;
768 *name = item_list[namespec].component;
769 *name_len = item_list[namespec].length;
770 *ext = item_list[typespec].component;
771 *ext_len = item_list[typespec].length;
772 *version = item_list[verspec].component;
773 *ver_len = item_list[verspec].length;
780 * Routine to retrieve the maximum equivalence index for an input
781 * logical name. Some calls to this routine have no knowledge if
782 * the variable is a logical or not. So on error we return a max
785 /*{{{int my_maxidx(const char *lnm) */
787 my_maxidx(const char *lnm)
791 int attr = LNM$M_CASE_BLIND;
792 struct dsc$descriptor lnmdsc;
793 struct itmlst_3 itlst[2] = {{sizeof(midx), LNM$_MAX_INDEX, &midx, 0},
796 lnmdsc.dsc$w_length = strlen(lnm);
797 lnmdsc.dsc$b_dtype = DSC$K_DTYPE_T;
798 lnmdsc.dsc$b_class = DSC$K_CLASS_S;
799 lnmdsc.dsc$a_pointer = (char *) lnm; /* Cast ok for read only parameter */
801 status = sys$trnlnm(&attr, &fildevdsc, &lnmdsc, 0, itlst);
802 if ((status & 1) == 0)
809 /*{{{int vmstrnenv(const char *lnm, char *eqv, unsigned long int idx, struct dsc$descriptor_s **tabvec, unsigned long int flags) */
811 Perl_vmstrnenv(const char *lnm, char *eqv, unsigned long int idx,
812 struct dsc$descriptor_s **tabvec, unsigned long int flags)
815 char uplnm[LNM$C_NAMLENGTH+1], *cp2;
816 unsigned short int eqvlen, curtab, ivlnm = 0, ivsym = 0, ivenv = 0, secure;
817 unsigned long int retsts, attr = LNM$M_CASE_BLIND;
819 unsigned char acmode;
820 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
821 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
822 struct itmlst_3 lnmlst[3] = {{sizeof idx, LNM$_INDEX, &idx, 0},
823 {LNM$C_NAMLENGTH, LNM$_STRING, eqv, &eqvlen},
825 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
826 #if defined(PERL_IMPLICIT_CONTEXT)
829 aTHX = PERL_GET_INTERP;
835 if (!lnm || !eqv || ((idx != 0) && ((idx-1) > PERL_LNM_MAX_ALLOWED_INDEX))) {
836 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return 0;
838 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
839 *cp2 = _toupper(*cp1);
840 if (cp1 - lnm > LNM$C_NAMLENGTH) {
841 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
845 lnmdsc.dsc$w_length = cp1 - lnm;
846 lnmdsc.dsc$a_pointer = uplnm;
847 uplnm[lnmdsc.dsc$w_length] = '\0';
848 secure = flags & PERL__TRNENV_SECURE;
849 acmode = secure ? PSL$C_EXEC : PSL$C_USER;
850 if (!tabvec || !*tabvec) tabvec = env_tables;
852 for (curtab = 0; tabvec[curtab]; curtab++) {
853 if (!str$case_blind_compare(tabvec[curtab],&crtlenv)) {
854 if (!ivenv && !secure) {
859 Perl_warn(aTHX_ "Can't read CRTL environ\n");
862 retsts = SS$_NOLOGNAM;
863 for (i = 0; environ[i]; i++) {
864 if ((eq = strchr(environ[i],'=')) &&
865 lnmdsc.dsc$w_length == (eq - environ[i]) &&
866 !strncmp(environ[i],uplnm,eq - environ[i])) {
868 for (eqvlen = 0; eq[eqvlen]; eqvlen++) eqv[eqvlen] = eq[eqvlen];
869 if (!eqvlen) continue;
874 if (retsts != SS$_NOLOGNAM) break;
877 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
878 !str$case_blind_compare(&tmpdsc,&clisym)) {
879 if (!ivsym && !secure) {
880 unsigned short int deflen = LNM$C_NAMLENGTH;
881 struct dsc$descriptor_d eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
882 /* dynamic dsc to accomodate possible long value */
883 _ckvmssts(lib$sget1_dd(&deflen,&eqvdsc));
884 retsts = lib$get_symbol(&lnmdsc,&eqvdsc,&eqvlen,0);
886 if (eqvlen > MAX_DCL_SYMBOL) {
887 set_errno(EVMSERR); set_vaxc_errno(LIB$_STRTRU);
888 eqvlen = MAX_DCL_SYMBOL;
889 /* Special hack--we might be called before the interpreter's */
890 /* fully initialized, in which case either thr or PL_curcop */
891 /* might be bogus. We have to check, since ckWARN needs them */
892 /* both to be valid if running threaded */
893 if (ckWARN(WARN_MISC)) {
894 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of CLI symbol \"%s\" too long",lnm);
897 strncpy(eqv,eqvdsc.dsc$a_pointer,eqvlen);
899 _ckvmssts(lib$sfree1_dd(&eqvdsc));
900 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
901 if (retsts == LIB$_NOSUCHSYM) continue;
906 if ( (idx == 0) && (flags & PERL__TRNENV_JOIN_SEARCHLIST) ) {
907 midx = my_maxidx(lnm);
908 for (idx = 0, cp2 = eqv; idx <= midx; idx++) {
909 lnmlst[1].bufadr = cp2;
911 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
912 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; break; }
913 if (retsts == SS$_NOLOGNAM) break;
914 /* PPFs have a prefix */
917 *((int *)uplnm) == *((int *)"SYS$") &&
919 eqvlen >= 4 && eqv[0] == 0x1b && eqv[1] == 0x00 &&
920 ( (uplnm[4] == 'O' && !strcmp(uplnm,"SYS$OUTPUT")) ||
921 (uplnm[4] == 'I' && !strcmp(uplnm,"SYS$INPUT")) ||
922 (uplnm[4] == 'E' && !strcmp(uplnm,"SYS$ERROR")) ||
923 (uplnm[4] == 'C' && !strcmp(uplnm,"SYS$COMMAND")) ) ) {
924 memmove(eqv,eqv+4,eqvlen-4);
930 if ((retsts == SS$_IVLOGNAM) ||
931 (retsts == SS$_NOLOGNAM)) { continue; }
934 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
935 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
936 if (retsts == SS$_NOLOGNAM) continue;
939 eqvlen = strlen(eqv);
943 if (retsts & 1) { eqv[eqvlen] = '\0'; return eqvlen; }
944 else if (retsts == LIB$_NOSUCHSYM || retsts == LIB$_INVSYMNAM ||
945 retsts == SS$_IVLOGNAM || retsts == SS$_IVLOGTAB ||
946 retsts == SS$_NOLOGNAM) {
947 set_errno(EINVAL); set_vaxc_errno(retsts);
949 else _ckvmssts(retsts);
951 } /* end of vmstrnenv */
954 /*{{{ int my_trnlnm(const char *lnm, char *eqv, unsigned long int idx)*/
955 /* Define as a function so we can access statics. */
956 int Perl_my_trnlnm(pTHX_ const char *lnm, char *eqv, unsigned long int idx)
958 return vmstrnenv(lnm,eqv,idx,fildev,
959 #ifdef SECURE_INTERNAL_GETENV
960 (PL_curinterp ? PL_tainting : will_taint) ? PERL__TRNENV_SECURE : 0
969 * Note: Uses Perl temp to store result so char * can be returned to
970 * caller; this pointer will be invalidated at next Perl statement
972 * We define this as a function rather than a macro in terms of my_getenv_len()
973 * so that it'll work when PL_curinterp is undefined (and we therefore can't
976 /*{{{ char *my_getenv(const char *lnm, bool sys)*/
978 Perl_my_getenv(pTHX_ const char *lnm, bool sys)
981 static char *__my_getenv_eqv = NULL;
982 char uplnm[LNM$C_NAMLENGTH+1], *cp2, *eqv;
983 unsigned long int idx = 0;
984 int trnsuccess, success, secure, saverr, savvmserr;
988 midx = my_maxidx(lnm) + 1;
990 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
991 /* Set up a temporary buffer for the return value; Perl will
992 * clean it up at the next statement transition */
993 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
994 if (!tmpsv) return NULL;
998 /* Assume no interpreter ==> single thread */
999 if (__my_getenv_eqv != NULL) {
1000 Renew(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
1003 Newx(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
1005 eqv = __my_getenv_eqv;
1008 for (cp1 = lnm, cp2 = eqv; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
1009 if (cp1 - lnm == 7 && !strncmp(eqv,"DEFAULT",7)) {
1011 getcwd(eqv,LNM$C_NAMLENGTH);
1015 /* Get rid of "000000/ in rooted filespecs */
1018 zeros = strstr(eqv, "/000000/");
1019 if (zeros != NULL) {
1021 mlen = len - (zeros - eqv) - 7;
1022 memmove(zeros, &zeros[7], mlen);
1030 /* Impose security constraints only if tainting */
1032 /* Impose security constraints only if tainting */
1033 secure = PL_curinterp ? PL_tainting : will_taint;
1034 saverr = errno; savvmserr = vaxc$errno;
1041 #ifdef SECURE_INTERNAL_GETENV
1042 secure ? PERL__TRNENV_SECURE : 0
1048 /* For the getenv interface we combine all the equivalence names
1049 * of a search list logical into one value to acquire a maximum
1050 * value length of 255*128 (assuming %ENV is using logicals).
1052 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
1054 /* If the name contains a semicolon-delimited index, parse it
1055 * off and make sure we only retrieve the equivalence name for
1057 if ((cp2 = strchr(lnm,';')) != NULL) {
1059 uplnm[cp2-lnm] = '\0';
1060 idx = strtoul(cp2+1,NULL,0);
1062 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
1065 success = vmstrnenv(lnm,eqv,idx,secure ? fildev : NULL,flags);
1067 /* Discard NOLOGNAM on internal calls since we're often looking
1068 * for an optional name, and this "error" often shows up as the
1069 * (bogus) exit status for a die() call later on. */
1070 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
1071 return success ? eqv : Nullch;
1074 } /* end of my_getenv() */
1078 /*{{{ SV *my_getenv_len(const char *lnm, bool sys)*/
1080 Perl_my_getenv_len(pTHX_ const char *lnm, unsigned long *len, bool sys)
1084 unsigned long idx = 0;
1086 static char *__my_getenv_len_eqv = NULL;
1087 int secure, saverr, savvmserr;
1090 midx = my_maxidx(lnm) + 1;
1092 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
1093 /* Set up a temporary buffer for the return value; Perl will
1094 * clean it up at the next statement transition */
1095 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
1096 if (!tmpsv) return NULL;
1100 /* Assume no interpreter ==> single thread */
1101 if (__my_getenv_len_eqv != NULL) {
1102 Renew(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
1105 Newx(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
1107 buf = __my_getenv_len_eqv;
1110 for (cp1 = lnm, cp2 = buf; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
1111 if (cp1 - lnm == 7 && !strncmp(buf,"DEFAULT",7)) {
1114 getcwd(buf,LNM$C_NAMLENGTH);
1117 /* Get rid of "000000/ in rooted filespecs */
1119 zeros = strstr(buf, "/000000/");
1120 if (zeros != NULL) {
1122 mlen = *len - (zeros - buf) - 7;
1123 memmove(zeros, &zeros[7], mlen);
1132 /* Impose security constraints only if tainting */
1133 secure = PL_curinterp ? PL_tainting : will_taint;
1134 saverr = errno; savvmserr = vaxc$errno;
1141 #ifdef SECURE_INTERNAL_GETENV
1142 secure ? PERL__TRNENV_SECURE : 0
1148 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
1150 if ((cp2 = strchr(lnm,';')) != NULL) {
1152 buf[cp2-lnm] = '\0';
1153 idx = strtoul(cp2+1,NULL,0);
1155 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
1158 *len = vmstrnenv(lnm,buf,idx,secure ? fildev : NULL,flags);
1160 /* Get rid of "000000/ in rooted filespecs */
1163 zeros = strstr(buf, "/000000/");
1164 if (zeros != NULL) {
1166 mlen = *len - (zeros - buf) - 7;
1167 memmove(zeros, &zeros[7], mlen);
1173 /* Discard NOLOGNAM on internal calls since we're often looking
1174 * for an optional name, and this "error" often shows up as the
1175 * (bogus) exit status for a die() call later on. */
1176 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
1177 return *len ? buf : Nullch;
1180 } /* end of my_getenv_len() */
1183 static void create_mbx(pTHX_ unsigned short int *, struct dsc$descriptor_s *);
1185 static void riseandshine(unsigned long int dummy) { sys$wake(0,0); }
1187 /*{{{ void prime_env_iter() */
1189 prime_env_iter(void)
1190 /* Fill the %ENV associative array with all logical names we can
1191 * find, in preparation for iterating over it.
1194 static int primed = 0;
1195 HV *seenhv = NULL, *envhv;
1197 char cmd[LNM$C_NAMLENGTH+24], mbxnam[LNM$C_NAMLENGTH], *buf = Nullch;
1198 unsigned short int chan;
1199 #ifndef CLI$M_TRUSTED
1200 # define CLI$M_TRUSTED 0x40 /* Missing from VAXC headers */
1202 unsigned long int defflags = CLI$M_NOWAIT | CLI$M_NOKEYPAD | CLI$M_TRUSTED;
1203 unsigned long int mbxbufsiz, flags, retsts, subpid = 0, substs = 0, wakect = 0;
1205 bool have_sym = FALSE, have_lnm = FALSE;
1206 struct dsc$descriptor_s tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1207 $DESCRIPTOR(cmddsc,cmd); $DESCRIPTOR(nldsc,"_NLA0:");
1208 $DESCRIPTOR(clidsc,"DCL"); $DESCRIPTOR(clitabdsc,"DCLTABLES");
1209 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
1210 $DESCRIPTOR(local,"_LOCAL"); $DESCRIPTOR(mbxdsc,mbxnam);
1211 #if defined(PERL_IMPLICIT_CONTEXT)
1214 #if defined(USE_ITHREADS)
1215 static perl_mutex primenv_mutex;
1216 MUTEX_INIT(&primenv_mutex);
1219 #if defined(PERL_IMPLICIT_CONTEXT)
1220 /* We jump through these hoops because we can be called at */
1221 /* platform-specific initialization time, which is before anything is */
1222 /* set up--we can't even do a plain dTHX since that relies on the */
1223 /* interpreter structure to be initialized */
1225 aTHX = PERL_GET_INTERP;
1231 if (primed || !PL_envgv) return;
1232 MUTEX_LOCK(&primenv_mutex);
1233 if (primed) { MUTEX_UNLOCK(&primenv_mutex); return; }
1234 envhv = GvHVn(PL_envgv);
1235 /* Perform a dummy fetch as an lval to insure that the hash table is
1236 * set up. Otherwise, the hv_store() will turn into a nullop. */
1237 (void) hv_fetch(envhv,"DEFAULT",7,TRUE);
1239 for (i = 0; env_tables[i]; i++) {
1240 if (!have_sym && (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
1241 !str$case_blind_compare(&tmpdsc,&clisym)) have_sym = 1;
1242 if (!have_lnm && str$case_blind_compare(env_tables[i],&crtlenv)) have_lnm = 1;
1244 if (have_sym || have_lnm) {
1245 long int syiitm = SYI$_MAXBUF, dviitm = DVI$_DEVNAM;
1246 _ckvmssts(lib$getsyi(&syiitm, &mbxbufsiz, 0, 0, 0, 0));
1247 _ckvmssts(sys$crembx(0,&chan,mbxbufsiz,mbxbufsiz,0xff0f,0,0));
1248 _ckvmssts(lib$getdvi(&dviitm, &chan, NULL, NULL, &mbxdsc, &mbxdsc.dsc$w_length));
1251 for (i--; i >= 0; i--) {
1252 if (!str$case_blind_compare(env_tables[i],&crtlenv)) {
1255 for (j = 0; environ[j]; j++) {
1256 if (!(start = strchr(environ[j],'='))) {
1257 if (ckWARN(WARN_INTERNAL))
1258 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed CRTL environ value \"%s\"\n",environ[j]);
1262 sv = newSVpv(start,0);
1264 (void) hv_store(envhv,environ[j],start - environ[j] - 1,sv,0);
1269 else if ((tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
1270 !str$case_blind_compare(&tmpdsc,&clisym)) {
1271 strcpy(cmd,"Show Symbol/Global *");
1272 cmddsc.dsc$w_length = 20;
1273 if (env_tables[i]->dsc$w_length == 12 &&
1274 (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer + 6) &&
1275 !str$case_blind_compare(&tmpdsc,&local)) strcpy(cmd+12,"Local *");
1276 flags = defflags | CLI$M_NOLOGNAM;
1279 strcpy(cmd,"Show Logical *");
1280 if (str$case_blind_compare(env_tables[i],&fildevdsc)) {
1281 strcat(cmd," /Table=");
1282 strncat(cmd,env_tables[i]->dsc$a_pointer,env_tables[i]->dsc$w_length);
1283 cmddsc.dsc$w_length = strlen(cmd);
1285 else cmddsc.dsc$w_length = 14; /* N.B. We test this below */
1286 flags = defflags | CLI$M_NOCLISYM;
1289 /* Create a new subprocess to execute each command, to exclude the
1290 * remote possibility that someone could subvert a mbx or file used
1291 * to write multiple commands to a single subprocess.
1294 retsts = lib$spawn(&cmddsc,&nldsc,&mbxdsc,&flags,0,&subpid,&substs,
1295 0,&riseandshine,0,0,&clidsc,&clitabdsc);
1296 flags &= ~CLI$M_TRUSTED; /* Just in case we hit a really old version */
1297 defflags &= ~CLI$M_TRUSTED;
1298 } while (retsts == LIB$_INVARG && (flags | CLI$M_TRUSTED));
1300 if (!buf) Newx(buf,mbxbufsiz + 1,char);
1301 if (seenhv) SvREFCNT_dec(seenhv);
1304 char *cp1, *cp2, *key;
1305 unsigned long int sts, iosb[2], retlen, keylen;
1308 sts = sys$qiow(0,chan,IO$_READVBLK,iosb,0,0,buf,mbxbufsiz,0,0,0,0);
1309 if (sts & 1) sts = iosb[0] & 0xffff;
1310 if (sts == SS$_ENDOFFILE) {
1312 while (substs == 0) { sys$hiber(); wakect++;}
1313 if (wakect > 1) sys$wake(0,0); /* Stole someone else's wake */
1318 retlen = iosb[0] >> 16;
1319 if (!retlen) continue; /* blank line */
1321 if (iosb[1] != subpid) {
1323 Perl_croak(aTHX_ "Unknown process %x sent message to prime_env_iter: %s",buf);
1327 if (sts == SS$_BUFFEROVF && ckWARN(WARN_INTERNAL))
1328 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Buffer overflow in prime_env_iter: %s",buf);
1330 for (cp1 = buf; *cp1 && isspace(*cp1); cp1++) ;
1331 if (*cp1 == '(' || /* Logical name table name */
1332 *cp1 == '=' /* Next eqv of searchlist */) continue;
1333 if (*cp1 == '"') cp1++;
1334 for (cp2 = cp1; *cp2 && *cp2 != '"' && *cp2 != ' '; cp2++) ;
1335 key = cp1; keylen = cp2 - cp1;
1336 if (keylen && hv_exists(seenhv,key,keylen)) continue;
1337 while (*cp2 && *cp2 != '=') cp2++;
1338 while (*cp2 && *cp2 == '=') cp2++;
1339 while (*cp2 && *cp2 == ' ') cp2++;
1340 if (*cp2 == '"') { /* String translation; may embed "" */
1341 for (cp1 = buf + retlen; *cp1 != '"'; cp1--) ;
1342 cp2++; cp1--; /* Skip "" surrounding translation */
1344 else { /* Numeric translation */
1345 for (cp1 = cp2; *cp1 && *cp1 != ' '; cp1++) ;
1346 cp1--; /* stop on last non-space char */
1348 if ((!keylen || (cp1 - cp2 < -1)) && ckWARN(WARN_INTERNAL)) {
1349 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed message in prime_env_iter: |%s|",buf);
1352 PERL_HASH(hash,key,keylen);
1354 if (cp1 == cp2 && *cp2 == '.') {
1355 /* A single dot usually means an unprintable character, such as a null
1356 * to indicate a zero-length value. Get the actual value to make sure.
1358 char lnm[LNM$C_NAMLENGTH+1];
1359 char eqv[MAX_DCL_SYMBOL+1];
1360 strncpy(lnm, key, keylen);
1361 int trnlen = vmstrnenv(lnm, eqv, 0, fildev, 0);
1362 sv = newSVpvn(eqv, strlen(eqv));
1365 sv = newSVpvn(cp2,cp1 - cp2 + 1);
1369 hv_store(envhv,key,keylen,sv,hash);
1370 hv_store(seenhv,key,keylen,&PL_sv_yes,hash);
1372 if (cmddsc.dsc$w_length == 14) { /* We just read LNM$FILE_DEV */
1373 /* get the PPFs for this process, not the subprocess */
1374 const char *ppfs[] = {"SYS$COMMAND", "SYS$INPUT", "SYS$OUTPUT", "SYS$ERROR", NULL};
1375 char eqv[LNM$C_NAMLENGTH+1];
1377 for (i = 0; ppfs[i]; i++) {
1378 trnlen = vmstrnenv(ppfs[i],eqv,0,fildev,0);
1379 sv = newSVpv(eqv,trnlen);
1381 hv_store(envhv,ppfs[i],strlen(ppfs[i]),sv,0);
1386 if (have_sym || have_lnm) _ckvmssts(sys$dassgn(chan));
1387 if (buf) Safefree(buf);
1388 if (seenhv) SvREFCNT_dec(seenhv);
1389 MUTEX_UNLOCK(&primenv_mutex);
1392 } /* end of prime_env_iter */
1396 /*{{{ int vmssetenv(const char *lnm, const char *eqv)*/
1397 /* Define or delete an element in the same "environment" as
1398 * vmstrnenv(). If an element is to be deleted, it's removed from
1399 * the first place it's found. If it's to be set, it's set in the
1400 * place designated by the first element of the table vector.
1401 * Like setenv() returns 0 for success, non-zero on error.
1404 Perl_vmssetenv(pTHX_ const char *lnm, const char *eqv, struct dsc$descriptor_s **tabvec)
1407 char uplnm[LNM$C_NAMLENGTH], *cp2, *c;
1408 unsigned short int curtab, ivlnm = 0, ivsym = 0, ivenv = 0;
1410 unsigned long int retsts, usermode = PSL$C_USER;
1411 struct itmlst_3 *ile, *ilist;
1412 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,uplnm},
1413 eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
1414 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1415 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
1416 $DESCRIPTOR(local,"_LOCAL");
1419 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1420 return SS$_IVLOGNAM;
1423 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
1424 *cp2 = _toupper(*cp1);
1425 if (cp1 - lnm > LNM$C_NAMLENGTH) {
1426 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1427 return SS$_IVLOGNAM;
1430 lnmdsc.dsc$w_length = cp1 - lnm;
1431 if (!tabvec || !*tabvec) tabvec = env_tables;
1433 if (!eqv) { /* we're deleting n element */
1434 for (curtab = 0; tabvec[curtab]; curtab++) {
1435 if (!ivenv && !str$case_blind_compare(tabvec[curtab],&crtlenv)) {
1437 for (i = 0; environ[i]; i++) { /* If it's an environ elt, reset */
1438 if ((cp1 = strchr(environ[i],'=')) &&
1439 lnmdsc.dsc$w_length == (cp1 - environ[i]) &&
1440 !strncmp(environ[i],lnm,cp1 - environ[i])) {
1442 return setenv(lnm,"",1) ? vaxc$errno : 0;
1445 ivenv = 1; retsts = SS$_NOLOGNAM;
1447 if (ckWARN(WARN_INTERNAL))
1448 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't reset CRTL environ elements (%s)",lnm);
1449 ivenv = 1; retsts = SS$_NOSUCHPGM;
1455 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
1456 !str$case_blind_compare(&tmpdsc,&clisym)) {
1457 unsigned int symtype;
1458 if (tabvec[curtab]->dsc$w_length == 12 &&
1459 (tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer + 6) &&
1460 !str$case_blind_compare(&tmpdsc,&local))
1461 symtype = LIB$K_CLI_LOCAL_SYM;
1462 else symtype = LIB$K_CLI_GLOBAL_SYM;
1463 retsts = lib$delete_symbol(&lnmdsc,&symtype);
1464 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
1465 if (retsts == LIB$_NOSUCHSYM) continue;
1469 retsts = sys$dellnm(tabvec[curtab],&lnmdsc,&usermode); /* try user mode first */
1470 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
1471 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
1472 retsts = lib$delete_logical(&lnmdsc,tabvec[curtab]); /* then supervisor mode */
1473 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
1477 else { /* we're defining a value */
1478 if (!ivenv && !str$case_blind_compare(tabvec[0],&crtlenv)) {
1480 return setenv(lnm,eqv,1) ? vaxc$errno : 0;
1482 if (ckWARN(WARN_INTERNAL))
1483 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't set CRTL environ elements (%s=%s)",lnm,eqv);
1484 retsts = SS$_NOSUCHPGM;
1488 eqvdsc.dsc$a_pointer = (char *) eqv; /* cast ok to readonly parameter */
1489 eqvdsc.dsc$w_length = strlen(eqv);
1490 if ((tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer) &&
1491 !str$case_blind_compare(&tmpdsc,&clisym)) {
1492 unsigned int symtype;
1493 if (tabvec[0]->dsc$w_length == 12 &&
1494 (tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer + 6) &&
1495 !str$case_blind_compare(&tmpdsc,&local))
1496 symtype = LIB$K_CLI_LOCAL_SYM;
1497 else symtype = LIB$K_CLI_GLOBAL_SYM;
1498 retsts = lib$set_symbol(&lnmdsc,&eqvdsc,&symtype);
1501 if (!*eqv) eqvdsc.dsc$w_length = 1;
1502 if (eqvdsc.dsc$w_length > LNM$C_NAMLENGTH) {
1504 nseg = (eqvdsc.dsc$w_length + LNM$C_NAMLENGTH - 1) / LNM$C_NAMLENGTH;
1505 if (nseg > PERL_LNM_MAX_ALLOWED_INDEX + 1) {
1506 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of logical \"%s\" too long. Truncating to %i bytes",
1507 lnm, LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1));
1508 eqvdsc.dsc$w_length = LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1);
1509 nseg = PERL_LNM_MAX_ALLOWED_INDEX + 1;
1512 Newx(ilist,nseg+1,struct itmlst_3);
1515 set_errno(ENOMEM); set_vaxc_errno(SS$_INSFMEM);
1518 memset(ilist, 0, (sizeof(struct itmlst_3) * (nseg+1)));
1520 for (j = 0, c = eqvdsc.dsc$a_pointer; j < nseg; j++, ile++, c += LNM$C_NAMLENGTH) {
1521 ile->itmcode = LNM$_STRING;
1523 if ((j+1) == nseg) {
1524 ile->buflen = strlen(c);
1525 /* in case we are truncating one that's too long */
1526 if (ile->buflen > LNM$C_NAMLENGTH) ile->buflen = LNM$C_NAMLENGTH;
1529 ile->buflen = LNM$C_NAMLENGTH;
1533 retsts = lib$set_logical(&lnmdsc,0,tabvec[0],0,ilist);
1537 retsts = lib$set_logical(&lnmdsc,&eqvdsc,tabvec[0],0,0);
1542 if (!(retsts & 1)) {
1544 case LIB$_AMBSYMDEF: case LIB$_INSCLIMEM:
1545 case SS$_NOLOGTAB: case SS$_TOOMANYLNAM: case SS$_IVLOGTAB:
1546 set_errno(EVMSERR); break;
1547 case LIB$_INVARG: case LIB$_INVSYMNAM: case SS$_IVLOGNAM:
1548 case LIB$_NOSUCHSYM: case SS$_NOLOGNAM:
1549 set_errno(EINVAL); break;
1551 set_errno(EACCES); break;
1556 set_vaxc_errno(retsts);
1557 return (int) retsts || 44; /* retsts should never be 0, but just in case */
1560 /* We reset error values on success because Perl does an hv_fetch()
1561 * before each hv_store(), and if the thing we're setting didn't
1562 * previously exist, we've got a leftover error message. (Of course,
1563 * this fails in the face of
1564 * $foo = $ENV{nonexistent}; $ENV{existent} = 'foo';
1565 * in that the error reported in $! isn't spurious,
1566 * but it's right more often than not.)
1568 set_errno(0); set_vaxc_errno(retsts);
1572 } /* end of vmssetenv() */
1575 /*{{{ void my_setenv(const char *lnm, const char *eqv)*/
1576 /* This has to be a function since there's a prototype for it in proto.h */
1578 Perl_my_setenv(pTHX_ const char *lnm, const char *eqv)
1581 int len = strlen(lnm);
1585 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
1586 if (!strcmp(uplnm,"DEFAULT")) {
1587 if (eqv && *eqv) my_chdir(eqv);
1591 #ifndef RTL_USES_UTC
1592 if (len == 6 || len == 2) {
1595 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
1597 if (!strcmp(uplnm,"UCX$TZ")) tz_updated = 1;
1598 if (!strcmp(uplnm,"TZ")) tz_updated = 1;
1602 (void) vmssetenv(lnm,eqv,NULL);
1606 /*{{{static void vmssetuserlnm(char *name, char *eqv); */
1608 * sets a user-mode logical in the process logical name table
1609 * used for redirection of sys$error
1612 Perl_vmssetuserlnm(pTHX_ const char *name, const char *eqv)
1614 $DESCRIPTOR(d_tab, "LNM$PROCESS");
1615 struct dsc$descriptor_d d_name = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
1616 unsigned long int iss, attr = LNM$M_CONFINE;
1617 unsigned char acmode = PSL$C_USER;
1618 struct itmlst_3 lnmlst[2] = {{0, LNM$_STRING, 0, 0},
1620 d_name.dsc$a_pointer = (char *)name; /* Cast OK for read only parameter */
1621 d_name.dsc$w_length = strlen(name);
1623 lnmlst[0].buflen = strlen(eqv);
1624 lnmlst[0].bufadr = (char *)eqv; /* Cast OK for read only parameter */
1626 iss = sys$crelnm(&attr,&d_tab,&d_name,&acmode,lnmlst);
1627 if (!(iss&1)) lib$signal(iss);
1632 /*{{{ char *my_crypt(const char *textpasswd, const char *usrname)*/
1633 /* my_crypt - VMS password hashing
1634 * my_crypt() provides an interface compatible with the Unix crypt()
1635 * C library function, and uses sys$hash_password() to perform VMS
1636 * password hashing. The quadword hashed password value is returned
1637 * as a NUL-terminated 8 character string. my_crypt() does not change
1638 * the case of its string arguments; in order to match the behavior
1639 * of LOGINOUT et al., alphabetic characters in both arguments must
1640 * be upcased by the caller.
1642 * - fix me to call ACM services when available
1645 Perl_my_crypt(pTHX_ const char *textpasswd, const char *usrname)
1647 # ifndef UAI$C_PREFERRED_ALGORITHM
1648 # define UAI$C_PREFERRED_ALGORITHM 127
1650 unsigned char alg = UAI$C_PREFERRED_ALGORITHM;
1651 unsigned short int salt = 0;
1652 unsigned long int sts;
1654 unsigned short int dsc$w_length;
1655 unsigned char dsc$b_type;
1656 unsigned char dsc$b_class;
1657 const char * dsc$a_pointer;
1658 } usrdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
1659 txtdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1660 struct itmlst_3 uailst[3] = {
1661 { sizeof alg, UAI$_ENCRYPT, &alg, 0},
1662 { sizeof salt, UAI$_SALT, &salt, 0},
1663 { 0, 0, NULL, NULL}};
1664 static char hash[9];
1666 usrdsc.dsc$w_length = strlen(usrname);
1667 usrdsc.dsc$a_pointer = usrname;
1668 if (!((sts = sys$getuai(0, 0, &usrdsc, uailst, 0, 0, 0)) & 1)) {
1670 case SS$_NOGRPPRV: case SS$_NOSYSPRV:
1674 set_errno(ESRCH); /* There isn't a Unix no-such-user error */
1679 set_vaxc_errno(sts);
1680 if (sts != RMS$_RNF) return NULL;
1683 txtdsc.dsc$w_length = strlen(textpasswd);
1684 txtdsc.dsc$a_pointer = textpasswd;
1685 if (!((sts = sys$hash_password(&txtdsc, alg, salt, &usrdsc, &hash)) & 1)) {
1686 set_errno(EVMSERR); set_vaxc_errno(sts); return NULL;
1689 return (char *) hash;
1691 } /* end of my_crypt() */
1695 static char *mp_do_rmsexpand(pTHX_ const char *, char *, int, const char *, unsigned, int *, int *);
1696 static char *mp_do_fileify_dirspec(pTHX_ const char *, char *, int, int *);
1697 static char *mp_do_tovmsspec(pTHX_ const char *, char *, int, int, int *);
1699 /* fixup barenames that are directories for internal use.
1700 * There have been problems with the consistent handling of UNIX
1701 * style directory names when routines are presented with a name that
1702 * has no directory delimitors at all. So this routine will eventually
1705 static char * fixup_bare_dirnames(const char * name)
1707 if (decc_disable_to_vms_logname_translation) {
1714 * A little hack to get around a bug in some implemenation of remove()
1715 * that do not know how to delete a directory
1717 * Delete any file to which user has control access, regardless of whether
1718 * delete access is explicitly allowed.
1719 * Limitations: User must have write access to parent directory.
1720 * Does not block signals or ASTs; if interrupted in midstream
1721 * may leave file with an altered ACL.
1724 /*{{{int mp_do_kill_file(const char *name, int dirflag)*/
1726 mp_do_kill_file(pTHX_ const char *name, int dirflag)
1728 char *vmsname, *rspec;
1730 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
1731 unsigned long int cxt = 0, aclsts, fndsts, rmsts = -1;
1732 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1734 unsigned char myace$b_length;
1735 unsigned char myace$b_type;
1736 unsigned short int myace$w_flags;
1737 unsigned long int myace$l_access;
1738 unsigned long int myace$l_ident;
1739 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
1740 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0},
1741 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
1743 findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0},
1744 {sizeof oldace, ACL$C_READACE, &oldace, 0},{0,0,0,0}},
1745 addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}},
1746 dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}},
1747 lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}},
1748 ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}};
1750 /* Expand the input spec using RMS, since the CRTL remove() and
1751 * system services won't do this by themselves, so we may miss
1752 * a file "hiding" behind a logical name or search list. */
1753 vmsname = PerlMem_malloc(NAM$C_MAXRSS+1);
1754 if (vmsname == NULL) _ckvmssts(SS$_INSFMEM);
1756 if (do_rmsexpand(name, vmsname, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL) == NULL) {
1757 PerlMem_free(vmsname);
1761 if (decc_posix_compliant_pathnames) {
1762 /* In POSIX mode, we prefer to remove the UNIX name */
1764 remove_name = (char *)name;
1767 rspec = PerlMem_malloc(NAM$C_MAXRSS+1);
1768 if (rspec == NULL) _ckvmssts(SS$_INSFMEM);
1769 if (do_rmsexpand(vmsname, rspec, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL) == NULL) {
1770 PerlMem_free(rspec);
1771 PerlMem_free(vmsname);
1774 PerlMem_free(vmsname);
1775 remove_name = rspec;
1778 #if defined(__CRTL_VER) && __CRTL_VER >= 70000000
1780 if (decc_dir_barename && decc_posix_compliant_pathnames) {
1781 remove_name = PerlMem_malloc(NAM$C_MAXRSS+1);
1782 if (remove_name == NULL) _ckvmssts(SS$_INSFMEM);
1784 do_pathify_dirspec(name, remove_name, 0, NULL);
1785 if (!rmdir(remove_name)) {
1787 PerlMem_free(remove_name);
1788 PerlMem_free(rspec);
1789 return 0; /* Can we just get rid of it? */
1793 if (!rmdir(remove_name)) {
1794 PerlMem_free(rspec);
1795 return 0; /* Can we just get rid of it? */
1801 if (!remove(remove_name)) {
1802 PerlMem_free(rspec);
1803 return 0; /* Can we just get rid of it? */
1806 /* If not, can changing protections help? */
1807 if (vaxc$errno != RMS$_PRV) {
1808 PerlMem_free(rspec);
1812 /* No, so we get our own UIC to use as a rights identifier,
1813 * and the insert an ACE at the head of the ACL which allows us
1814 * to delete the file.
1816 _ckvmssts(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
1817 fildsc.dsc$w_length = strlen(rspec);
1818 fildsc.dsc$a_pointer = rspec;
1820 newace.myace$l_ident = oldace.myace$l_ident;
1821 if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) {
1823 case RMS$_FNF: case RMS$_DNF: case SS$_NOSUCHOBJECT:
1824 set_errno(ENOENT); break;
1826 set_errno(ENOTDIR); break;
1828 set_errno(ENODEV); break;
1829 case RMS$_SYN: case SS$_INVFILFOROP:
1830 set_errno(EINVAL); break;
1832 set_errno(EACCES); break;
1836 set_vaxc_errno(aclsts);
1837 PerlMem_free(rspec);
1840 /* Grab any existing ACEs with this identifier in case we fail */
1841 aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt);
1842 if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY
1843 || fndsts == SS$_NOMOREACE ) {
1844 /* Add the new ACE . . . */
1845 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1))
1848 #if defined(__CRTL_VER) && __CRTL_VER >= 70000000
1850 if (decc_dir_barename && decc_posix_compliant_pathnames) {
1851 remove_name = PerlMem_malloc(NAM$C_MAXRSS+1);
1852 if (remove_name == NULL) _ckvmssts(SS$_INSFMEM);
1854 do_pathify_dirspec(name, remove_name, 0, NULL);
1855 rmsts = rmdir(remove_name);
1856 PerlMem_free(remove_name);
1859 rmsts = rmdir(remove_name);
1863 rmsts = remove(remove_name);
1865 /* We blew it - dir with files in it, no write priv for
1866 * parent directory, etc. Put things back the way they were. */
1867 if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1))
1870 addlst[0].bufadr = &oldace;
1871 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1))
1878 fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0);
1879 /* We just deleted it, so of course it's not there. Some versions of
1880 * VMS seem to return success on the unlock operation anyhow (after all
1881 * the unlock is successful), but others don't.
1883 if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL;
1884 if (aclsts & 1) aclsts = fndsts;
1885 if (!(aclsts & 1)) {
1887 set_vaxc_errno(aclsts);
1888 PerlMem_free(rspec);
1892 PerlMem_free(rspec);
1895 } /* end of kill_file() */
1899 /*{{{int do_rmdir(char *name)*/
1901 Perl_do_rmdir(pTHX_ const char *name)
1903 char dirfile[NAM$C_MAXRSS+1];
1907 if (do_fileify_dirspec(name,dirfile,0,NULL) == NULL) return -1;
1908 if (flex_stat(dirfile,&st) || !S_ISDIR(st.st_mode)) retval = -1;
1909 else retval = mp_do_kill_file(aTHX_ dirfile, 1);
1912 } /* end of do_rmdir */
1916 * Delete any file to which user has control access, regardless of whether
1917 * delete access is explicitly allowed.
1918 * Limitations: User must have write access to parent directory.
1919 * Does not block signals or ASTs; if interrupted in midstream
1920 * may leave file with an altered ACL.
1923 /*{{{int kill_file(char *name)*/
1925 Perl_kill_file(pTHX_ const char *name)
1927 char rspec[NAM$C_MAXRSS+1];
1929 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
1930 unsigned long int cxt = 0, aclsts, fndsts, rmsts = -1;
1931 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1933 unsigned char myace$b_length;
1934 unsigned char myace$b_type;
1935 unsigned short int myace$w_flags;
1936 unsigned long int myace$l_access;
1937 unsigned long int myace$l_ident;
1938 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
1939 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0},
1940 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
1942 findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0},
1943 {sizeof oldace, ACL$C_READACE, &oldace, 0},{0,0,0,0}},
1944 addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}},
1945 dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}},
1946 lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}},
1947 ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}};
1949 /* Expand the input spec using RMS, since the CRTL remove() and
1950 * system services won't do this by themselves, so we may miss
1951 * a file "hiding" behind a logical name or search list. */
1952 tspec = do_rmsexpand(name, rspec, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL);
1953 if (tspec == NULL) return -1;
1954 if (!remove(rspec)) return 0; /* Can we just get rid of it? */
1955 /* If not, can changing protections help? */
1956 if (vaxc$errno != RMS$_PRV) return -1;
1958 /* No, so we get our own UIC to use as a rights identifier,
1959 * and the insert an ACE at the head of the ACL which allows us
1960 * to delete the file.
1962 _ckvmssts(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
1963 fildsc.dsc$w_length = strlen(rspec);
1964 fildsc.dsc$a_pointer = rspec;
1966 newace.myace$l_ident = oldace.myace$l_ident;
1967 if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) {
1969 case RMS$_FNF: case RMS$_DNF: case SS$_NOSUCHOBJECT:
1970 set_errno(ENOENT); break;
1972 set_errno(ENOTDIR); break;
1974 set_errno(ENODEV); break;
1975 case RMS$_SYN: case SS$_INVFILFOROP:
1976 set_errno(EINVAL); break;
1978 set_errno(EACCES); break;
1982 set_vaxc_errno(aclsts);
1985 /* Grab any existing ACEs with this identifier in case we fail */
1986 aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt);
1987 if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY
1988 || fndsts == SS$_NOMOREACE ) {
1989 /* Add the new ACE . . . */
1990 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1))
1992 if ((rmsts = remove(name))) {
1993 /* We blew it - dir with files in it, no write priv for
1994 * parent directory, etc. Put things back the way they were. */
1995 if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1))
1998 addlst[0].bufadr = &oldace;
1999 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1))
2006 fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0);
2007 /* We just deleted it, so of course it's not there. Some versions of
2008 * VMS seem to return success on the unlock operation anyhow (after all
2009 * the unlock is successful), but others don't.
2011 if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL;
2012 if (aclsts & 1) aclsts = fndsts;
2013 if (!(aclsts & 1)) {
2015 set_vaxc_errno(aclsts);
2021 } /* end of kill_file() */
2025 /*{{{int my_mkdir(char *,Mode_t)*/
2027 Perl_my_mkdir(pTHX_ const char *dir, Mode_t mode)
2029 STRLEN dirlen = strlen(dir);
2031 /* zero length string sometimes gives ACCVIO */
2032 if (dirlen == 0) return -1;
2034 /* CRTL mkdir() doesn't tolerate trailing /, since that implies
2035 * null file name/type. However, it's commonplace under Unix,
2036 * so we'll allow it for a gain in portability.
2038 if (dir[dirlen-1] == '/') {
2039 char *newdir = savepvn(dir,dirlen-1);
2040 int ret = mkdir(newdir,mode);
2044 else return mkdir(dir,mode);
2045 } /* end of my_mkdir */
2048 /*{{{int my_chdir(char *)*/
2050 Perl_my_chdir(pTHX_ const char *dir)
2052 STRLEN dirlen = strlen(dir);
2054 /* zero length string sometimes gives ACCVIO */
2055 if (dirlen == 0) return -1;
2058 /* Perl is passing the output of the DCL SHOW DEFAULT with leading spaces.
2059 * This does not work if DECC$EFS_CHARSET is active. Hack it here
2060 * so that existing scripts do not need to be changed.
2063 while ((dirlen > 0) && (*dir1 == ' ')) {
2068 /* some versions of CRTL chdir() doesn't tolerate trailing /, since
2070 * null file name/type. However, it's commonplace under Unix,
2071 * so we'll allow it for a gain in portability.
2073 * - Preview- '/' will be valid soon on VMS
2075 if ((dirlen > 1) && (dir1[dirlen-1] == '/')) {
2076 char *newdir = savepvn(dir1,dirlen-1);
2077 int ret = chdir(newdir);
2081 else return chdir(dir1);
2082 } /* end of my_chdir */
2086 /*{{{FILE *my_tmpfile()*/
2093 if ((fp = tmpfile())) return fp;
2095 cp = PerlMem_malloc(L_tmpnam+24);
2096 if (cp == NULL) _ckvmssts_noperl(SS$_INSFMEM);
2098 if (decc_filename_unix_only == 0)
2099 strcpy(cp,"Sys$Scratch:");
2102 tmpnam(cp+strlen(cp));
2103 strcat(cp,".Perltmp");
2104 fp = fopen(cp,"w+","fop=dlt");
2111 #ifndef HOMEGROWN_POSIX_SIGNALS
2113 * The C RTL's sigaction fails to check for invalid signal numbers so we
2114 * help it out a bit. The docs are correct, but the actual routine doesn't
2115 * do what the docs say it will.
2117 /*{{{int Perl_my_sigaction (pTHX_ int, const struct sigaction*, struct sigaction*);*/
2119 Perl_my_sigaction (pTHX_ int sig, const struct sigaction* act,
2120 struct sigaction* oact)
2122 if (sig == SIGKILL || sig == SIGSTOP || sig == SIGCONT) {
2123 SETERRNO(EINVAL, SS$_INVARG);
2126 return sigaction(sig, act, oact);
2131 #ifdef KILL_BY_SIGPRC
2132 #include <errnodef.h>
2134 /* We implement our own kill() using the undocumented system service
2135 sys$sigprc for one of two reasons:
2137 1.) If the kill() in an older CRTL uses sys$forcex, causing the
2138 target process to do a sys$exit, which usually can't be handled
2139 gracefully...certainly not by Perl and the %SIG{} mechanism.
2141 2.) If the kill() in the CRTL can't be called from a signal
2142 handler without disappearing into the ether, i.e., the signal
2143 it purportedly sends is never trapped. Still true as of VMS 7.3.
2145 sys$sigprc has the same parameters as sys$forcex, but throws an exception
2146 in the target process rather than calling sys$exit.
2148 Note that distinguishing SIGSEGV from SIGBUS requires an extra arg
2149 on the ACCVIO condition, which sys$sigprc (and sys$forcex) don't
2150 provide. On VMS 7.0+ this is taken care of by doing sys$sigprc
2151 with condition codes C$_SIG0+nsig*8, catching the exception on the
2152 target process and resignaling with appropriate arguments.
2154 But we don't have that VMS 7.0+ exception handler, so if you
2155 Perl_my_kill(.., SIGSEGV) it will show up as a SIGBUS. Oh well.
2157 Also note that SIGTERM is listed in the docs as being "unimplemented",
2158 yet always seems to be signaled with a VMS condition code of 4 (and
2159 correctly handled for that code). So we hardwire it in.
2161 Unlike the VMS 7.0+ CRTL kill() function, we actually check the signal
2162 number to see if it's valid. So Perl_my_kill(pid,0) returns -1 rather
2163 than signalling with an unrecognized (and unhandled by CRTL) code.
2166 #define _MY_SIG_MAX 17
2169 Perl_sig_to_vmscondition_int(int sig)
2171 static unsigned int sig_code[_MY_SIG_MAX+1] =
2174 SS$_HANGUP, /* 1 SIGHUP */
2175 SS$_CONTROLC, /* 2 SIGINT */
2176 SS$_CONTROLY, /* 3 SIGQUIT */
2177 SS$_RADRMOD, /* 4 SIGILL */
2178 SS$_BREAK, /* 5 SIGTRAP */
2179 SS$_OPCCUS, /* 6 SIGABRT */
2180 SS$_COMPAT, /* 7 SIGEMT */
2182 SS$_FLTOVF, /* 8 SIGFPE VAX */
2184 SS$_HPARITH, /* 8 SIGFPE AXP */
2186 SS$_ABORT, /* 9 SIGKILL */
2187 SS$_ACCVIO, /* 10 SIGBUS */
2188 SS$_ACCVIO, /* 11 SIGSEGV */
2189 SS$_BADPARAM, /* 12 SIGSYS */
2190 SS$_NOMBX, /* 13 SIGPIPE */
2191 SS$_ASTFLT, /* 14 SIGALRM */
2197 #if __VMS_VER >= 60200000
2198 static int initted = 0;
2201 sig_code[16] = C$_SIGUSR1;
2202 sig_code[17] = C$_SIGUSR2;
2206 if (sig < _SIG_MIN) return 0;
2207 if (sig > _MY_SIG_MAX) return 0;
2208 return sig_code[sig];
2212 Perl_sig_to_vmscondition(int sig)
2215 if (vms_debug_on_exception != 0)
2216 lib$signal(SS$_DEBUG);
2218 return Perl_sig_to_vmscondition_int(sig);
2223 Perl_my_kill(int pid, int sig)
2228 int sys$sigprc(unsigned int *pidadr,
2229 struct dsc$descriptor_s *prcname,
2232 /* sig 0 means validate the PID */
2233 /*------------------------------*/
2235 const unsigned long int jpicode = JPI$_PID;
2238 status = lib$getjpi(&jpicode, &pid, NULL, &ret_pid, NULL, NULL);
2239 if ($VMS_STATUS_SUCCESS(status))
2242 case SS$_NOSUCHNODE:
2243 case SS$_UNREACHABLE:
2257 code = Perl_sig_to_vmscondition_int(sig);
2260 SETERRNO(EINVAL, SS$_BADPARAM);
2264 /* Fixme: Per official UNIX specification: If pid = 0, or negative then
2265 * signals are to be sent to multiple processes.
2266 * pid = 0 - all processes in group except ones that the system exempts
2267 * pid = -1 - all processes except ones that the system exempts
2268 * pid = -n - all processes in group (abs(n)) except ...
2269 * For now, just report as not supported.
2273 SETERRNO(ENOTSUP, SS$_UNSUPPORTED);
2277 iss = sys$sigprc((unsigned int *)&pid,0,code);
2278 if (iss&1) return 0;
2282 set_errno(EPERM); break;
2284 case SS$_NOSUCHNODE:
2285 case SS$_UNREACHABLE:
2286 set_errno(ESRCH); break;
2288 set_errno(ENOMEM); break;
2293 set_vaxc_errno(iss);
2299 /* Routine to convert a VMS status code to a UNIX status code.
2300 ** More tricky than it appears because of conflicting conventions with
2303 ** VMS status codes are a bit mask, with the least significant bit set for
2306 ** Special UNIX status of EVMSERR indicates that no translation is currently
2307 ** available, and programs should check the VMS status code.
2309 ** Programs compiled with _POSIX_EXIT have a special encoding that requires
2313 #ifndef C_FACILITY_NO
2314 #define C_FACILITY_NO 0x350000
2317 #define DCL_IVVERB 0x38090
2320 int Perl_vms_status_to_unix(int vms_status, int child_flag)
2328 /* Assume the best or the worst */
2329 if (vms_status & STS$M_SUCCESS)
2332 unix_status = EVMSERR;
2334 msg_status = vms_status & ~STS$M_CONTROL;
2336 facility = vms_status & STS$M_FAC_NO;
2337 fac_sp = vms_status & STS$M_FAC_SP;
2338 msg_no = vms_status & (STS$M_MSG_NO | STS$M_SEVERITY);
2340 if (((facility == 0) || (fac_sp == 0)) && (child_flag == 0)) {
2346 unix_status = EFAULT;
2348 case SS$_DEVOFFLINE:
2349 unix_status = EBUSY;
2352 unix_status = ENOTCONN;
2360 case SS$_INVFILFOROP:
2364 unix_status = EINVAL;
2366 case SS$_UNSUPPORTED:
2367 unix_status = ENOTSUP;
2372 unix_status = EACCES;
2374 case SS$_DEVICEFULL:
2375 unix_status = ENOSPC;
2378 unix_status = ENODEV;
2380 case SS$_NOSUCHFILE:
2381 case SS$_NOSUCHOBJECT:
2382 unix_status = ENOENT;
2384 case SS$_ABORT: /* Fatal case */
2385 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_ERROR): /* Error case */
2386 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_WARNING): /* Warning case */
2387 unix_status = EINTR;
2390 unix_status = E2BIG;
2393 unix_status = ENOMEM;
2396 unix_status = EPERM;
2398 case SS$_NOSUCHNODE:
2399 case SS$_UNREACHABLE:
2400 unix_status = ESRCH;
2403 unix_status = ECHILD;
2406 if ((facility == 0) && (msg_no < 8)) {
2407 /* These are not real VMS status codes so assume that they are
2408 ** already UNIX status codes
2410 unix_status = msg_no;
2416 /* Translate a POSIX exit code to a UNIX exit code */
2417 if ((facility == C_FACILITY_NO) && ((msg_no & 0xA000) == 0xA000)) {
2418 unix_status = (msg_no & 0x07F8) >> 3;
2422 /* Documented traditional behavior for handling VMS child exits */
2423 /*--------------------------------------------------------------*/
2424 if (child_flag != 0) {
2426 /* Success / Informational return 0 */
2427 /*----------------------------------*/
2428 if (msg_no & STS$K_SUCCESS)
2431 /* Warning returns 1 */
2432 /*-------------------*/
2433 if ((msg_no & (STS$K_ERROR | STS$K_SEVERE)) == 0)
2436 /* Everything else pass through the severity bits */
2437 /*------------------------------------------------*/
2438 return (msg_no & STS$M_SEVERITY);
2441 /* Normal VMS status to ERRNO mapping attempt */
2442 /*--------------------------------------------*/
2443 switch(msg_status) {
2444 /* case RMS$_EOF: */ /* End of File */
2445 case RMS$_FNF: /* File Not Found */
2446 case RMS$_DNF: /* Dir Not Found */
2447 unix_status = ENOENT;
2449 case RMS$_RNF: /* Record Not Found */
2450 unix_status = ESRCH;
2453 unix_status = ENOTDIR;
2456 unix_status = ENODEV;
2461 unix_status = EBADF;
2464 unix_status = EEXIST;
2468 case LIB$_INVSTRDES:
2470 case LIB$_NOSUCHSYM:
2471 case LIB$_INVSYMNAM:
2473 unix_status = EINVAL;
2479 unix_status = E2BIG;
2481 case RMS$_PRV: /* No privilege */
2482 case RMS$_ACC: /* ACP file access failed */
2483 case RMS$_WLK: /* Device write locked */
2484 unix_status = EACCES;
2486 /* case RMS$_NMF: */ /* No more files */
2494 /* Try to guess at what VMS error status should go with a UNIX errno
2495 * value. This is hard to do as there could be many possible VMS
2496 * error statuses that caused the errno value to be set.
2499 int Perl_unix_status_to_vms(int unix_status)
2501 int test_unix_status;
2503 /* Trivial cases first */
2504 /*---------------------*/
2505 if (unix_status == EVMSERR)
2508 /* Is vaxc$errno sane? */
2509 /*---------------------*/
2510 test_unix_status = Perl_vms_status_to_unix(vaxc$errno, 0);
2511 if (test_unix_status == unix_status)
2514 /* If way out of range, must be VMS code already */
2515 /*-----------------------------------------------*/
2516 if (unix_status > EVMSERR)
2519 /* If out of range, punt */
2520 /*-----------------------*/
2521 if (unix_status > __ERRNO_MAX)
2525 /* Ok, now we have to do it the hard way. */
2526 /*----------------------------------------*/
2527 switch(unix_status) {
2528 case 0: return SS$_NORMAL;
2529 case EPERM: return SS$_NOPRIV;
2530 case ENOENT: return SS$_NOSUCHOBJECT;
2531 case ESRCH: return SS$_UNREACHABLE;
2532 case EINTR: return SS$_ABORT;
2535 case E2BIG: return SS$_BUFFEROVF;
2537 case EBADF: return RMS$_IFI;
2538 case ECHILD: return SS$_NONEXPR;
2540 case ENOMEM: return SS$_INSFMEM;
2541 case EACCES: return SS$_FILACCERR;
2542 case EFAULT: return SS$_ACCVIO;
2544 case EBUSY: return SS$_DEVOFFLINE;
2545 case EEXIST: return RMS$_FEX;
2547 case ENODEV: return SS$_NOSUCHDEV;
2548 case ENOTDIR: return RMS$_DIR;
2550 case EINVAL: return SS$_INVARG;
2556 case ENOSPC: return SS$_DEVICEFULL;
2557 case ESPIPE: return LIB$_INVARG;
2562 case ERANGE: return LIB$_INVARG;
2563 /* case EWOULDBLOCK */
2564 /* case EINPROGRESS */
2567 /* case EDESTADDRREQ */
2569 /* case EPROTOTYPE */
2570 /* case ENOPROTOOPT */
2571 /* case EPROTONOSUPPORT */
2572 /* case ESOCKTNOSUPPORT */
2573 /* case EOPNOTSUPP */
2574 /* case EPFNOSUPPORT */
2575 /* case EAFNOSUPPORT */
2576 /* case EADDRINUSE */
2577 /* case EADDRNOTAVAIL */
2579 /* case ENETUNREACH */
2580 /* case ENETRESET */
2581 /* case ECONNABORTED */
2582 /* case ECONNRESET */
2585 case ENOTCONN: return SS$_CLEARED;
2586 /* case ESHUTDOWN */
2587 /* case ETOOMANYREFS */
2588 /* case ETIMEDOUT */
2589 /* case ECONNREFUSED */
2591 /* case ENAMETOOLONG */
2592 /* case EHOSTDOWN */
2593 /* case EHOSTUNREACH */
2594 /* case ENOTEMPTY */
2606 /* case ECANCELED */
2610 return SS$_UNSUPPORTED;
2616 /* case EABANDONED */
2618 return SS$_ABORT; /* punt */
2621 return SS$_ABORT; /* Should not get here */
2625 /* default piping mailbox size */
2626 #define PERL_BUFSIZ 512
2630 create_mbx(pTHX_ unsigned short int *chan, struct dsc$descriptor_s *namdsc)
2632 unsigned long int mbxbufsiz;
2633 static unsigned long int syssize = 0;
2634 unsigned long int dviitm = DVI$_DEVNAM;
2635 char csize[LNM$C_NAMLENGTH+1];
2639 unsigned long syiitm = SYI$_MAXBUF;
2641 * Get the SYSGEN parameter MAXBUF
2643 * If the logical 'PERL_MBX_SIZE' is defined
2644 * use the value of the logical instead of PERL_BUFSIZ, but
2645 * keep the size between 128 and MAXBUF.
2648 _ckvmssts(lib$getsyi(&syiitm, &syssize, 0, 0, 0, 0));
2651 if (vmstrnenv("PERL_MBX_SIZE", csize, 0, fildev, 0)) {
2652 mbxbufsiz = atoi(csize);
2654 mbxbufsiz = PERL_BUFSIZ;
2656 if (mbxbufsiz < 128) mbxbufsiz = 128;
2657 if (mbxbufsiz > syssize) mbxbufsiz = syssize;
2659 _ckvmssts(sts = sys$crembx(0,chan,mbxbufsiz,mbxbufsiz,0,0,0));
2661 _ckvmssts(sts = lib$getdvi(&dviitm, chan, NULL, NULL, namdsc, &namdsc->dsc$w_length));
2662 namdsc->dsc$a_pointer[namdsc->dsc$w_length] = '\0';
2664 } /* end of create_mbx() */
2667 /*{{{ my_popen and my_pclose*/
2669 typedef struct _iosb IOSB;
2670 typedef struct _iosb* pIOSB;
2671 typedef struct _pipe Pipe;
2672 typedef struct _pipe* pPipe;
2673 typedef struct pipe_details Info;
2674 typedef struct pipe_details* pInfo;
2675 typedef struct _srqp RQE;
2676 typedef struct _srqp* pRQE;
2677 typedef struct _tochildbuf CBuf;
2678 typedef struct _tochildbuf* pCBuf;
2681 unsigned short status;
2682 unsigned short count;
2683 unsigned long dvispec;
2686 #pragma member_alignment save
2687 #pragma nomember_alignment quadword
2688 struct _srqp { /* VMS self-relative queue entry */
2689 unsigned long qptr[2];
2691 #pragma member_alignment restore
2692 static RQE RQE_ZERO = {0,0};
2694 struct _tochildbuf {
2697 unsigned short size;
2705 unsigned short chan_in;
2706 unsigned short chan_out;
2708 unsigned int bufsize;
2720 #if defined(PERL_IMPLICIT_CONTEXT)
2721 void *thx; /* Either a thread or an interpreter */
2722 /* pointer, depending on how we're built */
2730 PerlIO *fp; /* file pointer to pipe mailbox */
2731 int useFILE; /* using stdio, not perlio */
2732 int pid; /* PID of subprocess */
2733 int mode; /* == 'r' if pipe open for reading */
2734 int done; /* subprocess has completed */
2735 int waiting; /* waiting for completion/closure */
2736 int closing; /* my_pclose is closing this pipe */
2737 unsigned long completion; /* termination status of subprocess */
2738 pPipe in; /* pipe in to sub */
2739 pPipe out; /* pipe out of sub */
2740 pPipe err; /* pipe of sub's sys$error */
2741 int in_done; /* true when in pipe finished */
2746 struct exit_control_block
2748 struct exit_control_block *flink;
2749 unsigned long int (*exit_routine)();
2750 unsigned long int arg_count;
2751 unsigned long int *status_address;
2752 unsigned long int exit_status;
2755 typedef struct _closed_pipes Xpipe;
2756 typedef struct _closed_pipes* pXpipe;
2758 struct _closed_pipes {
2759 int pid; /* PID of subprocess */
2760 unsigned long completion; /* termination status of subprocess */
2762 #define NKEEPCLOSED 50
2763 static Xpipe closed_list[NKEEPCLOSED];
2764 static int closed_index = 0;
2765 static int closed_num = 0;
2767 #define RETRY_DELAY "0 ::0.20"
2768 #define MAX_RETRY 50
2770 static int pipe_ef = 0; /* first call to safe_popen inits these*/
2771 static unsigned long mypid;
2772 static unsigned long delaytime[2];
2774 static pInfo open_pipes = NULL;
2775 static $DESCRIPTOR(nl_desc, "NL:");
2777 #define PIPE_COMPLETION_WAIT 30 /* seconds, for EOF/FORCEX wait */
2781 static unsigned long int
2782 pipe_exit_routine(pTHX)
2785 unsigned long int retsts = SS$_NORMAL, abort = SS$_TIMEOUT;
2786 int sts, did_stuff, need_eof, j;
2789 flush any pending i/o
2795 PerlIO_flush(info->fp); /* first, flush data */
2797 fflush((FILE *)info->fp);
2803 next we try sending an EOF...ignore if doesn't work, make sure we
2811 _ckvmssts_noperl(sys$setast(0));
2812 if (info->in && !info->in->shut_on_empty) {
2813 _ckvmssts_noperl(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
2818 _ckvmssts_noperl(sys$setast(1));
2822 /* wait for EOF to have effect, up to ~ 30 sec [default] */
2824 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
2829 _ckvmssts_noperl(sys$setast(0));
2830 if (info->waiting && info->done)
2832 nwait += info->waiting;
2833 _ckvmssts_noperl(sys$setast(1));
2843 _ckvmssts_noperl(sys$setast(0));
2844 if (!info->done) { /* Tap them gently on the shoulder . . .*/
2845 sts = sys$forcex(&info->pid,0,&abort);
2846 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
2849 _ckvmssts_noperl(sys$setast(1));
2853 /* again, wait for effect */
2855 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
2860 _ckvmssts_noperl(sys$setast(0));
2861 if (info->waiting && info->done)
2863 nwait += info->waiting;
2864 _ckvmssts_noperl(sys$setast(1));
2873 _ckvmssts_noperl(sys$setast(0));
2874 if (!info->done) { /* We tried to be nice . . . */
2875 sts = sys$delprc(&info->pid,0);
2876 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
2878 _ckvmssts_noperl(sys$setast(1));
2883 if ((sts = my_pclose(open_pipes->fp)) == -1) retsts = vaxc$errno;
2884 else if (!(sts & 1)) retsts = sts;
2889 static struct exit_control_block pipe_exitblock =
2890 {(struct exit_control_block *) 0,
2891 pipe_exit_routine, 0, &pipe_exitblock.exit_status, 0};
2893 static void pipe_mbxtofd_ast(pPipe p);
2894 static void pipe_tochild1_ast(pPipe p);
2895 static void pipe_tochild2_ast(pPipe p);
2898 popen_completion_ast(pInfo info)
2900 pInfo i = open_pipes;
2905 info->completion &= 0x0FFFFFFF; /* strip off "control" field */
2906 closed_list[closed_index].pid = info->pid;
2907 closed_list[closed_index].completion = info->completion;
2909 if (closed_index == NKEEPCLOSED)
2914 if (i == info) break;
2917 if (!i) return; /* unlinked, probably freed too */
2922 Writing to subprocess ...
2923 if my_pclose'd: EOF already sent, should shutdown chan_in part of pipe
2925 chan_out may be waiting for "done" flag, or hung waiting
2926 for i/o completion to child...cancel the i/o. This will
2927 put it into "snarf mode" (done but no EOF yet) that discards
2930 Output from subprocess (stdout, stderr) needs to be flushed and
2931 shut down. We try sending an EOF, but if the mbx is full the pipe
2932 routine should still catch the "shut_on_empty" flag, telling it to
2933 use immediate-style reads so that "mbx empty" -> EOF.
2937 if (info->in && !info->in_done) { /* only for mode=w */
2938 if (info->in->shut_on_empty && info->in->need_wake) {
2939 info->in->need_wake = FALSE;
2940 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,info->in,0));
2942 _ckvmssts_noperl(sys$cancel(info->in->chan_out));
2946 if (info->out && !info->out_done) { /* were we also piping output? */
2947 info->out->shut_on_empty = TRUE;
2948 iss = sys$qio(0,info->out->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2949 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
2950 _ckvmssts_noperl(iss);
2953 if (info->err && !info->err_done) { /* we were piping stderr */
2954 info->err->shut_on_empty = TRUE;
2955 iss = sys$qio(0,info->err->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2956 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
2957 _ckvmssts_noperl(iss);
2959 _ckvmssts_noperl(sys$setef(pipe_ef));
2963 static unsigned long int setup_cmddsc(pTHX_ const char *cmd, int check_img, int *suggest_quote, struct dsc$descriptor_s **pvmscmd);
2964 static void vms_execfree(struct dsc$descriptor_s *vmscmd);
2967 we actually differ from vmstrnenv since we use this to
2968 get the RMS IFI to check if SYS$OUTPUT and SYS$ERROR *really*
2969 are pointing to the same thing
2972 static unsigned short
2973 popen_translate(pTHX_ char *logical, char *result)
2976 $DESCRIPTOR(d_table,"LNM$PROCESS_TABLE");
2977 $DESCRIPTOR(d_log,"");
2979 unsigned short length;
2980 unsigned short code;
2982 unsigned short *retlenaddr;
2984 unsigned short l, ifi;
2986 d_log.dsc$a_pointer = logical;
2987 d_log.dsc$w_length = strlen(logical);
2989 itmlst[0].code = LNM$_STRING;
2990 itmlst[0].length = 255;
2991 itmlst[0].buffer_addr = result;
2992 itmlst[0].retlenaddr = &l;
2995 itmlst[1].length = 0;
2996 itmlst[1].buffer_addr = 0;
2997 itmlst[1].retlenaddr = 0;
2999 iss = sys$trnlnm(0, &d_table, &d_log, 0, itmlst);
3000 if (iss == SS$_NOLOGNAM) {
3004 if (!(iss&1)) lib$signal(iss);
3007 logicals for PPFs have a 4 byte prefix ESC+NUL+(RMS IFI)
3008 strip it off and return the ifi, if any
3011 if (result[0] == 0x1b && result[1] == 0x00) {
3012 memmove(&ifi,result+2,2);
3013 strcpy(result,result+4);
3015 return ifi; /* this is the RMS internal file id */
3018 static void pipe_infromchild_ast(pPipe p);
3021 I'm using LIB$(GET|FREE)_VM here so that we can allocate and deallocate
3022 inside an AST routine without worrying about reentrancy and which Perl
3023 memory allocator is being used.
3025 We read data and queue up the buffers, then spit them out one at a
3026 time to the output mailbox when the output mailbox is ready for one.
3029 #define INITIAL_TOCHILDQUEUE 2
3032 pipe_tochild_setup(pTHX_ char *rmbx, char *wmbx)
3036 char mbx1[64], mbx2[64];
3037 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3038 DSC$K_CLASS_S, mbx1},
3039 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
3040 DSC$K_CLASS_S, mbx2};
3041 unsigned int dviitm = DVI$_DEVBUFSIZ;
3045 _ckvmssts(lib$get_vm(&n, &p));
3047 create_mbx(aTHX_ &p->chan_in , &d_mbx1);
3048 create_mbx(aTHX_ &p->chan_out, &d_mbx2);
3049 _ckvmssts(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3052 p->shut_on_empty = FALSE;
3053 p->need_wake = FALSE;
3056 p->iosb.status = SS$_NORMAL;
3057 p->iosb2.status = SS$_NORMAL;
3063 #ifdef PERL_IMPLICIT_CONTEXT
3067 n = sizeof(CBuf) + p->bufsize;
3069 for (j = 0; j < INITIAL_TOCHILDQUEUE; j++) {
3070 _ckvmssts(lib$get_vm(&n, &b));
3071 b->buf = (char *) b + sizeof(CBuf);
3072 _ckvmssts(lib$insqhi(b, &p->free));
3075 pipe_tochild2_ast(p);
3076 pipe_tochild1_ast(p);
3082 /* reads the MBX Perl is writing, and queues */
3085 pipe_tochild1_ast(pPipe p)
3088 int iss = p->iosb.status;
3089 int eof = (iss == SS$_ENDOFFILE);
3091 #ifdef PERL_IMPLICIT_CONTEXT
3097 p->shut_on_empty = TRUE;
3099 _ckvmssts(sys$dassgn(p->chan_in));
3105 b->size = p->iosb.count;
3106 _ckvmssts(sts = lib$insqhi(b, &p->wait));
3108 p->need_wake = FALSE;
3109 _ckvmssts(sys$dclast(pipe_tochild2_ast,p,0));
3112 p->retry = 1; /* initial call */
3115 if (eof) { /* flush the free queue, return when done */
3116 int n = sizeof(CBuf) + p->bufsize;
3118 iss = lib$remqti(&p->free, &b);
3119 if (iss == LIB$_QUEWASEMP) return;
3121 _ckvmssts(lib$free_vm(&n, &b));
3125 iss = lib$remqti(&p->free, &b);
3126 if (iss == LIB$_QUEWASEMP) {
3127 int n = sizeof(CBuf) + p->bufsize;
3128 _ckvmssts(lib$get_vm(&n, &b));
3129 b->buf = (char *) b + sizeof(CBuf);
3135 iss = sys$qio(0,p->chan_in,
3136 IO$_READVBLK|(p->shut_on_empty ? IO$M_NOWAIT : 0),
3138 pipe_tochild1_ast, p, b->buf, p->bufsize, 0, 0, 0, 0);
3139 if (iss == SS$_ENDOFFILE && p->shut_on_empty) iss = SS$_NORMAL;
3144 /* writes queued buffers to output, waits for each to complete before
3148 pipe_tochild2_ast(pPipe p)
3151 int iss = p->iosb2.status;
3152 int n = sizeof(CBuf) + p->bufsize;
3153 int done = (p->info && p->info->done) ||
3154 iss == SS$_CANCEL || iss == SS$_ABORT;
3155 #if defined(PERL_IMPLICIT_CONTEXT)
3160 if (p->type) { /* type=1 has old buffer, dispose */
3161 if (p->shut_on_empty) {
3162 _ckvmssts(lib$free_vm(&n, &b));
3164 _ckvmssts(lib$insqhi(b, &p->free));
3169 iss = lib$remqti(&p->wait, &b);
3170 if (iss == LIB$_QUEWASEMP) {
3171 if (p->shut_on_empty) {
3173 _ckvmssts(sys$dassgn(p->chan_out));
3174 *p->pipe_done = TRUE;
3175 _ckvmssts(sys$setef(pipe_ef));
3177 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEOF,
3178 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3182 p->need_wake = TRUE;
3192 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEOF,
3193 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3195 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEVBLK,
3196 &p->iosb2, pipe_tochild2_ast, p, b->buf, b->size, 0, 0, 0, 0));
3205 pipe_infromchild_setup(pTHX_ char *rmbx, char *wmbx)
3208 char mbx1[64], mbx2[64];
3209 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3210 DSC$K_CLASS_S, mbx1},
3211 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
3212 DSC$K_CLASS_S, mbx2};
3213 unsigned int dviitm = DVI$_DEVBUFSIZ;
3215 int n = sizeof(Pipe);
3216 _ckvmssts(lib$get_vm(&n, &p));
3217 create_mbx(aTHX_ &p->chan_in , &d_mbx1);
3218 create_mbx(aTHX_ &p->chan_out, &d_mbx2);
3220 _ckvmssts(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3221 n = p->bufsize * sizeof(char);
3222 _ckvmssts(lib$get_vm(&n, &p->buf));
3223 p->shut_on_empty = FALSE;
3226 p->iosb.status = SS$_NORMAL;
3227 #if defined(PERL_IMPLICIT_CONTEXT)
3230 pipe_infromchild_ast(p);
3238 pipe_infromchild_ast(pPipe p)
3240 int iss = p->iosb.status;
3241 int eof = (iss == SS$_ENDOFFILE);
3242 int myeof = (eof && (p->iosb.dvispec == mypid || p->iosb.dvispec == 0));
3243 int kideof = (eof && (p->iosb.dvispec == p->info->pid));
3244 #if defined(PERL_IMPLICIT_CONTEXT)
3248 if (p->info && p->info->closing && p->chan_out) { /* output shutdown */
3249 _ckvmssts(sys$dassgn(p->chan_out));
3254 input shutdown if EOF from self (done or shut_on_empty)
3255 output shutdown if closing flag set (my_pclose)
3256 send data/eof from child or eof from self
3257 otherwise, re-read (snarf of data from child)
3262 if (myeof && p->chan_in) { /* input shutdown */
3263 _ckvmssts(sys$dassgn(p->chan_in));
3268 if (myeof || kideof) { /* pass EOF to parent */
3269 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEOF, &p->iosb,
3270 pipe_infromchild_ast, p,
3273 } else if (eof) { /* eat EOF --- fall through to read*/
3275 } else { /* transmit data */
3276 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEVBLK,&p->iosb,
3277 pipe_infromchild_ast,p,
3278 p->buf, p->iosb.count, 0, 0, 0, 0));
3284 /* everything shut? flag as done */
3286 if (!p->chan_in && !p->chan_out) {
3287 *p->pipe_done = TRUE;
3288 _ckvmssts(sys$setef(pipe_ef));
3292 /* write completed (or read, if snarfing from child)
3293 if still have input active,
3294 queue read...immediate mode if shut_on_empty so we get EOF if empty
3296 check if Perl reading, generate EOFs as needed
3302 iss = sys$qio(0,p->chan_in,IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0),&p->iosb,
3303 pipe_infromchild_ast,p,
3304 p->buf, p->bufsize, 0, 0, 0, 0);
3305 if (p->shut_on_empty && iss == SS$_ENDOFFILE) iss = SS$_NORMAL;
3307 } else { /* send EOFs for extra reads */
3308 p->iosb.status = SS$_ENDOFFILE;
3309 p->iosb.dvispec = 0;
3310 _ckvmssts(sys$qio(0,p->chan_out,IO$_SETMODE|IO$M_READATTN,
3312 pipe_infromchild_ast, p, 0, 0, 0, 0));
3318 pipe_mbxtofd_setup(pTHX_ int fd, char *out)
3322 unsigned long dviitm = DVI$_DEVBUFSIZ;
3324 struct dsc$descriptor_s d_mbx = {sizeof mbx, DSC$K_DTYPE_T,
3325 DSC$K_CLASS_S, mbx};
3326 int n = sizeof(Pipe);
3328 /* things like terminals and mbx's don't need this filter */
3329 if (fd && fstat(fd,&s) == 0) {
3330 unsigned long dviitm = DVI$_DEVCHAR, devchar;
3332 unsigned short dev_len;
3333 struct dsc$descriptor_s d_dev;
3335 struct item_list_3 items[3];
3337 unsigned short dvi_iosb[4];
3339 cptr = getname(fd, out, 1);
3340 if (cptr == NULL) _ckvmssts(SS$_NOSUCHDEV);
3341 d_dev.dsc$a_pointer = out;
3342 d_dev.dsc$w_length = strlen(out);
3343 d_dev.dsc$b_dtype = DSC$K_DTYPE_T;
3344 d_dev.dsc$b_class = DSC$K_CLASS_S;
3347 items[0].code = DVI$_DEVCHAR;
3348 items[0].bufadr = &devchar;
3349 items[0].retadr = NULL;
3351 items[1].code = DVI$_FULLDEVNAM;
3352 items[1].bufadr = device;
3353 items[1].retadr = &dev_len;
3357 status = sys$getdviw
3358 (NO_EFN, 0, &d_dev, items, dvi_iosb, NULL, NULL, NULL);
3360 if ($VMS_STATUS_SUCCESS(dvi_iosb[0])) {
3361 device[dev_len] = 0;
3363 if (!(devchar & DEV$M_DIR)) {
3364 strcpy(out, device);
3370 _ckvmssts(lib$get_vm(&n, &p));
3371 p->fd_out = dup(fd);
3372 create_mbx(aTHX_ &p->chan_in, &d_mbx);
3373 _ckvmssts(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3374 n = (p->bufsize+1) * sizeof(char);
3375 _ckvmssts(lib$get_vm(&n, &p->buf));
3376 p->shut_on_empty = FALSE;
3381 _ckvmssts(sys$qio(0, p->chan_in, IO$_READVBLK, &p->iosb,
3382 pipe_mbxtofd_ast, p,
3383 p->buf, p->bufsize, 0, 0, 0, 0));
3389 pipe_mbxtofd_ast(pPipe p)
3391 int iss = p->iosb.status;
3392 int done = p->info->done;
3394 int eof = (iss == SS$_ENDOFFILE);
3395 int myeof = eof && ((p->iosb.dvispec == mypid)||(p->iosb.dvispec == 0));
3396 int err = !(iss&1) && !eof;
3397 #if defined(PERL_IMPLICIT_CONTEXT)
3401 if (done && myeof) { /* end piping */
3403 sys$dassgn(p->chan_in);
3404 *p->pipe_done = TRUE;
3405 _ckvmssts(sys$setef(pipe_ef));
3409 if (!err && !eof) { /* good data to send to file */
3410 p->buf[p->iosb.count] = '\n';
3411 iss2 = write(p->fd_out, p->buf, p->iosb.count+1);
3414 if (p->retry < MAX_RETRY) {
3415 _ckvmssts(sys$setimr(0,delaytime,pipe_mbxtofd_ast,p));
3425 iss = sys$qio(0, p->chan_in, IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0), &p->iosb,
3426 pipe_mbxtofd_ast, p,
3427 p->buf, p->bufsize, 0, 0, 0, 0);
3428 if (p->shut_on_empty && (iss == SS$_ENDOFFILE)) iss = SS$_NORMAL;
3433 typedef struct _pipeloc PLOC;
3434 typedef struct _pipeloc* pPLOC;
3438 char dir[NAM$C_MAXRSS+1];
3440 static pPLOC head_PLOC = 0;
3443 free_pipelocs(pTHX_ void *head)
3446 pPLOC *pHead = (pPLOC *)head;
3458 store_pipelocs(pTHX)
3467 char temp[NAM$C_MAXRSS+1];
3471 free_pipelocs(aTHX_ &head_PLOC);
3473 /* the . directory from @INC comes last */
3475 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3476 if (p == NULL) _ckvmssts(SS$_INSFMEM);
3477 p->next = head_PLOC;
3479 strcpy(p->dir,"./");
3481 /* get the directory from $^X */
3483 unixdir = PerlMem_malloc(VMS_MAXRSS);
3484 if (unixdir == NULL) _ckvmssts(SS$_INSFMEM);
3486 #ifdef PERL_IMPLICIT_CONTEXT
3487 if (aTHX && PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
3489 if (PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
3491 strcpy(temp, PL_origargv[0]);
3492 x = strrchr(temp,']');
3494 x = strrchr(temp,'>');
3496 /* It could be a UNIX path */
3497 x = strrchr(temp,'/');
3503 /* Got a bare name, so use default directory */
3508 if ((tounixpath_utf8(temp, unixdir, NULL)) != Nullch) {
3509 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3510 if (p == NULL) _ckvmssts(SS$_INSFMEM);
3511 p->next = head_PLOC;
3513 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3514 p->dir[NAM$C_MAXRSS] = '\0';
3518 /* reverse order of @INC entries, skip "." since entered above */
3520 #ifdef PERL_IMPLICIT_CONTEXT
3523 if (PL_incgv) av = GvAVn(PL_incgv);
3525 for (i = 0; av && i <= AvFILL(av); i++) {
3526 dirsv = *av_fetch(av,i,TRUE);
3528 if (SvROK(dirsv)) continue;
3529 dir = SvPVx(dirsv,n_a);
3530 if (strcmp(dir,".") == 0) continue;
3531 if ((tounixpath_utf8(dir, unixdir, NULL)) == Nullch)
3534 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3535 p->next = head_PLOC;
3537 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3538 p->dir[NAM$C_MAXRSS] = '\0';
3541 /* most likely spot (ARCHLIB) put first in the list */
3544 if ((tounixpath_utf8(ARCHLIB_EXP, unixdir, NULL)) != Nullch) {
3545 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3546 if (p == NULL) _ckvmssts(SS$_INSFMEM);
3547 p->next = head_PLOC;
3549 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3550 p->dir[NAM$C_MAXRSS] = '\0';
3553 PerlMem_free(unixdir);
3557 Perl_cando_by_name_int
3558 (pTHX_ I32 bit, bool effective, const char *fname, int opts);
3559 #if !defined(PERL_IMPLICIT_CONTEXT)
3560 #define cando_by_name_int Perl_cando_by_name_int
3562 #define cando_by_name_int(a,b,c,d) Perl_cando_by_name_int(aTHX_ a,b,c,d)
3568 static int vmspipe_file_status = 0;
3569 static char vmspipe_file[NAM$C_MAXRSS+1];
3571 /* already found? Check and use ... need read+execute permission */
3573 if (vmspipe_file_status == 1) {
3574 if (cando_by_name_int(S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)
3575 && cando_by_name_int
3576 (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) {
3577 return vmspipe_file;
3579 vmspipe_file_status = 0;
3582 /* scan through stored @INC, $^X */
3584 if (vmspipe_file_status == 0) {
3585 char file[NAM$C_MAXRSS+1];
3586 pPLOC p = head_PLOC;
3591 strcpy(file, p->dir);
3592 dirlen = strlen(file);
3593 strncat(file, "vmspipe.com",NAM$C_MAXRSS - dirlen);
3594 file[NAM$C_MAXRSS] = '\0';
3597 exp_res = do_rmsexpand
3598 (file, vmspipe_file, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL);
3599 if (!exp_res) continue;
3601 if (cando_by_name_int
3602 (S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)
3603 && cando_by_name_int
3604 (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) {
3605 vmspipe_file_status = 1;
3606 return vmspipe_file;
3609 vmspipe_file_status = -1; /* failed, use tempfiles */
3616 vmspipe_tempfile(pTHX)
3618 char file[NAM$C_MAXRSS+1];
3620 static int index = 0;
3624 /* create a tempfile */
3626 /* we can't go from W, shr=get to R, shr=get without
3627 an intermediate vulnerable state, so don't bother trying...
3629 and lib$spawn doesn't shr=put, so have to close the write
3631 So... match up the creation date/time and the FID to
3632 make sure we're dealing with the same file
3637 if (!decc_filename_unix_only) {
3638 sprintf(file,"sys$scratch:perlpipe_%08.8x_%d.com",mypid,index);
3639 fp = fopen(file,"w");
3641 sprintf(file,"sys$login:perlpipe_%08.8x_%d.com",mypid,index);
3642 fp = fopen(file,"w");
3644 sprintf(file,"sys$disk:[]perlpipe_%08.8x_%d.com",mypid,index);
3645 fp = fopen(file,"w");
3650 sprintf(file,"/tmp/perlpipe_%08.8x_%d.com",mypid,index);
3651 fp = fopen(file,"w");
3653 sprintf(file,"/sys$login/perlpipe_%08.8x_%d.com",mypid,index);
3654 fp = fopen(file,"w");
3656 sprintf(file,"./perlpipe_%08.8x_%d.com",mypid,index);
3657 fp = fopen(file,"w");
3661 if (!fp) return 0; /* we're hosed */
3663 fprintf(fp,"$! 'f$verify(0)'\n");
3664 fprintf(fp,"$! --- protect against nonstandard definitions ---\n");
3665 fprintf(fp,"$ perl_cfile = f$environment(\"procedure\")\n");
3666 fprintf(fp,"$ perl_define = \"define/nolog\"\n");
3667 fprintf(fp,"$ perl_on = \"set noon\"\n");
3668 fprintf(fp,"$ perl_exit = \"exit\"\n");
3669 fprintf(fp,"$ perl_del = \"delete\"\n");
3670 fprintf(fp,"$ pif = \"if\"\n");
3671 fprintf(fp,"$! --- define i/o redirection (sys$output set by lib$spawn)\n");
3672 fprintf(fp,"$ pif perl_popen_in .nes. \"\" then perl_define/user/name_attributes=confine sys$input 'perl_popen_in'\n");
3673 fprintf(fp,"$ pif perl_popen_err .nes. \"\" then perl_define/user/name_attributes=confine sys$error 'perl_popen_err'\n");
3674 fprintf(fp,"$ pif perl_popen_out .nes. \"\" then perl_define sys$output 'perl_popen_out'\n");
3675 fprintf(fp,"$! --- build command line to get max possible length\n");
3676 fprintf(fp,"$c=perl_popen_cmd0\n");
3677 fprintf(fp,"$c=c+perl_popen_cmd1\n");
3678 fprintf(fp,"$c=c+perl_popen_cmd2\n");
3679 fprintf(fp,"$x=perl_popen_cmd3\n");
3680 fprintf(fp,"$c=c+x\n");
3681 fprintf(fp,"$ perl_on\n");
3682 fprintf(fp,"$ 'c'\n");
3683 fprintf(fp,"$ perl_status = $STATUS\n");
3684 fprintf(fp,"$ perl_del 'perl_cfile'\n");
3685 fprintf(fp,"$ perl_exit 'perl_status'\n");
3688 fgetname(fp, file, 1);
3689 fstat(fileno(fp), (struct stat *)&s0);
3692 if (decc_filename_unix_only)
3693 do_tounixspec(file, file, 0, NULL);
3694 fp = fopen(file,"r","shr=get");
3696 fstat(fileno(fp), (struct stat *)&s1);
3698 cmp_result = VMS_INO_T_COMPARE(s0.crtl_stat.st_ino, s1.crtl_stat.st_ino);
3699 if ((cmp_result != 0) && (s0.st_ctime != s1.st_ctime)) {
3710 safe_popen(pTHX_ const char *cmd, const char *in_mode, int *psts)
3712 static int handler_set_up = FALSE;
3713 unsigned long int sts, flags = CLI$M_NOWAIT;
3714 /* The use of a GLOBAL table (as was done previously) rendered
3715 * Perl's qx() or `` unusable from a C<$ SET SYMBOL/SCOPE=NOGLOBAL> DCL
3716 * environment. Hence we've switched to LOCAL symbol table.
3718 unsigned int table = LIB$K_CLI_LOCAL_SYM;
3720 char *p, mode[10], symbol[MAX_DCL_SYMBOL+1], *vmspipe;
3721 char *in, *out, *err, mbx[512];
3723 char tfilebuf[NAM$C_MAXRSS+1];
3725 char cmd_sym_name[20];
3726 struct dsc$descriptor_s d_symbol= {0, DSC$K_DTYPE_T,
3727 DSC$K_CLASS_S, symbol};
3728 struct dsc$descriptor_s vmspipedsc = {0, DSC$K_DTYPE_T,
3730 struct dsc$descriptor_s d_sym_cmd = {0, DSC$K_DTYPE_T,
3731 DSC$K_CLASS_S, cmd_sym_name};
3732 struct dsc$descriptor_s *vmscmd;
3733 $DESCRIPTOR(d_sym_in ,"PERL_POPEN_IN");
3734 $DESCRIPTOR(d_sym_out,"PERL_POPEN_OUT");
3735 $DESCRIPTOR(d_sym_err,"PERL_POPEN_ERR");
3737 if (!head_PLOC) store_pipelocs(aTHX); /* at least TRY to use a static vmspipe file */
3739 /* once-per-program initialization...
3740 note that the SETAST calls and the dual test of pipe_ef
3741 makes sure that only the FIRST thread through here does
3742 the initialization...all other threads wait until it's
3745 Yeah, uglier than a pthread call, it's got all the stuff inline
3746 rather than in a separate routine.
3750 _ckvmssts(sys$setast(0));
3752 unsigned long int pidcode = JPI$_PID;
3753 $DESCRIPTOR(d_delay, RETRY_DELAY);
3754 _ckvmssts(lib$get_ef(&pipe_ef));
3755 _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
3756 _ckvmssts(sys$bintim(&d_delay, delaytime));
3758 if (!handler_set_up) {
3759 _ckvmssts(sys$dclexh(&pipe_exitblock));
3760 handler_set_up = TRUE;
3762 _ckvmssts(sys$setast(1));
3765 /* see if we can find a VMSPIPE.COM */
3768 vmspipe = find_vmspipe(aTHX);
3770 strcpy(tfilebuf+1,vmspipe);
3771 } else { /* uh, oh...we're in tempfile hell */
3772 tpipe = vmspipe_tempfile(aTHX);
3773 if (!tpipe) { /* a fish popular in Boston */
3774 if (ckWARN(WARN_PIPE)) {
3775 Perl_warner(aTHX_ packWARN(WARN_PIPE),"unable to find VMSPIPE.COM for i/o piping");
3779 fgetname(tpipe,tfilebuf+1,1);
3781 vmspipedsc.dsc$a_pointer = tfilebuf;
3782 vmspipedsc.dsc$w_length = strlen(tfilebuf);
3784 sts = setup_cmddsc(aTHX_ cmd,0,0,&vmscmd);
3787 case RMS$_FNF: case RMS$_DNF:
3788 set_errno(ENOENT); break;
3790 set_errno(ENOTDIR); break;
3792 set_errno(ENODEV); break;
3794 set_errno(EACCES); break;
3796 set_errno(EINVAL); break;
3797 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
3798 set_errno(E2BIG); break;
3799 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
3800 _ckvmssts(sts); /* fall through */
3801 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
3804 set_vaxc_errno(sts);
3805 if (*mode != 'n' && ckWARN(WARN_PIPE)) {
3806 Perl_warner(aTHX_ packWARN(WARN_PIPE),"Can't pipe \"%*s\": %s", strlen(cmd), cmd, Strerror(errno));
3812 _ckvmssts(lib$get_vm(&n, &info));
3814 strcpy(mode,in_mode);
3817 info->completion = 0;
3818 info->closing = FALSE;
3825 info->in_done = TRUE;
3826 info->out_done = TRUE;
3827 info->err_done = TRUE;
3829 in = PerlMem_malloc(VMS_MAXRSS);
3830 if (in == NULL) _ckvmssts(SS$_INSFMEM);
3831 out = PerlMem_malloc(VMS_MAXRSS);
3832 if (out == NULL) _ckvmssts(SS$_INSFMEM);
3833 err = PerlMem_malloc(VMS_MAXRSS);
3834 if (err == NULL) _ckvmssts(SS$_INSFMEM);
3836 in[0] = out[0] = err[0] = '\0';
3838 if ((p = strchr(mode,'F')) != NULL) { /* F -> use FILE* */
3842 if ((p = strchr(mode,'W')) != NULL) { /* W -> wait for completion */
3847 if (*mode == 'r') { /* piping from subroutine */
3849 info->out = pipe_infromchild_setup(aTHX_ mbx,out);
3851 info->out->pipe_done = &info->out_done;
3852 info->out_done = FALSE;
3853 info->out->info = info;
3855 if (!info->useFILE) {
3856 info->fp = PerlIO_open(mbx, mode);
3858 info->fp = (PerlIO *) freopen(mbx, mode, stdin);
3859 Perl_vmssetuserlnm(aTHX_ "SYS$INPUT",mbx);
3862 if (!info->fp && info->out) {
3863 sys$cancel(info->out->chan_out);
3865 while (!info->out_done) {
3867 _ckvmssts(sys$setast(0));
3868 done = info->out_done;
3869 if (!done) _ckvmssts(sys$clref(pipe_ef));
3870 _ckvmssts(sys$setast(1));
3871 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
3874 if (info->out->buf) {
3875 n = info->out->bufsize * sizeof(char);
3876 _ckvmssts(lib$free_vm(&n, &info->out->buf));
3879 _ckvmssts(lib$free_vm(&n, &info->out));
3881 _ckvmssts(lib$free_vm(&n, &info));
3886 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
3888 info->err->pipe_done = &info->err_done;
3889 info->err_done = FALSE;
3890 info->err->info = info;
3893 } else if (*mode == 'w') { /* piping to subroutine */
3895 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
3897 info->out->pipe_done = &info->out_done;
3898 info->out_done = FALSE;
3899 info->out->info = info;
3902 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
3904 info->err->pipe_done = &info->err_done;
3905 info->err_done = FALSE;
3906 info->err->info = info;
3909 info->in = pipe_tochild_setup(aTHX_ in,mbx);
3910 if (!info->useFILE) {
3911 info->fp = PerlIO_open(mbx, mode);
3913 info->fp = (PerlIO *) freopen(mbx, mode, stdout);
3914 Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",mbx);
3918 info->in->pipe_done = &info->in_done;
3919 info->in_done = FALSE;
3920 info->in->info = info;
3924 if (!info->fp && info->in) {
3926 _ckvmssts(sys$qiow(0,info->in->chan_in, IO$_WRITEOF, 0,
3927 0, 0, 0, 0, 0, 0, 0, 0));
3929 while (!info->in_done) {
3931 _ckvmssts(sys$setast(0));
3932 done = info->in_done;
3933 if (!done) _ckvmssts(sys$clref(pipe_ef));
3934 _ckvmssts(sys$setast(1));
3935 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
3938 if (info->in->buf) {
3939 n = info->in->bufsize * sizeof(char);
3940 _ckvmssts(lib$free_vm(&n, &info->in->buf));
3943 _ckvmssts(lib$free_vm(&n, &info->in));
3945 _ckvmssts(lib$free_vm(&n, &info));
3951 } else if (*mode == 'n') { /* separate subprocess, no Perl i/o */
3952 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
3954 info->out->pipe_done = &info->out_done;
3955 info->out_done = FALSE;
3956 info->out->info = info;
3959 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
3961 info->err->pipe_done = &info->err_done;
3962 info->err_done = FALSE;
3963 info->err->info = info;
3967 symbol[MAX_DCL_SYMBOL] = '\0';
3969 strncpy(symbol, in, MAX_DCL_SYMBOL);
3970 d_symbol.dsc$w_length = strlen(symbol);
3971 _ckvmssts(lib$set_symbol(&d_sym_in, &d_symbol, &table));
3973 strncpy(symbol, err, MAX_DCL_SYMBOL);
3974 d_symbol.dsc$w_length = strlen(symbol);
3975 _ckvmssts(lib$set_symbol(&d_sym_err, &d_symbol, &table));
3977 strncpy(symbol, out, MAX_DCL_SYMBOL);
3978 d_symbol.dsc$w_length = strlen(symbol);
3979 _ckvmssts(lib$set_symbol(&d_sym_out, &d_symbol, &table));
3981 /* Done with the names for the pipes */
3986 p = vmscmd->dsc$a_pointer;
3987 while (*p == ' ' || *p == '\t') p++; /* remove leading whitespace */
3988 if (*p == '$') p++; /* remove leading $ */
3989 while (*p == ' ' || *p == '\t') p++;
3991 for (j = 0; j < 4; j++) {
3992 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
3993 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
3995 strncpy(symbol, p, MAX_DCL_SYMBOL);
3996 d_symbol.dsc$w_length = strlen(symbol);
3997 _ckvmssts(lib$set_symbol(&d_sym_cmd, &d_symbol, &table));
3999 if (strlen(p) > MAX_DCL_SYMBOL) {
4000 p += MAX_DCL_SYMBOL;
4005 _ckvmssts(sys$setast(0));
4006 info->next=open_pipes; /* prepend to list */
4008 _ckvmssts(sys$setast(1));
4009 /* Omit arg 2 (input file) so the child will get the parent's SYS$INPUT
4010 * and SYS$COMMAND. vmspipe.com will redefine SYS$INPUT, but we'll still
4011 * have SYS$COMMAND if we need it.
4013 _ckvmssts(lib$spawn(&vmspipedsc, 0, &nl_desc, &flags,
4014 0, &info->pid, &info->completion,
4015 0, popen_completion_ast,info,0,0,0));
4017 /* if we were using a tempfile, close it now */
4019 if (tpipe) fclose(tpipe);
4021 /* once the subprocess is spawned, it has copied the symbols and
4022 we can get rid of ours */
4024 for (j = 0; j < 4; j++) {
4025 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
4026 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
4027 _ckvmssts(lib$delete_symbol(&d_sym_cmd, &table));
4029 _ckvmssts(lib$delete_symbol(&d_sym_in, &table));
4030 _ckvmssts(lib$delete_symbol(&d_sym_err, &table));
4031 _ckvmssts(lib$delete_symbol(&d_sym_out, &table));
4032 vms_execfree(vmscmd);
4034 #ifdef PERL_IMPLICIT_CONTEXT
4037 PL_forkprocess = info->pid;
4042 _ckvmssts(sys$setast(0));
4044 if (!done) _ckvmssts(sys$clref(pipe_ef));
4045 _ckvmssts(sys$setast(1));
4046 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4048 *psts = info->completion;
4049 /* Caller thinks it is open and tries to close it. */
4050 /* This causes some problems, as it changes the error status */
4051 /* my_pclose(info->fp); */
4056 } /* end of safe_popen */
4059 /*{{{ PerlIO *my_popen(char *cmd, char *mode)*/
4061 Perl_my_popen(pTHX_ const char *cmd, const char *mode)
4065 TAINT_PROPER("popen");
4066 PERL_FLUSHALL_FOR_CHILD;
4067 return safe_popen(aTHX_ cmd,mode,&sts);
4072 /*{{{ I32 my_pclose(PerlIO *fp)*/
4073 I32 Perl_my_pclose(pTHX_ PerlIO *fp)
4075 pInfo info, last = NULL;
4076 unsigned long int retsts;
4079 for (info = open_pipes; info != NULL; last = info, info = info->next)
4080 if (info->fp == fp) break;
4082 if (info == NULL) { /* no such pipe open */
4083 set_errno(ECHILD); /* quoth POSIX */
4084 set_vaxc_errno(SS$_NONEXPR);
4088 /* If we were writing to a subprocess, insure that someone reading from
4089 * the mailbox gets an EOF. It looks like a simple fclose() doesn't
4090 * produce an EOF record in the mailbox.
4092 * well, at least sometimes it *does*, so we have to watch out for
4093 * the first EOF closing the pipe (and DASSGN'ing the channel)...
4097 PerlIO_flush(info->fp); /* first, flush data */
4099 fflush((FILE *)info->fp);
4102 _ckvmssts(sys$setast(0));
4103 info->closing = TRUE;
4104 done = info->done && info->in_done && info->out_done && info->err_done;
4105 /* hanging on write to Perl's input? cancel it */
4106 if (info->mode == 'r' && info->out && !info->out_done) {
4107 if (info->out->chan_out) {
4108 _ckvmssts(sys$cancel(info->out->chan_out));
4109 if (!info->out->chan_in) { /* EOF generation, need AST */
4110 _ckvmssts(sys$dclast(pipe_infromchild_ast,info->out,0));
4114 if (info->in && !info->in_done && !info->in->shut_on_empty) /* EOF if hasn't had one yet */
4115 _ckvmssts(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
4117 _ckvmssts(sys$setast(1));
4120 PerlIO_close(info->fp);
4122 fclose((FILE *)info->fp);
4125 we have to wait until subprocess completes, but ALSO wait until all
4126 the i/o completes...otherwise we'll be freeing the "info" structure
4127 that the i/o ASTs could still be using...
4131 _ckvmssts(sys$setast(0));
4132 done = info->done && info->in_done && info->out_done && info->err_done;
4133 if (!done) _ckvmssts(sys$clref(pipe_ef));
4134 _ckvmssts(sys$setast(1));
4135 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4137 retsts = info->completion;
4139 /* remove from list of open pipes */
4140 _ckvmssts(sys$setast(0));
4141 if (last) last->next = info->next;
4142 else open_pipes = info->next;
4143 _ckvmssts(sys$setast(1));
4145 /* free buffers and structures */
4148 if (info->in->buf) {
4149 n = info->in->bufsize * sizeof(char);
4150 _ckvmssts(lib$free_vm(&n, &info->in->buf));
4153 _ckvmssts(lib$free_vm(&n, &info->in));
4156 if (info->out->buf) {
4157 n = info->out->bufsize * sizeof(char);
4158 _ckvmssts(lib$free_vm(&n, &info->out->buf));
4161 _ckvmssts(lib$free_vm(&n, &info->out));
4164 if (info->err->buf) {
4165 n = info->err->bufsize * sizeof(char);
4166 _ckvmssts(lib$free_vm(&n, &info->err->buf));
4169 _ckvmssts(lib$free_vm(&n, &info->err));
4172 _ckvmssts(lib$free_vm(&n, &info));
4176 } /* end of my_pclose() */
4178 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
4179 /* Roll our own prototype because we want this regardless of whether
4180 * _VMS_WAIT is defined.
4182 __pid_t __vms_waitpid( __pid_t __pid, int *__stat_loc, int __options );
4184 /* sort-of waitpid; special handling of pipe clean-up for subprocesses
4185 created with popen(); otherwise partially emulate waitpid() unless
4186 we have a suitable one from the CRTL that came with VMS 7.2 and later.
4187 Also check processes not considered by the CRTL waitpid().
4189 /*{{{Pid_t my_waitpid(Pid_t pid, int *statusp, int flags)*/
4191 Perl_my_waitpid(pTHX_ Pid_t pid, int *statusp, int flags)
4198 if (statusp) *statusp = 0;
4200 for (info = open_pipes; info != NULL; info = info->next)
4201 if (info->pid == pid) break;
4203 if (info != NULL) { /* we know about this child */
4204 while (!info->done) {
4205 _ckvmssts(sys$setast(0));
4207 if (!done) _ckvmssts(sys$clref(pipe_ef));
4208 _ckvmssts(sys$setast(1));
4209 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4212 if (statusp) *statusp = info->completion;
4216 /* child that already terminated? */
4218 for (j = 0; j < NKEEPCLOSED && j < closed_num; j++) {
4219 if (closed_list[j].pid == pid) {
4220 if (statusp) *statusp = closed_list[j].completion;
4225 /* fall through if this child is not one of our own pipe children */
4227 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
4229 /* waitpid() became available in the CRTL as of VMS 7.0, but only
4230 * in 7.2 did we get a version that fills in the VMS completion
4231 * status as Perl has always tried to do.
4234 sts = __vms_waitpid( pid, statusp, flags );
4236 if ( sts == 0 || !(sts == -1 && errno == ECHILD) )
4239 /* If the real waitpid tells us the child does not exist, we
4240 * fall through here to implement waiting for a child that
4241 * was created by some means other than exec() (say, spawned
4242 * from DCL) or to wait for a process that is not a subprocess
4243 * of the current process.
4246 #endif /* defined(__CRTL_VER) && __CRTL_VER >= 70200000 */
4249 $DESCRIPTOR(intdsc,"0 00:00:01");
4250 unsigned long int ownercode = JPI$_OWNER, ownerpid;
4251 unsigned long int pidcode = JPI$_PID, mypid;
4252 unsigned long int interval[2];
4253 unsigned int jpi_iosb[2];
4254 struct itmlst_3 jpilist[2] = {
4255 {sizeof(ownerpid), JPI$_OWNER, &ownerpid, 0},
4260 /* Sorry folks, we don't presently implement rooting around for
4261 the first child we can find, and we definitely don't want to
4262 pass a pid of -1 to $getjpi, where it is a wildcard operation.
4268 /* Get the owner of the child so I can warn if it's not mine. If the
4269 * process doesn't exist or I don't have the privs to look at it,
4270 * I can go home early.
4272 sts = sys$getjpiw(0,&pid,NULL,&jpilist,&jpi_iosb,NULL,NULL);
4273 if (sts & 1) sts = jpi_iosb[0];
4285 set_vaxc_errno(sts);
4289 if (ckWARN(WARN_EXEC)) {
4290 /* remind folks they are asking for non-standard waitpid behavior */
4291 _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
4292 if (ownerpid != mypid)
4293 Perl_warner(aTHX_ packWARN(WARN_EXEC),
4294 "waitpid: process %x is not a child of process %x",
4298 /* simply check on it once a second until it's not there anymore. */
4300 _ckvmssts(sys$bintim(&intdsc,interval));
4301 while ((sts=lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0)) & 1) {
4302 _ckvmssts(sys$schdwk(0,0,interval,0));
4303 _ckvmssts(sys$hiber());
4305 if (sts == SS$_NONEXPR) sts = SS$_NORMAL;
4310 } /* end of waitpid() */
4315 /*{{{ char *my_gconvert(double val, int ndig, int trail, char *buf) */
4317 my_gconvert(double val, int ndig, int trail, char *buf)
4319 static char __gcvtbuf[DBL_DIG+1];
4322 loc = buf ? buf : __gcvtbuf;
4324 #ifndef __DECC /* VAXCRTL gcvt uses E format for numbers < 1 */
4326 sprintf(loc,"%.*g",ndig,val);
4332 if (!buf && ndig > DBL_DIG) ndig = DBL_DIG;
4333 return gcvt(val,ndig,loc);
4336 loc[0] = '0'; loc[1] = '\0';
4343 #if defined(__VAX) || !defined(NAML$C_MAXRSS)
4344 static int rms_free_search_context(struct FAB * fab)
4348 nam = fab->fab$l_nam;
4349 nam->nam$b_nop |= NAM$M_SYNCHK;
4350 nam->nam$l_rlf = NULL;
4352 return sys$parse(fab, NULL, NULL);
4355 #define rms_setup_nam(nam) struct NAM nam = cc$rms_nam
4356 #define rms_clear_nam_nop(nam) nam.nam$b_nop = 0;
4357 #define rms_set_nam_nop(nam, opt) nam.nam$b_nop |= (opt)
4358 #define rms_set_nam_fnb(nam, opt) nam.nam$l_fnb |= (opt)
4359 #define rms_is_nam_fnb(nam, opt) (nam.nam$l_fnb & (opt))
4360 #define rms_nam_esll(nam) nam.nam$b_esl
4361 #define rms_nam_esl(nam) nam.nam$b_esl
4362 #define rms_nam_name(nam) nam.nam$l_name
4363 #define rms_nam_namel(nam) nam.nam$l_name
4364 #define rms_nam_type(nam) nam.nam$l_type
4365 #define rms_nam_typel(nam) nam.nam$l_type
4366 #define rms_nam_ver(nam) nam.nam$l_ver
4367 #define rms_nam_verl(nam) nam.nam$l_ver
4368 #define rms_nam_rsll(nam) nam.nam$b_rsl
4369 #define rms_nam_rsl(nam) nam.nam$b_rsl
4370 #define rms_bind_fab_nam(fab, nam) fab.fab$l_nam = &nam
4371 #define rms_set_fna(fab, nam, name, size) \
4372 { fab.fab$b_fns = size; fab.fab$l_fna = name; }
4373 #define rms_get_fna(fab, nam) fab.fab$l_fna
4374 #define rms_set_dna(fab, nam, name, size) \
4375 { fab.fab$b_dns = size; fab.fab$l_dna = name; }
4376 #define rms_nam_dns(fab, nam) fab.fab$b_dns
4377 #define rms_set_esa(fab, nam, name, size) \
4378 { nam.nam$b_ess = size; nam.nam$l_esa = name; }
4379 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
4380 { nam.nam$l_esa = s_name; nam.nam$b_ess = s_size;}
4381 #define rms_set_rsa(nam, name, size) \
4382 { nam.nam$l_rsa = name; nam.nam$b_rss = size; }
4383 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
4384 { nam.nam$l_rsa = s_name; nam.nam$b_rss = s_size; }
4385 #define rms_nam_name_type_l_size(nam) \
4386 (nam.nam$b_name + nam.nam$b_type)
4388 static int rms_free_search_context(struct FAB * fab)
4392 nam = fab->fab$l_naml;
4393 nam->naml$b_nop |= NAM$M_SYNCHK;
4394 nam->naml$l_rlf = NULL;
4395 nam->naml$l_long_defname_size = 0;
4398 return sys$parse(fab, NULL, NULL);
4401 #define rms_setup_nam(nam) struct NAML nam = cc$rms_naml
4402 #define rms_clear_nam_nop(nam) nam.naml$b_nop = 0;
4403 #define rms_set_nam_nop(nam, opt) nam.naml$b_nop |= (opt)
4404 #define rms_set_nam_fnb(nam, opt) nam.naml$l_fnb |= (opt)
4405 #define rms_is_nam_fnb(nam, opt) (nam.naml$l_fnb & (opt))
4406 #define rms_nam_esll(nam) nam.naml$l_long_expand_size
4407 #define rms_nam_esl(nam) nam.naml$b_esl
4408 #define rms_nam_name(nam) nam.naml$l_name
4409 #define rms_nam_namel(nam) nam.naml$l_long_name
4410 #define rms_nam_type(nam) nam.naml$l_type
4411 #define rms_nam_typel(nam) nam.naml$l_long_type
4412 #define rms_nam_ver(nam) nam.naml$l_ver
4413 #define rms_nam_verl(nam) nam.naml$l_long_ver
4414 #define rms_nam_rsll(nam) nam.naml$l_long_result_size
4415 #define rms_nam_rsl(nam) nam.naml$b_rsl
4416 #define rms_bind_fab_nam(fab, nam) fab.fab$l_naml = &nam
4417 #define rms_set_fna(fab, nam, name, size) \
4418 { fab.fab$b_fns = 0; fab.fab$l_fna = (char *) -1; \
4419 nam.naml$l_long_filename_size = size; \
4420 nam.naml$l_long_filename = name;}
4421 #define rms_get_fna(fab, nam) nam.naml$l_long_filename
4422 #define rms_set_dna(fab, nam, name, size) \
4423 { fab.fab$b_dns = 0; fab.fab$l_dna = (char *) -1; \
4424 nam.naml$l_long_defname_size = size; \
4425 nam.naml$l_long_defname = name; }
4426 #define rms_nam_dns(fab, nam) nam.naml$l_long_defname_size
4427 #define rms_set_esa(fab, nam, name, size) \
4428 { nam.naml$b_ess = 0; nam.naml$l_esa = (char *) -1; \
4429 nam.naml$l_long_expand_alloc = size; \
4430 nam.naml$l_long_expand = name; }
4431 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
4432 { nam.naml$l_esa = s_name; nam.naml$b_ess = s_size; \
4433 nam.naml$l_long_expand = l_name; \
4434 nam.naml$l_long_expand_alloc = l_size; }
4435 #define rms_set_rsa(nam, name, size) \
4436 { nam.naml$l_rsa = NULL; nam.naml$b_rss = 0; \
4437 nam.naml$l_long_result = name; \
4438 nam.naml$l_long_result_alloc = size; }
4439 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
4440 { nam.naml$l_rsa = s_name; nam.naml$b_rss = s_size; \
4441 nam.naml$l_long_result = l_name; \
4442 nam.naml$l_long_result_alloc = l_size; }
4443 #define rms_nam_name_type_l_size(nam) \
4444 (nam.naml$l_long_name_size + nam.naml$l_long_type_size)
4448 /*{{{char *do_rmsexpand(char *fspec, char *out, int ts, char *def, unsigned opts)*/
4449 /* Shortcut for common case of simple calls to $PARSE and $SEARCH
4450 * to expand file specification. Allows for a single default file
4451 * specification and a simple mask of options. If outbuf is non-NULL,
4452 * it must point to a buffer at least NAM$C_MAXRSS bytes long, into which
4453 * the resultant file specification is placed. If outbuf is NULL, the
4454 * resultant file specification is placed into a static buffer.
4455 * The third argument, if non-NULL, is taken to be a default file
4456 * specification string. The fourth argument is unused at present.
4457 * rmesexpand() returns the address of the resultant string if
4458 * successful, and NULL on error.
4460 * New functionality for previously unused opts value:
4461 * PERL_RMSEXPAND_M_VMS - Force output file specification to VMS format.
4462 * PERL_RMSEXPAND_M_LONG - Want output in long formst
4463 * PERL_RMSEXPAND_M_VMS_IN - Input is already in VMS, so do not vmsify
4465 static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *);
4469 (pTHX_ const char *filespec,
4472 const char *defspec,
4477 static char __rmsexpand_retbuf[VMS_MAXRSS];
4478 char * vmsfspec, *tmpfspec;
4479 char * esa, *cp, *out = NULL;
4483 struct FAB myfab = cc$rms_fab;
4484 rms_setup_nam(mynam);
4486 unsigned long int retsts, trimver, trimtype, haslower = 0, isunix = 0;
4489 /* temp hack until UTF8 is actually implemented */
4490 if (fs_utf8 != NULL)
4493 if (!filespec || !*filespec) {
4494 set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL);
4498 if (ts) out = Newx(outbuf,VMS_MAXRSS,char);
4499 else outbuf = __rmsexpand_retbuf;
4507 if ((opts & PERL_RMSEXPAND_M_VMS_IN) == 0) {
4508 isunix = is_unix_filespec(filespec);
4510 vmsfspec = PerlMem_malloc(VMS_MAXRSS);
4511 if (vmsfspec == NULL) _ckvmssts(SS$_INSFMEM);
4512 if (do_tovmsspec(filespec,vmsfspec,0,fs_utf8) == NULL) {
4513 PerlMem_free(vmsfspec);
4518 filespec = vmsfspec;
4520 /* Unless we are forcing to VMS format, a UNIX input means
4521 * UNIX output, and that requires long names to be used
4523 if ((opts & PERL_RMSEXPAND_M_VMS) == 0)
4524 opts |= PERL_RMSEXPAND_M_LONG;
4531 rms_set_fna(myfab, mynam, (char *)filespec, strlen(filespec)); /* cast ok */
4532 rms_bind_fab_nam(myfab, mynam);
4534 if (defspec && *defspec) {
4536 t_isunix = is_unix_filespec(defspec);
4538 tmpfspec = PerlMem_malloc(VMS_MAXRSS);
4539 if (tmpfspec == NULL) _ckvmssts(SS$_INSFMEM);
4540 if (do_tovmsspec(defspec,tmpfspec,0,dfs_utf8) == NULL) {
4541 PerlMem_free(tmpfspec);
4542 if (vmsfspec != NULL)
4543 PerlMem_free(vmsfspec);
4550 rms_set_dna(myfab, mynam, (char *)defspec, strlen(defspec)); /* cast ok */
4553 esa = PerlMem_malloc(NAM$C_MAXRSS + 1);
4554 if (esa == NULL) _ckvmssts(SS$_INSFMEM);
4555 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
4556 esal = PerlMem_malloc(VMS_MAXRSS);
4557 if (esal == NULL) _ckvmssts(SS$_INSFMEM);
4559 rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS-1);
4561 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
4562 rms_set_rsa(mynam, outbuf, (VMS_MAXRSS - 1));
4565 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
4566 outbufl = PerlMem_malloc(VMS_MAXRSS);
4567 if (outbufl == NULL) _ckvmssts(SS$_INSFMEM);
4568 rms_set_rsal(mynam, outbuf, NAM$C_MAXRSS, outbufl, (VMS_MAXRSS - 1));
4570 rms_set_rsa(mynam, outbuf, NAM$C_MAXRSS);
4574 #ifdef NAM$M_NO_SHORT_UPCASE
4575 if (decc_efs_case_preserve)
4576 rms_set_nam_nop(mynam, NAM$M_NO_SHORT_UPCASE);
4579 /* First attempt to parse as an existing file */
4580 retsts = sys$parse(&myfab,0,0);
4581 if (!(retsts & STS$K_SUCCESS)) {
4583 /* Could not find the file, try as syntax only if error is not fatal */
4584 rms_set_nam_nop(mynam, NAM$M_SYNCHK);
4585 if (retsts == RMS$_DNF || retsts == RMS$_DIR || retsts == RMS$_DEV) {
4586 retsts = sys$parse(&myfab,0,0);
4587 if (retsts & STS$K_SUCCESS) goto expanded;
4590 /* Still could not parse the file specification */
4591 /*----------------------------------------------*/
4592 sts = rms_free_search_context(&myfab); /* Free search context */
4593 if (out) Safefree(out);
4594 if (tmpfspec != NULL)
4595 PerlMem_free(tmpfspec);
4596 if (vmsfspec != NULL)
4597 PerlMem_free(vmsfspec);
4598 if (outbufl != NULL)
4599 PerlMem_free(outbufl);
4602 set_vaxc_errno(retsts);
4603 if (retsts == RMS$_PRV) set_errno(EACCES);
4604 else if (retsts == RMS$_DEV) set_errno(ENODEV);
4605 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
4606 else set_errno(EVMSERR);
4609 retsts = sys$search(&myfab,0,0);
4610 if (!(retsts & STS$K_SUCCESS) && retsts != RMS$_FNF) {
4611 sts = rms_free_search_context(&myfab); /* Free search context */
4612 if (out) Safefree(out);
4613 if (tmpfspec != NULL)
4614 PerlMem_free(tmpfspec);
4615 if (vmsfspec != NULL)
4616 PerlMem_free(vmsfspec);
4617 if (outbufl != NULL)
4618 PerlMem_free(outbufl);
4621 set_vaxc_errno(retsts);
4622 if (retsts == RMS$_PRV) set_errno(EACCES);
4623 else set_errno(EVMSERR);
4627 /* If the input filespec contained any lowercase characters,
4628 * downcase the result for compatibility with Unix-minded code. */
4630 if (!decc_efs_case_preserve) {
4631 for (tbuf = rms_get_fna(myfab, mynam); *tbuf; tbuf++)
4632 if (islower(*tbuf)) { haslower = 1; break; }
4635 /* Is a long or a short name expected */
4636 /*------------------------------------*/
4637 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
4638 if (rms_nam_rsll(mynam)) {
4640 speclen = rms_nam_rsll(mynam);
4643 tbuf = esal; /* Not esa */
4644 speclen = rms_nam_esll(mynam);
4648 if (rms_nam_rsl(mynam)) {
4650 speclen = rms_nam_rsl(mynam);
4653 tbuf = esa; /* Not esal */
4654 speclen = rms_nam_esl(mynam);
4657 tbuf[speclen] = '\0';
4659 /* Trim off null fields added by $PARSE
4660 * If type > 1 char, must have been specified in original or default spec
4661 * (not true for version; $SEARCH may have added version of existing file).
4663 trimver = !rms_is_nam_fnb(mynam, NAM$M_EXP_VER);
4664 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
4665 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
4666 ((rms_nam_verl(mynam) - rms_nam_typel(mynam)) == 1);
4669 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
4670 ((rms_nam_ver(mynam) - rms_nam_type(mynam)) == 1);
4672 if (trimver || trimtype) {
4673 if (defspec && *defspec) {
4674 char *defesal = NULL;
4675 defesal = PerlMem_malloc(NAML$C_MAXRSS + 1);
4676 if (defesal != NULL) {
4677 struct FAB deffab = cc$rms_fab;
4678 rms_setup_nam(defnam);
4680 rms_bind_fab_nam(deffab, defnam);
4684 (deffab, defnam, (char *)defspec, rms_nam_dns(myfab, mynam));
4686 rms_set_esa(deffab, defnam, defesal, VMS_MAXRSS - 1);
4688 rms_clear_nam_nop(defnam);
4689 rms_set_nam_nop(defnam, NAM$M_SYNCHK);
4690 #ifdef NAM$M_NO_SHORT_UPCASE
4691 if (decc_efs_case_preserve)
4692 rms_set_nam_nop(defnam, NAM$M_NO_SHORT_UPCASE);
4694 if (sys$parse(&deffab,0,0) & STS$K_SUCCESS) {
4696 trimver = !rms_is_nam_fnb(defnam, NAM$M_EXP_VER);
4699 trimtype = !rms_is_nam_fnb(defnam, NAM$M_EXP_TYPE);
4702 PerlMem_free(defesal);
4706 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
4707 if (*(rms_nam_verl(mynam)) != '\"')
4708 speclen = rms_nam_verl(mynam) - tbuf;
4711 if (*(rms_nam_ver(mynam)) != '\"')
4712 speclen = rms_nam_ver(mynam) - tbuf;
4716 /* If we didn't already trim version, copy down */
4717 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
4718 if (speclen > rms_nam_verl(mynam) - tbuf)
4720 (rms_nam_typel(mynam),
4721 rms_nam_verl(mynam),
4722 speclen - (rms_nam_verl(mynam) - tbuf));
4723 speclen -= rms_nam_verl(mynam) - rms_nam_typel(mynam);
4726 if (speclen > rms_nam_ver(mynam) - tbuf)
4728 (rms_nam_type(mynam),
4730 speclen - (rms_nam_ver(mynam) - tbuf));
4731 speclen -= rms_nam_ver(mynam) - rms_nam_type(mynam);
4736 /* Done with these copies of the input files */
4737 /*-------------------------------------------*/
4738 if (vmsfspec != NULL)
4739 PerlMem_free(vmsfspec);
4740 if (tmpfspec != NULL)
4741 PerlMem_free(tmpfspec);
4743 /* If we just had a directory spec on input, $PARSE "helpfully"
4744 * adds an empty name and type for us */
4745 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
4746 if (rms_nam_namel(mynam) == rms_nam_typel(mynam) &&
4747 rms_nam_verl(mynam) == rms_nam_typel(mynam) + 1 &&
4748 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
4749 speclen = rms_nam_namel(mynam) - tbuf;
4752 if (rms_nam_name(mynam) == rms_nam_type(mynam) &&
4753 rms_nam_ver(mynam) == rms_nam_ver(mynam) + 1 &&
4754 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
4755 speclen = rms_nam_name(mynam) - tbuf;
4758 /* Posix format specifications must have matching quotes */
4759 if (speclen < (VMS_MAXRSS - 1)) {
4760 if (decc_posix_compliant_pathnames && (tbuf[0] == '\"')) {
4761 if ((speclen > 1) && (tbuf[speclen-1] != '\"')) {
4762 tbuf[speclen] = '\"';
4767 tbuf[speclen] = '\0';
4768 if (haslower && !decc_efs_case_preserve) __mystrtolower(tbuf);
4770 /* Have we been working with an expanded, but not resultant, spec? */
4771 /* Also, convert back to Unix syntax if necessary. */
4773 if (!rms_nam_rsll(mynam)) {
4775 if (do_tounixspec(esa,outbuf,0,fs_utf8) == NULL) {
4776 if (out) Safefree(out);
4779 if (outbufl != NULL)
4780 PerlMem_free(outbufl);
4784 else strcpy(outbuf,esa);
4787 tmpfspec = PerlMem_malloc(VMS_MAXRSS);
4788 if (tmpfspec == NULL) _ckvmssts(SS$_INSFMEM);
4789 if (do_tounixspec(outbuf,tmpfspec,0,fs_utf8) == NULL) {
4790 if (out) Safefree(out);
4793 PerlMem_free(tmpfspec);
4794 if (outbufl != NULL)
4795 PerlMem_free(outbufl);
4798 strcpy(outbuf,tmpfspec);
4799 PerlMem_free(tmpfspec);
4802 rms_set_rsal(mynam, NULL, 0, NULL, 0);
4803 sts = rms_free_search_context(&myfab); /* Free search context */
4806 if (outbufl != NULL)
4807 PerlMem_free(outbufl);
4811 /* External entry points */
4812 char *Perl_rmsexpand(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
4813 { return do_rmsexpand(spec,buf,0,def,opt,NULL,NULL); }
4814 char *Perl_rmsexpand_ts(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
4815 { return do_rmsexpand(spec,buf,1,def,opt,NULL,NULL); }
4816 char *Perl_rmsexpand_utf8
4817 (pTHX_ const char *spec, char *buf, const char *def,
4818 unsigned opt, int * fs_utf8, int * dfs_utf8)
4819 { return do_rmsexpand(spec,buf,0,def,opt, fs_utf8, dfs_utf8); }
4820 char *Perl_rmsexpand_utf8_ts
4821 (pTHX_ const char *spec, char *buf, const char *def,
4822 unsigned opt, int * fs_utf8, int * dfs_utf8)
4823 { return do_rmsexpand(spec,buf,1,def,opt, fs_utf8, dfs_utf8); }
4827 ** The following routines are provided to make life easier when
4828 ** converting among VMS-style and Unix-style directory specifications.
4829 ** All will take input specifications in either VMS or Unix syntax. On
4830 ** failure, all return NULL. If successful, the routines listed below
4831 ** return a pointer to a buffer containing the appropriately
4832 ** reformatted spec (and, therefore, subsequent calls to that routine
4833 ** will clobber the result), while the routines of the same names with
4834 ** a _ts suffix appended will return a pointer to a mallocd string
4835 ** containing the appropriately reformatted spec.
4836 ** In all cases, only explicit syntax is altered; no check is made that
4837 ** the resulting string is valid or that the directory in question
4840 ** fileify_dirspec() - convert a directory spec into the name of the
4841 ** directory file (i.e. what you can stat() to see if it's a dir).
4842 ** The style (VMS or Unix) of the result is the same as the style
4843 ** of the parameter passed in.
4844 ** pathify_dirspec() - convert a directory spec into a path (i.e.
4845 ** what you prepend to a filename to indicate what directory it's in).
4846 ** The style (VMS or Unix) of the result is the same as the style
4847 ** of the parameter passed in.
4848 ** tounixpath() - convert a directory spec into a Unix-style path.
4849 ** tovmspath() - convert a directory spec into a VMS-style path.
4850 ** tounixspec() - convert any file spec into a Unix-style file spec.
4851 ** tovmsspec() - convert any file spec into a VMS-style spec.
4852 ** xxxxx_utf8() - Variants that support UTF8 encoding of Unix-Style file spec.
4854 ** Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>
4855 ** Permission is given to distribute this code as part of the Perl
4856 ** standard distribution under the terms of the GNU General Public
4857 ** License or the Perl Artistic License. Copies of each may be
4858 ** found in the Perl standard distribution.
4861 /*{{{ char *fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/
4862 static char *mp_do_fileify_dirspec(pTHX_ const char *dir,char *buf,int ts, int *utf8_fl)
4864 static char __fileify_retbuf[VMS_MAXRSS];
4865 unsigned long int dirlen, retlen, addmfd = 0, hasfilename = 0;
4866 char *retspec, *cp1, *cp2, *lastdir;
4867 char *trndir, *vmsdir;
4868 unsigned short int trnlnm_iter_count;
4870 if (utf8_fl != NULL)
4873 if (!dir || !*dir) {
4874 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
4876 dirlen = strlen(dir);
4877 while (dirlen && dir[dirlen-1] == '/') --dirlen;
4878 if (!dirlen) { /* We had Unixish '/' -- substitute top of current tree */
4879 if (!decc_posix_compliant_pathnames && decc_disable_posix_root) {
4886 if (dirlen > (VMS_MAXRSS - 1)) {
4887 set_errno(ENAMETOOLONG); set_vaxc_errno(RMS$_SYN);
4890 trndir = PerlMem_malloc(VMS_MAXRSS + 1);
4891 if (trndir == NULL) _ckvmssts(SS$_INSFMEM);
4892 if (!strpbrk(dir+1,"/]>:") &&
4893 (!decc_posix_compliant_pathnames && decc_disable_posix_root)) {
4894 strcpy(trndir,*dir == '/' ? dir + 1: dir);
4895 trnlnm_iter_count = 0;
4896 while (!strpbrk(trndir,"/]>:>") && my_trnlnm(trndir,trndir,0)) {
4897 trnlnm_iter_count++;
4898 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
4900 dirlen = strlen(trndir);
4903 strncpy(trndir,dir,dirlen);
4904 trndir[dirlen] = '\0';
4907 /* At this point we are done with *dir and use *trndir which is a
4908 * copy that can be modified. *dir must not be modified.
4911 /* If we were handed a rooted logical name or spec, treat it like a
4912 * simple directory, so that
4913 * $ Define myroot dev:[dir.]
4914 * ... do_fileify_dirspec("myroot",buf,1) ...
4915 * does something useful.
4917 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".]")) {
4918 trndir[--dirlen] = '\0';
4919 trndir[dirlen-1] = ']';
4921 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".>")) {
4922 trndir[--dirlen] = '\0';
4923 trndir[dirlen-1] = '>';
4926 if ((cp1 = strrchr(trndir,']')) != NULL || (cp1 = strrchr(trndir,'>')) != NULL) {
4927 /* If we've got an explicit filename, we can just shuffle the string. */
4928 if (*(cp1+1)) hasfilename = 1;
4929 /* Similarly, we can just back up a level if we've got multiple levels
4930 of explicit directories in a VMS spec which ends with directories. */
4932 for (cp2 = cp1; cp2 > trndir; cp2--) {
4934 if ((cp2 - 1 > trndir) && (*(cp2 - 1) != '^')) {
4935 /* fix-me, can not scan EFS file specs backward like this */
4936 *cp2 = *cp1; *cp1 = '\0';
4941 if (*cp2 == '[' || *cp2 == '<') break;
4946 vmsdir = PerlMem_malloc(VMS_MAXRSS + 1);
4947 if (vmsdir == NULL) _ckvmssts(SS$_INSFMEM);
4948 cp1 = strpbrk(trndir,"]:>");
4949 if (hasfilename || !cp1) { /* Unix-style path or filename */
4950 if (trndir[0] == '.') {
4951 if (trndir[1] == '\0' || (trndir[1] == '/' && trndir[2] == '\0')) {
4952 PerlMem_free(trndir);
4953 PerlMem_free(vmsdir);
4954 return do_fileify_dirspec("[]",buf,ts,NULL);
4956 else if (trndir[1] == '.' &&
4957 (trndir[2] == '\0' || (trndir[2] == '/' && trndir[3] == '\0'))) {
4958 PerlMem_free(trndir);
4959 PerlMem_free(vmsdir);
4960 return do_fileify_dirspec("[-]",buf,ts,NULL);
4963 if (dirlen && trndir[dirlen-1] == '/') { /* path ends with '/'; just add .dir;1 */
4964 dirlen -= 1; /* to last element */
4965 lastdir = strrchr(trndir,'/');
4967 else if ((cp1 = strstr(trndir,"/.")) != NULL) {
4968 /* If we have "/." or "/..", VMSify it and let the VMS code
4969 * below expand it, rather than repeating the code to handle
4970 * relative components of a filespec here */
4972 if (*(cp1+2) == '.') cp1++;
4973 if (*(cp1+2) == '/' || *(cp1+2) == '\0') {
4975 if (do_tovmsspec(trndir,vmsdir,0,NULL) == NULL) {
4976 PerlMem_free(trndir);
4977 PerlMem_free(vmsdir);
4980 if (strchr(vmsdir,'/') != NULL) {
4981 /* If do_tovmsspec() returned it, it must have VMS syntax
4982 * delimiters in it, so it's a mixed VMS/Unix spec. We take
4983 * the time to check this here only so we avoid a recursion
4984 * loop; otherwise, gigo.
4986 PerlMem_free(trndir);
4987 PerlMem_free(vmsdir);
4988 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN);
4991 if (do_fileify_dirspec(vmsdir,trndir,0,NULL) == NULL) {
4992 PerlMem_free(trndir);
4993 PerlMem_free(vmsdir);
4996 ret_chr = do_tounixspec(trndir,buf,ts,NULL);
4997 PerlMem_free(trndir);
4998 PerlMem_free(vmsdir);
5002 } while ((cp1 = strstr(cp1,"/.")) != NULL);
5003 lastdir = strrchr(trndir,'/');
5005 else if (dirlen >= 7 && !strcmp(&trndir[dirlen-7],"/000000")) {
5007 /* Ditto for specs that end in an MFD -- let the VMS code
5008 * figure out whether it's a real device or a rooted logical. */
5010 /* This should not happen any more. Allowing the fake /000000
5011 * in a UNIX pathname causes all sorts of problems when trying
5012 * to run in UNIX emulation. So the VMS to UNIX conversions
5013 * now remove the fake /000000 directories.
5016 trndir[dirlen] = '/'; trndir[dirlen+1] = '\0';
5017 if (do_tovmsspec(trndir,vmsdir,0,NULL) == NULL) {
5018 PerlMem_free(trndir);
5019 PerlMem_free(vmsdir);
5022 if (do_fileify_dirspec(vmsdir,trndir,0,NULL) == NULL) {
5023 PerlMem_free(trndir);
5024 PerlMem_free(vmsdir);
5027 ret_chr = do_tounixspec(trndir,buf,ts,NULL);
5028 PerlMem_free(trndir);
5029 PerlMem_free(vmsdir);
5034 if ( !(lastdir = cp1 = strrchr(trndir,'/')) &&
5035 !(lastdir = cp1 = strrchr(trndir,']')) &&
5036 !(lastdir = cp1 = strrchr(trndir,'>'))) cp1 = trndir;
5037 if ((cp2 = strchr(cp1,'.'))) { /* look for explicit type */
5040 /* For EFS or ODS-5 look for the last dot */
5041 if (decc_efs_charset) {
5042 cp2 = strrchr(cp1,'.');
5044 if (vms_process_case_tolerant) {
5045 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
5046 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
5047 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
5048 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
5049 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
5050 (ver || *cp3)))))) {
5051 PerlMem_free(trndir);
5052 PerlMem_free(vmsdir);
5054 set_vaxc_errno(RMS$_DIR);
5059 if (!*(cp2+1) || *(cp2+1) != 'D' || /* Wrong type. */
5060 !*(cp2+2) || *(cp2+2) != 'I' || /* Bzzt. */
5061 !*(cp2+3) || *(cp2+3) != 'R' ||
5062 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
5063 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
5064 (ver || *cp3)))))) {
5065 PerlMem_free(trndir);
5066 PerlMem_free(vmsdir);
5068 set_vaxc_errno(RMS$_DIR);
5072 dirlen = cp2 - trndir;
5076 retlen = dirlen + 6;
5077 if (buf) retspec = buf;
5078 else if (ts) Newx(retspec,retlen+1,char);
5079 else retspec = __fileify_retbuf;
5080 memcpy(retspec,trndir,dirlen);
5081 retspec[dirlen] = '\0';
5083 /* We've picked up everything up to the directory file name.
5084 Now just add the type and version, and we're set. */
5085 if ((!decc_efs_case_preserve) && vms_process_case_tolerant)
5086 strcat(retspec,".dir;1");
5088 strcat(retspec,".DIR;1");
5089 PerlMem_free(trndir);
5090 PerlMem_free(vmsdir);
5093 else { /* VMS-style directory spec */
5095 char *esa, term, *cp;
5096 unsigned long int sts, cmplen, haslower = 0;
5097 unsigned int nam_fnb;
5099 struct FAB dirfab = cc$rms_fab;
5100 rms_setup_nam(savnam);
5101 rms_setup_nam(dirnam);
5103 esa = PerlMem_malloc(VMS_MAXRSS + 1);
5104 if (esa == NULL) _ckvmssts(SS$_INSFMEM);
5105 rms_set_fna(dirfab, dirnam, trndir, strlen(trndir));
5106 rms_bind_fab_nam(dirfab, dirnam);
5107 rms_set_dna(dirfab, dirnam, ".DIR;1", 6);
5108 rms_set_esa(dirfab, dirnam, esa, (VMS_MAXRSS - 1));
5109 #ifdef NAM$M_NO_SHORT_UPCASE
5110 if (decc_efs_case_preserve)
5111 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
5114 for (cp = trndir; *cp; cp++)
5115 if (islower(*cp)) { haslower = 1; break; }
5116 if (!((sts = sys$parse(&dirfab)) & STS$K_SUCCESS)) {
5117 if ((dirfab.fab$l_sts == RMS$_DIR) || (dirfab.fab$l_sts == RMS$_DNF)) {
5118 rms_set_nam_nop(dirnam, NAM$M_SYNCHK);
5119 sts = sys$parse(&dirfab) & STS$K_SUCCESS;
5123 PerlMem_free(trndir);
5124 PerlMem_free(vmsdir);
5126 set_vaxc_errno(dirfab.fab$l_sts);
5132 /* Does the file really exist? */
5133 if (sys$search(&dirfab)& STS$K_SUCCESS) {
5134 /* Yes; fake the fnb bits so we'll check type below */
5135 rms_set_nam_fnb(dirnam, (NAM$M_EXP_TYPE | NAM$M_EXP_VER));
5137 else { /* No; just work with potential name */
5138 if (dirfab.fab$l_sts == RMS$_FNF) dirnam = savnam;
5141 fab_sts = dirfab.fab$l_sts;
5142 sts = rms_free_search_context(&dirfab);
5144 PerlMem_free(trndir);
5145 PerlMem_free(vmsdir);
5146 set_errno(EVMSERR); set_vaxc_errno(fab_sts);
5151 esa[rms_nam_esll(dirnam)] = '\0';
5152 if (!rms_is_nam_fnb(dirnam, (NAM$M_EXP_DEV | NAM$M_EXP_DIR))) {
5153 cp1 = strchr(esa,']');
5154 if (!cp1) cp1 = strchr(esa,'>');
5155 if (cp1) { /* Should always be true */
5156 rms_nam_esll(dirnam) -= cp1 - esa - 1;
5157 memmove(esa,cp1 + 1, rms_nam_esll(dirnam));
5160 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_TYPE)) { /* Was type specified? */
5161 /* Yep; check version while we're at it, if it's there. */
5162 cmplen = rms_is_nam_fnb(dirnam, NAM$M_EXP_VER) ? 6 : 4;
5163 if (strncmp(rms_nam_typel(dirnam), ".DIR;1", cmplen)) {
5164 /* Something other than .DIR[;1]. Bzzt. */
5165 sts = rms_free_search_context(&dirfab);
5167 PerlMem_free(trndir);
5168 PerlMem_free(vmsdir);
5170 set_vaxc_errno(RMS$_DIR);
5175 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_NAME)) {
5176 /* They provided at least the name; we added the type, if necessary, */
5177 if (buf) retspec = buf; /* in sys$parse() */
5178 else if (ts) Newx(retspec, rms_nam_esll(dirnam)+1, char);
5179 else retspec = __fileify_retbuf;
5180 strcpy(retspec,esa);
5181 sts = rms_free_search_context(&dirfab);
5182 PerlMem_free(trndir);
5184 PerlMem_free(vmsdir);
5187 if ((cp1 = strstr(esa,".][000000]")) != NULL) {
5188 for (cp2 = cp1 + 9; *cp2; cp1++,cp2++) *cp1 = *cp2;
5190 rms_nam_esll(dirnam) -= 9;
5192 if ((cp1 = strrchr(esa,']')) == NULL) cp1 = strrchr(esa,'>');
5193 if (cp1 == NULL) { /* should never happen */
5194 sts = rms_free_search_context(&dirfab);
5195 PerlMem_free(trndir);
5197 PerlMem_free(vmsdir);
5202 retlen = strlen(esa);
5203 cp1 = strrchr(esa,'.');
5204 /* ODS-5 directory specifications can have extra "." in them. */
5205 /* Fix-me, can not scan EFS file specifications backwards */
5206 while (cp1 != NULL) {
5207 if ((cp1-1 == esa) || (*(cp1-1) != '^'))
5211 while ((cp1 > esa) && (*cp1 != '.'))
5218 if ((cp1) != NULL) {
5219 /* There's more than one directory in the path. Just roll back. */
5221 if (buf) retspec = buf;
5222 else if (ts) Newx(retspec,retlen+7,char);
5223 else retspec = __fileify_retbuf;
5224 strcpy(retspec,esa);
5227 if (rms_is_nam_fnb(dirnam, NAM$M_ROOT_DIR)) {
5228 /* Go back and expand rooted logical name */
5229 rms_set_nam_nop(dirnam, NAM$M_SYNCHK | NAM$M_NOCONCEAL);
5230 #ifdef NAM$M_NO_SHORT_UPCASE
5231 if (decc_efs_case_preserve)
5232 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
5234 if (!(sys$parse(&dirfab) & STS$K_SUCCESS)) {
5235 sts = rms_free_search_context(&dirfab);
5237 PerlMem_free(trndir);
5238 PerlMem_free(vmsdir);
5240 set_vaxc_errno(dirfab.fab$l_sts);
5243 retlen = rms_nam_esll(dirnam) - 9; /* esa - '][' - '].DIR;1' */
5244 if (buf) retspec = buf;
5245 else if (ts) Newx(retspec,retlen+16,char);
5246 else retspec = __fileify_retbuf;
5247 cp1 = strstr(esa,"][");
5248 if (!cp1) cp1 = strstr(esa,"]<");
5250 memcpy(retspec,esa,dirlen);
5251 if (!strncmp(cp1+2,"000000]",7)) {
5252 retspec[dirlen-1] = '\0';
5253 /* fix-me Not full ODS-5, just extra dots in directories for now */
5254 cp1 = retspec + dirlen - 1;
5255 while (cp1 > retspec)
5260 if (*(cp1-1) != '^')
5265 if (*cp1 == '.') *cp1 = ']';
5267 memmove(cp1+8,cp1+1,retspec+dirlen-cp1);
5268 memmove(cp1+1,"000000]",7);
5272 memmove(retspec+dirlen,cp1+2,retlen-dirlen);
5273 retspec[retlen] = '\0';
5274 /* Convert last '.' to ']' */
5275 cp1 = retspec+retlen-1;
5276 while (*cp != '[') {
5279 /* Do not trip on extra dots in ODS-5 directories */
5280 if ((cp1 == retspec) || (*(cp1-1) != '^'))
5284 if (*cp1 == '.') *cp1 = ']';
5286 memmove(cp1+8,cp1+1,retspec+dirlen-cp1);
5287 memmove(cp1+1,"000000]",7);
5291 else { /* This is a top-level dir. Add the MFD to the path. */
5292 if (buf) retspec = buf;
5293 else if (ts) Newx(retspec,retlen+16,char);
5294 else retspec = __fileify_retbuf;
5297 while ((*cp1 != ':') && (*cp1 != '\0')) *(cp2++) = *(cp1++);
5298 strcpy(cp2,":[000000]");
5303 sts = rms_free_search_context(&dirfab);
5304 /* We've set up the string up through the filename. Add the
5305 type and version, and we're done. */
5306 strcat(retspec,".DIR;1");
5308 /* $PARSE may have upcased filespec, so convert output to lower
5309 * case if input contained any lowercase characters. */
5310 if (haslower && !decc_efs_case_preserve) __mystrtolower(retspec);
5311 PerlMem_free(trndir);
5313 PerlMem_free(vmsdir);
5316 } /* end of do_fileify_dirspec() */
5318 /* External entry points */
5319 char *Perl_fileify_dirspec(pTHX_ const char *dir, char *buf)
5320 { return do_fileify_dirspec(dir,buf,0,NULL); }
5321 char *Perl_fileify_dirspec_ts(pTHX_ const char *dir, char *buf)
5322 { return do_fileify_dirspec(dir,buf,1,NULL); }
5323 char *Perl_fileify_dirspec_utf8(pTHX_ const char *dir, char *buf, int * utf8_fl)
5324 { return do_fileify_dirspec(dir,buf,0,utf8_fl); }
5325 char *Perl_fileify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int * utf8_fl)
5326 { return do_fileify_dirspec(dir,buf,1,utf8_fl); }
5328 /*{{{ char *pathify_dirspec[_ts](char *path, char *buf)*/
5329 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int * utf8_fl)
5331 static char __pathify_retbuf[VMS_MAXRSS];
5332 unsigned long int retlen;
5333 char *retpath, *cp1, *cp2, *trndir;
5334 unsigned short int trnlnm_iter_count;
5337 if (utf8_fl != NULL)
5340 if (!dir || !*dir) {
5341 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
5344 trndir = PerlMem_malloc(VMS_MAXRSS);
5345 if (trndir == NULL) _ckvmssts(SS$_INSFMEM);
5346 if (*dir) strcpy(trndir,dir);
5347 else getcwd(trndir,VMS_MAXRSS - 1);
5349 trnlnm_iter_count = 0;
5350 while (!strpbrk(trndir,"/]:>") && !no_translate_barewords
5351 && my_trnlnm(trndir,trndir,0)) {
5352 trnlnm_iter_count++;
5353 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
5354 trnlen = strlen(trndir);
5356 /* Trap simple rooted lnms, and return lnm:[000000] */
5357 if (!strcmp(trndir+trnlen-2,".]")) {
5358 if (buf) retpath = buf;
5359 else if (ts) Newx(retpath,strlen(dir)+10,char);
5360 else retpath = __pathify_retbuf;
5361 strcpy(retpath,dir);
5362 strcat(retpath,":[000000]");
5363 PerlMem_free(trndir);
5368 /* At this point we do not work with *dir, but the copy in
5369 * *trndir that is modifiable.
5372 if (!strpbrk(trndir,"]:>")) { /* Unix-style path or plain name */
5373 if (*trndir == '.' && (*(trndir+1) == '\0' ||
5374 (*(trndir+1) == '.' && *(trndir+2) == '\0')))
5375 retlen = 2 + (*(trndir+1) != '\0');
5377 if ( !(cp1 = strrchr(trndir,'/')) &&
5378 !(cp1 = strrchr(trndir,']')) &&
5379 !(cp1 = strrchr(trndir,'>')) ) cp1 = trndir;
5380 if ((cp2 = strchr(cp1,'.')) != NULL &&
5381 (*(cp2-1) != '/' || /* Trailing '.', '..', */
5382 !(*(cp2+1) == '\0' || /* or '...' are dirs. */
5383 (*(cp2+1) == '.' && *(cp2+2) == '\0') ||
5384 (*(cp2+1) == '.' && *(cp2+2) == '.' && *(cp2+3) == '\0')))) {
5387 /* For EFS or ODS-5 look for the last dot */
5388 if (decc_efs_charset) {
5389 cp2 = strrchr(cp1,'.');
5391 if (vms_process_case_tolerant) {
5392 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
5393 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
5394 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
5395 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
5396 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
5397 (ver || *cp3)))))) {
5398 PerlMem_free(trndir);
5400 set_vaxc_errno(RMS$_DIR);
5405 if (!*(cp2+1) || *(cp2+1) != 'D' || /* Wrong type. */
5406 !*(cp2+2) || *(cp2+2) != 'I' || /* Bzzt. */
5407 !*(cp2+3) || *(cp2+3) != 'R' ||
5408 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
5409 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
5410 (ver || *cp3)))))) {
5411 PerlMem_free(trndir);
5413 set_vaxc_errno(RMS$_DIR);
5417 retlen = cp2 - trndir + 1;
5419 else { /* No file type present. Treat the filename as a directory. */
5420 retlen = strlen(trndir) + 1;
5423 if (buf) retpath = buf;
5424 else if (ts) Newx(retpath,retlen+1,char);
5425 else retpath = __pathify_retbuf;
5426 strncpy(retpath, trndir, retlen-1);
5427 if (retpath[retlen-2] != '/') { /* If the path doesn't already end */
5428 retpath[retlen-1] = '/'; /* with '/', add it. */
5429 retpath[retlen] = '\0';
5431 else retpath[retlen-1] = '\0';
5433 else { /* VMS-style directory spec */
5435 unsigned long int sts, cmplen, haslower;
5436 struct FAB dirfab = cc$rms_fab;
5438 rms_setup_nam(savnam);
5439 rms_setup_nam(dirnam);
5441 /* If we've got an explicit filename, we can just shuffle the string. */
5442 if ( ( (cp1 = strrchr(trndir,']')) != NULL ||
5443 (cp1 = strrchr(trndir,'>')) != NULL ) && *(cp1+1)) {
5444 if ((cp2 = strchr(cp1,'.')) != NULL) {
5446 if (vms_process_case_tolerant) {
5447 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
5448 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
5449 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
5450 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
5451 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
5452 (ver || *cp3)))))) {
5453 PerlMem_free(trndir);
5455 set_vaxc_errno(RMS$_DIR);
5460 if (!*(cp2+1) || *(cp2+1) != 'D' || /* Wrong type. */
5461 !*(cp2+2) || *(cp2+2) != 'I' || /* Bzzt. */
5462 !*(cp2+3) || *(cp2+3) != 'R' ||
5463 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
5464 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
5465 (ver || *cp3)))))) {
5466 PerlMem_free(trndir);
5468 set_vaxc_errno(RMS$_DIR);
5473 else { /* No file type, so just draw name into directory part */
5474 for (cp2 = cp1; *cp2; cp2++) ;
5477 *(cp2+1) = '\0'; /* OK; trndir is guaranteed to be long enough */
5479 /* We've now got a VMS 'path'; fall through */
5482 dirlen = strlen(trndir);
5483 if (trndir[dirlen-1] == ']' ||
5484 trndir[dirlen-1] == '>' ||
5485 trndir[dirlen-1] == ':') { /* It's already a VMS 'path' */
5486 if (buf) retpath = buf;
5487 else if (ts) Newx(retpath,strlen(trndir)+1,char);
5488 else retpath = __pathify_retbuf;
5489 strcpy(retpath,trndir);
5490 PerlMem_free(trndir);
5493 rms_set_fna(dirfab, dirnam, trndir, dirlen);
5494 esa = PerlMem_malloc(VMS_MAXRSS);
5495 if (esa == NULL) _ckvmssts(SS$_INSFMEM);
5496 rms_set_dna(dirfab, dirnam, ".DIR;1", 6);
5497 rms_bind_fab_nam(dirfab, dirnam);
5498 rms_set_esa(dirfab, dirnam, esa, VMS_MAXRSS - 1);
5499 #ifdef NAM$M_NO_SHORT_UPCASE
5500 if (decc_efs_case_preserve)
5501 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
5504 for (cp = trndir; *cp; cp++)
5505 if (islower(*cp)) { haslower = 1; break; }
5507 if (!(sts = (sys$parse(&dirfab)& STS$K_SUCCESS))) {
5508 if ((dirfab.fab$l_sts == RMS$_DIR) || (dirfab.fab$l_sts == RMS$_DNF)) {
5509 rms_set_nam_nop(dirnam, NAM$M_SYNCHK);
5510 sts = sys$parse(&dirfab) & STS$K_SUCCESS;
5513 PerlMem_free(trndir);
5516 set_vaxc_errno(dirfab.fab$l_sts);
5522 /* Does the file really exist? */
5523 if (!(sys$search(&dirfab)&STS$K_SUCCESS)) {
5524 if (dirfab.fab$l_sts != RMS$_FNF) {
5526 sts1 = rms_free_search_context(&dirfab);
5527 PerlMem_free(trndir);
5530 set_vaxc_errno(dirfab.fab$l_sts);
5533 dirnam = savnam; /* No; just work with potential name */
5536 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_TYPE)) { /* Was type specified? */
5537 /* Yep; check version while we're at it, if it's there. */
5538 cmplen = rms_is_nam_fnb(dirnam, NAM$M_EXP_VER) ? 6 : 4;
5539 if (strncmp(rms_nam_typel(dirnam),".DIR;1",cmplen)) {
5541 /* Something other than .DIR[;1]. Bzzt. */
5542 sts2 = rms_free_search_context(&dirfab);
5543 PerlMem_free(trndir);
5546 set_vaxc_errno(RMS$_DIR);
5550 /* OK, the type was fine. Now pull any file name into the
5552 if ((cp1 = strrchr(esa,']'))) *(rms_nam_typel(dirnam)) = ']';
5554 cp1 = strrchr(esa,'>');
5555 *(rms_nam_typel(dirnam)) = '>';
5558 *(rms_nam_typel(dirnam) + 1) = '\0';
5559 retlen = (rms_nam_typel(dirnam)) - esa + 2;
5560 if (buf) retpath = buf;
5561 else if (ts) Newx(retpath,retlen,char);
5562 else retpath = __pathify_retbuf;
5563 strcpy(retpath,esa);
5565 sts = rms_free_search_context(&dirfab);
5566 /* $PARSE may have upcased filespec, so convert output to lower
5567 * case if input contained any lowercase characters. */
5568 if (haslower && !decc_efs_case_preserve) __mystrtolower(retpath);
5571 PerlMem_free(trndir);
5573 } /* end of do_pathify_dirspec() */
5575 /* External entry points */
5576 char *Perl_pathify_dirspec(pTHX_ const char *dir, char *buf)
5577 { return do_pathify_dirspec(dir,buf,0,NULL); }
5578 char *Perl_pathify_dirspec_ts(pTHX_ const char *dir, char *buf)
5579 { return do_pathify_dirspec(dir,buf,1,NULL); }
5580 char *Perl_pathify_dirspec_utf8(pTHX_ const char *dir, char *buf, int *utf8_fl)
5581 { return do_pathify_dirspec(dir,buf,0,utf8_fl); }
5582 char *Perl_pathify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int *utf8_fl)
5583 { return do_pathify_dirspec(dir,buf,1,utf8_fl); }
5585 /*{{{ char *tounixspec[_ts](char *spec, char *buf, int *)*/
5586 static char *mp_do_tounixspec(pTHX_ const char *spec, char *buf, int ts, int * utf8_fl)
5588 static char __tounixspec_retbuf[VMS_MAXRSS];
5589 char *dirend, *rslt, *cp1, *cp3, *tmp;
5591 int devlen, dirlen, retlen = VMS_MAXRSS;
5592 int expand = 1; /* guarantee room for leading and trailing slashes */
5593 unsigned short int trnlnm_iter_count;
5595 if (utf8_fl != NULL)
5598 if (spec == NULL) return NULL;
5599 if (strlen(spec) > (VMS_MAXRSS-1)) return NULL;
5600 if (buf) rslt = buf;
5602 Newx(rslt, VMS_MAXRSS, char);
5604 else rslt = __tounixspec_retbuf;
5606 /* New VMS specific format needs translation
5607 * glob passes filenames with trailing '\n' and expects this preserved.
5609 if (decc_posix_compliant_pathnames) {
5610 if (strncmp(spec, "\"^UP^", 5) == 0) {
5616 tunix = PerlMem_malloc(VMS_MAXRSS);
5617 if (tunix == NULL) _ckvmssts(SS$_INSFMEM);
5618 strcpy(tunix, spec);
5619 tunix_len = strlen(tunix);
5621 if (tunix[tunix_len - 1] == '\n') {
5622 tunix[tunix_len - 1] = '\"';
5623 tunix[tunix_len] = '\0';
5627 uspec = decc$translate_vms(tunix);
5628 PerlMem_free(tunix);
5629 if ((int)uspec > 0) {
5635 /* If we can not translate it, makemaker wants as-is */
5643 cmp_rslt = 0; /* Presume VMS */
5644 cp1 = strchr(spec, '/');
5648 /* Look for EFS ^/ */
5649 if (decc_efs_charset) {
5650 while (cp1 != NULL) {
5653 /* Found illegal VMS, assume UNIX */
5658 cp1 = strchr(cp1, '/');
5662 /* Look for "." and ".." */
5663 if (decc_filename_unix_report) {
5664 if (spec[0] == '.') {
5665 if ((spec[1] == '\0') || (spec[1] == '\n')) {
5669 if ((spec[1] == '.') && ((spec[2] == '\0') || (spec[2] == '\n'))) {
5675 /* This is already UNIX or at least nothing VMS understands */
5683 dirend = strrchr(spec,']');
5684 if (dirend == NULL) dirend = strrchr(spec,'>');
5685 if (dirend == NULL) dirend = strchr(spec,':');
5686 if (dirend == NULL) {
5691 /* Special case 1 - sys$posix_root = / */
5692 #if __CRTL_VER >= 70000000
5693 if (!decc_disable_posix_root) {
5694 if (strncasecmp(spec, "SYS$POSIX_ROOT:", 15) == 0) {
5702 /* Special case 2 - Convert NLA0: to /dev/null */
5703 #if __CRTL_VER < 70000000
5704 cmp_rslt = strncmp(spec,"NLA0:", 5);
5706 cmp_rslt = strncmp(spec,"nla0:", 5);
5708 cmp_rslt = strncasecmp(spec,"NLA0:", 5);
5710 if (cmp_rslt == 0) {
5711 strcpy(rslt, "/dev/null");
5714 if (spec[6] != '\0') {
5721 /* Also handle special case "SYS$SCRATCH:" */
5722 #if __CRTL_VER < 70000000
5723 cmp_rslt = strncmp(spec,"SYS$SCRATCH:", 12);
5725 cmp_rslt = strncmp(spec,"sys$scratch:", 12);
5727 cmp_rslt = strncasecmp(spec,"SYS$SCRATCH:", 12);
5729 tmp = PerlMem_malloc(VMS_MAXRSS);
5730 if (tmp == NULL) _ckvmssts(SS$_INSFMEM);
5731 if (cmp_rslt == 0) {
5734 islnm = my_trnlnm(tmp, "TMP", 0);
5736 strcpy(rslt, "/tmp");
5739 if (spec[12] != '\0') {
5747 if (*cp2 != '[' && *cp2 != '<') {
5750 else { /* the VMS spec begins with directories */
5752 if (*cp2 == ']' || *cp2 == '>') {
5753 *(cp1++) = '.'; *(cp1++) = '/'; *(cp1++) = '\0';
5757 else if ( *cp2 != '^' && *cp2 != '.' && *cp2 != '-') { /* add the implied device */
5758 if (getcwd(tmp, VMS_MAXRSS-1 ,1) == NULL) {
5759 if (ts) Safefree(rslt);
5763 trnlnm_iter_count = 0;
5766 while (*cp3 != ':' && *cp3) cp3++;
5768 if (strchr(cp3,']') != NULL) break;
5769 trnlnm_iter_count++;
5770 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER+1) break;
5771 } while (vmstrnenv(tmp,tmp,0,fildev,0));
5773 ((devlen = strlen(tmp)) + (dirlen = strlen(cp2)) + 1 > retlen)) {
5774 retlen = devlen + dirlen;
5775 Renew(rslt,retlen+1+2*expand,char);
5781 *(cp1++) = *(cp3++);
5782 if (cp1 - rslt > (VMS_MAXRSS - 1) && !ts && !buf) {
5784 return NULL; /* No room */
5789 if ((*cp2 == '^')) {
5790 /* EFS file escape, pass the next character as is */
5791 /* Fix me: HEX encoding for UNICODE not implemented */
5794 else if ( *cp2 == '.') {
5795 if (*(cp2+1) == '.' && *(cp2+2) == '.') {
5796 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
5803 for (; cp2 <= dirend; cp2++) {
5804 if ((*cp2 == '^')) {
5805 /* EFS file escape, pass the next character as is */
5806 /* Fix me: HEX encoding for UNICODE not implemented */
5812 if (*(cp2+1) == '[') cp2++;
5814 else if (*cp2 == ']' || *cp2 == '>') {
5815 if (*(cp1-1) != '/') *(cp1++) = '/'; /* Don't double after ellipsis */
5817 else if ((*cp2 == '.') && (*cp2-1 != '^')) {
5819 if (*(cp2+1) == ']' || *(cp2+1) == '>') {
5820 while (*(cp2+1) == ']' || *(cp2+1) == '>' ||
5821 *(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
5822 if (!strncmp(cp2,"[000000",7) && (*(cp2+7) == ']' ||
5823 *(cp2+7) == '>' || *(cp2+7) == '.')) cp2 += 7;
5825 else if ( *(cp2+1) == '.' && *(cp2+2) == '.') {
5826 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) ='/';
5830 else if (*cp2 == '-') {
5831 if (*(cp2-1) == '[' || *(cp2-1) == '<' || *(cp2-1) == '.') {
5832 while (*cp2 == '-') {
5834 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
5836 if (*cp2 != '.' && *cp2 != ']' && *cp2 != '>') { /* we don't allow */
5837 if (ts) Safefree(rslt); /* filespecs like */
5838 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); /* [fred.--foo.bar] */
5842 else *(cp1++) = *cp2;
5844 else *(cp1++) = *cp2;
5846 while (*cp2) *(cp1++) = *(cp2++);
5849 /* This still leaves /000000/ when working with a
5850 * VMS device root or concealed root.
5856 ulen = strlen(rslt);
5858 /* Get rid of "000000/ in rooted filespecs */
5860 zeros = strstr(rslt, "/000000/");
5861 if (zeros != NULL) {
5863 mlen = ulen - (zeros - rslt) - 7;
5864 memmove(zeros, &zeros[7], mlen);
5873 } /* end of do_tounixspec() */
5875 /* External entry points */
5876 char *Perl_tounixspec(pTHX_ const char *spec, char *buf)
5877 { return do_tounixspec(spec,buf,0, NULL); }
5878 char *Perl_tounixspec_ts(pTHX_ const char *spec, char *buf)
5879 { return do_tounixspec(spec,buf,1, NULL); }
5880 char *Perl_tounixspec_utf8(pTHX_ const char *spec, char *buf, int * utf8_fl)
5881 { return do_tounixspec(spec,buf,0, utf8_fl); }
5882 char *Perl_tounixspec_utf8_ts(pTHX_ const char *spec, char *buf, int * utf8_fl)
5883 { return do_tounixspec(spec,buf,1, utf8_fl); }
5885 #if __CRTL_VER >= 70200000 && !defined(__VAX)
5888 This procedure is used to identify if a path is based in either
5889 the old SYS$POSIX_ROOT: or the new 8.3 RMS based POSIX root, and
5890 it returns the OpenVMS format directory for it.
5892 It is expecting specifications of only '/' or '/xxxx/'
5894 If a posix root does not exist, or 'xxxx' is not a directory
5895 in the posix root, it returns a failure.
5897 FIX-ME: xxxx could be in UTF-8 and needs to be returned in VTF-7.
5899 It is used only internally by posix_to_vmsspec_hardway().
5902 static int posix_root_to_vms
5903 (char *vmspath, int vmspath_len,
5904 const char *unixpath,
5905 const int * utf8_fl) {
5907 struct FAB myfab = cc$rms_fab;
5908 struct NAML mynam = cc$rms_naml;
5909 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
5910 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
5917 unixlen = strlen(unixpath);
5923 #if __CRTL_VER >= 80200000
5924 /* If not a posix spec already, convert it */
5925 if (decc_posix_compliant_pathnames) {
5926 if (strncmp(unixpath,"\"^UP^",5) != 0) {
5927 sprintf(vmspath,"\"^UP^%s\"",unixpath);
5930 /* This is already a VMS specification, no conversion */
5932 strncpy(vmspath,unixpath, vmspath_len);
5941 /* Check to see if this is under the POSIX root */
5942 if (decc_disable_posix_root) {
5946 /* Skip leading / */
5947 if (unixpath[0] == '/') {
5953 strcpy(vmspath,"SYS$POSIX_ROOT:");
5955 /* If this is only the / , or blank, then... */
5956 if (unixpath[0] == '\0') {
5957 /* by definition, this is the answer */
5961 /* Need to look up a directory */
5965 /* Copy and add '^' escape characters as needed */
5968 while (unixpath[i] != 0) {
5971 j += copy_expand_unix_filename_escape
5972 (&vmspath[j], &unixpath[i], &k, utf8_fl);
5976 path_len = strlen(vmspath);
5977 if (vmspath[path_len - 1] == '/')
5979 vmspath[path_len] = ']';
5981 vmspath[path_len] = '\0';
5984 vmspath[vmspath_len] = 0;
5985 if (unixpath[unixlen - 1] == '/')
5987 esa = PerlMem_malloc(VMS_MAXRSS);
5988 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5989 myfab.fab$l_fna = vmspath;
5990 myfab.fab$b_fns = strlen(vmspath);
5991 myfab.fab$l_naml = &mynam;
5992 mynam.naml$l_esa = NULL;
5993 mynam.naml$b_ess = 0;
5994 mynam.naml$l_long_expand = esa;
5995 mynam.naml$l_long_expand_alloc = (unsigned char) VMS_MAXRSS - 1;
5996 mynam.naml$l_rsa = NULL;
5997 mynam.naml$b_rss = 0;
5998 if (decc_efs_case_preserve)
5999 mynam.naml$b_nop |= NAM$M_NO_SHORT_UPCASE;
6000 #ifdef NAML$M_OPEN_SPECIAL
6001 mynam.naml$l_input_flags |= NAML$M_OPEN_SPECIAL;
6004 /* Set up the remaining naml fields */
6005 sts = sys$parse(&myfab);
6007 /* It failed! Try again as a UNIX filespec */
6013 /* get the Device ID and the FID */
6014 sts = sys$search(&myfab);
6015 /* on any failure, returned the POSIX ^UP^ filespec */
6020 specdsc.dsc$a_pointer = vmspath;
6021 specdsc.dsc$w_length = vmspath_len;
6023 dvidsc.dsc$a_pointer = &mynam.naml$t_dvi[1];
6024 dvidsc.dsc$w_length = mynam.naml$t_dvi[0];
6025 sts = lib$fid_to_name
6026 (&dvidsc, mynam.naml$w_fid, &specdsc, &specdsc.dsc$w_length);
6028 /* on any failure, returned the POSIX ^UP^ filespec */
6030 /* This can happen if user does not have permission to read directories */
6031 if (strncmp(unixpath,"\"^UP^",5) != 0)
6032 sprintf(vmspath,"\"^UP^%s\"",unixpath);
6034 strcpy(vmspath, unixpath);
6037 vmspath[specdsc.dsc$w_length] = 0;
6039 /* Are we expecting a directory? */
6040 if (dir_flag != 0) {
6046 i = specdsc.dsc$w_length - 1;
6050 /* Version must be '1' */
6051 if (vmspath[i--] != '1')
6053 /* Version delimiter is one of ".;" */
6054 if ((vmspath[i] != '.') && (vmspath[i] != ';'))
6057 if (vmspath[i--] != 'R')
6059 if (vmspath[i--] != 'I')
6061 if (vmspath[i--] != 'D')
6063 if (vmspath[i--] != '.')
6065 eptr = &vmspath[i+1];
6067 if ((vmspath[i] == ']') || (vmspath[i] == '>')) {
6068 if (vmspath[i-1] != '^') {
6076 /* Get rid of 6 imaginary zero directory filename */
6077 vmspath[i+1] = '\0';
6081 if (vmspath[i] == '0')
6095 /* /dev/mumble needs to be handled special.
6096 /dev/null becomes NLA0:, And there is the potential for other stuff
6097 like /dev/tty which may need to be mapped to something.
6101 slash_dev_special_to_vms
6102 (const char * unixptr,
6112 nextslash = strchr(unixptr, '/');
6113 len = strlen(unixptr);
6114 if (nextslash != NULL)
6115 len = nextslash - unixptr;
6116 cmp = strncmp("null", unixptr, 5);
6118 if (vmspath_len >= 6) {
6119 strcpy(vmspath, "_NLA0:");
6126 /* The built in routines do not understand perl's special needs, so
6127 doing a manual conversion from UNIX to VMS
6129 If the utf8_fl is not null and points to a non-zero value, then
6130 treat 8 bit characters as UTF-8.
6132 The sequence starting with '$(' and ending with ')' will be passed
6133 through with out interpretation instead of being escaped.
6136 static int posix_to_vmsspec_hardway
6137 (char *vmspath, int vmspath_len,
6138 const char *unixpath,
6143 const char *unixptr;
6144 const char *unixend;
6146 const char *lastslash;
6147 const char *lastdot;
6153 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
6154 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
6156 if (utf8_fl != NULL)
6162 /* Ignore leading "/" characters */
6163 while((unixptr[0] == '/') && (unixptr[1] == '/')) {
6166 unixlen = strlen(unixptr);
6168 /* Do nothing with blank paths */
6175 /* This could have a "^UP^ on the front */
6176 if (strncmp(unixptr,"\"^UP^",5) == 0) {
6182 lastslash = strrchr(unixptr,'/');
6183 lastdot = strrchr(unixptr,'.');
6184 unixend = strrchr(unixptr,'\"');
6185 if (!quoted || !((unixend != NULL) && (unixend[1] == '\0'))) {
6186 unixend = unixptr + unixlen;
6189 /* last dot is last dot or past end of string */
6190 if (lastdot == NULL)
6191 lastdot = unixptr + unixlen;
6193 /* if no directories, set last slash to beginning of string */
6194 if (lastslash == NULL) {
6195 lastslash = unixptr;
6198 /* Watch out for trailing "." after last slash, still a directory */
6199 if ((lastslash[1] == '.') && (lastslash[2] == '\0')) {
6200 lastslash = unixptr + unixlen;
6203 /* Watch out for traiing ".." after last slash, still a directory */
6204 if ((lastslash[1] == '.')&&(lastslash[2] == '.')&&(lastslash[3] == '\0')) {
6205 lastslash = unixptr + unixlen;
6208 /* dots in directories are aways escaped */
6209 if (lastdot < lastslash)
6210 lastdot = unixptr + unixlen;
6213 /* if (unixptr < lastslash) then we are in a directory */
6220 /* Start with the UNIX path */
6221 if (*unixptr != '/') {
6222 /* relative paths */
6224 /* If allowing logical names on relative pathnames, then handle here */
6225 if ((unixptr[0] != '.') && !decc_disable_to_vms_logname_translation &&
6226 !decc_posix_compliant_pathnames) {
6232 /* Find the next slash */
6233 nextslash = strchr(unixptr,'/');
6235 esa = PerlMem_malloc(vmspath_len);
6236 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6238 trn = PerlMem_malloc(VMS_MAXRSS);
6239 if (trn == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6241 if (nextslash != NULL) {
6243 seg_len = nextslash - unixptr;
6244 strncpy(esa, unixptr, seg_len);
6248 strcpy(esa, unixptr);
6249 seg_len = strlen(unixptr);
6251 /* trnlnm(section) */
6252 islnm = vmstrnenv(esa, trn, 0, fildev, 0);
6255 /* Now fix up the directory */
6257 /* Split up the path to find the components */
6258 sts = vms_split_path
6277 /* A logical name must be a directory or the full
6278 specification. It is only a full specification if
6279 it is the only component */
6280 if ((unixptr[seg_len] == '\0') ||
6281 (unixptr[seg_len+1] == '\0')) {
6283 /* Is a directory being required? */
6284 if (((n_len + e_len) != 0) && (dir_flag !=0)) {
6285 /* Not a logical name */
6290 if ((unixptr[seg_len] == '/') || (dir_flag != 0)) {
6291 /* This must be a directory */
6292 if (((n_len + e_len) == 0)&&(seg_len <= vmspath_len)) {
6293 strcpy(vmsptr, esa);
6294 vmslen=strlen(vmsptr);
6295 vmsptr[vmslen] = ':';
6297 vmsptr[vmslen] = '\0';
6305 /* must be dev/directory - ignore version */
6306 if ((n_len + e_len) != 0)
6309 /* transfer the volume */
6310 if (v_len > 0 && ((v_len + vmslen) < vmspath_len)) {
6311 strncpy(vmsptr, v_spec, v_len);
6317 /* unroot the rooted directory */
6318 if ((r_len > 0) && ((r_len + d_len + vmslen) < vmspath_len)) {
6320 r_spec[r_len - 1] = ']';
6322 /* This should not be there, but nothing is perfect */
6324 cmp = strcmp(&r_spec[1], "000000.");
6334 strncpy(vmsptr, r_spec, r_len);
6340 /* Bring over the directory. */
6342 ((d_len + vmslen) < vmspath_len)) {
6344 d_spec[d_len - 1] = ']';
6346 cmp = strcmp(&d_spec[1], "000000.");
6357 /* Remove the redundant root */
6365 strncpy(vmsptr, d_spec, d_len);
6379 if (lastslash > unixptr) {
6382 /* skip leading ./ */
6384 while ((unixptr[0] == '.') && (unixptr[1] == '/')) {
6390 /* Are we still in a directory? */
6391 if (unixptr <= lastslash) {
6396 /* if not backing up, then it is relative forward. */
6397 if (!((*unixptr == '.') && (unixptr[1] == '.') &&
6398 ((unixptr[2] == '/') || (&unixptr[2] == unixend)))) {
6406 /* Perl wants an empty directory here to tell the difference
6407 * between a DCL commmand and a filename
6416 /* Handle two special files . and .. */
6417 if (unixptr[0] == '.') {
6418 if (&unixptr[1] == unixend) {
6425 if ((unixptr[1] == '.') && (&unixptr[2] == unixend)) {
6436 else { /* Absolute PATH handling */
6440 /* Need to find out where root is */
6442 /* In theory, this procedure should never get an absolute POSIX pathname
6443 * that can not be found on the POSIX root.
6444 * In practice, that can not be relied on, and things will show up
6445 * here that are a VMS device name or concealed logical name instead.
6446 * So to make things work, this procedure must be tolerant.
6448 esa = PerlMem_malloc(vmspath_len);
6449 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6452 nextslash = strchr(&unixptr[1],'/');
6454 if (nextslash != NULL) {
6456 seg_len = nextslash - &unixptr[1];
6457 strncpy(vmspath, unixptr, seg_len + 1);
6458 vmspath[seg_len+1] = 0;
6461 cmp = strncmp(vmspath, "dev", 4);
6463 sts = slash_dev_special_to_vms(unixptr, vmspath, vmspath_len);
6464 if (sts = SS$_NORMAL)
6468 sts = posix_root_to_vms(esa, vmspath_len, vmspath, utf8_fl);
6471 if ($VMS_STATUS_SUCCESS(sts)) {
6472 /* This is verified to be a real path */
6474 sts = posix_root_to_vms(esa, vmspath_len, "/", NULL);
6475 if ($VMS_STATUS_SUCCESS(sts)) {
6476 strcpy(vmspath, esa);
6477 vmslen = strlen(vmspath);
6478 vmsptr = vmspath + vmslen;
6480 if (unixptr < lastslash) {
6489 cmp = strcmp(rptr,"000000.");
6494 } /* removing 6 zeros */
6495 } /* vmslen < 7, no 6 zeros possible */
6496 } /* Not in a directory */
6497 } /* Posix root found */
6499 /* No posix root, fall back to default directory */
6500 strcpy(vmspath, "SYS$DISK:[");
6501 vmsptr = &vmspath[10];
6503 if (unixptr > lastslash) {
6512 } /* end of verified real path handling */
6517 /* Ok, we have a device or a concealed root that is not in POSIX
6518 * or we have garbage. Make the best of it.
6521 /* Posix to VMS destroyed this, so copy it again */
6522 strncpy(vmspath, &unixptr[1], seg_len);
6523 vmspath[seg_len] = 0;
6525 vmsptr = &vmsptr[vmslen];
6528 /* Now do we need to add the fake 6 zero directory to it? */
6530 if ((*lastslash == '/') && (nextslash < lastslash)) {
6531 /* No there is another directory */
6538 /* now we have foo:bar or foo:[000000]bar to decide from */
6539 islnm = vmstrnenv(vmspath, esa, 0, fildev, 0);
6541 if (!islnm && !decc_posix_compliant_pathnames) {
6543 cmp = strncmp("bin", vmspath, 4);
6545 /* bin => SYS$SYSTEM: */
6546 islnm = vmstrnenv("SYS$SYSTEM:", esa, 0, fildev, 0);
6549 /* tmp => SYS$SCRATCH: */
6550 cmp = strncmp("tmp", vmspath, 4);
6552 islnm = vmstrnenv("SYS$SCRATCH:", esa, 0, fildev, 0);
6557 trnend = islnm ? islnm - 1 : 0;
6559 /* if this was a logical name, ']' or '>' must be present */
6560 /* if not a logical name, then assume a device and hope. */
6561 islnm = trnend ? (esa[trnend] == ']' || esa[trnend] == '>') : 0;
6563 /* if log name and trailing '.' then rooted - treat as device */
6564 add_6zero = islnm ? (esa[trnend-1] == '.') : 0;
6566 /* Fix me, if not a logical name, a device lookup should be
6567 * done to see if the device is file structured. If the device
6568 * is not file structured, the 6 zeros should not be put on.
6570 * As it is, perl is occasionally looking for dev:[000000]tty.
6571 * which looks a little strange.
6573 * Not that easy to detect as "/dev" may be file structured with
6574 * special device files.
6577 if ((add_6zero == 0) && (*nextslash == '/') &&
6578 (&nextslash[1] == unixend)) {
6579 /* No real directory present */
6584 /* Put the device delimiter on */
6587 unixptr = nextslash;
6590 /* Start directory if needed */
6591 if (!islnm || add_6zero) {
6597 /* add fake 000000] if needed */
6610 } /* non-POSIX translation */
6612 } /* End of relative/absolute path handling */
6614 while ((unixptr <= unixend) && (vmslen < vmspath_len)){
6621 if (dir_start != 0) {
6623 /* First characters in a directory are handled special */
6624 while ((*unixptr == '/') ||
6625 ((*unixptr == '.') &&
6626 ((unixptr[1]=='.') || (unixptr[1]=='/') ||
6627 (&unixptr[1]==unixend)))) {
6632 /* Skip redundant / in specification */
6633 while ((*unixptr == '/') && (dir_start != 0)) {
6636 if (unixptr == lastslash)
6639 if (unixptr == lastslash)
6642 /* Skip redundant ./ characters */
6643 while ((*unixptr == '.') &&
6644 ((unixptr[1] == '/')||(&unixptr[1] == unixend))) {
6647 if (unixptr == lastslash)
6649 if (*unixptr == '/')
6652 if (unixptr == lastslash)
6655 /* Skip redundant ../ characters */
6656 while ((*unixptr == '.') && (unixptr[1] == '.') &&
6657 ((unixptr[2] == '/') || (&unixptr[2] == unixend))) {
6658 /* Set the backing up flag */
6664 unixptr++; /* first . */
6665 unixptr++; /* second . */
6666 if (unixptr == lastslash)
6668 if (*unixptr == '/') /* The slash */
6671 if (unixptr == lastslash)
6674 /* To do: Perl expects /.../ to be translated to [...] on VMS */
6675 /* Not needed when VMS is pretending to be UNIX. */
6677 /* Is this loop stuck because of too many dots? */
6678 if (loop_flag == 0) {
6679 /* Exit the loop and pass the rest through */
6684 /* Are we done with directories yet? */
6685 if (unixptr >= lastslash) {
6687 /* Watch out for trailing dots */
6696 if (*unixptr == '/')
6700 /* Have we stopped backing up? */
6705 /* dir_start continues to be = 1 */
6707 if (*unixptr == '-') {
6709 *vmsptr++ = *unixptr++;
6713 /* Now are we done with directories yet? */
6714 if (unixptr >= lastslash) {
6716 /* Watch out for trailing dots */
6732 if (unixptr >= unixend)
6735 /* Normal characters - More EFS work probably needed */
6741 /* remove multiple / */
6742 while (unixptr[1] == '/') {
6745 if (unixptr == lastslash) {
6746 /* Watch out for trailing dots */
6758 /* To do: Perl expects /.../ to be translated to [...] on VMS */
6759 /* Not needed when VMS is pretending to be UNIX. */
6763 if (unixptr != unixend)
6768 if ((unixptr < lastdot) || (unixptr < lastslash) ||
6769 (&unixptr[1] == unixend)) {
6775 /* trailing dot ==> '^..' on VMS */
6776 if (unixptr == unixend) {
6784 *vmsptr++ = *unixptr++;
6788 if (quoted && (&unixptr[1] == unixend)) {
6792 in_cnt = copy_expand_unix_filename_escape
6793 (vmsptr, unixptr, &out_cnt, utf8_fl);
6803 in_cnt = copy_expand_unix_filename_escape
6804 (vmsptr, unixptr, &out_cnt, utf8_fl);
6811 /* Make sure directory is closed */
6812 if (unixptr == lastslash) {
6814 vmsptr2 = vmsptr - 1;
6816 if (*vmsptr2 != ']') {
6819 /* directories do not end in a dot bracket */
6820 if (*vmsptr2 == '.') {
6824 if (*vmsptr2 != '^') {
6825 vmsptr--; /* back up over the dot */
6833 /* Add a trailing dot if a file with no extension */
6834 vmsptr2 = vmsptr - 1;
6836 (*vmsptr2 != ']') && (*vmsptr2 != '*') && (*vmsptr2 != '%') &&
6837 (*vmsptr2 != ')') && (*lastdot != '.')) {
6848 /* Eventual routine to convert a UTF-8 specification to VTF-7. */
6849 static char * utf8_to_vtf7(char * rslt, const char * path, int *utf8_fl)
6854 /* If a UTF8 flag is being passed, honor it */
6856 if (utf8_fl != NULL) {
6857 utf8_flag = *utf8_fl;
6862 /* If there is a possibility of UTF8, then if any UTF8 characters
6863 are present, then they must be converted to VTF-7
6865 result = strcpy(rslt, path); /* FIX-ME */
6868 result = strcpy(rslt, path);
6874 /*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/
6875 static char *mp_do_tovmsspec
6876 (pTHX_ const char *path, char *buf, int ts, int dir_flag, int * utf8_flag) {
6877 static char __tovmsspec_retbuf[VMS_MAXRSS];
6878 char *rslt, *dirend;
6883 unsigned long int infront = 0, hasdir = 1;
6886 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
6887 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
6889 if (path == NULL) return NULL;
6890 rslt_len = VMS_MAXRSS-1;
6891 if (buf) rslt = buf;
6892 else if (ts) Newx(rslt, VMS_MAXRSS, char);
6893 else rslt = __tovmsspec_retbuf;
6895 /* '.' and '..' are "[]" and "[-]" for a quick check */
6896 if (path[0] == '.') {
6897 if (path[1] == '\0') {
6899 if (utf8_flag != NULL)
6904 if (path[1] == '.' && path[2] == '\0') {
6906 if (utf8_flag != NULL)
6913 /* Posix specifications are now a native VMS format */
6914 /*--------------------------------------------------*/
6915 #if __CRTL_VER >= 80200000 && !defined(__VAX)
6916 if (decc_posix_compliant_pathnames) {
6917 if (strncmp(path,"\"^UP^",5) == 0) {
6918 posix_to_vmsspec_hardway(rslt, rslt_len, path, dir_flag, utf8_flag);
6924 /* This is really the only way to see if this is already in VMS format */
6925 sts = vms_split_path
6940 /* FIX-ME - If dir_flag is non-zero, then this is a mp_do_vmspath()
6941 replacement, because the above parse just took care of most of
6942 what is needed to do vmspath when the specification is already
6945 And if it is not already, it is easier to do the conversion as
6946 part of this routine than to call this routine and then work on
6950 /* If VMS punctuation was found, it is already VMS format */
6951 if ((v_len != 0) || (r_len != 0) || (d_len != 0) || (vs_len != 0)) {
6952 if (utf8_flag != NULL)
6957 /* Now, what to do with trailing "." cases where there is no
6958 extension? If this is a UNIX specification, and EFS characters
6959 are enabled, then the trailing "." should be converted to a "^.".
6960 But if this was already a VMS specification, then it should be
6963 So in the case of ambiguity, leave the specification alone.
6967 /* If there is a possibility of UTF8, then if any UTF8 characters
6968 are present, then they must be converted to VTF-7
6970 if (utf8_flag != NULL)
6976 dirend = strrchr(path,'/');
6978 if (dirend == NULL) {
6979 /* If we get here with no UNIX directory delimiters, then this is
6980 not a complete file specification, either garbage a UNIX glob
6981 specification that can not be converted to a VMS wildcard, or
6982 it a UNIX shell macro. MakeMaker wants these passed through AS-IS,
6983 so apparently other programs expect this also.
6985 utf8 flag setting needs to be preserved.
6991 /* If POSIX mode active, handle the conversion */
6992 #if __CRTL_VER >= 80200000 && !defined(__VAX)
6993 if (decc_efs_charset) {
6994 posix_to_vmsspec_hardway(rslt, rslt_len, path, dir_flag, utf8_flag);
6999 if (*(dirend+1) == '.') { /* do we have trailing "/." or "/.." or "/..."? */
7000 if (!*(dirend+2)) dirend +=2;
7001 if (*(dirend+2) == '.' && !*(dirend+3)) dirend += 3;
7002 if (decc_efs_charset == 0) {
7003 if (*(dirend+2) == '.' && *(dirend+3) == '.' && !*(dirend+4)) dirend += 4;
7009 lastdot = strrchr(cp2,'.');
7015 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
7017 if (decc_disable_posix_root) {
7018 strcpy(rslt,"sys$disk:[000000]");
7021 strcpy(rslt,"sys$posix_root:[000000]");
7023 if (utf8_flag != NULL)
7027 while (*(++cp2) != '/' && *cp2) *(cp1++) = *cp2;
7029 trndev = PerlMem_malloc(VMS_MAXRSS);
7030 if (trndev == NULL) _ckvmssts(SS$_INSFMEM);
7031 islnm = my_trnlnm(rslt,trndev,0);
7033 /* DECC special handling */
7035 if (strcmp(rslt,"bin") == 0) {
7036 strcpy(rslt,"sys$system");
7039 islnm = my_trnlnm(rslt,trndev,0);
7041 else if (strcmp(rslt,"tmp") == 0) {
7042 strcpy(rslt,"sys$scratch");
7045 islnm = my_trnlnm(rslt,trndev,0);
7047 else if (!decc_disable_posix_root) {
7048 strcpy(rslt, "sys$posix_root");
7052 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
7053 islnm = my_trnlnm(rslt,trndev,0);
7055 else if (strcmp(rslt,"dev") == 0) {
7056 if (strncmp(cp2,"/null", 5) == 0) {
7057 if ((cp2[5] == 0) || (cp2[5] == '/')) {
7058 strcpy(rslt,"NLA0");
7062 islnm = my_trnlnm(rslt,trndev,0);
7068 trnend = islnm ? strlen(trndev) - 1 : 0;
7069 islnm = trnend ? (trndev[trnend] == ']' || trndev[trnend] == '>') : 0;
7070 rooted = islnm ? (trndev[trnend-1] == '.') : 0;
7071 /* If the first element of the path is a logical name, determine
7072 * whether it has to be translated so we can add more directories. */
7073 if (!islnm || rooted) {
7076 if (cp2 == dirend) while (infront++ < 6) *(cp1++) = '0';
7080 if (cp2 != dirend) {
7081 strcpy(rslt,trndev);
7082 cp1 = rslt + trnend;
7089 if (decc_disable_posix_root) {
7095 PerlMem_free(trndev);
7100 if (*(cp2+1) == '/' || *(cp2+1) == '\0') {
7101 cp2 += 2; /* skip over "./" - it's redundant */
7102 *(cp1++) = '.'; /* but it does indicate a relative dirspec */
7104 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
7105 *(cp1++) = '-'; /* "../" --> "-" */
7108 else if (*(cp2+1) == '.' && *(cp2+2) == '.' &&
7109 (*(cp2+3) == '/' || *(cp2+3) == '\0')) {
7110 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
7111 if (!*(cp2+4)) *(cp1++) = '.'; /* Simulate trailing '/' for later */
7114 else if ((cp2 != lastdot) || (lastdot < dirend)) {
7115 /* Escape the extra dots in EFS file specifications */
7118 if (cp2 > dirend) cp2 = dirend;
7120 else *(cp1++) = '.';
7122 for (; cp2 < dirend; cp2++) {
7124 if (*(cp2-1) == '/') continue;
7125 if (*(cp1-1) != '.') *(cp1++) = '.';
7128 else if (!infront && *cp2 == '.') {
7129 if (cp2+1 == dirend || *(cp2+1) == '\0') { cp2++; break; }
7130 else if (*(cp2+1) == '/') cp2++; /* skip over "./" - it's redundant */
7131 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
7132 if (*(cp1-1) == '-' || *(cp1-1) == '[') *(cp1++) = '-'; /* handle "../" */
7133 else if (*(cp1-2) == '[') *(cp1-1) = '-';
7134 else { /* back up over previous directory name */
7136 while (*(cp1-1) != '.' && *(cp1-1) != '[') cp1--;
7137 if (*(cp1-1) == '[') {
7138 memcpy(cp1,"000000.",7);
7143 if (cp2 == dirend) break;
7145 else if ( *(cp2+1) == '.' && *(cp2+2) == '.' &&
7146 (*(cp2+3) == '/' || *(cp2+3) == '\0') ) {
7147 if (*(cp1-1) != '.') *(cp1++) = '.'; /* May already have 1 from '/' */
7148 *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
7150 *(cp1++) = '.'; /* Simulate trailing '/' */
7151 cp2 += 2; /* for loop will incr this to == dirend */
7153 else cp2 += 3; /* Trailing '/' was there, so skip it, too */
7156 if (decc_efs_charset == 0)
7157 *(cp1++) = '_'; /* fix up syntax - '.' in name not allowed */
7159 *(cp1++) = '^'; /* fix up syntax - '.' in name is allowed */
7165 if (!infront && *(cp1-1) == '-') *(cp1++) = '.';
7167 if (decc_efs_charset == 0)
7174 else *(cp1++) = *cp2;
7178 if (*(cp1-1) == '.') cp1--; /* Unix spec ending in '/' ==> trailing '.' */
7179 if (hasdir) *(cp1++) = ']';
7180 if (*cp2) cp2++; /* check in case we ended with trailing '..' */
7181 /* fixme for ODS5 */
7188 if (decc_efs_charset == 0)
7199 if (((cp2 < lastdot) || (cp2[1] == '\0')) &&
7200 decc_readdir_dropdotnotype) {
7205 /* trailing dot ==> '^..' on VMS */
7212 *(cp1++) = *(cp2++);
7217 /* This could be a macro to be passed through */
7218 *(cp1++) = *(cp2++);
7220 const char * save_cp2;
7224 /* paranoid check */
7230 *(cp1++) = *(cp2++);
7231 if (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
7232 *(cp1++) = *(cp2++);
7233 while (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
7234 *(cp1++) = *(cp2++);
7237 *(cp1++) = *(cp2++);
7241 if (is_macro == 0) {
7242 /* Not really a macro - never mind */
7272 *(cp1++) = *(cp2++);
7275 /* FIXME: This needs fixing as Perl is putting ".dir;" on UNIX filespecs
7276 * which is wrong. UNIX notation should be ".dir." unless
7277 * the DECC$FILENAME_UNIX_NO_VERSION is enabled.
7278 * changing this behavior could break more things at this time.
7279 * efs character set effectively does not allow "." to be a version
7280 * delimiter as a further complication about changing this.
7282 if (decc_filename_unix_report != 0) {
7285 *(cp1++) = *(cp2++);
7288 *(cp1++) = *(cp2++);
7291 if ((no_type_seen == 1) && decc_readdir_dropdotnotype) {
7295 /* Fix me for "^]", but that requires making sure that you do
7296 * not back up past the start of the filename
7298 if ((*lcp1 != ']') && (*lcp1 != '*') && (*lcp1 != '%'))
7303 if (utf8_flag != NULL)
7307 } /* end of do_tovmsspec() */
7309 /* External entry points */
7310 char *Perl_tovmsspec(pTHX_ const char *path, char *buf)
7311 { return do_tovmsspec(path,buf,0,NULL); }
7312 char *Perl_tovmsspec_ts(pTHX_ const char *path, char *buf)
7313 { return do_tovmsspec(path,buf,1,NULL); }
7314 char *Perl_tovmsspec_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
7315 { return do_tovmsspec(path,buf,0,utf8_fl); }
7316 char *Perl_tovmsspec_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
7317 { return do_tovmsspec(path,buf,1,utf8_fl); }
7319 /*{{{ char *tovmspath[_ts](char *path, char *buf, const int *)*/
7320 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) {
7321 static char __tovmspath_retbuf[VMS_MAXRSS];
7323 char *pathified, *vmsified, *cp;
7325 if (path == NULL) return NULL;
7326 pathified = PerlMem_malloc(VMS_MAXRSS);
7327 if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
7328 if (do_pathify_dirspec(path,pathified,0,NULL) == NULL) {
7329 PerlMem_free(pathified);
7335 Newx(vmsified, VMS_MAXRSS, char);
7336 if (do_tovmsspec(pathified, buf ? buf : vmsified, 0, NULL) == NULL) {
7337 PerlMem_free(pathified);
7338 if (vmsified) Safefree(vmsified);
7341 PerlMem_free(pathified);
7346 vmslen = strlen(vmsified);
7347 Newx(cp,vmslen+1,char);
7348 memcpy(cp,vmsified,vmslen);
7354 strcpy(__tovmspath_retbuf,vmsified);
7356 return __tovmspath_retbuf;
7359 } /* end of do_tovmspath() */
7361 /* External entry points */
7362 char *Perl_tovmspath(pTHX_ const char *path, char *buf)
7363 { return do_tovmspath(path,buf,0, NULL); }
7364 char *Perl_tovmspath_ts(pTHX_ const char *path, char *buf)
7365 { return do_tovmspath(path,buf,1, NULL); }
7366 char *Perl_tovmspath_utf8(pTHX_ const char *path, char *buf, int *utf8_fl)
7367 { return do_tovmspath(path,buf,0,utf8_fl); }
7368 char *Perl_tovmspath_utf8_ts(pTHX_ const char *path, char *buf, int *utf8_fl)
7369 { return do_tovmspath(path,buf,1,utf8_fl); }
7372 /*{{{ char *tounixpath[_ts](char *path, char *buf, int * utf8_fl)*/
7373 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) {
7374 static char __tounixpath_retbuf[VMS_MAXRSS];
7376 char *pathified, *unixified, *cp;
7378 if (path == NULL) return NULL;
7379 pathified = PerlMem_malloc(VMS_MAXRSS);
7380 if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
7381 if (do_pathify_dirspec(path,pathified,0,NULL) == NULL) {
7382 PerlMem_free(pathified);
7388 Newx(unixified, VMS_MAXRSS, char);
7390 if (do_tounixspec(pathified,buf ? buf : unixified,0,NULL) == NULL) {
7391 PerlMem_free(pathified);
7392 if (unixified) Safefree(unixified);
7395 PerlMem_free(pathified);
7400 unixlen = strlen(unixified);
7401 Newx(cp,unixlen+1,char);
7402 memcpy(cp,unixified,unixlen);
7404 Safefree(unixified);
7408 strcpy(__tounixpath_retbuf,unixified);
7409 Safefree(unixified);
7410 return __tounixpath_retbuf;
7413 } /* end of do_tounixpath() */
7415 /* External entry points */
7416 char *Perl_tounixpath(pTHX_ const char *path, char *buf)
7417 { return do_tounixpath(path,buf,0,NULL); }
7418 char *Perl_tounixpath_ts(pTHX_ const char *path, char *buf)
7419 { return do_tounixpath(path,buf,1,NULL); }
7420 char *Perl_tounixpath_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
7421 { return do_tounixpath(path,buf,0,utf8_fl); }
7422 char *Perl_tounixpath_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
7423 { return do_tounixpath(path,buf,1,utf8_fl); }
7426 * @(#)argproc.c 2.2 94/08/16 Mark Pizzolato (mark@infocomm.com)
7428 *****************************************************************************
7430 * Copyright (C) 1989-1994 by *
7431 * Mark Pizzolato - INFO COMM, Danville, California (510) 837-5600 *
7433 * Permission is hereby granted for the reproduction of this software, *
7434 * on condition that this copyright notice is included in the reproduction, *
7435 * and that such reproduction is not for purposes of profit or material *
7438 * 27-Aug-1994 Modified for inclusion in perl5 *
7439 * by Charles Bailey bailey@newman.upenn.edu *
7440 *****************************************************************************
7444 * getredirection() is intended to aid in porting C programs
7445 * to VMS (Vax-11 C). The native VMS environment does not support
7446 * '>' and '<' I/O redirection, or command line wild card expansion,
7447 * or a command line pipe mechanism using the '|' AND background
7448 * command execution '&'. All of these capabilities are provided to any
7449 * C program which calls this procedure as the first thing in the
7451 * The piping mechanism will probably work with almost any 'filter' type
7452 * of program. With suitable modification, it may useful for other
7453 * portability problems as well.
7455 * Author: Mark Pizzolato mark@infocomm.com
7459 struct list_item *next;
7463 static void add_item(struct list_item **head,
7464 struct list_item **tail,
7468 static void mp_expand_wild_cards(pTHX_ char *item,
7469 struct list_item **head,
7470 struct list_item **tail,
7473 static int background_process(pTHX_ int argc, char **argv);
7475 static void pipe_and_fork(pTHX_ char **cmargv);
7477 /*{{{ void getredirection(int *ac, char ***av)*/
7479 mp_getredirection(pTHX_ int *ac, char ***av)
7481 * Process vms redirection arg's. Exit if any error is seen.
7482 * If getredirection() processes an argument, it is erased
7483 * from the vector. getredirection() returns a new argc and argv value.
7484 * In the event that a background command is requested (by a trailing "&"),
7485 * this routine creates a background subprocess, and simply exits the program.
7487 * Warning: do not try to simplify the code for vms. The code
7488 * presupposes that getredirection() is called before any data is
7489 * read from stdin or written to stdout.
7491 * Normal usage is as follows:
7497 * getredirection(&argc, &argv);
7501 int argc = *ac; /* Argument Count */
7502 char **argv = *av; /* Argument Vector */
7503 char *ap; /* Argument pointer */
7504 int j; /* argv[] index */
7505 int item_count = 0; /* Count of Items in List */
7506 struct list_item *list_head = 0; /* First Item in List */
7507 struct list_item *list_tail; /* Last Item in List */
7508 char *in = NULL; /* Input File Name */
7509 char *out = NULL; /* Output File Name */
7510 char *outmode = "w"; /* Mode to Open Output File */
7511 char *err = NULL; /* Error File Name */
7512 char *errmode = "w"; /* Mode to Open Error File */
7513 int cmargc = 0; /* Piped Command Arg Count */
7514 char **cmargv = NULL;/* Piped Command Arg Vector */
7517 * First handle the case where the last thing on the line ends with
7518 * a '&'. This indicates the desire for the command to be run in a
7519 * subprocess, so we satisfy that desire.
7522 if (0 == strcmp("&", ap))
7523 exit(background_process(aTHX_ --argc, argv));
7524 if (*ap && '&' == ap[strlen(ap)-1])
7526 ap[strlen(ap)-1] = '\0';
7527 exit(background_process(aTHX_ argc, argv));
7530 * Now we handle the general redirection cases that involve '>', '>>',
7531 * '<', and pipes '|'.
7533 for (j = 0; j < argc; ++j)
7535 if (0 == strcmp("<", argv[j]))
7539 fprintf(stderr,"No input file after < on command line");
7540 exit(LIB$_WRONUMARG);
7545 if ('<' == *(ap = argv[j]))
7550 if (0 == strcmp(">", ap))
7554 fprintf(stderr,"No output file after > on command line");
7555 exit(LIB$_WRONUMARG);
7574 fprintf(stderr,"No output file after > or >> on command line");
7575 exit(LIB$_WRONUMARG);
7579 if (('2' == *ap) && ('>' == ap[1]))
7596 fprintf(stderr,"No output file after 2> or 2>> on command line");
7597 exit(LIB$_WRONUMARG);
7601 if (0 == strcmp("|", argv[j]))
7605 fprintf(stderr,"No command into which to pipe on command line");
7606 exit(LIB$_WRONUMARG);
7608 cmargc = argc-(j+1);
7609 cmargv = &argv[j+1];
7613 if ('|' == *(ap = argv[j]))
7621 expand_wild_cards(ap, &list_head, &list_tail, &item_count);
7624 * Allocate and fill in the new argument vector, Some Unix's terminate
7625 * the list with an extra null pointer.
7627 argv = (char **) PerlMem_malloc((item_count+1) * sizeof(char *));
7628 if (argv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7630 for (j = 0; j < item_count; ++j, list_head = list_head->next)
7631 argv[j] = list_head->value;
7637 fprintf(stderr,"'|' and '>' may not both be specified on command line");
7638 exit(LIB$_INVARGORD);
7640 pipe_and_fork(aTHX_ cmargv);
7643 /* Check for input from a pipe (mailbox) */
7645 if (in == NULL && 1 == isapipe(0))
7647 char mbxname[L_tmpnam];
7649 long int dvi_item = DVI$_DEVBUFSIZ;
7650 $DESCRIPTOR(mbxnam, "");
7651 $DESCRIPTOR(mbxdevnam, "");
7653 /* Input from a pipe, reopen it in binary mode to disable */
7654 /* carriage control processing. */
7656 fgetname(stdin, mbxname);
7657 mbxnam.dsc$a_pointer = mbxname;
7658 mbxnam.dsc$w_length = strlen(mbxnam.dsc$a_pointer);
7659 lib$getdvi(&dvi_item, 0, &mbxnam, &bufsize, 0, 0);
7660 mbxdevnam.dsc$a_pointer = mbxname;
7661 mbxdevnam.dsc$w_length = sizeof(mbxname);
7662 dvi_item = DVI$_DEVNAM;
7663 lib$getdvi(&dvi_item, 0, &mbxnam, 0, &mbxdevnam, &mbxdevnam.dsc$w_length);
7664 mbxdevnam.dsc$a_pointer[mbxdevnam.dsc$w_length] = '\0';
7667 freopen(mbxname, "rb", stdin);
7670 fprintf(stderr,"Can't reopen input pipe (name: %s) in binary mode",mbxname);
7674 if ((in != NULL) && (NULL == freopen(in, "r", stdin, "mbc=32", "mbf=2")))
7676 fprintf(stderr,"Can't open input file %s as stdin",in);
7679 if ((out != NULL) && (NULL == freopen(out, outmode, stdout, "mbc=32", "mbf=2")))
7681 fprintf(stderr,"Can't open output file %s as stdout",out);
7684 if (out != NULL) Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",out);
7687 if (strcmp(err,"&1") == 0) {
7688 dup2(fileno(stdout), fileno(stderr));
7689 Perl_vmssetuserlnm(aTHX_ "SYS$ERROR","SYS$OUTPUT");
7692 if (NULL == (tmperr = fopen(err, errmode, "mbc=32", "mbf=2")))
7694 fprintf(stderr,"Can't open error file %s as stderr",err);
7698 if (NULL == freopen(err, "a", stderr, "mbc=32", "mbf=2"))
7702 Perl_vmssetuserlnm(aTHX_ "SYS$ERROR",err);
7705 #ifdef ARGPROC_DEBUG
7706 PerlIO_printf(Perl_debug_log, "Arglist:\n");
7707 for (j = 0; j < *ac; ++j)
7708 PerlIO_printf(Perl_debug_log, "argv[%d] = '%s'\n", j, argv[j]);
7710 /* Clear errors we may have hit expanding wildcards, so they don't
7711 show up in Perl's $! later */
7712 set_errno(0); set_vaxc_errno(1);
7713 } /* end of getredirection() */
7716 static void add_item(struct list_item **head,
7717 struct list_item **tail,
7723 *head = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
7724 if (head == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7728 (*tail)->next = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
7729 if ((*tail)->next == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7730 *tail = (*tail)->next;
7732 (*tail)->value = value;
7736 static void mp_expand_wild_cards(pTHX_ char *item,
7737 struct list_item **head,
7738 struct list_item **tail,
7742 unsigned long int context = 0;
7750 $DESCRIPTOR(filespec, "");
7751 $DESCRIPTOR(defaultspec, "SYS$DISK:[]");
7752 $DESCRIPTOR(resultspec, "");
7753 unsigned long int lff_flags = 0;
7757 #ifdef VMS_LONGNAME_SUPPORT
7758 lff_flags = LIB$M_FIL_LONG_NAMES;
7761 for (cp = item; *cp; cp++) {
7762 if (*cp == '*' || *cp == '%' || isspace(*cp)) break;
7763 if (*cp == '.' && *(cp-1) == '.' && *(cp-2) =='.') break;
7765 if (!*cp || isspace(*cp))
7767 add_item(head, tail, item, count);
7772 /* "double quoted" wild card expressions pass as is */
7773 /* From DCL that means using e.g.: */
7774 /* perl program """perl.*""" */
7775 item_len = strlen(item);
7776 if ( '"' == *item && '"' == item[item_len-1] )
7779 item[item_len-2] = '\0';
7780 add_item(head, tail, item, count);
7784 resultspec.dsc$b_dtype = DSC$K_DTYPE_T;
7785 resultspec.dsc$b_class = DSC$K_CLASS_D;
7786 resultspec.dsc$a_pointer = NULL;
7787 vmsspec = PerlMem_malloc(VMS_MAXRSS);
7788 if (vmsspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7789 if ((isunix = (int) strchr(item,'/')) != (int) NULL)
7790 filespec.dsc$a_pointer = do_tovmsspec(item,vmsspec,0,NULL);
7791 if (!isunix || !filespec.dsc$a_pointer)
7792 filespec.dsc$a_pointer = item;
7793 filespec.dsc$w_length = strlen(filespec.dsc$a_pointer);
7795 * Only return version specs, if the caller specified a version
7797 had_version = strchr(item, ';');
7799 * Only return device and directory specs, if the caller specifed either.
7801 had_device = strchr(item, ':');
7802 had_directory = (isunix || NULL != strchr(item, '[')) || (NULL != strchr(item, '<'));
7804 while ($VMS_STATUS_SUCCESS(sts = lib$find_file
7805 (&filespec, &resultspec, &context,
7806 &defaultspec, 0, &rms_sts, &lff_flags)))
7811 string = PerlMem_malloc(resultspec.dsc$w_length+1);
7812 if (string == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7813 strncpy(string, resultspec.dsc$a_pointer, resultspec.dsc$w_length);
7814 string[resultspec.dsc$w_length] = '\0';
7815 if (NULL == had_version)
7816 *(strrchr(string, ';')) = '\0';
7817 if ((!had_directory) && (had_device == NULL))
7819 if (NULL == (devdir = strrchr(string, ']')))
7820 devdir = strrchr(string, '>');
7821 strcpy(string, devdir + 1);
7824 * Be consistent with what the C RTL has already done to the rest of
7825 * the argv items and lowercase all of these names.
7827 if (!decc_efs_case_preserve) {
7828 for (c = string; *c; ++c)
7832 if (isunix) trim_unixpath(string,item,1);
7833 add_item(head, tail, string, count);
7836 PerlMem_free(vmsspec);
7837 if (sts != RMS$_NMF)
7839 set_vaxc_errno(sts);
7842 case RMS$_FNF: case RMS$_DNF:
7843 set_errno(ENOENT); break;
7845 set_errno(ENOTDIR); break;
7847 set_errno(ENODEV); break;
7848 case RMS$_FNM: case RMS$_SYN:
7849 set_errno(EINVAL); break;
7851 set_errno(EACCES); break;
7853 _ckvmssts_noperl(sts);
7857 add_item(head, tail, item, count);
7858 _ckvmssts_noperl(lib$sfree1_dd(&resultspec));
7859 _ckvmssts_noperl(lib$find_file_end(&context));
7862 static int child_st[2];/* Event Flag set when child process completes */
7864 static unsigned short child_chan;/* I/O Channel for Pipe Mailbox */
7866 static unsigned long int exit_handler(int *status)
7870 if (0 == child_st[0])
7872 #ifdef ARGPROC_DEBUG
7873 PerlIO_printf(Perl_debug_log, "Waiting for Child Process to Finish . . .\n");
7875 fflush(stdout); /* Have to flush pipe for binary data to */
7876 /* terminate properly -- <tp@mccall.com> */
7877 sys$qiow(0, child_chan, IO$_WRITEOF, iosb, 0, 0, 0, 0, 0, 0, 0, 0);
7878 sys$dassgn(child_chan);
7880 sys$synch(0, child_st);
7885 static void sig_child(int chan)
7887 #ifdef ARGPROC_DEBUG
7888 PerlIO_printf(Perl_debug_log, "Child Completion AST\n");
7890 if (child_st[0] == 0)
7894 static struct exit_control_block exit_block =
7899 &exit_block.exit_status,
7904 pipe_and_fork(pTHX_ char **cmargv)
7907 struct dsc$descriptor_s *vmscmd;
7908 char subcmd[2*MAX_DCL_LINE_LENGTH], *p, *q;
7909 int sts, j, l, ismcr, quote, tquote = 0;
7911 sts = setup_cmddsc(aTHX_ cmargv[0],0,"e,&vmscmd);
7912 vms_execfree(vmscmd);
7917 ismcr = q && toupper(*q) == 'M' && toupper(*(q+1)) == 'C'
7918 && toupper(*(q+2)) == 'R' && !*(q+3);
7920 while (q && l < MAX_DCL_LINE_LENGTH) {
7922 if (j > 0 && quote) {
7928 if (ismcr && j > 1) quote = 1;
7929 tquote = (strchr(q,' ')) != NULL || *q == '\0';
7932 if (quote || tquote) {
7938 if ((quote||tquote) && *q == '"') {
7948 fp = safe_popen(aTHX_ subcmd,"wbF",&sts);
7950 PerlIO_printf(Perl_debug_log,"Can't open output pipe (status %d)",sts);
7954 static int background_process(pTHX_ int argc, char **argv)
7956 char command[MAX_DCL_SYMBOL + 1] = "$";
7957 $DESCRIPTOR(value, "");
7958 static $DESCRIPTOR(cmd, "BACKGROUND$COMMAND");
7959 static $DESCRIPTOR(null, "NLA0:");
7960 static $DESCRIPTOR(pidsymbol, "SHELL_BACKGROUND_PID");
7962 $DESCRIPTOR(pidstr, "");
7964 unsigned long int flags = 17, one = 1, retsts;
7967 strcat(command, argv[0]);
7968 len = strlen(command);
7969 while (--argc && (len < MAX_DCL_SYMBOL))
7971 strcat(command, " \"");
7972 strcat(command, *(++argv));
7973 strcat(command, "\"");
7974 len = strlen(command);
7976 value.dsc$a_pointer = command;
7977 value.dsc$w_length = strlen(value.dsc$a_pointer);
7978 _ckvmssts_noperl(lib$set_symbol(&cmd, &value));
7979 retsts = lib$spawn(&cmd, &null, 0, &flags, 0, &pid);
7980 if (retsts == 0x38250) { /* DCL-W-NOTIFY - We must be BATCH, so retry */
7981 _ckvmssts_noperl(lib$spawn(&cmd, &null, 0, &one, 0, &pid));
7984 _ckvmssts_noperl(retsts);
7986 #ifdef ARGPROC_DEBUG
7987 PerlIO_printf(Perl_debug_log, "%s\n", command);
7989 sprintf(pidstring, "%08X", pid);
7990 PerlIO_printf(Perl_debug_log, "%s\n", pidstring);
7991 pidstr.dsc$a_pointer = pidstring;
7992 pidstr.dsc$w_length = strlen(pidstr.dsc$a_pointer);
7993 lib$set_symbol(&pidsymbol, &pidstr);
7997 /***** End of code taken from Mark Pizzolato's argproc.c package *****/
8000 /* OS-specific initialization at image activation (not thread startup) */
8001 /* Older VAXC header files lack these constants */
8002 #ifndef JPI$_RIGHTS_SIZE
8003 # define JPI$_RIGHTS_SIZE 817
8005 #ifndef KGB$M_SUBSYSTEM
8006 # define KGB$M_SUBSYSTEM 0x8
8009 /* Avoid Newx() in vms_image_init as thread context has not been initialized. */
8011 /*{{{void vms_image_init(int *, char ***)*/
8013 vms_image_init(int *argcp, char ***argvp)
8015 char eqv[LNM$C_NAMLENGTH+1] = "";
8016 unsigned int len, tabct = 8, tabidx = 0;
8017 unsigned long int *mask, iosb[2], i, rlst[128], rsz;
8018 unsigned long int iprv[(sizeof(union prvdef) + sizeof(unsigned long int) - 1) / sizeof(unsigned long int)];
8019 unsigned short int dummy, rlen;
8020 struct dsc$descriptor_s **tabvec;
8021 #if defined(PERL_IMPLICIT_CONTEXT)
8024 struct itmlst_3 jpilist[4] = { {sizeof iprv, JPI$_IMAGPRIV, iprv, &dummy},
8025 {sizeof rlst, JPI$_RIGHTSLIST, rlst, &rlen},
8026 { sizeof rsz, JPI$_RIGHTS_SIZE, &rsz, &dummy},
8029 #ifdef KILL_BY_SIGPRC
8030 Perl_csighandler_init();
8033 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,jpilist,iosb,NULL,NULL));
8034 _ckvmssts_noperl(iosb[0]);
8035 for (i = 0; i < sizeof iprv / sizeof(unsigned long int); i++) {
8036 if (iprv[i]) { /* Running image installed with privs? */
8037 _ckvmssts_noperl(sys$setprv(0,iprv,0,NULL)); /* Turn 'em off. */
8042 /* Rights identifiers might trigger tainting as well. */
8043 if (!will_taint && (rlen || rsz)) {
8044 while (rlen < rsz) {
8045 /* We didn't get all the identifiers on the first pass. Allocate a
8046 * buffer much larger than $GETJPI wants (rsz is size in bytes that
8047 * were needed to hold all identifiers at time of last call; we'll
8048 * allocate that many unsigned long ints), and go back and get 'em.
8049 * If it gave us less than it wanted to despite ample buffer space,
8050 * something's broken. Is your system missing a system identifier?
8052 if (rsz <= jpilist[1].buflen) {
8053 /* Perl_croak accvios when used this early in startup. */
8054 fprintf(stderr, "vms_image_init: $getjpiw refuses to store RIGHTSLIST of %u bytes in buffer of %u bytes.\n%s",
8055 rsz, (unsigned long) jpilist[1].buflen,
8056 "Check your rights database for corruption.\n");
8059 if (jpilist[1].bufadr != rlst) PerlMem_free(jpilist[1].bufadr);
8060 jpilist[1].bufadr = mask = (unsigned long int *) PerlMem_malloc(rsz * sizeof(unsigned long int));
8061 if (mask == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8062 jpilist[1].buflen = rsz * sizeof(unsigned long int);
8063 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,&jpilist[1],iosb,NULL,NULL));
8064 _ckvmssts_noperl(iosb[0]);
8066 mask = jpilist[1].bufadr;
8067 /* Check attribute flags for each identifier (2nd longword); protected
8068 * subsystem identifiers trigger tainting.
8070 for (i = 1; i < (rlen + sizeof(unsigned long int) - 1) / sizeof(unsigned long int); i += 2) {
8071 if (mask[i] & KGB$M_SUBSYSTEM) {
8076 if (mask != rlst) PerlMem_free(mask);
8079 /* When Perl is in decc_filename_unix_report mode and is run from a concealed
8080 * logical, some versions of the CRTL will add a phanthom /000000/
8081 * directory. This needs to be removed.
8083 if (decc_filename_unix_report) {
8086 ulen = strlen(argvp[0][0]);
8088 zeros = strstr(argvp[0][0], "/000000/");
8089 if (zeros != NULL) {
8091 mlen = ulen - (zeros - argvp[0][0]) - 7;
8092 memmove(zeros, &zeros[7], mlen);
8094 argvp[0][0][ulen] = '\0';
8097 /* It also may have a trailing dot that needs to be removed otherwise
8098 * it will be converted to VMS mode incorrectly.
8101 if ((argvp[0][0][ulen] == '.') && (decc_readdir_dropdotnotype))
8102 argvp[0][0][ulen] = '\0';
8105 /* We need to use this hack to tell Perl it should run with tainting,
8106 * since its tainting flag may be part of the PL_curinterp struct, which
8107 * hasn't been allocated when vms_image_init() is called.
8110 char **newargv, **oldargv;
8112 newargv = (char **) PerlMem_malloc(((*argcp)+2) * sizeof(char *));
8113 if (newargv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8114 newargv[0] = oldargv[0];
8115 newargv[1] = PerlMem_malloc(3 * sizeof(char));
8116 if (newargv[1] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8117 strcpy(newargv[1], "-T");
8118 Copy(&oldargv[1],&newargv[2],(*argcp)-1,char **);
8120 newargv[*argcp] = NULL;
8121 /* We orphan the old argv, since we don't know where it's come from,
8122 * so we don't know how to free it.
8126 else { /* Did user explicitly request tainting? */
8128 char *cp, **av = *argvp;
8129 for (i = 1; i < *argcp; i++) {
8130 if (*av[i] != '-') break;
8131 for (cp = av[i]+1; *cp; cp++) {
8132 if (*cp == 'T') { will_taint = 1; break; }
8133 else if ( (*cp == 'd' || *cp == 'V') && *(cp+1) == ':' ||
8134 strchr("DFIiMmx",*cp)) break;
8136 if (will_taint) break;
8141 len = my_trnlnm("PERL_ENV_TABLES",eqv,tabidx);
8144 tabvec = (struct dsc$descriptor_s **)
8145 PerlMem_malloc(tabct * sizeof(struct dsc$descriptor_s *));
8146 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8148 else if (tabidx >= tabct) {
8150 tabvec = (struct dsc$descriptor_s **) PerlMem_realloc(tabvec, tabct * sizeof(struct dsc$descriptor_s *));
8151 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8153 tabvec[tabidx] = (struct dsc$descriptor_s *) PerlMem_malloc(sizeof(struct dsc$descriptor_s));
8154 if (tabvec[tabidx] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8155 tabvec[tabidx]->dsc$w_length = 0;
8156 tabvec[tabidx]->dsc$b_dtype = DSC$K_DTYPE_T;
8157 tabvec[tabidx]->dsc$b_class = DSC$K_CLASS_D;
8158 tabvec[tabidx]->dsc$a_pointer = NULL;
8159 _ckvmssts_noperl(lib$scopy_r_dx(&len,eqv,tabvec[tabidx]));
8161 if (tabidx) { tabvec[tabidx] = NULL; env_tables = tabvec; }
8163 getredirection(argcp,argvp);
8164 #if defined(USE_ITHREADS) && ( defined(__DECC) || defined(__DECCXX) )
8166 # include <reentrancy.h>
8167 decc$set_reentrancy(C$C_MULTITHREAD);
8176 * Trim Unix-style prefix off filespec, so it looks like what a shell
8177 * glob expansion would return (i.e. from specified prefix on, not
8178 * full path). Note that returned filespec is Unix-style, regardless
8179 * of whether input filespec was VMS-style or Unix-style.
8181 * fspec is filespec to be trimmed, and wildspec is wildcard spec used to
8182 * determine prefix (both may be in VMS or Unix syntax). opts is a bit
8183 * vector of options; at present, only bit 0 is used, and if set tells
8184 * trim unixpath to try the current default directory as a prefix when
8185 * presented with a possibly ambiguous ... wildcard.
8187 * Returns !=0 on success, with trimmed filespec replacing contents of
8188 * fspec, and 0 on failure, with contents of fpsec unchanged.
8190 /*{{{int trim_unixpath(char *fspec, char *wildspec, int opts)*/
8192 Perl_trim_unixpath(pTHX_ char *fspec, const char *wildspec, int opts)
8194 char *unixified, *unixwild,
8195 *template, *base, *end, *cp1, *cp2;
8196 register int tmplen, reslen = 0, dirs = 0;
8198 unixwild = PerlMem_malloc(VMS_MAXRSS);
8199 if (unixwild == NULL) _ckvmssts(SS$_INSFMEM);
8200 if (!wildspec || !fspec) return 0;
8201 template = unixwild;
8202 if (strpbrk(wildspec,"]>:") != NULL) {
8203 if (do_tounixspec(wildspec,unixwild,0,NULL) == NULL) {
8204 PerlMem_free(unixwild);
8209 strncpy(unixwild, wildspec, VMS_MAXRSS-1);
8210 unixwild[VMS_MAXRSS-1] = 0;
8212 unixified = PerlMem_malloc(VMS_MAXRSS);
8213 if (unixified == NULL) _ckvmssts(SS$_INSFMEM);
8214 if (strpbrk(fspec,"]>:") != NULL) {
8215 if (do_tounixspec(fspec,unixified,0,NULL) == NULL) {
8216 PerlMem_free(unixwild);
8217 PerlMem_free(unixified);
8220 else base = unixified;
8221 /* reslen != 0 ==> we had to unixify resultant filespec, so we must
8222 * check to see that final result fits into (isn't longer than) fspec */
8223 reslen = strlen(fspec);
8227 /* No prefix or absolute path on wildcard, so nothing to remove */
8228 if (!*template || *template == '/') {
8229 PerlMem_free(unixwild);
8230 if (base == fspec) {
8231 PerlMem_free(unixified);
8234 tmplen = strlen(unixified);
8235 if (tmplen > reslen) {
8236 PerlMem_free(unixified);
8237 return 0; /* not enough space */
8239 /* Copy unixified resultant, including trailing NUL */
8240 memmove(fspec,unixified,tmplen+1);
8241 PerlMem_free(unixified);
8245 for (end = base; *end; end++) ; /* Find end of resultant filespec */
8246 if ((cp1 = strstr(template,".../")) == NULL) { /* No ...; just count elts */
8247 for (cp1 = template; *cp1; cp1++) if (*cp1 == '/') dirs++;
8248 for (cp1 = end ;cp1 >= base; cp1--)
8249 if ((*cp1 == '/') && !dirs--) /* postdec so we get front of rel path */
8251 if (cp1 != fspec) memmove(fspec,cp1, end - cp1 + 1);
8252 PerlMem_free(unixified);
8253 PerlMem_free(unixwild);
8258 char *front, *nextell, *lcend, *lcfront, *ellipsis = cp1;
8259 int ells = 1, totells, segdirs, match;
8260 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL},
8261 resdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
8263 while ((cp1 = strstr(ellipsis+4,".../")) != NULL) {ellipsis = cp1; ells++;}
8265 for (cp1 = ellipsis+4; *cp1; cp1++) if (*cp1 == '/') dirs++;
8266 tpl = PerlMem_malloc(VMS_MAXRSS);
8267 if (tpl == NULL) _ckvmssts(SS$_INSFMEM);
8268 if (ellipsis == template && opts & 1) {
8269 /* Template begins with an ellipsis. Since we can't tell how many
8270 * directory names at the front of the resultant to keep for an
8271 * arbitrary starting point, we arbitrarily choose the current
8272 * default directory as a starting point. If it's there as a prefix,
8273 * clip it off. If not, fall through and act as if the leading
8274 * ellipsis weren't there (i.e. return shortest possible path that
8275 * could match template).
8277 if (getcwd(tpl, (VMS_MAXRSS - 1),0) == NULL) {
8279 PerlMem_free(unixified);
8280 PerlMem_free(unixwild);
8283 if (!decc_efs_case_preserve) {
8284 for (cp1 = tpl, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
8285 if (_tolower(*cp1) != _tolower(*cp2)) break;
8287 segdirs = dirs - totells; /* Min # of dirs we must have left */
8288 for (front = cp2+1; *front; front++) if (*front == '/') segdirs--;
8289 if (*cp1 == '\0' && *cp2 == '/' && segdirs < 1) {
8290 memmove(fspec,cp2+1,end - cp2);
8292 PerlMem_free(unixified);
8293 PerlMem_free(unixwild);
8297 /* First off, back up over constant elements at end of path */
8299 for (front = end ; front >= base; front--)
8300 if (*front == '/' && !dirs--) { front++; break; }
8302 lcres = PerlMem_malloc(VMS_MAXRSS);
8303 if (lcres == NULL) _ckvmssts(SS$_INSFMEM);
8304 for (cp1=template,cp2=lcres; *cp1 && cp2 <= lcres + (VMS_MAXRSS - 1);
8306 if (!decc_efs_case_preserve) {
8307 *cp2 = _tolower(*cp1); /* Make lc copy for match */
8315 PerlMem_free(unixified);
8316 PerlMem_free(unixwild);
8317 PerlMem_free(lcres);
8318 return 0; /* Path too long. */
8321 *cp2 = '\0'; /* Pick up with memcpy later */
8322 lcfront = lcres + (front - base);
8323 /* Now skip over each ellipsis and try to match the path in front of it. */
8325 for (cp1 = ellipsis - 2; cp1 >= template; cp1--)
8326 if (*(cp1) == '.' && *(cp1+1) == '.' &&
8327 *(cp1+2) == '.' && *(cp1+3) == '/' ) break;
8328 if (cp1 < template) break; /* template started with an ellipsis */
8329 if (cp1 + 4 == ellipsis) { /* Consecutive ellipses */
8330 ellipsis = cp1; continue;
8332 wilddsc.dsc$a_pointer = tpl;
8333 wilddsc.dsc$w_length = resdsc.dsc$w_length = ellipsis - 1 - cp1;
8335 for (segdirs = 0, cp2 = tpl;
8336 cp1 <= ellipsis - 1 && cp2 <= tpl + (VMS_MAXRSS-1);
8338 if (*cp1 == '?') *cp2 = '%'; /* Substitute VMS' wildcard for Unix' */
8340 if (!decc_efs_case_preserve) {
8341 *cp2 = _tolower(*cp1); /* else lowercase for match */
8344 *cp2 = *cp1; /* else preserve case for match */
8347 if (*cp2 == '/') segdirs++;
8349 if (cp1 != ellipsis - 1) {
8351 PerlMem_free(unixified);
8352 PerlMem_free(unixwild);
8353 PerlMem_free(lcres);
8354 return 0; /* Path too long */
8356 /* Back up at least as many dirs as in template before matching */
8357 for (cp1 = lcfront - 1; segdirs && cp1 >= lcres; cp1--)
8358 if (*cp1 == '/' && !segdirs--) { cp1++; break; }
8359 for (match = 0; cp1 > lcres;) {
8360 resdsc.dsc$a_pointer = cp1;
8361 if (str$match_wild(&wilddsc,&resdsc) == STR$_MATCH) {
8363 if (match == 1) lcfront = cp1;
8365 for ( ; cp1 >= lcres; cp1--) if (*cp1 == '/') { cp1++; break; }
8369 PerlMem_free(unixified);
8370 PerlMem_free(unixwild);
8371 PerlMem_free(lcres);
8372 return 0; /* Can't find prefix ??? */
8374 if (match > 1 && opts & 1) {
8375 /* This ... wildcard could cover more than one set of dirs (i.e.
8376 * a set of similar dir names is repeated). If the template
8377 * contains more than 1 ..., upstream elements could resolve the
8378 * ambiguity, but it's not worth a full backtracking setup here.
8379 * As a quick heuristic, clip off the current default directory
8380 * if it's present to find the trimmed spec, else use the
8381 * shortest string that this ... could cover.
8383 char def[NAM$C_MAXRSS+1], *st;
8385 if (getcwd(def, sizeof def,0) == NULL) {
8386 Safefree(unixified);
8392 if (!decc_efs_case_preserve) {
8393 for (cp1 = def, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
8394 if (_tolower(*cp1) != _tolower(*cp2)) break;
8396 segdirs = dirs - totells; /* Min # of dirs we must have left */
8397 for (st = cp2+1; *st; st++) if (*st == '/') segdirs--;
8398 if (*cp1 == '\0' && *cp2 == '/') {
8399 memmove(fspec,cp2+1,end - cp2);
8401 PerlMem_free(unixified);
8402 PerlMem_free(unixwild);
8403 PerlMem_free(lcres);
8406 /* Nope -- stick with lcfront from above and keep going. */
8409 memmove(fspec,base + (lcfront - lcres), lcend - lcfront + 1);
8411 PerlMem_free(unixified);
8412 PerlMem_free(unixwild);
8413 PerlMem_free(lcres);
8418 } /* end of trim_unixpath() */
8423 * VMS readdir() routines.
8424 * Written by Rich $alz, <rsalz@bbn.com> in August, 1990.
8426 * 21-Jul-1994 Charles Bailey bailey@newman.upenn.edu
8427 * Minor modifications to original routines.
8430 /* readdir may have been redefined by reentr.h, so make sure we get
8431 * the local version for what we do here.
8436 #if !defined(PERL_IMPLICIT_CONTEXT)
8437 # define readdir Perl_readdir
8439 # define readdir(a) Perl_readdir(aTHX_ a)
8442 /* Number of elements in vms_versions array */
8443 #define VERSIZE(e) (sizeof e->vms_versions / sizeof e->vms_versions[0])
8446 * Open a directory, return a handle for later use.
8448 /*{{{ DIR *opendir(char*name) */
8450 Perl_opendir(pTHX_ const char *name)
8458 if (decc_efs_charset) {
8459 unix_flag = is_unix_filespec(name);
8462 Newx(dir, VMS_MAXRSS, char);
8463 if (do_tovmspath(name,dir,0,NULL) == NULL) {
8467 /* Check access before stat; otherwise stat does not
8468 * accurately report whether it's a directory.
8470 if (!cando_by_name_int(S_IRUSR,0,dir,PERL_RMSEXPAND_M_VMS_IN)) {
8471 /* cando_by_name has already set errno */
8475 if (flex_stat(dir,&sb) == -1) return NULL;
8476 if (!S_ISDIR(sb.st_mode)) {
8478 set_errno(ENOTDIR); set_vaxc_errno(RMS$_DIR);
8481 /* Get memory for the handle, and the pattern. */
8483 Newx(dd->pattern,strlen(dir)+sizeof "*.*" + 1,char);
8485 /* Fill in the fields; mainly playing with the descriptor. */
8486 sprintf(dd->pattern, "%s*.*",dir);
8492 dd->flags = PERL_VMSDIR_M_UNIXSPECS;
8493 dd->pat.dsc$a_pointer = dd->pattern;
8494 dd->pat.dsc$w_length = strlen(dd->pattern);
8495 dd->pat.dsc$b_dtype = DSC$K_DTYPE_T;
8496 dd->pat.dsc$b_class = DSC$K_CLASS_S;
8497 #if defined(USE_ITHREADS)
8498 Newx(dd->mutex,1,perl_mutex);
8499 MUTEX_INIT( (perl_mutex *) dd->mutex );
8505 } /* end of opendir() */
8509 * Set the flag to indicate we want versions or not.
8511 /*{{{ void vmsreaddirversions(DIR *dd, int flag)*/
8513 vmsreaddirversions(DIR *dd, int flag)
8516 dd->flags |= PERL_VMSDIR_M_VERSIONS;
8518 dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
8523 * Free up an opened directory.
8525 /*{{{ void closedir(DIR *dd)*/
8527 Perl_closedir(DIR *dd)
8531 sts = lib$find_file_end(&dd->context);
8532 Safefree(dd->pattern);
8533 #if defined(USE_ITHREADS)
8534 MUTEX_DESTROY( (perl_mutex *) dd->mutex );
8535 Safefree(dd->mutex);
8542 * Collect all the version numbers for the current file.
8545 collectversions(pTHX_ DIR *dd)
8547 struct dsc$descriptor_s pat;
8548 struct dsc$descriptor_s res;
8550 char *p, *text, *buff;
8552 unsigned long context, tmpsts;
8554 /* Convenient shorthand. */
8557 /* Add the version wildcard, ignoring the "*.*" put on before */
8558 i = strlen(dd->pattern);
8559 Newx(text,i + e->d_namlen + 3,char);
8560 strcpy(text, dd->pattern);
8561 sprintf(&text[i - 3], "%s;*", e->d_name);
8563 /* Set up the pattern descriptor. */
8564 pat.dsc$a_pointer = text;
8565 pat.dsc$w_length = i + e->d_namlen - 1;
8566 pat.dsc$b_dtype = DSC$K_DTYPE_T;
8567 pat.dsc$b_class = DSC$K_CLASS_S;
8569 /* Set up result descriptor. */
8570 Newx(buff, VMS_MAXRSS, char);
8571 res.dsc$a_pointer = buff;
8572 res.dsc$w_length = VMS_MAXRSS - 1;
8573 res.dsc$b_dtype = DSC$K_DTYPE_T;
8574 res.dsc$b_class = DSC$K_CLASS_S;
8576 /* Read files, collecting versions. */
8577 for (context = 0, e->vms_verscount = 0;
8578 e->vms_verscount < VERSIZE(e);
8579 e->vms_verscount++) {
8581 unsigned long flags = 0;
8583 #ifdef VMS_LONGNAME_SUPPORT
8584 flags = LIB$M_FIL_LONG_NAMES;
8586 tmpsts = lib$find_file(&pat, &res, &context, NULL, NULL, &rsts, &flags);
8587 if (tmpsts == RMS$_NMF || context == 0) break;
8589 buff[VMS_MAXRSS - 1] = '\0';
8590 if ((p = strchr(buff, ';')))
8591 e->vms_versions[e->vms_verscount] = atoi(p + 1);
8593 e->vms_versions[e->vms_verscount] = -1;
8596 _ckvmssts(lib$find_file_end(&context));
8600 } /* end of collectversions() */
8603 * Read the next entry from the directory.
8605 /*{{{ struct dirent *readdir(DIR *dd)*/
8607 Perl_readdir(pTHX_ DIR *dd)
8609 struct dsc$descriptor_s res;
8611 unsigned long int tmpsts;
8613 unsigned long flags = 0;
8614 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
8615 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
8617 /* Set up result descriptor, and get next file. */
8618 Newx(buff, VMS_MAXRSS, char);
8619 res.dsc$a_pointer = buff;
8620 res.dsc$w_length = VMS_MAXRSS - 1;
8621 res.dsc$b_dtype = DSC$K_DTYPE_T;
8622 res.dsc$b_class = DSC$K_CLASS_S;
8624 #ifdef VMS_LONGNAME_SUPPORT
8625 flags = LIB$M_FIL_LONG_NAMES;
8628 tmpsts = lib$find_file
8629 (&dd->pat, &res, &dd->context, NULL, NULL, &rsts, &flags);
8630 if ( tmpsts == RMS$_NMF || dd->context == 0) return NULL; /* None left. */
8631 if (!(tmpsts & 1)) {
8632 set_vaxc_errno(tmpsts);
8635 set_errno(EACCES); break;
8637 set_errno(ENODEV); break;
8639 set_errno(ENOTDIR); break;
8640 case RMS$_FNF: case RMS$_DNF:
8641 set_errno(ENOENT); break;
8649 /* Force the buffer to end with a NUL, and downcase name to match C convention. */
8650 if (!decc_efs_case_preserve) {
8651 buff[VMS_MAXRSS - 1] = '\0';
8652 for (p = buff; *p; p++) *p = _tolower(*p);
8655 /* we don't want to force to lowercase, just null terminate */
8656 buff[res.dsc$w_length] = '\0';
8658 while (--p >= buff) if (!isspace(*p)) break; /* Do we really need this? */
8661 /* Skip any directory component and just copy the name. */
8662 sts = vms_split_path
8677 /* Drop NULL extensions on UNIX file specification */
8678 if ((dd->flags & PERL_VMSDIR_M_UNIXSPECS &&
8679 (e_len == 1) && decc_readdir_dropdotnotype)) {
8684 strncpy(dd->entry.d_name, n_spec, n_len + e_len);
8685 dd->entry.d_name[n_len + e_len] = '\0';
8686 dd->entry.d_namlen = strlen(dd->entry.d_name);
8688 /* Convert the filename to UNIX format if needed */
8689 if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
8691 /* Translate the encoded characters. */
8692 /* Fixme: unicode handling could result in embedded 0 characters */
8693 if (strchr(dd->entry.d_name, '^') != NULL) {
8697 p = dd->entry.d_name;
8701 x = copy_expand_vms_filename_escape(q, p, &y);
8705 /* if y > 1, then this is a wide file specification */
8706 /* Wide file specifications need to be passed in Perl */
8707 /* counted strings apparently with a unicode flag */
8710 strcpy(dd->entry.d_name, new_name);
8714 dd->entry.vms_verscount = 0;
8715 if (dd->flags & PERL_VMSDIR_M_VERSIONS) collectversions(aTHX_ dd);
8719 } /* end of readdir() */
8723 * Read the next entry from the directory -- thread-safe version.
8725 /*{{{ int readdir_r(DIR *dd, struct dirent *entry, struct dirent **result)*/
8727 Perl_readdir_r(pTHX_ DIR *dd, struct dirent *entry, struct dirent **result)
8731 MUTEX_LOCK( (perl_mutex *) dd->mutex );
8733 entry = readdir(dd);
8735 retval = ( *result == NULL ? errno : 0 );
8737 MUTEX_UNLOCK( (perl_mutex *) dd->mutex );
8741 } /* end of readdir_r() */
8745 * Return something that can be used in a seekdir later.
8747 /*{{{ long telldir(DIR *dd)*/
8749 Perl_telldir(DIR *dd)
8756 * Return to a spot where we used to be. Brute force.
8758 /*{{{ void seekdir(DIR *dd,long count)*/
8760 Perl_seekdir(pTHX_ DIR *dd, long count)
8764 /* If we haven't done anything yet... */
8768 /* Remember some state, and clear it. */
8769 old_flags = dd->flags;
8770 dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
8771 _ckvmssts(lib$find_file_end(&dd->context));
8774 /* The increment is in readdir(). */
8775 for (dd->count = 0; dd->count < count; )
8778 dd->flags = old_flags;
8780 } /* end of seekdir() */
8783 /* VMS subprocess management
8785 * my_vfork() - just a vfork(), after setting a flag to record that
8786 * the current script is trying a Unix-style fork/exec.
8788 * vms_do_aexec() and vms_do_exec() are called in response to the
8789 * perl 'exec' function. If this follows a vfork call, then they
8790 * call out the regular perl routines in doio.c which do an
8791 * execvp (for those who really want to try this under VMS).
8792 * Otherwise, they do exactly what the perl docs say exec should
8793 * do - terminate the current script and invoke a new command
8794 * (See below for notes on command syntax.)
8796 * do_aspawn() and do_spawn() implement the VMS side of the perl
8797 * 'system' function.
8799 * Note on command arguments to perl 'exec' and 'system': When handled
8800 * in 'VMSish fashion' (i.e. not after a call to vfork) The args
8801 * are concatenated to form a DCL command string. If the first arg
8802 * begins with '$' (i.e. the perl script had "\$ Type" or some such),
8803 * the command string is handed off to DCL directly. Otherwise,
8804 * the first token of the command is taken as the filespec of an image
8805 * to run. The filespec is expanded using a default type of '.EXE' and
8806 * the process defaults for device, directory, etc., and if found, the resultant
8807 * filespec is invoked using the DCL verb 'MCR', and passed the rest of
8808 * the command string as parameters. This is perhaps a bit complicated,
8809 * but I hope it will form a happy medium between what VMS folks expect
8810 * from lib$spawn and what Unix folks expect from exec.
8813 static int vfork_called;
8815 /*{{{int my_vfork()*/
8826 vms_execfree(struct dsc$descriptor_s *vmscmd)
8829 if (vmscmd->dsc$a_pointer) {
8830 PerlMem_free(vmscmd->dsc$a_pointer);
8832 PerlMem_free(vmscmd);
8837 setup_argstr(pTHX_ SV *really, SV **mark, SV **sp)
8839 char *junk, *tmps = Nullch;
8840 register size_t cmdlen = 0;
8847 tmps = SvPV(really,rlen);
8854 for (idx++; idx <= sp; idx++) {
8856 junk = SvPVx(*idx,rlen);
8857 cmdlen += rlen ? rlen + 1 : 0;
8860 Newx(PL_Cmd, cmdlen+1, char);
8862 if (tmps && *tmps) {
8863 strcpy(PL_Cmd,tmps);
8866 else *PL_Cmd = '\0';
8867 while (++mark <= sp) {
8869 char *s = SvPVx(*mark,n_a);
8871 if (*PL_Cmd) strcat(PL_Cmd," ");
8877 } /* end of setup_argstr() */
8880 static unsigned long int
8881 setup_cmddsc(pTHX_ const char *incmd, int check_img, int *suggest_quote,
8882 struct dsc$descriptor_s **pvmscmd)
8884 char vmsspec[NAM$C_MAXRSS+1], resspec[NAM$C_MAXRSS+1];
8885 char image_name[NAM$C_MAXRSS+1];
8886 char image_argv[NAM$C_MAXRSS+1];
8887 $DESCRIPTOR(defdsc,".EXE");
8888 $DESCRIPTOR(defdsc2,".");
8889 $DESCRIPTOR(resdsc,resspec);
8890 struct dsc$descriptor_s *vmscmd;
8891 struct dsc$descriptor_s imgdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
8892 unsigned long int cxt = 0, flags = 1, retsts = SS$_NORMAL;
8893 register char *s, *rest, *cp, *wordbreak;
8898 vmscmd = PerlMem_malloc(sizeof(struct dsc$descriptor_s));
8899 if (vmscmd == NULL) _ckvmssts(SS$_INSFMEM);
8901 /* Make a copy for modification */
8902 cmdlen = strlen(incmd);
8903 cmd = PerlMem_malloc(cmdlen+1);
8904 if (cmd == NULL) _ckvmssts(SS$_INSFMEM);
8905 strncpy(cmd, incmd, cmdlen);
8910 vmscmd->dsc$a_pointer = NULL;
8911 vmscmd->dsc$b_dtype = DSC$K_DTYPE_T;
8912 vmscmd->dsc$b_class = DSC$K_CLASS_S;
8913 vmscmd->dsc$w_length = 0;
8914 if (pvmscmd) *pvmscmd = vmscmd;
8916 if (suggest_quote) *suggest_quote = 0;
8918 if (strlen(cmd) > MAX_DCL_LINE_LENGTH) {
8920 return CLI$_BUFOVF; /* continuation lines currently unsupported */
8925 while (*s && isspace(*s)) s++;
8927 if (*s == '@' || *s == '$') {
8928 vmsspec[0] = *s; rest = s + 1;
8929 for (cp = &vmsspec[1]; *rest && isspace(*rest); rest++,cp++) *cp = *rest;
8931 else { cp = vmsspec; rest = s; }
8932 if (*rest == '.' || *rest == '/') {
8935 *rest && !isspace(*rest) && cp2 - resspec < sizeof resspec;
8936 rest++, cp2++) *cp2 = *rest;
8938 if (do_tovmsspec(resspec,cp,0,NULL)) {
8941 for (cp2 = vmsspec + strlen(vmsspec);
8942 *rest && cp2 - vmsspec < sizeof vmsspec;
8943 rest++, cp2++) *cp2 = *rest;
8948 /* Intuit whether verb (first word of cmd) is a DCL command:
8949 * - if first nonspace char is '@', it's a DCL indirection
8951 * - if verb contains a filespec separator, it's not a DCL command
8952 * - if it doesn't, caller tells us whether to default to a DCL
8953 * command, or to a local image unless told it's DCL (by leading '$')
8957 if (suggest_quote) *suggest_quote = 1;
8959 register char *filespec = strpbrk(s,":<[.;");
8960 rest = wordbreak = strpbrk(s," \"\t/");
8961 if (!wordbreak) wordbreak = s + strlen(s);
8962 if (*s == '$') check_img = 0;
8963 if (filespec && (filespec < wordbreak)) isdcl = 0;
8964 else isdcl = !check_img;
8969 imgdsc.dsc$a_pointer = s;
8970 imgdsc.dsc$w_length = wordbreak - s;
8971 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
8973 _ckvmssts(lib$find_file_end(&cxt));
8974 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
8975 if (!(retsts & 1) && *s == '$') {
8976 _ckvmssts(lib$find_file_end(&cxt));
8977 imgdsc.dsc$a_pointer++; imgdsc.dsc$w_length--;
8978 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
8980 _ckvmssts(lib$find_file_end(&cxt));
8981 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
8985 _ckvmssts(lib$find_file_end(&cxt));
8990 while (*s && !isspace(*s)) s++;
8993 /* check that it's really not DCL with no file extension */
8994 fp = fopen(resspec,"r","ctx=bin","ctx=rec","shr=get");
8996 char b[256] = {0,0,0,0};
8997 read(fileno(fp), b, 256);
8998 isdcl = isprint(b[0]) && isprint(b[1]) && isprint(b[2]) && isprint(b[3]);
9002 /* Check for script */
9004 if ((b[0] == '#') && (b[1] == '!'))
9006 #ifdef ALTERNATE_SHEBANG
9008 shebang_len = strlen(ALTERNATE_SHEBANG);
9009 if (strncmp(b, ALTERNATE_SHEBANG, shebang_len) == 0) {
9011 perlstr = strstr("perl",b);
9012 if (perlstr == NULL)
9020 if (shebang_len > 0) {
9023 char tmpspec[NAM$C_MAXRSS + 1];
9026 /* Image is following after white space */
9027 /*--------------------------------------*/
9028 while (isprint(b[i]) && isspace(b[i]))
9032 while (isprint(b[i]) && !isspace(b[i])) {
9033 tmpspec[j++] = b[i++];
9034 if (j >= NAM$C_MAXRSS)
9039 /* There may be some default parameters to the image */
9040 /*---------------------------------------------------*/
9042 while (isprint(b[i])) {
9043 image_argv[j++] = b[i++];
9044 if (j >= NAM$C_MAXRSS)
9047 while ((j > 0) && !isprint(image_argv[j-1]))
9051 /* It will need to be converted to VMS format and validated */
9052 if (tmpspec[0] != '\0') {
9055 /* Try to find the exact program requested to be run */
9056 /*---------------------------------------------------*/
9057 iname = do_rmsexpand
9058 (tmpspec, image_name, 0, ".exe",
9059 PERL_RMSEXPAND_M_VMS, NULL, NULL);
9060 if (iname != NULL) {
9061 if (cando_by_name_int
9062 (S_IXUSR,0,image_name,PERL_RMSEXPAND_M_VMS_IN)) {
9063 /* MCR prefix needed */
9067 /* Try again with a null type */
9068 /*----------------------------*/
9069 iname = do_rmsexpand
9070 (tmpspec, image_name, 0, ".",
9071 PERL_RMSEXPAND_M_VMS, NULL, NULL);
9072 if (iname != NULL) {
9073 if (cando_by_name_int
9074 (S_IXUSR,0,image_name, PERL_RMSEXPAND_M_VMS_IN)) {
9075 /* MCR prefix needed */
9081 /* Did we find the image to run the script? */
9082 /*------------------------------------------*/
9086 /* Assume DCL or foreign command exists */
9087 /*--------------------------------------*/
9088 tchr = strrchr(tmpspec, '/');
9095 strcpy(image_name, tchr);
9103 if (check_img && isdcl) return RMS$_FNF;
9105 if (cando_by_name(S_IXUSR,0,resspec)) {
9106 vmscmd->dsc$a_pointer = PerlMem_malloc(MAX_DCL_LINE_LENGTH);
9107 if (vmscmd->dsc$a_pointer == NULL) _ckvmssts(SS$_INSFMEM);
9109 strcpy(vmscmd->dsc$a_pointer,"$ MCR ");
9110 if (image_name[0] != 0) {
9111 strcat(vmscmd->dsc$a_pointer, image_name);
9112 strcat(vmscmd->dsc$a_pointer, " ");
9114 } else if (image_name[0] != 0) {
9115 strcpy(vmscmd->dsc$a_pointer, image_name);
9116 strcat(vmscmd->dsc$a_pointer, " ");
9118 strcpy(vmscmd->dsc$a_pointer,"@");
9120 if (suggest_quote) *suggest_quote = 1;
9122 /* If there is an image name, use original command */
9123 if (image_name[0] == 0)
9124 strcat(vmscmd->dsc$a_pointer,resspec);
9127 while (*rest && isspace(*rest)) rest++;
9130 if (image_argv[0] != 0) {
9131 strcat(vmscmd->dsc$a_pointer,image_argv);
9132 strcat(vmscmd->dsc$a_pointer, " ");
9138 rest_len = strlen(rest);
9139 vmscmd_len = strlen(vmscmd->dsc$a_pointer);
9140 if ((rest_len + vmscmd_len) < MAX_DCL_LINE_LENGTH)
9141 strcat(vmscmd->dsc$a_pointer,rest);
9143 retsts = CLI$_BUFOVF;
9145 vmscmd->dsc$w_length = strlen(vmscmd->dsc$a_pointer);
9147 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
9153 /* It's either a DCL command or we couldn't find a suitable image */
9154 vmscmd->dsc$w_length = strlen(cmd);
9156 vmscmd->dsc$a_pointer = PerlMem_malloc(vmscmd->dsc$w_length + 1);
9157 strncpy(vmscmd->dsc$a_pointer,cmd,vmscmd->dsc$w_length);
9158 vmscmd->dsc$a_pointer[vmscmd->dsc$w_length] = 0;
9162 /* check if it's a symbol (for quoting purposes) */
9163 if (suggest_quote && !*suggest_quote) {
9165 char equiv[LNM$C_NAMLENGTH];
9166 struct dsc$descriptor_s eqvdsc = {sizeof(equiv), DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
9167 eqvdsc.dsc$a_pointer = equiv;
9169 iss = lib$get_symbol(vmscmd,&eqvdsc);
9170 if (iss&1 && (*equiv == '$' || *equiv == '@')) *suggest_quote = 1;
9172 if (!(retsts & 1)) {
9173 /* just hand off status values likely to be due to user error */
9174 if (retsts == RMS$_FNF || retsts == RMS$_DNF || retsts == RMS$_PRV ||
9175 retsts == RMS$_DEV || retsts == RMS$_DIR || retsts == RMS$_SYN ||
9176 (retsts & STS$M_CODE) == (SHR$_NOWILD & STS$M_CODE)) return retsts;
9177 else { _ckvmssts(retsts); }
9180 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
9182 } /* end of setup_cmddsc() */
9185 /* {{{ bool vms_do_aexec(SV *really,SV **mark,SV **sp) */
9187 Perl_vms_do_aexec(pTHX_ SV *really,SV **mark,SV **sp)
9193 if (vfork_called) { /* this follows a vfork - act Unixish */
9195 if (vfork_called < 0) {
9196 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
9199 else return do_aexec(really,mark,sp);
9201 /* no vfork - act VMSish */
9202 cmd = setup_argstr(aTHX_ really,mark,sp);
9203 exec_sts = vms_do_exec(cmd);
9204 Safefree(cmd); /* Clean up from setup_argstr() */
9209 } /* end of vms_do_aexec() */
9212 /* {{{bool vms_do_exec(char *cmd) */
9214 Perl_vms_do_exec(pTHX_ const char *cmd)
9216 struct dsc$descriptor_s *vmscmd;
9218 if (vfork_called) { /* this follows a vfork - act Unixish */
9220 if (vfork_called < 0) {
9221 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
9224 else return do_exec(cmd);
9227 { /* no vfork - act VMSish */
9228 unsigned long int retsts;
9231 TAINT_PROPER("exec");
9232 if ((retsts = setup_cmddsc(aTHX_ cmd,1,0,&vmscmd)) & 1)
9233 retsts = lib$do_command(vmscmd);
9236 case RMS$_FNF: case RMS$_DNF:
9237 set_errno(ENOENT); break;
9239 set_errno(ENOTDIR); break;
9241 set_errno(ENODEV); break;
9243 set_errno(EACCES); break;
9245 set_errno(EINVAL); break;
9246 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
9247 set_errno(E2BIG); break;
9248 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
9249 _ckvmssts(retsts); /* fall through */
9250 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
9253 set_vaxc_errno(retsts);
9254 if (ckWARN(WARN_EXEC)) {
9255 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't exec \"%*s\": %s",
9256 vmscmd->dsc$w_length, vmscmd->dsc$a_pointer, Strerror(errno));
9258 vms_execfree(vmscmd);
9263 } /* end of vms_do_exec() */
9266 unsigned long int Perl_do_spawn(pTHX_ const char *);
9268 /* {{{ unsigned long int do_aspawn(void *really,void **mark,void **sp) */
9270 Perl_do_aspawn(pTHX_ void *really,void **mark,void **sp)
9272 unsigned long int sts;
9276 cmd = setup_argstr(aTHX_ (SV *)really,(SV **)mark,(SV **)sp);
9277 sts = do_spawn(cmd);
9278 /* pp_sys will clean up cmd */
9282 } /* end of do_aspawn() */
9285 /* {{{unsigned long int do_spawn(char *cmd) */
9287 Perl_do_spawn(pTHX_ const char *cmd)
9289 unsigned long int sts, substs;
9291 /* The caller of this routine expects to Safefree(PL_Cmd) */
9292 Newx(PL_Cmd,10,char);
9295 TAINT_PROPER("spawn");
9296 if (!cmd || !*cmd) {
9297 sts = lib$spawn(0,0,0,0,0,0,&substs,0,0,0,0,0,0);
9300 case RMS$_FNF: case RMS$_DNF:
9301 set_errno(ENOENT); break;
9303 set_errno(ENOTDIR); break;
9305 set_errno(ENODEV); break;
9307 set_errno(EACCES); break;
9309 set_errno(EINVAL); break;
9310 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
9311 set_errno(E2BIG); break;
9312 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
9313 _ckvmssts(sts); /* fall through */
9314 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
9317 set_vaxc_errno(sts);
9318 if (ckWARN(WARN_EXEC)) {
9319 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't spawn: %s",
9327 fp = safe_popen(aTHX_ cmd, "nW", (int *)&sts);
9332 } /* end of do_spawn() */
9336 static unsigned int *sockflags, sockflagsize;
9339 * Shim fdopen to identify sockets for my_fwrite later, since the stdio
9340 * routines found in some versions of the CRTL can't deal with sockets.
9341 * We don't shim the other file open routines since a socket isn't
9342 * likely to be opened by a name.
9344 /*{{{ FILE *my_fdopen(int fd, const char *mode)*/
9345 FILE *my_fdopen(int fd, const char *mode)
9347 FILE *fp = fdopen(fd, mode);
9350 unsigned int fdoff = fd / sizeof(unsigned int);
9351 Stat_t sbuf; /* native stat; we don't need flex_stat */
9352 if (!sockflagsize || fdoff > sockflagsize) {
9353 if (sockflags) Renew( sockflags,fdoff+2,unsigned int);
9354 else Newx (sockflags,fdoff+2,unsigned int);
9355 memset(sockflags+sockflagsize,0,fdoff + 2 - sockflagsize);
9356 sockflagsize = fdoff + 2;
9358 if (fstat(fd, (struct stat *)&sbuf) == 0 && S_ISSOCK(sbuf.st_mode))
9359 sockflags[fdoff] |= 1 << (fd % sizeof(unsigned int));
9368 * Clear the corresponding bit when the (possibly) socket stream is closed.
9369 * There still a small hole: we miss an implicit close which might occur
9370 * via freopen(). >> Todo
9372 /*{{{ int my_fclose(FILE *fp)*/
9373 int my_fclose(FILE *fp) {
9375 unsigned int fd = fileno(fp);
9376 unsigned int fdoff = fd / sizeof(unsigned int);
9378 if (sockflagsize && fdoff <= sockflagsize)
9379 sockflags[fdoff] &= ~(1 << fd % sizeof(unsigned int));
9387 * A simple fwrite replacement which outputs itmsz*nitm chars without
9388 * introducing record boundaries every itmsz chars.
9389 * We are using fputs, which depends on a terminating null. We may
9390 * well be writing binary data, so we need to accommodate not only
9391 * data with nulls sprinkled in the middle but also data with no null
9394 /*{{{ int my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)*/
9396 my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)
9398 register char *cp, *end, *cpd, *data;
9399 register unsigned int fd = fileno(dest);
9400 register unsigned int fdoff = fd / sizeof(unsigned int);
9402 int bufsize = itmsz * nitm + 1;
9404 if (fdoff < sockflagsize &&
9405 (sockflags[fdoff] | 1 << (fd % sizeof(unsigned int)))) {
9406 if (write(fd, src, itmsz * nitm) == EOF) return EOF;
9410 _ckvmssts_noperl(lib$get_vm(&bufsize, &data));
9411 memcpy( data, src, itmsz*nitm );
9412 data[itmsz*nitm] = '\0';
9414 end = data + itmsz * nitm;
9415 retval = (int) nitm; /* on success return # items written */
9418 while (cpd <= end) {
9419 for (cp = cpd; cp <= end; cp++) if (!*cp) break;
9420 if (fputs(cpd,dest) == EOF) { retval = EOF; break; }
9422 if (fputc('\0',dest) == EOF) { retval = EOF; break; }
9426 if (data) _ckvmssts_noperl(lib$free_vm(&bufsize, &data));
9429 } /* end of my_fwrite() */
9432 /*{{{ int my_flush(FILE *fp)*/
9434 Perl_my_flush(pTHX_ FILE *fp)
9437 if ((res = fflush(fp)) == 0 && fp) {
9438 #ifdef VMS_DO_SOCKETS
9440 if (Fstat(fileno(fp), &s) == 0 && !S_ISSOCK(s.st_mode))
9442 res = fsync(fileno(fp));
9445 * If the flush succeeded but set end-of-file, we need to clear
9446 * the error because our caller may check ferror(). BTW, this
9447 * probably means we just flushed an empty file.
9449 if (res == 0 && vaxc$errno == RMS$_EOF) clearerr(fp);
9456 * Here are replacements for the following Unix routines in the VMS environment:
9457 * getpwuid Get information for a particular UIC or UID
9458 * getpwnam Get information for a named user
9459 * getpwent Get information for each user in the rights database
9460 * setpwent Reset search to the start of the rights database
9461 * endpwent Finish searching for users in the rights database
9463 * getpwuid, getpwnam, and getpwent return a pointer to the passwd structure
9464 * (defined in pwd.h), which contains the following fields:-
9466 * char *pw_name; Username (in lower case)
9467 * char *pw_passwd; Hashed password
9468 * unsigned int pw_uid; UIC
9469 * unsigned int pw_gid; UIC group number
9470 * char *pw_unixdir; Default device/directory (VMS-style)
9471 * char *pw_gecos; Owner name
9472 * char *pw_dir; Default device/directory (Unix-style)
9473 * char *pw_shell; Default CLI name (eg. DCL)
9475 * If the specified user does not exist, getpwuid and getpwnam return NULL.
9477 * pw_uid is the full UIC (eg. what's returned by stat() in st_uid).
9478 * not the UIC member number (eg. what's returned by getuid()),
9479 * getpwuid() can accept either as input (if uid is specified, the caller's
9480 * UIC group is used), though it won't recognise gid=0.
9482 * Note that in VMS it is necessary to have GRPPRV or SYSPRV to return
9483 * information about other users in your group or in other groups, respectively.
9484 * If the required privilege is not available, then these routines fill only
9485 * the pw_name, pw_uid, and pw_gid fields (the others point to an empty
9488 * By Tim Adye (T.J.Adye@rl.ac.uk), 10th February 1995.
9491 /* sizes of various UAF record fields */
9492 #define UAI$S_USERNAME 12
9493 #define UAI$S_IDENT 31
9494 #define UAI$S_OWNER 31
9495 #define UAI$S_DEFDEV 31
9496 #define UAI$S_DEFDIR 63
9497 #define UAI$S_DEFCLI 31
9500 #define valid_uic(uic) ((uic).uic$v_format == UIC$K_UIC_FORMAT && \
9501 (uic).uic$v_member != UIC$K_WILD_MEMBER && \
9502 (uic).uic$v_group != UIC$K_WILD_GROUP)
9504 static char __empty[]= "";
9505 static struct passwd __passwd_empty=
9506 {(char *) __empty, (char *) __empty, 0, 0,
9507 (char *) __empty, (char *) __empty, (char *) __empty, (char *) __empty};
9508 static int contxt= 0;
9509 static struct passwd __pwdcache;
9510 static char __pw_namecache[UAI$S_IDENT+1];
9513 * This routine does most of the work extracting the user information.
9515 static int fillpasswd (pTHX_ const char *name, struct passwd *pwd)
9518 unsigned char length;
9519 char pw_gecos[UAI$S_OWNER+1];
9521 static union uicdef uic;
9523 unsigned char length;
9524 char pw_dir[UAI$S_DEFDEV+UAI$S_DEFDIR+1];
9527 unsigned char length;
9528 char unixdir[UAI$_DEFDEV+UAI$S_DEFDIR+1];
9531 unsigned char length;
9532 char pw_shell[UAI$S_DEFCLI+1];
9534 static char pw_passwd[UAI$S_PWD+1];
9536 static unsigned short lowner, luic, ldefdev, ldefdir, ldefcli, lpwd;
9537 struct dsc$descriptor_s name_desc;
9538 unsigned long int sts;
9540 static struct itmlst_3 itmlst[]= {
9541 {UAI$S_OWNER+1, UAI$_OWNER, &owner, &lowner},
9542 {sizeof(uic), UAI$_UIC, &uic, &luic},
9543 {UAI$S_DEFDEV+1, UAI$_DEFDEV, &defdev, &ldefdev},
9544 {UAI$S_DEFDIR+1, UAI$_DEFDIR, &defdir, &ldefdir},
9545 {UAI$S_DEFCLI+1, UAI$_DEFCLI, &defcli, &ldefcli},
9546 {UAI$S_PWD, UAI$_PWD, pw_passwd, &lpwd},
9547 {0, 0, NULL, NULL}};
9549 name_desc.dsc$w_length= strlen(name);
9550 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
9551 name_desc.dsc$b_class= DSC$K_CLASS_S;
9552 name_desc.dsc$a_pointer= (char *) name; /* read only pointer */
9554 /* Note that sys$getuai returns many fields as counted strings. */
9555 sts= sys$getuai(0, 0, &name_desc, &itmlst, 0, 0, 0);
9556 if (sts == SS$_NOSYSPRV || sts == SS$_NOGRPPRV || sts == RMS$_RNF) {
9557 set_vaxc_errno(sts); set_errno(sts == RMS$_RNF ? EINVAL : EACCES);
9559 else { _ckvmssts(sts); }
9560 if (!(sts & 1)) return 0; /* out here in case _ckvmssts() doesn't abort */
9562 if ((int) owner.length < lowner) lowner= (int) owner.length;
9563 if ((int) defdev.length < ldefdev) ldefdev= (int) defdev.length;
9564 if ((int) defdir.length < ldefdir) ldefdir= (int) defdir.length;
9565 if ((int) defcli.length < ldefcli) ldefcli= (int) defcli.length;
9566 memcpy(&defdev.pw_dir[ldefdev], &defdir.unixdir[0], ldefdir);
9567 owner.pw_gecos[lowner]= '\0';
9568 defdev.pw_dir[ldefdev+ldefdir]= '\0';
9569 defcli.pw_shell[ldefcli]= '\0';
9570 if (valid_uic(uic)) {
9571 pwd->pw_uid= uic.uic$l_uic;
9572 pwd->pw_gid= uic.uic$v_group;
9575 Perl_warn(aTHX_ "getpwnam returned invalid UIC %#o for user \"%s\"");
9576 pwd->pw_passwd= pw_passwd;
9577 pwd->pw_gecos= owner.pw_gecos;
9578 pwd->pw_dir= defdev.pw_dir;
9579 pwd->pw_unixdir= do_tounixpath(defdev.pw_dir, defdir.unixdir,1,NULL);
9580 pwd->pw_shell= defcli.pw_shell;
9581 if (pwd->pw_unixdir && pwd->pw_unixdir[0]) {
9583 ldir= strlen(pwd->pw_unixdir) - 1;
9584 if (pwd->pw_unixdir[ldir]=='/') pwd->pw_unixdir[ldir]= '\0';
9587 strcpy(pwd->pw_unixdir, pwd->pw_dir);
9588 if (!decc_efs_case_preserve)
9589 __mystrtolower(pwd->pw_unixdir);
9594 * Get information for a named user.
9596 /*{{{struct passwd *getpwnam(char *name)*/
9597 struct passwd *Perl_my_getpwnam(pTHX_ const char *name)
9599 struct dsc$descriptor_s name_desc;
9601 unsigned long int status, sts;
9603 __pwdcache = __passwd_empty;
9604 if (!fillpasswd(aTHX_ name, &__pwdcache)) {
9605 /* We still may be able to determine pw_uid and pw_gid */
9606 name_desc.dsc$w_length= strlen(name);
9607 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
9608 name_desc.dsc$b_class= DSC$K_CLASS_S;
9609 name_desc.dsc$a_pointer= (char *) name;
9610 if ((sts = sys$asctoid(&name_desc, &uic, 0)) == SS$_NORMAL) {
9611 __pwdcache.pw_uid= uic.uic$l_uic;
9612 __pwdcache.pw_gid= uic.uic$v_group;
9615 if (sts == SS$_NOSUCHID || sts == SS$_IVIDENT || sts == RMS$_PRV) {
9616 set_vaxc_errno(sts);
9617 set_errno(sts == RMS$_PRV ? EACCES : EINVAL);
9620 else { _ckvmssts(sts); }
9623 strncpy(__pw_namecache, name, sizeof(__pw_namecache));
9624 __pw_namecache[sizeof __pw_namecache - 1] = '\0';
9625 __pwdcache.pw_name= __pw_namecache;
9627 } /* end of my_getpwnam() */
9631 * Get information for a particular UIC or UID.
9632 * Called by my_getpwent with uid=-1 to list all users.
9634 /*{{{struct passwd *my_getpwuid(Uid_t uid)*/
9635 struct passwd *Perl_my_getpwuid(pTHX_ Uid_t uid)
9637 const $DESCRIPTOR(name_desc,__pw_namecache);
9638 unsigned short lname;
9640 unsigned long int status;
9642 if (uid == (unsigned int) -1) {
9644 status = sys$idtoasc(-1, &lname, &name_desc, &uic, 0, &contxt);
9645 if (status == SS$_NOSUCHID || status == RMS$_PRV) {
9646 set_vaxc_errno(status);
9647 set_errno(status == RMS$_PRV ? EACCES : EINVAL);
9651 else { _ckvmssts(status); }
9652 } while (!valid_uic (uic));
9656 if (!uic.uic$v_group)
9657 uic.uic$v_group= PerlProc_getgid();
9659 status = sys$idtoasc(uic.uic$l_uic, &lname, &name_desc, 0, 0, 0);
9660 else status = SS$_IVIDENT;
9661 if (status == SS$_IVIDENT || status == SS$_NOSUCHID ||
9662 status == RMS$_PRV) {
9663 set_vaxc_errno(status); set_errno(status == RMS$_PRV ? EACCES : EINVAL);
9666 else { _ckvmssts(status); }
9668 __pw_namecache[lname]= '\0';
9669 __mystrtolower(__pw_namecache);
9671 __pwdcache = __passwd_empty;
9672 __pwdcache.pw_name = __pw_namecache;
9674 /* Fill in the uid and gid in case fillpasswd can't (eg. no privilege).
9675 The identifier's value is usually the UIC, but it doesn't have to be,
9676 so if we can, we let fillpasswd update this. */
9677 __pwdcache.pw_uid = uic.uic$l_uic;
9678 __pwdcache.pw_gid = uic.uic$v_group;
9680 fillpasswd(aTHX_ __pw_namecache, &__pwdcache);
9683 } /* end of my_getpwuid() */
9687 * Get information for next user.
9689 /*{{{struct passwd *my_getpwent()*/
9690 struct passwd *Perl_my_getpwent(pTHX)
9692 return (my_getpwuid((unsigned int) -1));
9697 * Finish searching rights database for users.
9699 /*{{{void my_endpwent()*/
9700 void Perl_my_endpwent(pTHX)
9703 _ckvmssts(sys$finish_rdb(&contxt));
9709 #ifdef HOMEGROWN_POSIX_SIGNALS
9710 /* Signal handling routines, pulled into the core from POSIX.xs.
9712 * We need these for threads, so they've been rolled into the core,
9713 * rather than left in POSIX.xs.
9715 * (DRS, Oct 23, 1997)
9718 /* sigset_t is atomic under VMS, so these routines are easy */
9719 /*{{{int my_sigemptyset(sigset_t *) */
9720 int my_sigemptyset(sigset_t *set) {
9721 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
9727 /*{{{int my_sigfillset(sigset_t *)*/
9728 int my_sigfillset(sigset_t *set) {
9730 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
9731 for (i = 0; i < NSIG; i++) *set |= (1 << i);
9737 /*{{{int my_sigaddset(sigset_t *set, int sig)*/
9738 int my_sigaddset(sigset_t *set, int sig) {
9739 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
9740 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
9741 *set |= (1 << (sig - 1));
9747 /*{{{int my_sigdelset(sigset_t *set, int sig)*/
9748 int my_sigdelset(sigset_t *set, int sig) {
9749 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
9750 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
9751 *set &= ~(1 << (sig - 1));
9757 /*{{{int my_sigismember(sigset_t *set, int sig)*/
9758 int my_sigismember(sigset_t *set, int sig) {
9759 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
9760 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
9761 return *set & (1 << (sig - 1));
9766 /*{{{int my_sigprocmask(int how, sigset_t *set, sigset_t *oset)*/
9767 int my_sigprocmask(int how, sigset_t *set, sigset_t *oset) {
9770 /* If set and oset are both null, then things are badly wrong. Bail out. */
9771 if ((oset == NULL) && (set == NULL)) {
9772 set_errno(EFAULT); set_vaxc_errno(SS$_ACCVIO);
9776 /* If set's null, then we're just handling a fetch. */
9778 tempmask = sigblock(0);
9783 tempmask = sigsetmask(*set);
9786 tempmask = sigblock(*set);
9789 tempmask = sigblock(0);
9790 sigsetmask(*oset & ~tempmask);
9793 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
9798 /* Did they pass us an oset? If so, stick our holding mask into it */
9805 #endif /* HOMEGROWN_POSIX_SIGNALS */
9808 /* Used for UTC calculation in my_gmtime(), my_localtime(), my_time(),
9809 * my_utime(), and flex_stat(), all of which operate on UTC unless
9810 * VMSISH_TIMES is true.
9812 /* method used to handle UTC conversions:
9813 * 1 == CRTL gmtime(); 2 == SYS$TIMEZONE_DIFFERENTIAL; 3 == no correction
9815 static int gmtime_emulation_type;
9816 /* number of secs to add to UTC POSIX-style time to get local time */
9817 static long int utc_offset_secs;
9819 /* We #defined 'gmtime', 'localtime', and 'time' as 'my_gmtime' etc.
9820 * in vmsish.h. #undef them here so we can call the CRTL routines
9829 * DEC C previous to 6.0 corrupts the behavior of the /prefix
9830 * qualifier with the extern prefix pragma. This provisional
9831 * hack circumvents this prefix pragma problem in previous
9834 #if defined(__VMS_VER) && __VMS_VER >= 70000000
9835 # if defined(VMS_WE_ARE_CASE_SENSITIVE) && (__DECC_VER < 60000000)
9836 # pragma __extern_prefix save
9837 # pragma __extern_prefix "" /* set to empty to prevent prefixing */
9838 # define gmtime decc$__utctz_gmtime
9839 # define localtime decc$__utctz_localtime
9840 # define time decc$__utc_time
9841 # pragma __extern_prefix restore
9843 struct tm *gmtime(), *localtime();
9849 static time_t toutc_dst(time_t loc) {
9852 if ((rsltmp = localtime(&loc)) == NULL) return -1;
9853 loc -= utc_offset_secs;
9854 if (rsltmp->tm_isdst) loc -= 3600;
9857 #define _toutc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
9858 ((gmtime_emulation_type || my_time(NULL)), \
9859 (gmtime_emulation_type == 1 ? toutc_dst(secs) : \
9860 ((secs) - utc_offset_secs))))
9862 static time_t toloc_dst(time_t utc) {
9865 utc += utc_offset_secs;
9866 if ((rsltmp = localtime(&utc)) == NULL) return -1;
9867 if (rsltmp->tm_isdst) utc += 3600;
9870 #define _toloc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
9871 ((gmtime_emulation_type || my_time(NULL)), \
9872 (gmtime_emulation_type == 1 ? toloc_dst(secs) : \
9873 ((secs) + utc_offset_secs))))
9875 #ifndef RTL_USES_UTC
9878 ucx$tz = "EST5EDT4,M4.1.0,M10.5.0" typical
9879 DST starts on 1st sun of april at 02:00 std time
9880 ends on last sun of october at 02:00 dst time
9881 see the UCX management command reference, SET CONFIG TIMEZONE
9882 for formatting info.
9884 No, it's not as general as it should be, but then again, NOTHING
9885 will handle UK times in a sensible way.
9890 parse the DST start/end info:
9891 (Jddd|ddd|Mmon.nth.dow)[/hh:mm:ss]
9895 tz_parse_startend(char *s, struct tm *w, int *past)
9897 int dinm[] = {31,28,31,30,31,30,31,31,30,31,30,31};
9898 int ly, dozjd, d, m, n, hour, min, sec, j, k;
9903 if (!past) return 0;
9906 if (w->tm_year % 4 == 0) ly = 1;
9907 if (w->tm_year % 100 == 0) ly = 0;
9908 if (w->tm_year+1900 % 400 == 0) ly = 1;
9911 dozjd = isdigit(*s);
9912 if (*s == 'J' || *s == 'j' || dozjd) {
9913 if (!dozjd && !isdigit(*++s)) return 0;
9916 d = d*10 + *s++ - '0';
9918 d = d*10 + *s++ - '0';
9921 if (d == 0) return 0;
9922 if (d > 366) return 0;
9924 if (!dozjd && d > 58 && ly) d++; /* after 28 feb */
9927 } else if (*s == 'M' || *s == 'm') {
9928 if (!isdigit(*++s)) return 0;
9930 if (isdigit(*s)) m = 10*m + *s++ - '0';
9931 if (*s != '.') return 0;
9932 if (!isdigit(*++s)) return 0;
9934 if (n < 1 || n > 5) return 0;
9935 if (*s != '.') return 0;
9936 if (!isdigit(*++s)) return 0;
9938 if (d > 6) return 0;
9942 if (!isdigit(*++s)) return 0;
9944 if (isdigit(*s)) hour = 10*hour + *s++ - '0';
9946 if (!isdigit(*++s)) return 0;
9948 if (isdigit(*s)) min = 10*min + *s++ - '0';
9950 if (!isdigit(*++s)) return 0;
9952 if (isdigit(*s)) sec = 10*sec + *s++ - '0';
9962 if (w->tm_yday < d) goto before;
9963 if (w->tm_yday > d) goto after;
9965 if (w->tm_mon+1 < m) goto before;
9966 if (w->tm_mon+1 > m) goto after;
9968 j = (42 + w->tm_wday - w->tm_mday)%7; /*dow of mday 0 */
9969 k = d - j; /* mday of first d */
9971 k += 7 * ((n>4?4:n)-1); /* mday of n'th d */
9972 if (n == 5 && k+7 <= dinm[w->tm_mon]) k += 7;
9973 if (w->tm_mday < k) goto before;
9974 if (w->tm_mday > k) goto after;
9977 if (w->tm_hour < hour) goto before;
9978 if (w->tm_hour > hour) goto after;
9979 if (w->tm_min < min) goto before;
9980 if (w->tm_min > min) goto after;
9981 if (w->tm_sec < sec) goto before;
9995 /* parse the offset: (+|-)hh[:mm[:ss]] */
9998 tz_parse_offset(char *s, int *offset)
10000 int hour = 0, min = 0, sec = 0;
10003 if (!offset) return 0;
10005 if (*s == '-') {neg++; s++;}
10006 if (*s == '+') s++;
10007 if (!isdigit(*s)) return 0;
10009 if (isdigit(*s)) hour = hour*10+(*s++ - '0');
10010 if (hour > 24) return 0;
10012 if (!isdigit(*++s)) return 0;
10014 if (isdigit(*s)) min = min*10 + (*s++ - '0');
10015 if (min > 59) return 0;
10017 if (!isdigit(*++s)) return 0;
10019 if (isdigit(*s)) sec = sec*10 + (*s++ - '0');
10020 if (sec > 59) return 0;
10024 *offset = (hour*60+min)*60 + sec;
10025 if (neg) *offset = -*offset;
10030 input time is w, whatever type of time the CRTL localtime() uses.
10031 sets dst, the zone, and the gmtoff (seconds)
10033 caches the value of TZ and UCX$TZ env variables; note that
10034 my_setenv looks for these and sets a flag if they're changed
10037 We have to watch out for the "australian" case (dst starts in
10038 october, ends in april)...flagged by "reverse" and checked by
10039 scanning through the months of the previous year.
10044 tz_parse(pTHX_ time_t *w, int *dst, char *zone, int *gmtoff)
10049 char *dstzone, *tz, *s_start, *s_end;
10050 int std_off, dst_off, isdst;
10051 int y, dststart, dstend;
10052 static char envtz[1025]; /* longer than any logical, symbol, ... */
10053 static char ucxtz[1025];
10054 static char reversed = 0;
10060 reversed = -1; /* flag need to check */
10061 envtz[0] = ucxtz[0] = '\0';
10062 tz = my_getenv("TZ",0);
10063 if (tz) strcpy(envtz, tz);
10064 tz = my_getenv("UCX$TZ",0);
10065 if (tz) strcpy(ucxtz, tz);
10066 if (!envtz[0] && !ucxtz[0]) return 0; /* we give up */
10069 if (!*tz) tz = ucxtz;
10072 while (isalpha(*s)) s++;
10073 s = tz_parse_offset(s, &std_off);
10075 if (!*s) { /* no DST, hurray we're done! */
10081 while (isalpha(*s)) s++;
10082 s2 = tz_parse_offset(s, &dst_off);
10086 dst_off = std_off - 3600;
10089 if (!*s) { /* default dst start/end?? */
10090 if (tz != ucxtz) { /* if TZ tells zone only, UCX$TZ tells rule */
10091 s = strchr(ucxtz,',');
10093 if (!s || !*s) s = ",M4.1.0,M10.5.0"; /* we know we do dst, default rule */
10095 if (*s != ',') return 0;
10098 when = _toutc(when); /* convert to utc */
10099 when = when - std_off; /* convert to pseudolocal time*/
10101 w2 = localtime(&when);
10104 s = tz_parse_startend(s_start,w2,&dststart);
10106 if (*s != ',') return 0;
10109 when = _toutc(when); /* convert to utc */
10110 when = when - dst_off; /* convert to pseudolocal time*/
10111 w2 = localtime(&when);
10112 if (w2->tm_year != y) { /* spans a year, just check one time */
10113 when += dst_off - std_off;
10114 w2 = localtime(&when);
10117 s = tz_parse_startend(s_end,w2,&dstend);
10120 if (reversed == -1) { /* need to check if start later than end */
10124 if (when < 2*365*86400) {
10125 when += 2*365*86400;
10129 w2 =localtime(&when);
10130 when = when + (15 - w2->tm_yday) * 86400; /* jan 15 */
10132 for (j = 0; j < 12; j++) {
10133 w2 =localtime(&when);
10134 tz_parse_startend(s_start,w2,&ds);
10135 tz_parse_startend(s_end,w2,&de);
10136 if (ds != de) break;
10140 if (de && !ds) reversed = 1;
10143 isdst = dststart && !dstend;
10144 if (reversed) isdst = dststart || !dstend;
10147 if (dst) *dst = isdst;
10148 if (gmtoff) *gmtoff = isdst ? dst_off : std_off;
10149 if (isdst) tz = dstzone;
10151 while(isalpha(*tz)) *zone++ = *tz++;
10157 #endif /* !RTL_USES_UTC */
10159 /* my_time(), my_localtime(), my_gmtime()
10160 * By default traffic in UTC time values, using CRTL gmtime() or
10161 * SYS$TIMEZONE_DIFFERENTIAL to determine offset from local time zone.
10162 * Note: We need to use these functions even when the CRTL has working
10163 * UTC support, since they also handle C<use vmsish qw(times);>
10165 * Contributed by Chuck Lane <lane@duphy4.physics.drexel.edu>
10166 * Modified by Charles Bailey <bailey@newman.upenn.edu>
10169 /*{{{time_t my_time(time_t *timep)*/
10170 time_t Perl_my_time(pTHX_ time_t *timep)
10175 if (gmtime_emulation_type == 0) {
10177 time_t base = 15 * 86400; /* 15jan71; to avoid month/year ends between */
10178 /* results of calls to gmtime() and localtime() */
10179 /* for same &base */
10181 gmtime_emulation_type++;
10182 if ((tm_p = gmtime(&base)) == NULL) { /* CRTL gmtime() is a fake */
10183 char off[LNM$C_NAMLENGTH+1];;
10185 gmtime_emulation_type++;
10186 if (!vmstrnenv("SYS$TIMEZONE_DIFFERENTIAL",off,0,fildev,0)) {
10187 gmtime_emulation_type++;
10188 utc_offset_secs = 0;
10189 Perl_warn(aTHX_ "no UTC offset information; assuming local time is UTC");
10191 else { utc_offset_secs = atol(off); }
10193 else { /* We've got a working gmtime() */
10194 struct tm gmt, local;
10197 tm_p = localtime(&base);
10199 utc_offset_secs = (local.tm_mday - gmt.tm_mday) * 86400;
10200 utc_offset_secs += (local.tm_hour - gmt.tm_hour) * 3600;
10201 utc_offset_secs += (local.tm_min - gmt.tm_min) * 60;
10202 utc_offset_secs += (local.tm_sec - gmt.tm_sec);
10207 # ifdef VMSISH_TIME
10208 # ifdef RTL_USES_UTC
10209 if (VMSISH_TIME) when = _toloc(when);
10211 if (!VMSISH_TIME) when = _toutc(when);
10214 if (timep != NULL) *timep = when;
10217 } /* end of my_time() */
10221 /*{{{struct tm *my_gmtime(const time_t *timep)*/
10223 Perl_my_gmtime(pTHX_ const time_t *timep)
10229 if (timep == NULL) {
10230 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
10233 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
10236 # ifdef VMSISH_TIME
10237 if (VMSISH_TIME) when = _toutc(when); /* Input was local time */
10239 # ifdef RTL_USES_UTC /* this implies that the CRTL has a working gmtime() */
10240 return gmtime(&when);
10242 /* CRTL localtime() wants local time as input, so does no tz correction */
10243 rsltmp = localtime(&when);
10244 if (rsltmp) rsltmp->tm_isdst = 0; /* We already took DST into account */
10247 } /* end of my_gmtime() */
10251 /*{{{struct tm *my_localtime(const time_t *timep)*/
10253 Perl_my_localtime(pTHX_ const time_t *timep)
10255 time_t when, whenutc;
10259 if (timep == NULL) {
10260 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
10263 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
10264 if (gmtime_emulation_type == 0) my_time(NULL); /* Init UTC */
10267 # ifdef RTL_USES_UTC
10268 # ifdef VMSISH_TIME
10269 if (VMSISH_TIME) when = _toutc(when);
10271 /* CRTL localtime() wants UTC as input, does tz correction itself */
10272 return localtime(&when);
10274 # else /* !RTL_USES_UTC */
10276 # ifdef VMSISH_TIME
10277 if (!VMSISH_TIME) when = _toloc(whenutc); /* input was UTC */
10278 if (VMSISH_TIME) whenutc = _toutc(when); /* input was truelocal */
10281 #ifndef RTL_USES_UTC
10282 if (tz_parse(aTHX_ &when, &dst, 0, &offset)) { /* truelocal determines DST*/
10283 when = whenutc - offset; /* pseudolocal time*/
10286 /* CRTL localtime() wants local time as input, so does no tz correction */
10287 rsltmp = localtime(&when);
10288 if (rsltmp && gmtime_emulation_type != 1) rsltmp->tm_isdst = dst;
10292 } /* end of my_localtime() */
10295 /* Reset definitions for later calls */
10296 #define gmtime(t) my_gmtime(t)
10297 #define localtime(t) my_localtime(t)
10298 #define time(t) my_time(t)
10301 /* my_utime - update modification/access time of a file
10303 * VMS 7.3 and later implementation
10304 * Only the UTC translation is home-grown. The rest is handled by the
10305 * CRTL utime(), which will take into account the relevant feature
10306 * logicals and ODS-5 volume characteristics for true access times.
10308 * pre VMS 7.3 implementation:
10309 * The calling sequence is identical to POSIX utime(), but under
10310 * VMS with ODS-2, only the modification time is changed; ODS-2 does
10311 * not maintain access times. Restrictions differ from the POSIX
10312 * definition in that the time can be changed as long as the
10313 * caller has permission to execute the necessary IO$_MODIFY $QIO;
10314 * no separate checks are made to insure that the caller is the
10315 * owner of the file or has special privs enabled.
10316 * Code here is based on Joe Meadows' FILE utility.
10320 /* Adjustment from Unix epoch (01-JAN-1970 00:00:00.00)
10321 * to VMS epoch (01-JAN-1858 00:00:00.00)
10322 * in 100 ns intervals.
10324 static const long int utime_baseadjust[2] = { 0x4beb4000, 0x7c9567 };
10326 /*{{{int my_utime(const char *path, const struct utimbuf *utimes)*/
10327 int Perl_my_utime(pTHX_ const char *file, const struct utimbuf *utimes)
10329 #if __CRTL_VER >= 70300000
10330 struct utimbuf utc_utimes, *utc_utimesp;
10332 if (utimes != NULL) {
10333 utc_utimes.actime = utimes->actime;
10334 utc_utimes.modtime = utimes->modtime;
10335 # ifdef VMSISH_TIME
10336 /* If input was local; convert to UTC for sys svc */
10338 utc_utimes.actime = _toutc(utimes->actime);
10339 utc_utimes.modtime = _toutc(utimes->modtime);
10342 utc_utimesp = &utc_utimes;
10345 utc_utimesp = NULL;
10348 return utime(file, utc_utimesp);
10350 #else /* __CRTL_VER < 70300000 */
10354 long int bintime[2], len = 2, lowbit, unixtime,
10355 secscale = 10000000; /* seconds --> 100 ns intervals */
10356 unsigned long int chan, iosb[2], retsts;
10357 char vmsspec[NAM$C_MAXRSS+1], rsa[NAM$C_MAXRSS], esa[NAM$C_MAXRSS];
10358 struct FAB myfab = cc$rms_fab;
10359 struct NAM mynam = cc$rms_nam;
10360 #if defined (__DECC) && defined (__VAX)
10361 /* VAX DEC C atrdef.h has unsigned type for pointer member atr$l_addr,
10362 * at least through VMS V6.1, which causes a type-conversion warning.
10364 # pragma message save
10365 # pragma message disable cvtdiftypes
10367 struct atrdef myatr[2] = {{sizeof bintime, ATR$C_REVDATE, bintime}, {0,0,0}};
10368 struct fibdef myfib;
10369 #if defined (__DECC) && defined (__VAX)
10370 /* This should be right after the declaration of myatr, but due
10371 * to a bug in VAX DEC C, this takes effect a statement early.
10373 # pragma message restore
10375 /* cast ok for read only parameter */
10376 struct dsc$descriptor fibdsc = {sizeof(myfib), DSC$K_DTYPE_Z, DSC$K_CLASS_S,(char *) &myfib},
10377 devdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0},
10378 fnmdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0};
10380 if (file == NULL || *file == '\0') {
10381 SETERRNO(ENOENT, LIB$_INVARG);
10385 /* Convert to VMS format ensuring that it will fit in 255 characters */
10386 if (do_rmsexpand(file, vmsspec, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL) == NULL) {
10387 SETERRNO(ENOENT, LIB$_INVARG);
10390 if (utimes != NULL) {
10391 /* Convert Unix time (seconds since 01-JAN-1970 00:00:00.00)
10392 * to VMS quadword time (100 nsec intervals since 01-JAN-1858 00:00:00.00).
10393 * Since time_t is unsigned long int, and lib$emul takes a signed long int
10394 * as input, we force the sign bit to be clear by shifting unixtime right
10395 * one bit, then multiplying by an extra factor of 2 in lib$emul().
10397 lowbit = (utimes->modtime & 1) ? secscale : 0;
10398 unixtime = (long int) utimes->modtime;
10399 # ifdef VMSISH_TIME
10400 /* If input was UTC; convert to local for sys svc */
10401 if (!VMSISH_TIME) unixtime = _toloc(unixtime);
10403 unixtime >>= 1; secscale <<= 1;
10404 retsts = lib$emul(&secscale, &unixtime, &lowbit, bintime);
10405 if (!(retsts & 1)) {
10406 SETERRNO(EVMSERR, retsts);
10409 retsts = lib$addx(bintime,utime_baseadjust,bintime,&len);
10410 if (!(retsts & 1)) {
10411 SETERRNO(EVMSERR, retsts);
10416 /* Just get the current time in VMS format directly */
10417 retsts = sys$gettim(bintime);
10418 if (!(retsts & 1)) {
10419 SETERRNO(EVMSERR, retsts);
10424 myfab.fab$l_fna = vmsspec;
10425 myfab.fab$b_fns = (unsigned char) strlen(vmsspec);
10426 myfab.fab$l_nam = &mynam;
10427 mynam.nam$l_esa = esa;
10428 mynam.nam$b_ess = (unsigned char) sizeof esa;
10429 mynam.nam$l_rsa = rsa;
10430 mynam.nam$b_rss = (unsigned char) sizeof rsa;
10431 if (decc_efs_case_preserve)
10432 mynam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
10434 /* Look for the file to be affected, letting RMS parse the file
10435 * specification for us as well. I have set errno using only
10436 * values documented in the utime() man page for VMS POSIX.
10438 retsts = sys$parse(&myfab,0,0);
10439 if (!(retsts & 1)) {
10440 set_vaxc_errno(retsts);
10441 if (retsts == RMS$_PRV) set_errno(EACCES);
10442 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
10443 else set_errno(EVMSERR);
10446 retsts = sys$search(&myfab,0,0);
10447 if (!(retsts & 1)) {
10448 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
10449 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
10450 set_vaxc_errno(retsts);
10451 if (retsts == RMS$_PRV) set_errno(EACCES);
10452 else if (retsts == RMS$_FNF) set_errno(ENOENT);
10453 else set_errno(EVMSERR);
10457 devdsc.dsc$w_length = mynam.nam$b_dev;
10458 /* cast ok for read only parameter */
10459 devdsc.dsc$a_pointer = (char *) mynam.nam$l_dev;
10461 retsts = sys$assign(&devdsc,&chan,0,0);
10462 if (!(retsts & 1)) {
10463 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
10464 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
10465 set_vaxc_errno(retsts);
10466 if (retsts == SS$_IVDEVNAM) set_errno(ENOTDIR);
10467 else if (retsts == SS$_NOPRIV) set_errno(EACCES);
10468 else if (retsts == SS$_NOSUCHDEV) set_errno(ENOTDIR);
10469 else set_errno(EVMSERR);
10473 fnmdsc.dsc$a_pointer = mynam.nam$l_name;
10474 fnmdsc.dsc$w_length = mynam.nam$b_name + mynam.nam$b_type + mynam.nam$b_ver;
10476 memset((void *) &myfib, 0, sizeof myfib);
10477 #if defined(__DECC) || defined(__DECCXX)
10478 for (i=0;i<3;i++) myfib.fib$w_fid[i] = mynam.nam$w_fid[i];
10479 for (i=0;i<3;i++) myfib.fib$w_did[i] = mynam.nam$w_did[i];
10480 /* This prevents the revision time of the file being reset to the current
10481 * time as a result of our IO$_MODIFY $QIO. */
10482 myfib.fib$l_acctl = FIB$M_NORECORD;
10484 for (i=0;i<3;i++) myfib.fib$r_fid_overlay.fib$w_fid[i] = mynam.nam$w_fid[i];
10485 for (i=0;i<3;i++) myfib.fib$r_did_overlay.fib$w_did[i] = mynam.nam$w_did[i];
10486 myfib.fib$r_acctl_overlay.fib$l_acctl = FIB$M_NORECORD;
10488 retsts = sys$qiow(0,chan,IO$_MODIFY,iosb,0,0,&fibdsc,&fnmdsc,0,0,myatr,0);
10489 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
10490 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
10491 _ckvmssts(sys$dassgn(chan));
10492 if (retsts & 1) retsts = iosb[0];
10493 if (!(retsts & 1)) {
10494 set_vaxc_errno(retsts);
10495 if (retsts == SS$_NOPRIV) set_errno(EACCES);
10496 else set_errno(EVMSERR);
10502 #endif /* #if __CRTL_VER >= 70300000 */
10504 } /* end of my_utime() */
10508 * flex_stat, flex_lstat, flex_fstat
10509 * basic stat, but gets it right when asked to stat
10510 * a Unix-style path ending in a directory name (e.g. dir1/dir2/dir3)
10513 #ifndef _USE_STD_STAT
10514 /* encode_dev packs a VMS device name string into an integer to allow
10515 * simple comparisons. This can be used, for example, to check whether two
10516 * files are located on the same device, by comparing their encoded device
10517 * names. Even a string comparison would not do, because stat() reuses the
10518 * device name buffer for each call; so without encode_dev, it would be
10519 * necessary to save the buffer and use strcmp (this would mean a number of
10520 * changes to the standard Perl code, to say nothing of what a Perl script
10521 * would have to do.
10523 * The device lock id, if it exists, should be unique (unless perhaps compared
10524 * with lock ids transferred from other nodes). We have a lock id if the disk is
10525 * mounted cluster-wide, which is when we tend to get long (host-qualified)
10526 * device names. Thus we use the lock id in preference, and only if that isn't
10527 * available, do we try to pack the device name into an integer (flagged by
10528 * the sign bit (LOCKID_MASK) being set).
10530 * Note that encode_dev cannot guarantee an 1-to-1 correspondence twixt device
10531 * name and its encoded form, but it seems very unlikely that we will find
10532 * two files on different disks that share the same encoded device names,
10533 * and even more remote that they will share the same file id (if the test
10534 * is to check for the same file).
10536 * A better method might be to use sys$device_scan on the first call, and to
10537 * search for the device, returning an index into the cached array.
10538 * The number returned would be more intelligible.
10539 * This is probably not worth it, and anyway would take quite a bit longer
10540 * on the first call.
10542 #define LOCKID_MASK 0x80000000 /* Use 0 to force device name use only */
10543 static mydev_t encode_dev (pTHX_ const char *dev)
10546 unsigned long int f;
10551 if (!dev || !dev[0]) return 0;
10555 struct dsc$descriptor_s dev_desc;
10556 unsigned long int status, lockid = 0, item = DVI$_LOCKID;
10558 /* For cluster-mounted disks, the disk lock identifier is unique, so we
10559 can try that first. */
10560 dev_desc.dsc$w_length = strlen (dev);
10561 dev_desc.dsc$b_dtype = DSC$K_DTYPE_T;
10562 dev_desc.dsc$b_class = DSC$K_CLASS_S;
10563 dev_desc.dsc$a_pointer = (char *) dev; /* Read only parameter */
10564 status = lib$getdvi(&item, 0, &dev_desc, &lockid, 0, 0);
10565 if (!$VMS_STATUS_SUCCESS(status)) {
10567 case SS$_NOSUCHDEV:
10568 SETERRNO(ENODEV, status);
10574 if (lockid) return (lockid & ~LOCKID_MASK);
10578 /* Otherwise we try to encode the device name */
10582 for (q = dev + strlen(dev); q--; q >= dev) {
10587 else if (isalpha (toupper (*q)))
10588 c= toupper (*q) - 'A' + (char)10;
10590 continue; /* Skip '$'s */
10592 if (i>6) break; /* 36^7 is too large to fit in an unsigned long int */
10594 enc += f * (unsigned long int) c;
10596 return (enc | LOCKID_MASK); /* May have already overflowed into bit 31 */
10598 } /* end of encode_dev() */
10599 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
10600 device_no = encode_dev(aTHX_ devname)
10602 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
10603 device_no = new_dev_no
10607 is_null_device(name)
10610 if (decc_bug_devnull != 0) {
10611 if (strncmp("/dev/null", name, 9) == 0)
10614 /* The VMS null device is named "_NLA0:", usually abbreviated as "NL:".
10615 The underscore prefix, controller letter, and unit number are
10616 independently optional; for our purposes, the colon punctuation
10617 is not. The colon can be trailed by optional directory and/or
10618 filename, but two consecutive colons indicates a nodename rather
10619 than a device. [pr] */
10620 if (*name == '_') ++name;
10621 if (tolower(*name++) != 'n') return 0;
10622 if (tolower(*name++) != 'l') return 0;
10623 if (tolower(*name) == 'a') ++name;
10624 if (*name == '0') ++name;
10625 return (*name++ == ':') && (*name != ':');
10630 Perl_cando_by_name_int
10631 (pTHX_ I32 bit, bool effective, const char *fname, int opts)
10633 static char usrname[L_cuserid];
10634 static struct dsc$descriptor_s usrdsc =
10635 {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, usrname};
10636 char vmsname[NAM$C_MAXRSS+1];
10638 unsigned long int objtyp = ACL$C_FILE, access, retsts, privused, iosb[2], flags;
10639 unsigned short int retlen, trnlnm_iter_count;
10640 struct dsc$descriptor_s namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10641 union prvdef curprv;
10642 struct itmlst_3 armlst[4] = {{sizeof access, CHP$_ACCESS, &access, &retlen},
10643 {sizeof privused, CHP$_PRIVUSED, &privused, &retlen},
10644 {sizeof flags, CHP$_FLAGS, &flags, &retlen},{0,0,0,0}};
10645 struct itmlst_3 jpilst[3] = {{sizeof curprv, JPI$_CURPRIV, &curprv, &retlen},
10646 {sizeof usrname, JPI$_USERNAME, &usrname, &usrdsc.dsc$w_length},
10648 struct itmlst_3 usrprolst[2] = {{sizeof curprv, CHP$_PRIV, &curprv, &retlen},
10650 struct dsc$descriptor_s usrprodsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10652 if (!fname || !*fname) return FALSE;
10653 /* Make sure we expand logical names, since sys$check_access doesn't */
10656 if ((opts & PERL_RMSEXPAND_M_VMS_IN) != 0) {
10657 fileified = PerlMem_malloc(VMS_MAXRSS);
10658 if (!strpbrk(fname,"/]>:")) {
10659 strcpy(fileified,fname);
10660 trnlnm_iter_count = 0;
10661 while (!strpbrk(fileified,"/]>:>") && my_trnlnm(fileified,fileified,0)) {
10662 trnlnm_iter_count++;
10663 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
10667 if (!do_rmsexpand(fname, vmsname, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL)) {
10668 PerlMem_free(fileified);
10671 retlen = namdsc.dsc$w_length = strlen(vmsname);
10672 namdsc.dsc$a_pointer = vmsname;
10673 if (vmsname[retlen-1] == ']' || vmsname[retlen-1] == '>' ||
10674 vmsname[retlen-1] == ':') {
10675 if (!do_fileify_dirspec(vmsname,fileified,1,NULL)) return FALSE;
10676 namdsc.dsc$w_length = strlen(fileified);
10677 namdsc.dsc$a_pointer = fileified;
10681 retlen = namdsc.dsc$w_length = strlen(fname);
10682 namdsc.dsc$a_pointer = (char *)fname; /* cast ok */
10686 case S_IXUSR: case S_IXGRP: case S_IXOTH:
10687 access = ARM$M_EXECUTE;
10688 flags = CHP$M_READ;
10690 case S_IRUSR: case S_IRGRP: case S_IROTH:
10691 access = ARM$M_READ;
10692 flags = CHP$M_READ | CHP$M_USEREADALL;
10694 case S_IWUSR: case S_IWGRP: case S_IWOTH:
10695 access = ARM$M_WRITE;
10696 flags = CHP$M_READ | CHP$M_WRITE;
10698 case S_IDUSR: case S_IDGRP: case S_IDOTH:
10699 access = ARM$M_DELETE;
10700 flags = CHP$M_READ | CHP$M_WRITE;
10703 if (fileified != NULL)
10704 PerlMem_free(fileified);
10708 /* Before we call $check_access, create a user profile with the current
10709 * process privs since otherwise it just uses the default privs from the
10710 * UAF and might give false positives or negatives. This only works on
10711 * VMS versions v6.0 and later since that's when sys$create_user_profile
10712 * became available.
10715 /* get current process privs and username */
10716 _ckvmssts(sys$getjpiw(0,0,0,jpilst,iosb,0,0));
10717 _ckvmssts(iosb[0]);
10719 #if defined(__VMS_VER) && __VMS_VER >= 60000000
10721 /* find out the space required for the profile */
10722 _ckvmssts(sys$create_user_profile(&usrdsc,&usrprolst,0,0,
10723 &usrprodsc.dsc$w_length,0));
10725 /* allocate space for the profile and get it filled in */
10726 usrprodsc.dsc$a_pointer = PerlMem_malloc(usrprodsc.dsc$w_length);
10727 if (usrprodsc.dsc$a_pointer == NULL) _ckvmssts(SS$_INSFMEM);
10728 _ckvmssts(sys$create_user_profile(&usrdsc,&usrprolst,0,usrprodsc.dsc$a_pointer,
10729 &usrprodsc.dsc$w_length,0));
10731 /* use the profile to check access to the file; free profile & analyze results */
10732 retsts = sys$check_access(&objtyp,&namdsc,0,armlst,0,0,0,&usrprodsc);
10733 PerlMem_free(usrprodsc.dsc$a_pointer);
10734 if (retsts == SS$_NOCALLPRIV) retsts = SS$_NOPRIV; /* not really 3rd party */
10738 retsts = sys$check_access(&objtyp,&namdsc,&usrdsc,armlst);
10742 if (retsts == SS$_NOPRIV || retsts == SS$_NOSUCHOBJECT ||
10743 retsts == SS$_INVFILFOROP || retsts == RMS$_FNF || retsts == RMS$_SYN ||
10744 retsts == RMS$_DIR || retsts == RMS$_DEV || retsts == RMS$_DNF) {
10745 set_vaxc_errno(retsts);
10746 if (retsts == SS$_NOPRIV) set_errno(EACCES);
10747 else if (retsts == SS$_INVFILFOROP) set_errno(EINVAL);
10748 else set_errno(ENOENT);
10749 if (fileified != NULL)
10750 PerlMem_free(fileified);
10753 if (retsts == SS$_NORMAL || retsts == SS$_ACCONFLICT) {
10754 if (fileified != NULL)
10755 PerlMem_free(fileified);
10760 if (fileified != NULL)
10761 PerlMem_free(fileified);
10762 return FALSE; /* Should never get here */
10766 /* Do the permissions allow some operation? Assumes PL_statcache already set. */
10767 /* Do this via $Check_Access on VMS, since the CRTL stat() returns only a
10768 * subset of the applicable information.
10771 Perl_cando(pTHX_ Mode_t bit, bool effective, const Stat_t *statbufp)
10773 return cando_by_name_int
10774 (bit, effective, statbufp->st_devnam, PERL_RMSEXPAND_M_VMS_IN);
10775 } /* end of cando() */
10779 /*{{{I32 cando_by_name(I32 bit, bool effective, char *fname)*/
10781 Perl_cando_by_name(pTHX_ I32 bit, bool effective, const char *fname)
10783 return cando_by_name_int(bit, effective, fname, 0);
10785 } /* end of cando_by_name() */
10789 /*{{{ int flex_fstat(int fd, Stat_t *statbuf)*/
10791 Perl_flex_fstat(pTHX_ int fd, Stat_t *statbufp)
10793 if (!fstat(fd,(stat_t *) statbufp)) {
10795 char *vms_filename;
10796 vms_filename = PerlMem_malloc(VMS_MAXRSS);
10797 if (vms_filename == NULL) _ckvmssts(SS$_INSFMEM);
10799 /* Save name for cando by name in VMS format */
10800 cptr = getname(fd, vms_filename, 1);
10802 /* This should not happen, but just in case */
10803 if (cptr == NULL) {
10804 statbufp->st_devnam[0] = 0;
10807 /* Make sure that the saved name fits in 255 characters */
10808 cptr = do_rmsexpand
10810 statbufp->st_devnam,
10813 PERL_RMSEXPAND_M_VMS | PERL_RMSEXPAND_M_VMS_IN,
10817 statbufp->st_devnam[0] = 0;
10819 PerlMem_free(vms_filename);
10821 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
10823 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
10825 # ifdef RTL_USES_UTC
10826 # ifdef VMSISH_TIME
10828 statbufp->st_mtime = _toloc(statbufp->st_mtime);
10829 statbufp->st_atime = _toloc(statbufp->st_atime);
10830 statbufp->st_ctime = _toloc(statbufp->st_ctime);
10834 # ifdef VMSISH_TIME
10835 if (!VMSISH_TIME) { /* Return UTC instead of local time */
10839 statbufp->st_mtime = _toutc(statbufp->st_mtime);
10840 statbufp->st_atime = _toutc(statbufp->st_atime);
10841 statbufp->st_ctime = _toutc(statbufp->st_ctime);
10848 } /* end of flex_fstat() */
10851 #if !defined(__VAX) && __CRTL_VER >= 80200000
10859 #define lstat(_x, _y) stat(_x, _y)
10862 #define flex_stat_int(a,b,c) Perl_flex_stat_int(aTHX_ a,b,c)
10865 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag)
10867 char fileified[VMS_MAXRSS];
10868 char temp_fspec[VMS_MAXRSS];
10871 int saved_errno, saved_vaxc_errno;
10873 if (!fspec) return retval;
10874 saved_errno = errno; saved_vaxc_errno = vaxc$errno;
10875 strcpy(temp_fspec, fspec);
10877 if (decc_bug_devnull != 0) {
10878 if (is_null_device(temp_fspec)) { /* Fake a stat() for the null device */
10879 memset(statbufp,0,sizeof *statbufp);
10880 VMS_DEVICE_ENCODE(statbufp->st_dev, "_NLA0:", 0);
10881 statbufp->st_mode = S_IFBLK | S_IREAD | S_IWRITE | S_IEXEC;
10882 statbufp->st_uid = 0x00010001;
10883 statbufp->st_gid = 0x0001;
10884 time((time_t *)&statbufp->st_mtime);
10885 statbufp->st_atime = statbufp->st_ctime = statbufp->st_mtime;
10890 /* Try for a directory name first. If fspec contains a filename without
10891 * a type (e.g. sea:[wine.dark]water), and both sea:[wine.dark]water.dir
10892 * and sea:[wine.dark]water. exist, we prefer the directory here.
10893 * Similarly, sea:[wine.dark] returns the result for sea:[wine]dark.dir,
10894 * not sea:[wine.dark]., if the latter exists. If the intended target is
10895 * the file with null type, specify this by calling flex_stat() with
10896 * a '.' at the end of fspec.
10898 * If we are in Posix filespec mode, accept the filename as is.
10900 #if __CRTL_VER >= 80200000 && !defined(__VAX)
10901 if (decc_posix_compliant_pathnames == 0) {
10903 if (do_fileify_dirspec(temp_fspec,fileified,0,NULL) != NULL) {
10904 if (lstat_flag == 0)
10905 retval = stat(fileified,(stat_t *) statbufp);
10907 retval = lstat(fileified,(stat_t *) statbufp);
10908 save_spec = fileified;
10911 if (lstat_flag == 0)
10912 retval = stat(temp_fspec,(stat_t *) statbufp);
10914 retval = lstat(temp_fspec,(stat_t *) statbufp);
10915 save_spec = temp_fspec;
10917 #if __CRTL_VER >= 80200000 && !defined(__VAX)
10919 if (lstat_flag == 0)
10920 retval = stat(temp_fspec,(stat_t *) statbufp);
10922 retval = lstat(temp_fspec,(stat_t *) statbufp);
10923 save_spec = temp_fspec;
10928 cptr = do_rmsexpand
10929 (save_spec, statbufp->st_devnam, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL);
10931 statbufp->st_devnam[0] = 0;
10933 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
10935 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
10936 # ifdef RTL_USES_UTC
10937 # ifdef VMSISH_TIME
10939 statbufp->st_mtime = _toloc(statbufp->st_mtime);
10940 statbufp->st_atime = _toloc(statbufp->st_atime);
10941 statbufp->st_ctime = _toloc(statbufp->st_ctime);
10945 # ifdef VMSISH_TIME
10946 if (!VMSISH_TIME) { /* Return UTC instead of local time */
10950 statbufp->st_mtime = _toutc(statbufp->st_mtime);
10951 statbufp->st_atime = _toutc(statbufp->st_atime);
10952 statbufp->st_ctime = _toutc(statbufp->st_ctime);
10956 /* If we were successful, leave errno where we found it */
10957 if (retval == 0) { errno = saved_errno; vaxc$errno = saved_vaxc_errno; }
10960 } /* end of flex_stat_int() */
10963 /*{{{ int flex_stat(const char *fspec, Stat_t *statbufp)*/
10965 Perl_flex_stat(pTHX_ const char *fspec, Stat_t *statbufp)
10967 return flex_stat_int(fspec, statbufp, 0);
10971 /*{{{ int flex_lstat(const char *fspec, Stat_t *statbufp)*/
10973 Perl_flex_lstat(pTHX_ const char *fspec, Stat_t *statbufp)
10975 return flex_stat_int(fspec, statbufp, 1);
10980 /*{{{char *my_getlogin()*/
10981 /* VMS cuserid == Unix getlogin, except calling sequence */
10985 static char user[L_cuserid];
10986 return cuserid(user);
10991 /* rmscopy - copy a file using VMS RMS routines
10993 * Copies contents and attributes of spec_in to spec_out, except owner
10994 * and protection information. Name and type of spec_in are used as
10995 * defaults for spec_out. The third parameter specifies whether rmscopy()
10996 * should try to propagate timestamps from the input file to the output file.
10997 * If it is less than 0, no timestamps are preserved. If it is 0, then
10998 * rmscopy() will behave similarly to the DCL COPY command: timestamps are
10999 * propagated to the output file at creation iff the output file specification
11000 * did not contain an explicit name or type, and the revision date is always
11001 * updated at the end of the copy operation. If it is greater than 0, then
11002 * it is interpreted as a bitmask, in which bit 0 indicates that timestamps
11003 * other than the revision date should be propagated, and bit 1 indicates
11004 * that the revision date should be propagated.
11006 * Returns 1 on success; returns 0 and sets errno and vaxc$errno on failure.
11008 * Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>.
11009 * Incorporates, with permission, some code from EZCOPY by Tim Adye
11010 * <T.J.Adye@rl.ac.uk>. Permission is given to distribute this code
11011 * as part of the Perl standard distribution under the terms of the
11012 * GNU General Public License or the Perl Artistic License. Copies
11013 * of each may be found in the Perl standard distribution.
11015 /*{{{int rmscopy(char *src, char *dst, int preserve_dates)*/
11017 Perl_rmscopy(pTHX_ const char *spec_in, const char *spec_out, int preserve_dates)
11019 char *vmsin, * vmsout, *esa, *esa_out,
11021 unsigned long int i, sts, sts2;
11023 struct FAB fab_in, fab_out;
11024 struct RAB rab_in, rab_out;
11025 rms_setup_nam(nam);
11026 rms_setup_nam(nam_out);
11027 struct XABDAT xabdat;
11028 struct XABFHC xabfhc;
11029 struct XABRDT xabrdt;
11030 struct XABSUM xabsum;
11032 vmsin = PerlMem_malloc(VMS_MAXRSS);
11033 if (vmsin == NULL) _ckvmssts(SS$_INSFMEM);
11034 vmsout = PerlMem_malloc(VMS_MAXRSS);
11035 if (vmsout == NULL) _ckvmssts(SS$_INSFMEM);
11036 if (!spec_in || !*spec_in || !do_tovmsspec(spec_in,vmsin,1,NULL) ||
11037 !spec_out || !*spec_out || !do_tovmsspec(spec_out,vmsout,1,NULL)) {
11038 PerlMem_free(vmsin);
11039 PerlMem_free(vmsout);
11040 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11044 esa = PerlMem_malloc(VMS_MAXRSS);
11045 if (esa == NULL) _ckvmssts(SS$_INSFMEM);
11046 fab_in = cc$rms_fab;
11047 rms_set_fna(fab_in, nam, vmsin, strlen(vmsin));
11048 fab_in.fab$b_shr = FAB$M_SHRPUT | FAB$M_UPI;
11049 fab_in.fab$b_fac = FAB$M_BIO | FAB$M_GET;
11050 fab_in.fab$l_fop = FAB$M_SQO;
11051 rms_bind_fab_nam(fab_in, nam);
11052 fab_in.fab$l_xab = (void *) &xabdat;
11054 rsa = PerlMem_malloc(VMS_MAXRSS);
11055 if (rsa == NULL) _ckvmssts(SS$_INSFMEM);
11056 rms_set_rsa(nam, rsa, (VMS_MAXRSS-1));
11057 rms_set_esa(fab_in, nam, esa, (VMS_MAXRSS-1));
11058 rms_nam_esl(nam) = 0;
11059 rms_nam_rsl(nam) = 0;
11060 rms_nam_esll(nam) = 0;
11061 rms_nam_rsll(nam) = 0;
11062 #ifdef NAM$M_NO_SHORT_UPCASE
11063 if (decc_efs_case_preserve)
11064 rms_set_nam_nop(nam, NAM$M_NO_SHORT_UPCASE);
11067 xabdat = cc$rms_xabdat; /* To get creation date */
11068 xabdat.xab$l_nxt = (void *) &xabfhc;
11070 xabfhc = cc$rms_xabfhc; /* To get record length */
11071 xabfhc.xab$l_nxt = (void *) &xabsum;
11073 xabsum = cc$rms_xabsum; /* To get key and area information */
11075 if (!((sts = sys$open(&fab_in)) & 1)) {
11076 PerlMem_free(vmsin);
11077 PerlMem_free(vmsout);
11080 set_vaxc_errno(sts);
11082 case RMS$_FNF: case RMS$_DNF:
11083 set_errno(ENOENT); break;
11085 set_errno(ENOTDIR); break;
11087 set_errno(ENODEV); break;
11089 set_errno(EINVAL); break;
11091 set_errno(EACCES); break;
11093 set_errno(EVMSERR);
11100 fab_out.fab$w_ifi = 0;
11101 fab_out.fab$b_fac = FAB$M_BIO | FAB$M_PUT;
11102 fab_out.fab$b_shr = FAB$M_SHRGET | FAB$M_UPI;
11103 fab_out.fab$l_fop = FAB$M_SQO;
11104 rms_bind_fab_nam(fab_out, nam_out);
11105 rms_set_fna(fab_out, nam_out, vmsout, strlen(vmsout));
11106 dna_len = rms_nam_namel(nam) ? rms_nam_name_type_l_size(nam) : 0;
11107 rms_set_dna(fab_out, nam_out, rms_nam_namel(nam), dna_len);
11108 esa_out = PerlMem_malloc(VMS_MAXRSS);
11109 if (esa_out == NULL) _ckvmssts(SS$_INSFMEM);
11110 rms_set_rsa(nam_out, NULL, 0);
11111 rms_set_esa(fab_out, nam_out, esa_out, (VMS_MAXRSS - 1));
11113 if (preserve_dates == 0) { /* Act like DCL COPY */
11114 rms_set_nam_nop(nam_out, NAM$M_SYNCHK);
11115 fab_out.fab$l_xab = NULL; /* Don't disturb data from input file */
11116 if (!((sts = sys$parse(&fab_out)) & STS$K_SUCCESS)) {
11117 PerlMem_free(vmsin);
11118 PerlMem_free(vmsout);
11121 PerlMem_free(esa_out);
11122 set_errno(sts == RMS$_SYN ? EINVAL : EVMSERR);
11123 set_vaxc_errno(sts);
11126 fab_out.fab$l_xab = (void *) &xabdat;
11127 if (rms_is_nam_fnb(nam, NAM$M_EXP_NAME | NAM$M_EXP_TYPE))
11128 preserve_dates = 1;
11130 if (preserve_dates < 0) /* Clear all bits; we'll use it as a */
11131 preserve_dates =0; /* bitmask from this point forward */
11133 if (!(preserve_dates & 1)) fab_out.fab$l_xab = (void *) &xabfhc;
11134 if (!((sts = sys$create(&fab_out)) & STS$K_SUCCESS)) {
11135 PerlMem_free(vmsin);
11136 PerlMem_free(vmsout);
11139 PerlMem_free(esa_out);
11140 set_vaxc_errno(sts);
11143 set_errno(ENOENT); break;
11145 set_errno(ENOTDIR); break;
11147 set_errno(ENODEV); break;
11149 set_errno(EINVAL); break;
11151 set_errno(EACCES); break;
11153 set_errno(EVMSERR);
11157 fab_out.fab$l_fop |= FAB$M_DLT; /* in case we have to bail out */
11158 if (preserve_dates & 2) {
11159 /* sys$close() will process xabrdt, not xabdat */
11160 xabrdt = cc$rms_xabrdt;
11162 xabrdt.xab$q_rdt = xabdat.xab$q_rdt;
11164 /* gcc doesn't like the assignment, since its prototype for xab$q_rdt
11165 * is unsigned long[2], while DECC & VAXC use a struct */
11166 memcpy(xabrdt.xab$q_rdt,xabdat.xab$q_rdt,sizeof xabrdt.xab$q_rdt);
11168 fab_out.fab$l_xab = (void *) &xabrdt;
11171 ubf = PerlMem_malloc(32256);
11172 if (ubf == NULL) _ckvmssts(SS$_INSFMEM);
11173 rab_in = cc$rms_rab;
11174 rab_in.rab$l_fab = &fab_in;
11175 rab_in.rab$l_rop = RAB$M_BIO;
11176 rab_in.rab$l_ubf = ubf;
11177 rab_in.rab$w_usz = 32256;
11178 if (!((sts = sys$connect(&rab_in)) & 1)) {
11179 sys$close(&fab_in); sys$close(&fab_out);
11180 PerlMem_free(vmsin);
11181 PerlMem_free(vmsout);
11185 PerlMem_free(esa_out);
11186 set_errno(EVMSERR); set_vaxc_errno(sts);
11190 rab_out = cc$rms_rab;
11191 rab_out.rab$l_fab = &fab_out;
11192 rab_out.rab$l_rbf = ubf;
11193 if (!((sts = sys$connect(&rab_out)) & 1)) {
11194 sys$close(&fab_in); sys$close(&fab_out);
11195 PerlMem_free(vmsin);
11196 PerlMem_free(vmsout);
11200 PerlMem_free(esa_out);
11201 set_errno(EVMSERR); set_vaxc_errno(sts);
11205 while ((sts = sys$read(&rab_in))) { /* always true */
11206 if (sts == RMS$_EOF) break;
11207 rab_out.rab$w_rsz = rab_in.rab$w_rsz;
11208 if (!(sts & 1) || !((sts = sys$write(&rab_out)) & 1)) {
11209 sys$close(&fab_in); sys$close(&fab_out);
11210 PerlMem_free(vmsin);
11211 PerlMem_free(vmsout);
11215 PerlMem_free(esa_out);
11216 set_errno(EVMSERR); set_vaxc_errno(sts);
11222 fab_out.fab$l_fop &= ~FAB$M_DLT; /* We got this far; keep the output */
11223 sys$close(&fab_in); sys$close(&fab_out);
11224 sts = (fab_in.fab$l_sts & 1) ? fab_out.fab$l_sts : fab_in.fab$l_sts;
11226 PerlMem_free(vmsin);
11227 PerlMem_free(vmsout);
11231 PerlMem_free(esa_out);
11232 set_errno(EVMSERR); set_vaxc_errno(sts);
11236 PerlMem_free(vmsin);
11237 PerlMem_free(vmsout);
11241 PerlMem_free(esa_out);
11244 } /* end of rmscopy() */
11248 /*** The following glue provides 'hooks' to make some of the routines
11249 * from this file available from Perl. These routines are sufficiently
11250 * basic, and are required sufficiently early in the build process,
11251 * that's it's nice to have them available to miniperl as well as the
11252 * full Perl, so they're set up here instead of in an extension. The
11253 * Perl code which handles importation of these names into a given
11254 * package lives in [.VMS]Filespec.pm in @INC.
11258 rmsexpand_fromperl(pTHX_ CV *cv)
11261 char *fspec, *defspec = NULL, *rslt;
11263 int fs_utf8, dfs_utf8;
11267 if (!items || items > 2)
11268 Perl_croak(aTHX_ "Usage: VMS::Filespec::rmsexpand(spec[,defspec])");
11269 fspec = SvPV(ST(0),n_a);
11270 fs_utf8 = SvUTF8(ST(0));
11271 if (!fspec || !*fspec) XSRETURN_UNDEF;
11273 defspec = SvPV(ST(1),n_a);
11274 dfs_utf8 = SvUTF8(ST(1));
11276 rslt = do_rmsexpand(fspec,NULL,1,defspec,0,&fs_utf8,&dfs_utf8);
11277 ST(0) = sv_newmortal();
11278 if (rslt != NULL) {
11279 sv_usepvn(ST(0),rslt,strlen(rslt));
11288 vmsify_fromperl(pTHX_ CV *cv)
11295 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsify(spec)");
11296 utf8_fl = SvUTF8(ST(0));
11297 vmsified = do_tovmsspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
11298 ST(0) = sv_newmortal();
11299 if (vmsified != NULL) {
11300 sv_usepvn(ST(0),vmsified,strlen(vmsified));
11309 unixify_fromperl(pTHX_ CV *cv)
11316 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixify(spec)");
11317 utf8_fl = SvUTF8(ST(0));
11318 unixified = do_tounixspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
11319 ST(0) = sv_newmortal();
11320 if (unixified != NULL) {
11321 sv_usepvn(ST(0),unixified,strlen(unixified));
11330 fileify_fromperl(pTHX_ CV *cv)
11337 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::fileify(spec)");
11338 utf8_fl = SvUTF8(ST(0));
11339 fileified = do_fileify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
11340 ST(0) = sv_newmortal();
11341 if (fileified != NULL) {
11342 sv_usepvn(ST(0),fileified,strlen(fileified));
11351 pathify_fromperl(pTHX_ CV *cv)
11358 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::pathify(spec)");
11359 utf8_fl = SvUTF8(ST(0));
11360 pathified = do_pathify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
11361 ST(0) = sv_newmortal();
11362 if (pathified != NULL) {
11363 sv_usepvn(ST(0),pathified,strlen(pathified));
11372 vmspath_fromperl(pTHX_ CV *cv)
11379 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmspath(spec)");
11380 utf8_fl = SvUTF8(ST(0));
11381 vmspath = do_tovmspath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
11382 ST(0) = sv_newmortal();
11383 if (vmspath != NULL) {
11384 sv_usepvn(ST(0),vmspath,strlen(vmspath));
11393 unixpath_fromperl(pTHX_ CV *cv)
11400 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixpath(spec)");
11401 utf8_fl = SvUTF8(ST(0));
11402 unixpath = do_tounixpath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
11403 ST(0) = sv_newmortal();
11404 if (unixpath != NULL) {
11405 sv_usepvn(ST(0),unixpath,strlen(unixpath));
11414 candelete_fromperl(pTHX_ CV *cv)
11422 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::candelete(spec)");
11424 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
11425 Newx(fspec, VMS_MAXRSS, char);
11426 if (fspec == NULL) _ckvmssts(SS$_INSFMEM);
11427 if (SvTYPE(mysv) == SVt_PVGV) {
11428 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),fspec)) {
11429 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11437 if (mysv != ST(0) || !(fsp = SvPV(mysv,n_a)) || !*fsp) {
11438 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11445 ST(0) = boolSV(cando_by_name(S_IDUSR,0,fsp));
11451 rmscopy_fromperl(pTHX_ CV *cv)
11454 char *inspec, *outspec, *inp, *outp;
11456 struct dsc$descriptor indsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
11457 outdsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
11458 unsigned long int sts;
11463 if (items < 2 || items > 3)
11464 Perl_croak(aTHX_ "Usage: File::Copy::rmscopy(from,to[,date_flag])");
11466 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
11467 Newx(inspec, VMS_MAXRSS, char);
11468 if (SvTYPE(mysv) == SVt_PVGV) {
11469 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),inspec)) {
11470 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11478 if (mysv != ST(0) || !(inp = SvPV(mysv,n_a)) || !*inp) {
11479 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11485 mysv = SvROK(ST(1)) ? SvRV(ST(1)) : ST(1);
11486 Newx(outspec, VMS_MAXRSS, char);
11487 if (SvTYPE(mysv) == SVt_PVGV) {
11488 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),outspec)) {
11489 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11498 if (mysv != ST(1) || !(outp = SvPV(mysv,n_a)) || !*outp) {
11499 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11506 date_flag = (items == 3) ? SvIV(ST(2)) : 0;
11508 ST(0) = boolSV(rmscopy(inp,outp,date_flag));
11514 /* The mod2fname is limited to shorter filenames by design, so it should
11515 * not be modified to support longer EFS pathnames
11518 mod2fname(pTHX_ CV *cv)
11521 char ultimate_name[NAM$C_MAXRSS+1], work_name[NAM$C_MAXRSS*8 + 1],
11522 workbuff[NAM$C_MAXRSS*1 + 1];
11523 int total_namelen = 3, counter, num_entries;
11524 /* ODS-5 ups this, but we want to be consistent, so... */
11525 int max_name_len = 39;
11526 AV *in_array = (AV *)SvRV(ST(0));
11528 num_entries = av_len(in_array);
11530 /* All the names start with PL_. */
11531 strcpy(ultimate_name, "PL_");
11533 /* Clean up our working buffer */
11534 Zero(work_name, sizeof(work_name), char);
11536 /* Run through the entries and build up a working name */
11537 for(counter = 0; counter <= num_entries; counter++) {
11538 /* If it's not the first name then tack on a __ */
11540 strcat(work_name, "__");
11542 strcat(work_name, SvPV(*av_fetch(in_array, counter, FALSE),
11546 /* Check to see if we actually have to bother...*/
11547 if (strlen(work_name) + 3 <= max_name_len) {
11548 strcat(ultimate_name, work_name);
11550 /* It's too darned big, so we need to go strip. We use the same */
11551 /* algorithm as xsubpp does. First, strip out doubled __ */
11552 char *source, *dest, last;
11555 for (source = work_name; *source; source++) {
11556 if (last == *source && last == '_') {
11562 /* Go put it back */
11563 strcpy(work_name, workbuff);
11564 /* Is it still too big? */
11565 if (strlen(work_name) + 3 > max_name_len) {
11566 /* Strip duplicate letters */
11569 for (source = work_name; *source; source++) {
11570 if (last == toupper(*source)) {
11574 last = toupper(*source);
11576 strcpy(work_name, workbuff);
11579 /* Is it *still* too big? */
11580 if (strlen(work_name) + 3 > max_name_len) {
11581 /* Too bad, we truncate */
11582 work_name[max_name_len - 2] = 0;
11584 strcat(ultimate_name, work_name);
11587 /* Okay, return it */
11588 ST(0) = sv_2mortal(newSVpv(ultimate_name, 0));
11593 hushexit_fromperl(pTHX_ CV *cv)
11598 VMSISH_HUSHED = SvTRUE(ST(0));
11600 ST(0) = boolSV(VMSISH_HUSHED);
11606 Perl_vms_start_glob
11607 (pTHX_ SV *tmpglob,
11611 struct vs_str_st *rslt;
11615 $DESCRIPTOR(dfltdsc,"SYS$DISK:[]*.*;");
11618 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
11619 struct dsc$descriptor_vs rsdsc;
11620 unsigned long int cxt = 0, sts = 0, ok = 1, hasdir = 0;
11621 unsigned long hasver = 0, isunix = 0;
11622 unsigned long int lff_flags = 0;
11625 #ifdef VMS_LONGNAME_SUPPORT
11626 lff_flags = LIB$M_FIL_LONG_NAMES;
11628 /* The Newx macro will not allow me to assign a smaller array
11629 * to the rslt pointer, so we will assign it to the begin char pointer
11630 * and then copy the value into the rslt pointer.
11632 Newx(begin, VMS_MAXRSS + sizeof(unsigned short int), char);
11633 rslt = (struct vs_str_st *)begin;
11635 rstr = &rslt->str[0];
11636 rsdsc.dsc$a_pointer = (char *) rslt; /* cast required */
11637 rsdsc.dsc$w_maxstrlen = VMS_MAXRSS + sizeof(unsigned short int);
11638 rsdsc.dsc$b_dtype = DSC$K_DTYPE_VT;
11639 rsdsc.dsc$b_class = DSC$K_CLASS_VS;
11641 Newx(vmsspec, VMS_MAXRSS, char);
11643 /* We could find out if there's an explicit dev/dir or version
11644 by peeking into lib$find_file's internal context at
11645 ((struct NAM *)((struct FAB *)cxt)->fab$l_nam)->nam$l_fnb
11646 but that's unsupported, so I don't want to do it now and
11647 have it bite someone in the future. */
11648 /* Fix-me: vms_split_path() is the only way to do this, the
11649 existing method will fail with many legal EFS or UNIX specifications
11652 cp = SvPV(tmpglob,i);
11655 if (cp[i] == ';') hasver = 1;
11656 if (cp[i] == '.') {
11657 if (sts) hasver = 1;
11660 if (cp[i] == '/') {
11661 hasdir = isunix = 1;
11664 if (cp[i] == ']' || cp[i] == '>' || cp[i] == ':') {
11669 if ((tmpfp = PerlIO_tmpfile()) != NULL) {
11672 stat_sts = PerlLIO_stat(SvPVX_const(tmpglob),&st);
11673 if (!stat_sts && S_ISDIR(st.st_mode)) {
11674 wilddsc.dsc$a_pointer = tovmspath_utf8(SvPVX(tmpglob),vmsspec,NULL);
11675 ok = (wilddsc.dsc$a_pointer != NULL);
11678 wilddsc.dsc$a_pointer = tovmsspec_utf8(SvPVX(tmpglob),vmsspec,NULL);
11679 ok = (wilddsc.dsc$a_pointer != NULL);
11682 wilddsc.dsc$w_length = strlen(wilddsc.dsc$a_pointer);
11684 /* If not extended character set, replace ? with % */
11685 /* With extended character set, ? is a wildcard single character */
11686 if (!decc_efs_case_preserve) {
11687 for (cp=wilddsc.dsc$a_pointer; ok && cp && *cp; cp++)
11688 if (*cp == '?') *cp = '%';
11691 while (ok && $VMS_STATUS_SUCCESS(sts)) {
11692 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
11693 int v_sts, v_len, r_len, d_len, n_len, e_len, vs_len;
11695 sts = lib$find_file(&wilddsc,&rsdsc,&cxt,
11696 &dfltdsc,NULL,&rms_sts,&lff_flags);
11697 if (!$VMS_STATUS_SUCCESS(sts))
11700 /* with varying string, 1st word of buffer contains result length */
11701 rstr[rslt->length] = '\0';
11703 /* Find where all the components are */
11704 v_sts = vms_split_path
11719 /* If no version on input, truncate the version on output */
11720 if (!hasver && (vs_len > 0)) {
11724 /* No version & a null extension on UNIX handling */
11725 if (isunix && (e_len == 1) && decc_readdir_dropdotnotype) {
11731 if (!decc_efs_case_preserve) {
11732 for (cp = rstr; *cp; cp++) *cp = _tolower(*cp);
11736 if (isunix) trim_unixpath(rstr,SvPVX(tmpglob),1);
11740 /* Start with the name */
11743 strcat(begin,"\n");
11744 ok = (PerlIO_puts(tmpfp,begin) != EOF);
11746 if (cxt) (void)lib$find_file_end(&cxt);
11747 if (ok && sts != RMS$_NMF &&
11748 sts != RMS$_DNF && sts != RMS_FNF) ok = 0;
11751 SETERRNO((sts == RMS$_SYN ? EINVAL : EVMSERR),sts);
11753 PerlIO_close(tmpfp);
11757 PerlIO_rewind(tmpfp);
11758 IoTYPE(io) = IoTYPE_RDONLY;
11759 IoIFP(io) = fp = tmpfp;
11760 IoFLAGS(io) &= ~IOf_UNTAINT; /* maybe redundant */
11770 mp_do_vms_realpath(pTHX_ const char *filespec, char * rslt_spec, const int *utf8_fl);
11773 vms_realpath_fromperl(pTHX_ CV *cv)
11776 char *fspec, *rslt_spec, *rslt;
11779 if (!items || items != 1)
11780 Perl_croak(aTHX_ "Usage: VMS::Filespec::vms_realpath(spec)");
11782 fspec = SvPV(ST(0),n_a);
11783 if (!fspec || !*fspec) XSRETURN_UNDEF;
11785 Newx(rslt_spec, VMS_MAXRSS + 1, char);
11786 rslt = do_vms_realpath(fspec, rslt_spec, NULL);
11787 ST(0) = sv_newmortal();
11789 sv_usepvn(ST(0),rslt,strlen(rslt));
11791 Safefree(rslt_spec);
11796 #if __CRTL_VER >= 70301000 && !defined(__VAX)
11797 int do_vms_case_tolerant(void);
11800 vms_case_tolerant_fromperl(pTHX_ CV *cv)
11803 ST(0) = boolSV(do_vms_case_tolerant());
11809 Perl_sys_intern_dup(pTHX_ struct interp_intern *src,
11810 struct interp_intern *dst)
11812 memcpy(dst,src,sizeof(struct interp_intern));
11816 Perl_sys_intern_clear(pTHX)
11821 Perl_sys_intern_init(pTHX)
11823 unsigned int ix = RAND_MAX;
11828 /* fix me later to track running under GNV */
11829 /* this allows some limited testing */
11830 MY_POSIX_EXIT = decc_filename_unix_report;
11833 MY_INV_RAND_MAX = 1./x;
11837 init_os_extras(void)
11840 char* file = __FILE__;
11841 if (decc_disable_to_vms_logname_translation) {
11842 no_translate_barewords = TRUE;
11844 no_translate_barewords = FALSE;
11847 newXSproto("VMS::Filespec::rmsexpand",rmsexpand_fromperl,file,"$;$");
11848 newXSproto("VMS::Filespec::vmsify",vmsify_fromperl,file,"$");
11849 newXSproto("VMS::Filespec::unixify",unixify_fromperl,file,"$");
11850 newXSproto("VMS::Filespec::pathify",pathify_fromperl,file,"$");
11851 newXSproto("VMS::Filespec::fileify",fileify_fromperl,file,"$");
11852 newXSproto("VMS::Filespec::vmspath",vmspath_fromperl,file,"$");
11853 newXSproto("VMS::Filespec::unixpath",unixpath_fromperl,file,"$");
11854 newXSproto("VMS::Filespec::candelete",candelete_fromperl,file,"$");
11855 newXSproto("DynaLoader::mod2fname", mod2fname, file, "$");
11856 newXS("File::Copy::rmscopy",rmscopy_fromperl,file);
11857 newXSproto("vmsish::hushed",hushexit_fromperl,file,";$");
11859 newXSproto("VMS::Filespec::vms_realpath",vms_realpath_fromperl,file,"$;$");
11861 #if __CRTL_VER >= 70301000 && !defined(__VAX)
11862 newXSproto("VMS::Filespec::case_tolerant",vms_case_tolerant_fromperl,file,"$;$");
11865 store_pipelocs(aTHX); /* will redo any earlier attempts */
11872 #if __CRTL_VER == 80200000
11873 /* This missed getting in to the DECC SDK for 8.2 */
11874 char *realpath(const char *file_name, char * resolved_name, ...);
11877 /*{{{char *do_vms_realpath(const char *file_name, char *resolved_name)*/
11878 /* wrapper for the realpath() function added with 8.2 RMS SYMLINK SDK.
11879 * The perl fallback routine to provide realpath() is not as efficient
11883 mp_do_vms_realpath(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
11885 return realpath(filespec, outbuf);
11889 /* External entry points */
11890 char *Perl_vms_realpath(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
11891 { return do_vms_realpath(filespec, outbuf, utf8_fl); }
11893 char *Perl_vms_realpath(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
11898 #if __CRTL_VER >= 70301000 && !defined(__VAX)
11899 /* case_tolerant */
11901 /*{{{int do_vms_case_tolerant(void)*/
11902 /* OpenVMS provides a case sensitive implementation of ODS-5 and this is
11903 * controlled by a process setting.
11905 int do_vms_case_tolerant(void)
11907 return vms_process_case_tolerant;
11910 /* External entry points */
11911 int Perl_vms_case_tolerant(void)
11912 { return do_vms_case_tolerant(); }
11914 int Perl_vms_case_tolerant(void)
11915 { return vms_process_case_tolerant; }
11919 /* Start of DECC RTL Feature handling */
11921 static int sys_trnlnm
11922 (const char * logname,
11926 const $DESCRIPTOR(table_dsc, "LNM$FILE_DEV");
11927 const unsigned long attr = LNM$M_CASE_BLIND;
11928 struct dsc$descriptor_s name_dsc;
11930 unsigned short result;
11931 struct itmlst_3 itlst[2] = {{value_len, LNM$_STRING, value, &result},
11934 name_dsc.dsc$w_length = strlen(logname);
11935 name_dsc.dsc$a_pointer = (char *)logname;
11936 name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
11937 name_dsc.dsc$b_class = DSC$K_CLASS_S;
11939 status = sys$trnlnm(&attr, &table_dsc, &name_dsc, 0, itlst);
11941 if ($VMS_STATUS_SUCCESS(status)) {
11943 /* Null terminate and return the string */
11944 /*--------------------------------------*/
11951 static int sys_crelnm
11952 (const char * logname,
11953 const char * value)
11956 const char * proc_table = "LNM$PROCESS_TABLE";
11957 struct dsc$descriptor_s proc_table_dsc;
11958 struct dsc$descriptor_s logname_dsc;
11959 struct itmlst_3 item_list[2];
11961 proc_table_dsc.dsc$a_pointer = (char *) proc_table;
11962 proc_table_dsc.dsc$w_length = strlen(proc_table);
11963 proc_table_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
11964 proc_table_dsc.dsc$b_class = DSC$K_CLASS_S;
11966 logname_dsc.dsc$a_pointer = (char *) logname;
11967 logname_dsc.dsc$w_length = strlen(logname);
11968 logname_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
11969 logname_dsc.dsc$b_class = DSC$K_CLASS_S;
11971 item_list[0].buflen = strlen(value);
11972 item_list[0].itmcode = LNM$_STRING;
11973 item_list[0].bufadr = (char *)value;
11974 item_list[0].retlen = NULL;
11976 item_list[1].buflen = 0;
11977 item_list[1].itmcode = 0;
11979 ret_val = sys$crelnm
11981 (const struct dsc$descriptor_s *)&proc_table_dsc,
11982 (const struct dsc$descriptor_s *)&logname_dsc,
11984 (const struct item_list_3 *) item_list);
11989 /* C RTL Feature settings */
11991 static int set_features
11992 (int (* init_coroutine)(int *, int *, void *), /* Needs casts if used */
11993 int (* cli_routine)(void), /* Not documented */
11994 void *image_info) /* Not documented */
12001 #if defined(JPI$_CASE_LOOKUP_PERM) && !defined(__VAX)
12002 const unsigned long int jpicode1 = JPI$_CASE_LOOKUP_PERM;
12003 const unsigned long int jpicode2 = JPI$_CASE_LOOKUP_IMAGE;
12004 unsigned long case_perm;
12005 unsigned long case_image;
12008 /* Allow an exception to bring Perl into the VMS debugger */
12009 vms_debug_on_exception = 0;
12010 status = sys_trnlnm("PERL_VMS_EXCEPTION_DEBUG", val_str, sizeof(val_str));
12011 if ($VMS_STATUS_SUCCESS(status)) {
12012 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
12013 vms_debug_on_exception = 1;
12015 vms_debug_on_exception = 0;
12018 /* Create VTF-7 filenames from UNICODE instead of UTF-8 */
12019 vms_vtf7_filenames = 0;
12020 status = sys_trnlnm("PERL_VMS_VTF7_FILENAMES", val_str, sizeof(val_str));
12021 if ($VMS_STATUS_SUCCESS(status)) {
12022 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
12023 vms_vtf7_filenames = 1;
12025 vms_vtf7_filenames = 0;
12028 /* Dectect running under GNV Bash or other UNIX like shell */
12029 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12030 gnv_unix_shell = 0;
12031 status = sys_trnlnm("GNV$UNIX_SHELL", val_str, sizeof(val_str));
12032 if ($VMS_STATUS_SUCCESS(status)) {
12033 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
12034 gnv_unix_shell = 1;
12035 set_feature_default("DECC$EFS_CASE_PRESERVE", 1);
12036 set_feature_default("DECC$EFS_CHARSET", 1);
12037 set_feature_default("DECC$FILENAME_UNIX_NO_VERSION", 1);
12038 set_feature_default("DECC$FILENAME_UNIX_REPORT", 1);
12039 set_feature_default("DECC$READDIR_DROPDOTNOTYPE", 1);
12040 set_feature_default("DECC$DISABLE_POSIX_ROOT", 0);
12043 gnv_unix_shell = 0;
12047 /* hacks to see if known bugs are still present for testing */
12049 /* Readdir is returning filenames in VMS syntax always */
12050 decc_bug_readdir_efs1 = 1;
12051 status = sys_trnlnm("DECC_BUG_READDIR_EFS1", val_str, sizeof(val_str));
12052 if ($VMS_STATUS_SUCCESS(status)) {
12053 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
12054 decc_bug_readdir_efs1 = 1;
12056 decc_bug_readdir_efs1 = 0;
12059 /* PCP mode requires creating /dev/null special device file */
12060 decc_bug_devnull = 0;
12061 status = sys_trnlnm("DECC_BUG_DEVNULL", val_str, sizeof(val_str));
12062 if ($VMS_STATUS_SUCCESS(status)) {
12063 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
12064 decc_bug_devnull = 1;
12066 decc_bug_devnull = 0;
12069 /* fgetname returning a VMS name in UNIX mode */
12070 decc_bug_fgetname = 1;
12071 status = sys_trnlnm("DECC_BUG_FGETNAME", val_str, sizeof(val_str));
12072 if ($VMS_STATUS_SUCCESS(status)) {
12073 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
12074 decc_bug_fgetname = 1;
12076 decc_bug_fgetname = 0;
12079 /* UNIX directory names with no paths are broken in a lot of places */
12080 decc_dir_barename = 1;
12081 status = sys_trnlnm("DECC_DIR_BARENAME", val_str, sizeof(val_str));
12082 if ($VMS_STATUS_SUCCESS(status)) {
12083 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
12084 decc_dir_barename = 1;
12086 decc_dir_barename = 0;
12089 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12090 s = decc$feature_get_index("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION");
12092 decc_disable_to_vms_logname_translation = decc$feature_get_value(s, 1);
12093 if (decc_disable_to_vms_logname_translation < 0)
12094 decc_disable_to_vms_logname_translation = 0;
12097 s = decc$feature_get_index("DECC$EFS_CASE_PRESERVE");
12099 decc_efs_case_preserve = decc$feature_get_value(s, 1);
12100 if (decc_efs_case_preserve < 0)
12101 decc_efs_case_preserve = 0;
12104 s = decc$feature_get_index("DECC$EFS_CHARSET");
12106 decc_efs_charset = decc$feature_get_value(s, 1);
12107 if (decc_efs_charset < 0)
12108 decc_efs_charset = 0;
12111 s = decc$feature_get_index("DECC$FILENAME_UNIX_REPORT");
12113 decc_filename_unix_report = decc$feature_get_value(s, 1);
12114 if (decc_filename_unix_report > 0)
12115 decc_filename_unix_report = 1;
12117 decc_filename_unix_report = 0;
12120 s = decc$feature_get_index("DECC$FILENAME_UNIX_ONLY");
12122 decc_filename_unix_only = decc$feature_get_value(s, 1);
12123 if (decc_filename_unix_only > 0) {
12124 decc_filename_unix_only = 1;
12127 decc_filename_unix_only = 0;
12131 s = decc$feature_get_index("DECC$FILENAME_UNIX_NO_VERSION");
12133 decc_filename_unix_no_version = decc$feature_get_value(s, 1);
12134 if (decc_filename_unix_no_version < 0)
12135 decc_filename_unix_no_version = 0;
12138 s = decc$feature_get_index("DECC$READDIR_DROPDOTNOTYPE");
12140 decc_readdir_dropdotnotype = decc$feature_get_value(s, 1);
12141 if (decc_readdir_dropdotnotype < 0)
12142 decc_readdir_dropdotnotype = 0;
12145 status = sys_trnlnm("SYS$POSIX_ROOT", val_str, sizeof(val_str));
12146 if ($VMS_STATUS_SUCCESS(status)) {
12147 s = decc$feature_get_index("DECC$DISABLE_POSIX_ROOT");
12149 dflt = decc$feature_get_value(s, 4);
12151 decc_disable_posix_root = decc$feature_get_value(s, 1);
12152 if (decc_disable_posix_root <= 0) {
12153 decc$feature_set_value(s, 1, 1);
12154 decc_disable_posix_root = 1;
12158 /* Traditionally Perl assumes this is off */
12159 decc_disable_posix_root = 1;
12160 decc$feature_set_value(s, 1, 1);
12165 #if __CRTL_VER >= 80200000
12166 s = decc$feature_get_index("DECC$POSIX_COMPLIANT_PATHNAMES");
12168 decc_posix_compliant_pathnames = decc$feature_get_value(s, 1);
12169 if (decc_posix_compliant_pathnames < 0)
12170 decc_posix_compliant_pathnames = 0;
12171 if (decc_posix_compliant_pathnames > 4)
12172 decc_posix_compliant_pathnames = 0;
12177 status = sys_trnlnm
12178 ("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION", val_str, sizeof(val_str));
12179 if ($VMS_STATUS_SUCCESS(status)) {
12180 val_str[0] = _toupper(val_str[0]);
12181 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
12182 decc_disable_to_vms_logname_translation = 1;
12187 status = sys_trnlnm("DECC$EFS_CASE_PRESERVE", val_str, sizeof(val_str));
12188 if ($VMS_STATUS_SUCCESS(status)) {
12189 val_str[0] = _toupper(val_str[0]);
12190 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
12191 decc_efs_case_preserve = 1;
12196 status = sys_trnlnm("DECC$FILENAME_UNIX_REPORT", val_str, sizeof(val_str));
12197 if ($VMS_STATUS_SUCCESS(status)) {
12198 val_str[0] = _toupper(val_str[0]);
12199 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
12200 decc_filename_unix_report = 1;
12203 status = sys_trnlnm("DECC$FILENAME_UNIX_ONLY", val_str, sizeof(val_str));
12204 if ($VMS_STATUS_SUCCESS(status)) {
12205 val_str[0] = _toupper(val_str[0]);
12206 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
12207 decc_filename_unix_only = 1;
12208 decc_filename_unix_report = 1;
12211 status = sys_trnlnm("DECC$FILENAME_UNIX_NO_VERSION", val_str, sizeof(val_str));
12212 if ($VMS_STATUS_SUCCESS(status)) {
12213 val_str[0] = _toupper(val_str[0]);
12214 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
12215 decc_filename_unix_no_version = 1;
12218 status = sys_trnlnm("DECC$READDIR_DROPDOTNOTYPE", val_str, sizeof(val_str));
12219 if ($VMS_STATUS_SUCCESS(status)) {
12220 val_str[0] = _toupper(val_str[0]);
12221 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
12222 decc_readdir_dropdotnotype = 1;
12227 #if defined(JPI$_CASE_LOOKUP_PERM) && !defined(__VAX)
12229 /* Report true case tolerance */
12230 /*----------------------------*/
12231 status = lib$getjpi(&jpicode1, 0, 0, &case_perm, 0, 0);
12232 if (!$VMS_STATUS_SUCCESS(status))
12233 case_perm = PPROP$K_CASE_BLIND;
12234 status = lib$getjpi(&jpicode2, 0, 0, &case_image, 0, 0);
12235 if (!$VMS_STATUS_SUCCESS(status))
12236 case_image = PPROP$K_CASE_BLIND;
12237 if ((case_perm == PPROP$K_CASE_SENSITIVE) ||
12238 (case_image == PPROP$K_CASE_SENSITIVE))
12239 vms_process_case_tolerant = 0;
12244 /* CRTL can be initialized past this point, but not before. */
12245 /* DECC$CRTL_INIT(); */
12251 /* DECC dependent attributes */
12252 #if __DECC_VER < 60560002
12254 #define not_executable
12256 #define relative ,rel
12257 #define not_executable ,noexe
12260 #pragma extern_model save
12261 #pragma extern_model strict_refdef "LIB$INITIALIZ" nowrt
12263 const __align (LONGWORD) int spare[8] = {0};
12264 /* .psect LIB$INITIALIZE, NOPIC, USR, CON, REL, GBL, NOSHR, NOEXE, RD, */
12267 #pragma extern_model strict_refdef "LIB$INITIALIZE" con, gbl,noshr, \
12268 nowrt,noshr relative not_executable
12270 const long vms_cc_features = (const long)set_features;
12273 ** Force a reference to LIB$INITIALIZE to ensure it
12274 ** exists in the image.
12276 int lib$initialize(void);
12278 #pragma extern_model strict_refdef
12280 int lib_init_ref = (int) lib$initialize;
12283 #pragma extern_model restore