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 if (!rms_is_nam_fnb(dirnam, (NAM$M_EXP_DEV | NAM$M_EXP_DIR))) {
5152 cp1 = strchr(esa,']');
5153 if (!cp1) cp1 = strchr(esa,'>');
5154 if (cp1) { /* Should always be true */
5155 rms_nam_esll(dirnam) -= cp1 - esa - 1;
5156 memmove(esa,cp1 + 1, rms_nam_esll(dirnam));
5159 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_TYPE)) { /* Was type specified? */
5160 /* Yep; check version while we're at it, if it's there. */
5161 cmplen = rms_is_nam_fnb(dirnam, NAM$M_EXP_VER) ? 6 : 4;
5162 if (strncmp(rms_nam_typel(dirnam), ".DIR;1", cmplen)) {
5163 /* Something other than .DIR[;1]. Bzzt. */
5164 sts = rms_free_search_context(&dirfab);
5166 PerlMem_free(trndir);
5167 PerlMem_free(vmsdir);
5169 set_vaxc_errno(RMS$_DIR);
5173 esa[rms_nam_esll(dirnam)] = '\0';
5174 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_NAME)) {
5175 /* They provided at least the name; we added the type, if necessary, */
5176 if (buf) retspec = buf; /* in sys$parse() */
5177 else if (ts) Newx(retspec, rms_nam_esll(dirnam)+1, char);
5178 else retspec = __fileify_retbuf;
5179 strcpy(retspec,esa);
5180 sts = rms_free_search_context(&dirfab);
5181 PerlMem_free(trndir);
5183 PerlMem_free(vmsdir);
5186 if ((cp1 = strstr(esa,".][000000]")) != NULL) {
5187 for (cp2 = cp1 + 9; *cp2; cp1++,cp2++) *cp1 = *cp2;
5189 rms_nam_esll(dirnam) -= 9;
5191 if ((cp1 = strrchr(esa,']')) == NULL) cp1 = strrchr(esa,'>');
5192 if (cp1 == NULL) { /* should never happen */
5193 sts = rms_free_search_context(&dirfab);
5194 PerlMem_free(trndir);
5196 PerlMem_free(vmsdir);
5201 retlen = strlen(esa);
5202 cp1 = strrchr(esa,'.');
5203 /* ODS-5 directory specifications can have extra "." in them. */
5204 /* Fix-me, can not scan EFS file specifications backwards */
5205 while (cp1 != NULL) {
5206 if ((cp1-1 == esa) || (*(cp1-1) != '^'))
5210 while ((cp1 > esa) && (*cp1 != '.'))
5217 if ((cp1) != NULL) {
5218 /* There's more than one directory in the path. Just roll back. */
5220 if (buf) retspec = buf;
5221 else if (ts) Newx(retspec,retlen+7,char);
5222 else retspec = __fileify_retbuf;
5223 strcpy(retspec,esa);
5226 if (rms_is_nam_fnb(dirnam, NAM$M_ROOT_DIR)) {
5227 /* Go back and expand rooted logical name */
5228 rms_set_nam_nop(dirnam, NAM$M_SYNCHK | NAM$M_NOCONCEAL);
5229 #ifdef NAM$M_NO_SHORT_UPCASE
5230 if (decc_efs_case_preserve)
5231 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
5233 if (!(sys$parse(&dirfab) & STS$K_SUCCESS)) {
5234 sts = rms_free_search_context(&dirfab);
5236 PerlMem_free(trndir);
5237 PerlMem_free(vmsdir);
5239 set_vaxc_errno(dirfab.fab$l_sts);
5242 retlen = rms_nam_esll(dirnam) - 9; /* esa - '][' - '].DIR;1' */
5243 if (buf) retspec = buf;
5244 else if (ts) Newx(retspec,retlen+16,char);
5245 else retspec = __fileify_retbuf;
5246 cp1 = strstr(esa,"][");
5247 if (!cp1) cp1 = strstr(esa,"]<");
5249 memcpy(retspec,esa,dirlen);
5250 if (!strncmp(cp1+2,"000000]",7)) {
5251 retspec[dirlen-1] = '\0';
5252 /* fix-me Not full ODS-5, just extra dots in directories for now */
5253 cp1 = retspec + dirlen - 1;
5254 while (cp1 > retspec)
5259 if (*(cp1-1) != '^')
5264 if (*cp1 == '.') *cp1 = ']';
5266 memmove(cp1+8,cp1+1,retspec+dirlen-cp1);
5267 memmove(cp1+1,"000000]",7);
5271 memmove(retspec+dirlen,cp1+2,retlen-dirlen);
5272 retspec[retlen] = '\0';
5273 /* Convert last '.' to ']' */
5274 cp1 = retspec+retlen-1;
5275 while (*cp != '[') {
5278 /* Do not trip on extra dots in ODS-5 directories */
5279 if ((cp1 == retspec) || (*(cp1-1) != '^'))
5283 if (*cp1 == '.') *cp1 = ']';
5285 memmove(cp1+8,cp1+1,retspec+dirlen-cp1);
5286 memmove(cp1+1,"000000]",7);
5290 else { /* This is a top-level dir. Add the MFD to the path. */
5291 if (buf) retspec = buf;
5292 else if (ts) Newx(retspec,retlen+16,char);
5293 else retspec = __fileify_retbuf;
5296 while ((*cp1 != ':') && (*cp1 != '\0')) *(cp2++) = *(cp1++);
5297 strcpy(cp2,":[000000]");
5302 sts = rms_free_search_context(&dirfab);
5303 /* We've set up the string up through the filename. Add the
5304 type and version, and we're done. */
5305 strcat(retspec,".DIR;1");
5307 /* $PARSE may have upcased filespec, so convert output to lower
5308 * case if input contained any lowercase characters. */
5309 if (haslower && !decc_efs_case_preserve) __mystrtolower(retspec);
5310 PerlMem_free(trndir);
5312 PerlMem_free(vmsdir);
5315 } /* end of do_fileify_dirspec() */
5317 /* External entry points */
5318 char *Perl_fileify_dirspec(pTHX_ const char *dir, char *buf)
5319 { return do_fileify_dirspec(dir,buf,0,NULL); }
5320 char *Perl_fileify_dirspec_ts(pTHX_ const char *dir, char *buf)
5321 { return do_fileify_dirspec(dir,buf,1,NULL); }
5322 char *Perl_fileify_dirspec_utf8(pTHX_ const char *dir, char *buf, int * utf8_fl)
5323 { return do_fileify_dirspec(dir,buf,0,utf8_fl); }
5324 char *Perl_fileify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int * utf8_fl)
5325 { return do_fileify_dirspec(dir,buf,1,utf8_fl); }
5327 /*{{{ char *pathify_dirspec[_ts](char *path, char *buf)*/
5328 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int * utf8_fl)
5330 static char __pathify_retbuf[VMS_MAXRSS];
5331 unsigned long int retlen;
5332 char *retpath, *cp1, *cp2, *trndir;
5333 unsigned short int trnlnm_iter_count;
5336 if (utf8_fl != NULL)
5339 if (!dir || !*dir) {
5340 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
5343 trndir = PerlMem_malloc(VMS_MAXRSS);
5344 if (trndir == NULL) _ckvmssts(SS$_INSFMEM);
5345 if (*dir) strcpy(trndir,dir);
5346 else getcwd(trndir,VMS_MAXRSS - 1);
5348 trnlnm_iter_count = 0;
5349 while (!strpbrk(trndir,"/]:>") && !no_translate_barewords
5350 && my_trnlnm(trndir,trndir,0)) {
5351 trnlnm_iter_count++;
5352 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
5353 trnlen = strlen(trndir);
5355 /* Trap simple rooted lnms, and return lnm:[000000] */
5356 if (!strcmp(trndir+trnlen-2,".]")) {
5357 if (buf) retpath = buf;
5358 else if (ts) Newx(retpath,strlen(dir)+10,char);
5359 else retpath = __pathify_retbuf;
5360 strcpy(retpath,dir);
5361 strcat(retpath,":[000000]");
5362 PerlMem_free(trndir);
5367 /* At this point we do not work with *dir, but the copy in
5368 * *trndir that is modifiable.
5371 if (!strpbrk(trndir,"]:>")) { /* Unix-style path or plain name */
5372 if (*trndir == '.' && (*(trndir+1) == '\0' ||
5373 (*(trndir+1) == '.' && *(trndir+2) == '\0')))
5374 retlen = 2 + (*(trndir+1) != '\0');
5376 if ( !(cp1 = strrchr(trndir,'/')) &&
5377 !(cp1 = strrchr(trndir,']')) &&
5378 !(cp1 = strrchr(trndir,'>')) ) cp1 = trndir;
5379 if ((cp2 = strchr(cp1,'.')) != NULL &&
5380 (*(cp2-1) != '/' || /* Trailing '.', '..', */
5381 !(*(cp2+1) == '\0' || /* or '...' are dirs. */
5382 (*(cp2+1) == '.' && *(cp2+2) == '\0') ||
5383 (*(cp2+1) == '.' && *(cp2+2) == '.' && *(cp2+3) == '\0')))) {
5386 /* For EFS or ODS-5 look for the last dot */
5387 if (decc_efs_charset) {
5388 cp2 = strrchr(cp1,'.');
5390 if (vms_process_case_tolerant) {
5391 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
5392 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
5393 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
5394 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
5395 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
5396 (ver || *cp3)))))) {
5397 PerlMem_free(trndir);
5399 set_vaxc_errno(RMS$_DIR);
5404 if (!*(cp2+1) || *(cp2+1) != 'D' || /* Wrong type. */
5405 !*(cp2+2) || *(cp2+2) != 'I' || /* Bzzt. */
5406 !*(cp2+3) || *(cp2+3) != 'R' ||
5407 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
5408 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
5409 (ver || *cp3)))))) {
5410 PerlMem_free(trndir);
5412 set_vaxc_errno(RMS$_DIR);
5416 retlen = cp2 - trndir + 1;
5418 else { /* No file type present. Treat the filename as a directory. */
5419 retlen = strlen(trndir) + 1;
5422 if (buf) retpath = buf;
5423 else if (ts) Newx(retpath,retlen+1,char);
5424 else retpath = __pathify_retbuf;
5425 strncpy(retpath, trndir, retlen-1);
5426 if (retpath[retlen-2] != '/') { /* If the path doesn't already end */
5427 retpath[retlen-1] = '/'; /* with '/', add it. */
5428 retpath[retlen] = '\0';
5430 else retpath[retlen-1] = '\0';
5432 else { /* VMS-style directory spec */
5434 unsigned long int sts, cmplen, haslower;
5435 struct FAB dirfab = cc$rms_fab;
5437 rms_setup_nam(savnam);
5438 rms_setup_nam(dirnam);
5440 /* If we've got an explicit filename, we can just shuffle the string. */
5441 if ( ( (cp1 = strrchr(trndir,']')) != NULL ||
5442 (cp1 = strrchr(trndir,'>')) != NULL ) && *(cp1+1)) {
5443 if ((cp2 = strchr(cp1,'.')) != NULL) {
5445 if (vms_process_case_tolerant) {
5446 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
5447 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
5448 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
5449 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
5450 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
5451 (ver || *cp3)))))) {
5452 PerlMem_free(trndir);
5454 set_vaxc_errno(RMS$_DIR);
5459 if (!*(cp2+1) || *(cp2+1) != 'D' || /* Wrong type. */
5460 !*(cp2+2) || *(cp2+2) != 'I' || /* Bzzt. */
5461 !*(cp2+3) || *(cp2+3) != 'R' ||
5462 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
5463 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
5464 (ver || *cp3)))))) {
5465 PerlMem_free(trndir);
5467 set_vaxc_errno(RMS$_DIR);
5472 else { /* No file type, so just draw name into directory part */
5473 for (cp2 = cp1; *cp2; cp2++) ;
5476 *(cp2+1) = '\0'; /* OK; trndir is guaranteed to be long enough */
5478 /* We've now got a VMS 'path'; fall through */
5481 dirlen = strlen(trndir);
5482 if (trndir[dirlen-1] == ']' ||
5483 trndir[dirlen-1] == '>' ||
5484 trndir[dirlen-1] == ':') { /* It's already a VMS 'path' */
5485 if (buf) retpath = buf;
5486 else if (ts) Newx(retpath,strlen(trndir)+1,char);
5487 else retpath = __pathify_retbuf;
5488 strcpy(retpath,trndir);
5489 PerlMem_free(trndir);
5492 rms_set_fna(dirfab, dirnam, trndir, dirlen);
5493 esa = PerlMem_malloc(VMS_MAXRSS);
5494 if (esa == NULL) _ckvmssts(SS$_INSFMEM);
5495 rms_set_dna(dirfab, dirnam, ".DIR;1", 6);
5496 rms_bind_fab_nam(dirfab, dirnam);
5497 rms_set_esa(dirfab, dirnam, esa, VMS_MAXRSS - 1);
5498 #ifdef NAM$M_NO_SHORT_UPCASE
5499 if (decc_efs_case_preserve)
5500 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
5503 for (cp = trndir; *cp; cp++)
5504 if (islower(*cp)) { haslower = 1; break; }
5506 if (!(sts = (sys$parse(&dirfab)& STS$K_SUCCESS))) {
5507 if ((dirfab.fab$l_sts == RMS$_DIR) || (dirfab.fab$l_sts == RMS$_DNF)) {
5508 rms_set_nam_nop(dirnam, NAM$M_SYNCHK);
5509 sts = sys$parse(&dirfab) & STS$K_SUCCESS;
5512 PerlMem_free(trndir);
5515 set_vaxc_errno(dirfab.fab$l_sts);
5521 /* Does the file really exist? */
5522 if (!(sys$search(&dirfab)&STS$K_SUCCESS)) {
5523 if (dirfab.fab$l_sts != RMS$_FNF) {
5525 sts1 = rms_free_search_context(&dirfab);
5526 PerlMem_free(trndir);
5529 set_vaxc_errno(dirfab.fab$l_sts);
5532 dirnam = savnam; /* No; just work with potential name */
5535 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_TYPE)) { /* Was type specified? */
5536 /* Yep; check version while we're at it, if it's there. */
5537 cmplen = rms_is_nam_fnb(dirnam, NAM$M_EXP_VER) ? 6 : 4;
5538 if (strncmp(rms_nam_typel(dirnam),".DIR;1",cmplen)) {
5540 /* Something other than .DIR[;1]. Bzzt. */
5541 sts2 = rms_free_search_context(&dirfab);
5542 PerlMem_free(trndir);
5545 set_vaxc_errno(RMS$_DIR);
5549 /* OK, the type was fine. Now pull any file name into the
5551 if ((cp1 = strrchr(esa,']'))) *(rms_nam_typel(dirnam)) = ']';
5553 cp1 = strrchr(esa,'>');
5554 *(rms_nam_typel(dirnam)) = '>';
5557 *(rms_nam_typel(dirnam) + 1) = '\0';
5558 retlen = (rms_nam_typel(dirnam)) - esa + 2;
5559 if (buf) retpath = buf;
5560 else if (ts) Newx(retpath,retlen,char);
5561 else retpath = __pathify_retbuf;
5562 strcpy(retpath,esa);
5564 sts = rms_free_search_context(&dirfab);
5565 /* $PARSE may have upcased filespec, so convert output to lower
5566 * case if input contained any lowercase characters. */
5567 if (haslower && !decc_efs_case_preserve) __mystrtolower(retpath);
5570 PerlMem_free(trndir);
5572 } /* end of do_pathify_dirspec() */
5574 /* External entry points */
5575 char *Perl_pathify_dirspec(pTHX_ const char *dir, char *buf)
5576 { return do_pathify_dirspec(dir,buf,0,NULL); }
5577 char *Perl_pathify_dirspec_ts(pTHX_ const char *dir, char *buf)
5578 { return do_pathify_dirspec(dir,buf,1,NULL); }
5579 char *Perl_pathify_dirspec_utf8(pTHX_ const char *dir, char *buf, int *utf8_fl)
5580 { return do_pathify_dirspec(dir,buf,0,utf8_fl); }
5581 char *Perl_pathify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int *utf8_fl)
5582 { return do_pathify_dirspec(dir,buf,1,utf8_fl); }
5584 /*{{{ char *tounixspec[_ts](char *spec, char *buf, int *)*/
5585 static char *mp_do_tounixspec(pTHX_ const char *spec, char *buf, int ts, int * utf8_fl)
5587 static char __tounixspec_retbuf[VMS_MAXRSS];
5588 char *dirend, *rslt, *cp1, *cp3, *tmp;
5590 int devlen, dirlen, retlen = VMS_MAXRSS;
5591 int expand = 1; /* guarantee room for leading and trailing slashes */
5592 unsigned short int trnlnm_iter_count;
5594 if (utf8_fl != NULL)
5597 if (spec == NULL) return NULL;
5598 if (strlen(spec) > (VMS_MAXRSS-1)) return NULL;
5599 if (buf) rslt = buf;
5601 Newx(rslt, VMS_MAXRSS, char);
5603 else rslt = __tounixspec_retbuf;
5605 /* New VMS specific format needs translation
5606 * glob passes filenames with trailing '\n' and expects this preserved.
5608 if (decc_posix_compliant_pathnames) {
5609 if (strncmp(spec, "\"^UP^", 5) == 0) {
5615 tunix = PerlMem_malloc(VMS_MAXRSS);
5616 if (tunix == NULL) _ckvmssts(SS$_INSFMEM);
5617 strcpy(tunix, spec);
5618 tunix_len = strlen(tunix);
5620 if (tunix[tunix_len - 1] == '\n') {
5621 tunix[tunix_len - 1] = '\"';
5622 tunix[tunix_len] = '\0';
5626 uspec = decc$translate_vms(tunix);
5627 PerlMem_free(tunix);
5628 if ((int)uspec > 0) {
5634 /* If we can not translate it, makemaker wants as-is */
5642 cmp_rslt = 0; /* Presume VMS */
5643 cp1 = strchr(spec, '/');
5647 /* Look for EFS ^/ */
5648 if (decc_efs_charset) {
5649 while (cp1 != NULL) {
5652 /* Found illegal VMS, assume UNIX */
5657 cp1 = strchr(cp1, '/');
5661 /* Look for "." and ".." */
5662 if (decc_filename_unix_report) {
5663 if (spec[0] == '.') {
5664 if ((spec[1] == '\0') || (spec[1] == '\n')) {
5668 if ((spec[1] == '.') && ((spec[2] == '\0') || (spec[2] == '\n'))) {
5674 /* This is already UNIX or at least nothing VMS understands */
5682 dirend = strrchr(spec,']');
5683 if (dirend == NULL) dirend = strrchr(spec,'>');
5684 if (dirend == NULL) dirend = strchr(spec,':');
5685 if (dirend == NULL) {
5690 /* Special case 1 - sys$posix_root = / */
5691 #if __CRTL_VER >= 70000000
5692 if (!decc_disable_posix_root) {
5693 if (strncasecmp(spec, "SYS$POSIX_ROOT:", 15) == 0) {
5701 /* Special case 2 - Convert NLA0: to /dev/null */
5702 #if __CRTL_VER < 70000000
5703 cmp_rslt = strncmp(spec,"NLA0:", 5);
5705 cmp_rslt = strncmp(spec,"nla0:", 5);
5707 cmp_rslt = strncasecmp(spec,"NLA0:", 5);
5709 if (cmp_rslt == 0) {
5710 strcpy(rslt, "/dev/null");
5713 if (spec[6] != '\0') {
5720 /* Also handle special case "SYS$SCRATCH:" */
5721 #if __CRTL_VER < 70000000
5722 cmp_rslt = strncmp(spec,"SYS$SCRATCH:", 12);
5724 cmp_rslt = strncmp(spec,"sys$scratch:", 12);
5726 cmp_rslt = strncasecmp(spec,"SYS$SCRATCH:", 12);
5728 tmp = PerlMem_malloc(VMS_MAXRSS);
5729 if (tmp == NULL) _ckvmssts(SS$_INSFMEM);
5730 if (cmp_rslt == 0) {
5733 islnm = my_trnlnm(tmp, "TMP", 0);
5735 strcpy(rslt, "/tmp");
5738 if (spec[12] != '\0') {
5746 if (*cp2 != '[' && *cp2 != '<') {
5749 else { /* the VMS spec begins with directories */
5751 if (*cp2 == ']' || *cp2 == '>') {
5752 *(cp1++) = '.'; *(cp1++) = '/'; *(cp1++) = '\0';
5756 else if ( *cp2 != '^' && *cp2 != '.' && *cp2 != '-') { /* add the implied device */
5757 if (getcwd(tmp, VMS_MAXRSS-1 ,1) == NULL) {
5758 if (ts) Safefree(rslt);
5762 trnlnm_iter_count = 0;
5765 while (*cp3 != ':' && *cp3) cp3++;
5767 if (strchr(cp3,']') != NULL) break;
5768 trnlnm_iter_count++;
5769 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER+1) break;
5770 } while (vmstrnenv(tmp,tmp,0,fildev,0));
5772 ((devlen = strlen(tmp)) + (dirlen = strlen(cp2)) + 1 > retlen)) {
5773 retlen = devlen + dirlen;
5774 Renew(rslt,retlen+1+2*expand,char);
5780 *(cp1++) = *(cp3++);
5781 if (cp1 - rslt > (VMS_MAXRSS - 1) && !ts && !buf) {
5783 return NULL; /* No room */
5788 if ((*cp2 == '^')) {
5789 /* EFS file escape, pass the next character as is */
5790 /* Fix me: HEX encoding for UNICODE not implemented */
5793 else if ( *cp2 == '.') {
5794 if (*(cp2+1) == '.' && *(cp2+2) == '.') {
5795 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
5802 for (; cp2 <= dirend; cp2++) {
5803 if ((*cp2 == '^')) {
5804 /* EFS file escape, pass the next character as is */
5805 /* Fix me: HEX encoding for UNICODE not implemented */
5811 if (*(cp2+1) == '[') cp2++;
5813 else if (*cp2 == ']' || *cp2 == '>') {
5814 if (*(cp1-1) != '/') *(cp1++) = '/'; /* Don't double after ellipsis */
5816 else if ((*cp2 == '.') && (*cp2-1 != '^')) {
5818 if (*(cp2+1) == ']' || *(cp2+1) == '>') {
5819 while (*(cp2+1) == ']' || *(cp2+1) == '>' ||
5820 *(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
5821 if (!strncmp(cp2,"[000000",7) && (*(cp2+7) == ']' ||
5822 *(cp2+7) == '>' || *(cp2+7) == '.')) cp2 += 7;
5824 else if ( *(cp2+1) == '.' && *(cp2+2) == '.') {
5825 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) ='/';
5829 else if (*cp2 == '-') {
5830 if (*(cp2-1) == '[' || *(cp2-1) == '<' || *(cp2-1) == '.') {
5831 while (*cp2 == '-') {
5833 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
5835 if (*cp2 != '.' && *cp2 != ']' && *cp2 != '>') { /* we don't allow */
5836 if (ts) Safefree(rslt); /* filespecs like */
5837 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); /* [fred.--foo.bar] */
5841 else *(cp1++) = *cp2;
5843 else *(cp1++) = *cp2;
5845 while (*cp2) *(cp1++) = *(cp2++);
5848 /* This still leaves /000000/ when working with a
5849 * VMS device root or concealed root.
5855 ulen = strlen(rslt);
5857 /* Get rid of "000000/ in rooted filespecs */
5859 zeros = strstr(rslt, "/000000/");
5860 if (zeros != NULL) {
5862 mlen = ulen - (zeros - rslt) - 7;
5863 memmove(zeros, &zeros[7], mlen);
5872 } /* end of do_tounixspec() */
5874 /* External entry points */
5875 char *Perl_tounixspec(pTHX_ const char *spec, char *buf)
5876 { return do_tounixspec(spec,buf,0, NULL); }
5877 char *Perl_tounixspec_ts(pTHX_ const char *spec, char *buf)
5878 { return do_tounixspec(spec,buf,1, NULL); }
5879 char *Perl_tounixspec_utf8(pTHX_ const char *spec, char *buf, int * utf8_fl)
5880 { return do_tounixspec(spec,buf,0, utf8_fl); }
5881 char *Perl_tounixspec_utf8_ts(pTHX_ const char *spec, char *buf, int * utf8_fl)
5882 { return do_tounixspec(spec,buf,1, utf8_fl); }
5884 #if __CRTL_VER >= 70200000 && !defined(__VAX)
5887 This procedure is used to identify if a path is based in either
5888 the old SYS$POSIX_ROOT: or the new 8.3 RMS based POSIX root, and
5889 it returns the OpenVMS format directory for it.
5891 It is expecting specifications of only '/' or '/xxxx/'
5893 If a posix root does not exist, or 'xxxx' is not a directory
5894 in the posix root, it returns a failure.
5896 FIX-ME: xxxx could be in UTF-8 and needs to be returned in VTF-7.
5898 It is used only internally by posix_to_vmsspec_hardway().
5901 static int posix_root_to_vms
5902 (char *vmspath, int vmspath_len,
5903 const char *unixpath,
5904 const int * utf8_fl) {
5906 struct FAB myfab = cc$rms_fab;
5907 struct NAML mynam = cc$rms_naml;
5908 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
5909 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
5916 unixlen = strlen(unixpath);
5922 #if __CRTL_VER >= 80200000
5923 /* If not a posix spec already, convert it */
5924 if (decc_posix_compliant_pathnames) {
5925 if (strncmp(unixpath,"\"^UP^",5) != 0) {
5926 sprintf(vmspath,"\"^UP^%s\"",unixpath);
5929 /* This is already a VMS specification, no conversion */
5931 strncpy(vmspath,unixpath, vmspath_len);
5940 /* Check to see if this is under the POSIX root */
5941 if (decc_disable_posix_root) {
5945 /* Skip leading / */
5946 if (unixpath[0] == '/') {
5952 strcpy(vmspath,"SYS$POSIX_ROOT:");
5954 /* If this is only the / , or blank, then... */
5955 if (unixpath[0] == '\0') {
5956 /* by definition, this is the answer */
5960 /* Need to look up a directory */
5964 /* Copy and add '^' escape characters as needed */
5967 while (unixpath[i] != 0) {
5970 j += copy_expand_unix_filename_escape
5971 (&vmspath[j], &unixpath[i], &k, utf8_fl);
5975 path_len = strlen(vmspath);
5976 if (vmspath[path_len - 1] == '/')
5978 vmspath[path_len] = ']';
5980 vmspath[path_len] = '\0';
5983 vmspath[vmspath_len] = 0;
5984 if (unixpath[unixlen - 1] == '/')
5986 esa = PerlMem_malloc(VMS_MAXRSS);
5987 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5988 myfab.fab$l_fna = vmspath;
5989 myfab.fab$b_fns = strlen(vmspath);
5990 myfab.fab$l_naml = &mynam;
5991 mynam.naml$l_esa = NULL;
5992 mynam.naml$b_ess = 0;
5993 mynam.naml$l_long_expand = esa;
5994 mynam.naml$l_long_expand_alloc = (unsigned char) VMS_MAXRSS - 1;
5995 mynam.naml$l_rsa = NULL;
5996 mynam.naml$b_rss = 0;
5997 if (decc_efs_case_preserve)
5998 mynam.naml$b_nop |= NAM$M_NO_SHORT_UPCASE;
5999 mynam.naml$l_input_flags |= NAML$M_OPEN_SPECIAL;
6001 /* Set up the remaining naml fields */
6002 sts = sys$parse(&myfab);
6004 /* It failed! Try again as a UNIX filespec */
6010 /* get the Device ID and the FID */
6011 sts = sys$search(&myfab);
6012 /* on any failure, returned the POSIX ^UP^ filespec */
6017 specdsc.dsc$a_pointer = vmspath;
6018 specdsc.dsc$w_length = vmspath_len;
6020 dvidsc.dsc$a_pointer = &mynam.naml$t_dvi[1];
6021 dvidsc.dsc$w_length = mynam.naml$t_dvi[0];
6022 sts = lib$fid_to_name
6023 (&dvidsc, mynam.naml$w_fid, &specdsc, &specdsc.dsc$w_length);
6025 /* on any failure, returned the POSIX ^UP^ filespec */
6027 /* This can happen if user does not have permission to read directories */
6028 if (strncmp(unixpath,"\"^UP^",5) != 0)
6029 sprintf(vmspath,"\"^UP^%s\"",unixpath);
6031 strcpy(vmspath, unixpath);
6034 vmspath[specdsc.dsc$w_length] = 0;
6036 /* Are we expecting a directory? */
6037 if (dir_flag != 0) {
6043 i = specdsc.dsc$w_length - 1;
6047 /* Version must be '1' */
6048 if (vmspath[i--] != '1')
6050 /* Version delimiter is one of ".;" */
6051 if ((vmspath[i] != '.') && (vmspath[i] != ';'))
6054 if (vmspath[i--] != 'R')
6056 if (vmspath[i--] != 'I')
6058 if (vmspath[i--] != 'D')
6060 if (vmspath[i--] != '.')
6062 eptr = &vmspath[i+1];
6064 if ((vmspath[i] == ']') || (vmspath[i] == '>')) {
6065 if (vmspath[i-1] != '^') {
6073 /* Get rid of 6 imaginary zero directory filename */
6074 vmspath[i+1] = '\0';
6078 if (vmspath[i] == '0')
6092 /* /dev/mumble needs to be handled special.
6093 /dev/null becomes NLA0:, And there is the potential for other stuff
6094 like /dev/tty which may need to be mapped to something.
6098 slash_dev_special_to_vms
6099 (const char * unixptr,
6109 nextslash = strchr(unixptr, '/');
6110 len = strlen(unixptr);
6111 if (nextslash != NULL)
6112 len = nextslash - unixptr;
6113 cmp = strncmp("null", unixptr, 5);
6115 if (vmspath_len >= 6) {
6116 strcpy(vmspath, "_NLA0:");
6123 /* The built in routines do not understand perl's special needs, so
6124 doing a manual conversion from UNIX to VMS
6126 If the utf8_fl is not null and points to a non-zero value, then
6127 treat 8 bit characters as UTF-8.
6129 The sequence starting with '$(' and ending with ')' will be passed
6130 through with out interpretation instead of being escaped.
6133 static int posix_to_vmsspec_hardway
6134 (char *vmspath, int vmspath_len,
6135 const char *unixpath,
6140 const char *unixptr;
6141 const char *unixend;
6143 const char *lastslash;
6144 const char *lastdot;
6150 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
6151 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
6153 if (utf8_fl != NULL)
6159 /* Ignore leading "/" characters */
6160 while((unixptr[0] == '/') && (unixptr[1] == '/')) {
6163 unixlen = strlen(unixptr);
6165 /* Do nothing with blank paths */
6172 /* This could have a "^UP^ on the front */
6173 if (strncmp(unixptr,"\"^UP^",5) == 0) {
6179 lastslash = strrchr(unixptr,'/');
6180 lastdot = strrchr(unixptr,'.');
6181 unixend = strrchr(unixptr,'\"');
6182 if (!quoted || !((unixend != NULL) && (unixend[1] == '\0'))) {
6183 unixend = unixptr + unixlen;
6186 /* last dot is last dot or past end of string */
6187 if (lastdot == NULL)
6188 lastdot = unixptr + unixlen;
6190 /* if no directories, set last slash to beginning of string */
6191 if (lastslash == NULL) {
6192 lastslash = unixptr;
6195 /* Watch out for trailing "." after last slash, still a directory */
6196 if ((lastslash[1] == '.') && (lastslash[2] == '\0')) {
6197 lastslash = unixptr + unixlen;
6200 /* Watch out for traiing ".." after last slash, still a directory */
6201 if ((lastslash[1] == '.')&&(lastslash[2] == '.')&&(lastslash[3] == '\0')) {
6202 lastslash = unixptr + unixlen;
6205 /* dots in directories are aways escaped */
6206 if (lastdot < lastslash)
6207 lastdot = unixptr + unixlen;
6210 /* if (unixptr < lastslash) then we are in a directory */
6217 /* Start with the UNIX path */
6218 if (*unixptr != '/') {
6219 /* relative paths */
6221 /* If allowing logical names on relative pathnames, then handle here */
6222 if ((unixptr[0] != '.') && !decc_disable_to_vms_logname_translation &&
6223 !decc_posix_compliant_pathnames) {
6229 /* Find the next slash */
6230 nextslash = strchr(unixptr,'/');
6232 esa = PerlMem_malloc(vmspath_len);
6233 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6235 trn = PerlMem_malloc(VMS_MAXRSS);
6236 if (trn == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6238 if (nextslash != NULL) {
6240 seg_len = nextslash - unixptr;
6241 strncpy(esa, unixptr, seg_len);
6245 strcpy(esa, unixptr);
6246 seg_len = strlen(unixptr);
6248 /* trnlnm(section) */
6249 islnm = vmstrnenv(esa, trn, 0, fildev, 0);
6252 /* Now fix up the directory */
6254 /* Split up the path to find the components */
6255 sts = vms_split_path
6274 /* A logical name must be a directory or the full
6275 specification. It is only a full specification if
6276 it is the only component */
6277 if ((unixptr[seg_len] == '\0') ||
6278 (unixptr[seg_len+1] == '\0')) {
6280 /* Is a directory being required? */
6281 if (((n_len + e_len) != 0) && (dir_flag !=0)) {
6282 /* Not a logical name */
6287 if ((unixptr[seg_len] == '/') || (dir_flag != 0)) {
6288 /* This must be a directory */
6289 if (((n_len + e_len) == 0)&&(seg_len <= vmspath_len)) {
6290 strcpy(vmsptr, esa);
6291 vmslen=strlen(vmsptr);
6292 vmsptr[vmslen] = ':';
6294 vmsptr[vmslen] = '\0';
6302 /* must be dev/directory - ignore version */
6303 if ((n_len + e_len) != 0)
6306 /* transfer the volume */
6307 if (v_len > 0 && ((v_len + vmslen) < vmspath_len)) {
6308 strncpy(vmsptr, v_spec, v_len);
6314 /* unroot the rooted directory */
6315 if ((r_len > 0) && ((r_len + d_len + vmslen) < vmspath_len)) {
6317 r_spec[r_len - 1] = ']';
6319 /* This should not be there, but nothing is perfect */
6321 cmp = strcmp(&r_spec[1], "000000.");
6331 strncpy(vmsptr, r_spec, r_len);
6337 /* Bring over the directory. */
6339 ((d_len + vmslen) < vmspath_len)) {
6341 d_spec[d_len - 1] = ']';
6343 cmp = strcmp(&d_spec[1], "000000.");
6354 /* Remove the redundant root */
6362 strncpy(vmsptr, d_spec, d_len);
6376 if (lastslash > unixptr) {
6379 /* skip leading ./ */
6381 while ((unixptr[0] == '.') && (unixptr[1] == '/')) {
6387 /* Are we still in a directory? */
6388 if (unixptr <= lastslash) {
6393 /* if not backing up, then it is relative forward. */
6394 if (!((*unixptr == '.') && (unixptr[1] == '.') &&
6395 ((unixptr[2] == '/') || (&unixptr[2] == unixend)))) {
6403 /* Perl wants an empty directory here to tell the difference
6404 * between a DCL commmand and a filename
6413 /* Handle two special files . and .. */
6414 if (unixptr[0] == '.') {
6415 if (&unixptr[1] == unixend) {
6422 if ((unixptr[1] == '.') && (&unixptr[2] == unixend)) {
6433 else { /* Absolute PATH handling */
6437 /* Need to find out where root is */
6439 /* In theory, this procedure should never get an absolute POSIX pathname
6440 * that can not be found on the POSIX root.
6441 * In practice, that can not be relied on, and things will show up
6442 * here that are a VMS device name or concealed logical name instead.
6443 * So to make things work, this procedure must be tolerant.
6445 esa = PerlMem_malloc(vmspath_len);
6446 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6449 nextslash = strchr(&unixptr[1],'/');
6451 if (nextslash != NULL) {
6453 seg_len = nextslash - &unixptr[1];
6454 strncpy(vmspath, unixptr, seg_len + 1);
6455 vmspath[seg_len+1] = 0;
6458 cmp = strncmp(vmspath, "dev", 4);
6460 sts = slash_dev_special_to_vms(unixptr, vmspath, vmspath_len);
6461 if (sts = SS$_NORMAL)
6465 sts = posix_root_to_vms(esa, vmspath_len, vmspath, utf8_fl);
6468 if ($VMS_STATUS_SUCCESS(sts)) {
6469 /* This is verified to be a real path */
6471 sts = posix_root_to_vms(esa, vmspath_len, "/", NULL);
6472 if ($VMS_STATUS_SUCCESS(sts)) {
6473 strcpy(vmspath, esa);
6474 vmslen = strlen(vmspath);
6475 vmsptr = vmspath + vmslen;
6477 if (unixptr < lastslash) {
6486 cmp = strcmp(rptr,"000000.");
6491 } /* removing 6 zeros */
6492 } /* vmslen < 7, no 6 zeros possible */
6493 } /* Not in a directory */
6494 } /* Posix root found */
6496 /* No posix root, fall back to default directory */
6497 strcpy(vmspath, "SYS$DISK:[");
6498 vmsptr = &vmspath[10];
6500 if (unixptr > lastslash) {
6509 } /* end of verified real path handling */
6514 /* Ok, we have a device or a concealed root that is not in POSIX
6515 * or we have garbage. Make the best of it.
6518 /* Posix to VMS destroyed this, so copy it again */
6519 strncpy(vmspath, &unixptr[1], seg_len);
6520 vmspath[seg_len] = 0;
6522 vmsptr = &vmsptr[vmslen];
6525 /* Now do we need to add the fake 6 zero directory to it? */
6527 if ((*lastslash == '/') && (nextslash < lastslash)) {
6528 /* No there is another directory */
6535 /* now we have foo:bar or foo:[000000]bar to decide from */
6536 islnm = vmstrnenv(vmspath, esa, 0, fildev, 0);
6538 if (!islnm && !decc_posix_compliant_pathnames) {
6540 cmp = strncmp("bin", vmspath, 4);
6542 /* bin => SYS$SYSTEM: */
6543 islnm = vmstrnenv("SYS$SYSTEM:", esa, 0, fildev, 0);
6546 /* tmp => SYS$SCRATCH: */
6547 cmp = strncmp("tmp", vmspath, 4);
6549 islnm = vmstrnenv("SYS$SCRATCH:", esa, 0, fildev, 0);
6554 trnend = islnm ? islnm - 1 : 0;
6556 /* if this was a logical name, ']' or '>' must be present */
6557 /* if not a logical name, then assume a device and hope. */
6558 islnm = trnend ? (esa[trnend] == ']' || esa[trnend] == '>') : 0;
6560 /* if log name and trailing '.' then rooted - treat as device */
6561 add_6zero = islnm ? (esa[trnend-1] == '.') : 0;
6563 /* Fix me, if not a logical name, a device lookup should be
6564 * done to see if the device is file structured. If the device
6565 * is not file structured, the 6 zeros should not be put on.
6567 * As it is, perl is occasionally looking for dev:[000000]tty.
6568 * which looks a little strange.
6570 * Not that easy to detect as "/dev" may be file structured with
6571 * special device files.
6574 if ((add_6zero == 0) && (*nextslash == '/') &&
6575 (&nextslash[1] == unixend)) {
6576 /* No real directory present */
6581 /* Put the device delimiter on */
6584 unixptr = nextslash;
6587 /* Start directory if needed */
6588 if (!islnm || add_6zero) {
6594 /* add fake 000000] if needed */
6607 } /* non-POSIX translation */
6609 } /* End of relative/absolute path handling */
6611 while ((unixptr <= unixend) && (vmslen < vmspath_len)){
6618 if (dir_start != 0) {
6620 /* First characters in a directory are handled special */
6621 while ((*unixptr == '/') ||
6622 ((*unixptr == '.') &&
6623 ((unixptr[1]=='.') || (unixptr[1]=='/') ||
6624 (&unixptr[1]==unixend)))) {
6629 /* Skip redundant / in specification */
6630 while ((*unixptr == '/') && (dir_start != 0)) {
6633 if (unixptr == lastslash)
6636 if (unixptr == lastslash)
6639 /* Skip redundant ./ characters */
6640 while ((*unixptr == '.') &&
6641 ((unixptr[1] == '/')||(&unixptr[1] == unixend))) {
6644 if (unixptr == lastslash)
6646 if (*unixptr == '/')
6649 if (unixptr == lastslash)
6652 /* Skip redundant ../ characters */
6653 while ((*unixptr == '.') && (unixptr[1] == '.') &&
6654 ((unixptr[2] == '/') || (&unixptr[2] == unixend))) {
6655 /* Set the backing up flag */
6661 unixptr++; /* first . */
6662 unixptr++; /* second . */
6663 if (unixptr == lastslash)
6665 if (*unixptr == '/') /* The slash */
6668 if (unixptr == lastslash)
6671 /* To do: Perl expects /.../ to be translated to [...] on VMS */
6672 /* Not needed when VMS is pretending to be UNIX. */
6674 /* Is this loop stuck because of too many dots? */
6675 if (loop_flag == 0) {
6676 /* Exit the loop and pass the rest through */
6681 /* Are we done with directories yet? */
6682 if (unixptr >= lastslash) {
6684 /* Watch out for trailing dots */
6693 if (*unixptr == '/')
6697 /* Have we stopped backing up? */
6702 /* dir_start continues to be = 1 */
6704 if (*unixptr == '-') {
6706 *vmsptr++ = *unixptr++;
6710 /* Now are we done with directories yet? */
6711 if (unixptr >= lastslash) {
6713 /* Watch out for trailing dots */
6729 if (unixptr >= unixend)
6732 /* Normal characters - More EFS work probably needed */
6738 /* remove multiple / */
6739 while (unixptr[1] == '/') {
6742 if (unixptr == lastslash) {
6743 /* Watch out for trailing dots */
6755 /* To do: Perl expects /.../ to be translated to [...] on VMS */
6756 /* Not needed when VMS is pretending to be UNIX. */
6760 if (unixptr != unixend)
6765 if ((unixptr < lastdot) || (unixptr < lastslash) ||
6766 (&unixptr[1] == unixend)) {
6772 /* trailing dot ==> '^..' on VMS */
6773 if (unixptr == unixend) {
6781 *vmsptr++ = *unixptr++;
6785 if (quoted && (&unixptr[1] == unixend)) {
6789 in_cnt = copy_expand_unix_filename_escape
6790 (vmsptr, unixptr, &out_cnt, utf8_fl);
6800 in_cnt = copy_expand_unix_filename_escape
6801 (vmsptr, unixptr, &out_cnt, utf8_fl);
6808 /* Make sure directory is closed */
6809 if (unixptr == lastslash) {
6811 vmsptr2 = vmsptr - 1;
6813 if (*vmsptr2 != ']') {
6816 /* directories do not end in a dot bracket */
6817 if (*vmsptr2 == '.') {
6821 if (*vmsptr2 != '^') {
6822 vmsptr--; /* back up over the dot */
6830 /* Add a trailing dot if a file with no extension */
6831 vmsptr2 = vmsptr - 1;
6833 (*vmsptr2 != ']') && (*vmsptr2 != '*') && (*vmsptr2 != '%') &&
6834 (*vmsptr2 != ')') && (*lastdot != '.')) {
6845 /* Eventual routine to convert a UTF-8 specification to VTF-7. */
6846 static char * utf8_to_vtf7(char * rslt, const char * path, int *utf8_fl)
6851 /* If a UTF8 flag is being passed, honor it */
6853 if (utf8_fl != NULL) {
6854 utf8_flag = *utf8_fl;
6859 /* If there is a possibility of UTF8, then if any UTF8 characters
6860 are present, then they must be converted to VTF-7
6862 result = strcpy(rslt, path); /* FIX-ME */
6865 result = strcpy(rslt, path);
6871 /*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/
6872 static char *mp_do_tovmsspec
6873 (pTHX_ const char *path, char *buf, int ts, int dir_flag, int * utf8_flag) {
6874 static char __tovmsspec_retbuf[VMS_MAXRSS];
6875 char *rslt, *dirend;
6880 unsigned long int infront = 0, hasdir = 1;
6883 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
6884 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
6886 if (path == NULL) return NULL;
6887 rslt_len = VMS_MAXRSS-1;
6888 if (buf) rslt = buf;
6889 else if (ts) Newx(rslt, VMS_MAXRSS, char);
6890 else rslt = __tovmsspec_retbuf;
6892 /* '.' and '..' are "[]" and "[-]" for a quick check */
6893 if (path[0] == '.') {
6894 if (path[1] == '\0') {
6896 if (utf8_flag != NULL)
6901 if (path[1] == '.' && path[2] == '\0') {
6903 if (utf8_flag != NULL)
6910 /* Posix specifications are now a native VMS format */
6911 /*--------------------------------------------------*/
6912 #if __CRTL_VER >= 80200000 && !defined(__VAX)
6913 if (decc_posix_compliant_pathnames) {
6914 if (strncmp(path,"\"^UP^",5) == 0) {
6915 posix_to_vmsspec_hardway(rslt, rslt_len, path, dir_flag, utf8_flag);
6921 /* This is really the only way to see if this is already in VMS format */
6922 sts = vms_split_path
6937 /* FIX-ME - If dir_flag is non-zero, then this is a mp_do_vmspath()
6938 replacement, because the above parse just took care of most of
6939 what is needed to do vmspath when the specification is already
6942 And if it is not already, it is easier to do the conversion as
6943 part of this routine than to call this routine and then work on
6947 /* If VMS punctuation was found, it is already VMS format */
6948 if ((v_len != 0) || (r_len != 0) || (d_len != 0) || (vs_len != 0)) {
6949 if (utf8_flag != NULL)
6954 /* Now, what to do with trailing "." cases where there is no
6955 extension? If this is a UNIX specification, and EFS characters
6956 are enabled, then the trailing "." should be converted to a "^.".
6957 But if this was already a VMS specification, then it should be
6960 So in the case of ambiguity, leave the specification alone.
6964 /* If there is a possibility of UTF8, then if any UTF8 characters
6965 are present, then they must be converted to VTF-7
6967 if (utf8_flag != NULL)
6973 dirend = strrchr(path,'/');
6975 if (dirend == NULL) {
6976 /* If we get here with no UNIX directory delimiters, then this is
6977 not a complete file specification, either garbage a UNIX glob
6978 specification that can not be converted to a VMS wildcard, or
6979 it a UNIX shell macro. MakeMaker wants these passed through AS-IS,
6980 so apparently other programs expect this also.
6982 utf8 flag setting needs to be preserved.
6988 /* If POSIX mode active, handle the conversion */
6989 #if __CRTL_VER >= 80200000 && !defined(__VAX)
6990 if (decc_efs_charset) {
6991 posix_to_vmsspec_hardway(rslt, rslt_len, path, dir_flag, utf8_flag);
6996 if (*(dirend+1) == '.') { /* do we have trailing "/." or "/.." or "/..."? */
6997 if (!*(dirend+2)) dirend +=2;
6998 if (*(dirend+2) == '.' && !*(dirend+3)) dirend += 3;
6999 if (decc_efs_charset == 0) {
7000 if (*(dirend+2) == '.' && *(dirend+3) == '.' && !*(dirend+4)) dirend += 4;
7006 lastdot = strrchr(cp2,'.');
7012 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
7014 if (decc_disable_posix_root) {
7015 strcpy(rslt,"sys$disk:[000000]");
7018 strcpy(rslt,"sys$posix_root:[000000]");
7020 if (utf8_flag != NULL)
7024 while (*(++cp2) != '/' && *cp2) *(cp1++) = *cp2;
7026 trndev = PerlMem_malloc(VMS_MAXRSS);
7027 if (trndev == NULL) _ckvmssts(SS$_INSFMEM);
7028 islnm = my_trnlnm(rslt,trndev,0);
7030 /* DECC special handling */
7032 if (strcmp(rslt,"bin") == 0) {
7033 strcpy(rslt,"sys$system");
7036 islnm = my_trnlnm(rslt,trndev,0);
7038 else if (strcmp(rslt,"tmp") == 0) {
7039 strcpy(rslt,"sys$scratch");
7042 islnm = my_trnlnm(rslt,trndev,0);
7044 else if (!decc_disable_posix_root) {
7045 strcpy(rslt, "sys$posix_root");
7049 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
7050 islnm = my_trnlnm(rslt,trndev,0);
7052 else if (strcmp(rslt,"dev") == 0) {
7053 if (strncmp(cp2,"/null", 5) == 0) {
7054 if ((cp2[5] == 0) || (cp2[5] == '/')) {
7055 strcpy(rslt,"NLA0");
7059 islnm = my_trnlnm(rslt,trndev,0);
7065 trnend = islnm ? strlen(trndev) - 1 : 0;
7066 islnm = trnend ? (trndev[trnend] == ']' || trndev[trnend] == '>') : 0;
7067 rooted = islnm ? (trndev[trnend-1] == '.') : 0;
7068 /* If the first element of the path is a logical name, determine
7069 * whether it has to be translated so we can add more directories. */
7070 if (!islnm || rooted) {
7073 if (cp2 == dirend) while (infront++ < 6) *(cp1++) = '0';
7077 if (cp2 != dirend) {
7078 strcpy(rslt,trndev);
7079 cp1 = rslt + trnend;
7086 if (decc_disable_posix_root) {
7092 PerlMem_free(trndev);
7097 if (*(cp2+1) == '/' || *(cp2+1) == '\0') {
7098 cp2 += 2; /* skip over "./" - it's redundant */
7099 *(cp1++) = '.'; /* but it does indicate a relative dirspec */
7101 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
7102 *(cp1++) = '-'; /* "../" --> "-" */
7105 else if (*(cp2+1) == '.' && *(cp2+2) == '.' &&
7106 (*(cp2+3) == '/' || *(cp2+3) == '\0')) {
7107 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
7108 if (!*(cp2+4)) *(cp1++) = '.'; /* Simulate trailing '/' for later */
7111 else if ((cp2 != lastdot) || (lastdot < dirend)) {
7112 /* Escape the extra dots in EFS file specifications */
7115 if (cp2 > dirend) cp2 = dirend;
7117 else *(cp1++) = '.';
7119 for (; cp2 < dirend; cp2++) {
7121 if (*(cp2-1) == '/') continue;
7122 if (*(cp1-1) != '.') *(cp1++) = '.';
7125 else if (!infront && *cp2 == '.') {
7126 if (cp2+1 == dirend || *(cp2+1) == '\0') { cp2++; break; }
7127 else if (*(cp2+1) == '/') cp2++; /* skip over "./" - it's redundant */
7128 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
7129 if (*(cp1-1) == '-' || *(cp1-1) == '[') *(cp1++) = '-'; /* handle "../" */
7130 else if (*(cp1-2) == '[') *(cp1-1) = '-';
7131 else { /* back up over previous directory name */
7133 while (*(cp1-1) != '.' && *(cp1-1) != '[') cp1--;
7134 if (*(cp1-1) == '[') {
7135 memcpy(cp1,"000000.",7);
7140 if (cp2 == dirend) break;
7142 else if ( *(cp2+1) == '.' && *(cp2+2) == '.' &&
7143 (*(cp2+3) == '/' || *(cp2+3) == '\0') ) {
7144 if (*(cp1-1) != '.') *(cp1++) = '.'; /* May already have 1 from '/' */
7145 *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
7147 *(cp1++) = '.'; /* Simulate trailing '/' */
7148 cp2 += 2; /* for loop will incr this to == dirend */
7150 else cp2 += 3; /* Trailing '/' was there, so skip it, too */
7153 if (decc_efs_charset == 0)
7154 *(cp1++) = '_'; /* fix up syntax - '.' in name not allowed */
7156 *(cp1++) = '^'; /* fix up syntax - '.' in name is allowed */
7162 if (!infront && *(cp1-1) == '-') *(cp1++) = '.';
7164 if (decc_efs_charset == 0)
7171 else *(cp1++) = *cp2;
7175 if (*(cp1-1) == '.') cp1--; /* Unix spec ending in '/' ==> trailing '.' */
7176 if (hasdir) *(cp1++) = ']';
7177 if (*cp2) cp2++; /* check in case we ended with trailing '..' */
7178 /* fixme for ODS5 */
7185 if (decc_efs_charset == 0)
7196 if (((cp2 < lastdot) || (cp2[1] == '\0')) &&
7197 decc_readdir_dropdotnotype) {
7202 /* trailing dot ==> '^..' on VMS */
7209 *(cp1++) = *(cp2++);
7214 /* This could be a macro to be passed through */
7215 *(cp1++) = *(cp2++);
7217 const char * save_cp2;
7221 /* paranoid check */
7227 *(cp1++) = *(cp2++);
7228 if (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
7229 *(cp1++) = *(cp2++);
7230 while (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
7231 *(cp1++) = *(cp2++);
7234 *(cp1++) = *(cp2++);
7238 if (is_macro == 0) {
7239 /* Not really a macro - never mind */
7269 *(cp1++) = *(cp2++);
7272 /* FIXME: This needs fixing as Perl is putting ".dir;" on UNIX filespecs
7273 * which is wrong. UNIX notation should be ".dir." unless
7274 * the DECC$FILENAME_UNIX_NO_VERSION is enabled.
7275 * changing this behavior could break more things at this time.
7276 * efs character set effectively does not allow "." to be a version
7277 * delimiter as a further complication about changing this.
7279 if (decc_filename_unix_report != 0) {
7282 *(cp1++) = *(cp2++);
7285 *(cp1++) = *(cp2++);
7288 if ((no_type_seen == 1) && decc_readdir_dropdotnotype) {
7292 /* Fix me for "^]", but that requires making sure that you do
7293 * not back up past the start of the filename
7295 if ((*lcp1 != ']') && (*lcp1 != '*') && (*lcp1 != '%'))
7300 if (utf8_flag != NULL)
7304 } /* end of do_tovmsspec() */
7306 /* External entry points */
7307 char *Perl_tovmsspec(pTHX_ const char *path, char *buf)
7308 { return do_tovmsspec(path,buf,0,NULL); }
7309 char *Perl_tovmsspec_ts(pTHX_ const char *path, char *buf)
7310 { return do_tovmsspec(path,buf,1,NULL); }
7311 char *Perl_tovmsspec_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
7312 { return do_tovmsspec(path,buf,0,utf8_fl); }
7313 char *Perl_tovmsspec_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
7314 { return do_tovmsspec(path,buf,1,utf8_fl); }
7316 /*{{{ char *tovmspath[_ts](char *path, char *buf, const int *)*/
7317 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) {
7318 static char __tovmspath_retbuf[VMS_MAXRSS];
7320 char *pathified, *vmsified, *cp;
7322 if (path == NULL) return NULL;
7323 pathified = PerlMem_malloc(VMS_MAXRSS);
7324 if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
7325 if (do_pathify_dirspec(path,pathified,0,NULL) == NULL) {
7326 PerlMem_free(pathified);
7332 Newx(vmsified, VMS_MAXRSS, char);
7333 if (do_tovmsspec(pathified, buf ? buf : vmsified, 0, NULL) == NULL) {
7334 PerlMem_free(pathified);
7335 if (vmsified) Safefree(vmsified);
7338 PerlMem_free(pathified);
7343 vmslen = strlen(vmsified);
7344 Newx(cp,vmslen+1,char);
7345 memcpy(cp,vmsified,vmslen);
7351 strcpy(__tovmspath_retbuf,vmsified);
7353 return __tovmspath_retbuf;
7356 } /* end of do_tovmspath() */
7358 /* External entry points */
7359 char *Perl_tovmspath(pTHX_ const char *path, char *buf)
7360 { return do_tovmspath(path,buf,0, NULL); }
7361 char *Perl_tovmspath_ts(pTHX_ const char *path, char *buf)
7362 { return do_tovmspath(path,buf,1, NULL); }
7363 char *Perl_tovmspath_utf8(pTHX_ const char *path, char *buf, int *utf8_fl)
7364 { return do_tovmspath(path,buf,0,utf8_fl); }
7365 char *Perl_tovmspath_utf8_ts(pTHX_ const char *path, char *buf, int *utf8_fl)
7366 { return do_tovmspath(path,buf,1,utf8_fl); }
7369 /*{{{ char *tounixpath[_ts](char *path, char *buf, int * utf8_fl)*/
7370 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) {
7371 static char __tounixpath_retbuf[VMS_MAXRSS];
7373 char *pathified, *unixified, *cp;
7375 if (path == NULL) return NULL;
7376 pathified = PerlMem_malloc(VMS_MAXRSS);
7377 if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
7378 if (do_pathify_dirspec(path,pathified,0,NULL) == NULL) {
7379 PerlMem_free(pathified);
7385 Newx(unixified, VMS_MAXRSS, char);
7387 if (do_tounixspec(pathified,buf ? buf : unixified,0,NULL) == NULL) {
7388 PerlMem_free(pathified);
7389 if (unixified) Safefree(unixified);
7392 PerlMem_free(pathified);
7397 unixlen = strlen(unixified);
7398 Newx(cp,unixlen+1,char);
7399 memcpy(cp,unixified,unixlen);
7401 Safefree(unixified);
7405 strcpy(__tounixpath_retbuf,unixified);
7406 Safefree(unixified);
7407 return __tounixpath_retbuf;
7410 } /* end of do_tounixpath() */
7412 /* External entry points */
7413 char *Perl_tounixpath(pTHX_ const char *path, char *buf)
7414 { return do_tounixpath(path,buf,0,NULL); }
7415 char *Perl_tounixpath_ts(pTHX_ const char *path, char *buf)
7416 { return do_tounixpath(path,buf,1,NULL); }
7417 char *Perl_tounixpath_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
7418 { return do_tounixpath(path,buf,0,utf8_fl); }
7419 char *Perl_tounixpath_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
7420 { return do_tounixpath(path,buf,1,utf8_fl); }
7423 * @(#)argproc.c 2.2 94/08/16 Mark Pizzolato (mark@infocomm.com)
7425 *****************************************************************************
7427 * Copyright (C) 1989-1994 by *
7428 * Mark Pizzolato - INFO COMM, Danville, California (510) 837-5600 *
7430 * Permission is hereby granted for the reproduction of this software, *
7431 * on condition that this copyright notice is included in the reproduction, *
7432 * and that such reproduction is not for purposes of profit or material *
7435 * 27-Aug-1994 Modified for inclusion in perl5 *
7436 * by Charles Bailey bailey@newman.upenn.edu *
7437 *****************************************************************************
7441 * getredirection() is intended to aid in porting C programs
7442 * to VMS (Vax-11 C). The native VMS environment does not support
7443 * '>' and '<' I/O redirection, or command line wild card expansion,
7444 * or a command line pipe mechanism using the '|' AND background
7445 * command execution '&'. All of these capabilities are provided to any
7446 * C program which calls this procedure as the first thing in the
7448 * The piping mechanism will probably work with almost any 'filter' type
7449 * of program. With suitable modification, it may useful for other
7450 * portability problems as well.
7452 * Author: Mark Pizzolato mark@infocomm.com
7456 struct list_item *next;
7460 static void add_item(struct list_item **head,
7461 struct list_item **tail,
7465 static void mp_expand_wild_cards(pTHX_ char *item,
7466 struct list_item **head,
7467 struct list_item **tail,
7470 static int background_process(pTHX_ int argc, char **argv);
7472 static void pipe_and_fork(pTHX_ char **cmargv);
7474 /*{{{ void getredirection(int *ac, char ***av)*/
7476 mp_getredirection(pTHX_ int *ac, char ***av)
7478 * Process vms redirection arg's. Exit if any error is seen.
7479 * If getredirection() processes an argument, it is erased
7480 * from the vector. getredirection() returns a new argc and argv value.
7481 * In the event that a background command is requested (by a trailing "&"),
7482 * this routine creates a background subprocess, and simply exits the program.
7484 * Warning: do not try to simplify the code for vms. The code
7485 * presupposes that getredirection() is called before any data is
7486 * read from stdin or written to stdout.
7488 * Normal usage is as follows:
7494 * getredirection(&argc, &argv);
7498 int argc = *ac; /* Argument Count */
7499 char **argv = *av; /* Argument Vector */
7500 char *ap; /* Argument pointer */
7501 int j; /* argv[] index */
7502 int item_count = 0; /* Count of Items in List */
7503 struct list_item *list_head = 0; /* First Item in List */
7504 struct list_item *list_tail; /* Last Item in List */
7505 char *in = NULL; /* Input File Name */
7506 char *out = NULL; /* Output File Name */
7507 char *outmode = "w"; /* Mode to Open Output File */
7508 char *err = NULL; /* Error File Name */
7509 char *errmode = "w"; /* Mode to Open Error File */
7510 int cmargc = 0; /* Piped Command Arg Count */
7511 char **cmargv = NULL;/* Piped Command Arg Vector */
7514 * First handle the case where the last thing on the line ends with
7515 * a '&'. This indicates the desire for the command to be run in a
7516 * subprocess, so we satisfy that desire.
7519 if (0 == strcmp("&", ap))
7520 exit(background_process(aTHX_ --argc, argv));
7521 if (*ap && '&' == ap[strlen(ap)-1])
7523 ap[strlen(ap)-1] = '\0';
7524 exit(background_process(aTHX_ argc, argv));
7527 * Now we handle the general redirection cases that involve '>', '>>',
7528 * '<', and pipes '|'.
7530 for (j = 0; j < argc; ++j)
7532 if (0 == strcmp("<", argv[j]))
7536 fprintf(stderr,"No input file after < on command line");
7537 exit(LIB$_WRONUMARG);
7542 if ('<' == *(ap = argv[j]))
7547 if (0 == strcmp(">", ap))
7551 fprintf(stderr,"No output file after > on command line");
7552 exit(LIB$_WRONUMARG);
7571 fprintf(stderr,"No output file after > or >> on command line");
7572 exit(LIB$_WRONUMARG);
7576 if (('2' == *ap) && ('>' == ap[1]))
7593 fprintf(stderr,"No output file after 2> or 2>> on command line");
7594 exit(LIB$_WRONUMARG);
7598 if (0 == strcmp("|", argv[j]))
7602 fprintf(stderr,"No command into which to pipe on command line");
7603 exit(LIB$_WRONUMARG);
7605 cmargc = argc-(j+1);
7606 cmargv = &argv[j+1];
7610 if ('|' == *(ap = argv[j]))
7618 expand_wild_cards(ap, &list_head, &list_tail, &item_count);
7621 * Allocate and fill in the new argument vector, Some Unix's terminate
7622 * the list with an extra null pointer.
7624 argv = (char **) PerlMem_malloc((item_count+1) * sizeof(char *));
7625 if (argv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7627 for (j = 0; j < item_count; ++j, list_head = list_head->next)
7628 argv[j] = list_head->value;
7634 fprintf(stderr,"'|' and '>' may not both be specified on command line");
7635 exit(LIB$_INVARGORD);
7637 pipe_and_fork(aTHX_ cmargv);
7640 /* Check for input from a pipe (mailbox) */
7642 if (in == NULL && 1 == isapipe(0))
7644 char mbxname[L_tmpnam];
7646 long int dvi_item = DVI$_DEVBUFSIZ;
7647 $DESCRIPTOR(mbxnam, "");
7648 $DESCRIPTOR(mbxdevnam, "");
7650 /* Input from a pipe, reopen it in binary mode to disable */
7651 /* carriage control processing. */
7653 fgetname(stdin, mbxname);
7654 mbxnam.dsc$a_pointer = mbxname;
7655 mbxnam.dsc$w_length = strlen(mbxnam.dsc$a_pointer);
7656 lib$getdvi(&dvi_item, 0, &mbxnam, &bufsize, 0, 0);
7657 mbxdevnam.dsc$a_pointer = mbxname;
7658 mbxdevnam.dsc$w_length = sizeof(mbxname);
7659 dvi_item = DVI$_DEVNAM;
7660 lib$getdvi(&dvi_item, 0, &mbxnam, 0, &mbxdevnam, &mbxdevnam.dsc$w_length);
7661 mbxdevnam.dsc$a_pointer[mbxdevnam.dsc$w_length] = '\0';
7664 freopen(mbxname, "rb", stdin);
7667 fprintf(stderr,"Can't reopen input pipe (name: %s) in binary mode",mbxname);
7671 if ((in != NULL) && (NULL == freopen(in, "r", stdin, "mbc=32", "mbf=2")))
7673 fprintf(stderr,"Can't open input file %s as stdin",in);
7676 if ((out != NULL) && (NULL == freopen(out, outmode, stdout, "mbc=32", "mbf=2")))
7678 fprintf(stderr,"Can't open output file %s as stdout",out);
7681 if (out != NULL) Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",out);
7684 if (strcmp(err,"&1") == 0) {
7685 dup2(fileno(stdout), fileno(stderr));
7686 Perl_vmssetuserlnm(aTHX_ "SYS$ERROR","SYS$OUTPUT");
7689 if (NULL == (tmperr = fopen(err, errmode, "mbc=32", "mbf=2")))
7691 fprintf(stderr,"Can't open error file %s as stderr",err);
7695 if (NULL == freopen(err, "a", stderr, "mbc=32", "mbf=2"))
7699 Perl_vmssetuserlnm(aTHX_ "SYS$ERROR",err);
7702 #ifdef ARGPROC_DEBUG
7703 PerlIO_printf(Perl_debug_log, "Arglist:\n");
7704 for (j = 0; j < *ac; ++j)
7705 PerlIO_printf(Perl_debug_log, "argv[%d] = '%s'\n", j, argv[j]);
7707 /* Clear errors we may have hit expanding wildcards, so they don't
7708 show up in Perl's $! later */
7709 set_errno(0); set_vaxc_errno(1);
7710 } /* end of getredirection() */
7713 static void add_item(struct list_item **head,
7714 struct list_item **tail,
7720 *head = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
7721 if (head == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7725 (*tail)->next = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
7726 if ((*tail)->next == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7727 *tail = (*tail)->next;
7729 (*tail)->value = value;
7733 static void mp_expand_wild_cards(pTHX_ char *item,
7734 struct list_item **head,
7735 struct list_item **tail,
7739 unsigned long int context = 0;
7747 $DESCRIPTOR(filespec, "");
7748 $DESCRIPTOR(defaultspec, "SYS$DISK:[]");
7749 $DESCRIPTOR(resultspec, "");
7750 unsigned long int lff_flags = 0;
7754 #ifdef VMS_LONGNAME_SUPPORT
7755 lff_flags = LIB$M_FIL_LONG_NAMES;
7758 for (cp = item; *cp; cp++) {
7759 if (*cp == '*' || *cp == '%' || isspace(*cp)) break;
7760 if (*cp == '.' && *(cp-1) == '.' && *(cp-2) =='.') break;
7762 if (!*cp || isspace(*cp))
7764 add_item(head, tail, item, count);
7769 /* "double quoted" wild card expressions pass as is */
7770 /* From DCL that means using e.g.: */
7771 /* perl program """perl.*""" */
7772 item_len = strlen(item);
7773 if ( '"' == *item && '"' == item[item_len-1] )
7776 item[item_len-2] = '\0';
7777 add_item(head, tail, item, count);
7781 resultspec.dsc$b_dtype = DSC$K_DTYPE_T;
7782 resultspec.dsc$b_class = DSC$K_CLASS_D;
7783 resultspec.dsc$a_pointer = NULL;
7784 vmsspec = PerlMem_malloc(VMS_MAXRSS);
7785 if (vmsspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7786 if ((isunix = (int) strchr(item,'/')) != (int) NULL)
7787 filespec.dsc$a_pointer = do_tovmsspec(item,vmsspec,0,NULL);
7788 if (!isunix || !filespec.dsc$a_pointer)
7789 filespec.dsc$a_pointer = item;
7790 filespec.dsc$w_length = strlen(filespec.dsc$a_pointer);
7792 * Only return version specs, if the caller specified a version
7794 had_version = strchr(item, ';');
7796 * Only return device and directory specs, if the caller specifed either.
7798 had_device = strchr(item, ':');
7799 had_directory = (isunix || NULL != strchr(item, '[')) || (NULL != strchr(item, '<'));
7801 while ($VMS_STATUS_SUCCESS(sts = lib$find_file
7802 (&filespec, &resultspec, &context,
7803 &defaultspec, 0, &rms_sts, &lff_flags)))
7808 string = PerlMem_malloc(resultspec.dsc$w_length+1);
7809 if (string == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7810 strncpy(string, resultspec.dsc$a_pointer, resultspec.dsc$w_length);
7811 string[resultspec.dsc$w_length] = '\0';
7812 if (NULL == had_version)
7813 *(strrchr(string, ';')) = '\0';
7814 if ((!had_directory) && (had_device == NULL))
7816 if (NULL == (devdir = strrchr(string, ']')))
7817 devdir = strrchr(string, '>');
7818 strcpy(string, devdir + 1);
7821 * Be consistent with what the C RTL has already done to the rest of
7822 * the argv items and lowercase all of these names.
7824 if (!decc_efs_case_preserve) {
7825 for (c = string; *c; ++c)
7829 if (isunix) trim_unixpath(string,item,1);
7830 add_item(head, tail, string, count);
7833 PerlMem_free(vmsspec);
7834 if (sts != RMS$_NMF)
7836 set_vaxc_errno(sts);
7839 case RMS$_FNF: case RMS$_DNF:
7840 set_errno(ENOENT); break;
7842 set_errno(ENOTDIR); break;
7844 set_errno(ENODEV); break;
7845 case RMS$_FNM: case RMS$_SYN:
7846 set_errno(EINVAL); break;
7848 set_errno(EACCES); break;
7850 _ckvmssts_noperl(sts);
7854 add_item(head, tail, item, count);
7855 _ckvmssts_noperl(lib$sfree1_dd(&resultspec));
7856 _ckvmssts_noperl(lib$find_file_end(&context));
7859 static int child_st[2];/* Event Flag set when child process completes */
7861 static unsigned short child_chan;/* I/O Channel for Pipe Mailbox */
7863 static unsigned long int exit_handler(int *status)
7867 if (0 == child_st[0])
7869 #ifdef ARGPROC_DEBUG
7870 PerlIO_printf(Perl_debug_log, "Waiting for Child Process to Finish . . .\n");
7872 fflush(stdout); /* Have to flush pipe for binary data to */
7873 /* terminate properly -- <tp@mccall.com> */
7874 sys$qiow(0, child_chan, IO$_WRITEOF, iosb, 0, 0, 0, 0, 0, 0, 0, 0);
7875 sys$dassgn(child_chan);
7877 sys$synch(0, child_st);
7882 static void sig_child(int chan)
7884 #ifdef ARGPROC_DEBUG
7885 PerlIO_printf(Perl_debug_log, "Child Completion AST\n");
7887 if (child_st[0] == 0)
7891 static struct exit_control_block exit_block =
7896 &exit_block.exit_status,
7901 pipe_and_fork(pTHX_ char **cmargv)
7904 struct dsc$descriptor_s *vmscmd;
7905 char subcmd[2*MAX_DCL_LINE_LENGTH], *p, *q;
7906 int sts, j, l, ismcr, quote, tquote = 0;
7908 sts = setup_cmddsc(aTHX_ cmargv[0],0,"e,&vmscmd);
7909 vms_execfree(vmscmd);
7914 ismcr = q && toupper(*q) == 'M' && toupper(*(q+1)) == 'C'
7915 && toupper(*(q+2)) == 'R' && !*(q+3);
7917 while (q && l < MAX_DCL_LINE_LENGTH) {
7919 if (j > 0 && quote) {
7925 if (ismcr && j > 1) quote = 1;
7926 tquote = (strchr(q,' ')) != NULL || *q == '\0';
7929 if (quote || tquote) {
7935 if ((quote||tquote) && *q == '"') {
7945 fp = safe_popen(aTHX_ subcmd,"wbF",&sts);
7947 PerlIO_printf(Perl_debug_log,"Can't open output pipe (status %d)",sts);
7951 static int background_process(pTHX_ int argc, char **argv)
7953 char command[MAX_DCL_SYMBOL + 1] = "$";
7954 $DESCRIPTOR(value, "");
7955 static $DESCRIPTOR(cmd, "BACKGROUND$COMMAND");
7956 static $DESCRIPTOR(null, "NLA0:");
7957 static $DESCRIPTOR(pidsymbol, "SHELL_BACKGROUND_PID");
7959 $DESCRIPTOR(pidstr, "");
7961 unsigned long int flags = 17, one = 1, retsts;
7964 strcat(command, argv[0]);
7965 len = strlen(command);
7966 while (--argc && (len < MAX_DCL_SYMBOL))
7968 strcat(command, " \"");
7969 strcat(command, *(++argv));
7970 strcat(command, "\"");
7971 len = strlen(command);
7973 value.dsc$a_pointer = command;
7974 value.dsc$w_length = strlen(value.dsc$a_pointer);
7975 _ckvmssts_noperl(lib$set_symbol(&cmd, &value));
7976 retsts = lib$spawn(&cmd, &null, 0, &flags, 0, &pid);
7977 if (retsts == 0x38250) { /* DCL-W-NOTIFY - We must be BATCH, so retry */
7978 _ckvmssts_noperl(lib$spawn(&cmd, &null, 0, &one, 0, &pid));
7981 _ckvmssts_noperl(retsts);
7983 #ifdef ARGPROC_DEBUG
7984 PerlIO_printf(Perl_debug_log, "%s\n", command);
7986 sprintf(pidstring, "%08X", pid);
7987 PerlIO_printf(Perl_debug_log, "%s\n", pidstring);
7988 pidstr.dsc$a_pointer = pidstring;
7989 pidstr.dsc$w_length = strlen(pidstr.dsc$a_pointer);
7990 lib$set_symbol(&pidsymbol, &pidstr);
7994 /***** End of code taken from Mark Pizzolato's argproc.c package *****/
7997 /* OS-specific initialization at image activation (not thread startup) */
7998 /* Older VAXC header files lack these constants */
7999 #ifndef JPI$_RIGHTS_SIZE
8000 # define JPI$_RIGHTS_SIZE 817
8002 #ifndef KGB$M_SUBSYSTEM
8003 # define KGB$M_SUBSYSTEM 0x8
8006 /* Avoid Newx() in vms_image_init as thread context has not been initialized. */
8008 /*{{{void vms_image_init(int *, char ***)*/
8010 vms_image_init(int *argcp, char ***argvp)
8012 char eqv[LNM$C_NAMLENGTH+1] = "";
8013 unsigned int len, tabct = 8, tabidx = 0;
8014 unsigned long int *mask, iosb[2], i, rlst[128], rsz;
8015 unsigned long int iprv[(sizeof(union prvdef) + sizeof(unsigned long int) - 1) / sizeof(unsigned long int)];
8016 unsigned short int dummy, rlen;
8017 struct dsc$descriptor_s **tabvec;
8018 #if defined(PERL_IMPLICIT_CONTEXT)
8021 struct itmlst_3 jpilist[4] = { {sizeof iprv, JPI$_IMAGPRIV, iprv, &dummy},
8022 {sizeof rlst, JPI$_RIGHTSLIST, rlst, &rlen},
8023 { sizeof rsz, JPI$_RIGHTS_SIZE, &rsz, &dummy},
8026 #ifdef KILL_BY_SIGPRC
8027 Perl_csighandler_init();
8030 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,jpilist,iosb,NULL,NULL));
8031 _ckvmssts_noperl(iosb[0]);
8032 for (i = 0; i < sizeof iprv / sizeof(unsigned long int); i++) {
8033 if (iprv[i]) { /* Running image installed with privs? */
8034 _ckvmssts_noperl(sys$setprv(0,iprv,0,NULL)); /* Turn 'em off. */
8039 /* Rights identifiers might trigger tainting as well. */
8040 if (!will_taint && (rlen || rsz)) {
8041 while (rlen < rsz) {
8042 /* We didn't get all the identifiers on the first pass. Allocate a
8043 * buffer much larger than $GETJPI wants (rsz is size in bytes that
8044 * were needed to hold all identifiers at time of last call; we'll
8045 * allocate that many unsigned long ints), and go back and get 'em.
8046 * If it gave us less than it wanted to despite ample buffer space,
8047 * something's broken. Is your system missing a system identifier?
8049 if (rsz <= jpilist[1].buflen) {
8050 /* Perl_croak accvios when used this early in startup. */
8051 fprintf(stderr, "vms_image_init: $getjpiw refuses to store RIGHTSLIST of %u bytes in buffer of %u bytes.\n%s",
8052 rsz, (unsigned long) jpilist[1].buflen,
8053 "Check your rights database for corruption.\n");
8056 if (jpilist[1].bufadr != rlst) PerlMem_free(jpilist[1].bufadr);
8057 jpilist[1].bufadr = mask = (unsigned long int *) PerlMem_malloc(rsz * sizeof(unsigned long int));
8058 if (mask == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8059 jpilist[1].buflen = rsz * sizeof(unsigned long int);
8060 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,&jpilist[1],iosb,NULL,NULL));
8061 _ckvmssts_noperl(iosb[0]);
8063 mask = jpilist[1].bufadr;
8064 /* Check attribute flags for each identifier (2nd longword); protected
8065 * subsystem identifiers trigger tainting.
8067 for (i = 1; i < (rlen + sizeof(unsigned long int) - 1) / sizeof(unsigned long int); i += 2) {
8068 if (mask[i] & KGB$M_SUBSYSTEM) {
8073 if (mask != rlst) PerlMem_free(mask);
8076 /* When Perl is in decc_filename_unix_report mode and is run from a concealed
8077 * logical, some versions of the CRTL will add a phanthom /000000/
8078 * directory. This needs to be removed.
8080 if (decc_filename_unix_report) {
8083 ulen = strlen(argvp[0][0]);
8085 zeros = strstr(argvp[0][0], "/000000/");
8086 if (zeros != NULL) {
8088 mlen = ulen - (zeros - argvp[0][0]) - 7;
8089 memmove(zeros, &zeros[7], mlen);
8091 argvp[0][0][ulen] = '\0';
8094 /* It also may have a trailing dot that needs to be removed otherwise
8095 * it will be converted to VMS mode incorrectly.
8098 if ((argvp[0][0][ulen] == '.') && (decc_readdir_dropdotnotype))
8099 argvp[0][0][ulen] = '\0';
8102 /* We need to use this hack to tell Perl it should run with tainting,
8103 * since its tainting flag may be part of the PL_curinterp struct, which
8104 * hasn't been allocated when vms_image_init() is called.
8107 char **newargv, **oldargv;
8109 newargv = (char **) PerlMem_malloc(((*argcp)+2) * sizeof(char *));
8110 if (newargv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8111 newargv[0] = oldargv[0];
8112 newargv[1] = PerlMem_malloc(3 * sizeof(char));
8113 if (newargv[1] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8114 strcpy(newargv[1], "-T");
8115 Copy(&oldargv[1],&newargv[2],(*argcp)-1,char **);
8117 newargv[*argcp] = NULL;
8118 /* We orphan the old argv, since we don't know where it's come from,
8119 * so we don't know how to free it.
8123 else { /* Did user explicitly request tainting? */
8125 char *cp, **av = *argvp;
8126 for (i = 1; i < *argcp; i++) {
8127 if (*av[i] != '-') break;
8128 for (cp = av[i]+1; *cp; cp++) {
8129 if (*cp == 'T') { will_taint = 1; break; }
8130 else if ( (*cp == 'd' || *cp == 'V') && *(cp+1) == ':' ||
8131 strchr("DFIiMmx",*cp)) break;
8133 if (will_taint) break;
8138 len = my_trnlnm("PERL_ENV_TABLES",eqv,tabidx);
8141 tabvec = (struct dsc$descriptor_s **)
8142 PerlMem_malloc(tabct * sizeof(struct dsc$descriptor_s *));
8143 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8145 else if (tabidx >= tabct) {
8147 tabvec = (struct dsc$descriptor_s **) PerlMem_realloc(tabvec, tabct * sizeof(struct dsc$descriptor_s *));
8148 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8150 tabvec[tabidx] = (struct dsc$descriptor_s *) PerlMem_malloc(sizeof(struct dsc$descriptor_s));
8151 if (tabvec[tabidx] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8152 tabvec[tabidx]->dsc$w_length = 0;
8153 tabvec[tabidx]->dsc$b_dtype = DSC$K_DTYPE_T;
8154 tabvec[tabidx]->dsc$b_class = DSC$K_CLASS_D;
8155 tabvec[tabidx]->dsc$a_pointer = NULL;
8156 _ckvmssts_noperl(lib$scopy_r_dx(&len,eqv,tabvec[tabidx]));
8158 if (tabidx) { tabvec[tabidx] = NULL; env_tables = tabvec; }
8160 getredirection(argcp,argvp);
8161 #if defined(USE_ITHREADS) && ( defined(__DECC) || defined(__DECCXX) )
8163 # include <reentrancy.h>
8164 decc$set_reentrancy(C$C_MULTITHREAD);
8173 * Trim Unix-style prefix off filespec, so it looks like what a shell
8174 * glob expansion would return (i.e. from specified prefix on, not
8175 * full path). Note that returned filespec is Unix-style, regardless
8176 * of whether input filespec was VMS-style or Unix-style.
8178 * fspec is filespec to be trimmed, and wildspec is wildcard spec used to
8179 * determine prefix (both may be in VMS or Unix syntax). opts is a bit
8180 * vector of options; at present, only bit 0 is used, and if set tells
8181 * trim unixpath to try the current default directory as a prefix when
8182 * presented with a possibly ambiguous ... wildcard.
8184 * Returns !=0 on success, with trimmed filespec replacing contents of
8185 * fspec, and 0 on failure, with contents of fpsec unchanged.
8187 /*{{{int trim_unixpath(char *fspec, char *wildspec, int opts)*/
8189 Perl_trim_unixpath(pTHX_ char *fspec, const char *wildspec, int opts)
8191 char *unixified, *unixwild,
8192 *template, *base, *end, *cp1, *cp2;
8193 register int tmplen, reslen = 0, dirs = 0;
8195 unixwild = PerlMem_malloc(VMS_MAXRSS);
8196 if (unixwild == NULL) _ckvmssts(SS$_INSFMEM);
8197 if (!wildspec || !fspec) return 0;
8198 template = unixwild;
8199 if (strpbrk(wildspec,"]>:") != NULL) {
8200 if (do_tounixspec(wildspec,unixwild,0,NULL) == NULL) {
8201 PerlMem_free(unixwild);
8206 strncpy(unixwild, wildspec, VMS_MAXRSS-1);
8207 unixwild[VMS_MAXRSS-1] = 0;
8209 unixified = PerlMem_malloc(VMS_MAXRSS);
8210 if (unixified == NULL) _ckvmssts(SS$_INSFMEM);
8211 if (strpbrk(fspec,"]>:") != NULL) {
8212 if (do_tounixspec(fspec,unixified,0,NULL) == NULL) {
8213 PerlMem_free(unixwild);
8214 PerlMem_free(unixified);
8217 else base = unixified;
8218 /* reslen != 0 ==> we had to unixify resultant filespec, so we must
8219 * check to see that final result fits into (isn't longer than) fspec */
8220 reslen = strlen(fspec);
8224 /* No prefix or absolute path on wildcard, so nothing to remove */
8225 if (!*template || *template == '/') {
8226 PerlMem_free(unixwild);
8227 if (base == fspec) {
8228 PerlMem_free(unixified);
8231 tmplen = strlen(unixified);
8232 if (tmplen > reslen) {
8233 PerlMem_free(unixified);
8234 return 0; /* not enough space */
8236 /* Copy unixified resultant, including trailing NUL */
8237 memmove(fspec,unixified,tmplen+1);
8238 PerlMem_free(unixified);
8242 for (end = base; *end; end++) ; /* Find end of resultant filespec */
8243 if ((cp1 = strstr(template,".../")) == NULL) { /* No ...; just count elts */
8244 for (cp1 = template; *cp1; cp1++) if (*cp1 == '/') dirs++;
8245 for (cp1 = end ;cp1 >= base; cp1--)
8246 if ((*cp1 == '/') && !dirs--) /* postdec so we get front of rel path */
8248 if (cp1 != fspec) memmove(fspec,cp1, end - cp1 + 1);
8249 PerlMem_free(unixified);
8250 PerlMem_free(unixwild);
8255 char *front, *nextell, *lcend, *lcfront, *ellipsis = cp1;
8256 int ells = 1, totells, segdirs, match;
8257 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL},
8258 resdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
8260 while ((cp1 = strstr(ellipsis+4,".../")) != NULL) {ellipsis = cp1; ells++;}
8262 for (cp1 = ellipsis+4; *cp1; cp1++) if (*cp1 == '/') dirs++;
8263 tpl = PerlMem_malloc(VMS_MAXRSS);
8264 if (tpl == NULL) _ckvmssts(SS$_INSFMEM);
8265 if (ellipsis == template && opts & 1) {
8266 /* Template begins with an ellipsis. Since we can't tell how many
8267 * directory names at the front of the resultant to keep for an
8268 * arbitrary starting point, we arbitrarily choose the current
8269 * default directory as a starting point. If it's there as a prefix,
8270 * clip it off. If not, fall through and act as if the leading
8271 * ellipsis weren't there (i.e. return shortest possible path that
8272 * could match template).
8274 if (getcwd(tpl, (VMS_MAXRSS - 1),0) == NULL) {
8276 PerlMem_free(unixified);
8277 PerlMem_free(unixwild);
8280 if (!decc_efs_case_preserve) {
8281 for (cp1 = tpl, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
8282 if (_tolower(*cp1) != _tolower(*cp2)) break;
8284 segdirs = dirs - totells; /* Min # of dirs we must have left */
8285 for (front = cp2+1; *front; front++) if (*front == '/') segdirs--;
8286 if (*cp1 == '\0' && *cp2 == '/' && segdirs < 1) {
8287 memmove(fspec,cp2+1,end - cp2);
8289 PerlMem_free(unixified);
8290 PerlMem_free(unixwild);
8294 /* First off, back up over constant elements at end of path */
8296 for (front = end ; front >= base; front--)
8297 if (*front == '/' && !dirs--) { front++; break; }
8299 lcres = PerlMem_malloc(VMS_MAXRSS);
8300 if (lcres == NULL) _ckvmssts(SS$_INSFMEM);
8301 for (cp1=template,cp2=lcres; *cp1 && cp2 <= lcres + (VMS_MAXRSS - 1);
8303 if (!decc_efs_case_preserve) {
8304 *cp2 = _tolower(*cp1); /* Make lc copy for match */
8312 PerlMem_free(unixified);
8313 PerlMem_free(unixwild);
8314 PerlMem_free(lcres);
8315 return 0; /* Path too long. */
8318 *cp2 = '\0'; /* Pick up with memcpy later */
8319 lcfront = lcres + (front - base);
8320 /* Now skip over each ellipsis and try to match the path in front of it. */
8322 for (cp1 = ellipsis - 2; cp1 >= template; cp1--)
8323 if (*(cp1) == '.' && *(cp1+1) == '.' &&
8324 *(cp1+2) == '.' && *(cp1+3) == '/' ) break;
8325 if (cp1 < template) break; /* template started with an ellipsis */
8326 if (cp1 + 4 == ellipsis) { /* Consecutive ellipses */
8327 ellipsis = cp1; continue;
8329 wilddsc.dsc$a_pointer = tpl;
8330 wilddsc.dsc$w_length = resdsc.dsc$w_length = ellipsis - 1 - cp1;
8332 for (segdirs = 0, cp2 = tpl;
8333 cp1 <= ellipsis - 1 && cp2 <= tpl + (VMS_MAXRSS-1);
8335 if (*cp1 == '?') *cp2 = '%'; /* Substitute VMS' wildcard for Unix' */
8337 if (!decc_efs_case_preserve) {
8338 *cp2 = _tolower(*cp1); /* else lowercase for match */
8341 *cp2 = *cp1; /* else preserve case for match */
8344 if (*cp2 == '/') segdirs++;
8346 if (cp1 != ellipsis - 1) {
8348 PerlMem_free(unixified);
8349 PerlMem_free(unixwild);
8350 PerlMem_free(lcres);
8351 return 0; /* Path too long */
8353 /* Back up at least as many dirs as in template before matching */
8354 for (cp1 = lcfront - 1; segdirs && cp1 >= lcres; cp1--)
8355 if (*cp1 == '/' && !segdirs--) { cp1++; break; }
8356 for (match = 0; cp1 > lcres;) {
8357 resdsc.dsc$a_pointer = cp1;
8358 if (str$match_wild(&wilddsc,&resdsc) == STR$_MATCH) {
8360 if (match == 1) lcfront = cp1;
8362 for ( ; cp1 >= lcres; cp1--) if (*cp1 == '/') { cp1++; break; }
8366 PerlMem_free(unixified);
8367 PerlMem_free(unixwild);
8368 PerlMem_free(lcres);
8369 return 0; /* Can't find prefix ??? */
8371 if (match > 1 && opts & 1) {
8372 /* This ... wildcard could cover more than one set of dirs (i.e.
8373 * a set of similar dir names is repeated). If the template
8374 * contains more than 1 ..., upstream elements could resolve the
8375 * ambiguity, but it's not worth a full backtracking setup here.
8376 * As a quick heuristic, clip off the current default directory
8377 * if it's present to find the trimmed spec, else use the
8378 * shortest string that this ... could cover.
8380 char def[NAM$C_MAXRSS+1], *st;
8382 if (getcwd(def, sizeof def,0) == NULL) {
8383 Safefree(unixified);
8389 if (!decc_efs_case_preserve) {
8390 for (cp1 = def, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
8391 if (_tolower(*cp1) != _tolower(*cp2)) break;
8393 segdirs = dirs - totells; /* Min # of dirs we must have left */
8394 for (st = cp2+1; *st; st++) if (*st == '/') segdirs--;
8395 if (*cp1 == '\0' && *cp2 == '/') {
8396 memmove(fspec,cp2+1,end - cp2);
8398 PerlMem_free(unixified);
8399 PerlMem_free(unixwild);
8400 PerlMem_free(lcres);
8403 /* Nope -- stick with lcfront from above and keep going. */
8406 memmove(fspec,base + (lcfront - lcres), lcend - lcfront + 1);
8408 PerlMem_free(unixified);
8409 PerlMem_free(unixwild);
8410 PerlMem_free(lcres);
8415 } /* end of trim_unixpath() */
8420 * VMS readdir() routines.
8421 * Written by Rich $alz, <rsalz@bbn.com> in August, 1990.
8423 * 21-Jul-1994 Charles Bailey bailey@newman.upenn.edu
8424 * Minor modifications to original routines.
8427 /* readdir may have been redefined by reentr.h, so make sure we get
8428 * the local version for what we do here.
8433 #if !defined(PERL_IMPLICIT_CONTEXT)
8434 # define readdir Perl_readdir
8436 # define readdir(a) Perl_readdir(aTHX_ a)
8439 /* Number of elements in vms_versions array */
8440 #define VERSIZE(e) (sizeof e->vms_versions / sizeof e->vms_versions[0])
8443 * Open a directory, return a handle for later use.
8445 /*{{{ DIR *opendir(char*name) */
8447 Perl_opendir(pTHX_ const char *name)
8455 if (decc_efs_charset) {
8456 unix_flag = is_unix_filespec(name);
8459 Newx(dir, VMS_MAXRSS, char);
8460 if (do_tovmspath(name,dir,0,NULL) == NULL) {
8464 /* Check access before stat; otherwise stat does not
8465 * accurately report whether it's a directory.
8467 if (!cando_by_name_int(S_IRUSR,0,dir,PERL_RMSEXPAND_M_VMS_IN)) {
8468 /* cando_by_name has already set errno */
8472 if (flex_stat(dir,&sb) == -1) return NULL;
8473 if (!S_ISDIR(sb.st_mode)) {
8475 set_errno(ENOTDIR); set_vaxc_errno(RMS$_DIR);
8478 /* Get memory for the handle, and the pattern. */
8480 Newx(dd->pattern,strlen(dir)+sizeof "*.*" + 1,char);
8482 /* Fill in the fields; mainly playing with the descriptor. */
8483 sprintf(dd->pattern, "%s*.*",dir);
8489 dd->flags = PERL_VMSDIR_M_UNIXSPECS;
8490 dd->pat.dsc$a_pointer = dd->pattern;
8491 dd->pat.dsc$w_length = strlen(dd->pattern);
8492 dd->pat.dsc$b_dtype = DSC$K_DTYPE_T;
8493 dd->pat.dsc$b_class = DSC$K_CLASS_S;
8494 #if defined(USE_ITHREADS)
8495 Newx(dd->mutex,1,perl_mutex);
8496 MUTEX_INIT( (perl_mutex *) dd->mutex );
8502 } /* end of opendir() */
8506 * Set the flag to indicate we want versions or not.
8508 /*{{{ void vmsreaddirversions(DIR *dd, int flag)*/
8510 vmsreaddirversions(DIR *dd, int flag)
8513 dd->flags |= PERL_VMSDIR_M_VERSIONS;
8515 dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
8520 * Free up an opened directory.
8522 /*{{{ void closedir(DIR *dd)*/
8524 Perl_closedir(DIR *dd)
8528 sts = lib$find_file_end(&dd->context);
8529 Safefree(dd->pattern);
8530 #if defined(USE_ITHREADS)
8531 MUTEX_DESTROY( (perl_mutex *) dd->mutex );
8532 Safefree(dd->mutex);
8539 * Collect all the version numbers for the current file.
8542 collectversions(pTHX_ DIR *dd)
8544 struct dsc$descriptor_s pat;
8545 struct dsc$descriptor_s res;
8547 char *p, *text, *buff;
8549 unsigned long context, tmpsts;
8551 /* Convenient shorthand. */
8554 /* Add the version wildcard, ignoring the "*.*" put on before */
8555 i = strlen(dd->pattern);
8556 Newx(text,i + e->d_namlen + 3,char);
8557 strcpy(text, dd->pattern);
8558 sprintf(&text[i - 3], "%s;*", e->d_name);
8560 /* Set up the pattern descriptor. */
8561 pat.dsc$a_pointer = text;
8562 pat.dsc$w_length = i + e->d_namlen - 1;
8563 pat.dsc$b_dtype = DSC$K_DTYPE_T;
8564 pat.dsc$b_class = DSC$K_CLASS_S;
8566 /* Set up result descriptor. */
8567 Newx(buff, VMS_MAXRSS, char);
8568 res.dsc$a_pointer = buff;
8569 res.dsc$w_length = VMS_MAXRSS - 1;
8570 res.dsc$b_dtype = DSC$K_DTYPE_T;
8571 res.dsc$b_class = DSC$K_CLASS_S;
8573 /* Read files, collecting versions. */
8574 for (context = 0, e->vms_verscount = 0;
8575 e->vms_verscount < VERSIZE(e);
8576 e->vms_verscount++) {
8578 unsigned long flags = 0;
8580 #ifdef VMS_LONGNAME_SUPPORT
8581 flags = LIB$M_FIL_LONG_NAMES;
8583 tmpsts = lib$find_file(&pat, &res, &context, NULL, NULL, &rsts, &flags);
8584 if (tmpsts == RMS$_NMF || context == 0) break;
8586 buff[VMS_MAXRSS - 1] = '\0';
8587 if ((p = strchr(buff, ';')))
8588 e->vms_versions[e->vms_verscount] = atoi(p + 1);
8590 e->vms_versions[e->vms_verscount] = -1;
8593 _ckvmssts(lib$find_file_end(&context));
8597 } /* end of collectversions() */
8600 * Read the next entry from the directory.
8602 /*{{{ struct dirent *readdir(DIR *dd)*/
8604 Perl_readdir(pTHX_ DIR *dd)
8606 struct dsc$descriptor_s res;
8608 unsigned long int tmpsts;
8610 unsigned long flags = 0;
8611 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
8612 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
8614 /* Set up result descriptor, and get next file. */
8615 Newx(buff, VMS_MAXRSS, char);
8616 res.dsc$a_pointer = buff;
8617 res.dsc$w_length = VMS_MAXRSS - 1;
8618 res.dsc$b_dtype = DSC$K_DTYPE_T;
8619 res.dsc$b_class = DSC$K_CLASS_S;
8621 #ifdef VMS_LONGNAME_SUPPORT
8622 flags = LIB$M_FIL_LONG_NAMES;
8625 tmpsts = lib$find_file
8626 (&dd->pat, &res, &dd->context, NULL, NULL, &rsts, &flags);
8627 if ( tmpsts == RMS$_NMF || dd->context == 0) return NULL; /* None left. */
8628 if (!(tmpsts & 1)) {
8629 set_vaxc_errno(tmpsts);
8632 set_errno(EACCES); break;
8634 set_errno(ENODEV); break;
8636 set_errno(ENOTDIR); break;
8637 case RMS$_FNF: case RMS$_DNF:
8638 set_errno(ENOENT); break;
8646 /* Force the buffer to end with a NUL, and downcase name to match C convention. */
8647 if (!decc_efs_case_preserve) {
8648 buff[VMS_MAXRSS - 1] = '\0';
8649 for (p = buff; *p; p++) *p = _tolower(*p);
8652 /* we don't want to force to lowercase, just null terminate */
8653 buff[res.dsc$w_length] = '\0';
8655 while (--p >= buff) if (!isspace(*p)) break; /* Do we really need this? */
8658 /* Skip any directory component and just copy the name. */
8659 sts = vms_split_path
8674 /* Drop NULL extensions on UNIX file specification */
8675 if ((dd->flags & PERL_VMSDIR_M_UNIXSPECS &&
8676 (e_len == 1) && decc_readdir_dropdotnotype)) {
8681 strncpy(dd->entry.d_name, n_spec, n_len + e_len);
8682 dd->entry.d_name[n_len + e_len] = '\0';
8683 dd->entry.d_namlen = strlen(dd->entry.d_name);
8685 /* Convert the filename to UNIX format if needed */
8686 if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
8688 /* Translate the encoded characters. */
8689 /* Fixme: unicode handling could result in embedded 0 characters */
8690 if (strchr(dd->entry.d_name, '^') != NULL) {
8694 p = dd->entry.d_name;
8698 x = copy_expand_vms_filename_escape(q, p, &y);
8702 /* if y > 1, then this is a wide file specification */
8703 /* Wide file specifications need to be passed in Perl */
8704 /* counted strings apparently with a unicode flag */
8707 strcpy(dd->entry.d_name, new_name);
8711 dd->entry.vms_verscount = 0;
8712 if (dd->flags & PERL_VMSDIR_M_VERSIONS) collectversions(aTHX_ dd);
8716 } /* end of readdir() */
8720 * Read the next entry from the directory -- thread-safe version.
8722 /*{{{ int readdir_r(DIR *dd, struct dirent *entry, struct dirent **result)*/
8724 Perl_readdir_r(pTHX_ DIR *dd, struct dirent *entry, struct dirent **result)
8728 MUTEX_LOCK( (perl_mutex *) dd->mutex );
8730 entry = readdir(dd);
8732 retval = ( *result == NULL ? errno : 0 );
8734 MUTEX_UNLOCK( (perl_mutex *) dd->mutex );
8738 } /* end of readdir_r() */
8742 * Return something that can be used in a seekdir later.
8744 /*{{{ long telldir(DIR *dd)*/
8746 Perl_telldir(DIR *dd)
8753 * Return to a spot where we used to be. Brute force.
8755 /*{{{ void seekdir(DIR *dd,long count)*/
8757 Perl_seekdir(pTHX_ DIR *dd, long count)
8761 /* If we haven't done anything yet... */
8765 /* Remember some state, and clear it. */
8766 old_flags = dd->flags;
8767 dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
8768 _ckvmssts(lib$find_file_end(&dd->context));
8771 /* The increment is in readdir(). */
8772 for (dd->count = 0; dd->count < count; )
8775 dd->flags = old_flags;
8777 } /* end of seekdir() */
8780 /* VMS subprocess management
8782 * my_vfork() - just a vfork(), after setting a flag to record that
8783 * the current script is trying a Unix-style fork/exec.
8785 * vms_do_aexec() and vms_do_exec() are called in response to the
8786 * perl 'exec' function. If this follows a vfork call, then they
8787 * call out the regular perl routines in doio.c which do an
8788 * execvp (for those who really want to try this under VMS).
8789 * Otherwise, they do exactly what the perl docs say exec should
8790 * do - terminate the current script and invoke a new command
8791 * (See below for notes on command syntax.)
8793 * do_aspawn() and do_spawn() implement the VMS side of the perl
8794 * 'system' function.
8796 * Note on command arguments to perl 'exec' and 'system': When handled
8797 * in 'VMSish fashion' (i.e. not after a call to vfork) The args
8798 * are concatenated to form a DCL command string. If the first arg
8799 * begins with '$' (i.e. the perl script had "\$ Type" or some such),
8800 * the command string is handed off to DCL directly. Otherwise,
8801 * the first token of the command is taken as the filespec of an image
8802 * to run. The filespec is expanded using a default type of '.EXE' and
8803 * the process defaults for device, directory, etc., and if found, the resultant
8804 * filespec is invoked using the DCL verb 'MCR', and passed the rest of
8805 * the command string as parameters. This is perhaps a bit complicated,
8806 * but I hope it will form a happy medium between what VMS folks expect
8807 * from lib$spawn and what Unix folks expect from exec.
8810 static int vfork_called;
8812 /*{{{int my_vfork()*/
8823 vms_execfree(struct dsc$descriptor_s *vmscmd)
8826 if (vmscmd->dsc$a_pointer) {
8827 PerlMem_free(vmscmd->dsc$a_pointer);
8829 PerlMem_free(vmscmd);
8834 setup_argstr(pTHX_ SV *really, SV **mark, SV **sp)
8836 char *junk, *tmps = Nullch;
8837 register size_t cmdlen = 0;
8844 tmps = SvPV(really,rlen);
8851 for (idx++; idx <= sp; idx++) {
8853 junk = SvPVx(*idx,rlen);
8854 cmdlen += rlen ? rlen + 1 : 0;
8857 Newx(PL_Cmd, cmdlen+1, char);
8859 if (tmps && *tmps) {
8860 strcpy(PL_Cmd,tmps);
8863 else *PL_Cmd = '\0';
8864 while (++mark <= sp) {
8866 char *s = SvPVx(*mark,n_a);
8868 if (*PL_Cmd) strcat(PL_Cmd," ");
8874 } /* end of setup_argstr() */
8877 static unsigned long int
8878 setup_cmddsc(pTHX_ const char *incmd, int check_img, int *suggest_quote,
8879 struct dsc$descriptor_s **pvmscmd)
8881 char vmsspec[NAM$C_MAXRSS+1], resspec[NAM$C_MAXRSS+1];
8882 char image_name[NAM$C_MAXRSS+1];
8883 char image_argv[NAM$C_MAXRSS+1];
8884 $DESCRIPTOR(defdsc,".EXE");
8885 $DESCRIPTOR(defdsc2,".");
8886 $DESCRIPTOR(resdsc,resspec);
8887 struct dsc$descriptor_s *vmscmd;
8888 struct dsc$descriptor_s imgdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
8889 unsigned long int cxt = 0, flags = 1, retsts = SS$_NORMAL;
8890 register char *s, *rest, *cp, *wordbreak;
8895 vmscmd = PerlMem_malloc(sizeof(struct dsc$descriptor_s));
8896 if (vmscmd == NULL) _ckvmssts(SS$_INSFMEM);
8898 /* Make a copy for modification */
8899 cmdlen = strlen(incmd);
8900 cmd = PerlMem_malloc(cmdlen+1);
8901 if (cmd == NULL) _ckvmssts(SS$_INSFMEM);
8902 strncpy(cmd, incmd, cmdlen);
8907 vmscmd->dsc$a_pointer = NULL;
8908 vmscmd->dsc$b_dtype = DSC$K_DTYPE_T;
8909 vmscmd->dsc$b_class = DSC$K_CLASS_S;
8910 vmscmd->dsc$w_length = 0;
8911 if (pvmscmd) *pvmscmd = vmscmd;
8913 if (suggest_quote) *suggest_quote = 0;
8915 if (strlen(cmd) > MAX_DCL_LINE_LENGTH) {
8917 return CLI$_BUFOVF; /* continuation lines currently unsupported */
8922 while (*s && isspace(*s)) s++;
8924 if (*s == '@' || *s == '$') {
8925 vmsspec[0] = *s; rest = s + 1;
8926 for (cp = &vmsspec[1]; *rest && isspace(*rest); rest++,cp++) *cp = *rest;
8928 else { cp = vmsspec; rest = s; }
8929 if (*rest == '.' || *rest == '/') {
8932 *rest && !isspace(*rest) && cp2 - resspec < sizeof resspec;
8933 rest++, cp2++) *cp2 = *rest;
8935 if (do_tovmsspec(resspec,cp,0,NULL)) {
8938 for (cp2 = vmsspec + strlen(vmsspec);
8939 *rest && cp2 - vmsspec < sizeof vmsspec;
8940 rest++, cp2++) *cp2 = *rest;
8945 /* Intuit whether verb (first word of cmd) is a DCL command:
8946 * - if first nonspace char is '@', it's a DCL indirection
8948 * - if verb contains a filespec separator, it's not a DCL command
8949 * - if it doesn't, caller tells us whether to default to a DCL
8950 * command, or to a local image unless told it's DCL (by leading '$')
8954 if (suggest_quote) *suggest_quote = 1;
8956 register char *filespec = strpbrk(s,":<[.;");
8957 rest = wordbreak = strpbrk(s," \"\t/");
8958 if (!wordbreak) wordbreak = s + strlen(s);
8959 if (*s == '$') check_img = 0;
8960 if (filespec && (filespec < wordbreak)) isdcl = 0;
8961 else isdcl = !check_img;
8966 imgdsc.dsc$a_pointer = s;
8967 imgdsc.dsc$w_length = wordbreak - s;
8968 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
8970 _ckvmssts(lib$find_file_end(&cxt));
8971 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
8972 if (!(retsts & 1) && *s == '$') {
8973 _ckvmssts(lib$find_file_end(&cxt));
8974 imgdsc.dsc$a_pointer++; imgdsc.dsc$w_length--;
8975 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
8977 _ckvmssts(lib$find_file_end(&cxt));
8978 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
8982 _ckvmssts(lib$find_file_end(&cxt));
8987 while (*s && !isspace(*s)) s++;
8990 /* check that it's really not DCL with no file extension */
8991 fp = fopen(resspec,"r","ctx=bin","ctx=rec","shr=get");
8993 char b[256] = {0,0,0,0};
8994 read(fileno(fp), b, 256);
8995 isdcl = isprint(b[0]) && isprint(b[1]) && isprint(b[2]) && isprint(b[3]);
8999 /* Check for script */
9001 if ((b[0] == '#') && (b[1] == '!'))
9003 #ifdef ALTERNATE_SHEBANG
9005 shebang_len = strlen(ALTERNATE_SHEBANG);
9006 if (strncmp(b, ALTERNATE_SHEBANG, shebang_len) == 0) {
9008 perlstr = strstr("perl",b);
9009 if (perlstr == NULL)
9017 if (shebang_len > 0) {
9020 char tmpspec[NAM$C_MAXRSS + 1];
9023 /* Image is following after white space */
9024 /*--------------------------------------*/
9025 while (isprint(b[i]) && isspace(b[i]))
9029 while (isprint(b[i]) && !isspace(b[i])) {
9030 tmpspec[j++] = b[i++];
9031 if (j >= NAM$C_MAXRSS)
9036 /* There may be some default parameters to the image */
9037 /*---------------------------------------------------*/
9039 while (isprint(b[i])) {
9040 image_argv[j++] = b[i++];
9041 if (j >= NAM$C_MAXRSS)
9044 while ((j > 0) && !isprint(image_argv[j-1]))
9048 /* It will need to be converted to VMS format and validated */
9049 if (tmpspec[0] != '\0') {
9052 /* Try to find the exact program requested to be run */
9053 /*---------------------------------------------------*/
9054 iname = do_rmsexpand
9055 (tmpspec, image_name, 0, ".exe",
9056 PERL_RMSEXPAND_M_VMS, NULL, NULL);
9057 if (iname != NULL) {
9058 if (cando_by_name_int
9059 (S_IXUSR,0,image_name,PERL_RMSEXPAND_M_VMS_IN)) {
9060 /* MCR prefix needed */
9064 /* Try again with a null type */
9065 /*----------------------------*/
9066 iname = do_rmsexpand
9067 (tmpspec, image_name, 0, ".",
9068 PERL_RMSEXPAND_M_VMS, NULL, NULL);
9069 if (iname != NULL) {
9070 if (cando_by_name_int
9071 (S_IXUSR,0,image_name, PERL_RMSEXPAND_M_VMS_IN)) {
9072 /* MCR prefix needed */
9078 /* Did we find the image to run the script? */
9079 /*------------------------------------------*/
9083 /* Assume DCL or foreign command exists */
9084 /*--------------------------------------*/
9085 tchr = strrchr(tmpspec, '/');
9092 strcpy(image_name, tchr);
9100 if (check_img && isdcl) return RMS$_FNF;
9102 if (cando_by_name(S_IXUSR,0,resspec)) {
9103 vmscmd->dsc$a_pointer = PerlMem_malloc(MAX_DCL_LINE_LENGTH);
9104 if (vmscmd->dsc$a_pointer == NULL) _ckvmssts(SS$_INSFMEM);
9106 strcpy(vmscmd->dsc$a_pointer,"$ MCR ");
9107 if (image_name[0] != 0) {
9108 strcat(vmscmd->dsc$a_pointer, image_name);
9109 strcat(vmscmd->dsc$a_pointer, " ");
9111 } else if (image_name[0] != 0) {
9112 strcpy(vmscmd->dsc$a_pointer, image_name);
9113 strcat(vmscmd->dsc$a_pointer, " ");
9115 strcpy(vmscmd->dsc$a_pointer,"@");
9117 if (suggest_quote) *suggest_quote = 1;
9119 /* If there is an image name, use original command */
9120 if (image_name[0] == 0)
9121 strcat(vmscmd->dsc$a_pointer,resspec);
9124 while (*rest && isspace(*rest)) rest++;
9127 if (image_argv[0] != 0) {
9128 strcat(vmscmd->dsc$a_pointer,image_argv);
9129 strcat(vmscmd->dsc$a_pointer, " ");
9135 rest_len = strlen(rest);
9136 vmscmd_len = strlen(vmscmd->dsc$a_pointer);
9137 if ((rest_len + vmscmd_len) < MAX_DCL_LINE_LENGTH)
9138 strcat(vmscmd->dsc$a_pointer,rest);
9140 retsts = CLI$_BUFOVF;
9142 vmscmd->dsc$w_length = strlen(vmscmd->dsc$a_pointer);
9144 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
9150 /* It's either a DCL command or we couldn't find a suitable image */
9151 vmscmd->dsc$w_length = strlen(cmd);
9153 vmscmd->dsc$a_pointer = PerlMem_malloc(vmscmd->dsc$w_length + 1);
9154 strncpy(vmscmd->dsc$a_pointer,cmd,vmscmd->dsc$w_length);
9155 vmscmd->dsc$a_pointer[vmscmd->dsc$w_length] = 0;
9159 /* check if it's a symbol (for quoting purposes) */
9160 if (suggest_quote && !*suggest_quote) {
9162 char equiv[LNM$C_NAMLENGTH];
9163 struct dsc$descriptor_s eqvdsc = {sizeof(equiv), DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
9164 eqvdsc.dsc$a_pointer = equiv;
9166 iss = lib$get_symbol(vmscmd,&eqvdsc);
9167 if (iss&1 && (*equiv == '$' || *equiv == '@')) *suggest_quote = 1;
9169 if (!(retsts & 1)) {
9170 /* just hand off status values likely to be due to user error */
9171 if (retsts == RMS$_FNF || retsts == RMS$_DNF || retsts == RMS$_PRV ||
9172 retsts == RMS$_DEV || retsts == RMS$_DIR || retsts == RMS$_SYN ||
9173 (retsts & STS$M_CODE) == (SHR$_NOWILD & STS$M_CODE)) return retsts;
9174 else { _ckvmssts(retsts); }
9177 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
9179 } /* end of setup_cmddsc() */
9182 /* {{{ bool vms_do_aexec(SV *really,SV **mark,SV **sp) */
9184 Perl_vms_do_aexec(pTHX_ SV *really,SV **mark,SV **sp)
9190 if (vfork_called) { /* this follows a vfork - act Unixish */
9192 if (vfork_called < 0) {
9193 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
9196 else return do_aexec(really,mark,sp);
9198 /* no vfork - act VMSish */
9199 cmd = setup_argstr(aTHX_ really,mark,sp);
9200 exec_sts = vms_do_exec(cmd);
9201 Safefree(cmd); /* Clean up from setup_argstr() */
9206 } /* end of vms_do_aexec() */
9209 /* {{{bool vms_do_exec(char *cmd) */
9211 Perl_vms_do_exec(pTHX_ const char *cmd)
9213 struct dsc$descriptor_s *vmscmd;
9215 if (vfork_called) { /* this follows a vfork - act Unixish */
9217 if (vfork_called < 0) {
9218 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
9221 else return do_exec(cmd);
9224 { /* no vfork - act VMSish */
9225 unsigned long int retsts;
9228 TAINT_PROPER("exec");
9229 if ((retsts = setup_cmddsc(aTHX_ cmd,1,0,&vmscmd)) & 1)
9230 retsts = lib$do_command(vmscmd);
9233 case RMS$_FNF: case RMS$_DNF:
9234 set_errno(ENOENT); break;
9236 set_errno(ENOTDIR); break;
9238 set_errno(ENODEV); break;
9240 set_errno(EACCES); break;
9242 set_errno(EINVAL); break;
9243 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
9244 set_errno(E2BIG); break;
9245 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
9246 _ckvmssts(retsts); /* fall through */
9247 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
9250 set_vaxc_errno(retsts);
9251 if (ckWARN(WARN_EXEC)) {
9252 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't exec \"%*s\": %s",
9253 vmscmd->dsc$w_length, vmscmd->dsc$a_pointer, Strerror(errno));
9255 vms_execfree(vmscmd);
9260 } /* end of vms_do_exec() */
9263 unsigned long int Perl_do_spawn(pTHX_ const char *);
9265 /* {{{ unsigned long int do_aspawn(void *really,void **mark,void **sp) */
9267 Perl_do_aspawn(pTHX_ void *really,void **mark,void **sp)
9269 unsigned long int sts;
9273 cmd = setup_argstr(aTHX_ (SV *)really,(SV **)mark,(SV **)sp);
9274 sts = do_spawn(cmd);
9275 /* pp_sys will clean up cmd */
9279 } /* end of do_aspawn() */
9282 /* {{{unsigned long int do_spawn(char *cmd) */
9284 Perl_do_spawn(pTHX_ const char *cmd)
9286 unsigned long int sts, substs;
9288 /* The caller of this routine expects to Safefree(PL_Cmd) */
9289 Newx(PL_Cmd,10,char);
9292 TAINT_PROPER("spawn");
9293 if (!cmd || !*cmd) {
9294 sts = lib$spawn(0,0,0,0,0,0,&substs,0,0,0,0,0,0);
9297 case RMS$_FNF: case RMS$_DNF:
9298 set_errno(ENOENT); break;
9300 set_errno(ENOTDIR); break;
9302 set_errno(ENODEV); break;
9304 set_errno(EACCES); break;
9306 set_errno(EINVAL); break;
9307 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
9308 set_errno(E2BIG); break;
9309 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
9310 _ckvmssts(sts); /* fall through */
9311 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
9314 set_vaxc_errno(sts);
9315 if (ckWARN(WARN_EXEC)) {
9316 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't spawn: %s",
9324 fp = safe_popen(aTHX_ cmd, "nW", (int *)&sts);
9329 } /* end of do_spawn() */
9333 static unsigned int *sockflags, sockflagsize;
9336 * Shim fdopen to identify sockets for my_fwrite later, since the stdio
9337 * routines found in some versions of the CRTL can't deal with sockets.
9338 * We don't shim the other file open routines since a socket isn't
9339 * likely to be opened by a name.
9341 /*{{{ FILE *my_fdopen(int fd, const char *mode)*/
9342 FILE *my_fdopen(int fd, const char *mode)
9344 FILE *fp = fdopen(fd, mode);
9347 unsigned int fdoff = fd / sizeof(unsigned int);
9348 Stat_t sbuf; /* native stat; we don't need flex_stat */
9349 if (!sockflagsize || fdoff > sockflagsize) {
9350 if (sockflags) Renew( sockflags,fdoff+2,unsigned int);
9351 else Newx (sockflags,fdoff+2,unsigned int);
9352 memset(sockflags+sockflagsize,0,fdoff + 2 - sockflagsize);
9353 sockflagsize = fdoff + 2;
9355 if (fstat(fd, (struct stat *)&sbuf) == 0 && S_ISSOCK(sbuf.st_mode))
9356 sockflags[fdoff] |= 1 << (fd % sizeof(unsigned int));
9365 * Clear the corresponding bit when the (possibly) socket stream is closed.
9366 * There still a small hole: we miss an implicit close which might occur
9367 * via freopen(). >> Todo
9369 /*{{{ int my_fclose(FILE *fp)*/
9370 int my_fclose(FILE *fp) {
9372 unsigned int fd = fileno(fp);
9373 unsigned int fdoff = fd / sizeof(unsigned int);
9375 if (sockflagsize && fdoff <= sockflagsize)
9376 sockflags[fdoff] &= ~(1 << fd % sizeof(unsigned int));
9384 * A simple fwrite replacement which outputs itmsz*nitm chars without
9385 * introducing record boundaries every itmsz chars.
9386 * We are using fputs, which depends on a terminating null. We may
9387 * well be writing binary data, so we need to accommodate not only
9388 * data with nulls sprinkled in the middle but also data with no null
9391 /*{{{ int my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)*/
9393 my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)
9395 register char *cp, *end, *cpd, *data;
9396 register unsigned int fd = fileno(dest);
9397 register unsigned int fdoff = fd / sizeof(unsigned int);
9399 int bufsize = itmsz * nitm + 1;
9401 if (fdoff < sockflagsize &&
9402 (sockflags[fdoff] | 1 << (fd % sizeof(unsigned int)))) {
9403 if (write(fd, src, itmsz * nitm) == EOF) return EOF;
9407 _ckvmssts_noperl(lib$get_vm(&bufsize, &data));
9408 memcpy( data, src, itmsz*nitm );
9409 data[itmsz*nitm] = '\0';
9411 end = data + itmsz * nitm;
9412 retval = (int) nitm; /* on success return # items written */
9415 while (cpd <= end) {
9416 for (cp = cpd; cp <= end; cp++) if (!*cp) break;
9417 if (fputs(cpd,dest) == EOF) { retval = EOF; break; }
9419 if (fputc('\0',dest) == EOF) { retval = EOF; break; }
9423 if (data) _ckvmssts_noperl(lib$free_vm(&bufsize, &data));
9426 } /* end of my_fwrite() */
9429 /*{{{ int my_flush(FILE *fp)*/
9431 Perl_my_flush(pTHX_ FILE *fp)
9434 if ((res = fflush(fp)) == 0 && fp) {
9435 #ifdef VMS_DO_SOCKETS
9437 if (Fstat(fileno(fp), &s) == 0 && !S_ISSOCK(s.st_mode))
9439 res = fsync(fileno(fp));
9442 * If the flush succeeded but set end-of-file, we need to clear
9443 * the error because our caller may check ferror(). BTW, this
9444 * probably means we just flushed an empty file.
9446 if (res == 0 && vaxc$errno == RMS$_EOF) clearerr(fp);
9453 * Here are replacements for the following Unix routines in the VMS environment:
9454 * getpwuid Get information for a particular UIC or UID
9455 * getpwnam Get information for a named user
9456 * getpwent Get information for each user in the rights database
9457 * setpwent Reset search to the start of the rights database
9458 * endpwent Finish searching for users in the rights database
9460 * getpwuid, getpwnam, and getpwent return a pointer to the passwd structure
9461 * (defined in pwd.h), which contains the following fields:-
9463 * char *pw_name; Username (in lower case)
9464 * char *pw_passwd; Hashed password
9465 * unsigned int pw_uid; UIC
9466 * unsigned int pw_gid; UIC group number
9467 * char *pw_unixdir; Default device/directory (VMS-style)
9468 * char *pw_gecos; Owner name
9469 * char *pw_dir; Default device/directory (Unix-style)
9470 * char *pw_shell; Default CLI name (eg. DCL)
9472 * If the specified user does not exist, getpwuid and getpwnam return NULL.
9474 * pw_uid is the full UIC (eg. what's returned by stat() in st_uid).
9475 * not the UIC member number (eg. what's returned by getuid()),
9476 * getpwuid() can accept either as input (if uid is specified, the caller's
9477 * UIC group is used), though it won't recognise gid=0.
9479 * Note that in VMS it is necessary to have GRPPRV or SYSPRV to return
9480 * information about other users in your group or in other groups, respectively.
9481 * If the required privilege is not available, then these routines fill only
9482 * the pw_name, pw_uid, and pw_gid fields (the others point to an empty
9485 * By Tim Adye (T.J.Adye@rl.ac.uk), 10th February 1995.
9488 /* sizes of various UAF record fields */
9489 #define UAI$S_USERNAME 12
9490 #define UAI$S_IDENT 31
9491 #define UAI$S_OWNER 31
9492 #define UAI$S_DEFDEV 31
9493 #define UAI$S_DEFDIR 63
9494 #define UAI$S_DEFCLI 31
9497 #define valid_uic(uic) ((uic).uic$v_format == UIC$K_UIC_FORMAT && \
9498 (uic).uic$v_member != UIC$K_WILD_MEMBER && \
9499 (uic).uic$v_group != UIC$K_WILD_GROUP)
9501 static char __empty[]= "";
9502 static struct passwd __passwd_empty=
9503 {(char *) __empty, (char *) __empty, 0, 0,
9504 (char *) __empty, (char *) __empty, (char *) __empty, (char *) __empty};
9505 static int contxt= 0;
9506 static struct passwd __pwdcache;
9507 static char __pw_namecache[UAI$S_IDENT+1];
9510 * This routine does most of the work extracting the user information.
9512 static int fillpasswd (pTHX_ const char *name, struct passwd *pwd)
9515 unsigned char length;
9516 char pw_gecos[UAI$S_OWNER+1];
9518 static union uicdef uic;
9520 unsigned char length;
9521 char pw_dir[UAI$S_DEFDEV+UAI$S_DEFDIR+1];
9524 unsigned char length;
9525 char unixdir[UAI$_DEFDEV+UAI$S_DEFDIR+1];
9528 unsigned char length;
9529 char pw_shell[UAI$S_DEFCLI+1];
9531 static char pw_passwd[UAI$S_PWD+1];
9533 static unsigned short lowner, luic, ldefdev, ldefdir, ldefcli, lpwd;
9534 struct dsc$descriptor_s name_desc;
9535 unsigned long int sts;
9537 static struct itmlst_3 itmlst[]= {
9538 {UAI$S_OWNER+1, UAI$_OWNER, &owner, &lowner},
9539 {sizeof(uic), UAI$_UIC, &uic, &luic},
9540 {UAI$S_DEFDEV+1, UAI$_DEFDEV, &defdev, &ldefdev},
9541 {UAI$S_DEFDIR+1, UAI$_DEFDIR, &defdir, &ldefdir},
9542 {UAI$S_DEFCLI+1, UAI$_DEFCLI, &defcli, &ldefcli},
9543 {UAI$S_PWD, UAI$_PWD, pw_passwd, &lpwd},
9544 {0, 0, NULL, NULL}};
9546 name_desc.dsc$w_length= strlen(name);
9547 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
9548 name_desc.dsc$b_class= DSC$K_CLASS_S;
9549 name_desc.dsc$a_pointer= (char *) name; /* read only pointer */
9551 /* Note that sys$getuai returns many fields as counted strings. */
9552 sts= sys$getuai(0, 0, &name_desc, &itmlst, 0, 0, 0);
9553 if (sts == SS$_NOSYSPRV || sts == SS$_NOGRPPRV || sts == RMS$_RNF) {
9554 set_vaxc_errno(sts); set_errno(sts == RMS$_RNF ? EINVAL : EACCES);
9556 else { _ckvmssts(sts); }
9557 if (!(sts & 1)) return 0; /* out here in case _ckvmssts() doesn't abort */
9559 if ((int) owner.length < lowner) lowner= (int) owner.length;
9560 if ((int) defdev.length < ldefdev) ldefdev= (int) defdev.length;
9561 if ((int) defdir.length < ldefdir) ldefdir= (int) defdir.length;
9562 if ((int) defcli.length < ldefcli) ldefcli= (int) defcli.length;
9563 memcpy(&defdev.pw_dir[ldefdev], &defdir.unixdir[0], ldefdir);
9564 owner.pw_gecos[lowner]= '\0';
9565 defdev.pw_dir[ldefdev+ldefdir]= '\0';
9566 defcli.pw_shell[ldefcli]= '\0';
9567 if (valid_uic(uic)) {
9568 pwd->pw_uid= uic.uic$l_uic;
9569 pwd->pw_gid= uic.uic$v_group;
9572 Perl_warn(aTHX_ "getpwnam returned invalid UIC %#o for user \"%s\"");
9573 pwd->pw_passwd= pw_passwd;
9574 pwd->pw_gecos= owner.pw_gecos;
9575 pwd->pw_dir= defdev.pw_dir;
9576 pwd->pw_unixdir= do_tounixpath(defdev.pw_dir, defdir.unixdir,1,NULL);
9577 pwd->pw_shell= defcli.pw_shell;
9578 if (pwd->pw_unixdir && pwd->pw_unixdir[0]) {
9580 ldir= strlen(pwd->pw_unixdir) - 1;
9581 if (pwd->pw_unixdir[ldir]=='/') pwd->pw_unixdir[ldir]= '\0';
9584 strcpy(pwd->pw_unixdir, pwd->pw_dir);
9585 if (!decc_efs_case_preserve)
9586 __mystrtolower(pwd->pw_unixdir);
9591 * Get information for a named user.
9593 /*{{{struct passwd *getpwnam(char *name)*/
9594 struct passwd *Perl_my_getpwnam(pTHX_ const char *name)
9596 struct dsc$descriptor_s name_desc;
9598 unsigned long int status, sts;
9600 __pwdcache = __passwd_empty;
9601 if (!fillpasswd(aTHX_ name, &__pwdcache)) {
9602 /* We still may be able to determine pw_uid and pw_gid */
9603 name_desc.dsc$w_length= strlen(name);
9604 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
9605 name_desc.dsc$b_class= DSC$K_CLASS_S;
9606 name_desc.dsc$a_pointer= (char *) name;
9607 if ((sts = sys$asctoid(&name_desc, &uic, 0)) == SS$_NORMAL) {
9608 __pwdcache.pw_uid= uic.uic$l_uic;
9609 __pwdcache.pw_gid= uic.uic$v_group;
9612 if (sts == SS$_NOSUCHID || sts == SS$_IVIDENT || sts == RMS$_PRV) {
9613 set_vaxc_errno(sts);
9614 set_errno(sts == RMS$_PRV ? EACCES : EINVAL);
9617 else { _ckvmssts(sts); }
9620 strncpy(__pw_namecache, name, sizeof(__pw_namecache));
9621 __pw_namecache[sizeof __pw_namecache - 1] = '\0';
9622 __pwdcache.pw_name= __pw_namecache;
9624 } /* end of my_getpwnam() */
9628 * Get information for a particular UIC or UID.
9629 * Called by my_getpwent with uid=-1 to list all users.
9631 /*{{{struct passwd *my_getpwuid(Uid_t uid)*/
9632 struct passwd *Perl_my_getpwuid(pTHX_ Uid_t uid)
9634 const $DESCRIPTOR(name_desc,__pw_namecache);
9635 unsigned short lname;
9637 unsigned long int status;
9639 if (uid == (unsigned int) -1) {
9641 status = sys$idtoasc(-1, &lname, &name_desc, &uic, 0, &contxt);
9642 if (status == SS$_NOSUCHID || status == RMS$_PRV) {
9643 set_vaxc_errno(status);
9644 set_errno(status == RMS$_PRV ? EACCES : EINVAL);
9648 else { _ckvmssts(status); }
9649 } while (!valid_uic (uic));
9653 if (!uic.uic$v_group)
9654 uic.uic$v_group= PerlProc_getgid();
9656 status = sys$idtoasc(uic.uic$l_uic, &lname, &name_desc, 0, 0, 0);
9657 else status = SS$_IVIDENT;
9658 if (status == SS$_IVIDENT || status == SS$_NOSUCHID ||
9659 status == RMS$_PRV) {
9660 set_vaxc_errno(status); set_errno(status == RMS$_PRV ? EACCES : EINVAL);
9663 else { _ckvmssts(status); }
9665 __pw_namecache[lname]= '\0';
9666 __mystrtolower(__pw_namecache);
9668 __pwdcache = __passwd_empty;
9669 __pwdcache.pw_name = __pw_namecache;
9671 /* Fill in the uid and gid in case fillpasswd can't (eg. no privilege).
9672 The identifier's value is usually the UIC, but it doesn't have to be,
9673 so if we can, we let fillpasswd update this. */
9674 __pwdcache.pw_uid = uic.uic$l_uic;
9675 __pwdcache.pw_gid = uic.uic$v_group;
9677 fillpasswd(aTHX_ __pw_namecache, &__pwdcache);
9680 } /* end of my_getpwuid() */
9684 * Get information for next user.
9686 /*{{{struct passwd *my_getpwent()*/
9687 struct passwd *Perl_my_getpwent(pTHX)
9689 return (my_getpwuid((unsigned int) -1));
9694 * Finish searching rights database for users.
9696 /*{{{void my_endpwent()*/
9697 void Perl_my_endpwent(pTHX)
9700 _ckvmssts(sys$finish_rdb(&contxt));
9706 #ifdef HOMEGROWN_POSIX_SIGNALS
9707 /* Signal handling routines, pulled into the core from POSIX.xs.
9709 * We need these for threads, so they've been rolled into the core,
9710 * rather than left in POSIX.xs.
9712 * (DRS, Oct 23, 1997)
9715 /* sigset_t is atomic under VMS, so these routines are easy */
9716 /*{{{int my_sigemptyset(sigset_t *) */
9717 int my_sigemptyset(sigset_t *set) {
9718 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
9724 /*{{{int my_sigfillset(sigset_t *)*/
9725 int my_sigfillset(sigset_t *set) {
9727 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
9728 for (i = 0; i < NSIG; i++) *set |= (1 << i);
9734 /*{{{int my_sigaddset(sigset_t *set, int sig)*/
9735 int my_sigaddset(sigset_t *set, int sig) {
9736 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
9737 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
9738 *set |= (1 << (sig - 1));
9744 /*{{{int my_sigdelset(sigset_t *set, int sig)*/
9745 int my_sigdelset(sigset_t *set, int sig) {
9746 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
9747 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
9748 *set &= ~(1 << (sig - 1));
9754 /*{{{int my_sigismember(sigset_t *set, int sig)*/
9755 int my_sigismember(sigset_t *set, int sig) {
9756 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
9757 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
9758 return *set & (1 << (sig - 1));
9763 /*{{{int my_sigprocmask(int how, sigset_t *set, sigset_t *oset)*/
9764 int my_sigprocmask(int how, sigset_t *set, sigset_t *oset) {
9767 /* If set and oset are both null, then things are badly wrong. Bail out. */
9768 if ((oset == NULL) && (set == NULL)) {
9769 set_errno(EFAULT); set_vaxc_errno(SS$_ACCVIO);
9773 /* If set's null, then we're just handling a fetch. */
9775 tempmask = sigblock(0);
9780 tempmask = sigsetmask(*set);
9783 tempmask = sigblock(*set);
9786 tempmask = sigblock(0);
9787 sigsetmask(*oset & ~tempmask);
9790 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
9795 /* Did they pass us an oset? If so, stick our holding mask into it */
9802 #endif /* HOMEGROWN_POSIX_SIGNALS */
9805 /* Used for UTC calculation in my_gmtime(), my_localtime(), my_time(),
9806 * my_utime(), and flex_stat(), all of which operate on UTC unless
9807 * VMSISH_TIMES is true.
9809 /* method used to handle UTC conversions:
9810 * 1 == CRTL gmtime(); 2 == SYS$TIMEZONE_DIFFERENTIAL; 3 == no correction
9812 static int gmtime_emulation_type;
9813 /* number of secs to add to UTC POSIX-style time to get local time */
9814 static long int utc_offset_secs;
9816 /* We #defined 'gmtime', 'localtime', and 'time' as 'my_gmtime' etc.
9817 * in vmsish.h. #undef them here so we can call the CRTL routines
9826 * DEC C previous to 6.0 corrupts the behavior of the /prefix
9827 * qualifier with the extern prefix pragma. This provisional
9828 * hack circumvents this prefix pragma problem in previous
9831 #if defined(__VMS_VER) && __VMS_VER >= 70000000
9832 # if defined(VMS_WE_ARE_CASE_SENSITIVE) && (__DECC_VER < 60000000)
9833 # pragma __extern_prefix save
9834 # pragma __extern_prefix "" /* set to empty to prevent prefixing */
9835 # define gmtime decc$__utctz_gmtime
9836 # define localtime decc$__utctz_localtime
9837 # define time decc$__utc_time
9838 # pragma __extern_prefix restore
9840 struct tm *gmtime(), *localtime();
9846 static time_t toutc_dst(time_t loc) {
9849 if ((rsltmp = localtime(&loc)) == NULL) return -1;
9850 loc -= utc_offset_secs;
9851 if (rsltmp->tm_isdst) loc -= 3600;
9854 #define _toutc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
9855 ((gmtime_emulation_type || my_time(NULL)), \
9856 (gmtime_emulation_type == 1 ? toutc_dst(secs) : \
9857 ((secs) - utc_offset_secs))))
9859 static time_t toloc_dst(time_t utc) {
9862 utc += utc_offset_secs;
9863 if ((rsltmp = localtime(&utc)) == NULL) return -1;
9864 if (rsltmp->tm_isdst) utc += 3600;
9867 #define _toloc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
9868 ((gmtime_emulation_type || my_time(NULL)), \
9869 (gmtime_emulation_type == 1 ? toloc_dst(secs) : \
9870 ((secs) + utc_offset_secs))))
9872 #ifndef RTL_USES_UTC
9875 ucx$tz = "EST5EDT4,M4.1.0,M10.5.0" typical
9876 DST starts on 1st sun of april at 02:00 std time
9877 ends on last sun of october at 02:00 dst time
9878 see the UCX management command reference, SET CONFIG TIMEZONE
9879 for formatting info.
9881 No, it's not as general as it should be, but then again, NOTHING
9882 will handle UK times in a sensible way.
9887 parse the DST start/end info:
9888 (Jddd|ddd|Mmon.nth.dow)[/hh:mm:ss]
9892 tz_parse_startend(char *s, struct tm *w, int *past)
9894 int dinm[] = {31,28,31,30,31,30,31,31,30,31,30,31};
9895 int ly, dozjd, d, m, n, hour, min, sec, j, k;
9900 if (!past) return 0;
9903 if (w->tm_year % 4 == 0) ly = 1;
9904 if (w->tm_year % 100 == 0) ly = 0;
9905 if (w->tm_year+1900 % 400 == 0) ly = 1;
9908 dozjd = isdigit(*s);
9909 if (*s == 'J' || *s == 'j' || dozjd) {
9910 if (!dozjd && !isdigit(*++s)) return 0;
9913 d = d*10 + *s++ - '0';
9915 d = d*10 + *s++ - '0';
9918 if (d == 0) return 0;
9919 if (d > 366) return 0;
9921 if (!dozjd && d > 58 && ly) d++; /* after 28 feb */
9924 } else if (*s == 'M' || *s == 'm') {
9925 if (!isdigit(*++s)) return 0;
9927 if (isdigit(*s)) m = 10*m + *s++ - '0';
9928 if (*s != '.') return 0;
9929 if (!isdigit(*++s)) return 0;
9931 if (n < 1 || n > 5) return 0;
9932 if (*s != '.') return 0;
9933 if (!isdigit(*++s)) return 0;
9935 if (d > 6) return 0;
9939 if (!isdigit(*++s)) return 0;
9941 if (isdigit(*s)) hour = 10*hour + *s++ - '0';
9943 if (!isdigit(*++s)) return 0;
9945 if (isdigit(*s)) min = 10*min + *s++ - '0';
9947 if (!isdigit(*++s)) return 0;
9949 if (isdigit(*s)) sec = 10*sec + *s++ - '0';
9959 if (w->tm_yday < d) goto before;
9960 if (w->tm_yday > d) goto after;
9962 if (w->tm_mon+1 < m) goto before;
9963 if (w->tm_mon+1 > m) goto after;
9965 j = (42 + w->tm_wday - w->tm_mday)%7; /*dow of mday 0 */
9966 k = d - j; /* mday of first d */
9968 k += 7 * ((n>4?4:n)-1); /* mday of n'th d */
9969 if (n == 5 && k+7 <= dinm[w->tm_mon]) k += 7;
9970 if (w->tm_mday < k) goto before;
9971 if (w->tm_mday > k) goto after;
9974 if (w->tm_hour < hour) goto before;
9975 if (w->tm_hour > hour) goto after;
9976 if (w->tm_min < min) goto before;
9977 if (w->tm_min > min) goto after;
9978 if (w->tm_sec < sec) goto before;
9992 /* parse the offset: (+|-)hh[:mm[:ss]] */
9995 tz_parse_offset(char *s, int *offset)
9997 int hour = 0, min = 0, sec = 0;
10000 if (!offset) return 0;
10002 if (*s == '-') {neg++; s++;}
10003 if (*s == '+') s++;
10004 if (!isdigit(*s)) return 0;
10006 if (isdigit(*s)) hour = hour*10+(*s++ - '0');
10007 if (hour > 24) return 0;
10009 if (!isdigit(*++s)) return 0;
10011 if (isdigit(*s)) min = min*10 + (*s++ - '0');
10012 if (min > 59) return 0;
10014 if (!isdigit(*++s)) return 0;
10016 if (isdigit(*s)) sec = sec*10 + (*s++ - '0');
10017 if (sec > 59) return 0;
10021 *offset = (hour*60+min)*60 + sec;
10022 if (neg) *offset = -*offset;
10027 input time is w, whatever type of time the CRTL localtime() uses.
10028 sets dst, the zone, and the gmtoff (seconds)
10030 caches the value of TZ and UCX$TZ env variables; note that
10031 my_setenv looks for these and sets a flag if they're changed
10034 We have to watch out for the "australian" case (dst starts in
10035 october, ends in april)...flagged by "reverse" and checked by
10036 scanning through the months of the previous year.
10041 tz_parse(pTHX_ time_t *w, int *dst, char *zone, int *gmtoff)
10046 char *dstzone, *tz, *s_start, *s_end;
10047 int std_off, dst_off, isdst;
10048 int y, dststart, dstend;
10049 static char envtz[1025]; /* longer than any logical, symbol, ... */
10050 static char ucxtz[1025];
10051 static char reversed = 0;
10057 reversed = -1; /* flag need to check */
10058 envtz[0] = ucxtz[0] = '\0';
10059 tz = my_getenv("TZ",0);
10060 if (tz) strcpy(envtz, tz);
10061 tz = my_getenv("UCX$TZ",0);
10062 if (tz) strcpy(ucxtz, tz);
10063 if (!envtz[0] && !ucxtz[0]) return 0; /* we give up */
10066 if (!*tz) tz = ucxtz;
10069 while (isalpha(*s)) s++;
10070 s = tz_parse_offset(s, &std_off);
10072 if (!*s) { /* no DST, hurray we're done! */
10078 while (isalpha(*s)) s++;
10079 s2 = tz_parse_offset(s, &dst_off);
10083 dst_off = std_off - 3600;
10086 if (!*s) { /* default dst start/end?? */
10087 if (tz != ucxtz) { /* if TZ tells zone only, UCX$TZ tells rule */
10088 s = strchr(ucxtz,',');
10090 if (!s || !*s) s = ",M4.1.0,M10.5.0"; /* we know we do dst, default rule */
10092 if (*s != ',') return 0;
10095 when = _toutc(when); /* convert to utc */
10096 when = when - std_off; /* convert to pseudolocal time*/
10098 w2 = localtime(&when);
10101 s = tz_parse_startend(s_start,w2,&dststart);
10103 if (*s != ',') return 0;
10106 when = _toutc(when); /* convert to utc */
10107 when = when - dst_off; /* convert to pseudolocal time*/
10108 w2 = localtime(&when);
10109 if (w2->tm_year != y) { /* spans a year, just check one time */
10110 when += dst_off - std_off;
10111 w2 = localtime(&when);
10114 s = tz_parse_startend(s_end,w2,&dstend);
10117 if (reversed == -1) { /* need to check if start later than end */
10121 if (when < 2*365*86400) {
10122 when += 2*365*86400;
10126 w2 =localtime(&when);
10127 when = when + (15 - w2->tm_yday) * 86400; /* jan 15 */
10129 for (j = 0; j < 12; j++) {
10130 w2 =localtime(&when);
10131 tz_parse_startend(s_start,w2,&ds);
10132 tz_parse_startend(s_end,w2,&de);
10133 if (ds != de) break;
10137 if (de && !ds) reversed = 1;
10140 isdst = dststart && !dstend;
10141 if (reversed) isdst = dststart || !dstend;
10144 if (dst) *dst = isdst;
10145 if (gmtoff) *gmtoff = isdst ? dst_off : std_off;
10146 if (isdst) tz = dstzone;
10148 while(isalpha(*tz)) *zone++ = *tz++;
10154 #endif /* !RTL_USES_UTC */
10156 /* my_time(), my_localtime(), my_gmtime()
10157 * By default traffic in UTC time values, using CRTL gmtime() or
10158 * SYS$TIMEZONE_DIFFERENTIAL to determine offset from local time zone.
10159 * Note: We need to use these functions even when the CRTL has working
10160 * UTC support, since they also handle C<use vmsish qw(times);>
10162 * Contributed by Chuck Lane <lane@duphy4.physics.drexel.edu>
10163 * Modified by Charles Bailey <bailey@newman.upenn.edu>
10166 /*{{{time_t my_time(time_t *timep)*/
10167 time_t Perl_my_time(pTHX_ time_t *timep)
10172 if (gmtime_emulation_type == 0) {
10174 time_t base = 15 * 86400; /* 15jan71; to avoid month/year ends between */
10175 /* results of calls to gmtime() and localtime() */
10176 /* for same &base */
10178 gmtime_emulation_type++;
10179 if ((tm_p = gmtime(&base)) == NULL) { /* CRTL gmtime() is a fake */
10180 char off[LNM$C_NAMLENGTH+1];;
10182 gmtime_emulation_type++;
10183 if (!vmstrnenv("SYS$TIMEZONE_DIFFERENTIAL",off,0,fildev,0)) {
10184 gmtime_emulation_type++;
10185 utc_offset_secs = 0;
10186 Perl_warn(aTHX_ "no UTC offset information; assuming local time is UTC");
10188 else { utc_offset_secs = atol(off); }
10190 else { /* We've got a working gmtime() */
10191 struct tm gmt, local;
10194 tm_p = localtime(&base);
10196 utc_offset_secs = (local.tm_mday - gmt.tm_mday) * 86400;
10197 utc_offset_secs += (local.tm_hour - gmt.tm_hour) * 3600;
10198 utc_offset_secs += (local.tm_min - gmt.tm_min) * 60;
10199 utc_offset_secs += (local.tm_sec - gmt.tm_sec);
10204 # ifdef VMSISH_TIME
10205 # ifdef RTL_USES_UTC
10206 if (VMSISH_TIME) when = _toloc(when);
10208 if (!VMSISH_TIME) when = _toutc(when);
10211 if (timep != NULL) *timep = when;
10214 } /* end of my_time() */
10218 /*{{{struct tm *my_gmtime(const time_t *timep)*/
10220 Perl_my_gmtime(pTHX_ const time_t *timep)
10226 if (timep == NULL) {
10227 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
10230 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
10233 # ifdef VMSISH_TIME
10234 if (VMSISH_TIME) when = _toutc(when); /* Input was local time */
10236 # ifdef RTL_USES_UTC /* this implies that the CRTL has a working gmtime() */
10237 return gmtime(&when);
10239 /* CRTL localtime() wants local time as input, so does no tz correction */
10240 rsltmp = localtime(&when);
10241 if (rsltmp) rsltmp->tm_isdst = 0; /* We already took DST into account */
10244 } /* end of my_gmtime() */
10248 /*{{{struct tm *my_localtime(const time_t *timep)*/
10250 Perl_my_localtime(pTHX_ const time_t *timep)
10252 time_t when, whenutc;
10256 if (timep == NULL) {
10257 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
10260 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
10261 if (gmtime_emulation_type == 0) my_time(NULL); /* Init UTC */
10264 # ifdef RTL_USES_UTC
10265 # ifdef VMSISH_TIME
10266 if (VMSISH_TIME) when = _toutc(when);
10268 /* CRTL localtime() wants UTC as input, does tz correction itself */
10269 return localtime(&when);
10271 # else /* !RTL_USES_UTC */
10273 # ifdef VMSISH_TIME
10274 if (!VMSISH_TIME) when = _toloc(whenutc); /* input was UTC */
10275 if (VMSISH_TIME) whenutc = _toutc(when); /* input was truelocal */
10278 #ifndef RTL_USES_UTC
10279 if (tz_parse(aTHX_ &when, &dst, 0, &offset)) { /* truelocal determines DST*/
10280 when = whenutc - offset; /* pseudolocal time*/
10283 /* CRTL localtime() wants local time as input, so does no tz correction */
10284 rsltmp = localtime(&when);
10285 if (rsltmp && gmtime_emulation_type != 1) rsltmp->tm_isdst = dst;
10289 } /* end of my_localtime() */
10292 /* Reset definitions for later calls */
10293 #define gmtime(t) my_gmtime(t)
10294 #define localtime(t) my_localtime(t)
10295 #define time(t) my_time(t)
10298 /* my_utime - update modification/access time of a file
10300 * VMS 7.3 and later implementation
10301 * Only the UTC translation is home-grown. The rest is handled by the
10302 * CRTL utime(), which will take into account the relevant feature
10303 * logicals and ODS-5 volume characteristics for true access times.
10305 * pre VMS 7.3 implementation:
10306 * The calling sequence is identical to POSIX utime(), but under
10307 * VMS with ODS-2, only the modification time is changed; ODS-2 does
10308 * not maintain access times. Restrictions differ from the POSIX
10309 * definition in that the time can be changed as long as the
10310 * caller has permission to execute the necessary IO$_MODIFY $QIO;
10311 * no separate checks are made to insure that the caller is the
10312 * owner of the file or has special privs enabled.
10313 * Code here is based on Joe Meadows' FILE utility.
10317 /* Adjustment from Unix epoch (01-JAN-1970 00:00:00.00)
10318 * to VMS epoch (01-JAN-1858 00:00:00.00)
10319 * in 100 ns intervals.
10321 static const long int utime_baseadjust[2] = { 0x4beb4000, 0x7c9567 };
10323 /*{{{int my_utime(const char *path, const struct utimbuf *utimes)*/
10324 int Perl_my_utime(pTHX_ const char *file, const struct utimbuf *utimes)
10326 #if __CRTL_VER >= 70300000
10327 struct utimbuf utc_utimes, *utc_utimesp;
10329 if (utimes != NULL) {
10330 utc_utimes.actime = utimes->actime;
10331 utc_utimes.modtime = utimes->modtime;
10332 # ifdef VMSISH_TIME
10333 /* If input was local; convert to UTC for sys svc */
10335 utc_utimes.actime = _toutc(utimes->actime);
10336 utc_utimes.modtime = _toutc(utimes->modtime);
10339 utc_utimesp = &utc_utimes;
10342 utc_utimesp = NULL;
10345 return utime(file, utc_utimesp);
10347 #else /* __CRTL_VER < 70300000 */
10351 long int bintime[2], len = 2, lowbit, unixtime,
10352 secscale = 10000000; /* seconds --> 100 ns intervals */
10353 unsigned long int chan, iosb[2], retsts;
10354 char vmsspec[NAM$C_MAXRSS+1], rsa[NAM$C_MAXRSS], esa[NAM$C_MAXRSS];
10355 struct FAB myfab = cc$rms_fab;
10356 struct NAM mynam = cc$rms_nam;
10357 #if defined (__DECC) && defined (__VAX)
10358 /* VAX DEC C atrdef.h has unsigned type for pointer member atr$l_addr,
10359 * at least through VMS V6.1, which causes a type-conversion warning.
10361 # pragma message save
10362 # pragma message disable cvtdiftypes
10364 struct atrdef myatr[2] = {{sizeof bintime, ATR$C_REVDATE, bintime}, {0,0,0}};
10365 struct fibdef myfib;
10366 #if defined (__DECC) && defined (__VAX)
10367 /* This should be right after the declaration of myatr, but due
10368 * to a bug in VAX DEC C, this takes effect a statement early.
10370 # pragma message restore
10372 /* cast ok for read only parameter */
10373 struct dsc$descriptor fibdsc = {sizeof(myfib), DSC$K_DTYPE_Z, DSC$K_CLASS_S,(char *) &myfib},
10374 devdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0},
10375 fnmdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0};
10377 if (file == NULL || *file == '\0') {
10378 SETERRNO(ENOENT, LIB$_INVARG);
10382 /* Convert to VMS format ensuring that it will fit in 255 characters */
10383 if (do_rmsexpand(file, vmsspec, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL) == NULL) {
10384 SETERRNO(ENOENT, LIB$_INVARG);
10387 if (utimes != NULL) {
10388 /* Convert Unix time (seconds since 01-JAN-1970 00:00:00.00)
10389 * to VMS quadword time (100 nsec intervals since 01-JAN-1858 00:00:00.00).
10390 * Since time_t is unsigned long int, and lib$emul takes a signed long int
10391 * as input, we force the sign bit to be clear by shifting unixtime right
10392 * one bit, then multiplying by an extra factor of 2 in lib$emul().
10394 lowbit = (utimes->modtime & 1) ? secscale : 0;
10395 unixtime = (long int) utimes->modtime;
10396 # ifdef VMSISH_TIME
10397 /* If input was UTC; convert to local for sys svc */
10398 if (!VMSISH_TIME) unixtime = _toloc(unixtime);
10400 unixtime >>= 1; secscale <<= 1;
10401 retsts = lib$emul(&secscale, &unixtime, &lowbit, bintime);
10402 if (!(retsts & 1)) {
10403 SETERRNO(EVMSERR, retsts);
10406 retsts = lib$addx(bintime,utime_baseadjust,bintime,&len);
10407 if (!(retsts & 1)) {
10408 SETERRNO(EVMSERR, retsts);
10413 /* Just get the current time in VMS format directly */
10414 retsts = sys$gettim(bintime);
10415 if (!(retsts & 1)) {
10416 SETERRNO(EVMSERR, retsts);
10421 myfab.fab$l_fna = vmsspec;
10422 myfab.fab$b_fns = (unsigned char) strlen(vmsspec);
10423 myfab.fab$l_nam = &mynam;
10424 mynam.nam$l_esa = esa;
10425 mynam.nam$b_ess = (unsigned char) sizeof esa;
10426 mynam.nam$l_rsa = rsa;
10427 mynam.nam$b_rss = (unsigned char) sizeof rsa;
10428 if (decc_efs_case_preserve)
10429 mynam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
10431 /* Look for the file to be affected, letting RMS parse the file
10432 * specification for us as well. I have set errno using only
10433 * values documented in the utime() man page for VMS POSIX.
10435 retsts = sys$parse(&myfab,0,0);
10436 if (!(retsts & 1)) {
10437 set_vaxc_errno(retsts);
10438 if (retsts == RMS$_PRV) set_errno(EACCES);
10439 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
10440 else set_errno(EVMSERR);
10443 retsts = sys$search(&myfab,0,0);
10444 if (!(retsts & 1)) {
10445 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
10446 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
10447 set_vaxc_errno(retsts);
10448 if (retsts == RMS$_PRV) set_errno(EACCES);
10449 else if (retsts == RMS$_FNF) set_errno(ENOENT);
10450 else set_errno(EVMSERR);
10454 devdsc.dsc$w_length = mynam.nam$b_dev;
10455 /* cast ok for read only parameter */
10456 devdsc.dsc$a_pointer = (char *) mynam.nam$l_dev;
10458 retsts = sys$assign(&devdsc,&chan,0,0);
10459 if (!(retsts & 1)) {
10460 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
10461 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
10462 set_vaxc_errno(retsts);
10463 if (retsts == SS$_IVDEVNAM) set_errno(ENOTDIR);
10464 else if (retsts == SS$_NOPRIV) set_errno(EACCES);
10465 else if (retsts == SS$_NOSUCHDEV) set_errno(ENOTDIR);
10466 else set_errno(EVMSERR);
10470 fnmdsc.dsc$a_pointer = mynam.nam$l_name;
10471 fnmdsc.dsc$w_length = mynam.nam$b_name + mynam.nam$b_type + mynam.nam$b_ver;
10473 memset((void *) &myfib, 0, sizeof myfib);
10474 #if defined(__DECC) || defined(__DECCXX)
10475 for (i=0;i<3;i++) myfib.fib$w_fid[i] = mynam.nam$w_fid[i];
10476 for (i=0;i<3;i++) myfib.fib$w_did[i] = mynam.nam$w_did[i];
10477 /* This prevents the revision time of the file being reset to the current
10478 * time as a result of our IO$_MODIFY $QIO. */
10479 myfib.fib$l_acctl = FIB$M_NORECORD;
10481 for (i=0;i<3;i++) myfib.fib$r_fid_overlay.fib$w_fid[i] = mynam.nam$w_fid[i];
10482 for (i=0;i<3;i++) myfib.fib$r_did_overlay.fib$w_did[i] = mynam.nam$w_did[i];
10483 myfib.fib$r_acctl_overlay.fib$l_acctl = FIB$M_NORECORD;
10485 retsts = sys$qiow(0,chan,IO$_MODIFY,iosb,0,0,&fibdsc,&fnmdsc,0,0,myatr,0);
10486 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
10487 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
10488 _ckvmssts(sys$dassgn(chan));
10489 if (retsts & 1) retsts = iosb[0];
10490 if (!(retsts & 1)) {
10491 set_vaxc_errno(retsts);
10492 if (retsts == SS$_NOPRIV) set_errno(EACCES);
10493 else set_errno(EVMSERR);
10499 #endif /* #if __CRTL_VER >= 70300000 */
10501 } /* end of my_utime() */
10505 * flex_stat, flex_lstat, flex_fstat
10506 * basic stat, but gets it right when asked to stat
10507 * a Unix-style path ending in a directory name (e.g. dir1/dir2/dir3)
10510 #ifndef _USE_STD_STAT
10511 /* encode_dev packs a VMS device name string into an integer to allow
10512 * simple comparisons. This can be used, for example, to check whether two
10513 * files are located on the same device, by comparing their encoded device
10514 * names. Even a string comparison would not do, because stat() reuses the
10515 * device name buffer for each call; so without encode_dev, it would be
10516 * necessary to save the buffer and use strcmp (this would mean a number of
10517 * changes to the standard Perl code, to say nothing of what a Perl script
10518 * would have to do.
10520 * The device lock id, if it exists, should be unique (unless perhaps compared
10521 * with lock ids transferred from other nodes). We have a lock id if the disk is
10522 * mounted cluster-wide, which is when we tend to get long (host-qualified)
10523 * device names. Thus we use the lock id in preference, and only if that isn't
10524 * available, do we try to pack the device name into an integer (flagged by
10525 * the sign bit (LOCKID_MASK) being set).
10527 * Note that encode_dev cannot guarantee an 1-to-1 correspondence twixt device
10528 * name and its encoded form, but it seems very unlikely that we will find
10529 * two files on different disks that share the same encoded device names,
10530 * and even more remote that they will share the same file id (if the test
10531 * is to check for the same file).
10533 * A better method might be to use sys$device_scan on the first call, and to
10534 * search for the device, returning an index into the cached array.
10535 * The number returned would be more intelligible.
10536 * This is probably not worth it, and anyway would take quite a bit longer
10537 * on the first call.
10539 #define LOCKID_MASK 0x80000000 /* Use 0 to force device name use only */
10540 static mydev_t encode_dev (pTHX_ const char *dev)
10543 unsigned long int f;
10548 if (!dev || !dev[0]) return 0;
10552 struct dsc$descriptor_s dev_desc;
10553 unsigned long int status, lockid = 0, item = DVI$_LOCKID;
10555 /* For cluster-mounted disks, the disk lock identifier is unique, so we
10556 can try that first. */
10557 dev_desc.dsc$w_length = strlen (dev);
10558 dev_desc.dsc$b_dtype = DSC$K_DTYPE_T;
10559 dev_desc.dsc$b_class = DSC$K_CLASS_S;
10560 dev_desc.dsc$a_pointer = (char *) dev; /* Read only parameter */
10561 status = lib$getdvi(&item, 0, &dev_desc, &lockid, 0, 0);
10562 if (!$VMS_STATUS_SUCCESS(status)) {
10564 case SS$_NOSUCHDEV:
10565 SETERRNO(ENODEV, status);
10571 if (lockid) return (lockid & ~LOCKID_MASK);
10575 /* Otherwise we try to encode the device name */
10579 for (q = dev + strlen(dev); q--; q >= dev) {
10584 else if (isalpha (toupper (*q)))
10585 c= toupper (*q) - 'A' + (char)10;
10587 continue; /* Skip '$'s */
10589 if (i>6) break; /* 36^7 is too large to fit in an unsigned long int */
10591 enc += f * (unsigned long int) c;
10593 return (enc | LOCKID_MASK); /* May have already overflowed into bit 31 */
10595 } /* end of encode_dev() */
10596 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
10597 device_no = encode_dev(aTHX_ devname)
10599 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
10600 device_no = new_dev_no
10604 is_null_device(name)
10607 if (decc_bug_devnull != 0) {
10608 if (strncmp("/dev/null", name, 9) == 0)
10611 /* The VMS null device is named "_NLA0:", usually abbreviated as "NL:".
10612 The underscore prefix, controller letter, and unit number are
10613 independently optional; for our purposes, the colon punctuation
10614 is not. The colon can be trailed by optional directory and/or
10615 filename, but two consecutive colons indicates a nodename rather
10616 than a device. [pr] */
10617 if (*name == '_') ++name;
10618 if (tolower(*name++) != 'n') return 0;
10619 if (tolower(*name++) != 'l') return 0;
10620 if (tolower(*name) == 'a') ++name;
10621 if (*name == '0') ++name;
10622 return (*name++ == ':') && (*name != ':');
10627 Perl_cando_by_name_int
10628 (pTHX_ I32 bit, bool effective, const char *fname, int opts)
10630 static char usrname[L_cuserid];
10631 static struct dsc$descriptor_s usrdsc =
10632 {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, usrname};
10633 char vmsname[NAM$C_MAXRSS+1];
10635 unsigned long int objtyp = ACL$C_FILE, access, retsts, privused, iosb[2], flags;
10636 unsigned short int retlen, trnlnm_iter_count;
10637 struct dsc$descriptor_s namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10638 union prvdef curprv;
10639 struct itmlst_3 armlst[4] = {{sizeof access, CHP$_ACCESS, &access, &retlen},
10640 {sizeof privused, CHP$_PRIVUSED, &privused, &retlen},
10641 {sizeof flags, CHP$_FLAGS, &flags, &retlen},{0,0,0,0}};
10642 struct itmlst_3 jpilst[3] = {{sizeof curprv, JPI$_CURPRIV, &curprv, &retlen},
10643 {sizeof usrname, JPI$_USERNAME, &usrname, &usrdsc.dsc$w_length},
10645 struct itmlst_3 usrprolst[2] = {{sizeof curprv, CHP$_PRIV, &curprv, &retlen},
10647 struct dsc$descriptor_s usrprodsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10649 if (!fname || !*fname) return FALSE;
10650 /* Make sure we expand logical names, since sys$check_access doesn't */
10653 if ((opts & PERL_RMSEXPAND_M_VMS_IN) != 0) {
10654 fileified = PerlMem_malloc(VMS_MAXRSS);
10655 if (!strpbrk(fname,"/]>:")) {
10656 strcpy(fileified,fname);
10657 trnlnm_iter_count = 0;
10658 while (!strpbrk(fileified,"/]>:>") && my_trnlnm(fileified,fileified,0)) {
10659 trnlnm_iter_count++;
10660 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
10664 if (!do_rmsexpand(fname, vmsname, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL)) {
10665 PerlMem_free(fileified);
10668 retlen = namdsc.dsc$w_length = strlen(vmsname);
10669 namdsc.dsc$a_pointer = vmsname;
10670 if (vmsname[retlen-1] == ']' || vmsname[retlen-1] == '>' ||
10671 vmsname[retlen-1] == ':') {
10672 if (!do_fileify_dirspec(vmsname,fileified,1,NULL)) return FALSE;
10673 namdsc.dsc$w_length = strlen(fileified);
10674 namdsc.dsc$a_pointer = fileified;
10678 retlen = namdsc.dsc$w_length = strlen(fname);
10679 namdsc.dsc$a_pointer = (char *)fname; /* cast ok */
10683 case S_IXUSR: case S_IXGRP: case S_IXOTH:
10684 access = ARM$M_EXECUTE;
10685 flags = CHP$M_READ;
10687 case S_IRUSR: case S_IRGRP: case S_IROTH:
10688 access = ARM$M_READ;
10689 flags = CHP$M_READ | CHP$M_USEREADALL;
10691 case S_IWUSR: case S_IWGRP: case S_IWOTH:
10692 access = ARM$M_WRITE;
10693 flags = CHP$M_READ | CHP$M_WRITE;
10695 case S_IDUSR: case S_IDGRP: case S_IDOTH:
10696 access = ARM$M_DELETE;
10697 flags = CHP$M_READ | CHP$M_WRITE;
10700 if (fileified != NULL)
10701 PerlMem_free(fileified);
10705 /* Before we call $check_access, create a user profile with the current
10706 * process privs since otherwise it just uses the default privs from the
10707 * UAF and might give false positives or negatives. This only works on
10708 * VMS versions v6.0 and later since that's when sys$create_user_profile
10709 * became available.
10712 /* get current process privs and username */
10713 _ckvmssts(sys$getjpiw(0,0,0,jpilst,iosb,0,0));
10714 _ckvmssts(iosb[0]);
10716 #if defined(__VMS_VER) && __VMS_VER >= 60000000
10718 /* find out the space required for the profile */
10719 _ckvmssts(sys$create_user_profile(&usrdsc,&usrprolst,0,0,
10720 &usrprodsc.dsc$w_length,0));
10722 /* allocate space for the profile and get it filled in */
10723 usrprodsc.dsc$a_pointer = PerlMem_malloc(usrprodsc.dsc$w_length);
10724 if (usrprodsc.dsc$a_pointer == NULL) _ckvmssts(SS$_INSFMEM);
10725 _ckvmssts(sys$create_user_profile(&usrdsc,&usrprolst,0,usrprodsc.dsc$a_pointer,
10726 &usrprodsc.dsc$w_length,0));
10728 /* use the profile to check access to the file; free profile & analyze results */
10729 retsts = sys$check_access(&objtyp,&namdsc,0,armlst,0,0,0,&usrprodsc);
10730 PerlMem_free(usrprodsc.dsc$a_pointer);
10731 if (retsts == SS$_NOCALLPRIV) retsts = SS$_NOPRIV; /* not really 3rd party */
10735 retsts = sys$check_access(&objtyp,&namdsc,&usrdsc,armlst);
10739 if (retsts == SS$_NOPRIV || retsts == SS$_NOSUCHOBJECT ||
10740 retsts == SS$_INVFILFOROP || retsts == RMS$_FNF || retsts == RMS$_SYN ||
10741 retsts == RMS$_DIR || retsts == RMS$_DEV || retsts == RMS$_DNF) {
10742 set_vaxc_errno(retsts);
10743 if (retsts == SS$_NOPRIV) set_errno(EACCES);
10744 else if (retsts == SS$_INVFILFOROP) set_errno(EINVAL);
10745 else set_errno(ENOENT);
10746 if (fileified != NULL)
10747 PerlMem_free(fileified);
10750 if (retsts == SS$_NORMAL || retsts == SS$_ACCONFLICT) {
10751 if (fileified != NULL)
10752 PerlMem_free(fileified);
10757 if (fileified != NULL)
10758 PerlMem_free(fileified);
10759 return FALSE; /* Should never get here */
10763 /* Do the permissions allow some operation? Assumes PL_statcache already set. */
10764 /* Do this via $Check_Access on VMS, since the CRTL stat() returns only a
10765 * subset of the applicable information.
10768 Perl_cando(pTHX_ Mode_t bit, bool effective, const Stat_t *statbufp)
10770 return cando_by_name_int
10771 (bit, effective, statbufp->st_devnam, PERL_RMSEXPAND_M_VMS_IN);
10772 } /* end of cando() */
10776 /*{{{I32 cando_by_name(I32 bit, bool effective, char *fname)*/
10778 Perl_cando_by_name(pTHX_ I32 bit, bool effective, const char *fname)
10780 return cando_by_name_int(bit, effective, fname, 0);
10782 } /* end of cando_by_name() */
10786 /*{{{ int flex_fstat(int fd, Stat_t *statbuf)*/
10788 Perl_flex_fstat(pTHX_ int fd, Stat_t *statbufp)
10790 if (!fstat(fd,(stat_t *) statbufp)) {
10792 char *vms_filename;
10793 vms_filename = PerlMem_malloc(VMS_MAXRSS);
10794 if (vms_filename == NULL) _ckvmssts(SS$_INSFMEM);
10796 /* Save name for cando by name in VMS format */
10797 cptr = getname(fd, vms_filename, 1);
10799 /* This should not happen, but just in case */
10800 if (cptr == NULL) {
10801 statbufp->st_devnam[0] = 0;
10804 /* Make sure that the saved name fits in 255 characters */
10805 cptr = do_rmsexpand
10807 statbufp->st_devnam,
10810 PERL_RMSEXPAND_M_VMS | PERL_RMSEXPAND_M_VMS_IN,
10814 statbufp->st_devnam[0] = 0;
10816 PerlMem_free(vms_filename);
10818 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
10820 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
10822 # ifdef RTL_USES_UTC
10823 # ifdef VMSISH_TIME
10825 statbufp->st_mtime = _toloc(statbufp->st_mtime);
10826 statbufp->st_atime = _toloc(statbufp->st_atime);
10827 statbufp->st_ctime = _toloc(statbufp->st_ctime);
10831 # ifdef VMSISH_TIME
10832 if (!VMSISH_TIME) { /* Return UTC instead of local time */
10836 statbufp->st_mtime = _toutc(statbufp->st_mtime);
10837 statbufp->st_atime = _toutc(statbufp->st_atime);
10838 statbufp->st_ctime = _toutc(statbufp->st_ctime);
10845 } /* end of flex_fstat() */
10848 #if !defined(__VAX) && __CRTL_VER >= 80200000
10856 #define lstat(_x, _y) stat(_x, _y)
10859 #define flex_stat_int(a,b,c) Perl_flex_stat_int(aTHX_ a,b,c)
10862 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag)
10864 char fileified[VMS_MAXRSS];
10865 char temp_fspec[VMS_MAXRSS];
10868 int saved_errno, saved_vaxc_errno;
10870 if (!fspec) return retval;
10871 saved_errno = errno; saved_vaxc_errno = vaxc$errno;
10872 strcpy(temp_fspec, fspec);
10874 if (decc_bug_devnull != 0) {
10875 if (is_null_device(temp_fspec)) { /* Fake a stat() for the null device */
10876 memset(statbufp,0,sizeof *statbufp);
10877 VMS_DEVICE_ENCODE(statbufp->st_dev, "_NLA0:", 0);
10878 statbufp->st_mode = S_IFBLK | S_IREAD | S_IWRITE | S_IEXEC;
10879 statbufp->st_uid = 0x00010001;
10880 statbufp->st_gid = 0x0001;
10881 time((time_t *)&statbufp->st_mtime);
10882 statbufp->st_atime = statbufp->st_ctime = statbufp->st_mtime;
10887 /* Try for a directory name first. If fspec contains a filename without
10888 * a type (e.g. sea:[wine.dark]water), and both sea:[wine.dark]water.dir
10889 * and sea:[wine.dark]water. exist, we prefer the directory here.
10890 * Similarly, sea:[wine.dark] returns the result for sea:[wine]dark.dir,
10891 * not sea:[wine.dark]., if the latter exists. If the intended target is
10892 * the file with null type, specify this by calling flex_stat() with
10893 * a '.' at the end of fspec.
10895 * If we are in Posix filespec mode, accept the filename as is.
10897 #if __CRTL_VER >= 80200000 && !defined(__VAX)
10898 if (decc_posix_compliant_pathnames == 0) {
10900 if (do_fileify_dirspec(temp_fspec,fileified,0,NULL) != NULL) {
10901 if (lstat_flag == 0)
10902 retval = stat(fileified,(stat_t *) statbufp);
10904 retval = lstat(fileified,(stat_t *) statbufp);
10905 save_spec = fileified;
10908 if (lstat_flag == 0)
10909 retval = stat(temp_fspec,(stat_t *) statbufp);
10911 retval = lstat(temp_fspec,(stat_t *) statbufp);
10912 save_spec = temp_fspec;
10914 #if __CRTL_VER >= 80200000 && !defined(__VAX)
10916 if (lstat_flag == 0)
10917 retval = stat(temp_fspec,(stat_t *) statbufp);
10919 retval = lstat(temp_fspec,(stat_t *) statbufp);
10920 save_spec = temp_fspec;
10925 cptr = do_rmsexpand
10926 (save_spec, statbufp->st_devnam, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL);
10928 statbufp->st_devnam[0] = 0;
10930 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
10932 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
10933 # ifdef RTL_USES_UTC
10934 # ifdef VMSISH_TIME
10936 statbufp->st_mtime = _toloc(statbufp->st_mtime);
10937 statbufp->st_atime = _toloc(statbufp->st_atime);
10938 statbufp->st_ctime = _toloc(statbufp->st_ctime);
10942 # ifdef VMSISH_TIME
10943 if (!VMSISH_TIME) { /* Return UTC instead of local time */
10947 statbufp->st_mtime = _toutc(statbufp->st_mtime);
10948 statbufp->st_atime = _toutc(statbufp->st_atime);
10949 statbufp->st_ctime = _toutc(statbufp->st_ctime);
10953 /* If we were successful, leave errno where we found it */
10954 if (retval == 0) { errno = saved_errno; vaxc$errno = saved_vaxc_errno; }
10957 } /* end of flex_stat_int() */
10960 /*{{{ int flex_stat(const char *fspec, Stat_t *statbufp)*/
10962 Perl_flex_stat(pTHX_ const char *fspec, Stat_t *statbufp)
10964 return flex_stat_int(fspec, statbufp, 0);
10968 /*{{{ int flex_lstat(const char *fspec, Stat_t *statbufp)*/
10970 Perl_flex_lstat(pTHX_ const char *fspec, Stat_t *statbufp)
10972 return flex_stat_int(fspec, statbufp, 1);
10977 /*{{{char *my_getlogin()*/
10978 /* VMS cuserid == Unix getlogin, except calling sequence */
10982 static char user[L_cuserid];
10983 return cuserid(user);
10988 /* rmscopy - copy a file using VMS RMS routines
10990 * Copies contents and attributes of spec_in to spec_out, except owner
10991 * and protection information. Name and type of spec_in are used as
10992 * defaults for spec_out. The third parameter specifies whether rmscopy()
10993 * should try to propagate timestamps from the input file to the output file.
10994 * If it is less than 0, no timestamps are preserved. If it is 0, then
10995 * rmscopy() will behave similarly to the DCL COPY command: timestamps are
10996 * propagated to the output file at creation iff the output file specification
10997 * did not contain an explicit name or type, and the revision date is always
10998 * updated at the end of the copy operation. If it is greater than 0, then
10999 * it is interpreted as a bitmask, in which bit 0 indicates that timestamps
11000 * other than the revision date should be propagated, and bit 1 indicates
11001 * that the revision date should be propagated.
11003 * Returns 1 on success; returns 0 and sets errno and vaxc$errno on failure.
11005 * Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>.
11006 * Incorporates, with permission, some code from EZCOPY by Tim Adye
11007 * <T.J.Adye@rl.ac.uk>. Permission is given to distribute this code
11008 * as part of the Perl standard distribution under the terms of the
11009 * GNU General Public License or the Perl Artistic License. Copies
11010 * of each may be found in the Perl standard distribution.
11012 /*{{{int rmscopy(char *src, char *dst, int preserve_dates)*/
11014 Perl_rmscopy(pTHX_ const char *spec_in, const char *spec_out, int preserve_dates)
11016 char *vmsin, * vmsout, *esa, *esa_out,
11018 unsigned long int i, sts, sts2;
11020 struct FAB fab_in, fab_out;
11021 struct RAB rab_in, rab_out;
11022 rms_setup_nam(nam);
11023 rms_setup_nam(nam_out);
11024 struct XABDAT xabdat;
11025 struct XABFHC xabfhc;
11026 struct XABRDT xabrdt;
11027 struct XABSUM xabsum;
11029 vmsin = PerlMem_malloc(VMS_MAXRSS);
11030 if (vmsin == NULL) _ckvmssts(SS$_INSFMEM);
11031 vmsout = PerlMem_malloc(VMS_MAXRSS);
11032 if (vmsout == NULL) _ckvmssts(SS$_INSFMEM);
11033 if (!spec_in || !*spec_in || !do_tovmsspec(spec_in,vmsin,1,NULL) ||
11034 !spec_out || !*spec_out || !do_tovmsspec(spec_out,vmsout,1,NULL)) {
11035 PerlMem_free(vmsin);
11036 PerlMem_free(vmsout);
11037 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11041 esa = PerlMem_malloc(VMS_MAXRSS);
11042 if (esa == NULL) _ckvmssts(SS$_INSFMEM);
11043 fab_in = cc$rms_fab;
11044 rms_set_fna(fab_in, nam, vmsin, strlen(vmsin));
11045 fab_in.fab$b_shr = FAB$M_SHRPUT | FAB$M_UPI;
11046 fab_in.fab$b_fac = FAB$M_BIO | FAB$M_GET;
11047 fab_in.fab$l_fop = FAB$M_SQO;
11048 rms_bind_fab_nam(fab_in, nam);
11049 fab_in.fab$l_xab = (void *) &xabdat;
11051 rsa = PerlMem_malloc(VMS_MAXRSS);
11052 if (rsa == NULL) _ckvmssts(SS$_INSFMEM);
11053 rms_set_rsa(nam, rsa, (VMS_MAXRSS-1));
11054 rms_set_esa(fab_in, nam, esa, (VMS_MAXRSS-1));
11055 rms_nam_esl(nam) = 0;
11056 rms_nam_rsl(nam) = 0;
11057 rms_nam_esll(nam) = 0;
11058 rms_nam_rsll(nam) = 0;
11059 #ifdef NAM$M_NO_SHORT_UPCASE
11060 if (decc_efs_case_preserve)
11061 rms_set_nam_nop(nam, NAM$M_NO_SHORT_UPCASE);
11064 xabdat = cc$rms_xabdat; /* To get creation date */
11065 xabdat.xab$l_nxt = (void *) &xabfhc;
11067 xabfhc = cc$rms_xabfhc; /* To get record length */
11068 xabfhc.xab$l_nxt = (void *) &xabsum;
11070 xabsum = cc$rms_xabsum; /* To get key and area information */
11072 if (!((sts = sys$open(&fab_in)) & 1)) {
11073 PerlMem_free(vmsin);
11074 PerlMem_free(vmsout);
11077 set_vaxc_errno(sts);
11079 case RMS$_FNF: case RMS$_DNF:
11080 set_errno(ENOENT); break;
11082 set_errno(ENOTDIR); break;
11084 set_errno(ENODEV); break;
11086 set_errno(EINVAL); break;
11088 set_errno(EACCES); break;
11090 set_errno(EVMSERR);
11097 fab_out.fab$w_ifi = 0;
11098 fab_out.fab$b_fac = FAB$M_BIO | FAB$M_PUT;
11099 fab_out.fab$b_shr = FAB$M_SHRGET | FAB$M_UPI;
11100 fab_out.fab$l_fop = FAB$M_SQO;
11101 rms_bind_fab_nam(fab_out, nam_out);
11102 rms_set_fna(fab_out, nam_out, vmsout, strlen(vmsout));
11103 dna_len = rms_nam_namel(nam) ? rms_nam_name_type_l_size(nam) : 0;
11104 rms_set_dna(fab_out, nam_out, rms_nam_namel(nam), dna_len);
11105 esa_out = PerlMem_malloc(VMS_MAXRSS);
11106 if (esa_out == NULL) _ckvmssts(SS$_INSFMEM);
11107 rms_set_rsa(nam_out, NULL, 0);
11108 rms_set_esa(fab_out, nam_out, esa_out, (VMS_MAXRSS - 1));
11110 if (preserve_dates == 0) { /* Act like DCL COPY */
11111 rms_set_nam_nop(nam_out, NAM$M_SYNCHK);
11112 fab_out.fab$l_xab = NULL; /* Don't disturb data from input file */
11113 if (!((sts = sys$parse(&fab_out)) & STS$K_SUCCESS)) {
11114 PerlMem_free(vmsin);
11115 PerlMem_free(vmsout);
11118 PerlMem_free(esa_out);
11119 set_errno(sts == RMS$_SYN ? EINVAL : EVMSERR);
11120 set_vaxc_errno(sts);
11123 fab_out.fab$l_xab = (void *) &xabdat;
11124 if (rms_is_nam_fnb(nam, NAM$M_EXP_NAME | NAM$M_EXP_TYPE))
11125 preserve_dates = 1;
11127 if (preserve_dates < 0) /* Clear all bits; we'll use it as a */
11128 preserve_dates =0; /* bitmask from this point forward */
11130 if (!(preserve_dates & 1)) fab_out.fab$l_xab = (void *) &xabfhc;
11131 if (!((sts = sys$create(&fab_out)) & STS$K_SUCCESS)) {
11132 PerlMem_free(vmsin);
11133 PerlMem_free(vmsout);
11136 PerlMem_free(esa_out);
11137 set_vaxc_errno(sts);
11140 set_errno(ENOENT); break;
11142 set_errno(ENOTDIR); break;
11144 set_errno(ENODEV); break;
11146 set_errno(EINVAL); break;
11148 set_errno(EACCES); break;
11150 set_errno(EVMSERR);
11154 fab_out.fab$l_fop |= FAB$M_DLT; /* in case we have to bail out */
11155 if (preserve_dates & 2) {
11156 /* sys$close() will process xabrdt, not xabdat */
11157 xabrdt = cc$rms_xabrdt;
11159 xabrdt.xab$q_rdt = xabdat.xab$q_rdt;
11161 /* gcc doesn't like the assignment, since its prototype for xab$q_rdt
11162 * is unsigned long[2], while DECC & VAXC use a struct */
11163 memcpy(xabrdt.xab$q_rdt,xabdat.xab$q_rdt,sizeof xabrdt.xab$q_rdt);
11165 fab_out.fab$l_xab = (void *) &xabrdt;
11168 ubf = PerlMem_malloc(32256);
11169 if (ubf == NULL) _ckvmssts(SS$_INSFMEM);
11170 rab_in = cc$rms_rab;
11171 rab_in.rab$l_fab = &fab_in;
11172 rab_in.rab$l_rop = RAB$M_BIO;
11173 rab_in.rab$l_ubf = ubf;
11174 rab_in.rab$w_usz = 32256;
11175 if (!((sts = sys$connect(&rab_in)) & 1)) {
11176 sys$close(&fab_in); sys$close(&fab_out);
11177 PerlMem_free(vmsin);
11178 PerlMem_free(vmsout);
11182 PerlMem_free(esa_out);
11183 set_errno(EVMSERR); set_vaxc_errno(sts);
11187 rab_out = cc$rms_rab;
11188 rab_out.rab$l_fab = &fab_out;
11189 rab_out.rab$l_rbf = ubf;
11190 if (!((sts = sys$connect(&rab_out)) & 1)) {
11191 sys$close(&fab_in); sys$close(&fab_out);
11192 PerlMem_free(vmsin);
11193 PerlMem_free(vmsout);
11197 PerlMem_free(esa_out);
11198 set_errno(EVMSERR); set_vaxc_errno(sts);
11202 while ((sts = sys$read(&rab_in))) { /* always true */
11203 if (sts == RMS$_EOF) break;
11204 rab_out.rab$w_rsz = rab_in.rab$w_rsz;
11205 if (!(sts & 1) || !((sts = sys$write(&rab_out)) & 1)) {
11206 sys$close(&fab_in); sys$close(&fab_out);
11207 PerlMem_free(vmsin);
11208 PerlMem_free(vmsout);
11212 PerlMem_free(esa_out);
11213 set_errno(EVMSERR); set_vaxc_errno(sts);
11219 fab_out.fab$l_fop &= ~FAB$M_DLT; /* We got this far; keep the output */
11220 sys$close(&fab_in); sys$close(&fab_out);
11221 sts = (fab_in.fab$l_sts & 1) ? fab_out.fab$l_sts : fab_in.fab$l_sts;
11223 PerlMem_free(vmsin);
11224 PerlMem_free(vmsout);
11228 PerlMem_free(esa_out);
11229 set_errno(EVMSERR); set_vaxc_errno(sts);
11233 PerlMem_free(vmsin);
11234 PerlMem_free(vmsout);
11238 PerlMem_free(esa_out);
11241 } /* end of rmscopy() */
11245 /*** The following glue provides 'hooks' to make some of the routines
11246 * from this file available from Perl. These routines are sufficiently
11247 * basic, and are required sufficiently early in the build process,
11248 * that's it's nice to have them available to miniperl as well as the
11249 * full Perl, so they're set up here instead of in an extension. The
11250 * Perl code which handles importation of these names into a given
11251 * package lives in [.VMS]Filespec.pm in @INC.
11255 rmsexpand_fromperl(pTHX_ CV *cv)
11258 char *fspec, *defspec = NULL, *rslt;
11260 int fs_utf8, dfs_utf8;
11264 if (!items || items > 2)
11265 Perl_croak(aTHX_ "Usage: VMS::Filespec::rmsexpand(spec[,defspec])");
11266 fspec = SvPV(ST(0),n_a);
11267 fs_utf8 = SvUTF8(ST(0));
11268 if (!fspec || !*fspec) XSRETURN_UNDEF;
11270 defspec = SvPV(ST(1),n_a);
11271 dfs_utf8 = SvUTF8(ST(1));
11273 rslt = do_rmsexpand(fspec,NULL,1,defspec,0,&fs_utf8,&dfs_utf8);
11274 ST(0) = sv_newmortal();
11275 if (rslt != NULL) {
11276 sv_usepvn(ST(0),rslt,strlen(rslt));
11285 vmsify_fromperl(pTHX_ CV *cv)
11292 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsify(spec)");
11293 utf8_fl = SvUTF8(ST(0));
11294 vmsified = do_tovmsspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
11295 ST(0) = sv_newmortal();
11296 if (vmsified != NULL) {
11297 sv_usepvn(ST(0),vmsified,strlen(vmsified));
11306 unixify_fromperl(pTHX_ CV *cv)
11313 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixify(spec)");
11314 utf8_fl = SvUTF8(ST(0));
11315 unixified = do_tounixspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
11316 ST(0) = sv_newmortal();
11317 if (unixified != NULL) {
11318 sv_usepvn(ST(0),unixified,strlen(unixified));
11327 fileify_fromperl(pTHX_ CV *cv)
11334 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::fileify(spec)");
11335 utf8_fl = SvUTF8(ST(0));
11336 fileified = do_fileify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
11337 ST(0) = sv_newmortal();
11338 if (fileified != NULL) {
11339 sv_usepvn(ST(0),fileified,strlen(fileified));
11348 pathify_fromperl(pTHX_ CV *cv)
11355 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::pathify(spec)");
11356 utf8_fl = SvUTF8(ST(0));
11357 pathified = do_pathify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
11358 ST(0) = sv_newmortal();
11359 if (pathified != NULL) {
11360 sv_usepvn(ST(0),pathified,strlen(pathified));
11369 vmspath_fromperl(pTHX_ CV *cv)
11376 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmspath(spec)");
11377 utf8_fl = SvUTF8(ST(0));
11378 vmspath = do_tovmspath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
11379 ST(0) = sv_newmortal();
11380 if (vmspath != NULL) {
11381 sv_usepvn(ST(0),vmspath,strlen(vmspath));
11390 unixpath_fromperl(pTHX_ CV *cv)
11397 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixpath(spec)");
11398 utf8_fl = SvUTF8(ST(0));
11399 unixpath = do_tounixpath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
11400 ST(0) = sv_newmortal();
11401 if (unixpath != NULL) {
11402 sv_usepvn(ST(0),unixpath,strlen(unixpath));
11411 candelete_fromperl(pTHX_ CV *cv)
11419 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::candelete(spec)");
11421 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
11422 Newx(fspec, VMS_MAXRSS, char);
11423 if (fspec == NULL) _ckvmssts(SS$_INSFMEM);
11424 if (SvTYPE(mysv) == SVt_PVGV) {
11425 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),fspec)) {
11426 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11434 if (mysv != ST(0) || !(fsp = SvPV(mysv,n_a)) || !*fsp) {
11435 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11442 ST(0) = boolSV(cando_by_name(S_IDUSR,0,fsp));
11448 rmscopy_fromperl(pTHX_ CV *cv)
11451 char *inspec, *outspec, *inp, *outp;
11453 struct dsc$descriptor indsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
11454 outdsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
11455 unsigned long int sts;
11460 if (items < 2 || items > 3)
11461 Perl_croak(aTHX_ "Usage: File::Copy::rmscopy(from,to[,date_flag])");
11463 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
11464 Newx(inspec, VMS_MAXRSS, char);
11465 if (SvTYPE(mysv) == SVt_PVGV) {
11466 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),inspec)) {
11467 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11475 if (mysv != ST(0) || !(inp = SvPV(mysv,n_a)) || !*inp) {
11476 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11482 mysv = SvROK(ST(1)) ? SvRV(ST(1)) : ST(1);
11483 Newx(outspec, VMS_MAXRSS, char);
11484 if (SvTYPE(mysv) == SVt_PVGV) {
11485 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),outspec)) {
11486 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11495 if (mysv != ST(1) || !(outp = SvPV(mysv,n_a)) || !*outp) {
11496 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11503 date_flag = (items == 3) ? SvIV(ST(2)) : 0;
11505 ST(0) = boolSV(rmscopy(inp,outp,date_flag));
11511 /* The mod2fname is limited to shorter filenames by design, so it should
11512 * not be modified to support longer EFS pathnames
11515 mod2fname(pTHX_ CV *cv)
11518 char ultimate_name[NAM$C_MAXRSS+1], work_name[NAM$C_MAXRSS*8 + 1],
11519 workbuff[NAM$C_MAXRSS*1 + 1];
11520 int total_namelen = 3, counter, num_entries;
11521 /* ODS-5 ups this, but we want to be consistent, so... */
11522 int max_name_len = 39;
11523 AV *in_array = (AV *)SvRV(ST(0));
11525 num_entries = av_len(in_array);
11527 /* All the names start with PL_. */
11528 strcpy(ultimate_name, "PL_");
11530 /* Clean up our working buffer */
11531 Zero(work_name, sizeof(work_name), char);
11533 /* Run through the entries and build up a working name */
11534 for(counter = 0; counter <= num_entries; counter++) {
11535 /* If it's not the first name then tack on a __ */
11537 strcat(work_name, "__");
11539 strcat(work_name, SvPV(*av_fetch(in_array, counter, FALSE),
11543 /* Check to see if we actually have to bother...*/
11544 if (strlen(work_name) + 3 <= max_name_len) {
11545 strcat(ultimate_name, work_name);
11547 /* It's too darned big, so we need to go strip. We use the same */
11548 /* algorithm as xsubpp does. First, strip out doubled __ */
11549 char *source, *dest, last;
11552 for (source = work_name; *source; source++) {
11553 if (last == *source && last == '_') {
11559 /* Go put it back */
11560 strcpy(work_name, workbuff);
11561 /* Is it still too big? */
11562 if (strlen(work_name) + 3 > max_name_len) {
11563 /* Strip duplicate letters */
11566 for (source = work_name; *source; source++) {
11567 if (last == toupper(*source)) {
11571 last = toupper(*source);
11573 strcpy(work_name, workbuff);
11576 /* Is it *still* too big? */
11577 if (strlen(work_name) + 3 > max_name_len) {
11578 /* Too bad, we truncate */
11579 work_name[max_name_len - 2] = 0;
11581 strcat(ultimate_name, work_name);
11584 /* Okay, return it */
11585 ST(0) = sv_2mortal(newSVpv(ultimate_name, 0));
11590 hushexit_fromperl(pTHX_ CV *cv)
11595 VMSISH_HUSHED = SvTRUE(ST(0));
11597 ST(0) = boolSV(VMSISH_HUSHED);
11603 Perl_vms_start_glob
11604 (pTHX_ SV *tmpglob,
11608 struct vs_str_st *rslt;
11612 $DESCRIPTOR(dfltdsc,"SYS$DISK:[]*.*;");
11615 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
11616 struct dsc$descriptor_vs rsdsc;
11617 unsigned long int cxt = 0, sts = 0, ok = 1, hasdir = 0;
11618 unsigned long hasver = 0, isunix = 0;
11619 unsigned long int lff_flags = 0;
11622 #ifdef VMS_LONGNAME_SUPPORT
11623 lff_flags = LIB$M_FIL_LONG_NAMES;
11625 /* The Newx macro will not allow me to assign a smaller array
11626 * to the rslt pointer, so we will assign it to the begin char pointer
11627 * and then copy the value into the rslt pointer.
11629 Newx(begin, VMS_MAXRSS + sizeof(unsigned short int), char);
11630 rslt = (struct vs_str_st *)begin;
11632 rstr = &rslt->str[0];
11633 rsdsc.dsc$a_pointer = (char *) rslt; /* cast required */
11634 rsdsc.dsc$w_maxstrlen = VMS_MAXRSS + sizeof(unsigned short int);
11635 rsdsc.dsc$b_dtype = DSC$K_DTYPE_VT;
11636 rsdsc.dsc$b_class = DSC$K_CLASS_VS;
11638 Newx(vmsspec, VMS_MAXRSS, char);
11640 /* We could find out if there's an explicit dev/dir or version
11641 by peeking into lib$find_file's internal context at
11642 ((struct NAM *)((struct FAB *)cxt)->fab$l_nam)->nam$l_fnb
11643 but that's unsupported, so I don't want to do it now and
11644 have it bite someone in the future. */
11645 /* Fix-me: vms_split_path() is the only way to do this, the
11646 existing method will fail with many legal EFS or UNIX specifications
11649 cp = SvPV(tmpglob,i);
11652 if (cp[i] == ';') hasver = 1;
11653 if (cp[i] == '.') {
11654 if (sts) hasver = 1;
11657 if (cp[i] == '/') {
11658 hasdir = isunix = 1;
11661 if (cp[i] == ']' || cp[i] == '>' || cp[i] == ':') {
11666 if ((tmpfp = PerlIO_tmpfile()) != NULL) {
11669 stat_sts = PerlLIO_stat(SvPVX_const(tmpglob),&st);
11670 if (!stat_sts && S_ISDIR(st.st_mode)) {
11671 wilddsc.dsc$a_pointer = tovmspath_utf8(SvPVX(tmpglob),vmsspec,NULL);
11672 ok = (wilddsc.dsc$a_pointer != NULL);
11675 wilddsc.dsc$a_pointer = tovmsspec_utf8(SvPVX(tmpglob),vmsspec,NULL);
11676 ok = (wilddsc.dsc$a_pointer != NULL);
11679 wilddsc.dsc$w_length = strlen(wilddsc.dsc$a_pointer);
11681 /* If not extended character set, replace ? with % */
11682 /* With extended character set, ? is a wildcard single character */
11683 if (!decc_efs_case_preserve) {
11684 for (cp=wilddsc.dsc$a_pointer; ok && cp && *cp; cp++)
11685 if (*cp == '?') *cp = '%';
11688 while (ok && $VMS_STATUS_SUCCESS(sts)) {
11689 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
11690 int v_sts, v_len, r_len, d_len, n_len, e_len, vs_len;
11692 sts = lib$find_file(&wilddsc,&rsdsc,&cxt,
11693 &dfltdsc,NULL,&rms_sts,&lff_flags);
11694 if (!$VMS_STATUS_SUCCESS(sts))
11697 /* with varying string, 1st word of buffer contains result length */
11698 rstr[rslt->length] = '\0';
11700 /* Find where all the components are */
11701 v_sts = vms_split_path
11716 /* If no version on input, truncate the version on output */
11717 if (!hasver && (vs_len > 0)) {
11721 /* No version & a null extension on UNIX handling */
11722 if (isunix && (e_len == 1) && decc_readdir_dropdotnotype) {
11728 if (!decc_efs_case_preserve) {
11729 for (cp = rstr; *cp; cp++) *cp = _tolower(*cp);
11733 if (isunix) trim_unixpath(rstr,SvPVX(tmpglob),1);
11737 /* Start with the name */
11740 strcat(begin,"\n");
11741 ok = (PerlIO_puts(tmpfp,begin) != EOF);
11743 if (cxt) (void)lib$find_file_end(&cxt);
11744 if (ok && sts != RMS$_NMF &&
11745 sts != RMS$_DNF && sts != RMS_FNF) ok = 0;
11748 SETERRNO((sts == RMS$_SYN ? EINVAL : EVMSERR),sts);
11750 PerlIO_close(tmpfp);
11754 PerlIO_rewind(tmpfp);
11755 IoTYPE(io) = IoTYPE_RDONLY;
11756 IoIFP(io) = fp = tmpfp;
11757 IoFLAGS(io) &= ~IOf_UNTAINT; /* maybe redundant */
11767 mp_do_vms_realpath(pTHX_ const char *filespec, char * rslt_spec, const int *utf8_fl);
11770 vms_realpath_fromperl(pTHX_ CV *cv)
11773 char *fspec, *rslt_spec, *rslt;
11776 if (!items || items != 1)
11777 Perl_croak(aTHX_ "Usage: VMS::Filespec::vms_realpath(spec)");
11779 fspec = SvPV(ST(0),n_a);
11780 if (!fspec || !*fspec) XSRETURN_UNDEF;
11782 Newx(rslt_spec, VMS_MAXRSS + 1, char);
11783 rslt = do_vms_realpath(fspec, rslt_spec, NULL);
11784 ST(0) = sv_newmortal();
11786 sv_usepvn(ST(0),rslt,strlen(rslt));
11788 Safefree(rslt_spec);
11793 #if __CRTL_VER >= 70301000 && !defined(__VAX)
11794 int do_vms_case_tolerant(void);
11797 vms_case_tolerant_fromperl(pTHX_ CV *cv)
11800 ST(0) = boolSV(do_vms_case_tolerant());
11806 Perl_sys_intern_dup(pTHX_ struct interp_intern *src,
11807 struct interp_intern *dst)
11809 memcpy(dst,src,sizeof(struct interp_intern));
11813 Perl_sys_intern_clear(pTHX)
11818 Perl_sys_intern_init(pTHX)
11820 unsigned int ix = RAND_MAX;
11825 /* fix me later to track running under GNV */
11826 /* this allows some limited testing */
11827 MY_POSIX_EXIT = decc_filename_unix_report;
11830 MY_INV_RAND_MAX = 1./x;
11834 init_os_extras(void)
11837 char* file = __FILE__;
11838 if (decc_disable_to_vms_logname_translation) {
11839 no_translate_barewords = TRUE;
11841 no_translate_barewords = FALSE;
11844 newXSproto("VMS::Filespec::rmsexpand",rmsexpand_fromperl,file,"$;$");
11845 newXSproto("VMS::Filespec::vmsify",vmsify_fromperl,file,"$");
11846 newXSproto("VMS::Filespec::unixify",unixify_fromperl,file,"$");
11847 newXSproto("VMS::Filespec::pathify",pathify_fromperl,file,"$");
11848 newXSproto("VMS::Filespec::fileify",fileify_fromperl,file,"$");
11849 newXSproto("VMS::Filespec::vmspath",vmspath_fromperl,file,"$");
11850 newXSproto("VMS::Filespec::unixpath",unixpath_fromperl,file,"$");
11851 newXSproto("VMS::Filespec::candelete",candelete_fromperl,file,"$");
11852 newXSproto("DynaLoader::mod2fname", mod2fname, file, "$");
11853 newXS("File::Copy::rmscopy",rmscopy_fromperl,file);
11854 newXSproto("vmsish::hushed",hushexit_fromperl,file,";$");
11856 newXSproto("VMS::Filespec::vms_realpath",vms_realpath_fromperl,file,"$;$");
11858 #if __CRTL_VER >= 70301000 && !defined(__VAX)
11859 newXSproto("VMS::Filespec::case_tolerant",vms_case_tolerant_fromperl,file,"$;$");
11862 store_pipelocs(aTHX); /* will redo any earlier attempts */
11869 #if __CRTL_VER == 80200000
11870 /* This missed getting in to the DECC SDK for 8.2 */
11871 char *realpath(const char *file_name, char * resolved_name, ...);
11874 /*{{{char *do_vms_realpath(const char *file_name, char *resolved_name)*/
11875 /* wrapper for the realpath() function added with 8.2 RMS SYMLINK SDK.
11876 * The perl fallback routine to provide realpath() is not as efficient
11880 mp_do_vms_realpath(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
11882 return realpath(filespec, outbuf);
11886 /* External entry points */
11887 char *Perl_vms_realpath(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
11888 { return do_vms_realpath(filespec, outbuf, utf8_fl); }
11890 char *Perl_vms_realpath(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
11895 #if __CRTL_VER >= 70301000 && !defined(__VAX)
11896 /* case_tolerant */
11898 /*{{{int do_vms_case_tolerant(void)*/
11899 /* OpenVMS provides a case sensitive implementation of ODS-5 and this is
11900 * controlled by a process setting.
11902 int do_vms_case_tolerant(void)
11904 return vms_process_case_tolerant;
11907 /* External entry points */
11908 int Perl_vms_case_tolerant(void)
11909 { return do_vms_case_tolerant(); }
11911 int Perl_vms_case_tolerant(void)
11912 { return vms_process_case_tolerant; }
11916 /* Start of DECC RTL Feature handling */
11918 static int sys_trnlnm
11919 (const char * logname,
11923 const $DESCRIPTOR(table_dsc, "LNM$FILE_DEV");
11924 const unsigned long attr = LNM$M_CASE_BLIND;
11925 struct dsc$descriptor_s name_dsc;
11927 unsigned short result;
11928 struct itmlst_3 itlst[2] = {{value_len, LNM$_STRING, value, &result},
11931 name_dsc.dsc$w_length = strlen(logname);
11932 name_dsc.dsc$a_pointer = (char *)logname;
11933 name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
11934 name_dsc.dsc$b_class = DSC$K_CLASS_S;
11936 status = sys$trnlnm(&attr, &table_dsc, &name_dsc, 0, itlst);
11938 if ($VMS_STATUS_SUCCESS(status)) {
11940 /* Null terminate and return the string */
11941 /*--------------------------------------*/
11948 static int sys_crelnm
11949 (const char * logname,
11950 const char * value)
11953 const char * proc_table = "LNM$PROCESS_TABLE";
11954 struct dsc$descriptor_s proc_table_dsc;
11955 struct dsc$descriptor_s logname_dsc;
11956 struct itmlst_3 item_list[2];
11958 proc_table_dsc.dsc$a_pointer = (char *) proc_table;
11959 proc_table_dsc.dsc$w_length = strlen(proc_table);
11960 proc_table_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
11961 proc_table_dsc.dsc$b_class = DSC$K_CLASS_S;
11963 logname_dsc.dsc$a_pointer = (char *) logname;
11964 logname_dsc.dsc$w_length = strlen(logname);
11965 logname_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
11966 logname_dsc.dsc$b_class = DSC$K_CLASS_S;
11968 item_list[0].buflen = strlen(value);
11969 item_list[0].itmcode = LNM$_STRING;
11970 item_list[0].bufadr = (char *)value;
11971 item_list[0].retlen = NULL;
11973 item_list[1].buflen = 0;
11974 item_list[1].itmcode = 0;
11976 ret_val = sys$crelnm
11978 (const struct dsc$descriptor_s *)&proc_table_dsc,
11979 (const struct dsc$descriptor_s *)&logname_dsc,
11981 (const struct item_list_3 *) item_list);
11986 /* C RTL Feature settings */
11988 static int set_features
11989 (int (* init_coroutine)(int *, int *, void *), /* Needs casts if used */
11990 int (* cli_routine)(void), /* Not documented */
11991 void *image_info) /* Not documented */
11998 #if defined(JPI$_CASE_LOOKUP_PERM) && !defined(__VAX)
11999 const unsigned long int jpicode1 = JPI$_CASE_LOOKUP_PERM;
12000 const unsigned long int jpicode2 = JPI$_CASE_LOOKUP_IMAGE;
12001 unsigned long case_perm;
12002 unsigned long case_image;
12005 /* Allow an exception to bring Perl into the VMS debugger */
12006 vms_debug_on_exception = 0;
12007 status = sys_trnlnm("PERL_VMS_EXCEPTION_DEBUG", val_str, sizeof(val_str));
12008 if ($VMS_STATUS_SUCCESS(status)) {
12009 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
12010 vms_debug_on_exception = 1;
12012 vms_debug_on_exception = 0;
12015 /* Create VTF-7 filenames from UNICODE instead of UTF-8 */
12016 vms_vtf7_filenames = 0;
12017 status = sys_trnlnm("PERL_VMS_VTF7_FILENAMES", val_str, sizeof(val_str));
12018 if ($VMS_STATUS_SUCCESS(status)) {
12019 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
12020 vms_vtf7_filenames = 1;
12022 vms_vtf7_filenames = 0;
12025 /* Dectect running under GNV Bash or other UNIX like shell */
12026 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12027 gnv_unix_shell = 0;
12028 status = sys_trnlnm("GNV$UNIX_SHELL", val_str, sizeof(val_str));
12029 if ($VMS_STATUS_SUCCESS(status)) {
12030 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
12031 gnv_unix_shell = 1;
12032 set_feature_default("DECC$EFS_CASE_PRESERVE", 1);
12033 set_feature_default("DECC$EFS_CHARSET", 1);
12034 set_feature_default("DECC$FILENAME_UNIX_NO_VERSION", 1);
12035 set_feature_default("DECC$FILENAME_UNIX_REPORT", 1);
12036 set_feature_default("DECC$READDIR_DROPDOTNOTYPE", 1);
12037 set_feature_default("DECC$DISABLE_POSIX_ROOT", 0);
12040 gnv_unix_shell = 0;
12044 /* hacks to see if known bugs are still present for testing */
12046 /* Readdir is returning filenames in VMS syntax always */
12047 decc_bug_readdir_efs1 = 1;
12048 status = sys_trnlnm("DECC_BUG_READDIR_EFS1", val_str, sizeof(val_str));
12049 if ($VMS_STATUS_SUCCESS(status)) {
12050 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
12051 decc_bug_readdir_efs1 = 1;
12053 decc_bug_readdir_efs1 = 0;
12056 /* PCP mode requires creating /dev/null special device file */
12057 decc_bug_devnull = 0;
12058 status = sys_trnlnm("DECC_BUG_DEVNULL", val_str, sizeof(val_str));
12059 if ($VMS_STATUS_SUCCESS(status)) {
12060 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
12061 decc_bug_devnull = 1;
12063 decc_bug_devnull = 0;
12066 /* fgetname returning a VMS name in UNIX mode */
12067 decc_bug_fgetname = 1;
12068 status = sys_trnlnm("DECC_BUG_FGETNAME", val_str, sizeof(val_str));
12069 if ($VMS_STATUS_SUCCESS(status)) {
12070 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
12071 decc_bug_fgetname = 1;
12073 decc_bug_fgetname = 0;
12076 /* UNIX directory names with no paths are broken in a lot of places */
12077 decc_dir_barename = 1;
12078 status = sys_trnlnm("DECC_DIR_BARENAME", val_str, sizeof(val_str));
12079 if ($VMS_STATUS_SUCCESS(status)) {
12080 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
12081 decc_dir_barename = 1;
12083 decc_dir_barename = 0;
12086 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12087 s = decc$feature_get_index("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION");
12089 decc_disable_to_vms_logname_translation = decc$feature_get_value(s, 1);
12090 if (decc_disable_to_vms_logname_translation < 0)
12091 decc_disable_to_vms_logname_translation = 0;
12094 s = decc$feature_get_index("DECC$EFS_CASE_PRESERVE");
12096 decc_efs_case_preserve = decc$feature_get_value(s, 1);
12097 if (decc_efs_case_preserve < 0)
12098 decc_efs_case_preserve = 0;
12101 s = decc$feature_get_index("DECC$EFS_CHARSET");
12103 decc_efs_charset = decc$feature_get_value(s, 1);
12104 if (decc_efs_charset < 0)
12105 decc_efs_charset = 0;
12108 s = decc$feature_get_index("DECC$FILENAME_UNIX_REPORT");
12110 decc_filename_unix_report = decc$feature_get_value(s, 1);
12111 if (decc_filename_unix_report > 0)
12112 decc_filename_unix_report = 1;
12114 decc_filename_unix_report = 0;
12117 s = decc$feature_get_index("DECC$FILENAME_UNIX_ONLY");
12119 decc_filename_unix_only = decc$feature_get_value(s, 1);
12120 if (decc_filename_unix_only > 0) {
12121 decc_filename_unix_only = 1;
12124 decc_filename_unix_only = 0;
12128 s = decc$feature_get_index("DECC$FILENAME_UNIX_NO_VERSION");
12130 decc_filename_unix_no_version = decc$feature_get_value(s, 1);
12131 if (decc_filename_unix_no_version < 0)
12132 decc_filename_unix_no_version = 0;
12135 s = decc$feature_get_index("DECC$READDIR_DROPDOTNOTYPE");
12137 decc_readdir_dropdotnotype = decc$feature_get_value(s, 1);
12138 if (decc_readdir_dropdotnotype < 0)
12139 decc_readdir_dropdotnotype = 0;
12142 status = sys_trnlnm("SYS$POSIX_ROOT", val_str, sizeof(val_str));
12143 if ($VMS_STATUS_SUCCESS(status)) {
12144 s = decc$feature_get_index("DECC$DISABLE_POSIX_ROOT");
12146 dflt = decc$feature_get_value(s, 4);
12148 decc_disable_posix_root = decc$feature_get_value(s, 1);
12149 if (decc_disable_posix_root <= 0) {
12150 decc$feature_set_value(s, 1, 1);
12151 decc_disable_posix_root = 1;
12155 /* Traditionally Perl assumes this is off */
12156 decc_disable_posix_root = 1;
12157 decc$feature_set_value(s, 1, 1);
12162 #if __CRTL_VER >= 80200000
12163 s = decc$feature_get_index("DECC$POSIX_COMPLIANT_PATHNAMES");
12165 decc_posix_compliant_pathnames = decc$feature_get_value(s, 1);
12166 if (decc_posix_compliant_pathnames < 0)
12167 decc_posix_compliant_pathnames = 0;
12168 if (decc_posix_compliant_pathnames > 4)
12169 decc_posix_compliant_pathnames = 0;
12174 status = sys_trnlnm
12175 ("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION", val_str, sizeof(val_str));
12176 if ($VMS_STATUS_SUCCESS(status)) {
12177 val_str[0] = _toupper(val_str[0]);
12178 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
12179 decc_disable_to_vms_logname_translation = 1;
12184 status = sys_trnlnm("DECC$EFS_CASE_PRESERVE", val_str, sizeof(val_str));
12185 if ($VMS_STATUS_SUCCESS(status)) {
12186 val_str[0] = _toupper(val_str[0]);
12187 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
12188 decc_efs_case_preserve = 1;
12193 status = sys_trnlnm("DECC$FILENAME_UNIX_REPORT", val_str, sizeof(val_str));
12194 if ($VMS_STATUS_SUCCESS(status)) {
12195 val_str[0] = _toupper(val_str[0]);
12196 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
12197 decc_filename_unix_report = 1;
12200 status = sys_trnlnm("DECC$FILENAME_UNIX_ONLY", val_str, sizeof(val_str));
12201 if ($VMS_STATUS_SUCCESS(status)) {
12202 val_str[0] = _toupper(val_str[0]);
12203 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
12204 decc_filename_unix_only = 1;
12205 decc_filename_unix_report = 1;
12208 status = sys_trnlnm("DECC$FILENAME_UNIX_NO_VERSION", val_str, sizeof(val_str));
12209 if ($VMS_STATUS_SUCCESS(status)) {
12210 val_str[0] = _toupper(val_str[0]);
12211 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
12212 decc_filename_unix_no_version = 1;
12215 status = sys_trnlnm("DECC$READDIR_DROPDOTNOTYPE", val_str, sizeof(val_str));
12216 if ($VMS_STATUS_SUCCESS(status)) {
12217 val_str[0] = _toupper(val_str[0]);
12218 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
12219 decc_readdir_dropdotnotype = 1;
12224 #if defined(JPI$_CASE_LOOKUP_PERM) && !defined(__VAX)
12226 /* Report true case tolerance */
12227 /*----------------------------*/
12228 status = lib$getjpi(&jpicode1, 0, 0, &case_perm, 0, 0);
12229 if (!$VMS_STATUS_SUCCESS(status))
12230 case_perm = PPROP$K_CASE_BLIND;
12231 status = lib$getjpi(&jpicode2, 0, 0, &case_image, 0, 0);
12232 if (!$VMS_STATUS_SUCCESS(status))
12233 case_image = PPROP$K_CASE_BLIND;
12234 if ((case_perm == PPROP$K_CASE_SENSITIVE) ||
12235 (case_image == PPROP$K_CASE_SENSITIVE))
12236 vms_process_case_tolerant = 0;
12241 /* CRTL can be initialized past this point, but not before. */
12242 /* DECC$CRTL_INIT(); */
12248 /* DECC dependent attributes */
12249 #if __DECC_VER < 60560002
12251 #define not_executable
12253 #define relative ,rel
12254 #define not_executable ,noexe
12257 #pragma extern_model save
12258 #pragma extern_model strict_refdef "LIB$INITIALIZ" nowrt
12260 const __align (LONGWORD) int spare[8] = {0};
12261 /* .psect LIB$INITIALIZE, NOPIC, USR, CON, REL, GBL, NOSHR, NOEXE, RD, */
12264 #pragma extern_model strict_refdef "LIB$INITIALIZE" con, gbl,noshr, \
12265 nowrt,noshr relative not_executable
12267 const long vms_cc_features = (const long)set_features;
12270 ** Force a reference to LIB$INITIALIZE to ensure it
12271 ** exists in the image.
12273 int lib$initialize(void);
12275 #pragma extern_model strict_refdef
12277 int lib_init_ref = (int) lib$initialize;
12280 #pragma extern_model restore