3 * VMS-specific routines for perl5
6 * August 2005 Convert VMS status code to UNIX status codes
7 * August 2000 tweaks to vms_image_init, my_flush, my_fwrite, cando_by_name,
8 * and Perl_cando by Craig Berry
9 * 29-Aug-2000 Charles Lane's piping improvements rolled in
10 * 20-Aug-1999 revisions by Charles Bailey bailey@newman.upenn.edu
19 #include <climsgdef.h>
30 #include <libclidef.h>
32 #include <lib$routines.h>
36 #if __CRTL_VER >= 70301000 && !defined(__VAX)
46 #include <str$routines.h>
53 #if __CRTL_VER >= 70000000 /* FIXME to earliest version */
55 #define NO_EFN EFN$C_ENF
60 #if __CRTL_VER < 70301000 && __CRTL_VER >= 70300000
61 int decc$feature_get_index(const char *name);
62 char* decc$feature_get_name(int index);
63 int decc$feature_get_value(int index, int mode);
64 int decc$feature_set_value(int index, int mode, int value);
69 #pragma member_alignment save
70 #pragma nomember_alignment longword
75 unsigned short * retadr;
77 #pragma member_alignment restore
79 /* More specific prototype than in starlet_c.h makes programming errors
87 const struct dsc$descriptor_s * devnam,
88 const struct item_list_3 * itmlst,
90 void * (astadr)(unsigned long),
95 #ifdef sys$get_security
96 #undef sys$get_security
98 (const struct dsc$descriptor_s * clsnam,
99 const struct dsc$descriptor_s * objnam,
100 const unsigned int *objhan,
102 const struct item_list_3 * itmlst,
103 unsigned int * contxt,
104 const unsigned int * acmode);
107 #ifdef sys$set_security
108 #undef sys$set_security
110 (const struct dsc$descriptor_s * clsnam,
111 const struct dsc$descriptor_s * objnam,
112 const unsigned int *objhan,
114 const struct item_list_3 * itmlst,
115 unsigned int * contxt,
116 const unsigned int * acmode);
119 #ifdef lib$find_image_symbol
120 #undef lib$find_image_symbol
121 int lib$find_image_symbol
122 (const struct dsc$descriptor_s * imgname,
123 const struct dsc$descriptor_s * symname,
125 const struct dsc$descriptor_s * defspec,
129 #ifdef lib$rename_file
130 #undef lib$rename_file
132 (const struct dsc$descriptor_s * old_file_dsc,
133 const struct dsc$descriptor_s * new_file_dsc,
134 const struct dsc$descriptor_s * default_file_dsc,
135 const struct dsc$descriptor_s * related_file_dsc,
136 const unsigned long * flags,
137 void * (success)(const struct dsc$descriptor_s * old_dsc,
138 const struct dsc$descriptor_s * new_dsc,
140 void * (error)(const struct dsc$descriptor_s * old_dsc,
141 const struct dsc$descriptor_s * new_dsc,
144 const int * error_src,
145 const void * usr_arg),
146 int (confirm)(const struct dsc$descriptor_s * old_dsc,
147 const struct dsc$descriptor_s * new_dsc,
148 const void * old_fab,
149 const void * usr_arg),
151 struct dsc$descriptor_s * old_result_name_dsc,
152 struct dsc$descriptor_s * new_result_name_dsc,
153 unsigned long * file_scan_context);
156 #if __CRTL_VER >= 70300000 && !defined(__VAX)
158 static int set_feature_default(const char *name, int value)
163 index = decc$feature_get_index(name);
165 status = decc$feature_set_value(index, 1, value);
166 if (index == -1 || (status == -1)) {
170 status = decc$feature_get_value(index, 1);
171 if (status != value) {
179 /* Older versions of ssdef.h don't have these */
180 #ifndef SS$_INVFILFOROP
181 # define SS$_INVFILFOROP 3930
183 #ifndef SS$_NOSUCHOBJECT
184 # define SS$_NOSUCHOBJECT 2696
187 /* We implement I/O here, so we will be mixing PerlIO and stdio calls. */
188 #define PERLIO_NOT_STDIO 0
190 /* Don't replace system definitions of vfork, getenv, lstat, and stat,
191 * code below needs to get to the underlying CRTL routines. */
192 #define DONT_MASK_RTL_CALLS
196 /* Anticipating future expansion in lexical warnings . . . */
197 #ifndef WARN_INTERNAL
198 # define WARN_INTERNAL WARN_MISC
201 #ifdef VMS_LONGNAME_SUPPORT
202 #include <libfildef.h>
205 #if defined(__VMS_VER) && __VMS_VER >= 70000000 && __DECC_VER >= 50200000
206 # define RTL_USES_UTC 1
209 /* Routine to create a decterm for use with the Perl debugger */
210 /* No headers, this information was found in the Programming Concepts Manual */
212 static int (*decw_term_port)
213 (const struct dsc$descriptor_s * display,
214 const struct dsc$descriptor_s * setup_file,
215 const struct dsc$descriptor_s * customization,
216 struct dsc$descriptor_s * result_device_name,
217 unsigned short * result_device_name_length,
220 void * char_change_buffer) = 0;
222 /* gcc's header files don't #define direct access macros
223 * corresponding to VAXC's variant structs */
225 # define uic$v_format uic$r_uic_form.uic$v_format
226 # define uic$v_group uic$r_uic_form.uic$v_group
227 # define uic$v_member uic$r_uic_form.uic$v_member
228 # define prv$v_bypass prv$r_prvdef_bits0.prv$v_bypass
229 # define prv$v_grpprv prv$r_prvdef_bits0.prv$v_grpprv
230 # define prv$v_readall prv$r_prvdef_bits0.prv$v_readall
231 # define prv$v_sysprv prv$r_prvdef_bits0.prv$v_sysprv
234 #if defined(NEED_AN_H_ERRNO)
239 #pragma message disable pragma
240 #pragma member_alignment save
241 #pragma nomember_alignment longword
243 #pragma message disable misalgndmem
246 unsigned short int buflen;
247 unsigned short int itmcode;
249 unsigned short int *retlen;
252 struct filescan_itmlst_2 {
253 unsigned short length;
254 unsigned short itmcode;
259 unsigned short length;
264 #pragma message restore
265 #pragma member_alignment restore
268 #define do_fileify_dirspec(a,b,c,d) mp_do_fileify_dirspec(aTHX_ a,b,c,d)
269 #define do_pathify_dirspec(a,b,c,d) mp_do_pathify_dirspec(aTHX_ a,b,c,d)
270 #define do_tovmsspec(a,b,c,d) mp_do_tovmsspec(aTHX_ a,b,c,0,d)
271 #define do_tovmspath(a,b,c,d) mp_do_tovmspath(aTHX_ a,b,c,d)
272 #define do_rmsexpand(a,b,c,d,e,f,g) mp_do_rmsexpand(aTHX_ a,b,c,d,e,f,g)
273 #define do_vms_realpath(a,b,c) mp_do_vms_realpath(aTHX_ a,b,c)
274 #define do_tounixspec(a,b,c,d) mp_do_tounixspec(aTHX_ a,b,c,d)
275 #define do_tounixpath(a,b,c,d) mp_do_tounixpath(aTHX_ a,b,c,d)
276 #define do_vms_case_tolerant(a) mp_do_vms_case_tolerant(a)
277 #define expand_wild_cards(a,b,c,d) mp_expand_wild_cards(aTHX_ a,b,c,d)
278 #define getredirection(a,b) mp_getredirection(aTHX_ a,b)
280 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int *);
281 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int *);
282 static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *);
283 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int *);
285 /* see system service docs for $TRNLNM -- NOT the same as LNM$_MAX_INDEX */
286 #define PERL_LNM_MAX_ALLOWED_INDEX 127
288 /* OpenVMS User's Guide says at least 9 iterative translations will be performed,
289 * depending on the facility. SHOW LOGICAL does 10, so we'll imitate that for
292 #define PERL_LNM_MAX_ITER 10
294 /* New values with 7.3-2*/ /* why does max DCL have 4 byte subtracted from it? */
295 #if __CRTL_VER >= 70302000 && !defined(__VAX)
296 #define MAX_DCL_SYMBOL (8192)
297 #define MAX_DCL_LINE_LENGTH (4096 - 4)
299 #define MAX_DCL_SYMBOL (1024)
300 #define MAX_DCL_LINE_LENGTH (1024 - 4)
303 static char *__mystrtolower(char *str)
305 if (str) for (; *str; ++str) *str= tolower(*str);
309 static struct dsc$descriptor_s fildevdsc =
310 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "LNM$FILE_DEV" };
311 static struct dsc$descriptor_s crtlenvdsc =
312 { 8, DSC$K_DTYPE_T, DSC$K_CLASS_S, "CRTL_ENV" };
313 static struct dsc$descriptor_s *fildev[] = { &fildevdsc, NULL };
314 static struct dsc$descriptor_s *defenv[] = { &fildevdsc, &crtlenvdsc, NULL };
315 static struct dsc$descriptor_s **env_tables = defenv;
316 static bool will_taint = FALSE; /* tainting active, but no PL_curinterp yet */
318 /* True if we shouldn't treat barewords as logicals during directory */
320 static int no_translate_barewords;
323 static int tz_updated = 1;
326 /* DECC Features that may need to affect how Perl interprets
327 * displays filename information
329 static int decc_disable_to_vms_logname_translation = 1;
330 static int decc_disable_posix_root = 1;
331 int decc_efs_case_preserve = 0;
332 static int decc_efs_charset = 0;
333 static int decc_filename_unix_no_version = 0;
334 static int decc_filename_unix_only = 0;
335 int decc_filename_unix_report = 0;
336 int decc_posix_compliant_pathnames = 0;
337 int decc_readdir_dropdotnotype = 0;
338 static int vms_process_case_tolerant = 1;
339 int vms_vtf7_filenames = 0;
340 int gnv_unix_shell = 0;
341 static int vms_unlink_all_versions = 0;
343 /* bug workarounds if needed */
344 int decc_bug_readdir_efs1 = 0;
345 int decc_bug_devnull = 1;
346 int decc_bug_fgetname = 0;
347 int decc_dir_barename = 0;
349 static int vms_debug_on_exception = 0;
351 /* Is this a UNIX file specification?
352 * No longer a simple check with EFS file specs
353 * For now, not a full check, but need to
354 * handle POSIX ^UP^ specifications
355 * Fixing to handle ^/ cases would require
356 * changes to many other conversion routines.
359 static int is_unix_filespec(const char *path)
365 if (strncmp(path,"\"^UP^",5) != 0) {
366 pch1 = strchr(path, '/');
371 /* If the user wants UNIX files, "." needs to be treated as in UNIX */
372 if (decc_filename_unix_report || decc_filename_unix_only) {
373 if (strcmp(path,".") == 0)
381 /* This routine converts a UCS-2 character to be VTF-7 encoded.
384 static void ucs2_to_vtf7
386 unsigned long ucs2_char,
389 unsigned char * ucs_ptr;
392 ucs_ptr = (unsigned char *)&ucs2_char;
396 hex = (ucs_ptr[1] >> 4) & 0xf;
398 outspec[2] = hex + '0';
400 outspec[2] = (hex - 9) + 'A';
401 hex = ucs_ptr[1] & 0xF;
403 outspec[3] = hex + '0';
405 outspec[3] = (hex - 9) + 'A';
407 hex = (ucs_ptr[0] >> 4) & 0xf;
409 outspec[4] = hex + '0';
411 outspec[4] = (hex - 9) + 'A';
412 hex = ucs_ptr[1] & 0xF;
414 outspec[5] = hex + '0';
416 outspec[5] = (hex - 9) + 'A';
422 /* This handles the conversion of a UNIX extended character set to a ^
423 * escaped VMS character.
424 * in a UNIX file specification.
426 * The output count variable contains the number of characters added
427 * to the output string.
429 * The return value is the number of characters read from the input string
431 static int copy_expand_unix_filename_escape
432 (char *outspec, const char *inspec, int *output_cnt, const int * utf8_fl)
440 utf8_flag = *utf8_fl;
444 if (*inspec >= 0x80) {
445 if (utf8_fl && vms_vtf7_filenames) {
446 unsigned long ucs_char;
450 if ((*inspec & 0xE0) == 0xC0) {
452 ucs_char = ((inspec[0] & 0x1F) << 6) + (inspec[1] & 0x3f);
453 if (ucs_char >= 0x80) {
454 ucs2_to_vtf7(outspec, ucs_char, output_cnt);
457 } else if ((*inspec & 0xF0) == 0xE0) {
459 ucs_char = ((inspec[0] & 0xF) << 12) +
460 ((inspec[1] & 0x3f) << 6) +
462 if (ucs_char >= 0x800) {
463 ucs2_to_vtf7(outspec, ucs_char, output_cnt);
467 #if 0 /* I do not see longer sequences supported by OpenVMS */
468 /* Maybe some one can fix this later */
469 } else if ((*inspec & 0xF8) == 0xF0) {
472 } else if ((*inspec & 0xFC) == 0xF8) {
475 } else if ((*inspec & 0xFE) == 0xFC) {
482 /* High bit set, but not a Unicode character! */
484 /* Non printing DECMCS or ISO Latin-1 character? */
485 if (*inspec <= 0x9F) {
489 hex = (*inspec >> 4) & 0xF;
491 outspec[1] = hex + '0';
493 outspec[1] = (hex - 9) + 'A';
497 outspec[2] = hex + '0';
499 outspec[2] = (hex - 9) + 'A';
503 } else if (*inspec == 0xA0) {
509 } else if (*inspec == 0xFF) {
521 /* Is this a macro that needs to be passed through?
522 * Macros start with $( and an alpha character, followed
523 * by a string of alpha numeric characters ending with a )
524 * If this does not match, then encode it as ODS-5.
526 if ((inspec[0] == '$') && (inspec[1] == '(')) {
529 if (isalnum(inspec[2]) || (inspec[2] == '.') || (inspec[2] == '_')) {
531 outspec[0] = inspec[0];
532 outspec[1] = inspec[1];
533 outspec[2] = inspec[2];
535 while(isalnum(inspec[tcnt]) ||
536 (inspec[2] == '.') || (inspec[2] == '_')) {
537 outspec[tcnt] = inspec[tcnt];
540 if (inspec[tcnt] == ')') {
541 outspec[tcnt] = inspec[tcnt];
558 if (decc_efs_charset == 0)
584 /* Don't escape again if following character is
585 * already something we escape.
587 if (strchr(".~!#&\'`()+@{},;[]%^=_", *(inspec+1))) {
593 /* But otherwise fall through and escape it. */
595 /* Assume that this is to be escaped */
597 outspec[1] = *inspec;
601 case ' ': /* space */
602 /* Assume that this is to be escaped */
617 /* This handles the expansion of a '^' prefix to the proper character
618 * in a UNIX file specification.
620 * The output count variable contains the number of characters added
621 * to the output string.
623 * The return value is the number of characters read from the input
626 static int copy_expand_vms_filename_escape
627 (char *outspec, const char *inspec, int *output_cnt)
634 if (*inspec == '^') {
637 /* Spaces and non-trailing dots should just be passed through,
638 * but eat the escape character.
645 case '_': /* space */
651 /* Hmm. Better leave the escape escaped. */
657 case 'U': /* Unicode - FIX-ME this is wrong. */
660 scnt = strspn(inspec, "0123456789ABCDEFabcdef");
663 scnt = sscanf(inspec, "%2x%2x", &c1, &c2);
664 outspec[0] == c1 & 0xff;
665 outspec[1] == c2 & 0xff;
672 /* Error - do best we can to continue */
682 scnt = strspn(inspec, "0123456789ABCDEFabcdef");
686 scnt = sscanf(inspec, "%2x", &c1);
687 outspec[0] = c1 & 0xff;
711 (const struct dsc$descriptor_s * srcstr,
712 struct filescan_itmlst_2 * valuelist,
713 unsigned long * fldflags,
714 struct dsc$descriptor_s *auxout,
715 unsigned short * retlen);
718 /* vms_split_path - Verify that the input file specification is a
719 * VMS format file specification, and provide pointers to the components of
720 * it. With EFS format filenames, this is virtually the only way to
721 * parse a VMS path specification into components.
723 * If the sum of the components do not add up to the length of the
724 * string, then the passed file specification is probably a UNIX style
727 static int vms_split_path
742 struct dsc$descriptor path_desc;
746 struct filescan_itmlst_2 item_list[9];
747 const int filespec = 0;
748 const int nodespec = 1;
749 const int devspec = 2;
750 const int rootspec = 3;
751 const int dirspec = 4;
752 const int namespec = 5;
753 const int typespec = 6;
754 const int verspec = 7;
756 /* Assume the worst for an easy exit */
771 path_desc.dsc$a_pointer = (char *)path; /* cast ok */
772 path_desc.dsc$w_length = strlen(path);
773 path_desc.dsc$b_dtype = DSC$K_DTYPE_T;
774 path_desc.dsc$b_class = DSC$K_CLASS_S;
776 /* Get the total length, if it is shorter than the string passed
777 * then this was probably not a VMS formatted file specification
779 item_list[filespec].itmcode = FSCN$_FILESPEC;
780 item_list[filespec].length = 0;
781 item_list[filespec].component = NULL;
783 /* If the node is present, then it gets considered as part of the
784 * volume name to hopefully make things simple.
786 item_list[nodespec].itmcode = FSCN$_NODE;
787 item_list[nodespec].length = 0;
788 item_list[nodespec].component = NULL;
790 item_list[devspec].itmcode = FSCN$_DEVICE;
791 item_list[devspec].length = 0;
792 item_list[devspec].component = NULL;
794 /* root is a special case, adding it to either the directory or
795 * the device components will probalby complicate things for the
796 * callers of this routine, so leave it separate.
798 item_list[rootspec].itmcode = FSCN$_ROOT;
799 item_list[rootspec].length = 0;
800 item_list[rootspec].component = NULL;
802 item_list[dirspec].itmcode = FSCN$_DIRECTORY;
803 item_list[dirspec].length = 0;
804 item_list[dirspec].component = NULL;
806 item_list[namespec].itmcode = FSCN$_NAME;
807 item_list[namespec].length = 0;
808 item_list[namespec].component = NULL;
810 item_list[typespec].itmcode = FSCN$_TYPE;
811 item_list[typespec].length = 0;
812 item_list[typespec].component = NULL;
814 item_list[verspec].itmcode = FSCN$_VERSION;
815 item_list[verspec].length = 0;
816 item_list[verspec].component = NULL;
818 item_list[8].itmcode = 0;
819 item_list[8].length = 0;
820 item_list[8].component = NULL;
822 status = sys$filescan
823 ((const struct dsc$descriptor_s *)&path_desc, item_list,
825 _ckvmssts_noperl(status); /* All failure status values indicate a coding error */
827 /* If we parsed it successfully these two lengths should be the same */
828 if (path_desc.dsc$w_length != item_list[filespec].length)
831 /* If we got here, then it is a VMS file specification */
834 /* set the volume name */
835 if (item_list[nodespec].length > 0) {
836 *volume = item_list[nodespec].component;
837 *vol_len = item_list[nodespec].length + item_list[devspec].length;
840 *volume = item_list[devspec].component;
841 *vol_len = item_list[devspec].length;
844 *root = item_list[rootspec].component;
845 *root_len = item_list[rootspec].length;
847 *dir = item_list[dirspec].component;
848 *dir_len = item_list[dirspec].length;
850 /* Now fun with versions and EFS file specifications
851 * The parser can not tell the difference when a "." is a version
852 * delimiter or a part of the file specification.
854 if ((decc_efs_charset) &&
855 (item_list[verspec].length > 0) &&
856 (item_list[verspec].component[0] == '.')) {
857 *name = item_list[namespec].component;
858 *name_len = item_list[namespec].length + item_list[typespec].length;
859 *ext = item_list[verspec].component;
860 *ext_len = item_list[verspec].length;
865 *name = item_list[namespec].component;
866 *name_len = item_list[namespec].length;
867 *ext = item_list[typespec].component;
868 *ext_len = item_list[typespec].length;
869 *version = item_list[verspec].component;
870 *ver_len = item_list[verspec].length;
877 * Routine to retrieve the maximum equivalence index for an input
878 * logical name. Some calls to this routine have no knowledge if
879 * the variable is a logical or not. So on error we return a max
882 /*{{{int my_maxidx(const char *lnm) */
884 my_maxidx(const char *lnm)
888 int attr = LNM$M_CASE_BLIND;
889 struct dsc$descriptor lnmdsc;
890 struct itmlst_3 itlst[2] = {{sizeof(midx), LNM$_MAX_INDEX, &midx, 0},
893 lnmdsc.dsc$w_length = strlen(lnm);
894 lnmdsc.dsc$b_dtype = DSC$K_DTYPE_T;
895 lnmdsc.dsc$b_class = DSC$K_CLASS_S;
896 lnmdsc.dsc$a_pointer = (char *) lnm; /* Cast ok for read only parameter */
898 status = sys$trnlnm(&attr, &fildevdsc, &lnmdsc, 0, itlst);
899 if ((status & 1) == 0)
906 /*{{{int vmstrnenv(const char *lnm, char *eqv, unsigned long int idx, struct dsc$descriptor_s **tabvec, unsigned long int flags) */
908 Perl_vmstrnenv(const char *lnm, char *eqv, unsigned long int idx,
909 struct dsc$descriptor_s **tabvec, unsigned long int flags)
912 char uplnm[LNM$C_NAMLENGTH+1], *cp2;
913 unsigned short int eqvlen, curtab, ivlnm = 0, ivsym = 0, ivenv = 0, secure;
914 unsigned long int retsts, attr = LNM$M_CASE_BLIND;
916 unsigned char acmode;
917 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
918 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
919 struct itmlst_3 lnmlst[3] = {{sizeof idx, LNM$_INDEX, &idx, 0},
920 {LNM$C_NAMLENGTH, LNM$_STRING, eqv, &eqvlen},
922 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
923 #if defined(PERL_IMPLICIT_CONTEXT)
926 aTHX = PERL_GET_INTERP;
932 if (!lnm || !eqv || ((idx != 0) && ((idx-1) > PERL_LNM_MAX_ALLOWED_INDEX))) {
933 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return 0;
935 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
936 *cp2 = _toupper(*cp1);
937 if (cp1 - lnm > LNM$C_NAMLENGTH) {
938 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
942 lnmdsc.dsc$w_length = cp1 - lnm;
943 lnmdsc.dsc$a_pointer = uplnm;
944 uplnm[lnmdsc.dsc$w_length] = '\0';
945 secure = flags & PERL__TRNENV_SECURE;
946 acmode = secure ? PSL$C_EXEC : PSL$C_USER;
947 if (!tabvec || !*tabvec) tabvec = env_tables;
949 for (curtab = 0; tabvec[curtab]; curtab++) {
950 if (!str$case_blind_compare(tabvec[curtab],&crtlenv)) {
951 if (!ivenv && !secure) {
956 Perl_warn(aTHX_ "Can't read CRTL environ\n");
959 retsts = SS$_NOLOGNAM;
960 for (i = 0; environ[i]; i++) {
961 if ((eq = strchr(environ[i],'=')) &&
962 lnmdsc.dsc$w_length == (eq - environ[i]) &&
963 !strncmp(environ[i],uplnm,eq - environ[i])) {
965 for (eqvlen = 0; eq[eqvlen]; eqvlen++) eqv[eqvlen] = eq[eqvlen];
966 if (!eqvlen) continue;
971 if (retsts != SS$_NOLOGNAM) break;
974 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
975 !str$case_blind_compare(&tmpdsc,&clisym)) {
976 if (!ivsym && !secure) {
977 unsigned short int deflen = LNM$C_NAMLENGTH;
978 struct dsc$descriptor_d eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
979 /* dynamic dsc to accomodate possible long value */
980 _ckvmssts(lib$sget1_dd(&deflen,&eqvdsc));
981 retsts = lib$get_symbol(&lnmdsc,&eqvdsc,&eqvlen,0);
983 if (eqvlen > MAX_DCL_SYMBOL) {
984 set_errno(EVMSERR); set_vaxc_errno(LIB$_STRTRU);
985 eqvlen = MAX_DCL_SYMBOL;
986 /* Special hack--we might be called before the interpreter's */
987 /* fully initialized, in which case either thr or PL_curcop */
988 /* might be bogus. We have to check, since ckWARN needs them */
989 /* both to be valid if running threaded */
990 if (ckWARN(WARN_MISC)) {
991 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of CLI symbol \"%s\" too long",lnm);
994 strncpy(eqv,eqvdsc.dsc$a_pointer,eqvlen);
996 _ckvmssts(lib$sfree1_dd(&eqvdsc));
997 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
998 if (retsts == LIB$_NOSUCHSYM) continue;
1003 if ( (idx == 0) && (flags & PERL__TRNENV_JOIN_SEARCHLIST) ) {
1004 midx = my_maxidx(lnm);
1005 for (idx = 0, cp2 = eqv; idx <= midx; idx++) {
1006 lnmlst[1].bufadr = cp2;
1008 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
1009 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; break; }
1010 if (retsts == SS$_NOLOGNAM) break;
1011 /* PPFs have a prefix */
1014 *((int *)uplnm) == *((int *)"SYS$") &&
1016 eqvlen >= 4 && eqv[0] == 0x1b && eqv[1] == 0x00 &&
1017 ( (uplnm[4] == 'O' && !strcmp(uplnm,"SYS$OUTPUT")) ||
1018 (uplnm[4] == 'I' && !strcmp(uplnm,"SYS$INPUT")) ||
1019 (uplnm[4] == 'E' && !strcmp(uplnm,"SYS$ERROR")) ||
1020 (uplnm[4] == 'C' && !strcmp(uplnm,"SYS$COMMAND")) ) ) {
1021 memmove(eqv,eqv+4,eqvlen-4);
1027 if ((retsts == SS$_IVLOGNAM) ||
1028 (retsts == SS$_NOLOGNAM)) { continue; }
1031 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
1032 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
1033 if (retsts == SS$_NOLOGNAM) continue;
1036 eqvlen = strlen(eqv);
1040 if (retsts & 1) { eqv[eqvlen] = '\0'; return eqvlen; }
1041 else if (retsts == LIB$_NOSUCHSYM || retsts == LIB$_INVSYMNAM ||
1042 retsts == SS$_IVLOGNAM || retsts == SS$_IVLOGTAB ||
1043 retsts == SS$_NOLOGNAM) {
1044 set_errno(EINVAL); set_vaxc_errno(retsts);
1046 else _ckvmssts(retsts);
1048 } /* end of vmstrnenv */
1051 /*{{{ int my_trnlnm(const char *lnm, char *eqv, unsigned long int idx)*/
1052 /* Define as a function so we can access statics. */
1053 int Perl_my_trnlnm(pTHX_ const char *lnm, char *eqv, unsigned long int idx)
1055 return vmstrnenv(lnm,eqv,idx,fildev,
1056 #ifdef SECURE_INTERNAL_GETENV
1057 (PL_curinterp ? PL_tainting : will_taint) ? PERL__TRNENV_SECURE : 0
1066 * Note: Uses Perl temp to store result so char * can be returned to
1067 * caller; this pointer will be invalidated at next Perl statement
1069 * We define this as a function rather than a macro in terms of my_getenv_len()
1070 * so that it'll work when PL_curinterp is undefined (and we therefore can't
1073 /*{{{ char *my_getenv(const char *lnm, bool sys)*/
1075 Perl_my_getenv(pTHX_ const char *lnm, bool sys)
1078 static char *__my_getenv_eqv = NULL;
1079 char uplnm[LNM$C_NAMLENGTH+1], *cp2, *eqv;
1080 unsigned long int idx = 0;
1081 int trnsuccess, success, secure, saverr, savvmserr;
1085 midx = my_maxidx(lnm) + 1;
1087 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
1088 /* Set up a temporary buffer for the return value; Perl will
1089 * clean it up at the next statement transition */
1090 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
1091 if (!tmpsv) return NULL;
1095 /* Assume no interpreter ==> single thread */
1096 if (__my_getenv_eqv != NULL) {
1097 Renew(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
1100 Newx(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
1102 eqv = __my_getenv_eqv;
1105 for (cp1 = lnm, cp2 = eqv; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
1106 if (cp1 - lnm == 7 && !strncmp(eqv,"DEFAULT",7)) {
1108 getcwd(eqv,LNM$C_NAMLENGTH);
1112 /* Get rid of "000000/ in rooted filespecs */
1115 zeros = strstr(eqv, "/000000/");
1116 if (zeros != NULL) {
1118 mlen = len - (zeros - eqv) - 7;
1119 memmove(zeros, &zeros[7], mlen);
1127 /* Impose security constraints only if tainting */
1129 /* Impose security constraints only if tainting */
1130 secure = PL_curinterp ? PL_tainting : will_taint;
1131 saverr = errno; savvmserr = vaxc$errno;
1138 #ifdef SECURE_INTERNAL_GETENV
1139 secure ? PERL__TRNENV_SECURE : 0
1145 /* For the getenv interface we combine all the equivalence names
1146 * of a search list logical into one value to acquire a maximum
1147 * value length of 255*128 (assuming %ENV is using logicals).
1149 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
1151 /* If the name contains a semicolon-delimited index, parse it
1152 * off and make sure we only retrieve the equivalence name for
1154 if ((cp2 = strchr(lnm,';')) != NULL) {
1156 uplnm[cp2-lnm] = '\0';
1157 idx = strtoul(cp2+1,NULL,0);
1159 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
1162 success = vmstrnenv(lnm,eqv,idx,secure ? fildev : NULL,flags);
1164 /* Discard NOLOGNAM on internal calls since we're often looking
1165 * for an optional name, and this "error" often shows up as the
1166 * (bogus) exit status for a die() call later on. */
1167 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
1168 return success ? eqv : Nullch;
1171 } /* end of my_getenv() */
1175 /*{{{ SV *my_getenv_len(const char *lnm, bool sys)*/
1177 Perl_my_getenv_len(pTHX_ const char *lnm, unsigned long *len, bool sys)
1181 unsigned long idx = 0;
1183 static char *__my_getenv_len_eqv = NULL;
1184 int secure, saverr, savvmserr;
1187 midx = my_maxidx(lnm) + 1;
1189 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
1190 /* Set up a temporary buffer for the return value; Perl will
1191 * clean it up at the next statement transition */
1192 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
1193 if (!tmpsv) return NULL;
1197 /* Assume no interpreter ==> single thread */
1198 if (__my_getenv_len_eqv != NULL) {
1199 Renew(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
1202 Newx(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
1204 buf = __my_getenv_len_eqv;
1207 for (cp1 = lnm, cp2 = buf; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
1208 if (cp1 - lnm == 7 && !strncmp(buf,"DEFAULT",7)) {
1211 getcwd(buf,LNM$C_NAMLENGTH);
1214 /* Get rid of "000000/ in rooted filespecs */
1216 zeros = strstr(buf, "/000000/");
1217 if (zeros != NULL) {
1219 mlen = *len - (zeros - buf) - 7;
1220 memmove(zeros, &zeros[7], mlen);
1229 /* Impose security constraints only if tainting */
1230 secure = PL_curinterp ? PL_tainting : will_taint;
1231 saverr = errno; savvmserr = vaxc$errno;
1238 #ifdef SECURE_INTERNAL_GETENV
1239 secure ? PERL__TRNENV_SECURE : 0
1245 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
1247 if ((cp2 = strchr(lnm,';')) != NULL) {
1249 buf[cp2-lnm] = '\0';
1250 idx = strtoul(cp2+1,NULL,0);
1252 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
1255 *len = vmstrnenv(lnm,buf,idx,secure ? fildev : NULL,flags);
1257 /* Get rid of "000000/ in rooted filespecs */
1260 zeros = strstr(buf, "/000000/");
1261 if (zeros != NULL) {
1263 mlen = *len - (zeros - buf) - 7;
1264 memmove(zeros, &zeros[7], mlen);
1270 /* Discard NOLOGNAM on internal calls since we're often looking
1271 * for an optional name, and this "error" often shows up as the
1272 * (bogus) exit status for a die() call later on. */
1273 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
1274 return *len ? buf : Nullch;
1277 } /* end of my_getenv_len() */
1280 static void create_mbx(pTHX_ unsigned short int *, struct dsc$descriptor_s *);
1282 static void riseandshine(unsigned long int dummy) { sys$wake(0,0); }
1284 /*{{{ void prime_env_iter() */
1286 prime_env_iter(void)
1287 /* Fill the %ENV associative array with all logical names we can
1288 * find, in preparation for iterating over it.
1291 static int primed = 0;
1292 HV *seenhv = NULL, *envhv;
1294 char cmd[LNM$C_NAMLENGTH+24], mbxnam[LNM$C_NAMLENGTH], *buf = Nullch;
1295 unsigned short int chan;
1296 #ifndef CLI$M_TRUSTED
1297 # define CLI$M_TRUSTED 0x40 /* Missing from VAXC headers */
1299 unsigned long int defflags = CLI$M_NOWAIT | CLI$M_NOKEYPAD | CLI$M_TRUSTED;
1300 unsigned long int mbxbufsiz, flags, retsts, subpid = 0, substs = 0, wakect = 0;
1302 bool have_sym = FALSE, have_lnm = FALSE;
1303 struct dsc$descriptor_s tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1304 $DESCRIPTOR(cmddsc,cmd); $DESCRIPTOR(nldsc,"_NLA0:");
1305 $DESCRIPTOR(clidsc,"DCL"); $DESCRIPTOR(clitabdsc,"DCLTABLES");
1306 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
1307 $DESCRIPTOR(local,"_LOCAL"); $DESCRIPTOR(mbxdsc,mbxnam);
1308 #if defined(PERL_IMPLICIT_CONTEXT)
1311 #if defined(USE_ITHREADS)
1312 static perl_mutex primenv_mutex;
1313 MUTEX_INIT(&primenv_mutex);
1316 #if defined(PERL_IMPLICIT_CONTEXT)
1317 /* We jump through these hoops because we can be called at */
1318 /* platform-specific initialization time, which is before anything is */
1319 /* set up--we can't even do a plain dTHX since that relies on the */
1320 /* interpreter structure to be initialized */
1322 aTHX = PERL_GET_INTERP;
1328 if (primed || !PL_envgv) return;
1329 MUTEX_LOCK(&primenv_mutex);
1330 if (primed) { MUTEX_UNLOCK(&primenv_mutex); return; }
1331 envhv = GvHVn(PL_envgv);
1332 /* Perform a dummy fetch as an lval to insure that the hash table is
1333 * set up. Otherwise, the hv_store() will turn into a nullop. */
1334 (void) hv_fetch(envhv,"DEFAULT",7,TRUE);
1336 for (i = 0; env_tables[i]; i++) {
1337 if (!have_sym && (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
1338 !str$case_blind_compare(&tmpdsc,&clisym)) have_sym = 1;
1339 if (!have_lnm && str$case_blind_compare(env_tables[i],&crtlenv)) have_lnm = 1;
1341 if (have_sym || have_lnm) {
1342 long int syiitm = SYI$_MAXBUF, dviitm = DVI$_DEVNAM;
1343 _ckvmssts(lib$getsyi(&syiitm, &mbxbufsiz, 0, 0, 0, 0));
1344 _ckvmssts(sys$crembx(0,&chan,mbxbufsiz,mbxbufsiz,0xff0f,0,0));
1345 _ckvmssts(lib$getdvi(&dviitm, &chan, NULL, NULL, &mbxdsc, &mbxdsc.dsc$w_length));
1348 for (i--; i >= 0; i--) {
1349 if (!str$case_blind_compare(env_tables[i],&crtlenv)) {
1352 for (j = 0; environ[j]; j++) {
1353 if (!(start = strchr(environ[j],'='))) {
1354 if (ckWARN(WARN_INTERNAL))
1355 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed CRTL environ value \"%s\"\n",environ[j]);
1359 sv = newSVpv(start,0);
1361 (void) hv_store(envhv,environ[j],start - environ[j] - 1,sv,0);
1366 else if ((tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
1367 !str$case_blind_compare(&tmpdsc,&clisym)) {
1368 strcpy(cmd,"Show Symbol/Global *");
1369 cmddsc.dsc$w_length = 20;
1370 if (env_tables[i]->dsc$w_length == 12 &&
1371 (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer + 6) &&
1372 !str$case_blind_compare(&tmpdsc,&local)) strcpy(cmd+12,"Local *");
1373 flags = defflags | CLI$M_NOLOGNAM;
1376 strcpy(cmd,"Show Logical *");
1377 if (str$case_blind_compare(env_tables[i],&fildevdsc)) {
1378 strcat(cmd," /Table=");
1379 strncat(cmd,env_tables[i]->dsc$a_pointer,env_tables[i]->dsc$w_length);
1380 cmddsc.dsc$w_length = strlen(cmd);
1382 else cmddsc.dsc$w_length = 14; /* N.B. We test this below */
1383 flags = defflags | CLI$M_NOCLISYM;
1386 /* Create a new subprocess to execute each command, to exclude the
1387 * remote possibility that someone could subvert a mbx or file used
1388 * to write multiple commands to a single subprocess.
1391 retsts = lib$spawn(&cmddsc,&nldsc,&mbxdsc,&flags,0,&subpid,&substs,
1392 0,&riseandshine,0,0,&clidsc,&clitabdsc);
1393 flags &= ~CLI$M_TRUSTED; /* Just in case we hit a really old version */
1394 defflags &= ~CLI$M_TRUSTED;
1395 } while (retsts == LIB$_INVARG && (flags | CLI$M_TRUSTED));
1397 if (!buf) Newx(buf,mbxbufsiz + 1,char);
1398 if (seenhv) SvREFCNT_dec(seenhv);
1401 char *cp1, *cp2, *key;
1402 unsigned long int sts, iosb[2], retlen, keylen;
1405 sts = sys$qiow(0,chan,IO$_READVBLK,iosb,0,0,buf,mbxbufsiz,0,0,0,0);
1406 if (sts & 1) sts = iosb[0] & 0xffff;
1407 if (sts == SS$_ENDOFFILE) {
1409 while (substs == 0) { sys$hiber(); wakect++;}
1410 if (wakect > 1) sys$wake(0,0); /* Stole someone else's wake */
1415 retlen = iosb[0] >> 16;
1416 if (!retlen) continue; /* blank line */
1418 if (iosb[1] != subpid) {
1420 Perl_croak(aTHX_ "Unknown process %x sent message to prime_env_iter: %s",buf);
1424 if (sts == SS$_BUFFEROVF && ckWARN(WARN_INTERNAL))
1425 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Buffer overflow in prime_env_iter: %s",buf);
1427 for (cp1 = buf; *cp1 && isspace(*cp1); cp1++) ;
1428 if (*cp1 == '(' || /* Logical name table name */
1429 *cp1 == '=' /* Next eqv of searchlist */) continue;
1430 if (*cp1 == '"') cp1++;
1431 for (cp2 = cp1; *cp2 && *cp2 != '"' && *cp2 != ' '; cp2++) ;
1432 key = cp1; keylen = cp2 - cp1;
1433 if (keylen && hv_exists(seenhv,key,keylen)) continue;
1434 while (*cp2 && *cp2 != '=') cp2++;
1435 while (*cp2 && *cp2 == '=') cp2++;
1436 while (*cp2 && *cp2 == ' ') cp2++;
1437 if (*cp2 == '"') { /* String translation; may embed "" */
1438 for (cp1 = buf + retlen; *cp1 != '"'; cp1--) ;
1439 cp2++; cp1--; /* Skip "" surrounding translation */
1441 else { /* Numeric translation */
1442 for (cp1 = cp2; *cp1 && *cp1 != ' '; cp1++) ;
1443 cp1--; /* stop on last non-space char */
1445 if ((!keylen || (cp1 - cp2 < -1)) && ckWARN(WARN_INTERNAL)) {
1446 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed message in prime_env_iter: |%s|",buf);
1449 PERL_HASH(hash,key,keylen);
1451 if (cp1 == cp2 && *cp2 == '.') {
1452 /* A single dot usually means an unprintable character, such as a null
1453 * to indicate a zero-length value. Get the actual value to make sure.
1455 char lnm[LNM$C_NAMLENGTH+1];
1456 char eqv[MAX_DCL_SYMBOL+1];
1458 strncpy(lnm, key, keylen);
1459 trnlen = vmstrnenv(lnm, eqv, 0, fildev, 0);
1460 sv = newSVpvn(eqv, strlen(eqv));
1463 sv = newSVpvn(cp2,cp1 - cp2 + 1);
1467 hv_store(envhv,key,keylen,sv,hash);
1468 hv_store(seenhv,key,keylen,&PL_sv_yes,hash);
1470 if (cmddsc.dsc$w_length == 14) { /* We just read LNM$FILE_DEV */
1471 /* get the PPFs for this process, not the subprocess */
1472 const char *ppfs[] = {"SYS$COMMAND", "SYS$INPUT", "SYS$OUTPUT", "SYS$ERROR", NULL};
1473 char eqv[LNM$C_NAMLENGTH+1];
1475 for (i = 0; ppfs[i]; i++) {
1476 trnlen = vmstrnenv(ppfs[i],eqv,0,fildev,0);
1477 sv = newSVpv(eqv,trnlen);
1479 hv_store(envhv,ppfs[i],strlen(ppfs[i]),sv,0);
1484 if (have_sym || have_lnm) _ckvmssts(sys$dassgn(chan));
1485 if (buf) Safefree(buf);
1486 if (seenhv) SvREFCNT_dec(seenhv);
1487 MUTEX_UNLOCK(&primenv_mutex);
1490 } /* end of prime_env_iter */
1494 /*{{{ int vmssetenv(const char *lnm, const char *eqv)*/
1495 /* Define or delete an element in the same "environment" as
1496 * vmstrnenv(). If an element is to be deleted, it's removed from
1497 * the first place it's found. If it's to be set, it's set in the
1498 * place designated by the first element of the table vector.
1499 * Like setenv() returns 0 for success, non-zero on error.
1502 Perl_vmssetenv(pTHX_ const char *lnm, const char *eqv, struct dsc$descriptor_s **tabvec)
1505 char uplnm[LNM$C_NAMLENGTH], *cp2, *c;
1506 unsigned short int curtab, ivlnm = 0, ivsym = 0, ivenv = 0;
1508 unsigned long int retsts, usermode = PSL$C_USER;
1509 struct itmlst_3 *ile, *ilist;
1510 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,uplnm},
1511 eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
1512 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1513 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
1514 $DESCRIPTOR(local,"_LOCAL");
1517 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1518 return SS$_IVLOGNAM;
1521 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
1522 *cp2 = _toupper(*cp1);
1523 if (cp1 - lnm > LNM$C_NAMLENGTH) {
1524 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1525 return SS$_IVLOGNAM;
1528 lnmdsc.dsc$w_length = cp1 - lnm;
1529 if (!tabvec || !*tabvec) tabvec = env_tables;
1531 if (!eqv) { /* we're deleting n element */
1532 for (curtab = 0; tabvec[curtab]; curtab++) {
1533 if (!ivenv && !str$case_blind_compare(tabvec[curtab],&crtlenv)) {
1535 for (i = 0; environ[i]; i++) { /* If it's an environ elt, reset */
1536 if ((cp1 = strchr(environ[i],'=')) &&
1537 lnmdsc.dsc$w_length == (cp1 - environ[i]) &&
1538 !strncmp(environ[i],lnm,cp1 - environ[i])) {
1540 return setenv(lnm,"",1) ? vaxc$errno : 0;
1543 ivenv = 1; retsts = SS$_NOLOGNAM;
1545 if (ckWARN(WARN_INTERNAL))
1546 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't reset CRTL environ elements (%s)",lnm);
1547 ivenv = 1; retsts = SS$_NOSUCHPGM;
1553 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
1554 !str$case_blind_compare(&tmpdsc,&clisym)) {
1555 unsigned int symtype;
1556 if (tabvec[curtab]->dsc$w_length == 12 &&
1557 (tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer + 6) &&
1558 !str$case_blind_compare(&tmpdsc,&local))
1559 symtype = LIB$K_CLI_LOCAL_SYM;
1560 else symtype = LIB$K_CLI_GLOBAL_SYM;
1561 retsts = lib$delete_symbol(&lnmdsc,&symtype);
1562 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
1563 if (retsts == LIB$_NOSUCHSYM) continue;
1567 retsts = sys$dellnm(tabvec[curtab],&lnmdsc,&usermode); /* try user mode first */
1568 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
1569 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
1570 retsts = lib$delete_logical(&lnmdsc,tabvec[curtab]); /* then supervisor mode */
1571 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
1575 else { /* we're defining a value */
1576 if (!ivenv && !str$case_blind_compare(tabvec[0],&crtlenv)) {
1578 return setenv(lnm,eqv,1) ? vaxc$errno : 0;
1580 if (ckWARN(WARN_INTERNAL))
1581 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't set CRTL environ elements (%s=%s)",lnm,eqv);
1582 retsts = SS$_NOSUCHPGM;
1586 eqvdsc.dsc$a_pointer = (char *) eqv; /* cast ok to readonly parameter */
1587 eqvdsc.dsc$w_length = strlen(eqv);
1588 if ((tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer) &&
1589 !str$case_blind_compare(&tmpdsc,&clisym)) {
1590 unsigned int symtype;
1591 if (tabvec[0]->dsc$w_length == 12 &&
1592 (tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer + 6) &&
1593 !str$case_blind_compare(&tmpdsc,&local))
1594 symtype = LIB$K_CLI_LOCAL_SYM;
1595 else symtype = LIB$K_CLI_GLOBAL_SYM;
1596 retsts = lib$set_symbol(&lnmdsc,&eqvdsc,&symtype);
1599 if (!*eqv) eqvdsc.dsc$w_length = 1;
1600 if (eqvdsc.dsc$w_length > LNM$C_NAMLENGTH) {
1602 nseg = (eqvdsc.dsc$w_length + LNM$C_NAMLENGTH - 1) / LNM$C_NAMLENGTH;
1603 if (nseg > PERL_LNM_MAX_ALLOWED_INDEX + 1) {
1604 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of logical \"%s\" too long. Truncating to %i bytes",
1605 lnm, LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1));
1606 eqvdsc.dsc$w_length = LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1);
1607 nseg = PERL_LNM_MAX_ALLOWED_INDEX + 1;
1610 Newx(ilist,nseg+1,struct itmlst_3);
1613 set_errno(ENOMEM); set_vaxc_errno(SS$_INSFMEM);
1616 memset(ilist, 0, (sizeof(struct itmlst_3) * (nseg+1)));
1618 for (j = 0, c = eqvdsc.dsc$a_pointer; j < nseg; j++, ile++, c += LNM$C_NAMLENGTH) {
1619 ile->itmcode = LNM$_STRING;
1621 if ((j+1) == nseg) {
1622 ile->buflen = strlen(c);
1623 /* in case we are truncating one that's too long */
1624 if (ile->buflen > LNM$C_NAMLENGTH) ile->buflen = LNM$C_NAMLENGTH;
1627 ile->buflen = LNM$C_NAMLENGTH;
1631 retsts = lib$set_logical(&lnmdsc,0,tabvec[0],0,ilist);
1635 retsts = lib$set_logical(&lnmdsc,&eqvdsc,tabvec[0],0,0);
1640 if (!(retsts & 1)) {
1642 case LIB$_AMBSYMDEF: case LIB$_INSCLIMEM:
1643 case SS$_NOLOGTAB: case SS$_TOOMANYLNAM: case SS$_IVLOGTAB:
1644 set_errno(EVMSERR); break;
1645 case LIB$_INVARG: case LIB$_INVSYMNAM: case SS$_IVLOGNAM:
1646 case LIB$_NOSUCHSYM: case SS$_NOLOGNAM:
1647 set_errno(EINVAL); break;
1649 set_errno(EACCES); break;
1654 set_vaxc_errno(retsts);
1655 return (int) retsts || 44; /* retsts should never be 0, but just in case */
1658 /* We reset error values on success because Perl does an hv_fetch()
1659 * before each hv_store(), and if the thing we're setting didn't
1660 * previously exist, we've got a leftover error message. (Of course,
1661 * this fails in the face of
1662 * $foo = $ENV{nonexistent}; $ENV{existent} = 'foo';
1663 * in that the error reported in $! isn't spurious,
1664 * but it's right more often than not.)
1666 set_errno(0); set_vaxc_errno(retsts);
1670 } /* end of vmssetenv() */
1673 /*{{{ void my_setenv(const char *lnm, const char *eqv)*/
1674 /* This has to be a function since there's a prototype for it in proto.h */
1676 Perl_my_setenv(pTHX_ const char *lnm, const char *eqv)
1679 int len = strlen(lnm);
1683 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
1684 if (!strcmp(uplnm,"DEFAULT")) {
1685 if (eqv && *eqv) my_chdir(eqv);
1689 #ifndef RTL_USES_UTC
1690 if (len == 6 || len == 2) {
1693 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
1695 if (!strcmp(uplnm,"UCX$TZ")) tz_updated = 1;
1696 if (!strcmp(uplnm,"TZ")) tz_updated = 1;
1700 (void) vmssetenv(lnm,eqv,NULL);
1704 /*{{{static void vmssetuserlnm(char *name, char *eqv); */
1706 * sets a user-mode logical in the process logical name table
1707 * used for redirection of sys$error
1710 Perl_vmssetuserlnm(pTHX_ const char *name, const char *eqv)
1712 $DESCRIPTOR(d_tab, "LNM$PROCESS");
1713 struct dsc$descriptor_d d_name = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
1714 unsigned long int iss, attr = LNM$M_CONFINE;
1715 unsigned char acmode = PSL$C_USER;
1716 struct itmlst_3 lnmlst[2] = {{0, LNM$_STRING, 0, 0},
1718 d_name.dsc$a_pointer = (char *)name; /* Cast OK for read only parameter */
1719 d_name.dsc$w_length = strlen(name);
1721 lnmlst[0].buflen = strlen(eqv);
1722 lnmlst[0].bufadr = (char *)eqv; /* Cast OK for read only parameter */
1724 iss = sys$crelnm(&attr,&d_tab,&d_name,&acmode,lnmlst);
1725 if (!(iss&1)) lib$signal(iss);
1730 /*{{{ char *my_crypt(const char *textpasswd, const char *usrname)*/
1731 /* my_crypt - VMS password hashing
1732 * my_crypt() provides an interface compatible with the Unix crypt()
1733 * C library function, and uses sys$hash_password() to perform VMS
1734 * password hashing. The quadword hashed password value is returned
1735 * as a NUL-terminated 8 character string. my_crypt() does not change
1736 * the case of its string arguments; in order to match the behavior
1737 * of LOGINOUT et al., alphabetic characters in both arguments must
1738 * be upcased by the caller.
1740 * - fix me to call ACM services when available
1743 Perl_my_crypt(pTHX_ const char *textpasswd, const char *usrname)
1745 # ifndef UAI$C_PREFERRED_ALGORITHM
1746 # define UAI$C_PREFERRED_ALGORITHM 127
1748 unsigned char alg = UAI$C_PREFERRED_ALGORITHM;
1749 unsigned short int salt = 0;
1750 unsigned long int sts;
1752 unsigned short int dsc$w_length;
1753 unsigned char dsc$b_type;
1754 unsigned char dsc$b_class;
1755 const char * dsc$a_pointer;
1756 } usrdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
1757 txtdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1758 struct itmlst_3 uailst[3] = {
1759 { sizeof alg, UAI$_ENCRYPT, &alg, 0},
1760 { sizeof salt, UAI$_SALT, &salt, 0},
1761 { 0, 0, NULL, NULL}};
1762 static char hash[9];
1764 usrdsc.dsc$w_length = strlen(usrname);
1765 usrdsc.dsc$a_pointer = usrname;
1766 if (!((sts = sys$getuai(0, 0, &usrdsc, uailst, 0, 0, 0)) & 1)) {
1768 case SS$_NOGRPPRV: case SS$_NOSYSPRV:
1772 set_errno(ESRCH); /* There isn't a Unix no-such-user error */
1777 set_vaxc_errno(sts);
1778 if (sts != RMS$_RNF) return NULL;
1781 txtdsc.dsc$w_length = strlen(textpasswd);
1782 txtdsc.dsc$a_pointer = textpasswd;
1783 if (!((sts = sys$hash_password(&txtdsc, alg, salt, &usrdsc, &hash)) & 1)) {
1784 set_errno(EVMSERR); set_vaxc_errno(sts); return NULL;
1787 return (char *) hash;
1789 } /* end of my_crypt() */
1793 static char *mp_do_rmsexpand(pTHX_ const char *, char *, int, const char *, unsigned, int *, int *);
1794 static char *mp_do_fileify_dirspec(pTHX_ const char *, char *, int, int *);
1795 static char *mp_do_tovmsspec(pTHX_ const char *, char *, int, int, int *);
1797 /* fixup barenames that are directories for internal use.
1798 * There have been problems with the consistent handling of UNIX
1799 * style directory names when routines are presented with a name that
1800 * has no directory delimitors at all. So this routine will eventually
1803 static char * fixup_bare_dirnames(const char * name)
1805 if (decc_disable_to_vms_logname_translation) {
1811 /* 8.3, remove() is now broken on symbolic links */
1812 static int rms_erase(const char * vmsname);
1816 * A little hack to get around a bug in some implemenation of remove()
1817 * that do not know how to delete a directory
1819 * Delete any file to which user has control access, regardless of whether
1820 * delete access is explicitly allowed.
1821 * Limitations: User must have write access to parent directory.
1822 * Does not block signals or ASTs; if interrupted in midstream
1823 * may leave file with an altered ACL.
1826 /*{{{int mp_do_kill_file(const char *name, int dirflag)*/
1828 mp_do_kill_file(pTHX_ const char *name, int dirflag)
1832 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
1833 unsigned long int cxt = 0, aclsts, fndsts, rmsts = -1;
1834 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1836 unsigned char myace$b_length;
1837 unsigned char myace$b_type;
1838 unsigned short int myace$w_flags;
1839 unsigned long int myace$l_access;
1840 unsigned long int myace$l_ident;
1841 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
1842 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0},
1843 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
1845 findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0},
1846 {sizeof oldace, ACL$C_READACE, &oldace, 0},{0,0,0,0}},
1847 addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}},
1848 dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}},
1849 lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}},
1850 ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}};
1852 /* Expand the input spec using RMS, since the CRTL remove() and
1853 * system services won't do this by themselves, so we may miss
1854 * a file "hiding" behind a logical name or search list. */
1855 vmsname = PerlMem_malloc(NAM$C_MAXRSS+1);
1856 if (vmsname == NULL) _ckvmssts(SS$_INSFMEM);
1858 rslt = do_rmsexpand(name,
1862 PERL_RMSEXPAND_M_VMS | PERL_RMSEXPAND_M_SYMLINK,
1866 PerlMem_free(vmsname);
1870 /* Erase the file */
1871 rmsts = rms_erase(vmsname);
1873 /* Did it succeed */
1874 if ($VMS_STATUS_SUCCESS(rmsts)) {
1875 PerlMem_free(vmsname);
1879 /* If not, can changing protections help? */
1880 if (rmsts != RMS$_PRV) {
1881 set_vaxc_errno(rmsts);
1882 PerlMem_free(vmsname);
1886 /* No, so we get our own UIC to use as a rights identifier,
1887 * and the insert an ACE at the head of the ACL which allows us
1888 * to delete the file.
1890 _ckvmssts(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
1891 fildsc.dsc$w_length = strlen(vmsname);
1892 fildsc.dsc$a_pointer = vmsname;
1894 newace.myace$l_ident = oldace.myace$l_ident;
1896 if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) {
1898 case RMS$_FNF: case RMS$_DNF: case SS$_NOSUCHOBJECT:
1899 set_errno(ENOENT); break;
1901 set_errno(ENOTDIR); break;
1903 set_errno(ENODEV); break;
1904 case RMS$_SYN: case SS$_INVFILFOROP:
1905 set_errno(EINVAL); break;
1907 set_errno(EACCES); break;
1911 set_vaxc_errno(aclsts);
1912 PerlMem_free(vmsname);
1915 /* Grab any existing ACEs with this identifier in case we fail */
1916 aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt);
1917 if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY
1918 || fndsts == SS$_NOMOREACE ) {
1919 /* Add the new ACE . . . */
1920 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1))
1923 rmsts = rms_erase(vmsname);
1924 if ($VMS_STATUS_SUCCESS(rmsts)) {
1929 /* We blew it - dir with files in it, no write priv for
1930 * parent directory, etc. Put things back the way they were. */
1931 if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1))
1934 addlst[0].bufadr = &oldace;
1935 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1))
1942 fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0);
1943 /* We just deleted it, so of course it's not there. Some versions of
1944 * VMS seem to return success on the unlock operation anyhow (after all
1945 * the unlock is successful), but others don't.
1947 if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL;
1948 if (aclsts & 1) aclsts = fndsts;
1949 if (!(aclsts & 1)) {
1951 set_vaxc_errno(aclsts);
1954 PerlMem_free(vmsname);
1957 } /* end of kill_file() */
1961 /*{{{int do_rmdir(char *name)*/
1963 Perl_do_rmdir(pTHX_ const char *name)
1969 dirfile = PerlMem_malloc(VMS_MAXRSS + 1);
1970 if (dirfile == NULL)
1971 _ckvmssts(SS$_INSFMEM);
1973 /* Force to a directory specification */
1974 if (do_fileify_dirspec(name, dirfile, 0, NULL) == NULL) {
1975 PerlMem_free(dirfile);
1978 if (Perl_flex_lstat(aTHX_ dirfile, &st) || !S_ISDIR(st.st_mode)) {
1983 retval = mp_do_kill_file(aTHX_ dirfile, 1);
1985 PerlMem_free(dirfile);
1988 } /* end of do_rmdir */
1992 * Delete any file to which user has control access, regardless of whether
1993 * delete access is explicitly allowed.
1994 * Limitations: User must have write access to parent directory.
1995 * Does not block signals or ASTs; if interrupted in midstream
1996 * may leave file with an altered ACL.
1999 /*{{{int kill_file(char *name)*/
2001 Perl_kill_file(pTHX_ const char *name)
2003 char rspec[NAM$C_MAXRSS+1];
2008 /* Remove() is allowed to delete directories, according to the X/Open
2010 * This may need special handling to work with the ACL hacks.
2012 if ((flex_lstat(name, &st) == 0) && S_ISDIR(st.st_mode)) {
2013 rmsts = Perl_do_rmdir(aTHX_ name);
2017 rmsts = mp_do_kill_file(aTHX_ name, 0);
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 /*{{{int my_chmod(char *, mode_t)*/
2088 Perl_my_chmod(pTHX_ const char *file_spec, mode_t mode)
2090 STRLEN speclen = strlen(file_spec);
2092 /* zero length string sometimes gives ACCVIO */
2093 if (speclen == 0) return -1;
2095 /* some versions of CRTL chmod() doesn't tolerate trailing /, since
2096 * that implies null file name/type. However, it's commonplace under Unix,
2097 * so we'll allow it for a gain in portability.
2099 * Tests are showing that chmod() on VMS 8.3 is only accepting directories
2100 * in VMS file.dir notation.
2102 if ((speclen > 1) && (file_spec[speclen-1] == '/')) {
2103 char *vms_src, *vms_dir, *rslt;
2107 /* First convert this to a VMS format specification */
2108 vms_src = PerlMem_malloc(VMS_MAXRSS);
2109 if (vms_src == NULL)
2110 _ckvmssts(SS$_INSFMEM);
2112 rslt = do_tovmsspec(file_spec, vms_src, 0, NULL);
2114 /* If we fail, then not a file specification */
2115 PerlMem_free(vms_src);
2120 /* Now make it a directory spec so chmod is happy */
2121 vms_dir = PerlMem_malloc(VMS_MAXRSS + 1);
2122 if (vms_dir == NULL)
2123 _ckvmssts(SS$_INSFMEM);
2124 rslt = do_fileify_dirspec(vms_src, vms_dir, 0, NULL);
2125 PerlMem_free(vms_src);
2129 ret = chmod(vms_dir, mode);
2133 PerlMem_free(vms_dir);
2136 else return chmod(file_spec, mode);
2137 } /* end of my_chmod */
2141 /*{{{FILE *my_tmpfile()*/
2148 if ((fp = tmpfile())) return fp;
2150 cp = PerlMem_malloc(L_tmpnam+24);
2151 if (cp == NULL) _ckvmssts_noperl(SS$_INSFMEM);
2153 if (decc_filename_unix_only == 0)
2154 strcpy(cp,"Sys$Scratch:");
2157 tmpnam(cp+strlen(cp));
2158 strcat(cp,".Perltmp");
2159 fp = fopen(cp,"w+","fop=dlt");
2166 #ifndef HOMEGROWN_POSIX_SIGNALS
2168 * The C RTL's sigaction fails to check for invalid signal numbers so we
2169 * help it out a bit. The docs are correct, but the actual routine doesn't
2170 * do what the docs say it will.
2172 /*{{{int Perl_my_sigaction (pTHX_ int, const struct sigaction*, struct sigaction*);*/
2174 Perl_my_sigaction (pTHX_ int sig, const struct sigaction* act,
2175 struct sigaction* oact)
2177 if (sig == SIGKILL || sig == SIGSTOP || sig == SIGCONT) {
2178 SETERRNO(EINVAL, SS$_INVARG);
2181 return sigaction(sig, act, oact);
2186 #ifdef KILL_BY_SIGPRC
2187 #include <errnodef.h>
2189 /* We implement our own kill() using the undocumented system service
2190 sys$sigprc for one of two reasons:
2192 1.) If the kill() in an older CRTL uses sys$forcex, causing the
2193 target process to do a sys$exit, which usually can't be handled
2194 gracefully...certainly not by Perl and the %SIG{} mechanism.
2196 2.) If the kill() in the CRTL can't be called from a signal
2197 handler without disappearing into the ether, i.e., the signal
2198 it purportedly sends is never trapped. Still true as of VMS 7.3.
2200 sys$sigprc has the same parameters as sys$forcex, but throws an exception
2201 in the target process rather than calling sys$exit.
2203 Note that distinguishing SIGSEGV from SIGBUS requires an extra arg
2204 on the ACCVIO condition, which sys$sigprc (and sys$forcex) don't
2205 provide. On VMS 7.0+ this is taken care of by doing sys$sigprc
2206 with condition codes C$_SIG0+nsig*8, catching the exception on the
2207 target process and resignaling with appropriate arguments.
2209 But we don't have that VMS 7.0+ exception handler, so if you
2210 Perl_my_kill(.., SIGSEGV) it will show up as a SIGBUS. Oh well.
2212 Also note that SIGTERM is listed in the docs as being "unimplemented",
2213 yet always seems to be signaled with a VMS condition code of 4 (and
2214 correctly handled for that code). So we hardwire it in.
2216 Unlike the VMS 7.0+ CRTL kill() function, we actually check the signal
2217 number to see if it's valid. So Perl_my_kill(pid,0) returns -1 rather
2218 than signalling with an unrecognized (and unhandled by CRTL) code.
2221 #define _MY_SIG_MAX 28
2224 Perl_sig_to_vmscondition_int(int sig)
2226 static unsigned int sig_code[_MY_SIG_MAX+1] =
2229 SS$_HANGUP, /* 1 SIGHUP */
2230 SS$_CONTROLC, /* 2 SIGINT */
2231 SS$_CONTROLY, /* 3 SIGQUIT */
2232 SS$_RADRMOD, /* 4 SIGILL */
2233 SS$_BREAK, /* 5 SIGTRAP */
2234 SS$_OPCCUS, /* 6 SIGABRT */
2235 SS$_COMPAT, /* 7 SIGEMT */
2237 SS$_FLTOVF, /* 8 SIGFPE VAX */
2239 SS$_HPARITH, /* 8 SIGFPE AXP */
2241 SS$_ABORT, /* 9 SIGKILL */
2242 SS$_ACCVIO, /* 10 SIGBUS */
2243 SS$_ACCVIO, /* 11 SIGSEGV */
2244 SS$_BADPARAM, /* 12 SIGSYS */
2245 SS$_NOMBX, /* 13 SIGPIPE */
2246 SS$_ASTFLT, /* 14 SIGALRM */
2263 #if __VMS_VER >= 60200000
2264 static int initted = 0;
2267 sig_code[16] = C$_SIGUSR1;
2268 sig_code[17] = C$_SIGUSR2;
2269 #if __CRTL_VER >= 70000000
2270 sig_code[20] = C$_SIGCHLD;
2272 #if __CRTL_VER >= 70300000
2273 sig_code[28] = C$_SIGWINCH;
2278 if (sig < _SIG_MIN) return 0;
2279 if (sig > _MY_SIG_MAX) return 0;
2280 return sig_code[sig];
2284 Perl_sig_to_vmscondition(int sig)
2287 if (vms_debug_on_exception != 0)
2288 lib$signal(SS$_DEBUG);
2290 return Perl_sig_to_vmscondition_int(sig);
2295 Perl_my_kill(int pid, int sig)
2300 int sys$sigprc(unsigned int *pidadr,
2301 struct dsc$descriptor_s *prcname,
2304 /* sig 0 means validate the PID */
2305 /*------------------------------*/
2307 const unsigned long int jpicode = JPI$_PID;
2310 status = lib$getjpi(&jpicode, &pid, NULL, &ret_pid, NULL, NULL);
2311 if ($VMS_STATUS_SUCCESS(status))
2314 case SS$_NOSUCHNODE:
2315 case SS$_UNREACHABLE:
2329 code = Perl_sig_to_vmscondition_int(sig);
2332 SETERRNO(EINVAL, SS$_BADPARAM);
2336 /* Fixme: Per official UNIX specification: If pid = 0, or negative then
2337 * signals are to be sent to multiple processes.
2338 * pid = 0 - all processes in group except ones that the system exempts
2339 * pid = -1 - all processes except ones that the system exempts
2340 * pid = -n - all processes in group (abs(n)) except ...
2341 * For now, just report as not supported.
2345 SETERRNO(ENOTSUP, SS$_UNSUPPORTED);
2349 iss = sys$sigprc((unsigned int *)&pid,0,code);
2350 if (iss&1) return 0;
2354 set_errno(EPERM); break;
2356 case SS$_NOSUCHNODE:
2357 case SS$_UNREACHABLE:
2358 set_errno(ESRCH); break;
2360 set_errno(ENOMEM); break;
2365 set_vaxc_errno(iss);
2371 /* Routine to convert a VMS status code to a UNIX status code.
2372 ** More tricky than it appears because of conflicting conventions with
2375 ** VMS status codes are a bit mask, with the least significant bit set for
2378 ** Special UNIX status of EVMSERR indicates that no translation is currently
2379 ** available, and programs should check the VMS status code.
2381 ** Programs compiled with _POSIX_EXIT have a special encoding that requires
2385 #ifndef C_FACILITY_NO
2386 #define C_FACILITY_NO 0x350000
2389 #define DCL_IVVERB 0x38090
2392 int Perl_vms_status_to_unix(int vms_status, int child_flag)
2400 /* Assume the best or the worst */
2401 if (vms_status & STS$M_SUCCESS)
2404 unix_status = EVMSERR;
2406 msg_status = vms_status & ~STS$M_CONTROL;
2408 facility = vms_status & STS$M_FAC_NO;
2409 fac_sp = vms_status & STS$M_FAC_SP;
2410 msg_no = vms_status & (STS$M_MSG_NO | STS$M_SEVERITY);
2412 if (((facility == 0) || (fac_sp == 0)) && (child_flag == 0)) {
2418 unix_status = EFAULT;
2420 case SS$_DEVOFFLINE:
2421 unix_status = EBUSY;
2424 unix_status = ENOTCONN;
2432 case SS$_INVFILFOROP:
2436 unix_status = EINVAL;
2438 case SS$_UNSUPPORTED:
2439 unix_status = ENOTSUP;
2444 unix_status = EACCES;
2446 case SS$_DEVICEFULL:
2447 unix_status = ENOSPC;
2450 unix_status = ENODEV;
2452 case SS$_NOSUCHFILE:
2453 case SS$_NOSUCHOBJECT:
2454 unix_status = ENOENT;
2456 case SS$_ABORT: /* Fatal case */
2457 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_ERROR): /* Error case */
2458 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_WARNING): /* Warning case */
2459 unix_status = EINTR;
2462 unix_status = E2BIG;
2465 unix_status = ENOMEM;
2468 unix_status = EPERM;
2470 case SS$_NOSUCHNODE:
2471 case SS$_UNREACHABLE:
2472 unix_status = ESRCH;
2475 unix_status = ECHILD;
2478 if ((facility == 0) && (msg_no < 8)) {
2479 /* These are not real VMS status codes so assume that they are
2480 ** already UNIX status codes
2482 unix_status = msg_no;
2488 /* Translate a POSIX exit code to a UNIX exit code */
2489 if ((facility == C_FACILITY_NO) && ((msg_no & 0xA000) == 0xA000)) {
2490 unix_status = (msg_no & 0x07F8) >> 3;
2494 /* Documented traditional behavior for handling VMS child exits */
2495 /*--------------------------------------------------------------*/
2496 if (child_flag != 0) {
2498 /* Success / Informational return 0 */
2499 /*----------------------------------*/
2500 if (msg_no & STS$K_SUCCESS)
2503 /* Warning returns 1 */
2504 /*-------------------*/
2505 if ((msg_no & (STS$K_ERROR | STS$K_SEVERE)) == 0)
2508 /* Everything else pass through the severity bits */
2509 /*------------------------------------------------*/
2510 return (msg_no & STS$M_SEVERITY);
2513 /* Normal VMS status to ERRNO mapping attempt */
2514 /*--------------------------------------------*/
2515 switch(msg_status) {
2516 /* case RMS$_EOF: */ /* End of File */
2517 case RMS$_FNF: /* File Not Found */
2518 case RMS$_DNF: /* Dir Not Found */
2519 unix_status = ENOENT;
2521 case RMS$_RNF: /* Record Not Found */
2522 unix_status = ESRCH;
2525 unix_status = ENOTDIR;
2528 unix_status = ENODEV;
2533 unix_status = EBADF;
2536 unix_status = EEXIST;
2540 case LIB$_INVSTRDES:
2542 case LIB$_NOSUCHSYM:
2543 case LIB$_INVSYMNAM:
2545 unix_status = EINVAL;
2551 unix_status = E2BIG;
2553 case RMS$_PRV: /* No privilege */
2554 case RMS$_ACC: /* ACP file access failed */
2555 case RMS$_WLK: /* Device write locked */
2556 unix_status = EACCES;
2558 /* case RMS$_NMF: */ /* No more files */
2566 /* Try to guess at what VMS error status should go with a UNIX errno
2567 * value. This is hard to do as there could be many possible VMS
2568 * error statuses that caused the errno value to be set.
2571 int Perl_unix_status_to_vms(int unix_status)
2573 int test_unix_status;
2575 /* Trivial cases first */
2576 /*---------------------*/
2577 if (unix_status == EVMSERR)
2580 /* Is vaxc$errno sane? */
2581 /*---------------------*/
2582 test_unix_status = Perl_vms_status_to_unix(vaxc$errno, 0);
2583 if (test_unix_status == unix_status)
2586 /* If way out of range, must be VMS code already */
2587 /*-----------------------------------------------*/
2588 if (unix_status > EVMSERR)
2591 /* If out of range, punt */
2592 /*-----------------------*/
2593 if (unix_status > __ERRNO_MAX)
2597 /* Ok, now we have to do it the hard way. */
2598 /*----------------------------------------*/
2599 switch(unix_status) {
2600 case 0: return SS$_NORMAL;
2601 case EPERM: return SS$_NOPRIV;
2602 case ENOENT: return SS$_NOSUCHOBJECT;
2603 case ESRCH: return SS$_UNREACHABLE;
2604 case EINTR: return SS$_ABORT;
2607 case E2BIG: return SS$_BUFFEROVF;
2609 case EBADF: return RMS$_IFI;
2610 case ECHILD: return SS$_NONEXPR;
2612 case ENOMEM: return SS$_INSFMEM;
2613 case EACCES: return SS$_FILACCERR;
2614 case EFAULT: return SS$_ACCVIO;
2616 case EBUSY: return SS$_DEVOFFLINE;
2617 case EEXIST: return RMS$_FEX;
2619 case ENODEV: return SS$_NOSUCHDEV;
2620 case ENOTDIR: return RMS$_DIR;
2622 case EINVAL: return SS$_INVARG;
2628 case ENOSPC: return SS$_DEVICEFULL;
2629 case ESPIPE: return LIB$_INVARG;
2634 case ERANGE: return LIB$_INVARG;
2635 /* case EWOULDBLOCK */
2636 /* case EINPROGRESS */
2639 /* case EDESTADDRREQ */
2641 /* case EPROTOTYPE */
2642 /* case ENOPROTOOPT */
2643 /* case EPROTONOSUPPORT */
2644 /* case ESOCKTNOSUPPORT */
2645 /* case EOPNOTSUPP */
2646 /* case EPFNOSUPPORT */
2647 /* case EAFNOSUPPORT */
2648 /* case EADDRINUSE */
2649 /* case EADDRNOTAVAIL */
2651 /* case ENETUNREACH */
2652 /* case ENETRESET */
2653 /* case ECONNABORTED */
2654 /* case ECONNRESET */
2657 case ENOTCONN: return SS$_CLEARED;
2658 /* case ESHUTDOWN */
2659 /* case ETOOMANYREFS */
2660 /* case ETIMEDOUT */
2661 /* case ECONNREFUSED */
2663 /* case ENAMETOOLONG */
2664 /* case EHOSTDOWN */
2665 /* case EHOSTUNREACH */
2666 /* case ENOTEMPTY */
2678 /* case ECANCELED */
2682 return SS$_UNSUPPORTED;
2688 /* case EABANDONED */
2690 return SS$_ABORT; /* punt */
2693 return SS$_ABORT; /* Should not get here */
2697 /* default piping mailbox size */
2698 #define PERL_BUFSIZ 512
2702 create_mbx(pTHX_ unsigned short int *chan, struct dsc$descriptor_s *namdsc)
2704 unsigned long int mbxbufsiz;
2705 static unsigned long int syssize = 0;
2706 unsigned long int dviitm = DVI$_DEVNAM;
2707 char csize[LNM$C_NAMLENGTH+1];
2711 unsigned long syiitm = SYI$_MAXBUF;
2713 * Get the SYSGEN parameter MAXBUF
2715 * If the logical 'PERL_MBX_SIZE' is defined
2716 * use the value of the logical instead of PERL_BUFSIZ, but
2717 * keep the size between 128 and MAXBUF.
2720 _ckvmssts(lib$getsyi(&syiitm, &syssize, 0, 0, 0, 0));
2723 if (vmstrnenv("PERL_MBX_SIZE", csize, 0, fildev, 0)) {
2724 mbxbufsiz = atoi(csize);
2726 mbxbufsiz = PERL_BUFSIZ;
2728 if (mbxbufsiz < 128) mbxbufsiz = 128;
2729 if (mbxbufsiz > syssize) mbxbufsiz = syssize;
2731 _ckvmssts(sts = sys$crembx(0,chan,mbxbufsiz,mbxbufsiz,0,0,0));
2733 _ckvmssts(sts = lib$getdvi(&dviitm, chan, NULL, NULL, namdsc, &namdsc->dsc$w_length));
2734 namdsc->dsc$a_pointer[namdsc->dsc$w_length] = '\0';
2736 } /* end of create_mbx() */
2739 /*{{{ my_popen and my_pclose*/
2741 typedef struct _iosb IOSB;
2742 typedef struct _iosb* pIOSB;
2743 typedef struct _pipe Pipe;
2744 typedef struct _pipe* pPipe;
2745 typedef struct pipe_details Info;
2746 typedef struct pipe_details* pInfo;
2747 typedef struct _srqp RQE;
2748 typedef struct _srqp* pRQE;
2749 typedef struct _tochildbuf CBuf;
2750 typedef struct _tochildbuf* pCBuf;
2753 unsigned short status;
2754 unsigned short count;
2755 unsigned long dvispec;
2758 #pragma member_alignment save
2759 #pragma nomember_alignment quadword
2760 struct _srqp { /* VMS self-relative queue entry */
2761 unsigned long qptr[2];
2763 #pragma member_alignment restore
2764 static RQE RQE_ZERO = {0,0};
2766 struct _tochildbuf {
2769 unsigned short size;
2777 unsigned short chan_in;
2778 unsigned short chan_out;
2780 unsigned int bufsize;
2792 #if defined(PERL_IMPLICIT_CONTEXT)
2793 void *thx; /* Either a thread or an interpreter */
2794 /* pointer, depending on how we're built */
2802 PerlIO *fp; /* file pointer to pipe mailbox */
2803 int useFILE; /* using stdio, not perlio */
2804 int pid; /* PID of subprocess */
2805 int mode; /* == 'r' if pipe open for reading */
2806 int done; /* subprocess has completed */
2807 int waiting; /* waiting for completion/closure */
2808 int closing; /* my_pclose is closing this pipe */
2809 unsigned long completion; /* termination status of subprocess */
2810 pPipe in; /* pipe in to sub */
2811 pPipe out; /* pipe out of sub */
2812 pPipe err; /* pipe of sub's sys$error */
2813 int in_done; /* true when in pipe finished */
2816 unsigned short xchan; /* channel to debug xterm */
2817 unsigned short xchan_valid; /* channel is assigned */
2820 struct exit_control_block
2822 struct exit_control_block *flink;
2823 unsigned long int (*exit_routine)();
2824 unsigned long int arg_count;
2825 unsigned long int *status_address;
2826 unsigned long int exit_status;
2829 typedef struct _closed_pipes Xpipe;
2830 typedef struct _closed_pipes* pXpipe;
2832 struct _closed_pipes {
2833 int pid; /* PID of subprocess */
2834 unsigned long completion; /* termination status of subprocess */
2836 #define NKEEPCLOSED 50
2837 static Xpipe closed_list[NKEEPCLOSED];
2838 static int closed_index = 0;
2839 static int closed_num = 0;
2841 #define RETRY_DELAY "0 ::0.20"
2842 #define MAX_RETRY 50
2844 static int pipe_ef = 0; /* first call to safe_popen inits these*/
2845 static unsigned long mypid;
2846 static unsigned long delaytime[2];
2848 static pInfo open_pipes = NULL;
2849 static $DESCRIPTOR(nl_desc, "NL:");
2851 #define PIPE_COMPLETION_WAIT 30 /* seconds, for EOF/FORCEX wait */
2855 static unsigned long int
2856 pipe_exit_routine(pTHX)
2859 unsigned long int retsts = SS$_NORMAL, abort = SS$_TIMEOUT;
2860 int sts, did_stuff, need_eof, j;
2863 * Flush any pending i/o, but since we are in process run-down, be
2864 * careful about referencing PerlIO structures that may already have
2865 * been deallocated. We may not even have an interpreter anymore.
2871 #if defined(USE_ITHREADS)
2874 && PL_perlio_fd_refcnt)
2875 PerlIO_flush(info->fp);
2877 fflush((FILE *)info->fp);
2883 next we try sending an EOF...ignore if doesn't work, make sure we
2891 _ckvmssts_noperl(sys$setast(0));
2892 if (info->in && !info->in->shut_on_empty) {
2893 _ckvmssts_noperl(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
2898 _ckvmssts_noperl(sys$setast(1));
2902 /* wait for EOF to have effect, up to ~ 30 sec [default] */
2904 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
2909 _ckvmssts_noperl(sys$setast(0));
2910 if (info->waiting && info->done)
2912 nwait += info->waiting;
2913 _ckvmssts_noperl(sys$setast(1));
2923 _ckvmssts_noperl(sys$setast(0));
2924 if (!info->done) { /* Tap them gently on the shoulder . . .*/
2925 sts = sys$forcex(&info->pid,0,&abort);
2926 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
2929 _ckvmssts_noperl(sys$setast(1));
2933 /* again, wait for effect */
2935 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
2940 _ckvmssts_noperl(sys$setast(0));
2941 if (info->waiting && info->done)
2943 nwait += info->waiting;
2944 _ckvmssts_noperl(sys$setast(1));
2953 _ckvmssts_noperl(sys$setast(0));
2954 if (!info->done) { /* We tried to be nice . . . */
2955 sts = sys$delprc(&info->pid,0);
2956 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
2957 info->done = 1; /* sys$delprc is as done as we're going to get. */
2959 _ckvmssts_noperl(sys$setast(1));
2964 if ((sts = my_pclose(open_pipes->fp)) == -1) retsts = vaxc$errno;
2965 else if (!(sts & 1)) retsts = sts;
2970 static struct exit_control_block pipe_exitblock =
2971 {(struct exit_control_block *) 0,
2972 pipe_exit_routine, 0, &pipe_exitblock.exit_status, 0};
2974 static void pipe_mbxtofd_ast(pPipe p);
2975 static void pipe_tochild1_ast(pPipe p);
2976 static void pipe_tochild2_ast(pPipe p);
2979 popen_completion_ast(pInfo info)
2981 pInfo i = open_pipes;
2986 info->completion &= 0x0FFFFFFF; /* strip off "control" field */
2987 closed_list[closed_index].pid = info->pid;
2988 closed_list[closed_index].completion = info->completion;
2990 if (closed_index == NKEEPCLOSED)
2995 if (i == info) break;
2998 if (!i) return; /* unlinked, probably freed too */
3003 Writing to subprocess ...
3004 if my_pclose'd: EOF already sent, should shutdown chan_in part of pipe
3006 chan_out may be waiting for "done" flag, or hung waiting
3007 for i/o completion to child...cancel the i/o. This will
3008 put it into "snarf mode" (done but no EOF yet) that discards
3011 Output from subprocess (stdout, stderr) needs to be flushed and
3012 shut down. We try sending an EOF, but if the mbx is full the pipe
3013 routine should still catch the "shut_on_empty" flag, telling it to
3014 use immediate-style reads so that "mbx empty" -> EOF.
3018 if (info->in && !info->in_done) { /* only for mode=w */
3019 if (info->in->shut_on_empty && info->in->need_wake) {
3020 info->in->need_wake = FALSE;
3021 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,info->in,0));
3023 _ckvmssts_noperl(sys$cancel(info->in->chan_out));
3027 if (info->out && !info->out_done) { /* were we also piping output? */
3028 info->out->shut_on_empty = TRUE;
3029 iss = sys$qio(0,info->out->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
3030 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
3031 _ckvmssts_noperl(iss);
3034 if (info->err && !info->err_done) { /* we were piping stderr */
3035 info->err->shut_on_empty = TRUE;
3036 iss = sys$qio(0,info->err->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
3037 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
3038 _ckvmssts_noperl(iss);
3040 _ckvmssts_noperl(sys$setef(pipe_ef));
3044 static unsigned long int setup_cmddsc(pTHX_ const char *cmd, int check_img, int *suggest_quote, struct dsc$descriptor_s **pvmscmd);
3045 static void vms_execfree(struct dsc$descriptor_s *vmscmd);
3048 we actually differ from vmstrnenv since we use this to
3049 get the RMS IFI to check if SYS$OUTPUT and SYS$ERROR *really*
3050 are pointing to the same thing
3053 static unsigned short
3054 popen_translate(pTHX_ char *logical, char *result)
3057 $DESCRIPTOR(d_table,"LNM$PROCESS_TABLE");
3058 $DESCRIPTOR(d_log,"");
3060 unsigned short length;
3061 unsigned short code;
3063 unsigned short *retlenaddr;
3065 unsigned short l, ifi;
3067 d_log.dsc$a_pointer = logical;
3068 d_log.dsc$w_length = strlen(logical);
3070 itmlst[0].code = LNM$_STRING;
3071 itmlst[0].length = 255;
3072 itmlst[0].buffer_addr = result;
3073 itmlst[0].retlenaddr = &l;
3076 itmlst[1].length = 0;
3077 itmlst[1].buffer_addr = 0;
3078 itmlst[1].retlenaddr = 0;
3080 iss = sys$trnlnm(0, &d_table, &d_log, 0, itmlst);
3081 if (iss == SS$_NOLOGNAM) {
3085 if (!(iss&1)) lib$signal(iss);
3088 logicals for PPFs have a 4 byte prefix ESC+NUL+(RMS IFI)
3089 strip it off and return the ifi, if any
3092 if (result[0] == 0x1b && result[1] == 0x00) {
3093 memmove(&ifi,result+2,2);
3094 strcpy(result,result+4);
3096 return ifi; /* this is the RMS internal file id */
3099 static void pipe_infromchild_ast(pPipe p);
3102 I'm using LIB$(GET|FREE)_VM here so that we can allocate and deallocate
3103 inside an AST routine without worrying about reentrancy and which Perl
3104 memory allocator is being used.
3106 We read data and queue up the buffers, then spit them out one at a
3107 time to the output mailbox when the output mailbox is ready for one.
3110 #define INITIAL_TOCHILDQUEUE 2
3113 pipe_tochild_setup(pTHX_ char *rmbx, char *wmbx)
3117 char mbx1[64], mbx2[64];
3118 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3119 DSC$K_CLASS_S, mbx1},
3120 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
3121 DSC$K_CLASS_S, mbx2};
3122 unsigned int dviitm = DVI$_DEVBUFSIZ;
3126 _ckvmssts(lib$get_vm(&n, &p));
3128 create_mbx(aTHX_ &p->chan_in , &d_mbx1);
3129 create_mbx(aTHX_ &p->chan_out, &d_mbx2);
3130 _ckvmssts(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3133 p->shut_on_empty = FALSE;
3134 p->need_wake = FALSE;
3137 p->iosb.status = SS$_NORMAL;
3138 p->iosb2.status = SS$_NORMAL;
3144 #ifdef PERL_IMPLICIT_CONTEXT
3148 n = sizeof(CBuf) + p->bufsize;
3150 for (j = 0; j < INITIAL_TOCHILDQUEUE; j++) {
3151 _ckvmssts(lib$get_vm(&n, &b));
3152 b->buf = (char *) b + sizeof(CBuf);
3153 _ckvmssts(lib$insqhi(b, &p->free));
3156 pipe_tochild2_ast(p);
3157 pipe_tochild1_ast(p);
3163 /* reads the MBX Perl is writing, and queues */
3166 pipe_tochild1_ast(pPipe p)
3169 int iss = p->iosb.status;
3170 int eof = (iss == SS$_ENDOFFILE);
3172 #ifdef PERL_IMPLICIT_CONTEXT
3178 p->shut_on_empty = TRUE;
3180 _ckvmssts(sys$dassgn(p->chan_in));
3186 b->size = p->iosb.count;
3187 _ckvmssts(sts = lib$insqhi(b, &p->wait));
3189 p->need_wake = FALSE;
3190 _ckvmssts(sys$dclast(pipe_tochild2_ast,p,0));
3193 p->retry = 1; /* initial call */
3196 if (eof) { /* flush the free queue, return when done */
3197 int n = sizeof(CBuf) + p->bufsize;
3199 iss = lib$remqti(&p->free, &b);
3200 if (iss == LIB$_QUEWASEMP) return;
3202 _ckvmssts(lib$free_vm(&n, &b));
3206 iss = lib$remqti(&p->free, &b);
3207 if (iss == LIB$_QUEWASEMP) {
3208 int n = sizeof(CBuf) + p->bufsize;
3209 _ckvmssts(lib$get_vm(&n, &b));
3210 b->buf = (char *) b + sizeof(CBuf);
3216 iss = sys$qio(0,p->chan_in,
3217 IO$_READVBLK|(p->shut_on_empty ? IO$M_NOWAIT : 0),
3219 pipe_tochild1_ast, p, b->buf, p->bufsize, 0, 0, 0, 0);
3220 if (iss == SS$_ENDOFFILE && p->shut_on_empty) iss = SS$_NORMAL;
3225 /* writes queued buffers to output, waits for each to complete before
3229 pipe_tochild2_ast(pPipe p)
3232 int iss = p->iosb2.status;
3233 int n = sizeof(CBuf) + p->bufsize;
3234 int done = (p->info && p->info->done) ||
3235 iss == SS$_CANCEL || iss == SS$_ABORT;
3236 #if defined(PERL_IMPLICIT_CONTEXT)
3241 if (p->type) { /* type=1 has old buffer, dispose */
3242 if (p->shut_on_empty) {
3243 _ckvmssts(lib$free_vm(&n, &b));
3245 _ckvmssts(lib$insqhi(b, &p->free));
3250 iss = lib$remqti(&p->wait, &b);
3251 if (iss == LIB$_QUEWASEMP) {
3252 if (p->shut_on_empty) {
3254 _ckvmssts(sys$dassgn(p->chan_out));
3255 *p->pipe_done = TRUE;
3256 _ckvmssts(sys$setef(pipe_ef));
3258 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEOF,
3259 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3263 p->need_wake = TRUE;
3273 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEOF,
3274 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3276 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEVBLK,
3277 &p->iosb2, pipe_tochild2_ast, p, b->buf, b->size, 0, 0, 0, 0));
3286 pipe_infromchild_setup(pTHX_ char *rmbx, char *wmbx)
3289 char mbx1[64], mbx2[64];
3290 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3291 DSC$K_CLASS_S, mbx1},
3292 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
3293 DSC$K_CLASS_S, mbx2};
3294 unsigned int dviitm = DVI$_DEVBUFSIZ;
3296 int n = sizeof(Pipe);
3297 _ckvmssts(lib$get_vm(&n, &p));
3298 create_mbx(aTHX_ &p->chan_in , &d_mbx1);
3299 create_mbx(aTHX_ &p->chan_out, &d_mbx2);
3301 _ckvmssts(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3302 n = p->bufsize * sizeof(char);
3303 _ckvmssts(lib$get_vm(&n, &p->buf));
3304 p->shut_on_empty = FALSE;
3307 p->iosb.status = SS$_NORMAL;
3308 #if defined(PERL_IMPLICIT_CONTEXT)
3311 pipe_infromchild_ast(p);
3319 pipe_infromchild_ast(pPipe p)
3321 int iss = p->iosb.status;
3322 int eof = (iss == SS$_ENDOFFILE);
3323 int myeof = (eof && (p->iosb.dvispec == mypid || p->iosb.dvispec == 0));
3324 int kideof = (eof && (p->iosb.dvispec == p->info->pid));
3325 #if defined(PERL_IMPLICIT_CONTEXT)
3329 if (p->info && p->info->closing && p->chan_out) { /* output shutdown */
3330 _ckvmssts(sys$dassgn(p->chan_out));
3335 input shutdown if EOF from self (done or shut_on_empty)
3336 output shutdown if closing flag set (my_pclose)
3337 send data/eof from child or eof from self
3338 otherwise, re-read (snarf of data from child)
3343 if (myeof && p->chan_in) { /* input shutdown */
3344 _ckvmssts(sys$dassgn(p->chan_in));
3349 if (myeof || kideof) { /* pass EOF to parent */
3350 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEOF, &p->iosb,
3351 pipe_infromchild_ast, p,
3354 } else if (eof) { /* eat EOF --- fall through to read*/
3356 } else { /* transmit data */
3357 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEVBLK,&p->iosb,
3358 pipe_infromchild_ast,p,
3359 p->buf, p->iosb.count, 0, 0, 0, 0));
3365 /* everything shut? flag as done */
3367 if (!p->chan_in && !p->chan_out) {
3368 *p->pipe_done = TRUE;
3369 _ckvmssts(sys$setef(pipe_ef));
3373 /* write completed (or read, if snarfing from child)
3374 if still have input active,
3375 queue read...immediate mode if shut_on_empty so we get EOF if empty
3377 check if Perl reading, generate EOFs as needed
3383 iss = sys$qio(0,p->chan_in,IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0),&p->iosb,
3384 pipe_infromchild_ast,p,
3385 p->buf, p->bufsize, 0, 0, 0, 0);
3386 if (p->shut_on_empty && iss == SS$_ENDOFFILE) iss = SS$_NORMAL;
3388 } else { /* send EOFs for extra reads */
3389 p->iosb.status = SS$_ENDOFFILE;
3390 p->iosb.dvispec = 0;
3391 _ckvmssts(sys$qio(0,p->chan_out,IO$_SETMODE|IO$M_READATTN,
3393 pipe_infromchild_ast, p, 0, 0, 0, 0));
3399 pipe_mbxtofd_setup(pTHX_ int fd, char *out)
3403 unsigned long dviitm = DVI$_DEVBUFSIZ;
3405 struct dsc$descriptor_s d_mbx = {sizeof mbx, DSC$K_DTYPE_T,
3406 DSC$K_CLASS_S, mbx};
3407 int n = sizeof(Pipe);
3409 /* things like terminals and mbx's don't need this filter */
3410 if (fd && fstat(fd,&s) == 0) {
3411 unsigned long dviitm = DVI$_DEVCHAR, devchar;
3413 unsigned short dev_len;
3414 struct dsc$descriptor_s d_dev;
3416 struct item_list_3 items[3];
3418 unsigned short dvi_iosb[4];
3420 cptr = getname(fd, out, 1);
3421 if (cptr == NULL) _ckvmssts(SS$_NOSUCHDEV);
3422 d_dev.dsc$a_pointer = out;
3423 d_dev.dsc$w_length = strlen(out);
3424 d_dev.dsc$b_dtype = DSC$K_DTYPE_T;
3425 d_dev.dsc$b_class = DSC$K_CLASS_S;
3428 items[0].code = DVI$_DEVCHAR;
3429 items[0].bufadr = &devchar;
3430 items[0].retadr = NULL;
3432 items[1].code = DVI$_FULLDEVNAM;
3433 items[1].bufadr = device;
3434 items[1].retadr = &dev_len;
3438 status = sys$getdviw
3439 (NO_EFN, 0, &d_dev, items, dvi_iosb, NULL, NULL, NULL);
3441 if ($VMS_STATUS_SUCCESS(dvi_iosb[0])) {
3442 device[dev_len] = 0;
3444 if (!(devchar & DEV$M_DIR)) {
3445 strcpy(out, device);
3451 _ckvmssts(lib$get_vm(&n, &p));
3452 p->fd_out = dup(fd);
3453 create_mbx(aTHX_ &p->chan_in, &d_mbx);
3454 _ckvmssts(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3455 n = (p->bufsize+1) * sizeof(char);
3456 _ckvmssts(lib$get_vm(&n, &p->buf));
3457 p->shut_on_empty = FALSE;
3462 _ckvmssts(sys$qio(0, p->chan_in, IO$_READVBLK, &p->iosb,
3463 pipe_mbxtofd_ast, p,
3464 p->buf, p->bufsize, 0, 0, 0, 0));
3470 pipe_mbxtofd_ast(pPipe p)
3472 int iss = p->iosb.status;
3473 int done = p->info->done;
3475 int eof = (iss == SS$_ENDOFFILE);
3476 int myeof = eof && ((p->iosb.dvispec == mypid)||(p->iosb.dvispec == 0));
3477 int err = !(iss&1) && !eof;
3478 #if defined(PERL_IMPLICIT_CONTEXT)
3482 if (done && myeof) { /* end piping */
3484 sys$dassgn(p->chan_in);
3485 *p->pipe_done = TRUE;
3486 _ckvmssts(sys$setef(pipe_ef));
3490 if (!err && !eof) { /* good data to send to file */
3491 p->buf[p->iosb.count] = '\n';
3492 iss2 = write(p->fd_out, p->buf, p->iosb.count+1);
3495 if (p->retry < MAX_RETRY) {
3496 _ckvmssts(sys$setimr(0,delaytime,pipe_mbxtofd_ast,p));
3506 iss = sys$qio(0, p->chan_in, IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0), &p->iosb,
3507 pipe_mbxtofd_ast, p,
3508 p->buf, p->bufsize, 0, 0, 0, 0);
3509 if (p->shut_on_empty && (iss == SS$_ENDOFFILE)) iss = SS$_NORMAL;
3514 typedef struct _pipeloc PLOC;
3515 typedef struct _pipeloc* pPLOC;
3519 char dir[NAM$C_MAXRSS+1];
3521 static pPLOC head_PLOC = 0;
3524 free_pipelocs(pTHX_ void *head)
3527 pPLOC *pHead = (pPLOC *)head;
3539 store_pipelocs(pTHX)
3548 char temp[NAM$C_MAXRSS+1];
3552 free_pipelocs(aTHX_ &head_PLOC);
3554 /* the . directory from @INC comes last */
3556 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3557 if (p == NULL) _ckvmssts(SS$_INSFMEM);
3558 p->next = head_PLOC;
3560 strcpy(p->dir,"./");
3562 /* get the directory from $^X */
3564 unixdir = PerlMem_malloc(VMS_MAXRSS);
3565 if (unixdir == NULL) _ckvmssts(SS$_INSFMEM);
3567 #ifdef PERL_IMPLICIT_CONTEXT
3568 if (aTHX && PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
3570 if (PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
3572 strcpy(temp, PL_origargv[0]);
3573 x = strrchr(temp,']');
3575 x = strrchr(temp,'>');
3577 /* It could be a UNIX path */
3578 x = strrchr(temp,'/');
3584 /* Got a bare name, so use default directory */
3589 if ((tounixpath_utf8(temp, unixdir, NULL)) != Nullch) {
3590 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3591 if (p == NULL) _ckvmssts(SS$_INSFMEM);
3592 p->next = head_PLOC;
3594 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3595 p->dir[NAM$C_MAXRSS] = '\0';
3599 /* reverse order of @INC entries, skip "." since entered above */
3601 #ifdef PERL_IMPLICIT_CONTEXT
3604 if (PL_incgv) av = GvAVn(PL_incgv);
3606 for (i = 0; av && i <= AvFILL(av); i++) {
3607 dirsv = *av_fetch(av,i,TRUE);
3609 if (SvROK(dirsv)) continue;
3610 dir = SvPVx(dirsv,n_a);
3611 if (strcmp(dir,".") == 0) continue;
3612 if ((tounixpath_utf8(dir, unixdir, NULL)) == Nullch)
3615 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3616 p->next = head_PLOC;
3618 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3619 p->dir[NAM$C_MAXRSS] = '\0';
3622 /* most likely spot (ARCHLIB) put first in the list */
3625 if ((tounixpath_utf8(ARCHLIB_EXP, unixdir, NULL)) != Nullch) {
3626 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3627 if (p == NULL) _ckvmssts(SS$_INSFMEM);
3628 p->next = head_PLOC;
3630 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3631 p->dir[NAM$C_MAXRSS] = '\0';
3634 PerlMem_free(unixdir);
3638 Perl_cando_by_name_int
3639 (pTHX_ I32 bit, bool effective, const char *fname, int opts);
3640 #if !defined(PERL_IMPLICIT_CONTEXT)
3641 #define cando_by_name_int Perl_cando_by_name_int
3643 #define cando_by_name_int(a,b,c,d) Perl_cando_by_name_int(aTHX_ a,b,c,d)
3649 static int vmspipe_file_status = 0;
3650 static char vmspipe_file[NAM$C_MAXRSS+1];
3652 /* already found? Check and use ... need read+execute permission */
3654 if (vmspipe_file_status == 1) {
3655 if (cando_by_name_int(S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)
3656 && cando_by_name_int
3657 (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) {
3658 return vmspipe_file;
3660 vmspipe_file_status = 0;
3663 /* scan through stored @INC, $^X */
3665 if (vmspipe_file_status == 0) {
3666 char file[NAM$C_MAXRSS+1];
3667 pPLOC p = head_PLOC;
3672 strcpy(file, p->dir);
3673 dirlen = strlen(file);
3674 strncat(file, "vmspipe.com",NAM$C_MAXRSS - dirlen);
3675 file[NAM$C_MAXRSS] = '\0';
3678 exp_res = do_rmsexpand
3679 (file, vmspipe_file, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL);
3680 if (!exp_res) continue;
3682 if (cando_by_name_int
3683 (S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)
3684 && cando_by_name_int
3685 (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) {
3686 vmspipe_file_status = 1;
3687 return vmspipe_file;
3690 vmspipe_file_status = -1; /* failed, use tempfiles */
3697 vmspipe_tempfile(pTHX)
3699 char file[NAM$C_MAXRSS+1];
3701 static int index = 0;
3705 /* create a tempfile */
3707 /* we can't go from W, shr=get to R, shr=get without
3708 an intermediate vulnerable state, so don't bother trying...
3710 and lib$spawn doesn't shr=put, so have to close the write
3712 So... match up the creation date/time and the FID to
3713 make sure we're dealing with the same file
3718 if (!decc_filename_unix_only) {
3719 sprintf(file,"sys$scratch:perlpipe_%08.8x_%d.com",mypid,index);
3720 fp = fopen(file,"w");
3722 sprintf(file,"sys$login:perlpipe_%08.8x_%d.com",mypid,index);
3723 fp = fopen(file,"w");
3725 sprintf(file,"sys$disk:[]perlpipe_%08.8x_%d.com",mypid,index);
3726 fp = fopen(file,"w");
3731 sprintf(file,"/tmp/perlpipe_%08.8x_%d.com",mypid,index);
3732 fp = fopen(file,"w");
3734 sprintf(file,"/sys$login/perlpipe_%08.8x_%d.com",mypid,index);
3735 fp = fopen(file,"w");
3737 sprintf(file,"./perlpipe_%08.8x_%d.com",mypid,index);
3738 fp = fopen(file,"w");
3742 if (!fp) return 0; /* we're hosed */
3744 fprintf(fp,"$! 'f$verify(0)'\n");
3745 fprintf(fp,"$! --- protect against nonstandard definitions ---\n");
3746 fprintf(fp,"$ perl_cfile = f$environment(\"procedure\")\n");
3747 fprintf(fp,"$ perl_define = \"define/nolog\"\n");
3748 fprintf(fp,"$ perl_on = \"set noon\"\n");
3749 fprintf(fp,"$ perl_exit = \"exit\"\n");
3750 fprintf(fp,"$ perl_del = \"delete\"\n");
3751 fprintf(fp,"$ pif = \"if\"\n");
3752 fprintf(fp,"$! --- define i/o redirection (sys$output set by lib$spawn)\n");
3753 fprintf(fp,"$ pif perl_popen_in .nes. \"\" then perl_define/user/name_attributes=confine sys$input 'perl_popen_in'\n");
3754 fprintf(fp,"$ pif perl_popen_err .nes. \"\" then perl_define/user/name_attributes=confine sys$error 'perl_popen_err'\n");
3755 fprintf(fp,"$ pif perl_popen_out .nes. \"\" then perl_define sys$output 'perl_popen_out'\n");
3756 fprintf(fp,"$! --- build command line to get max possible length\n");
3757 fprintf(fp,"$c=perl_popen_cmd0\n");
3758 fprintf(fp,"$c=c+perl_popen_cmd1\n");
3759 fprintf(fp,"$c=c+perl_popen_cmd2\n");
3760 fprintf(fp,"$x=perl_popen_cmd3\n");
3761 fprintf(fp,"$c=c+x\n");
3762 fprintf(fp,"$ perl_on\n");
3763 fprintf(fp,"$ 'c'\n");
3764 fprintf(fp,"$ perl_status = $STATUS\n");
3765 fprintf(fp,"$ perl_del 'perl_cfile'\n");
3766 fprintf(fp,"$ perl_exit 'perl_status'\n");
3769 fgetname(fp, file, 1);
3770 fstat(fileno(fp), (struct stat *)&s0);
3773 if (decc_filename_unix_only)
3774 do_tounixspec(file, file, 0, NULL);
3775 fp = fopen(file,"r","shr=get");
3777 fstat(fileno(fp), (struct stat *)&s1);
3779 cmp_result = VMS_INO_T_COMPARE(s0.crtl_stat.st_ino, s1.crtl_stat.st_ino);
3780 if ((cmp_result != 0) && (s0.st_ctime != s1.st_ctime)) {
3789 static int vms_is_syscommand_xterm(void)
3791 const static struct dsc$descriptor_s syscommand_dsc =
3792 { 11, DSC$K_DTYPE_T, DSC$K_CLASS_S, "SYS$COMMAND" };
3794 const static struct dsc$descriptor_s decwdisplay_dsc =
3795 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "DECW$DISPLAY" };
3797 struct item_list_3 items[2];
3798 unsigned short dvi_iosb[4];
3799 unsigned long devchar;
3800 unsigned long devclass;
3803 /* Very simple check to guess if sys$command is a decterm? */
3804 /* First see if the DECW$DISPLAY: device exists */
3806 items[0].code = DVI$_DEVCHAR;
3807 items[0].bufadr = &devchar;
3808 items[0].retadr = NULL;
3812 status = sys$getdviw
3813 (NO_EFN, 0, &decwdisplay_dsc, items, dvi_iosb, NULL, NULL, NULL);
3815 if ($VMS_STATUS_SUCCESS(status)) {
3816 status = dvi_iosb[0];
3819 if (!$VMS_STATUS_SUCCESS(status)) {
3820 SETERRNO(EVMSERR, status);
3824 /* If it does, then for now assume that we are on a workstation */
3825 /* Now verify that SYS$COMMAND is a terminal */
3826 /* for creating the debugger DECTerm */
3829 items[0].code = DVI$_DEVCLASS;
3830 items[0].bufadr = &devclass;
3831 items[0].retadr = NULL;
3835 status = sys$getdviw
3836 (NO_EFN, 0, &syscommand_dsc, items, dvi_iosb, NULL, NULL, NULL);
3838 if ($VMS_STATUS_SUCCESS(status)) {
3839 status = dvi_iosb[0];
3842 if (!$VMS_STATUS_SUCCESS(status)) {
3843 SETERRNO(EVMSERR, status);
3847 if (devclass == DC$_TERM) {
3854 /* If we are on a DECTerm, we can pretend to fork xterms when requested */
3855 static PerlIO * create_forked_xterm(pTHX_ const char *cmd, const char *mode)
3860 char device_name[65];
3861 unsigned short device_name_len;
3862 struct dsc$descriptor_s customization_dsc;
3863 struct dsc$descriptor_s device_name_dsc;
3866 char customization[200];
3870 unsigned short p_chan;
3872 unsigned short iosb[4];
3873 struct item_list_3 items[2];
3874 const char * cust_str =
3875 "DECW$TERMINAL.iconName:\tPerl Dbg\nDECW$TERMINAL.title:\t%40s\n";
3876 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3877 DSC$K_CLASS_S, mbx1};
3879 /* LIB$FIND_IMAGE_SIGNAL needs a handler */
3880 /*---------------------------------------*/
3881 VAXC$ESTABLISH((__vms_handler)LIB$SIG_TO_RET);
3884 /* Make sure that this is from the Perl debugger */
3885 ret_char = strstr(cmd," xterm ");
3886 if (ret_char == NULL)
3888 cptr = ret_char + 7;
3889 ret_char = strstr(cmd,"tty");
3890 if (ret_char == NULL)
3892 ret_char = strstr(cmd,"sleep");
3893 if (ret_char == NULL)
3896 if (decw_term_port == 0) {
3897 $DESCRIPTOR(filename1_dsc, "DECW$TERMINALSHR12");
3898 $DESCRIPTOR(filename2_dsc, "DECW$TERMINALSHR");
3899 $DESCRIPTOR(decw_term_port_dsc, "DECW$TERM_PORT");
3901 status = LIB$FIND_IMAGE_SYMBOL
3903 &decw_term_port_dsc,
3904 (void *)&decw_term_port,
3908 /* Try again with the other image name */
3909 if (!$VMS_STATUS_SUCCESS(status)) {
3911 status = LIB$FIND_IMAGE_SYMBOL
3913 &decw_term_port_dsc,
3914 (void *)&decw_term_port,
3923 /* No decw$term_port, give it up */
3924 if (!$VMS_STATUS_SUCCESS(status))
3927 /* Are we on a workstation? */
3928 /* to do: capture the rows / columns and pass their properties */
3929 ret_stat = vms_is_syscommand_xterm();
3933 /* Make the title: */
3934 ret_char = strstr(cptr,"-title");
3935 if (ret_char != NULL) {
3936 while ((*cptr != 0) && (*cptr != '\"')) {
3942 while ((*cptr != 0) && (*cptr != '\"')) {
3955 strcpy(title,"Perl Debug DECTerm");
3957 sprintf(customization, cust_str, title);
3959 customization_dsc.dsc$a_pointer = customization;
3960 customization_dsc.dsc$w_length = strlen(customization);
3961 customization_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
3962 customization_dsc.dsc$b_class = DSC$K_CLASS_S;
3964 device_name_dsc.dsc$a_pointer = device_name;
3965 device_name_dsc.dsc$w_length = sizeof device_name -1;
3966 device_name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
3967 device_name_dsc.dsc$b_class = DSC$K_CLASS_S;
3969 device_name_len = 0;
3971 /* Try to create the window */
3972 status = (*decw_term_port)
3981 if (!$VMS_STATUS_SUCCESS(status)) {
3982 SETERRNO(EVMSERR, status);
3986 device_name[device_name_len] = '\0';
3988 /* Need to set this up to look like a pipe for cleanup */
3990 status = lib$get_vm(&n, &info);
3991 if (!$VMS_STATUS_SUCCESS(status)) {
3992 SETERRNO(ENOMEM, status);
3998 info->completion = 0;
3999 info->closing = FALSE;
4006 info->in_done = TRUE;
4007 info->out_done = TRUE;
4008 info->err_done = TRUE;
4010 /* Assign a channel on this so that it will persist, and not login */
4011 /* We stash this channel in the info structure for reference. */
4012 /* The created xterm self destructs when the last channel is removed */
4013 /* and it appears that perl5db.pl (perl debugger) does this routinely */
4014 /* So leave this assigned. */
4015 device_name_dsc.dsc$w_length = device_name_len;
4016 status = sys$assign(&device_name_dsc,&info->xchan,0,0);
4017 if (!$VMS_STATUS_SUCCESS(status)) {
4018 SETERRNO(EVMSERR, status);
4021 info->xchan_valid = 1;
4023 /* Now create a mailbox to be read by the application */
4025 create_mbx(aTHX_ &p_chan, &d_mbx1);
4027 /* write the name of the created terminal to the mailbox */
4028 status = sys$qiow(NO_EFN, p_chan, IO$_WRITEVBLK|IO$M_NOW,
4029 iosb, NULL, NULL, device_name, device_name_len, 0, 0, 0, 0);
4031 if (!$VMS_STATUS_SUCCESS(status)) {
4032 SETERRNO(EVMSERR, status);
4036 info->fp = PerlIO_open(mbx1, mode);
4038 /* Done with this channel */
4041 /* If any errors, then clean up */
4044 _ckvmssts(lib$free_vm(&n, &info));
4053 safe_popen(pTHX_ const char *cmd, const char *in_mode, int *psts)
4055 static int handler_set_up = FALSE;
4056 unsigned long int sts, flags = CLI$M_NOWAIT;
4057 /* The use of a GLOBAL table (as was done previously) rendered
4058 * Perl's qx() or `` unusable from a C<$ SET SYMBOL/SCOPE=NOGLOBAL> DCL
4059 * environment. Hence we've switched to LOCAL symbol table.
4061 unsigned int table = LIB$K_CLI_LOCAL_SYM;
4063 char *p, mode[10], symbol[MAX_DCL_SYMBOL+1], *vmspipe;
4064 char *in, *out, *err, mbx[512];
4066 char tfilebuf[NAM$C_MAXRSS+1];
4068 char cmd_sym_name[20];
4069 struct dsc$descriptor_s d_symbol= {0, DSC$K_DTYPE_T,
4070 DSC$K_CLASS_S, symbol};
4071 struct dsc$descriptor_s vmspipedsc = {0, DSC$K_DTYPE_T,
4073 struct dsc$descriptor_s d_sym_cmd = {0, DSC$K_DTYPE_T,
4074 DSC$K_CLASS_S, cmd_sym_name};
4075 struct dsc$descriptor_s *vmscmd;
4076 $DESCRIPTOR(d_sym_in ,"PERL_POPEN_IN");
4077 $DESCRIPTOR(d_sym_out,"PERL_POPEN_OUT");
4078 $DESCRIPTOR(d_sym_err,"PERL_POPEN_ERR");
4080 /* Check here for Xterm create request. This means looking for
4081 * "3>&1 xterm\b" and "\btty 1>&3\b$" in the command, and that it
4082 * is possible to create an xterm.
4084 if (*in_mode == 'r') {
4087 xterm_fd = create_forked_xterm(aTHX_ cmd, in_mode);
4088 if (xterm_fd != Nullfp)
4092 if (!head_PLOC) store_pipelocs(aTHX); /* at least TRY to use a static vmspipe file */
4094 /* once-per-program initialization...
4095 note that the SETAST calls and the dual test of pipe_ef
4096 makes sure that only the FIRST thread through here does
4097 the initialization...all other threads wait until it's
4100 Yeah, uglier than a pthread call, it's got all the stuff inline
4101 rather than in a separate routine.
4105 _ckvmssts(sys$setast(0));
4107 unsigned long int pidcode = JPI$_PID;
4108 $DESCRIPTOR(d_delay, RETRY_DELAY);
4109 _ckvmssts(lib$get_ef(&pipe_ef));
4110 _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
4111 _ckvmssts(sys$bintim(&d_delay, delaytime));
4113 if (!handler_set_up) {
4114 _ckvmssts(sys$dclexh(&pipe_exitblock));
4115 handler_set_up = TRUE;
4117 _ckvmssts(sys$setast(1));
4120 /* see if we can find a VMSPIPE.COM */
4123 vmspipe = find_vmspipe(aTHX);
4125 strcpy(tfilebuf+1,vmspipe);
4126 } else { /* uh, oh...we're in tempfile hell */
4127 tpipe = vmspipe_tempfile(aTHX);
4128 if (!tpipe) { /* a fish popular in Boston */
4129 if (ckWARN(WARN_PIPE)) {
4130 Perl_warner(aTHX_ packWARN(WARN_PIPE),"unable to find VMSPIPE.COM for i/o piping");
4134 fgetname(tpipe,tfilebuf+1,1);
4136 vmspipedsc.dsc$a_pointer = tfilebuf;
4137 vmspipedsc.dsc$w_length = strlen(tfilebuf);
4139 sts = setup_cmddsc(aTHX_ cmd,0,0,&vmscmd);
4142 case RMS$_FNF: case RMS$_DNF:
4143 set_errno(ENOENT); break;
4145 set_errno(ENOTDIR); break;
4147 set_errno(ENODEV); break;
4149 set_errno(EACCES); break;
4151 set_errno(EINVAL); break;
4152 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
4153 set_errno(E2BIG); break;
4154 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
4155 _ckvmssts(sts); /* fall through */
4156 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
4159 set_vaxc_errno(sts);
4160 if (*in_mode != 'n' && ckWARN(WARN_PIPE)) {
4161 Perl_warner(aTHX_ packWARN(WARN_PIPE),"Can't pipe \"%*s\": %s", strlen(cmd), cmd, Strerror(errno));
4167 _ckvmssts(lib$get_vm(&n, &info));
4169 strcpy(mode,in_mode);
4172 info->completion = 0;
4173 info->closing = FALSE;
4180 info->in_done = TRUE;
4181 info->out_done = TRUE;
4182 info->err_done = TRUE;
4184 info->xchan_valid = 0;
4186 in = PerlMem_malloc(VMS_MAXRSS);
4187 if (in == NULL) _ckvmssts(SS$_INSFMEM);
4188 out = PerlMem_malloc(VMS_MAXRSS);
4189 if (out == NULL) _ckvmssts(SS$_INSFMEM);
4190 err = PerlMem_malloc(VMS_MAXRSS);
4191 if (err == NULL) _ckvmssts(SS$_INSFMEM);
4193 in[0] = out[0] = err[0] = '\0';
4195 if ((p = strchr(mode,'F')) != NULL) { /* F -> use FILE* */
4199 if ((p = strchr(mode,'W')) != NULL) { /* W -> wait for completion */
4204 if (*mode == 'r') { /* piping from subroutine */
4206 info->out = pipe_infromchild_setup(aTHX_ mbx,out);
4208 info->out->pipe_done = &info->out_done;
4209 info->out_done = FALSE;
4210 info->out->info = info;
4212 if (!info->useFILE) {
4213 info->fp = PerlIO_open(mbx, mode);
4215 info->fp = (PerlIO *) freopen(mbx, mode, stdin);
4216 Perl_vmssetuserlnm(aTHX_ "SYS$INPUT",mbx);
4219 if (!info->fp && info->out) {
4220 sys$cancel(info->out->chan_out);
4222 while (!info->out_done) {
4224 _ckvmssts(sys$setast(0));
4225 done = info->out_done;
4226 if (!done) _ckvmssts(sys$clref(pipe_ef));
4227 _ckvmssts(sys$setast(1));
4228 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4231 if (info->out->buf) {
4232 n = info->out->bufsize * sizeof(char);
4233 _ckvmssts(lib$free_vm(&n, &info->out->buf));
4236 _ckvmssts(lib$free_vm(&n, &info->out));
4238 _ckvmssts(lib$free_vm(&n, &info));
4243 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4245 info->err->pipe_done = &info->err_done;
4246 info->err_done = FALSE;
4247 info->err->info = info;
4250 } else if (*mode == 'w') { /* piping to subroutine */
4252 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
4254 info->out->pipe_done = &info->out_done;
4255 info->out_done = FALSE;
4256 info->out->info = info;
4259 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4261 info->err->pipe_done = &info->err_done;
4262 info->err_done = FALSE;
4263 info->err->info = info;
4266 info->in = pipe_tochild_setup(aTHX_ in,mbx);
4267 if (!info->useFILE) {
4268 info->fp = PerlIO_open(mbx, mode);
4270 info->fp = (PerlIO *) freopen(mbx, mode, stdout);
4271 Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",mbx);
4275 info->in->pipe_done = &info->in_done;
4276 info->in_done = FALSE;
4277 info->in->info = info;
4281 if (!info->fp && info->in) {
4283 _ckvmssts(sys$qiow(0,info->in->chan_in, IO$_WRITEOF, 0,
4284 0, 0, 0, 0, 0, 0, 0, 0));
4286 while (!info->in_done) {
4288 _ckvmssts(sys$setast(0));
4289 done = info->in_done;
4290 if (!done) _ckvmssts(sys$clref(pipe_ef));
4291 _ckvmssts(sys$setast(1));
4292 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4295 if (info->in->buf) {
4296 n = info->in->bufsize * sizeof(char);
4297 _ckvmssts(lib$free_vm(&n, &info->in->buf));
4300 _ckvmssts(lib$free_vm(&n, &info->in));
4302 _ckvmssts(lib$free_vm(&n, &info));
4308 } else if (*mode == 'n') { /* separate subprocess, no Perl i/o */
4309 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
4311 info->out->pipe_done = &info->out_done;
4312 info->out_done = FALSE;
4313 info->out->info = info;
4316 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4318 info->err->pipe_done = &info->err_done;
4319 info->err_done = FALSE;
4320 info->err->info = info;
4324 symbol[MAX_DCL_SYMBOL] = '\0';
4326 strncpy(symbol, in, MAX_DCL_SYMBOL);
4327 d_symbol.dsc$w_length = strlen(symbol);
4328 _ckvmssts(lib$set_symbol(&d_sym_in, &d_symbol, &table));
4330 strncpy(symbol, err, MAX_DCL_SYMBOL);
4331 d_symbol.dsc$w_length = strlen(symbol);
4332 _ckvmssts(lib$set_symbol(&d_sym_err, &d_symbol, &table));
4334 strncpy(symbol, out, MAX_DCL_SYMBOL);
4335 d_symbol.dsc$w_length = strlen(symbol);
4336 _ckvmssts(lib$set_symbol(&d_sym_out, &d_symbol, &table));
4338 /* Done with the names for the pipes */
4343 p = vmscmd->dsc$a_pointer;
4344 while (*p == ' ' || *p == '\t') p++; /* remove leading whitespace */
4345 if (*p == '$') p++; /* remove leading $ */
4346 while (*p == ' ' || *p == '\t') p++;
4348 for (j = 0; j < 4; j++) {
4349 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
4350 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
4352 strncpy(symbol, p, MAX_DCL_SYMBOL);
4353 d_symbol.dsc$w_length = strlen(symbol);
4354 _ckvmssts(lib$set_symbol(&d_sym_cmd, &d_symbol, &table));
4356 if (strlen(p) > MAX_DCL_SYMBOL) {
4357 p += MAX_DCL_SYMBOL;
4362 _ckvmssts(sys$setast(0));
4363 info->next=open_pipes; /* prepend to list */
4365 _ckvmssts(sys$setast(1));
4366 /* Omit arg 2 (input file) so the child will get the parent's SYS$INPUT
4367 * and SYS$COMMAND. vmspipe.com will redefine SYS$INPUT, but we'll still
4368 * have SYS$COMMAND if we need it.
4370 _ckvmssts(lib$spawn(&vmspipedsc, 0, &nl_desc, &flags,
4371 0, &info->pid, &info->completion,
4372 0, popen_completion_ast,info,0,0,0));
4374 /* if we were using a tempfile, close it now */
4376 if (tpipe) fclose(tpipe);
4378 /* once the subprocess is spawned, it has copied the symbols and
4379 we can get rid of ours */
4381 for (j = 0; j < 4; j++) {
4382 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
4383 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
4384 _ckvmssts(lib$delete_symbol(&d_sym_cmd, &table));
4386 _ckvmssts(lib$delete_symbol(&d_sym_in, &table));
4387 _ckvmssts(lib$delete_symbol(&d_sym_err, &table));
4388 _ckvmssts(lib$delete_symbol(&d_sym_out, &table));
4389 vms_execfree(vmscmd);
4391 #ifdef PERL_IMPLICIT_CONTEXT
4394 PL_forkprocess = info->pid;
4399 _ckvmssts(sys$setast(0));
4401 if (!done) _ckvmssts(sys$clref(pipe_ef));
4402 _ckvmssts(sys$setast(1));
4403 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4405 *psts = info->completion;
4406 /* Caller thinks it is open and tries to close it. */
4407 /* This causes some problems, as it changes the error status */
4408 /* my_pclose(info->fp); */
4413 } /* end of safe_popen */
4416 /*{{{ PerlIO *my_popen(char *cmd, char *mode)*/
4418 Perl_my_popen(pTHX_ const char *cmd, const char *mode)
4422 TAINT_PROPER("popen");
4423 PERL_FLUSHALL_FOR_CHILD;
4424 return safe_popen(aTHX_ cmd,mode,&sts);
4429 /*{{{ I32 my_pclose(PerlIO *fp)*/
4430 I32 Perl_my_pclose(pTHX_ PerlIO *fp)
4432 pInfo info, last = NULL;
4433 unsigned long int retsts;
4437 for (info = open_pipes; info != NULL; last = info, info = info->next)
4438 if (info->fp == fp) break;
4440 if (info == NULL) { /* no such pipe open */
4441 set_errno(ECHILD); /* quoth POSIX */
4442 set_vaxc_errno(SS$_NONEXPR);
4446 /* If we were writing to a subprocess, insure that someone reading from
4447 * the mailbox gets an EOF. It looks like a simple fclose() doesn't
4448 * produce an EOF record in the mailbox.
4450 * well, at least sometimes it *does*, so we have to watch out for
4451 * the first EOF closing the pipe (and DASSGN'ing the channel)...
4455 #if defined(USE_ITHREADS)
4458 && PL_perlio_fd_refcnt)
4459 PerlIO_flush(info->fp);
4461 fflush((FILE *)info->fp);
4464 _ckvmssts(sys$setast(0));
4465 info->closing = TRUE;
4466 done = info->done && info->in_done && info->out_done && info->err_done;
4467 /* hanging on write to Perl's input? cancel it */
4468 if (info->mode == 'r' && info->out && !info->out_done) {
4469 if (info->out->chan_out) {
4470 _ckvmssts(sys$cancel(info->out->chan_out));
4471 if (!info->out->chan_in) { /* EOF generation, need AST */
4472 _ckvmssts(sys$dclast(pipe_infromchild_ast,info->out,0));
4476 if (info->in && !info->in_done && !info->in->shut_on_empty) /* EOF if hasn't had one yet */
4477 _ckvmssts(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
4479 _ckvmssts(sys$setast(1));
4482 #if defined(USE_ITHREADS)
4485 && PL_perlio_fd_refcnt)
4486 PerlIO_close(info->fp);
4488 fclose((FILE *)info->fp);
4491 we have to wait until subprocess completes, but ALSO wait until all
4492 the i/o completes...otherwise we'll be freeing the "info" structure
4493 that the i/o ASTs could still be using...
4497 _ckvmssts(sys$setast(0));
4498 done = info->done && info->in_done && info->out_done && info->err_done;
4499 if (!done) _ckvmssts(sys$clref(pipe_ef));
4500 _ckvmssts(sys$setast(1));
4501 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4503 retsts = info->completion;
4505 /* remove from list of open pipes */
4506 _ckvmssts(sys$setast(0));
4507 if (last) last->next = info->next;
4508 else open_pipes = info->next;
4509 _ckvmssts(sys$setast(1));
4511 /* free buffers and structures */
4514 if (info->in->buf) {
4515 n = info->in->bufsize * sizeof(char);
4516 _ckvmssts(lib$free_vm(&n, &info->in->buf));
4519 _ckvmssts(lib$free_vm(&n, &info->in));
4522 if (info->out->buf) {
4523 n = info->out->bufsize * sizeof(char);
4524 _ckvmssts(lib$free_vm(&n, &info->out->buf));
4527 _ckvmssts(lib$free_vm(&n, &info->out));
4530 if (info->err->buf) {
4531 n = info->err->bufsize * sizeof(char);
4532 _ckvmssts(lib$free_vm(&n, &info->err->buf));
4535 _ckvmssts(lib$free_vm(&n, &info->err));
4538 _ckvmssts(lib$free_vm(&n, &info));
4542 } /* end of my_pclose() */
4544 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
4545 /* Roll our own prototype because we want this regardless of whether
4546 * _VMS_WAIT is defined.
4548 __pid_t __vms_waitpid( __pid_t __pid, int *__stat_loc, int __options );
4550 /* sort-of waitpid; special handling of pipe clean-up for subprocesses
4551 created with popen(); otherwise partially emulate waitpid() unless
4552 we have a suitable one from the CRTL that came with VMS 7.2 and later.
4553 Also check processes not considered by the CRTL waitpid().
4555 /*{{{Pid_t my_waitpid(Pid_t pid, int *statusp, int flags)*/
4557 Perl_my_waitpid(pTHX_ Pid_t pid, int *statusp, int flags)
4564 if (statusp) *statusp = 0;
4566 for (info = open_pipes; info != NULL; info = info->next)
4567 if (info->pid == pid) break;
4569 if (info != NULL) { /* we know about this child */
4570 while (!info->done) {
4571 _ckvmssts(sys$setast(0));
4573 if (!done) _ckvmssts(sys$clref(pipe_ef));
4574 _ckvmssts(sys$setast(1));
4575 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4578 if (statusp) *statusp = info->completion;
4582 /* child that already terminated? */
4584 for (j = 0; j < NKEEPCLOSED && j < closed_num; j++) {
4585 if (closed_list[j].pid == pid) {
4586 if (statusp) *statusp = closed_list[j].completion;
4591 /* fall through if this child is not one of our own pipe children */
4593 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
4595 /* waitpid() became available in the CRTL as of VMS 7.0, but only
4596 * in 7.2 did we get a version that fills in the VMS completion
4597 * status as Perl has always tried to do.
4600 sts = __vms_waitpid( pid, statusp, flags );
4602 if ( sts == 0 || !(sts == -1 && errno == ECHILD) )
4605 /* If the real waitpid tells us the child does not exist, we
4606 * fall through here to implement waiting for a child that
4607 * was created by some means other than exec() (say, spawned
4608 * from DCL) or to wait for a process that is not a subprocess
4609 * of the current process.
4612 #endif /* defined(__CRTL_VER) && __CRTL_VER >= 70200000 */
4615 $DESCRIPTOR(intdsc,"0 00:00:01");
4616 unsigned long int ownercode = JPI$_OWNER, ownerpid;
4617 unsigned long int pidcode = JPI$_PID, mypid;
4618 unsigned long int interval[2];
4619 unsigned int jpi_iosb[2];
4620 struct itmlst_3 jpilist[2] = {
4621 {sizeof(ownerpid), JPI$_OWNER, &ownerpid, 0},
4626 /* Sorry folks, we don't presently implement rooting around for
4627 the first child we can find, and we definitely don't want to
4628 pass a pid of -1 to $getjpi, where it is a wildcard operation.
4634 /* Get the owner of the child so I can warn if it's not mine. If the
4635 * process doesn't exist or I don't have the privs to look at it,
4636 * I can go home early.
4638 sts = sys$getjpiw(0,&pid,NULL,&jpilist,&jpi_iosb,NULL,NULL);
4639 if (sts & 1) sts = jpi_iosb[0];
4651 set_vaxc_errno(sts);
4655 if (ckWARN(WARN_EXEC)) {
4656 /* remind folks they are asking for non-standard waitpid behavior */
4657 _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
4658 if (ownerpid != mypid)
4659 Perl_warner(aTHX_ packWARN(WARN_EXEC),
4660 "waitpid: process %x is not a child of process %x",
4664 /* simply check on it once a second until it's not there anymore. */
4666 _ckvmssts(sys$bintim(&intdsc,interval));
4667 while ((sts=lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0)) & 1) {
4668 _ckvmssts(sys$schdwk(0,0,interval,0));
4669 _ckvmssts(sys$hiber());
4671 if (sts == SS$_NONEXPR) sts = SS$_NORMAL;
4676 } /* end of waitpid() */
4681 /*{{{ char *my_gconvert(double val, int ndig, int trail, char *buf) */
4683 my_gconvert(double val, int ndig, int trail, char *buf)
4685 static char __gcvtbuf[DBL_DIG+1];
4688 loc = buf ? buf : __gcvtbuf;
4690 #ifndef __DECC /* VAXCRTL gcvt uses E format for numbers < 1 */
4692 sprintf(loc,"%.*g",ndig,val);
4698 if (!buf && ndig > DBL_DIG) ndig = DBL_DIG;
4699 return gcvt(val,ndig,loc);
4702 loc[0] = '0'; loc[1] = '\0';
4709 #if defined(__VAX) || !defined(NAML$C_MAXRSS)
4710 static int rms_free_search_context(struct FAB * fab)
4714 nam = fab->fab$l_nam;
4715 nam->nam$b_nop |= NAM$M_SYNCHK;
4716 nam->nam$l_rlf = NULL;
4718 return sys$parse(fab, NULL, NULL);
4721 #define rms_setup_nam(nam) struct NAM nam = cc$rms_nam
4722 #define rms_clear_nam_nop(nam) nam.nam$b_nop = 0;
4723 #define rms_set_nam_nop(nam, opt) nam.nam$b_nop |= (opt)
4724 #define rms_set_nam_fnb(nam, opt) nam.nam$l_fnb |= (opt)
4725 #define rms_is_nam_fnb(nam, opt) (nam.nam$l_fnb & (opt))
4726 #define rms_nam_esll(nam) nam.nam$b_esl
4727 #define rms_nam_esl(nam) nam.nam$b_esl
4728 #define rms_nam_name(nam) nam.nam$l_name
4729 #define rms_nam_namel(nam) nam.nam$l_name
4730 #define rms_nam_type(nam) nam.nam$l_type
4731 #define rms_nam_typel(nam) nam.nam$l_type
4732 #define rms_nam_ver(nam) nam.nam$l_ver
4733 #define rms_nam_verl(nam) nam.nam$l_ver
4734 #define rms_nam_rsll(nam) nam.nam$b_rsl
4735 #define rms_nam_rsl(nam) nam.nam$b_rsl
4736 #define rms_bind_fab_nam(fab, nam) fab.fab$l_nam = &nam
4737 #define rms_set_fna(fab, nam, name, size) \
4738 { fab.fab$b_fns = size; fab.fab$l_fna = name; }
4739 #define rms_get_fna(fab, nam) fab.fab$l_fna
4740 #define rms_set_dna(fab, nam, name, size) \
4741 { fab.fab$b_dns = size; fab.fab$l_dna = name; }
4742 #define rms_nam_dns(fab, nam) fab.fab$b_dns
4743 #define rms_set_esa(fab, nam, name, size) \
4744 { nam.nam$b_ess = size; nam.nam$l_esa = name; }
4745 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
4746 { nam.nam$l_esa = s_name; nam.nam$b_ess = s_size;}
4747 #define rms_set_rsa(nam, name, size) \
4748 { nam.nam$l_rsa = name; nam.nam$b_rss = size; }
4749 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
4750 { nam.nam$l_rsa = s_name; nam.nam$b_rss = s_size; }
4751 #define rms_nam_name_type_l_size(nam) \
4752 (nam.nam$b_name + nam.nam$b_type)
4754 static int rms_free_search_context(struct FAB * fab)
4758 nam = fab->fab$l_naml;
4759 nam->naml$b_nop |= NAM$M_SYNCHK;
4760 nam->naml$l_rlf = NULL;
4761 nam->naml$l_long_defname_size = 0;
4764 return sys$parse(fab, NULL, NULL);
4767 #define rms_setup_nam(nam) struct NAML nam = cc$rms_naml
4768 #define rms_clear_nam_nop(nam) nam.naml$b_nop = 0;
4769 #define rms_set_nam_nop(nam, opt) nam.naml$b_nop |= (opt)
4770 #define rms_set_nam_fnb(nam, opt) nam.naml$l_fnb |= (opt)
4771 #define rms_is_nam_fnb(nam, opt) (nam.naml$l_fnb & (opt))
4772 #define rms_nam_esll(nam) nam.naml$l_long_expand_size
4773 #define rms_nam_esl(nam) nam.naml$b_esl
4774 #define rms_nam_name(nam) nam.naml$l_name
4775 #define rms_nam_namel(nam) nam.naml$l_long_name
4776 #define rms_nam_type(nam) nam.naml$l_type
4777 #define rms_nam_typel(nam) nam.naml$l_long_type
4778 #define rms_nam_ver(nam) nam.naml$l_ver
4779 #define rms_nam_verl(nam) nam.naml$l_long_ver
4780 #define rms_nam_rsll(nam) nam.naml$l_long_result_size
4781 #define rms_nam_rsl(nam) nam.naml$b_rsl
4782 #define rms_bind_fab_nam(fab, nam) fab.fab$l_naml = &nam
4783 #define rms_set_fna(fab, nam, name, size) \
4784 { fab.fab$b_fns = 0; fab.fab$l_fna = (char *) -1; \
4785 nam.naml$l_long_filename_size = size; \
4786 nam.naml$l_long_filename = name;}
4787 #define rms_get_fna(fab, nam) nam.naml$l_long_filename
4788 #define rms_set_dna(fab, nam, name, size) \
4789 { fab.fab$b_dns = 0; fab.fab$l_dna = (char *) -1; \
4790 nam.naml$l_long_defname_size = size; \
4791 nam.naml$l_long_defname = name; }
4792 #define rms_nam_dns(fab, nam) nam.naml$l_long_defname_size
4793 #define rms_set_esa(fab, nam, name, size) \
4794 { nam.naml$b_ess = 0; nam.naml$l_esa = (char *) -1; \
4795 nam.naml$l_long_expand_alloc = size; \
4796 nam.naml$l_long_expand = name; }
4797 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
4798 { nam.naml$l_esa = s_name; nam.naml$b_ess = s_size; \
4799 nam.naml$l_long_expand = l_name; \
4800 nam.naml$l_long_expand_alloc = l_size; }
4801 #define rms_set_rsa(nam, name, size) \
4802 { nam.naml$l_rsa = NULL; nam.naml$b_rss = 0; \
4803 nam.naml$l_long_result = name; \
4804 nam.naml$l_long_result_alloc = size; }
4805 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
4806 { nam.naml$l_rsa = s_name; nam.naml$b_rss = s_size; \
4807 nam.naml$l_long_result = l_name; \
4808 nam.naml$l_long_result_alloc = l_size; }
4809 #define rms_nam_name_type_l_size(nam) \
4810 (nam.naml$l_long_name_size + nam.naml$l_long_type_size)
4815 * The CRTL for 8.3 and later can create symbolic links in any mode,
4816 * however in 8.3 the unlink/remove/delete routines will only properly handle
4817 * them if one of the PCP modes is active.
4819 static int rms_erase(const char * vmsname)
4822 struct FAB myfab = cc$rms_fab;
4823 rms_setup_nam(mynam);
4825 rms_set_fna(myfab, mynam, (char *)vmsname, strlen(vmsname)); /* cast ok */
4826 rms_bind_fab_nam(myfab, mynam);
4828 /* Are we removing all versions? */
4829 if (vms_unlink_all_versions == 1) {
4830 const char * defspec = ";*";
4831 rms_set_dna(myfab, mynam, (char *)defspec, strlen(defspec)); /* cast ok */
4834 #ifdef NAML$M_OPEN_SPECIAL
4835 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
4838 status = SYS$ERASE(&myfab, 0, 0);
4845 vms_rename_with_acl(pTHX_ const struct dsc$descriptor_s * vms_src_dsc,
4846 const struct dsc$descriptor_s * vms_dst_dsc,
4847 unsigned long flags)
4849 /* VMS and UNIX handle file permissions differently and the
4850 * the same ACL trick may be needed for renaming files,
4851 * especially if they are directories.
4854 /* todo: get kill_file and rename to share common code */
4855 /* I can not find online documentation for $change_acl
4856 * it appears to be replaced by $set_security some time ago */
4858 const unsigned int access_mode = 0;
4859 $DESCRIPTOR(obj_file_dsc,"FILE");
4862 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
4863 int aclsts, fndsts, rnsts = -1;
4864 unsigned int ctx = 0;
4865 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
4866 struct dsc$descriptor_s * clean_dsc;
4869 unsigned char myace$b_length;
4870 unsigned char myace$b_type;
4871 unsigned short int myace$w_flags;
4872 unsigned long int myace$l_access;
4873 unsigned long int myace$l_ident;
4874 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
4875 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL,
4877 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
4880 findlst[3] = {{sizeof oldace, OSS$_ACL_FIND_ENTRY, &oldace, 0},
4881 {sizeof oldace, OSS$_ACL_READ_ENTRY, &oldace, 0},
4883 addlst[2] = {{sizeof newace, OSS$_ACL_ADD_ENTRY, &newace, 0},{0,0,0,0}},
4884 dellst[2] = {{sizeof newace, OSS$_ACL_DELETE_ENTRY, &newace, 0},
4888 /* Expand the input spec using RMS, since we do not want to put
4889 * ACLs on the target of a symbolic link */
4890 vmsname = PerlMem_malloc(NAM$C_MAXRSS+1);
4891 if (vmsname == NULL)
4894 rslt = do_rmsexpand(vms_src_dsc->dsc$a_pointer,
4898 PERL_RMSEXPAND_M_VMS | PERL_RMSEXPAND_M_SYMLINK,
4902 PerlMem_free(vmsname);
4906 /* So we get our own UIC to use as a rights identifier,
4907 * and the insert an ACE at the head of the ACL which allows us
4908 * to delete the file.
4910 _ckvmssts(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
4912 fildsc.dsc$w_length = strlen(vmsname);
4913 fildsc.dsc$a_pointer = vmsname;
4915 newace.myace$l_ident = oldace.myace$l_ident;
4918 /* Grab any existing ACEs with this identifier in case we fail */
4919 clean_dsc = &fildsc;
4920 aclsts = fndsts = sys$get_security(&obj_file_dsc,
4928 if ($VMS_STATUS_SUCCESS(fndsts) || (fndsts == SS$_ACLEMPTY)) {
4929 /* Add the new ACE . . . */
4931 /* if the sys$get_security succeeded, then ctx is valid, and the
4932 * object/file descriptors will be ignored. But otherwise they
4935 aclsts = sys$set_security(&obj_file_dsc, &fildsc, NULL,
4936 OSS$M_RELCTX, addlst, &ctx, &access_mode);
4937 if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) {
4939 set_vaxc_errno(aclsts);
4940 PerlMem_free(vmsname);
4944 rnsts = lib$rename_file(vms_src_dsc, vms_dst_dsc,
4947 NULL, NULL, NULL, NULL, NULL, NULL, NULL);
4949 if ($VMS_STATUS_SUCCESS(rnsts)) {
4950 clean_dsc = (struct dsc$descriptor_s *)vms_dst_dsc;
4953 /* Put things back the way they were. */
4955 aclsts = sys$get_security(&obj_file_dsc,
4963 if ($VMS_STATUS_SUCCESS(aclsts)) {
4967 if (!$VMS_STATUS_SUCCESS(fndsts))
4968 sec_flags = OSS$M_RELCTX;
4970 /* Get rid of the new ACE */
4971 aclsts = sys$set_security(NULL, NULL, NULL,
4972 sec_flags, dellst, &ctx, &access_mode);
4974 /* If there was an old ACE, put it back */
4975 if ($VMS_STATUS_SUCCESS(aclsts) && $VMS_STATUS_SUCCESS(fndsts)) {
4976 addlst[0].bufadr = &oldace;
4977 aclsts = sys$set_security(NULL, NULL, NULL,
4978 OSS$M_RELCTX, addlst, &ctx, &access_mode);
4979 if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) {
4981 set_vaxc_errno(aclsts);
4987 /* Try to clear the lock on the ACL list */
4988 aclsts2 = sys$set_security(NULL, NULL, NULL,
4989 OSS$M_RELCTX, NULL, &ctx, &access_mode);
4991 /* Rename errors are most important */
4992 if (!$VMS_STATUS_SUCCESS(rnsts))
4995 set_vaxc_errno(aclsts);
5000 if (aclsts != SS$_ACLEMPTY)
5007 PerlMem_free(vmsname);
5012 /*{{{int rename(const char *, const char * */
5013 /* Not exactly what X/Open says to do, but doing it absolutely right
5014 * and efficiently would require a lot more work. This should be close
5015 * enough to pass all but the most strict X/Open compliance test.
5018 Perl_rename(pTHX_ const char *src, const char * dst)
5027 /* Validate the source file */
5028 src_sts = flex_lstat(src, &src_st);
5031 /* No source file or other problem */
5035 dst_sts = flex_lstat(dst, &dst_st);
5038 if (dst_st.st_dev != src_st.st_dev) {
5039 /* Must be on the same device */
5044 /* VMS_INO_T_COMPARE is true if the inodes are different
5045 * to match the output of memcmp
5048 if (!VMS_INO_T_COMPARE(src_st.st_ino, dst_st.st_ino)) {
5049 /* That was easy, the files are the same! */
5053 if (S_ISDIR(src_st.st_mode) && !S_ISDIR(dst_st.st_mode)) {
5054 /* If source is a directory, so must be dest */
5062 if ((dst_sts == 0) &&
5063 (vms_unlink_all_versions || S_ISDIR(dst_st.st_mode))) {
5065 /* We have issues here if vms_unlink_all_versions is set
5066 * If the destination exists, and is not a directory, then
5067 * we must delete in advance.
5069 * If the src is a directory, then we must always pre-delete
5072 * If we successfully delete the dst in advance, and the rename fails
5073 * X/Open requires that errno be EIO.
5077 if (!S_ISDIR(dst_st.st_mode) || S_ISDIR(src_st.st_mode)) {
5079 d_sts = mp_do_kill_file(aTHX_ dst, S_ISDIR(dst_st.st_mode));
5083 /* We killed the destination, so only errno now is EIO */
5088 /* Originally the idea was to call the CRTL rename() and only
5089 * try the lib$rename_file if it failed.
5090 * It turns out that there are too many variants in what the
5091 * the CRTL rename might do, so only use lib$rename_file
5096 /* Is the source and dest both in VMS format */
5097 /* if the source is a directory, then need to fileify */
5098 /* and dest must be a directory or non-existant. */
5104 unsigned long flags;
5105 struct dsc$descriptor_s old_file_dsc;
5106 struct dsc$descriptor_s new_file_dsc;
5108 /* We need to modify the src and dst depending
5109 * on if one or more of them are directories.
5112 vms_src = PerlMem_malloc(VMS_MAXRSS);
5113 if (vms_src == NULL)
5114 _ckvmssts(SS$_INSFMEM);
5116 /* Source is always a VMS format file */
5117 ret_str = do_tovmsspec(src, vms_src, 0, NULL);
5118 if (ret_str == NULL) {
5119 PerlMem_free(vms_src);
5124 vms_dst = PerlMem_malloc(VMS_MAXRSS);
5125 if (vms_dst == NULL)
5126 _ckvmssts(SS$_INSFMEM);
5128 if (S_ISDIR(src_st.st_mode)) {
5130 char * vms_dir_file;
5132 vms_dir_file = PerlMem_malloc(VMS_MAXRSS);
5133 if (vms_dir_file == NULL)
5134 _ckvmssts(SS$_INSFMEM);
5136 /* The source must be a file specification */
5137 ret_str = do_fileify_dirspec(vms_src, vms_dir_file, 0, NULL);
5138 if (ret_str == NULL) {
5139 PerlMem_free(vms_src);
5140 PerlMem_free(vms_dst);
5141 PerlMem_free(vms_dir_file);
5145 PerlMem_free(vms_src);
5146 vms_src = vms_dir_file;
5148 /* If the dest is a directory, we must remove it
5151 d_sts = mp_do_kill_file(aTHX_ dst, 1);
5153 PerlMem_free(vms_src);
5154 PerlMem_free(vms_dst);
5162 /* The dest must be a VMS file specification */
5163 ret_str = do_tovmsspec(dst, vms_dst, 0, NULL);
5164 if (ret_str == NULL) {
5165 PerlMem_free(vms_src);
5166 PerlMem_free(vms_dst);
5171 /* The source must be a file specification */
5172 vms_dir_file = PerlMem_malloc(VMS_MAXRSS);
5173 if (vms_dir_file == NULL)
5174 _ckvmssts(SS$_INSFMEM);
5176 ret_str = do_fileify_dirspec(vms_dst, vms_dir_file, 0, NULL);
5177 if (ret_str == NULL) {
5178 PerlMem_free(vms_src);
5179 PerlMem_free(vms_dst);
5180 PerlMem_free(vms_dir_file);
5184 PerlMem_free(vms_dst);
5185 vms_dst = vms_dir_file;
5188 /* File to file or file to new dir */
5190 if ((dst_sts == 0) && S_ISDIR(dst_st.st_mode)) {
5191 /* VMS pathify a dir target */
5192 ret_str = do_tovmspath(dst, vms_dst, 0, NULL);
5193 if (ret_str == NULL) {
5194 PerlMem_free(vms_src);
5195 PerlMem_free(vms_dst);
5201 /* fileify a target VMS file specification */
5202 ret_str = do_tovmsspec(dst, vms_dst, 0, NULL);
5203 if (ret_str == NULL) {
5204 PerlMem_free(vms_src);
5205 PerlMem_free(vms_dst);
5212 old_file_dsc.dsc$a_pointer = vms_src;
5213 old_file_dsc.dsc$w_length = strlen(vms_src);
5214 old_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
5215 old_file_dsc.dsc$b_class = DSC$K_CLASS_S;
5217 new_file_dsc.dsc$a_pointer = vms_dst;
5218 new_file_dsc.dsc$w_length = strlen(vms_dst);
5219 new_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
5220 new_file_dsc.dsc$b_class = DSC$K_CLASS_S;
5223 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5224 flags |= 2; /* LIB$M_FIL_LONG_NAMES */
5227 sts = lib$rename_file(&old_file_dsc,
5231 NULL, NULL, NULL, NULL, NULL, NULL, NULL);
5232 if (!$VMS_STATUS_SUCCESS(sts)) {
5234 /* We could have failed because VMS style permissions do not
5235 * permit renames that UNIX will allow. Just like the hack
5238 sts = vms_rename_with_acl(aTHX_ &old_file_dsc, &new_file_dsc, flags);
5241 PerlMem_free(vms_src);
5242 PerlMem_free(vms_dst);
5243 if (!$VMS_STATUS_SUCCESS(sts)) {
5250 if (vms_unlink_all_versions) {
5251 /* Now get rid of any previous versions of the source file that
5256 src_sts = mp_do_kill_file(aTHX_ src, S_ISDIR(src_st.st_mode));
5260 /* We deleted the destination, so must force the error to be EIO */
5261 if ((retval != 0) && (pre_delete != 0))
5269 /*{{{char *do_rmsexpand(char *fspec, char *out, int ts, char *def, unsigned opts)*/
5270 /* Shortcut for common case of simple calls to $PARSE and $SEARCH
5271 * to expand file specification. Allows for a single default file
5272 * specification and a simple mask of options. If outbuf is non-NULL,
5273 * it must point to a buffer at least NAM$C_MAXRSS bytes long, into which
5274 * the resultant file specification is placed. If outbuf is NULL, the
5275 * resultant file specification is placed into a static buffer.
5276 * The third argument, if non-NULL, is taken to be a default file
5277 * specification string. The fourth argument is unused at present.
5278 * rmesexpand() returns the address of the resultant string if
5279 * successful, and NULL on error.
5281 * New functionality for previously unused opts value:
5282 * PERL_RMSEXPAND_M_VMS - Force output file specification to VMS format.
5283 * PERL_RMSEXPAND_M_LONG - Want output in long formst
5284 * PERL_RMSEXPAND_M_VMS_IN - Input is already in VMS, so do not vmsify
5285 * PERL_RMSEXPAND_M_SYMLINK - Use symbolic link, not target
5287 static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *);
5291 (pTHX_ const char *filespec,
5294 const char *defspec,
5299 static char __rmsexpand_retbuf[VMS_MAXRSS];
5300 char * vmsfspec, *tmpfspec;
5301 char * esa, *cp, *out = NULL;
5305 struct FAB myfab = cc$rms_fab;
5306 rms_setup_nam(mynam);
5308 unsigned long int retsts, trimver, trimtype, haslower = 0, isunix = 0;
5311 /* temp hack until UTF8 is actually implemented */
5312 if (fs_utf8 != NULL)
5315 if (!filespec || !*filespec) {
5316 set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL);
5320 if (ts) out = Newx(outbuf,VMS_MAXRSS,char);
5321 else outbuf = __rmsexpand_retbuf;
5329 if ((opts & PERL_RMSEXPAND_M_VMS_IN) == 0) {
5330 isunix = is_unix_filespec(filespec);
5332 vmsfspec = PerlMem_malloc(VMS_MAXRSS);
5333 if (vmsfspec == NULL) _ckvmssts(SS$_INSFMEM);
5334 if (do_tovmsspec(filespec,vmsfspec,0,fs_utf8) == NULL) {
5335 PerlMem_free(vmsfspec);
5340 filespec = vmsfspec;
5342 /* Unless we are forcing to VMS format, a UNIX input means
5343 * UNIX output, and that requires long names to be used
5345 if ((opts & PERL_RMSEXPAND_M_VMS) == 0)
5346 opts |= PERL_RMSEXPAND_M_LONG;
5353 rms_set_fna(myfab, mynam, (char *)filespec, strlen(filespec)); /* cast ok */
5354 rms_bind_fab_nam(myfab, mynam);
5356 if (defspec && *defspec) {
5358 t_isunix = is_unix_filespec(defspec);
5360 tmpfspec = PerlMem_malloc(VMS_MAXRSS);
5361 if (tmpfspec == NULL) _ckvmssts(SS$_INSFMEM);
5362 if (do_tovmsspec(defspec,tmpfspec,0,dfs_utf8) == NULL) {
5363 PerlMem_free(tmpfspec);
5364 if (vmsfspec != NULL)
5365 PerlMem_free(vmsfspec);
5372 rms_set_dna(myfab, mynam, (char *)defspec, strlen(defspec)); /* cast ok */
5375 esa = PerlMem_malloc(NAM$C_MAXRSS + 1);
5376 if (esa == NULL) _ckvmssts(SS$_INSFMEM);
5377 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5378 esal = PerlMem_malloc(VMS_MAXRSS);
5379 if (esal == NULL) _ckvmssts(SS$_INSFMEM);
5381 rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS-1);
5383 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5384 rms_set_rsa(mynam, outbuf, (VMS_MAXRSS - 1));
5387 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5388 outbufl = PerlMem_malloc(VMS_MAXRSS);
5389 if (outbufl == NULL) _ckvmssts(SS$_INSFMEM);
5390 rms_set_rsal(mynam, outbuf, NAM$C_MAXRSS, outbufl, (VMS_MAXRSS - 1));
5392 rms_set_rsa(mynam, outbuf, NAM$C_MAXRSS);
5396 #ifdef NAM$M_NO_SHORT_UPCASE
5397 if (decc_efs_case_preserve)
5398 rms_set_nam_nop(mynam, NAM$M_NO_SHORT_UPCASE);
5401 /* We may not want to follow symbolic links */
5402 #ifdef NAML$M_OPEN_SPECIAL
5403 if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0)
5404 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5407 /* First attempt to parse as an existing file */
5408 retsts = sys$parse(&myfab,0,0);
5409 if (!(retsts & STS$K_SUCCESS)) {
5411 /* Could not find the file, try as syntax only if error is not fatal */
5412 rms_set_nam_nop(mynam, NAM$M_SYNCHK);
5413 if (retsts == RMS$_DNF || retsts == RMS$_DIR || retsts == RMS$_DEV) {
5414 retsts = sys$parse(&myfab,0,0);
5415 if (retsts & STS$K_SUCCESS) goto expanded;
5418 /* Still could not parse the file specification */
5419 /*----------------------------------------------*/
5420 sts = rms_free_search_context(&myfab); /* Free search context */
5421 if (out) Safefree(out);
5422 if (tmpfspec != NULL)
5423 PerlMem_free(tmpfspec);
5424 if (vmsfspec != NULL)
5425 PerlMem_free(vmsfspec);
5426 if (outbufl != NULL)
5427 PerlMem_free(outbufl);
5431 set_vaxc_errno(retsts);
5432 if (retsts == RMS$_PRV) set_errno(EACCES);
5433 else if (retsts == RMS$_DEV) set_errno(ENODEV);
5434 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
5435 else set_errno(EVMSERR);
5438 retsts = sys$search(&myfab,0,0);
5439 if (!(retsts & STS$K_SUCCESS) && retsts != RMS$_FNF) {
5440 sts = rms_free_search_context(&myfab); /* Free search context */
5441 if (out) Safefree(out);
5442 if (tmpfspec != NULL)
5443 PerlMem_free(tmpfspec);
5444 if (vmsfspec != NULL)
5445 PerlMem_free(vmsfspec);
5446 if (outbufl != NULL)
5447 PerlMem_free(outbufl);
5451 set_vaxc_errno(retsts);
5452 if (retsts == RMS$_PRV) set_errno(EACCES);
5453 else set_errno(EVMSERR);
5457 /* If the input filespec contained any lowercase characters,
5458 * downcase the result for compatibility with Unix-minded code. */
5460 if (!decc_efs_case_preserve) {
5461 for (tbuf = rms_get_fna(myfab, mynam); *tbuf; tbuf++)
5462 if (islower(*tbuf)) { haslower = 1; break; }
5465 /* Is a long or a short name expected */
5466 /*------------------------------------*/
5467 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5468 if (rms_nam_rsll(mynam)) {
5470 speclen = rms_nam_rsll(mynam);
5473 tbuf = esal; /* Not esa */
5474 speclen = rms_nam_esll(mynam);
5478 if (rms_nam_rsl(mynam)) {
5480 speclen = rms_nam_rsl(mynam);
5483 tbuf = esa; /* Not esal */
5484 speclen = rms_nam_esl(mynam);
5487 tbuf[speclen] = '\0';
5489 /* Trim off null fields added by $PARSE
5490 * If type > 1 char, must have been specified in original or default spec
5491 * (not true for version; $SEARCH may have added version of existing file).
5493 trimver = !rms_is_nam_fnb(mynam, NAM$M_EXP_VER);
5494 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5495 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
5496 ((rms_nam_verl(mynam) - rms_nam_typel(mynam)) == 1);
5499 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
5500 ((rms_nam_ver(mynam) - rms_nam_type(mynam)) == 1);
5502 if (trimver || trimtype) {
5503 if (defspec && *defspec) {
5504 char *defesal = NULL;
5505 defesal = PerlMem_malloc(VMS_MAXRSS + 1);
5506 if (defesal != NULL) {
5507 struct FAB deffab = cc$rms_fab;
5508 rms_setup_nam(defnam);
5510 rms_bind_fab_nam(deffab, defnam);
5514 (deffab, defnam, (char *)defspec, rms_nam_dns(myfab, mynam));
5516 rms_set_esa(deffab, defnam, defesal, VMS_MAXRSS - 1);
5518 rms_clear_nam_nop(defnam);
5519 rms_set_nam_nop(defnam, NAM$M_SYNCHK);
5520 #ifdef NAM$M_NO_SHORT_UPCASE
5521 if (decc_efs_case_preserve)
5522 rms_set_nam_nop(defnam, NAM$M_NO_SHORT_UPCASE);
5524 #ifdef NAML$M_OPEN_SPECIAL
5525 if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0)
5526 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5528 if (sys$parse(&deffab,0,0) & STS$K_SUCCESS) {
5530 trimver = !rms_is_nam_fnb(defnam, NAM$M_EXP_VER);
5533 trimtype = !rms_is_nam_fnb(defnam, NAM$M_EXP_TYPE);
5536 PerlMem_free(defesal);
5540 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5541 if (*(rms_nam_verl(mynam)) != '\"')
5542 speclen = rms_nam_verl(mynam) - tbuf;
5545 if (*(rms_nam_ver(mynam)) != '\"')
5546 speclen = rms_nam_ver(mynam) - tbuf;
5550 /* If we didn't already trim version, copy down */
5551 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5552 if (speclen > rms_nam_verl(mynam) - tbuf)
5554 (rms_nam_typel(mynam),
5555 rms_nam_verl(mynam),
5556 speclen - (rms_nam_verl(mynam) - tbuf));
5557 speclen -= rms_nam_verl(mynam) - rms_nam_typel(mynam);
5560 if (speclen > rms_nam_ver(mynam) - tbuf)
5562 (rms_nam_type(mynam),
5564 speclen - (rms_nam_ver(mynam) - tbuf));
5565 speclen -= rms_nam_ver(mynam) - rms_nam_type(mynam);
5570 /* Done with these copies of the input files */
5571 /*-------------------------------------------*/
5572 if (vmsfspec != NULL)
5573 PerlMem_free(vmsfspec);
5574 if (tmpfspec != NULL)
5575 PerlMem_free(tmpfspec);
5577 /* If we just had a directory spec on input, $PARSE "helpfully"
5578 * adds an empty name and type for us */
5579 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5580 if (rms_nam_namel(mynam) == rms_nam_typel(mynam) &&
5581 rms_nam_verl(mynam) == rms_nam_typel(mynam) + 1 &&
5582 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
5583 speclen = rms_nam_namel(mynam) - tbuf;
5586 if (rms_nam_name(mynam) == rms_nam_type(mynam) &&
5587 rms_nam_ver(mynam) == rms_nam_ver(mynam) + 1 &&
5588 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
5589 speclen = rms_nam_name(mynam) - tbuf;
5592 /* Posix format specifications must have matching quotes */
5593 if (speclen < (VMS_MAXRSS - 1)) {
5594 if (decc_posix_compliant_pathnames && (tbuf[0] == '\"')) {
5595 if ((speclen > 1) && (tbuf[speclen-1] != '\"')) {
5596 tbuf[speclen] = '\"';
5601 tbuf[speclen] = '\0';
5602 if (haslower && !decc_efs_case_preserve) __mystrtolower(tbuf);
5604 /* Have we been working with an expanded, but not resultant, spec? */
5605 /* Also, convert back to Unix syntax if necessary. */
5607 if (!rms_nam_rsll(mynam)) {
5609 if (do_tounixspec(tbuf, outbuf ,0 , fs_utf8) == NULL) {
5610 if (out) Safefree(out);
5614 if (outbufl != NULL)
5615 PerlMem_free(outbufl);
5619 else strcpy(outbuf, tbuf);
5622 tmpfspec = PerlMem_malloc(VMS_MAXRSS);
5623 if (tmpfspec == NULL) _ckvmssts(SS$_INSFMEM);
5624 if (do_tounixspec(outbuf,tmpfspec,0,fs_utf8) == NULL) {
5625 if (out) Safefree(out);
5629 PerlMem_free(tmpfspec);
5630 if (outbufl != NULL)
5631 PerlMem_free(outbufl);
5634 strcpy(outbuf,tmpfspec);
5635 PerlMem_free(tmpfspec);
5638 rms_set_rsal(mynam, NULL, 0, NULL, 0);
5639 sts = rms_free_search_context(&myfab); /* Free search context */
5643 if (outbufl != NULL)
5644 PerlMem_free(outbufl);
5648 /* External entry points */
5649 char *Perl_rmsexpand(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
5650 { return do_rmsexpand(spec,buf,0,def,opt,NULL,NULL); }
5651 char *Perl_rmsexpand_ts(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
5652 { return do_rmsexpand(spec,buf,1,def,opt,NULL,NULL); }
5653 char *Perl_rmsexpand_utf8
5654 (pTHX_ const char *spec, char *buf, const char *def,
5655 unsigned opt, int * fs_utf8, int * dfs_utf8)
5656 { return do_rmsexpand(spec,buf,0,def,opt, fs_utf8, dfs_utf8); }
5657 char *Perl_rmsexpand_utf8_ts
5658 (pTHX_ const char *spec, char *buf, const char *def,
5659 unsigned opt, int * fs_utf8, int * dfs_utf8)
5660 { return do_rmsexpand(spec,buf,1,def,opt, fs_utf8, dfs_utf8); }
5664 ** The following routines are provided to make life easier when
5665 ** converting among VMS-style and Unix-style directory specifications.
5666 ** All will take input specifications in either VMS or Unix syntax. On
5667 ** failure, all return NULL. If successful, the routines listed below
5668 ** return a pointer to a buffer containing the appropriately
5669 ** reformatted spec (and, therefore, subsequent calls to that routine
5670 ** will clobber the result), while the routines of the same names with
5671 ** a _ts suffix appended will return a pointer to a mallocd string
5672 ** containing the appropriately reformatted spec.
5673 ** In all cases, only explicit syntax is altered; no check is made that
5674 ** the resulting string is valid or that the directory in question
5677 ** fileify_dirspec() - convert a directory spec into the name of the
5678 ** directory file (i.e. what you can stat() to see if it's a dir).
5679 ** The style (VMS or Unix) of the result is the same as the style
5680 ** of the parameter passed in.
5681 ** pathify_dirspec() - convert a directory spec into a path (i.e.
5682 ** what you prepend to a filename to indicate what directory it's in).
5683 ** The style (VMS or Unix) of the result is the same as the style
5684 ** of the parameter passed in.
5685 ** tounixpath() - convert a directory spec into a Unix-style path.
5686 ** tovmspath() - convert a directory spec into a VMS-style path.
5687 ** tounixspec() - convert any file spec into a Unix-style file spec.
5688 ** tovmsspec() - convert any file spec into a VMS-style spec.
5689 ** xxxxx_utf8() - Variants that support UTF8 encoding of Unix-Style file spec.
5691 ** Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>
5692 ** Permission is given to distribute this code as part of the Perl
5693 ** standard distribution under the terms of the GNU General Public
5694 ** License or the Perl Artistic License. Copies of each may be
5695 ** found in the Perl standard distribution.
5698 /*{{{ char *fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/
5699 static char *mp_do_fileify_dirspec(pTHX_ const char *dir,char *buf,int ts, int *utf8_fl)
5701 static char __fileify_retbuf[VMS_MAXRSS];
5702 unsigned long int dirlen, retlen, addmfd = 0, hasfilename = 0;
5703 char *retspec, *cp1, *cp2, *lastdir;
5704 char *trndir, *vmsdir;
5705 unsigned short int trnlnm_iter_count;
5707 if (utf8_fl != NULL)
5710 if (!dir || !*dir) {
5711 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
5713 dirlen = strlen(dir);
5714 while (dirlen && dir[dirlen-1] == '/') --dirlen;
5715 if (!dirlen) { /* We had Unixish '/' -- substitute top of current tree */
5716 if (!decc_posix_compliant_pathnames && decc_disable_posix_root) {
5723 if (dirlen > (VMS_MAXRSS - 1)) {
5724 set_errno(ENAMETOOLONG); set_vaxc_errno(RMS$_SYN);
5727 trndir = PerlMem_malloc(VMS_MAXRSS + 1);
5728 if (trndir == NULL) _ckvmssts(SS$_INSFMEM);
5729 if (!strpbrk(dir+1,"/]>:") &&
5730 (!decc_posix_compliant_pathnames && decc_disable_posix_root)) {
5731 strcpy(trndir,*dir == '/' ? dir + 1: dir);
5732 trnlnm_iter_count = 0;
5733 while (!strpbrk(trndir,"/]>:") && my_trnlnm(trndir,trndir,0)) {
5734 trnlnm_iter_count++;
5735 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
5737 dirlen = strlen(trndir);
5740 strncpy(trndir,dir,dirlen);
5741 trndir[dirlen] = '\0';
5744 /* At this point we are done with *dir and use *trndir which is a
5745 * copy that can be modified. *dir must not be modified.
5748 /* If we were handed a rooted logical name or spec, treat it like a
5749 * simple directory, so that
5750 * $ Define myroot dev:[dir.]
5751 * ... do_fileify_dirspec("myroot",buf,1) ...
5752 * does something useful.
5754 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".]")) {
5755 trndir[--dirlen] = '\0';
5756 trndir[dirlen-1] = ']';
5758 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".>")) {
5759 trndir[--dirlen] = '\0';
5760 trndir[dirlen-1] = '>';
5763 if ((cp1 = strrchr(trndir,']')) != NULL || (cp1 = strrchr(trndir,'>')) != NULL) {
5764 /* If we've got an explicit filename, we can just shuffle the string. */
5765 if (*(cp1+1)) hasfilename = 1;
5766 /* Similarly, we can just back up a level if we've got multiple levels
5767 of explicit directories in a VMS spec which ends with directories. */
5769 for (cp2 = cp1; cp2 > trndir; cp2--) {
5771 if ((cp2 - 1 > trndir) && (*(cp2 - 1) != '^')) {
5772 /* fix-me, can not scan EFS file specs backward like this */
5773 *cp2 = *cp1; *cp1 = '\0';
5778 if (*cp2 == '[' || *cp2 == '<') break;
5783 vmsdir = PerlMem_malloc(VMS_MAXRSS + 1);
5784 if (vmsdir == NULL) _ckvmssts(SS$_INSFMEM);
5785 cp1 = strpbrk(trndir,"]:>");
5786 if (hasfilename || !cp1) { /* Unix-style path or filename */
5787 if (trndir[0] == '.') {
5788 if (trndir[1] == '\0' || (trndir[1] == '/' && trndir[2] == '\0')) {
5789 PerlMem_free(trndir);
5790 PerlMem_free(vmsdir);
5791 return do_fileify_dirspec("[]",buf,ts,NULL);
5793 else if (trndir[1] == '.' &&
5794 (trndir[2] == '\0' || (trndir[2] == '/' && trndir[3] == '\0'))) {
5795 PerlMem_free(trndir);
5796 PerlMem_free(vmsdir);
5797 return do_fileify_dirspec("[-]",buf,ts,NULL);
5800 if (dirlen && trndir[dirlen-1] == '/') { /* path ends with '/'; just add .dir;1 */
5801 dirlen -= 1; /* to last element */
5802 lastdir = strrchr(trndir,'/');
5804 else if ((cp1 = strstr(trndir,"/.")) != NULL) {
5805 /* If we have "/." or "/..", VMSify it and let the VMS code
5806 * below expand it, rather than repeating the code to handle
5807 * relative components of a filespec here */
5809 if (*(cp1+2) == '.') cp1++;
5810 if (*(cp1+2) == '/' || *(cp1+2) == '\0') {
5812 if (do_tovmsspec(trndir,vmsdir,0,NULL) == NULL) {
5813 PerlMem_free(trndir);
5814 PerlMem_free(vmsdir);
5817 if (strchr(vmsdir,'/') != NULL) {
5818 /* If do_tovmsspec() returned it, it must have VMS syntax
5819 * delimiters in it, so it's a mixed VMS/Unix spec. We take
5820 * the time to check this here only so we avoid a recursion
5821 * loop; otherwise, gigo.
5823 PerlMem_free(trndir);
5824 PerlMem_free(vmsdir);
5825 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN);
5828 if (do_fileify_dirspec(vmsdir,trndir,0,NULL) == NULL) {
5829 PerlMem_free(trndir);
5830 PerlMem_free(vmsdir);
5833 ret_chr = do_tounixspec(trndir,buf,ts,NULL);
5834 PerlMem_free(trndir);
5835 PerlMem_free(vmsdir);
5839 } while ((cp1 = strstr(cp1,"/.")) != NULL);
5840 lastdir = strrchr(trndir,'/');
5842 else if (dirlen >= 7 && !strcmp(&trndir[dirlen-7],"/000000")) {
5844 /* Ditto for specs that end in an MFD -- let the VMS code
5845 * figure out whether it's a real device or a rooted logical. */
5847 /* This should not happen any more. Allowing the fake /000000
5848 * in a UNIX pathname causes all sorts of problems when trying
5849 * to run in UNIX emulation. So the VMS to UNIX conversions
5850 * now remove the fake /000000 directories.
5853 trndir[dirlen] = '/'; trndir[dirlen+1] = '\0';
5854 if (do_tovmsspec(trndir,vmsdir,0,NULL) == NULL) {
5855 PerlMem_free(trndir);
5856 PerlMem_free(vmsdir);
5859 if (do_fileify_dirspec(vmsdir,trndir,0,NULL) == NULL) {
5860 PerlMem_free(trndir);
5861 PerlMem_free(vmsdir);
5864 ret_chr = do_tounixspec(trndir,buf,ts,NULL);
5865 PerlMem_free(trndir);
5866 PerlMem_free(vmsdir);
5871 if ( !(lastdir = cp1 = strrchr(trndir,'/')) &&
5872 !(lastdir = cp1 = strrchr(trndir,']')) &&
5873 !(lastdir = cp1 = strrchr(trndir,'>'))) cp1 = trndir;
5874 if ((cp2 = strchr(cp1,'.'))) { /* look for explicit type */
5877 /* For EFS or ODS-5 look for the last dot */
5878 if (decc_efs_charset) {
5879 cp2 = strrchr(cp1,'.');
5881 if (vms_process_case_tolerant) {
5882 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
5883 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
5884 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
5885 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
5886 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
5887 (ver || *cp3)))))) {
5888 PerlMem_free(trndir);
5889 PerlMem_free(vmsdir);
5891 set_vaxc_errno(RMS$_DIR);
5896 if (!*(cp2+1) || *(cp2+1) != 'D' || /* Wrong type. */
5897 !*(cp2+2) || *(cp2+2) != 'I' || /* Bzzt. */
5898 !*(cp2+3) || *(cp2+3) != 'R' ||
5899 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
5900 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
5901 (ver || *cp3)))))) {
5902 PerlMem_free(trndir);
5903 PerlMem_free(vmsdir);
5905 set_vaxc_errno(RMS$_DIR);
5909 dirlen = cp2 - trndir;
5913 retlen = dirlen + 6;
5914 if (buf) retspec = buf;
5915 else if (ts) Newx(retspec,retlen+1,char);
5916 else retspec = __fileify_retbuf;
5917 memcpy(retspec,trndir,dirlen);
5918 retspec[dirlen] = '\0';
5920 /* We've picked up everything up to the directory file name.
5921 Now just add the type and version, and we're set. */
5922 if ((!decc_efs_case_preserve) && vms_process_case_tolerant)
5923 strcat(retspec,".dir;1");
5925 strcat(retspec,".DIR;1");
5926 PerlMem_free(trndir);
5927 PerlMem_free(vmsdir);
5930 else { /* VMS-style directory spec */
5932 char *esa, term, *cp;
5933 unsigned long int sts, cmplen, haslower = 0;
5934 unsigned int nam_fnb;
5936 struct FAB dirfab = cc$rms_fab;
5937 rms_setup_nam(savnam);
5938 rms_setup_nam(dirnam);
5940 esa = PerlMem_malloc(VMS_MAXRSS + 1);
5941 if (esa == NULL) _ckvmssts(SS$_INSFMEM);
5942 rms_set_fna(dirfab, dirnam, trndir, strlen(trndir));
5943 rms_bind_fab_nam(dirfab, dirnam);
5944 rms_set_dna(dirfab, dirnam, ".DIR;1", 6);
5945 rms_set_esa(dirfab, dirnam, esa, (VMS_MAXRSS - 1));
5946 #ifdef NAM$M_NO_SHORT_UPCASE
5947 if (decc_efs_case_preserve)
5948 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
5951 for (cp = trndir; *cp; cp++)
5952 if (islower(*cp)) { haslower = 1; break; }
5953 if (!((sts = sys$parse(&dirfab)) & STS$K_SUCCESS)) {
5954 if ((dirfab.fab$l_sts == RMS$_DIR) || (dirfab.fab$l_sts == RMS$_DNF)) {
5955 rms_set_nam_nop(dirnam, NAM$M_SYNCHK);
5956 sts = sys$parse(&dirfab) & STS$K_SUCCESS;
5960 PerlMem_free(trndir);
5961 PerlMem_free(vmsdir);
5963 set_vaxc_errno(dirfab.fab$l_sts);
5969 /* Does the file really exist? */
5970 if (sys$search(&dirfab)& STS$K_SUCCESS) {
5971 /* Yes; fake the fnb bits so we'll check type below */
5972 rms_set_nam_fnb(dirnam, (NAM$M_EXP_TYPE | NAM$M_EXP_VER));
5974 else { /* No; just work with potential name */
5975 if (dirfab.fab$l_sts == RMS$_FNF) dirnam = savnam;
5978 fab_sts = dirfab.fab$l_sts;
5979 sts = rms_free_search_context(&dirfab);
5981 PerlMem_free(trndir);
5982 PerlMem_free(vmsdir);
5983 set_errno(EVMSERR); set_vaxc_errno(fab_sts);
5988 esa[rms_nam_esll(dirnam)] = '\0';
5989 if (!rms_is_nam_fnb(dirnam, (NAM$M_EXP_DEV | NAM$M_EXP_DIR))) {
5990 cp1 = strchr(esa,']');
5991 if (!cp1) cp1 = strchr(esa,'>');
5992 if (cp1) { /* Should always be true */
5993 rms_nam_esll(dirnam) -= cp1 - esa - 1;
5994 memmove(esa,cp1 + 1, rms_nam_esll(dirnam));
5997 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_TYPE)) { /* Was type specified? */
5998 /* Yep; check version while we're at it, if it's there. */
5999 cmplen = rms_is_nam_fnb(dirnam, NAM$M_EXP_VER) ? 6 : 4;
6000 if (strncmp(rms_nam_typel(dirnam), ".DIR;1", cmplen)) {
6001 /* Something other than .DIR[;1]. Bzzt. */
6002 sts = rms_free_search_context(&dirfab);
6004 PerlMem_free(trndir);
6005 PerlMem_free(vmsdir);
6007 set_vaxc_errno(RMS$_DIR);
6012 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_NAME)) {
6013 /* They provided at least the name; we added the type, if necessary, */
6014 if (buf) retspec = buf; /* in sys$parse() */
6015 else if (ts) Newx(retspec, rms_nam_esll(dirnam)+1, char);
6016 else retspec = __fileify_retbuf;
6017 strcpy(retspec,esa);
6018 sts = rms_free_search_context(&dirfab);
6019 PerlMem_free(trndir);
6021 PerlMem_free(vmsdir);
6024 if ((cp1 = strstr(esa,".][000000]")) != NULL) {
6025 for (cp2 = cp1 + 9; *cp2; cp1++,cp2++) *cp1 = *cp2;
6027 rms_nam_esll(dirnam) -= 9;
6029 if ((cp1 = strrchr(esa,']')) == NULL) cp1 = strrchr(esa,'>');
6030 if (cp1 == NULL) { /* should never happen */
6031 sts = rms_free_search_context(&dirfab);
6032 PerlMem_free(trndir);
6034 PerlMem_free(vmsdir);
6039 retlen = strlen(esa);
6040 cp1 = strrchr(esa,'.');
6041 /* ODS-5 directory specifications can have extra "." in them. */
6042 /* Fix-me, can not scan EFS file specifications backwards */
6043 while (cp1 != NULL) {
6044 if ((cp1-1 == esa) || (*(cp1-1) != '^'))
6048 while ((cp1 > esa) && (*cp1 != '.'))
6055 if ((cp1) != NULL) {
6056 /* There's more than one directory in the path. Just roll back. */
6058 if (buf) retspec = buf;
6059 else if (ts) Newx(retspec,retlen+7,char);
6060 else retspec = __fileify_retbuf;
6061 strcpy(retspec,esa);
6064 if (rms_is_nam_fnb(dirnam, NAM$M_ROOT_DIR)) {
6065 /* Go back and expand rooted logical name */
6066 rms_set_nam_nop(dirnam, NAM$M_SYNCHK | NAM$M_NOCONCEAL);
6067 #ifdef NAM$M_NO_SHORT_UPCASE
6068 if (decc_efs_case_preserve)
6069 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
6071 if (!(sys$parse(&dirfab) & STS$K_SUCCESS)) {
6072 sts = rms_free_search_context(&dirfab);
6074 PerlMem_free(trndir);
6075 PerlMem_free(vmsdir);
6077 set_vaxc_errno(dirfab.fab$l_sts);
6080 retlen = rms_nam_esll(dirnam) - 9; /* esa - '][' - '].DIR;1' */
6081 if (buf) retspec = buf;
6082 else if (ts) Newx(retspec,retlen+16,char);
6083 else retspec = __fileify_retbuf;
6084 cp1 = strstr(esa,"][");
6085 if (!cp1) cp1 = strstr(esa,"]<");
6087 memcpy(retspec,esa,dirlen);
6088 if (!strncmp(cp1+2,"000000]",7)) {
6089 retspec[dirlen-1] = '\0';
6090 /* fix-me Not full ODS-5, just extra dots in directories for now */
6091 cp1 = retspec + dirlen - 1;
6092 while (cp1 > retspec)
6097 if (*(cp1-1) != '^')
6102 if (*cp1 == '.') *cp1 = ']';
6104 memmove(cp1+8,cp1+1,retspec+dirlen-cp1);
6105 memmove(cp1+1,"000000]",7);
6109 memmove(retspec+dirlen,cp1+2,retlen-dirlen);
6110 retspec[retlen] = '\0';
6111 /* Convert last '.' to ']' */
6112 cp1 = retspec+retlen-1;
6113 while (*cp != '[') {
6116 /* Do not trip on extra dots in ODS-5 directories */
6117 if ((cp1 == retspec) || (*(cp1-1) != '^'))
6121 if (*cp1 == '.') *cp1 = ']';
6123 memmove(cp1+8,cp1+1,retspec+dirlen-cp1);
6124 memmove(cp1+1,"000000]",7);
6128 else { /* This is a top-level dir. Add the MFD to the path. */
6129 if (buf) retspec = buf;
6130 else if (ts) Newx(retspec,retlen+16,char);
6131 else retspec = __fileify_retbuf;
6134 while ((*cp1 != ':') && (*cp1 != '\0')) *(cp2++) = *(cp1++);
6135 strcpy(cp2,":[000000]");
6140 sts = rms_free_search_context(&dirfab);
6141 /* We've set up the string up through the filename. Add the
6142 type and version, and we're done. */
6143 strcat(retspec,".DIR;1");
6145 /* $PARSE may have upcased filespec, so convert output to lower
6146 * case if input contained any lowercase characters. */
6147 if (haslower && !decc_efs_case_preserve) __mystrtolower(retspec);
6148 PerlMem_free(trndir);
6150 PerlMem_free(vmsdir);
6153 } /* end of do_fileify_dirspec() */
6155 /* External entry points */
6156 char *Perl_fileify_dirspec(pTHX_ const char *dir, char *buf)
6157 { return do_fileify_dirspec(dir,buf,0,NULL); }
6158 char *Perl_fileify_dirspec_ts(pTHX_ const char *dir, char *buf)
6159 { return do_fileify_dirspec(dir,buf,1,NULL); }
6160 char *Perl_fileify_dirspec_utf8(pTHX_ const char *dir, char *buf, int * utf8_fl)
6161 { return do_fileify_dirspec(dir,buf,0,utf8_fl); }
6162 char *Perl_fileify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int * utf8_fl)
6163 { return do_fileify_dirspec(dir,buf,1,utf8_fl); }
6165 /*{{{ char *pathify_dirspec[_ts](char *path, char *buf)*/
6166 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int * utf8_fl)
6168 static char __pathify_retbuf[VMS_MAXRSS];
6169 unsigned long int retlen;
6170 char *retpath, *cp1, *cp2, *trndir;
6171 unsigned short int trnlnm_iter_count;
6174 if (utf8_fl != NULL)
6177 if (!dir || !*dir) {
6178 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
6181 trndir = PerlMem_malloc(VMS_MAXRSS);
6182 if (trndir == NULL) _ckvmssts(SS$_INSFMEM);
6183 if (*dir) strcpy(trndir,dir);
6184 else getcwd(trndir,VMS_MAXRSS - 1);
6186 trnlnm_iter_count = 0;
6187 while (!strpbrk(trndir,"/]:>") && !no_translate_barewords
6188 && my_trnlnm(trndir,trndir,0)) {
6189 trnlnm_iter_count++;
6190 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
6191 trnlen = strlen(trndir);
6193 /* Trap simple rooted lnms, and return lnm:[000000] */
6194 if (!strcmp(trndir+trnlen-2,".]")) {
6195 if (buf) retpath = buf;
6196 else if (ts) Newx(retpath,strlen(dir)+10,char);
6197 else retpath = __pathify_retbuf;
6198 strcpy(retpath,dir);
6199 strcat(retpath,":[000000]");
6200 PerlMem_free(trndir);
6205 /* At this point we do not work with *dir, but the copy in
6206 * *trndir that is modifiable.
6209 if (!strpbrk(trndir,"]:>")) { /* Unix-style path or plain name */
6210 if (*trndir == '.' && (*(trndir+1) == '\0' ||
6211 (*(trndir+1) == '.' && *(trndir+2) == '\0')))
6212 retlen = 2 + (*(trndir+1) != '\0');
6214 if ( !(cp1 = strrchr(trndir,'/')) &&
6215 !(cp1 = strrchr(trndir,']')) &&
6216 !(cp1 = strrchr(trndir,'>')) ) cp1 = trndir;
6217 if ((cp2 = strchr(cp1,'.')) != NULL &&
6218 (*(cp2-1) != '/' || /* Trailing '.', '..', */
6219 !(*(cp2+1) == '\0' || /* or '...' are dirs. */
6220 (*(cp2+1) == '.' && *(cp2+2) == '\0') ||
6221 (*(cp2+1) == '.' && *(cp2+2) == '.' && *(cp2+3) == '\0')))) {
6224 /* For EFS or ODS-5 look for the last dot */
6225 if (decc_efs_charset) {
6226 cp2 = strrchr(cp1,'.');
6228 if (vms_process_case_tolerant) {
6229 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
6230 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
6231 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
6232 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
6233 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
6234 (ver || *cp3)))))) {
6235 PerlMem_free(trndir);
6237 set_vaxc_errno(RMS$_DIR);
6242 if (!*(cp2+1) || *(cp2+1) != 'D' || /* Wrong type. */
6243 !*(cp2+2) || *(cp2+2) != 'I' || /* Bzzt. */
6244 !*(cp2+3) || *(cp2+3) != 'R' ||
6245 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
6246 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
6247 (ver || *cp3)))))) {
6248 PerlMem_free(trndir);
6250 set_vaxc_errno(RMS$_DIR);
6254 retlen = cp2 - trndir + 1;
6256 else { /* No file type present. Treat the filename as a directory. */
6257 retlen = strlen(trndir) + 1;
6260 if (buf) retpath = buf;
6261 else if (ts) Newx(retpath,retlen+1,char);
6262 else retpath = __pathify_retbuf;
6263 strncpy(retpath, trndir, retlen-1);
6264 if (retpath[retlen-2] != '/') { /* If the path doesn't already end */
6265 retpath[retlen-1] = '/'; /* with '/', add it. */
6266 retpath[retlen] = '\0';
6268 else retpath[retlen-1] = '\0';
6270 else { /* VMS-style directory spec */
6272 unsigned long int sts, cmplen, haslower;
6273 struct FAB dirfab = cc$rms_fab;
6275 rms_setup_nam(savnam);
6276 rms_setup_nam(dirnam);
6278 /* If we've got an explicit filename, we can just shuffle the string. */
6279 if ( ( (cp1 = strrchr(trndir,']')) != NULL ||
6280 (cp1 = strrchr(trndir,'>')) != NULL ) && *(cp1+1)) {
6281 if ((cp2 = strchr(cp1,'.')) != NULL) {
6283 if (vms_process_case_tolerant) {
6284 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
6285 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
6286 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
6287 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
6288 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
6289 (ver || *cp3)))))) {
6290 PerlMem_free(trndir);
6292 set_vaxc_errno(RMS$_DIR);
6297 if (!*(cp2+1) || *(cp2+1) != 'D' || /* Wrong type. */
6298 !*(cp2+2) || *(cp2+2) != 'I' || /* Bzzt. */
6299 !*(cp2+3) || *(cp2+3) != 'R' ||
6300 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
6301 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
6302 (ver || *cp3)))))) {
6303 PerlMem_free(trndir);
6305 set_vaxc_errno(RMS$_DIR);
6310 else { /* No file type, so just draw name into directory part */
6311 for (cp2 = cp1; *cp2; cp2++) ;
6314 *(cp2+1) = '\0'; /* OK; trndir is guaranteed to be long enough */
6316 /* We've now got a VMS 'path'; fall through */
6319 dirlen = strlen(trndir);
6320 if (trndir[dirlen-1] == ']' ||
6321 trndir[dirlen-1] == '>' ||
6322 trndir[dirlen-1] == ':') { /* It's already a VMS 'path' */
6323 if (buf) retpath = buf;
6324 else if (ts) Newx(retpath,strlen(trndir)+1,char);
6325 else retpath = __pathify_retbuf;
6326 strcpy(retpath,trndir);
6327 PerlMem_free(trndir);
6330 rms_set_fna(dirfab, dirnam, trndir, dirlen);
6331 esa = PerlMem_malloc(VMS_MAXRSS);
6332 if (esa == NULL) _ckvmssts(SS$_INSFMEM);
6333 rms_set_dna(dirfab, dirnam, ".DIR;1", 6);
6334 rms_bind_fab_nam(dirfab, dirnam);
6335 rms_set_esa(dirfab, dirnam, esa, VMS_MAXRSS - 1);
6336 #ifdef NAM$M_NO_SHORT_UPCASE
6337 if (decc_efs_case_preserve)
6338 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
6341 for (cp = trndir; *cp; cp++)
6342 if (islower(*cp)) { haslower = 1; break; }
6344 if (!(sts = (sys$parse(&dirfab)& STS$K_SUCCESS))) {
6345 if ((dirfab.fab$l_sts == RMS$_DIR) || (dirfab.fab$l_sts == RMS$_DNF)) {
6346 rms_set_nam_nop(dirnam, NAM$M_SYNCHK);
6347 sts = sys$parse(&dirfab) & STS$K_SUCCESS;
6350 PerlMem_free(trndir);
6353 set_vaxc_errno(dirfab.fab$l_sts);
6359 /* Does the file really exist? */
6360 if (!(sys$search(&dirfab)&STS$K_SUCCESS)) {
6361 if (dirfab.fab$l_sts != RMS$_FNF) {
6363 sts1 = rms_free_search_context(&dirfab);
6364 PerlMem_free(trndir);
6367 set_vaxc_errno(dirfab.fab$l_sts);
6370 dirnam = savnam; /* No; just work with potential name */
6373 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_TYPE)) { /* Was type specified? */
6374 /* Yep; check version while we're at it, if it's there. */
6375 cmplen = rms_is_nam_fnb(dirnam, NAM$M_EXP_VER) ? 6 : 4;
6376 if (strncmp(rms_nam_typel(dirnam),".DIR;1",cmplen)) {
6378 /* Something other than .DIR[;1]. Bzzt. */
6379 sts2 = rms_free_search_context(&dirfab);
6380 PerlMem_free(trndir);
6383 set_vaxc_errno(RMS$_DIR);
6387 /* OK, the type was fine. Now pull any file name into the
6389 if ((cp1 = strrchr(esa,']'))) *(rms_nam_typel(dirnam)) = ']';
6391 cp1 = strrchr(esa,'>');
6392 *(rms_nam_typel(dirnam)) = '>';
6395 *(rms_nam_typel(dirnam) + 1) = '\0';
6396 retlen = (rms_nam_typel(dirnam)) - esa + 2;
6397 if (buf) retpath = buf;
6398 else if (ts) Newx(retpath,retlen,char);
6399 else retpath = __pathify_retbuf;
6400 strcpy(retpath,esa);
6402 sts = rms_free_search_context(&dirfab);
6403 /* $PARSE may have upcased filespec, so convert output to lower
6404 * case if input contained any lowercase characters. */
6405 if (haslower && !decc_efs_case_preserve) __mystrtolower(retpath);
6408 PerlMem_free(trndir);
6410 } /* end of do_pathify_dirspec() */
6412 /* External entry points */
6413 char *Perl_pathify_dirspec(pTHX_ const char *dir, char *buf)
6414 { return do_pathify_dirspec(dir,buf,0,NULL); }
6415 char *Perl_pathify_dirspec_ts(pTHX_ const char *dir, char *buf)
6416 { return do_pathify_dirspec(dir,buf,1,NULL); }
6417 char *Perl_pathify_dirspec_utf8(pTHX_ const char *dir, char *buf, int *utf8_fl)
6418 { return do_pathify_dirspec(dir,buf,0,utf8_fl); }
6419 char *Perl_pathify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int *utf8_fl)
6420 { return do_pathify_dirspec(dir,buf,1,utf8_fl); }
6422 /*{{{ char *tounixspec[_ts](char *spec, char *buf, int *)*/
6423 static char *mp_do_tounixspec(pTHX_ const char *spec, char *buf, int ts, int * utf8_fl)
6425 static char __tounixspec_retbuf[VMS_MAXRSS];
6426 char *dirend, *rslt, *cp1, *cp3, *tmp;
6428 int devlen, dirlen, retlen = VMS_MAXRSS;
6429 int expand = 1; /* guarantee room for leading and trailing slashes */
6430 unsigned short int trnlnm_iter_count;
6432 if (utf8_fl != NULL)
6435 if (spec == NULL) return NULL;
6436 if (strlen(spec) > (VMS_MAXRSS-1)) return NULL;
6437 if (buf) rslt = buf;
6439 Newx(rslt, VMS_MAXRSS, char);
6441 else rslt = __tounixspec_retbuf;
6443 /* New VMS specific format needs translation
6444 * glob passes filenames with trailing '\n' and expects this preserved.
6446 if (decc_posix_compliant_pathnames) {
6447 if (strncmp(spec, "\"^UP^", 5) == 0) {
6453 tunix = PerlMem_malloc(VMS_MAXRSS);
6454 if (tunix == NULL) _ckvmssts(SS$_INSFMEM);
6455 strcpy(tunix, spec);
6456 tunix_len = strlen(tunix);
6458 if (tunix[tunix_len - 1] == '\n') {
6459 tunix[tunix_len - 1] = '\"';
6460 tunix[tunix_len] = '\0';
6464 uspec = decc$translate_vms(tunix);
6465 PerlMem_free(tunix);
6466 if ((int)uspec > 0) {
6472 /* If we can not translate it, makemaker wants as-is */
6480 cmp_rslt = 0; /* Presume VMS */
6481 cp1 = strchr(spec, '/');
6485 /* Look for EFS ^/ */
6486 if (decc_efs_charset) {
6487 while (cp1 != NULL) {
6490 /* Found illegal VMS, assume UNIX */
6495 cp1 = strchr(cp1, '/');
6499 /* Look for "." and ".." */
6500 if (decc_filename_unix_report) {
6501 if (spec[0] == '.') {
6502 if ((spec[1] == '\0') || (spec[1] == '\n')) {
6506 if ((spec[1] == '.') && ((spec[2] == '\0') || (spec[2] == '\n'))) {
6512 /* This is already UNIX or at least nothing VMS understands */
6520 dirend = strrchr(spec,']');
6521 if (dirend == NULL) dirend = strrchr(spec,'>');
6522 if (dirend == NULL) dirend = strchr(spec,':');
6523 if (dirend == NULL) {
6528 /* Special case 1 - sys$posix_root = / */
6529 #if __CRTL_VER >= 70000000
6530 if (!decc_disable_posix_root) {
6531 if (strncasecmp(spec, "SYS$POSIX_ROOT:", 15) == 0) {
6539 /* Special case 2 - Convert NLA0: to /dev/null */
6540 #if __CRTL_VER < 70000000
6541 cmp_rslt = strncmp(spec,"NLA0:", 5);
6543 cmp_rslt = strncmp(spec,"nla0:", 5);
6545 cmp_rslt = strncasecmp(spec,"NLA0:", 5);
6547 if (cmp_rslt == 0) {
6548 strcpy(rslt, "/dev/null");
6551 if (spec[6] != '\0') {
6558 /* Also handle special case "SYS$SCRATCH:" */
6559 #if __CRTL_VER < 70000000
6560 cmp_rslt = strncmp(spec,"SYS$SCRATCH:", 12);
6562 cmp_rslt = strncmp(spec,"sys$scratch:", 12);
6564 cmp_rslt = strncasecmp(spec,"SYS$SCRATCH:", 12);
6566 tmp = PerlMem_malloc(VMS_MAXRSS);
6567 if (tmp == NULL) _ckvmssts(SS$_INSFMEM);
6568 if (cmp_rslt == 0) {
6571 islnm = my_trnlnm(tmp, "TMP", 0);
6573 strcpy(rslt, "/tmp");
6576 if (spec[12] != '\0') {
6584 if (*cp2 != '[' && *cp2 != '<') {
6587 else { /* the VMS spec begins with directories */
6589 if (*cp2 == ']' || *cp2 == '>') {
6590 *(cp1++) = '.'; *(cp1++) = '/'; *(cp1++) = '\0';
6594 else if ( *cp2 != '^' && *cp2 != '.' && *cp2 != '-') { /* add the implied device */
6595 if (getcwd(tmp, VMS_MAXRSS-1 ,1) == NULL) {
6596 if (ts) Safefree(rslt);
6600 trnlnm_iter_count = 0;
6603 while (*cp3 != ':' && *cp3) cp3++;
6605 if (strchr(cp3,']') != NULL) break;
6606 trnlnm_iter_count++;
6607 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER+1) break;
6608 } while (vmstrnenv(tmp,tmp,0,fildev,0));
6610 ((devlen = strlen(tmp)) + (dirlen = strlen(cp2)) + 1 > retlen)) {
6611 retlen = devlen + dirlen;
6612 Renew(rslt,retlen+1+2*expand,char);
6618 *(cp1++) = *(cp3++);
6619 if (cp1 - rslt > (VMS_MAXRSS - 1) && !ts && !buf) {
6621 return NULL; /* No room */
6626 if ((*cp2 == '^')) {
6627 /* EFS file escape, pass the next character as is */
6628 /* Fix me: HEX encoding for Unicode not implemented */
6631 else if ( *cp2 == '.') {
6632 if (*(cp2+1) == '.' && *(cp2+2) == '.') {
6633 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
6640 for (; cp2 <= dirend; cp2++) {
6641 if ((*cp2 == '^')) {
6642 /* EFS file escape, pass the next character as is */
6643 /* Fix me: HEX encoding for Unicode not implemented */
6644 *(cp1++) = *(++cp2);
6645 /* An escaped dot stays as is -- don't convert to slash */
6646 if (*cp2 == '.') cp2++;
6650 if (*(cp2+1) == '[') cp2++;
6652 else if (*cp2 == ']' || *cp2 == '>') {
6653 if (*(cp1-1) != '/') *(cp1++) = '/'; /* Don't double after ellipsis */
6655 else if ((*cp2 == '.') && (*cp2-1 != '^')) {
6657 if (*(cp2+1) == ']' || *(cp2+1) == '>') {
6658 while (*(cp2+1) == ']' || *(cp2+1) == '>' ||
6659 *(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
6660 if (!strncmp(cp2,"[000000",7) && (*(cp2+7) == ']' ||
6661 *(cp2+7) == '>' || *(cp2+7) == '.')) cp2 += 7;
6663 else if ( *(cp2+1) == '.' && *(cp2+2) == '.') {
6664 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) ='/';
6668 else if (*cp2 == '-') {
6669 if (*(cp2-1) == '[' || *(cp2-1) == '<' || *(cp2-1) == '.') {
6670 while (*cp2 == '-') {
6672 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
6674 if (*cp2 != '.' && *cp2 != ']' && *cp2 != '>') { /* we don't allow */
6675 if (ts) Safefree(rslt); /* filespecs like */
6676 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); /* [fred.--foo.bar] */
6680 else *(cp1++) = *cp2;
6682 else *(cp1++) = *cp2;
6685 if ((*cp2 == '^') && (*(cp2+1) == '.')) cp2++; /* '^.' --> '.' */
6686 *(cp1++) = *(cp2++);
6690 /* This still leaves /000000/ when working with a
6691 * VMS device root or concealed root.
6697 ulen = strlen(rslt);
6699 /* Get rid of "000000/ in rooted filespecs */
6701 zeros = strstr(rslt, "/000000/");
6702 if (zeros != NULL) {
6704 mlen = ulen - (zeros - rslt) - 7;
6705 memmove(zeros, &zeros[7], mlen);
6714 } /* end of do_tounixspec() */
6716 /* External entry points */
6717 char *Perl_tounixspec(pTHX_ const char *spec, char *buf)
6718 { return do_tounixspec(spec,buf,0, NULL); }
6719 char *Perl_tounixspec_ts(pTHX_ const char *spec, char *buf)
6720 { return do_tounixspec(spec,buf,1, NULL); }
6721 char *Perl_tounixspec_utf8(pTHX_ const char *spec, char *buf, int * utf8_fl)
6722 { return do_tounixspec(spec,buf,0, utf8_fl); }
6723 char *Perl_tounixspec_utf8_ts(pTHX_ const char *spec, char *buf, int * utf8_fl)
6724 { return do_tounixspec(spec,buf,1, utf8_fl); }
6726 #if __CRTL_VER >= 70200000 && !defined(__VAX)
6729 This procedure is used to identify if a path is based in either
6730 the old SYS$POSIX_ROOT: or the new 8.3 RMS based POSIX root, and
6731 it returns the OpenVMS format directory for it.
6733 It is expecting specifications of only '/' or '/xxxx/'
6735 If a posix root does not exist, or 'xxxx' is not a directory
6736 in the posix root, it returns a failure.
6738 FIX-ME: xxxx could be in UTF-8 and needs to be returned in VTF-7.
6740 It is used only internally by posix_to_vmsspec_hardway().
6743 static int posix_root_to_vms
6744 (char *vmspath, int vmspath_len,
6745 const char *unixpath,
6746 const int * utf8_fl) {
6748 struct FAB myfab = cc$rms_fab;
6749 struct NAML mynam = cc$rms_naml;
6750 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
6751 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
6758 unixlen = strlen(unixpath);
6764 #if __CRTL_VER >= 80200000
6765 /* If not a posix spec already, convert it */
6766 if (decc_posix_compliant_pathnames) {
6767 if (strncmp(unixpath,"\"^UP^",5) != 0) {
6768 sprintf(vmspath,"\"^UP^%s\"",unixpath);
6771 /* This is already a VMS specification, no conversion */
6773 strncpy(vmspath,unixpath, vmspath_len);
6782 /* Check to see if this is under the POSIX root */
6783 if (decc_disable_posix_root) {
6787 /* Skip leading / */
6788 if (unixpath[0] == '/') {
6794 strcpy(vmspath,"SYS$POSIX_ROOT:");
6796 /* If this is only the / , or blank, then... */
6797 if (unixpath[0] == '\0') {
6798 /* by definition, this is the answer */
6802 /* Need to look up a directory */
6806 /* Copy and add '^' escape characters as needed */
6809 while (unixpath[i] != 0) {
6812 j += copy_expand_unix_filename_escape
6813 (&vmspath[j], &unixpath[i], &k, utf8_fl);
6817 path_len = strlen(vmspath);
6818 if (vmspath[path_len - 1] == '/')
6820 vmspath[path_len] = ']';
6822 vmspath[path_len] = '\0';
6825 vmspath[vmspath_len] = 0;
6826 if (unixpath[unixlen - 1] == '/')
6828 esa = PerlMem_malloc(VMS_MAXRSS);
6829 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6830 myfab.fab$l_fna = vmspath;
6831 myfab.fab$b_fns = strlen(vmspath);
6832 myfab.fab$l_naml = &mynam;
6833 mynam.naml$l_esa = NULL;
6834 mynam.naml$b_ess = 0;
6835 mynam.naml$l_long_expand = esa;
6836 mynam.naml$l_long_expand_alloc = (unsigned char) VMS_MAXRSS - 1;
6837 mynam.naml$l_rsa = NULL;
6838 mynam.naml$b_rss = 0;
6839 if (decc_efs_case_preserve)
6840 mynam.naml$b_nop |= NAM$M_NO_SHORT_UPCASE;
6841 #ifdef NAML$M_OPEN_SPECIAL
6842 mynam.naml$l_input_flags |= NAML$M_OPEN_SPECIAL;
6845 /* Set up the remaining naml fields */
6846 sts = sys$parse(&myfab);
6848 /* It failed! Try again as a UNIX filespec */
6854 /* get the Device ID and the FID */
6855 sts = sys$search(&myfab);
6856 /* on any failure, returned the POSIX ^UP^ filespec */
6861 specdsc.dsc$a_pointer = vmspath;
6862 specdsc.dsc$w_length = vmspath_len;
6864 dvidsc.dsc$a_pointer = &mynam.naml$t_dvi[1];
6865 dvidsc.dsc$w_length = mynam.naml$t_dvi[0];
6866 sts = lib$fid_to_name
6867 (&dvidsc, mynam.naml$w_fid, &specdsc, &specdsc.dsc$w_length);
6869 /* on any failure, returned the POSIX ^UP^ filespec */
6871 /* This can happen if user does not have permission to read directories */
6872 if (strncmp(unixpath,"\"^UP^",5) != 0)
6873 sprintf(vmspath,"\"^UP^%s\"",unixpath);
6875 strcpy(vmspath, unixpath);
6878 vmspath[specdsc.dsc$w_length] = 0;
6880 /* Are we expecting a directory? */
6881 if (dir_flag != 0) {
6887 i = specdsc.dsc$w_length - 1;
6891 /* Version must be '1' */
6892 if (vmspath[i--] != '1')
6894 /* Version delimiter is one of ".;" */
6895 if ((vmspath[i] != '.') && (vmspath[i] != ';'))
6898 if (vmspath[i--] != 'R')
6900 if (vmspath[i--] != 'I')
6902 if (vmspath[i--] != 'D')
6904 if (vmspath[i--] != '.')
6906 eptr = &vmspath[i+1];
6908 if ((vmspath[i] == ']') || (vmspath[i] == '>')) {
6909 if (vmspath[i-1] != '^') {
6917 /* Get rid of 6 imaginary zero directory filename */
6918 vmspath[i+1] = '\0';
6922 if (vmspath[i] == '0')
6936 /* /dev/mumble needs to be handled special.
6937 /dev/null becomes NLA0:, And there is the potential for other stuff
6938 like /dev/tty which may need to be mapped to something.
6942 slash_dev_special_to_vms
6943 (const char * unixptr,
6953 nextslash = strchr(unixptr, '/');
6954 len = strlen(unixptr);
6955 if (nextslash != NULL)
6956 len = nextslash - unixptr;
6957 cmp = strncmp("null", unixptr, 5);
6959 if (vmspath_len >= 6) {
6960 strcpy(vmspath, "_NLA0:");
6967 /* The built in routines do not understand perl's special needs, so
6968 doing a manual conversion from UNIX to VMS
6970 If the utf8_fl is not null and points to a non-zero value, then
6971 treat 8 bit characters as UTF-8.
6973 The sequence starting with '$(' and ending with ')' will be passed
6974 through with out interpretation instead of being escaped.
6977 static int posix_to_vmsspec_hardway
6978 (char *vmspath, int vmspath_len,
6979 const char *unixpath,
6984 const char *unixptr;
6985 const char *unixend;
6987 const char *lastslash;
6988 const char *lastdot;
6994 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
6995 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
6997 if (utf8_fl != NULL)
7003 /* Ignore leading "/" characters */
7004 while((unixptr[0] == '/') && (unixptr[1] == '/')) {
7007 unixlen = strlen(unixptr);
7009 /* Do nothing with blank paths */
7016 /* This could have a "^UP^ on the front */
7017 if (strncmp(unixptr,"\"^UP^",5) == 0) {
7023 lastslash = strrchr(unixptr,'/');
7024 lastdot = strrchr(unixptr,'.');
7025 unixend = strrchr(unixptr,'\"');
7026 if (!quoted || !((unixend != NULL) && (unixend[1] == '\0'))) {
7027 unixend = unixptr + unixlen;
7030 /* last dot is last dot or past end of string */
7031 if (lastdot == NULL)
7032 lastdot = unixptr + unixlen;
7034 /* if no directories, set last slash to beginning of string */
7035 if (lastslash == NULL) {
7036 lastslash = unixptr;
7039 /* Watch out for trailing "." after last slash, still a directory */
7040 if ((lastslash[1] == '.') && (lastslash[2] == '\0')) {
7041 lastslash = unixptr + unixlen;
7044 /* Watch out for traiing ".." after last slash, still a directory */
7045 if ((lastslash[1] == '.')&&(lastslash[2] == '.')&&(lastslash[3] == '\0')) {
7046 lastslash = unixptr + unixlen;
7049 /* dots in directories are aways escaped */
7050 if (lastdot < lastslash)
7051 lastdot = unixptr + unixlen;
7054 /* if (unixptr < lastslash) then we are in a directory */
7061 /* Start with the UNIX path */
7062 if (*unixptr != '/') {
7063 /* relative paths */
7065 /* If allowing logical names on relative pathnames, then handle here */
7066 if ((unixptr[0] != '.') && !decc_disable_to_vms_logname_translation &&
7067 !decc_posix_compliant_pathnames) {
7073 /* Find the next slash */
7074 nextslash = strchr(unixptr,'/');
7076 esa = PerlMem_malloc(vmspath_len);
7077 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7079 trn = PerlMem_malloc(VMS_MAXRSS);
7080 if (trn == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7082 if (nextslash != NULL) {
7084 seg_len = nextslash - unixptr;
7085 strncpy(esa, unixptr, seg_len);
7089 strcpy(esa, unixptr);
7090 seg_len = strlen(unixptr);
7092 /* trnlnm(section) */
7093 islnm = vmstrnenv(esa, trn, 0, fildev, 0);
7096 /* Now fix up the directory */
7098 /* Split up the path to find the components */
7099 sts = vms_split_path
7118 /* A logical name must be a directory or the full
7119 specification. It is only a full specification if
7120 it is the only component */
7121 if ((unixptr[seg_len] == '\0') ||
7122 (unixptr[seg_len+1] == '\0')) {
7124 /* Is a directory being required? */
7125 if (((n_len + e_len) != 0) && (dir_flag !=0)) {
7126 /* Not a logical name */
7131 if ((unixptr[seg_len] == '/') || (dir_flag != 0)) {
7132 /* This must be a directory */
7133 if (((n_len + e_len) == 0)&&(seg_len <= vmspath_len)) {
7134 strcpy(vmsptr, esa);
7135 vmslen=strlen(vmsptr);
7136 vmsptr[vmslen] = ':';
7138 vmsptr[vmslen] = '\0';
7146 /* must be dev/directory - ignore version */
7147 if ((n_len + e_len) != 0)
7150 /* transfer the volume */
7151 if (v_len > 0 && ((v_len + vmslen) < vmspath_len)) {
7152 strncpy(vmsptr, v_spec, v_len);
7158 /* unroot the rooted directory */
7159 if ((r_len > 0) && ((r_len + d_len + vmslen) < vmspath_len)) {
7161 r_spec[r_len - 1] = ']';
7163 /* This should not be there, but nothing is perfect */
7165 cmp = strcmp(&r_spec[1], "000000.");
7175 strncpy(vmsptr, r_spec, r_len);
7181 /* Bring over the directory. */
7183 ((d_len + vmslen) < vmspath_len)) {
7185 d_spec[d_len - 1] = ']';
7187 cmp = strcmp(&d_spec[1], "000000.");
7198 /* Remove the redundant root */
7206 strncpy(vmsptr, d_spec, d_len);
7220 if (lastslash > unixptr) {
7223 /* skip leading ./ */
7225 while ((unixptr[0] == '.') && (unixptr[1] == '/')) {
7231 /* Are we still in a directory? */
7232 if (unixptr <= lastslash) {
7237 /* if not backing up, then it is relative forward. */
7238 if (!((*unixptr == '.') && (unixptr[1] == '.') &&
7239 ((unixptr[2] == '/') || (&unixptr[2] == unixend)))) {
7247 /* Perl wants an empty directory here to tell the difference
7248 * between a DCL commmand and a filename
7257 /* Handle two special files . and .. */
7258 if (unixptr[0] == '.') {
7259 if (&unixptr[1] == unixend) {
7266 if ((unixptr[1] == '.') && (&unixptr[2] == unixend)) {
7277 else { /* Absolute PATH handling */
7281 /* Need to find out where root is */
7283 /* In theory, this procedure should never get an absolute POSIX pathname
7284 * that can not be found on the POSIX root.
7285 * In practice, that can not be relied on, and things will show up
7286 * here that are a VMS device name or concealed logical name instead.
7287 * So to make things work, this procedure must be tolerant.
7289 esa = PerlMem_malloc(vmspath_len);
7290 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7293 nextslash = strchr(&unixptr[1],'/');
7295 if (nextslash != NULL) {
7297 seg_len = nextslash - &unixptr[1];
7298 strncpy(vmspath, unixptr, seg_len + 1);
7299 vmspath[seg_len+1] = 0;
7302 cmp = strncmp(vmspath, "dev", 4);
7304 sts = slash_dev_special_to_vms(unixptr, vmspath, vmspath_len);
7305 if (sts = SS$_NORMAL)
7309 sts = posix_root_to_vms(esa, vmspath_len, vmspath, utf8_fl);
7312 if ($VMS_STATUS_SUCCESS(sts)) {
7313 /* This is verified to be a real path */
7315 sts = posix_root_to_vms(esa, vmspath_len, "/", NULL);
7316 if ($VMS_STATUS_SUCCESS(sts)) {
7317 strcpy(vmspath, esa);
7318 vmslen = strlen(vmspath);
7319 vmsptr = vmspath + vmslen;
7321 if (unixptr < lastslash) {
7330 cmp = strcmp(rptr,"000000.");
7335 } /* removing 6 zeros */
7336 } /* vmslen < 7, no 6 zeros possible */
7337 } /* Not in a directory */
7338 } /* Posix root found */
7340 /* No posix root, fall back to default directory */
7341 strcpy(vmspath, "SYS$DISK:[");
7342 vmsptr = &vmspath[10];
7344 if (unixptr > lastslash) {
7353 } /* end of verified real path handling */
7358 /* Ok, we have a device or a concealed root that is not in POSIX
7359 * or we have garbage. Make the best of it.
7362 /* Posix to VMS destroyed this, so copy it again */
7363 strncpy(vmspath, &unixptr[1], seg_len);
7364 vmspath[seg_len] = 0;
7366 vmsptr = &vmsptr[vmslen];
7369 /* Now do we need to add the fake 6 zero directory to it? */
7371 if ((*lastslash == '/') && (nextslash < lastslash)) {
7372 /* No there is another directory */
7379 /* now we have foo:bar or foo:[000000]bar to decide from */
7380 islnm = vmstrnenv(vmspath, esa, 0, fildev, 0);
7382 if (!islnm && !decc_posix_compliant_pathnames) {
7384 cmp = strncmp("bin", vmspath, 4);
7386 /* bin => SYS$SYSTEM: */
7387 islnm = vmstrnenv("SYS$SYSTEM:", esa, 0, fildev, 0);
7390 /* tmp => SYS$SCRATCH: */
7391 cmp = strncmp("tmp", vmspath, 4);
7393 islnm = vmstrnenv("SYS$SCRATCH:", esa, 0, fildev, 0);
7398 trnend = islnm ? islnm - 1 : 0;
7400 /* if this was a logical name, ']' or '>' must be present */
7401 /* if not a logical name, then assume a device and hope. */
7402 islnm = trnend ? (esa[trnend] == ']' || esa[trnend] == '>') : 0;
7404 /* if log name and trailing '.' then rooted - treat as device */
7405 add_6zero = islnm ? (esa[trnend-1] == '.') : 0;
7407 /* Fix me, if not a logical name, a device lookup should be
7408 * done to see if the device is file structured. If the device
7409 * is not file structured, the 6 zeros should not be put on.
7411 * As it is, perl is occasionally looking for dev:[000000]tty.
7412 * which looks a little strange.
7414 * Not that easy to detect as "/dev" may be file structured with
7415 * special device files.
7418 if ((add_6zero == 0) && (*nextslash == '/') &&
7419 (&nextslash[1] == unixend)) {
7420 /* No real directory present */
7425 /* Put the device delimiter on */
7428 unixptr = nextslash;
7431 /* Start directory if needed */
7432 if (!islnm || add_6zero) {
7438 /* add fake 000000] if needed */
7451 } /* non-POSIX translation */
7453 } /* End of relative/absolute path handling */
7455 while ((unixptr <= unixend) && (vmslen < vmspath_len)){
7462 if (dir_start != 0) {
7464 /* First characters in a directory are handled special */
7465 while ((*unixptr == '/') ||
7466 ((*unixptr == '.') &&
7467 ((unixptr[1]=='.') || (unixptr[1]=='/') ||
7468 (&unixptr[1]==unixend)))) {
7473 /* Skip redundant / in specification */
7474 while ((*unixptr == '/') && (dir_start != 0)) {
7477 if (unixptr == lastslash)
7480 if (unixptr == lastslash)
7483 /* Skip redundant ./ characters */
7484 while ((*unixptr == '.') &&
7485 ((unixptr[1] == '/')||(&unixptr[1] == unixend))) {
7488 if (unixptr == lastslash)
7490 if (*unixptr == '/')
7493 if (unixptr == lastslash)
7496 /* Skip redundant ../ characters */
7497 while ((*unixptr == '.') && (unixptr[1] == '.') &&
7498 ((unixptr[2] == '/') || (&unixptr[2] == unixend))) {
7499 /* Set the backing up flag */
7505 unixptr++; /* first . */
7506 unixptr++; /* second . */
7507 if (unixptr == lastslash)
7509 if (*unixptr == '/') /* The slash */
7512 if (unixptr == lastslash)
7515 /* To do: Perl expects /.../ to be translated to [...] on VMS */
7516 /* Not needed when VMS is pretending to be UNIX. */
7518 /* Is this loop stuck because of too many dots? */
7519 if (loop_flag == 0) {
7520 /* Exit the loop and pass the rest through */
7525 /* Are we done with directories yet? */
7526 if (unixptr >= lastslash) {
7528 /* Watch out for trailing dots */
7537 if (*unixptr == '/')
7541 /* Have we stopped backing up? */
7546 /* dir_start continues to be = 1 */
7548 if (*unixptr == '-') {
7550 *vmsptr++ = *unixptr++;
7554 /* Now are we done with directories yet? */
7555 if (unixptr >= lastslash) {
7557 /* Watch out for trailing dots */
7573 if (unixptr >= unixend)
7576 /* Normal characters - More EFS work probably needed */
7582 /* remove multiple / */
7583 while (unixptr[1] == '/') {
7586 if (unixptr == lastslash) {
7587 /* Watch out for trailing dots */
7599 /* To do: Perl expects /.../ to be translated to [...] on VMS */
7600 /* Not needed when VMS is pretending to be UNIX. */
7604 if (unixptr != unixend)
7609 if ((unixptr < lastdot) || (unixptr < lastslash) ||
7610 (&unixptr[1] == unixend)) {
7616 /* trailing dot ==> '^..' on VMS */
7617 if (unixptr == unixend) {
7625 *vmsptr++ = *unixptr++;
7629 if (quoted && (&unixptr[1] == unixend)) {
7633 in_cnt = copy_expand_unix_filename_escape
7634 (vmsptr, unixptr, &out_cnt, utf8_fl);
7644 in_cnt = copy_expand_unix_filename_escape
7645 (vmsptr, unixptr, &out_cnt, utf8_fl);
7652 /* Make sure directory is closed */
7653 if (unixptr == lastslash) {
7655 vmsptr2 = vmsptr - 1;
7657 if (*vmsptr2 != ']') {
7660 /* directories do not end in a dot bracket */
7661 if (*vmsptr2 == '.') {
7665 if (*vmsptr2 != '^') {
7666 vmsptr--; /* back up over the dot */
7674 /* Add a trailing dot if a file with no extension */
7675 vmsptr2 = vmsptr - 1;
7677 (*vmsptr2 != ']') && (*vmsptr2 != '*') && (*vmsptr2 != '%') &&
7678 (*vmsptr2 != ')') && (*lastdot != '.')) {
7689 /* Eventual routine to convert a UTF-8 specification to VTF-7. */
7690 static char * utf8_to_vtf7(char * rslt, const char * path, int *utf8_fl)
7695 /* If a UTF8 flag is being passed, honor it */
7697 if (utf8_fl != NULL) {
7698 utf8_flag = *utf8_fl;
7703 /* If there is a possibility of UTF8, then if any UTF8 characters
7704 are present, then they must be converted to VTF-7
7706 result = strcpy(rslt, path); /* FIX-ME */
7709 result = strcpy(rslt, path);
7715 /*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/
7716 static char *mp_do_tovmsspec
7717 (pTHX_ const char *path, char *buf, int ts, int dir_flag, int * utf8_flag) {
7718 static char __tovmsspec_retbuf[VMS_MAXRSS];
7719 char *rslt, *dirend;
7724 unsigned long int infront = 0, hasdir = 1;
7727 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
7728 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
7730 if (path == NULL) return NULL;
7731 rslt_len = VMS_MAXRSS-1;
7732 if (buf) rslt = buf;
7733 else if (ts) Newx(rslt, VMS_MAXRSS, char);
7734 else rslt = __tovmsspec_retbuf;
7736 /* '.' and '..' are "[]" and "[-]" for a quick check */
7737 if (path[0] == '.') {
7738 if (path[1] == '\0') {
7740 if (utf8_flag != NULL)
7745 if (path[1] == '.' && path[2] == '\0') {
7747 if (utf8_flag != NULL)
7754 /* Posix specifications are now a native VMS format */
7755 /*--------------------------------------------------*/
7756 #if __CRTL_VER >= 80200000 && !defined(__VAX)
7757 if (decc_posix_compliant_pathnames) {
7758 if (strncmp(path,"\"^UP^",5) == 0) {
7759 posix_to_vmsspec_hardway(rslt, rslt_len, path, dir_flag, utf8_flag);
7765 /* This is really the only way to see if this is already in VMS format */
7766 sts = vms_split_path
7781 /* FIX-ME - If dir_flag is non-zero, then this is a mp_do_vmspath()
7782 replacement, because the above parse just took care of most of
7783 what is needed to do vmspath when the specification is already
7786 And if it is not already, it is easier to do the conversion as
7787 part of this routine than to call this routine and then work on
7791 /* If VMS punctuation was found, it is already VMS format */
7792 if ((v_len != 0) || (r_len != 0) || (d_len != 0) || (vs_len != 0)) {
7793 if (utf8_flag != NULL)
7798 /* Now, what to do with trailing "." cases where there is no
7799 extension? If this is a UNIX specification, and EFS characters
7800 are enabled, then the trailing "." should be converted to a "^.".
7801 But if this was already a VMS specification, then it should be
7804 So in the case of ambiguity, leave the specification alone.
7808 /* If there is a possibility of UTF8, then if any UTF8 characters
7809 are present, then they must be converted to VTF-7
7811 if (utf8_flag != NULL)
7817 dirend = strrchr(path,'/');
7819 if (dirend == NULL) {
7820 /* If we get here with no UNIX directory delimiters, then this is
7821 not a complete file specification, either garbage a UNIX glob
7822 specification that can not be converted to a VMS wildcard, or
7823 it a UNIX shell macro. MakeMaker wants these passed through AS-IS,
7824 so apparently other programs expect this also.
7826 utf8 flag setting needs to be preserved.
7832 /* If POSIX mode active, handle the conversion */
7833 #if __CRTL_VER >= 80200000 && !defined(__VAX)
7834 if (decc_efs_charset) {
7835 posix_to_vmsspec_hardway(rslt, rslt_len, path, dir_flag, utf8_flag);
7840 if (*(dirend+1) == '.') { /* do we have trailing "/." or "/.." or "/..."? */
7841 if (!*(dirend+2)) dirend +=2;
7842 if (*(dirend+2) == '.' && !*(dirend+3)) dirend += 3;
7843 if (decc_efs_charset == 0) {
7844 if (*(dirend+2) == '.' && *(dirend+3) == '.' && !*(dirend+4)) dirend += 4;
7850 lastdot = strrchr(cp2,'.');
7856 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
7858 if (decc_disable_posix_root) {
7859 strcpy(rslt,"sys$disk:[000000]");
7862 strcpy(rslt,"sys$posix_root:[000000]");
7864 if (utf8_flag != NULL)
7868 while (*(++cp2) != '/' && *cp2) *(cp1++) = *cp2;
7870 trndev = PerlMem_malloc(VMS_MAXRSS);
7871 if (trndev == NULL) _ckvmssts(SS$_INSFMEM);
7872 islnm = my_trnlnm(rslt,trndev,0);
7874 /* DECC special handling */
7876 if (strcmp(rslt,"bin") == 0) {
7877 strcpy(rslt,"sys$system");
7880 islnm = my_trnlnm(rslt,trndev,0);
7882 else if (strcmp(rslt,"tmp") == 0) {
7883 strcpy(rslt,"sys$scratch");
7886 islnm = my_trnlnm(rslt,trndev,0);
7888 else if (!decc_disable_posix_root) {
7889 strcpy(rslt, "sys$posix_root");
7893 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
7894 islnm = my_trnlnm(rslt,trndev,0);
7896 else if (strcmp(rslt,"dev") == 0) {
7897 if (strncmp(cp2,"/null", 5) == 0) {
7898 if ((cp2[5] == 0) || (cp2[5] == '/')) {
7899 strcpy(rslt,"NLA0");
7903 islnm = my_trnlnm(rslt,trndev,0);
7909 trnend = islnm ? strlen(trndev) - 1 : 0;
7910 islnm = trnend ? (trndev[trnend] == ']' || trndev[trnend] == '>') : 0;
7911 rooted = islnm ? (trndev[trnend-1] == '.') : 0;
7912 /* If the first element of the path is a logical name, determine
7913 * whether it has to be translated so we can add more directories. */
7914 if (!islnm || rooted) {
7917 if (cp2 == dirend) while (infront++ < 6) *(cp1++) = '0';
7921 if (cp2 != dirend) {
7922 strcpy(rslt,trndev);
7923 cp1 = rslt + trnend;
7930 if (decc_disable_posix_root) {
7936 PerlMem_free(trndev);
7941 if (*(cp2+1) == '/' || *(cp2+1) == '\0') {
7942 cp2 += 2; /* skip over "./" - it's redundant */
7943 *(cp1++) = '.'; /* but it does indicate a relative dirspec */
7945 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
7946 *(cp1++) = '-'; /* "../" --> "-" */
7949 else if (*(cp2+1) == '.' && *(cp2+2) == '.' &&
7950 (*(cp2+3) == '/' || *(cp2+3) == '\0')) {
7951 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
7952 if (!*(cp2+4)) *(cp1++) = '.'; /* Simulate trailing '/' for later */
7955 else if ((cp2 != lastdot) || (lastdot < dirend)) {
7956 /* Escape the extra dots in EFS file specifications */
7959 if (cp2 > dirend) cp2 = dirend;
7961 else *(cp1++) = '.';
7963 for (; cp2 < dirend; cp2++) {
7965 if (*(cp2-1) == '/') continue;
7966 if (*(cp1-1) != '.') *(cp1++) = '.';
7969 else if (!infront && *cp2 == '.') {
7970 if (cp2+1 == dirend || *(cp2+1) == '\0') { cp2++; break; }
7971 else if (*(cp2+1) == '/') cp2++; /* skip over "./" - it's redundant */
7972 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
7973 if (*(cp1-1) == '-' || *(cp1-1) == '[') *(cp1++) = '-'; /* handle "../" */
7974 else if (*(cp1-2) == '[') *(cp1-1) = '-';
7975 else { /* back up over previous directory name */
7977 while (*(cp1-1) != '.' && *(cp1-1) != '[') cp1--;
7978 if (*(cp1-1) == '[') {
7979 memcpy(cp1,"000000.",7);
7984 if (cp2 == dirend) break;
7986 else if ( *(cp2+1) == '.' && *(cp2+2) == '.' &&
7987 (*(cp2+3) == '/' || *(cp2+3) == '\0') ) {
7988 if (*(cp1-1) != '.') *(cp1++) = '.'; /* May already have 1 from '/' */
7989 *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
7991 *(cp1++) = '.'; /* Simulate trailing '/' */
7992 cp2 += 2; /* for loop will incr this to == dirend */
7994 else cp2 += 3; /* Trailing '/' was there, so skip it, too */
7997 if (decc_efs_charset == 0)
7998 *(cp1++) = '_'; /* fix up syntax - '.' in name not allowed */
8000 *(cp1++) = '^'; /* fix up syntax - '.' in name is allowed */
8006 if (!infront && *(cp1-1) == '-') *(cp1++) = '.';
8008 if (decc_efs_charset == 0)
8015 else *(cp1++) = *cp2;
8019 if (*(cp1-1) == '.') cp1--; /* Unix spec ending in '/' ==> trailing '.' */
8020 if (hasdir) *(cp1++) = ']';
8021 if (*cp2) cp2++; /* check in case we ended with trailing '..' */
8022 /* fixme for ODS5 */
8029 if (decc_efs_charset == 0)
8040 if (((cp2 < lastdot) || (cp2[1] == '\0')) &&
8041 decc_readdir_dropdotnotype) {
8046 /* trailing dot ==> '^..' on VMS */
8053 *(cp1++) = *(cp2++);
8058 /* This could be a macro to be passed through */
8059 *(cp1++) = *(cp2++);
8061 const char * save_cp2;
8065 /* paranoid check */
8071 *(cp1++) = *(cp2++);
8072 if (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
8073 *(cp1++) = *(cp2++);
8074 while (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
8075 *(cp1++) = *(cp2++);
8078 *(cp1++) = *(cp2++);
8082 if (is_macro == 0) {
8083 /* Not really a macro - never mind */
8096 /* Don't escape again if following character is
8097 * already something we escape.
8099 if (strchr("\"~`!#%^&()=+\'@[]{}:\\|<>_.", *(cp2+1))) {
8100 *(cp1++) = *(cp2++);
8103 /* But otherwise fall through and escape it. */
8121 *(cp1++) = *(cp2++);
8124 /* FIXME: This needs fixing as Perl is putting ".dir;" on UNIX filespecs
8125 * which is wrong. UNIX notation should be ".dir." unless
8126 * the DECC$FILENAME_UNIX_NO_VERSION is enabled.
8127 * changing this behavior could break more things at this time.
8128 * efs character set effectively does not allow "." to be a version
8129 * delimiter as a further complication about changing this.
8131 if (decc_filename_unix_report != 0) {
8134 *(cp1++) = *(cp2++);
8137 *(cp1++) = *(cp2++);
8140 if ((no_type_seen == 1) && decc_readdir_dropdotnotype) {
8144 /* Fix me for "^]", but that requires making sure that you do
8145 * not back up past the start of the filename
8147 if ((*lcp1 != ']') && (*lcp1 != '*') && (*lcp1 != '%'))
8152 if (utf8_flag != NULL)
8156 } /* end of do_tovmsspec() */
8158 /* External entry points */
8159 char *Perl_tovmsspec(pTHX_ const char *path, char *buf)
8160 { return do_tovmsspec(path,buf,0,NULL); }
8161 char *Perl_tovmsspec_ts(pTHX_ const char *path, char *buf)
8162 { return do_tovmsspec(path,buf,1,NULL); }
8163 char *Perl_tovmsspec_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
8164 { return do_tovmsspec(path,buf,0,utf8_fl); }
8165 char *Perl_tovmsspec_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
8166 { return do_tovmsspec(path,buf,1,utf8_fl); }
8168 /*{{{ char *tovmspath[_ts](char *path, char *buf, const int *)*/
8169 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) {
8170 static char __tovmspath_retbuf[VMS_MAXRSS];
8172 char *pathified, *vmsified, *cp;
8174 if (path == NULL) return NULL;
8175 pathified = PerlMem_malloc(VMS_MAXRSS);
8176 if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
8177 if (do_pathify_dirspec(path,pathified,0,NULL) == NULL) {
8178 PerlMem_free(pathified);
8184 Newx(vmsified, VMS_MAXRSS, char);
8185 if (do_tovmsspec(pathified, buf ? buf : vmsified, 0, NULL) == NULL) {
8186 PerlMem_free(pathified);
8187 if (vmsified) Safefree(vmsified);
8190 PerlMem_free(pathified);
8195 vmslen = strlen(vmsified);
8196 Newx(cp,vmslen+1,char);
8197 memcpy(cp,vmsified,vmslen);
8203 strcpy(__tovmspath_retbuf,vmsified);
8205 return __tovmspath_retbuf;
8208 } /* end of do_tovmspath() */
8210 /* External entry points */
8211 char *Perl_tovmspath(pTHX_ const char *path, char *buf)
8212 { return do_tovmspath(path,buf,0, NULL); }
8213 char *Perl_tovmspath_ts(pTHX_ const char *path, char *buf)
8214 { return do_tovmspath(path,buf,1, NULL); }
8215 char *Perl_tovmspath_utf8(pTHX_ const char *path, char *buf, int *utf8_fl)
8216 { return do_tovmspath(path,buf,0,utf8_fl); }
8217 char *Perl_tovmspath_utf8_ts(pTHX_ const char *path, char *buf, int *utf8_fl)
8218 { return do_tovmspath(path,buf,1,utf8_fl); }
8221 /*{{{ char *tounixpath[_ts](char *path, char *buf, int * utf8_fl)*/
8222 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) {
8223 static char __tounixpath_retbuf[VMS_MAXRSS];
8225 char *pathified, *unixified, *cp;
8227 if (path == NULL) return NULL;
8228 pathified = PerlMem_malloc(VMS_MAXRSS);
8229 if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
8230 if (do_pathify_dirspec(path,pathified,0,NULL) == NULL) {
8231 PerlMem_free(pathified);
8237 Newx(unixified, VMS_MAXRSS, char);
8239 if (do_tounixspec(pathified,buf ? buf : unixified,0,NULL) == NULL) {
8240 PerlMem_free(pathified);
8241 if (unixified) Safefree(unixified);
8244 PerlMem_free(pathified);
8249 unixlen = strlen(unixified);
8250 Newx(cp,unixlen+1,char);
8251 memcpy(cp,unixified,unixlen);
8253 Safefree(unixified);
8257 strcpy(__tounixpath_retbuf,unixified);
8258 Safefree(unixified);
8259 return __tounixpath_retbuf;
8262 } /* end of do_tounixpath() */
8264 /* External entry points */
8265 char *Perl_tounixpath(pTHX_ const char *path, char *buf)
8266 { return do_tounixpath(path,buf,0,NULL); }
8267 char *Perl_tounixpath_ts(pTHX_ const char *path, char *buf)
8268 { return do_tounixpath(path,buf,1,NULL); }
8269 char *Perl_tounixpath_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
8270 { return do_tounixpath(path,buf,0,utf8_fl); }
8271 char *Perl_tounixpath_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
8272 { return do_tounixpath(path,buf,1,utf8_fl); }
8275 * @(#)argproc.c 2.2 94/08/16 Mark Pizzolato (mark AT infocomm DOT com)
8277 *****************************************************************************
8279 * Copyright (C) 1989-1994, 2007 by *
8280 * Mark Pizzolato - INFO COMM, Danville, California (510) 837-5600 *
8282 * Permission is hereby granted for the reproduction of this software *
8283 * on condition that this copyright notice is included in source *
8284 * distributions of the software. The code may be modified and *
8285 * distributed under the same terms as Perl itself. *
8287 * 27-Aug-1994 Modified for inclusion in perl5 *
8288 * by Charles Bailey (bailey AT newman DOT upenn DOT edu) *
8289 *****************************************************************************
8293 * getredirection() is intended to aid in porting C programs
8294 * to VMS (Vax-11 C). The native VMS environment does not support
8295 * '>' and '<' I/O redirection, or command line wild card expansion,
8296 * or a command line pipe mechanism using the '|' AND background
8297 * command execution '&'. All of these capabilities are provided to any
8298 * C program which calls this procedure as the first thing in the
8300 * The piping mechanism will probably work with almost any 'filter' type
8301 * of program. With suitable modification, it may useful for other
8302 * portability problems as well.
8304 * Author: Mark Pizzolato (mark AT infocomm DOT com)
8308 struct list_item *next;
8312 static void add_item(struct list_item **head,
8313 struct list_item **tail,
8317 static void mp_expand_wild_cards(pTHX_ char *item,
8318 struct list_item **head,
8319 struct list_item **tail,
8322 static int background_process(pTHX_ int argc, char **argv);
8324 static void pipe_and_fork(pTHX_ char **cmargv);
8326 /*{{{ void getredirection(int *ac, char ***av)*/
8328 mp_getredirection(pTHX_ int *ac, char ***av)
8330 * Process vms redirection arg's. Exit if any error is seen.
8331 * If getredirection() processes an argument, it is erased
8332 * from the vector. getredirection() returns a new argc and argv value.
8333 * In the event that a background command is requested (by a trailing "&"),
8334 * this routine creates a background subprocess, and simply exits the program.
8336 * Warning: do not try to simplify the code for vms. The code
8337 * presupposes that getredirection() is called before any data is
8338 * read from stdin or written to stdout.
8340 * Normal usage is as follows:
8346 * getredirection(&argc, &argv);
8350 int argc = *ac; /* Argument Count */
8351 char **argv = *av; /* Argument Vector */
8352 char *ap; /* Argument pointer */
8353 int j; /* argv[] index */
8354 int item_count = 0; /* Count of Items in List */
8355 struct list_item *list_head = 0; /* First Item in List */
8356 struct list_item *list_tail; /* Last Item in List */
8357 char *in = NULL; /* Input File Name */
8358 char *out = NULL; /* Output File Name */
8359 char *outmode = "w"; /* Mode to Open Output File */
8360 char *err = NULL; /* Error File Name */
8361 char *errmode = "w"; /* Mode to Open Error File */
8362 int cmargc = 0; /* Piped Command Arg Count */
8363 char **cmargv = NULL;/* Piped Command Arg Vector */
8366 * First handle the case where the last thing on the line ends with
8367 * a '&'. This indicates the desire for the command to be run in a
8368 * subprocess, so we satisfy that desire.
8371 if (0 == strcmp("&", ap))
8372 exit(background_process(aTHX_ --argc, argv));
8373 if (*ap && '&' == ap[strlen(ap)-1])
8375 ap[strlen(ap)-1] = '\0';
8376 exit(background_process(aTHX_ argc, argv));
8379 * Now we handle the general redirection cases that involve '>', '>>',
8380 * '<', and pipes '|'.
8382 for (j = 0; j < argc; ++j)
8384 if (0 == strcmp("<", argv[j]))
8388 fprintf(stderr,"No input file after < on command line");
8389 exit(LIB$_WRONUMARG);
8394 if ('<' == *(ap = argv[j]))
8399 if (0 == strcmp(">", ap))
8403 fprintf(stderr,"No output file after > on command line");
8404 exit(LIB$_WRONUMARG);
8423 fprintf(stderr,"No output file after > or >> on command line");
8424 exit(LIB$_WRONUMARG);
8428 if (('2' == *ap) && ('>' == ap[1]))
8445 fprintf(stderr,"No output file after 2> or 2>> on command line");
8446 exit(LIB$_WRONUMARG);
8450 if (0 == strcmp("|", argv[j]))
8454 fprintf(stderr,"No command into which to pipe on command line");
8455 exit(LIB$_WRONUMARG);
8457 cmargc = argc-(j+1);
8458 cmargv = &argv[j+1];
8462 if ('|' == *(ap = argv[j]))
8470 expand_wild_cards(ap, &list_head, &list_tail, &item_count);
8473 * Allocate and fill in the new argument vector, Some Unix's terminate
8474 * the list with an extra null pointer.
8476 argv = (char **) PerlMem_malloc((item_count+1) * sizeof(char *));
8477 if (argv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8479 for (j = 0; j < item_count; ++j, list_head = list_head->next)
8480 argv[j] = list_head->value;
8486 fprintf(stderr,"'|' and '>' may not both be specified on command line");
8487 exit(LIB$_INVARGORD);
8489 pipe_and_fork(aTHX_ cmargv);
8492 /* Check for input from a pipe (mailbox) */
8494 if (in == NULL && 1 == isapipe(0))
8496 char mbxname[L_tmpnam];
8498 long int dvi_item = DVI$_DEVBUFSIZ;
8499 $DESCRIPTOR(mbxnam, "");
8500 $DESCRIPTOR(mbxdevnam, "");
8502 /* Input from a pipe, reopen it in binary mode to disable */
8503 /* carriage control processing. */
8505 fgetname(stdin, mbxname);
8506 mbxnam.dsc$a_pointer = mbxname;
8507 mbxnam.dsc$w_length = strlen(mbxnam.dsc$a_pointer);
8508 lib$getdvi(&dvi_item, 0, &mbxnam, &bufsize, 0, 0);
8509 mbxdevnam.dsc$a_pointer = mbxname;
8510 mbxdevnam.dsc$w_length = sizeof(mbxname);
8511 dvi_item = DVI$_DEVNAM;
8512 lib$getdvi(&dvi_item, 0, &mbxnam, 0, &mbxdevnam, &mbxdevnam.dsc$w_length);
8513 mbxdevnam.dsc$a_pointer[mbxdevnam.dsc$w_length] = '\0';
8516 freopen(mbxname, "rb", stdin);
8519 fprintf(stderr,"Can't reopen input pipe (name: %s) in binary mode",mbxname);
8523 if ((in != NULL) && (NULL == freopen(in, "r", stdin, "mbc=32", "mbf=2")))
8525 fprintf(stderr,"Can't open input file %s as stdin",in);
8528 if ((out != NULL) && (NULL == freopen(out, outmode, stdout, "mbc=32", "mbf=2")))
8530 fprintf(stderr,"Can't open output file %s as stdout",out);
8533 if (out != NULL) Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",out);
8536 if (strcmp(err,"&1") == 0) {
8537 dup2(fileno(stdout), fileno(stderr));
8538 Perl_vmssetuserlnm(aTHX_ "SYS$ERROR","SYS$OUTPUT");
8541 if (NULL == (tmperr = fopen(err, errmode, "mbc=32", "mbf=2")))
8543 fprintf(stderr,"Can't open error file %s as stderr",err);
8547 if (NULL == freopen(err, "a", stderr, "mbc=32", "mbf=2"))
8551 Perl_vmssetuserlnm(aTHX_ "SYS$ERROR",err);
8554 #ifdef ARGPROC_DEBUG
8555 PerlIO_printf(Perl_debug_log, "Arglist:\n");
8556 for (j = 0; j < *ac; ++j)
8557 PerlIO_printf(Perl_debug_log, "argv[%d] = '%s'\n", j, argv[j]);
8559 /* Clear errors we may have hit expanding wildcards, so they don't
8560 show up in Perl's $! later */
8561 set_errno(0); set_vaxc_errno(1);
8562 } /* end of getredirection() */
8565 static void add_item(struct list_item **head,
8566 struct list_item **tail,
8572 *head = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
8573 if (head == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8577 (*tail)->next = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
8578 if ((*tail)->next == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8579 *tail = (*tail)->next;
8581 (*tail)->value = value;
8585 static void mp_expand_wild_cards(pTHX_ char *item,
8586 struct list_item **head,
8587 struct list_item **tail,
8591 unsigned long int context = 0;
8599 $DESCRIPTOR(filespec, "");
8600 $DESCRIPTOR(defaultspec, "SYS$DISK:[]");
8601 $DESCRIPTOR(resultspec, "");
8602 unsigned long int lff_flags = 0;
8606 #ifdef VMS_LONGNAME_SUPPORT
8607 lff_flags = LIB$M_FIL_LONG_NAMES;
8610 for (cp = item; *cp; cp++) {
8611 if (*cp == '*' || *cp == '%' || isspace(*cp)) break;
8612 if (*cp == '.' && *(cp-1) == '.' && *(cp-2) =='.') break;
8614 if (!*cp || isspace(*cp))
8616 add_item(head, tail, item, count);
8621 /* "double quoted" wild card expressions pass as is */
8622 /* From DCL that means using e.g.: */
8623 /* perl program """perl.*""" */
8624 item_len = strlen(item);
8625 if ( '"' == *item && '"' == item[item_len-1] )
8628 item[item_len-2] = '\0';
8629 add_item(head, tail, item, count);
8633 resultspec.dsc$b_dtype = DSC$K_DTYPE_T;
8634 resultspec.dsc$b_class = DSC$K_CLASS_D;
8635 resultspec.dsc$a_pointer = NULL;
8636 vmsspec = PerlMem_malloc(VMS_MAXRSS);
8637 if (vmsspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8638 if ((isunix = (int) strchr(item,'/')) != (int) NULL)
8639 filespec.dsc$a_pointer = do_tovmsspec(item,vmsspec,0,NULL);
8640 if (!isunix || !filespec.dsc$a_pointer)
8641 filespec.dsc$a_pointer = item;
8642 filespec.dsc$w_length = strlen(filespec.dsc$a_pointer);
8644 * Only return version specs, if the caller specified a version
8646 had_version = strchr(item, ';');
8648 * Only return device and directory specs, if the caller specifed either.
8650 had_device = strchr(item, ':');
8651 had_directory = (isunix || NULL != strchr(item, '[')) || (NULL != strchr(item, '<'));
8653 while ($VMS_STATUS_SUCCESS(sts = lib$find_file
8654 (&filespec, &resultspec, &context,
8655 &defaultspec, 0, &rms_sts, &lff_flags)))
8660 string = PerlMem_malloc(resultspec.dsc$w_length+1);
8661 if (string == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8662 strncpy(string, resultspec.dsc$a_pointer, resultspec.dsc$w_length);
8663 string[resultspec.dsc$w_length] = '\0';
8664 if (NULL == had_version)
8665 *(strrchr(string, ';')) = '\0';
8666 if ((!had_directory) && (had_device == NULL))
8668 if (NULL == (devdir = strrchr(string, ']')))
8669 devdir = strrchr(string, '>');
8670 strcpy(string, devdir + 1);
8673 * Be consistent with what the C RTL has already done to the rest of
8674 * the argv items and lowercase all of these names.
8676 if (!decc_efs_case_preserve) {
8677 for (c = string; *c; ++c)
8681 if (isunix) trim_unixpath(string,item,1);
8682 add_item(head, tail, string, count);
8685 PerlMem_free(vmsspec);
8686 if (sts != RMS$_NMF)
8688 set_vaxc_errno(sts);
8691 case RMS$_FNF: case RMS$_DNF:
8692 set_errno(ENOENT); break;
8694 set_errno(ENOTDIR); break;
8696 set_errno(ENODEV); break;
8697 case RMS$_FNM: case RMS$_SYN:
8698 set_errno(EINVAL); break;
8700 set_errno(EACCES); break;
8702 _ckvmssts_noperl(sts);
8706 add_item(head, tail, item, count);
8707 _ckvmssts_noperl(lib$sfree1_dd(&resultspec));
8708 _ckvmssts_noperl(lib$find_file_end(&context));
8711 static int child_st[2];/* Event Flag set when child process completes */
8713 static unsigned short child_chan;/* I/O Channel for Pipe Mailbox */
8715 static unsigned long int exit_handler(int *status)
8719 if (0 == child_st[0])
8721 #ifdef ARGPROC_DEBUG
8722 PerlIO_printf(Perl_debug_log, "Waiting for Child Process to Finish . . .\n");
8724 fflush(stdout); /* Have to flush pipe for binary data to */
8725 /* terminate properly -- <tp@mccall.com> */
8726 sys$qiow(0, child_chan, IO$_WRITEOF, iosb, 0, 0, 0, 0, 0, 0, 0, 0);
8727 sys$dassgn(child_chan);
8729 sys$synch(0, child_st);
8734 static void sig_child(int chan)
8736 #ifdef ARGPROC_DEBUG
8737 PerlIO_printf(Perl_debug_log, "Child Completion AST\n");
8739 if (child_st[0] == 0)
8743 static struct exit_control_block exit_block =
8748 &exit_block.exit_status,
8753 pipe_and_fork(pTHX_ char **cmargv)
8756 struct dsc$descriptor_s *vmscmd;
8757 char subcmd[2*MAX_DCL_LINE_LENGTH], *p, *q;
8758 int sts, j, l, ismcr, quote, tquote = 0;
8760 sts = setup_cmddsc(aTHX_ cmargv[0],0,"e,&vmscmd);
8761 vms_execfree(vmscmd);
8766 ismcr = q && toupper(*q) == 'M' && toupper(*(q+1)) == 'C'
8767 && toupper(*(q+2)) == 'R' && !*(q+3);
8769 while (q && l < MAX_DCL_LINE_LENGTH) {
8771 if (j > 0 && quote) {
8777 if (ismcr && j > 1) quote = 1;
8778 tquote = (strchr(q,' ')) != NULL || *q == '\0';
8781 if (quote || tquote) {
8787 if ((quote||tquote) && *q == '"') {
8797 fp = safe_popen(aTHX_ subcmd,"wbF",&sts);
8799 PerlIO_printf(Perl_debug_log,"Can't open output pipe (status %d)",sts);
8803 static int background_process(pTHX_ int argc, char **argv)
8805 char command[MAX_DCL_SYMBOL + 1] = "$";
8806 $DESCRIPTOR(value, "");
8807 static $DESCRIPTOR(cmd, "BACKGROUND$COMMAND");
8808 static $DESCRIPTOR(null, "NLA0:");
8809 static $DESCRIPTOR(pidsymbol, "SHELL_BACKGROUND_PID");
8811 $DESCRIPTOR(pidstr, "");
8813 unsigned long int flags = 17, one = 1, retsts;
8816 strcat(command, argv[0]);
8817 len = strlen(command);
8818 while (--argc && (len < MAX_DCL_SYMBOL))
8820 strcat(command, " \"");
8821 strcat(command, *(++argv));
8822 strcat(command, "\"");
8823 len = strlen(command);
8825 value.dsc$a_pointer = command;
8826 value.dsc$w_length = strlen(value.dsc$a_pointer);
8827 _ckvmssts_noperl(lib$set_symbol(&cmd, &value));
8828 retsts = lib$spawn(&cmd, &null, 0, &flags, 0, &pid);
8829 if (retsts == 0x38250) { /* DCL-W-NOTIFY - We must be BATCH, so retry */
8830 _ckvmssts_noperl(lib$spawn(&cmd, &null, 0, &one, 0, &pid));
8833 _ckvmssts_noperl(retsts);
8835 #ifdef ARGPROC_DEBUG
8836 PerlIO_printf(Perl_debug_log, "%s\n", command);
8838 sprintf(pidstring, "%08X", pid);
8839 PerlIO_printf(Perl_debug_log, "%s\n", pidstring);
8840 pidstr.dsc$a_pointer = pidstring;
8841 pidstr.dsc$w_length = strlen(pidstr.dsc$a_pointer);
8842 lib$set_symbol(&pidsymbol, &pidstr);
8846 /***** End of code taken from Mark Pizzolato's argproc.c package *****/
8849 /* OS-specific initialization at image activation (not thread startup) */
8850 /* Older VAXC header files lack these constants */
8851 #ifndef JPI$_RIGHTS_SIZE
8852 # define JPI$_RIGHTS_SIZE 817
8854 #ifndef KGB$M_SUBSYSTEM
8855 # define KGB$M_SUBSYSTEM 0x8
8858 /* Avoid Newx() in vms_image_init as thread context has not been initialized. */
8860 /*{{{void vms_image_init(int *, char ***)*/
8862 vms_image_init(int *argcp, char ***argvp)
8864 char eqv[LNM$C_NAMLENGTH+1] = "";
8865 unsigned int len, tabct = 8, tabidx = 0;
8866 unsigned long int *mask, iosb[2], i, rlst[128], rsz;
8867 unsigned long int iprv[(sizeof(union prvdef) + sizeof(unsigned long int) - 1) / sizeof(unsigned long int)];
8868 unsigned short int dummy, rlen;
8869 struct dsc$descriptor_s **tabvec;
8870 #if defined(PERL_IMPLICIT_CONTEXT)
8873 struct itmlst_3 jpilist[4] = { {sizeof iprv, JPI$_IMAGPRIV, iprv, &dummy},
8874 {sizeof rlst, JPI$_RIGHTSLIST, rlst, &rlen},
8875 { sizeof rsz, JPI$_RIGHTS_SIZE, &rsz, &dummy},
8878 #ifdef KILL_BY_SIGPRC
8879 Perl_csighandler_init();
8882 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,jpilist,iosb,NULL,NULL));
8883 _ckvmssts_noperl(iosb[0]);
8884 for (i = 0; i < sizeof iprv / sizeof(unsigned long int); i++) {
8885 if (iprv[i]) { /* Running image installed with privs? */
8886 _ckvmssts_noperl(sys$setprv(0,iprv,0,NULL)); /* Turn 'em off. */
8891 /* Rights identifiers might trigger tainting as well. */
8892 if (!will_taint && (rlen || rsz)) {
8893 while (rlen < rsz) {
8894 /* We didn't get all the identifiers on the first pass. Allocate a
8895 * buffer much larger than $GETJPI wants (rsz is size in bytes that
8896 * were needed to hold all identifiers at time of last call; we'll
8897 * allocate that many unsigned long ints), and go back and get 'em.
8898 * If it gave us less than it wanted to despite ample buffer space,
8899 * something's broken. Is your system missing a system identifier?
8901 if (rsz <= jpilist[1].buflen) {
8902 /* Perl_croak accvios when used this early in startup. */
8903 fprintf(stderr, "vms_image_init: $getjpiw refuses to store RIGHTSLIST of %u bytes in buffer of %u bytes.\n%s",
8904 rsz, (unsigned long) jpilist[1].buflen,
8905 "Check your rights database for corruption.\n");
8908 if (jpilist[1].bufadr != rlst) PerlMem_free(jpilist[1].bufadr);
8909 jpilist[1].bufadr = mask = (unsigned long int *) PerlMem_malloc(rsz * sizeof(unsigned long int));
8910 if (mask == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8911 jpilist[1].buflen = rsz * sizeof(unsigned long int);
8912 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,&jpilist[1],iosb,NULL,NULL));
8913 _ckvmssts_noperl(iosb[0]);
8915 mask = jpilist[1].bufadr;
8916 /* Check attribute flags for each identifier (2nd longword); protected
8917 * subsystem identifiers trigger tainting.
8919 for (i = 1; i < (rlen + sizeof(unsigned long int) - 1) / sizeof(unsigned long int); i += 2) {
8920 if (mask[i] & KGB$M_SUBSYSTEM) {
8925 if (mask != rlst) PerlMem_free(mask);
8928 /* When Perl is in decc_filename_unix_report mode and is run from a concealed
8929 * logical, some versions of the CRTL will add a phanthom /000000/
8930 * directory. This needs to be removed.
8932 if (decc_filename_unix_report) {
8935 ulen = strlen(argvp[0][0]);
8937 zeros = strstr(argvp[0][0], "/000000/");
8938 if (zeros != NULL) {
8940 mlen = ulen - (zeros - argvp[0][0]) - 7;
8941 memmove(zeros, &zeros[7], mlen);
8943 argvp[0][0][ulen] = '\0';
8946 /* It also may have a trailing dot that needs to be removed otherwise
8947 * it will be converted to VMS mode incorrectly.
8950 if ((argvp[0][0][ulen] == '.') && (decc_readdir_dropdotnotype))
8951 argvp[0][0][ulen] = '\0';
8954 /* We need to use this hack to tell Perl it should run with tainting,
8955 * since its tainting flag may be part of the PL_curinterp struct, which
8956 * hasn't been allocated when vms_image_init() is called.
8959 char **newargv, **oldargv;
8961 newargv = (char **) PerlMem_malloc(((*argcp)+2) * sizeof(char *));
8962 if (newargv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8963 newargv[0] = oldargv[0];
8964 newargv[1] = PerlMem_malloc(3 * sizeof(char));
8965 if (newargv[1] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8966 strcpy(newargv[1], "-T");
8967 Copy(&oldargv[1],&newargv[2],(*argcp)-1,char **);
8969 newargv[*argcp] = NULL;
8970 /* We orphan the old argv, since we don't know where it's come from,
8971 * so we don't know how to free it.
8975 else { /* Did user explicitly request tainting? */
8977 char *cp, **av = *argvp;
8978 for (i = 1; i < *argcp; i++) {
8979 if (*av[i] != '-') break;
8980 for (cp = av[i]+1; *cp; cp++) {
8981 if (*cp == 'T') { will_taint = 1; break; }
8982 else if ( (*cp == 'd' || *cp == 'V') && *(cp+1) == ':' ||
8983 strchr("DFIiMmx",*cp)) break;
8985 if (will_taint) break;
8990 len = my_trnlnm("PERL_ENV_TABLES",eqv,tabidx);
8993 tabvec = (struct dsc$descriptor_s **)
8994 PerlMem_malloc(tabct * sizeof(struct dsc$descriptor_s *));
8995 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8997 else if (tabidx >= tabct) {
8999 tabvec = (struct dsc$descriptor_s **) PerlMem_realloc(tabvec, tabct * sizeof(struct dsc$descriptor_s *));
9000 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9002 tabvec[tabidx] = (struct dsc$descriptor_s *) PerlMem_malloc(sizeof(struct dsc$descriptor_s));
9003 if (tabvec[tabidx] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9004 tabvec[tabidx]->dsc$w_length = 0;
9005 tabvec[tabidx]->dsc$b_dtype = DSC$K_DTYPE_T;
9006 tabvec[tabidx]->dsc$b_class = DSC$K_CLASS_D;
9007 tabvec[tabidx]->dsc$a_pointer = NULL;
9008 _ckvmssts_noperl(lib$scopy_r_dx(&len,eqv,tabvec[tabidx]));
9010 if (tabidx) { tabvec[tabidx] = NULL; env_tables = tabvec; }
9012 getredirection(argcp,argvp);
9013 #if defined(USE_ITHREADS) && ( defined(__DECC) || defined(__DECCXX) )
9015 # include <reentrancy.h>
9016 decc$set_reentrancy(C$C_MULTITHREAD);
9025 * Trim Unix-style prefix off filespec, so it looks like what a shell
9026 * glob expansion would return (i.e. from specified prefix on, not
9027 * full path). Note that returned filespec is Unix-style, regardless
9028 * of whether input filespec was VMS-style or Unix-style.
9030 * fspec is filespec to be trimmed, and wildspec is wildcard spec used to
9031 * determine prefix (both may be in VMS or Unix syntax). opts is a bit
9032 * vector of options; at present, only bit 0 is used, and if set tells
9033 * trim unixpath to try the current default directory as a prefix when
9034 * presented with a possibly ambiguous ... wildcard.
9036 * Returns !=0 on success, with trimmed filespec replacing contents of
9037 * fspec, and 0 on failure, with contents of fpsec unchanged.
9039 /*{{{int trim_unixpath(char *fspec, char *wildspec, int opts)*/
9041 Perl_trim_unixpath(pTHX_ char *fspec, const char *wildspec, int opts)
9043 char *unixified, *unixwild,
9044 *template, *base, *end, *cp1, *cp2;
9045 register int tmplen, reslen = 0, dirs = 0;
9047 unixwild = PerlMem_malloc(VMS_MAXRSS);
9048 if (unixwild == NULL) _ckvmssts(SS$_INSFMEM);
9049 if (!wildspec || !fspec) return 0;
9050 template = unixwild;
9051 if (strpbrk(wildspec,"]>:") != NULL) {
9052 if (do_tounixspec(wildspec,unixwild,0,NULL) == NULL) {
9053 PerlMem_free(unixwild);
9058 strncpy(unixwild, wildspec, VMS_MAXRSS-1);
9059 unixwild[VMS_MAXRSS-1] = 0;
9061 unixified = PerlMem_malloc(VMS_MAXRSS);
9062 if (unixified == NULL) _ckvmssts(SS$_INSFMEM);
9063 if (strpbrk(fspec,"]>:") != NULL) {
9064 if (do_tounixspec(fspec,unixified,0,NULL) == NULL) {
9065 PerlMem_free(unixwild);
9066 PerlMem_free(unixified);
9069 else base = unixified;
9070 /* reslen != 0 ==> we had to unixify resultant filespec, so we must
9071 * check to see that final result fits into (isn't longer than) fspec */
9072 reslen = strlen(fspec);
9076 /* No prefix or absolute path on wildcard, so nothing to remove */
9077 if (!*template || *template == '/') {
9078 PerlMem_free(unixwild);
9079 if (base == fspec) {
9080 PerlMem_free(unixified);
9083 tmplen = strlen(unixified);
9084 if (tmplen > reslen) {
9085 PerlMem_free(unixified);
9086 return 0; /* not enough space */
9088 /* Copy unixified resultant, including trailing NUL */
9089 memmove(fspec,unixified,tmplen+1);
9090 PerlMem_free(unixified);
9094 for (end = base; *end; end++) ; /* Find end of resultant filespec */
9095 if ((cp1 = strstr(template,".../")) == NULL) { /* No ...; just count elts */
9096 for (cp1 = template; *cp1; cp1++) if (*cp1 == '/') dirs++;
9097 for (cp1 = end ;cp1 >= base; cp1--)
9098 if ((*cp1 == '/') && !dirs--) /* postdec so we get front of rel path */
9100 if (cp1 != fspec) memmove(fspec,cp1, end - cp1 + 1);
9101 PerlMem_free(unixified);
9102 PerlMem_free(unixwild);
9107 char *front, *nextell, *lcend, *lcfront, *ellipsis = cp1;
9108 int ells = 1, totells, segdirs, match;
9109 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL},
9110 resdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
9112 while ((cp1 = strstr(ellipsis+4,".../")) != NULL) {ellipsis = cp1; ells++;}
9114 for (cp1 = ellipsis+4; *cp1; cp1++) if (*cp1 == '/') dirs++;
9115 tpl = PerlMem_malloc(VMS_MAXRSS);
9116 if (tpl == NULL) _ckvmssts(SS$_INSFMEM);
9117 if (ellipsis == template && opts & 1) {
9118 /* Template begins with an ellipsis. Since we can't tell how many
9119 * directory names at the front of the resultant to keep for an
9120 * arbitrary starting point, we arbitrarily choose the current
9121 * default directory as a starting point. If it's there as a prefix,
9122 * clip it off. If not, fall through and act as if the leading
9123 * ellipsis weren't there (i.e. return shortest possible path that
9124 * could match template).
9126 if (getcwd(tpl, (VMS_MAXRSS - 1),0) == NULL) {
9128 PerlMem_free(unixified);
9129 PerlMem_free(unixwild);
9132 if (!decc_efs_case_preserve) {
9133 for (cp1 = tpl, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
9134 if (_tolower(*cp1) != _tolower(*cp2)) break;
9136 segdirs = dirs - totells; /* Min # of dirs we must have left */
9137 for (front = cp2+1; *front; front++) if (*front == '/') segdirs--;
9138 if (*cp1 == '\0' && *cp2 == '/' && segdirs < 1) {
9139 memmove(fspec,cp2+1,end - cp2);
9141 PerlMem_free(unixified);
9142 PerlMem_free(unixwild);
9146 /* First off, back up over constant elements at end of path */
9148 for (front = end ; front >= base; front--)
9149 if (*front == '/' && !dirs--) { front++; break; }
9151 lcres = PerlMem_malloc(VMS_MAXRSS);
9152 if (lcres == NULL) _ckvmssts(SS$_INSFMEM);
9153 for (cp1=template,cp2=lcres; *cp1 && cp2 <= lcres + (VMS_MAXRSS - 1);
9155 if (!decc_efs_case_preserve) {
9156 *cp2 = _tolower(*cp1); /* Make lc copy for match */
9164 PerlMem_free(unixified);
9165 PerlMem_free(unixwild);
9166 PerlMem_free(lcres);
9167 return 0; /* Path too long. */
9170 *cp2 = '\0'; /* Pick up with memcpy later */
9171 lcfront = lcres + (front - base);
9172 /* Now skip over each ellipsis and try to match the path in front of it. */
9174 for (cp1 = ellipsis - 2; cp1 >= template; cp1--)
9175 if (*(cp1) == '.' && *(cp1+1) == '.' &&
9176 *(cp1+2) == '.' && *(cp1+3) == '/' ) break;
9177 if (cp1 < template) break; /* template started with an ellipsis */
9178 if (cp1 + 4 == ellipsis) { /* Consecutive ellipses */
9179 ellipsis = cp1; continue;
9181 wilddsc.dsc$a_pointer = tpl;
9182 wilddsc.dsc$w_length = resdsc.dsc$w_length = ellipsis - 1 - cp1;
9184 for (segdirs = 0, cp2 = tpl;
9185 cp1 <= ellipsis - 1 && cp2 <= tpl + (VMS_MAXRSS-1);
9187 if (*cp1 == '?') *cp2 = '%'; /* Substitute VMS' wildcard for Unix' */
9189 if (!decc_efs_case_preserve) {
9190 *cp2 = _tolower(*cp1); /* else lowercase for match */
9193 *cp2 = *cp1; /* else preserve case for match */
9196 if (*cp2 == '/') segdirs++;
9198 if (cp1 != ellipsis - 1) {
9200 PerlMem_free(unixified);
9201 PerlMem_free(unixwild);
9202 PerlMem_free(lcres);
9203 return 0; /* Path too long */
9205 /* Back up at least as many dirs as in template before matching */
9206 for (cp1 = lcfront - 1; segdirs && cp1 >= lcres; cp1--)
9207 if (*cp1 == '/' && !segdirs--) { cp1++; break; }
9208 for (match = 0; cp1 > lcres;) {
9209 resdsc.dsc$a_pointer = cp1;
9210 if (str$match_wild(&wilddsc,&resdsc) == STR$_MATCH) {
9212 if (match == 1) lcfront = cp1;
9214 for ( ; cp1 >= lcres; cp1--) if (*cp1 == '/') { cp1++; break; }
9218 PerlMem_free(unixified);
9219 PerlMem_free(unixwild);
9220 PerlMem_free(lcres);
9221 return 0; /* Can't find prefix ??? */
9223 if (match > 1 && opts & 1) {
9224 /* This ... wildcard could cover more than one set of dirs (i.e.
9225 * a set of similar dir names is repeated). If the template
9226 * contains more than 1 ..., upstream elements could resolve the
9227 * ambiguity, but it's not worth a full backtracking setup here.
9228 * As a quick heuristic, clip off the current default directory
9229 * if it's present to find the trimmed spec, else use the
9230 * shortest string that this ... could cover.
9232 char def[NAM$C_MAXRSS+1], *st;
9234 if (getcwd(def, sizeof def,0) == NULL) {
9235 Safefree(unixified);
9241 if (!decc_efs_case_preserve) {
9242 for (cp1 = def, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
9243 if (_tolower(*cp1) != _tolower(*cp2)) break;
9245 segdirs = dirs - totells; /* Min # of dirs we must have left */
9246 for (st = cp2+1; *st; st++) if (*st == '/') segdirs--;
9247 if (*cp1 == '\0' && *cp2 == '/') {
9248 memmove(fspec,cp2+1,end - cp2);
9250 PerlMem_free(unixified);
9251 PerlMem_free(unixwild);
9252 PerlMem_free(lcres);
9255 /* Nope -- stick with lcfront from above and keep going. */
9258 memmove(fspec,base + (lcfront - lcres), lcend - lcfront + 1);
9260 PerlMem_free(unixified);
9261 PerlMem_free(unixwild);
9262 PerlMem_free(lcres);
9267 } /* end of trim_unixpath() */
9272 * VMS readdir() routines.
9273 * Written by Rich $alz, <rsalz@bbn.com> in August, 1990.
9275 * 21-Jul-1994 Charles Bailey bailey@newman.upenn.edu
9276 * Minor modifications to original routines.
9279 /* readdir may have been redefined by reentr.h, so make sure we get
9280 * the local version for what we do here.
9285 #if !defined(PERL_IMPLICIT_CONTEXT)
9286 # define readdir Perl_readdir
9288 # define readdir(a) Perl_readdir(aTHX_ a)
9291 /* Number of elements in vms_versions array */
9292 #define VERSIZE(e) (sizeof e->vms_versions / sizeof e->vms_versions[0])
9295 * Open a directory, return a handle for later use.
9297 /*{{{ DIR *opendir(char*name) */
9299 Perl_opendir(pTHX_ const char *name)
9305 Newx(dir, VMS_MAXRSS, char);
9306 if (do_tovmspath(name,dir,0,NULL) == NULL) {
9310 /* Check access before stat; otherwise stat does not
9311 * accurately report whether it's a directory.
9313 if (!cando_by_name_int(S_IRUSR,0,dir,PERL_RMSEXPAND_M_VMS_IN)) {
9314 /* cando_by_name has already set errno */
9318 if (flex_stat(dir,&sb) == -1) return NULL;
9319 if (!S_ISDIR(sb.st_mode)) {
9321 set_errno(ENOTDIR); set_vaxc_errno(RMS$_DIR);
9324 /* Get memory for the handle, and the pattern. */
9326 Newx(dd->pattern,strlen(dir)+sizeof "*.*" + 1,char);
9328 /* Fill in the fields; mainly playing with the descriptor. */
9329 sprintf(dd->pattern, "%s*.*",dir);
9334 /* By saying we always want the result of readdir() in unix format, we
9335 * are really saying we want all the escapes removed. Otherwise the caller,
9336 * having no way to know whether it's already in VMS format, might send it
9337 * through tovmsspec again, thus double escaping.
9339 dd->flags = PERL_VMSDIR_M_UNIXSPECS;
9340 dd->pat.dsc$a_pointer = dd->pattern;
9341 dd->pat.dsc$w_length = strlen(dd->pattern);
9342 dd->pat.dsc$b_dtype = DSC$K_DTYPE_T;
9343 dd->pat.dsc$b_class = DSC$K_CLASS_S;
9344 #if defined(USE_ITHREADS)
9345 Newx(dd->mutex,1,perl_mutex);
9346 MUTEX_INIT( (perl_mutex *) dd->mutex );
9352 } /* end of opendir() */
9356 * Set the flag to indicate we want versions or not.
9358 /*{{{ void vmsreaddirversions(DIR *dd, int flag)*/
9360 vmsreaddirversions(DIR *dd, int flag)
9363 dd->flags |= PERL_VMSDIR_M_VERSIONS;
9365 dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
9370 * Free up an opened directory.
9372 /*{{{ void closedir(DIR *dd)*/
9374 Perl_closedir(DIR *dd)
9378 sts = lib$find_file_end(&dd->context);
9379 Safefree(dd->pattern);
9380 #if defined(USE_ITHREADS)
9381 MUTEX_DESTROY( (perl_mutex *) dd->mutex );
9382 Safefree(dd->mutex);
9389 * Collect all the version numbers for the current file.
9392 collectversions(pTHX_ DIR *dd)
9394 struct dsc$descriptor_s pat;
9395 struct dsc$descriptor_s res;
9397 char *p, *text, *buff;
9399 unsigned long context, tmpsts;
9401 /* Convenient shorthand. */
9404 /* Add the version wildcard, ignoring the "*.*" put on before */
9405 i = strlen(dd->pattern);
9406 Newx(text,i + e->d_namlen + 3,char);
9407 strcpy(text, dd->pattern);
9408 sprintf(&text[i - 3], "%s;*", e->d_name);
9410 /* Set up the pattern descriptor. */
9411 pat.dsc$a_pointer = text;
9412 pat.dsc$w_length = i + e->d_namlen - 1;
9413 pat.dsc$b_dtype = DSC$K_DTYPE_T;
9414 pat.dsc$b_class = DSC$K_CLASS_S;
9416 /* Set up result descriptor. */
9417 Newx(buff, VMS_MAXRSS, char);
9418 res.dsc$a_pointer = buff;
9419 res.dsc$w_length = VMS_MAXRSS - 1;
9420 res.dsc$b_dtype = DSC$K_DTYPE_T;
9421 res.dsc$b_class = DSC$K_CLASS_S;
9423 /* Read files, collecting versions. */
9424 for (context = 0, e->vms_verscount = 0;
9425 e->vms_verscount < VERSIZE(e);
9426 e->vms_verscount++) {
9428 unsigned long flags = 0;
9430 #ifdef VMS_LONGNAME_SUPPORT
9431 flags = LIB$M_FIL_LONG_NAMES;
9433 tmpsts = lib$find_file(&pat, &res, &context, NULL, NULL, &rsts, &flags);
9434 if (tmpsts == RMS$_NMF || context == 0) break;
9436 buff[VMS_MAXRSS - 1] = '\0';
9437 if ((p = strchr(buff, ';')))
9438 e->vms_versions[e->vms_verscount] = atoi(p + 1);
9440 e->vms_versions[e->vms_verscount] = -1;
9443 _ckvmssts(lib$find_file_end(&context));
9447 } /* end of collectversions() */
9450 * Read the next entry from the directory.
9452 /*{{{ struct dirent *readdir(DIR *dd)*/
9454 Perl_readdir(pTHX_ DIR *dd)
9456 struct dsc$descriptor_s res;
9458 unsigned long int tmpsts;
9460 unsigned long flags = 0;
9461 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
9462 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
9464 /* Set up result descriptor, and get next file. */
9465 Newx(buff, VMS_MAXRSS, char);
9466 res.dsc$a_pointer = buff;
9467 res.dsc$w_length = VMS_MAXRSS - 1;
9468 res.dsc$b_dtype = DSC$K_DTYPE_T;
9469 res.dsc$b_class = DSC$K_CLASS_S;
9471 #ifdef VMS_LONGNAME_SUPPORT
9472 flags = LIB$M_FIL_LONG_NAMES;
9475 tmpsts = lib$find_file
9476 (&dd->pat, &res, &dd->context, NULL, NULL, &rsts, &flags);
9477 if ( tmpsts == RMS$_NMF || dd->context == 0) return NULL; /* None left. */
9478 if (!(tmpsts & 1)) {
9479 set_vaxc_errno(tmpsts);
9482 set_errno(EACCES); break;
9484 set_errno(ENODEV); break;
9486 set_errno(ENOTDIR); break;
9487 case RMS$_FNF: case RMS$_DNF:
9488 set_errno(ENOENT); break;
9496 /* Force the buffer to end with a NUL, and downcase name to match C convention. */
9497 if (!decc_efs_case_preserve) {
9498 buff[VMS_MAXRSS - 1] = '\0';
9499 for (p = buff; *p; p++) *p = _tolower(*p);
9502 /* we don't want to force to lowercase, just null terminate */
9503 buff[res.dsc$w_length] = '\0';
9505 while (--p >= buff) if (!isspace(*p)) break; /* Do we really need this? */
9508 /* Skip any directory component and just copy the name. */
9509 sts = vms_split_path
9524 /* Drop NULL extensions on UNIX file specification */
9525 if ((dd->flags & PERL_VMSDIR_M_UNIXSPECS &&
9526 (e_len == 1) && decc_readdir_dropdotnotype)) {
9531 strncpy(dd->entry.d_name, n_spec, n_len + e_len);
9532 dd->entry.d_name[n_len + e_len] = '\0';
9533 dd->entry.d_namlen = strlen(dd->entry.d_name);
9535 /* Convert the filename to UNIX format if needed */
9536 if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
9538 /* Translate the encoded characters. */
9539 /* Fixme: Unicode handling could result in embedded 0 characters */
9540 if (strchr(dd->entry.d_name, '^') != NULL) {
9543 p = dd->entry.d_name;
9546 int inchars_read, outchars_added;
9547 inchars_read = copy_expand_vms_filename_escape(q, p, &outchars_added);
9549 q += outchars_added;
9551 /* if outchars_added > 1, then this is a wide file specification */
9552 /* Wide file specifications need to be passed in Perl */
9553 /* counted strings apparently with a Unicode flag */
9556 strcpy(dd->entry.d_name, new_name);
9557 dd->entry.d_namlen = strlen(dd->entry.d_name);
9561 dd->entry.vms_verscount = 0;
9562 if (dd->flags & PERL_VMSDIR_M_VERSIONS) collectversions(aTHX_ dd);
9566 } /* end of readdir() */
9570 * Read the next entry from the directory -- thread-safe version.
9572 /*{{{ int readdir_r(DIR *dd, struct dirent *entry, struct dirent **result)*/
9574 Perl_readdir_r(pTHX_ DIR *dd, struct dirent *entry, struct dirent **result)
9578 MUTEX_LOCK( (perl_mutex *) dd->mutex );
9580 entry = readdir(dd);
9582 retval = ( *result == NULL ? errno : 0 );
9584 MUTEX_UNLOCK( (perl_mutex *) dd->mutex );
9588 } /* end of readdir_r() */
9592 * Return something that can be used in a seekdir later.
9594 /*{{{ long telldir(DIR *dd)*/
9596 Perl_telldir(DIR *dd)
9603 * Return to a spot where we used to be. Brute force.
9605 /*{{{ void seekdir(DIR *dd,long count)*/
9607 Perl_seekdir(pTHX_ DIR *dd, long count)
9611 /* If we haven't done anything yet... */
9615 /* Remember some state, and clear it. */
9616 old_flags = dd->flags;
9617 dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
9618 _ckvmssts(lib$find_file_end(&dd->context));
9621 /* The increment is in readdir(). */
9622 for (dd->count = 0; dd->count < count; )
9625 dd->flags = old_flags;
9627 } /* end of seekdir() */
9630 /* VMS subprocess management
9632 * my_vfork() - just a vfork(), after setting a flag to record that
9633 * the current script is trying a Unix-style fork/exec.
9635 * vms_do_aexec() and vms_do_exec() are called in response to the
9636 * perl 'exec' function. If this follows a vfork call, then they
9637 * call out the regular perl routines in doio.c which do an
9638 * execvp (for those who really want to try this under VMS).
9639 * Otherwise, they do exactly what the perl docs say exec should
9640 * do - terminate the current script and invoke a new command
9641 * (See below for notes on command syntax.)
9643 * do_aspawn() and do_spawn() implement the VMS side of the perl
9644 * 'system' function.
9646 * Note on command arguments to perl 'exec' and 'system': When handled
9647 * in 'VMSish fashion' (i.e. not after a call to vfork) The args
9648 * are concatenated to form a DCL command string. If the first arg
9649 * begins with '$' (i.e. the perl script had "\$ Type" or some such),
9650 * the command string is handed off to DCL directly. Otherwise,
9651 * the first token of the command is taken as the filespec of an image
9652 * to run. The filespec is expanded using a default type of '.EXE' and
9653 * the process defaults for device, directory, etc., and if found, the resultant
9654 * filespec is invoked using the DCL verb 'MCR', and passed the rest of
9655 * the command string as parameters. This is perhaps a bit complicated,
9656 * but I hope it will form a happy medium between what VMS folks expect
9657 * from lib$spawn and what Unix folks expect from exec.
9660 static int vfork_called;
9662 /*{{{int my_vfork()*/
9673 vms_execfree(struct dsc$descriptor_s *vmscmd)
9676 if (vmscmd->dsc$a_pointer) {
9677 PerlMem_free(vmscmd->dsc$a_pointer);
9679 PerlMem_free(vmscmd);
9684 setup_argstr(pTHX_ SV *really, SV **mark, SV **sp)
9686 char *junk, *tmps = Nullch;
9687 register size_t cmdlen = 0;
9694 tmps = SvPV(really,rlen);
9701 for (idx++; idx <= sp; idx++) {
9703 junk = SvPVx(*idx,rlen);
9704 cmdlen += rlen ? rlen + 1 : 0;
9707 Newx(PL_Cmd, cmdlen+1, char);
9709 if (tmps && *tmps) {
9710 strcpy(PL_Cmd,tmps);
9713 else *PL_Cmd = '\0';
9714 while (++mark <= sp) {
9716 char *s = SvPVx(*mark,n_a);
9718 if (*PL_Cmd) strcat(PL_Cmd," ");
9724 } /* end of setup_argstr() */
9727 static unsigned long int
9728 setup_cmddsc(pTHX_ const char *incmd, int check_img, int *suggest_quote,
9729 struct dsc$descriptor_s **pvmscmd)
9731 char vmsspec[NAM$C_MAXRSS+1], resspec[NAM$C_MAXRSS+1];
9732 char image_name[NAM$C_MAXRSS+1];
9733 char image_argv[NAM$C_MAXRSS+1];
9734 $DESCRIPTOR(defdsc,".EXE");
9735 $DESCRIPTOR(defdsc2,".");
9736 $DESCRIPTOR(resdsc,resspec);
9737 struct dsc$descriptor_s *vmscmd;
9738 struct dsc$descriptor_s imgdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
9739 unsigned long int cxt = 0, flags = 1, retsts = SS$_NORMAL;
9740 register char *s, *rest, *cp, *wordbreak;
9745 vmscmd = PerlMem_malloc(sizeof(struct dsc$descriptor_s));
9746 if (vmscmd == NULL) _ckvmssts(SS$_INSFMEM);
9748 /* Make a copy for modification */
9749 cmdlen = strlen(incmd);
9750 cmd = PerlMem_malloc(cmdlen+1);
9751 if (cmd == NULL) _ckvmssts(SS$_INSFMEM);
9752 strncpy(cmd, incmd, cmdlen);
9757 vmscmd->dsc$a_pointer = NULL;
9758 vmscmd->dsc$b_dtype = DSC$K_DTYPE_T;
9759 vmscmd->dsc$b_class = DSC$K_CLASS_S;
9760 vmscmd->dsc$w_length = 0;
9761 if (pvmscmd) *pvmscmd = vmscmd;
9763 if (suggest_quote) *suggest_quote = 0;
9765 if (strlen(cmd) > MAX_DCL_LINE_LENGTH) {
9767 return CLI$_BUFOVF; /* continuation lines currently unsupported */
9772 while (*s && isspace(*s)) s++;
9774 if (*s == '@' || *s == '$') {
9775 vmsspec[0] = *s; rest = s + 1;
9776 for (cp = &vmsspec[1]; *rest && isspace(*rest); rest++,cp++) *cp = *rest;
9778 else { cp = vmsspec; rest = s; }
9779 if (*rest == '.' || *rest == '/') {
9782 *rest && !isspace(*rest) && cp2 - resspec < sizeof resspec;
9783 rest++, cp2++) *cp2 = *rest;
9785 if (do_tovmsspec(resspec,cp,0,NULL)) {
9788 for (cp2 = vmsspec + strlen(vmsspec);
9789 *rest && cp2 - vmsspec < sizeof vmsspec;
9790 rest++, cp2++) *cp2 = *rest;
9795 /* Intuit whether verb (first word of cmd) is a DCL command:
9796 * - if first nonspace char is '@', it's a DCL indirection
9798 * - if verb contains a filespec separator, it's not a DCL command
9799 * - if it doesn't, caller tells us whether to default to a DCL
9800 * command, or to a local image unless told it's DCL (by leading '$')
9804 if (suggest_quote) *suggest_quote = 1;
9806 register char *filespec = strpbrk(s,":<[.;");
9807 rest = wordbreak = strpbrk(s," \"\t/");
9808 if (!wordbreak) wordbreak = s + strlen(s);
9809 if (*s == '$') check_img = 0;
9810 if (filespec && (filespec < wordbreak)) isdcl = 0;
9811 else isdcl = !check_img;
9816 imgdsc.dsc$a_pointer = s;
9817 imgdsc.dsc$w_length = wordbreak - s;
9818 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
9820 _ckvmssts(lib$find_file_end(&cxt));
9821 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
9822 if (!(retsts & 1) && *s == '$') {
9823 _ckvmssts(lib$find_file_end(&cxt));
9824 imgdsc.dsc$a_pointer++; imgdsc.dsc$w_length--;
9825 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
9827 _ckvmssts(lib$find_file_end(&cxt));
9828 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
9832 _ckvmssts(lib$find_file_end(&cxt));
9837 while (*s && !isspace(*s)) s++;
9840 /* check that it's really not DCL with no file extension */
9841 fp = fopen(resspec,"r","ctx=bin","ctx=rec","shr=get");
9843 char b[256] = {0,0,0,0};
9844 read(fileno(fp), b, 256);
9845 isdcl = isprint(b[0]) && isprint(b[1]) && isprint(b[2]) && isprint(b[3]);
9849 /* Check for script */
9851 if ((b[0] == '#') && (b[1] == '!'))
9853 #ifdef ALTERNATE_SHEBANG
9855 shebang_len = strlen(ALTERNATE_SHEBANG);
9856 if (strncmp(b, ALTERNATE_SHEBANG, shebang_len) == 0) {
9858 perlstr = strstr("perl",b);
9859 if (perlstr == NULL)
9867 if (shebang_len > 0) {
9870 char tmpspec[NAM$C_MAXRSS + 1];
9873 /* Image is following after white space */
9874 /*--------------------------------------*/
9875 while (isprint(b[i]) && isspace(b[i]))
9879 while (isprint(b[i]) && !isspace(b[i])) {
9880 tmpspec[j++] = b[i++];
9881 if (j >= NAM$C_MAXRSS)
9886 /* There may be some default parameters to the image */
9887 /*---------------------------------------------------*/
9889 while (isprint(b[i])) {
9890 image_argv[j++] = b[i++];
9891 if (j >= NAM$C_MAXRSS)
9894 while ((j > 0) && !isprint(image_argv[j-1]))
9898 /* It will need to be converted to VMS format and validated */
9899 if (tmpspec[0] != '\0') {
9902 /* Try to find the exact program requested to be run */
9903 /*---------------------------------------------------*/
9904 iname = do_rmsexpand
9905 (tmpspec, image_name, 0, ".exe",
9906 PERL_RMSEXPAND_M_VMS, NULL, NULL);
9907 if (iname != NULL) {
9908 if (cando_by_name_int
9909 (S_IXUSR,0,image_name,PERL_RMSEXPAND_M_VMS_IN)) {
9910 /* MCR prefix needed */
9914 /* Try again with a null type */
9915 /*----------------------------*/
9916 iname = do_rmsexpand
9917 (tmpspec, image_name, 0, ".",
9918 PERL_RMSEXPAND_M_VMS, NULL, NULL);
9919 if (iname != NULL) {
9920 if (cando_by_name_int
9921 (S_IXUSR,0,image_name, PERL_RMSEXPAND_M_VMS_IN)) {
9922 /* MCR prefix needed */
9928 /* Did we find the image to run the script? */
9929 /*------------------------------------------*/
9933 /* Assume DCL or foreign command exists */
9934 /*--------------------------------------*/
9935 tchr = strrchr(tmpspec, '/');
9942 strcpy(image_name, tchr);
9950 if (check_img && isdcl) return RMS$_FNF;
9952 if (cando_by_name(S_IXUSR,0,resspec)) {
9953 vmscmd->dsc$a_pointer = PerlMem_malloc(MAX_DCL_LINE_LENGTH);
9954 if (vmscmd->dsc$a_pointer == NULL) _ckvmssts(SS$_INSFMEM);
9956 strcpy(vmscmd->dsc$a_pointer,"$ MCR ");
9957 if (image_name[0] != 0) {
9958 strcat(vmscmd->dsc$a_pointer, image_name);
9959 strcat(vmscmd->dsc$a_pointer, " ");
9961 } else if (image_name[0] != 0) {
9962 strcpy(vmscmd->dsc$a_pointer, image_name);
9963 strcat(vmscmd->dsc$a_pointer, " ");
9965 strcpy(vmscmd->dsc$a_pointer,"@");
9967 if (suggest_quote) *suggest_quote = 1;
9969 /* If there is an image name, use original command */
9970 if (image_name[0] == 0)
9971 strcat(vmscmd->dsc$a_pointer,resspec);
9974 while (*rest && isspace(*rest)) rest++;
9977 if (image_argv[0] != 0) {
9978 strcat(vmscmd->dsc$a_pointer,image_argv);
9979 strcat(vmscmd->dsc$a_pointer, " ");
9985 rest_len = strlen(rest);
9986 vmscmd_len = strlen(vmscmd->dsc$a_pointer);
9987 if ((rest_len + vmscmd_len) < MAX_DCL_LINE_LENGTH)
9988 strcat(vmscmd->dsc$a_pointer,rest);
9990 retsts = CLI$_BUFOVF;
9992 vmscmd->dsc$w_length = strlen(vmscmd->dsc$a_pointer);
9994 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
10000 /* It's either a DCL command or we couldn't find a suitable image */
10001 vmscmd->dsc$w_length = strlen(cmd);
10003 vmscmd->dsc$a_pointer = PerlMem_malloc(vmscmd->dsc$w_length + 1);
10004 strncpy(vmscmd->dsc$a_pointer,cmd,vmscmd->dsc$w_length);
10005 vmscmd->dsc$a_pointer[vmscmd->dsc$w_length] = 0;
10009 /* check if it's a symbol (for quoting purposes) */
10010 if (suggest_quote && !*suggest_quote) {
10012 char equiv[LNM$C_NAMLENGTH];
10013 struct dsc$descriptor_s eqvdsc = {sizeof(equiv), DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10014 eqvdsc.dsc$a_pointer = equiv;
10016 iss = lib$get_symbol(vmscmd,&eqvdsc);
10017 if (iss&1 && (*equiv == '$' || *equiv == '@')) *suggest_quote = 1;
10019 if (!(retsts & 1)) {
10020 /* just hand off status values likely to be due to user error */
10021 if (retsts == RMS$_FNF || retsts == RMS$_DNF || retsts == RMS$_PRV ||
10022 retsts == RMS$_DEV || retsts == RMS$_DIR || retsts == RMS$_SYN ||
10023 (retsts & STS$M_CODE) == (SHR$_NOWILD & STS$M_CODE)) return retsts;
10024 else { _ckvmssts(retsts); }
10027 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
10029 } /* end of setup_cmddsc() */
10032 /* {{{ bool vms_do_aexec(SV *really,SV **mark,SV **sp) */
10034 Perl_vms_do_aexec(pTHX_ SV *really,SV **mark,SV **sp)
10040 if (vfork_called) { /* this follows a vfork - act Unixish */
10042 if (vfork_called < 0) {
10043 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
10046 else return do_aexec(really,mark,sp);
10048 /* no vfork - act VMSish */
10049 cmd = setup_argstr(aTHX_ really,mark,sp);
10050 exec_sts = vms_do_exec(cmd);
10051 Safefree(cmd); /* Clean up from setup_argstr() */
10056 } /* end of vms_do_aexec() */
10059 /* {{{bool vms_do_exec(char *cmd) */
10061 Perl_vms_do_exec(pTHX_ const char *cmd)
10063 struct dsc$descriptor_s *vmscmd;
10065 if (vfork_called) { /* this follows a vfork - act Unixish */
10067 if (vfork_called < 0) {
10068 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
10071 else return do_exec(cmd);
10074 { /* no vfork - act VMSish */
10075 unsigned long int retsts;
10078 TAINT_PROPER("exec");
10079 if ((retsts = setup_cmddsc(aTHX_ cmd,1,0,&vmscmd)) & 1)
10080 retsts = lib$do_command(vmscmd);
10083 case RMS$_FNF: case RMS$_DNF:
10084 set_errno(ENOENT); break;
10086 set_errno(ENOTDIR); break;
10088 set_errno(ENODEV); break;
10090 set_errno(EACCES); break;
10092 set_errno(EINVAL); break;
10093 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
10094 set_errno(E2BIG); break;
10095 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
10096 _ckvmssts(retsts); /* fall through */
10097 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
10098 set_errno(EVMSERR);
10100 set_vaxc_errno(retsts);
10101 if (ckWARN(WARN_EXEC)) {
10102 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't exec \"%*s\": %s",
10103 vmscmd->dsc$w_length, vmscmd->dsc$a_pointer, Strerror(errno));
10105 vms_execfree(vmscmd);
10110 } /* end of vms_do_exec() */
10113 unsigned long int Perl_do_spawn(pTHX_ const char *);
10115 /* {{{ unsigned long int do_aspawn(void *really,void **mark,void **sp) */
10117 Perl_do_aspawn(pTHX_ void *really,void **mark,void **sp)
10119 unsigned long int sts;
10123 cmd = setup_argstr(aTHX_ (SV *)really,(SV **)mark,(SV **)sp);
10124 sts = do_spawn(cmd);
10125 /* pp_sys will clean up cmd */
10129 } /* end of do_aspawn() */
10132 /* {{{unsigned long int do_spawn(char *cmd) */
10134 Perl_do_spawn(pTHX_ const char *cmd)
10136 unsigned long int sts, substs;
10138 /* The caller of this routine expects to Safefree(PL_Cmd) */
10139 Newx(PL_Cmd,10,char);
10142 TAINT_PROPER("spawn");
10143 if (!cmd || !*cmd) {
10144 sts = lib$spawn(0,0,0,0,0,0,&substs,0,0,0,0,0,0);
10147 case RMS$_FNF: case RMS$_DNF:
10148 set_errno(ENOENT); break;
10150 set_errno(ENOTDIR); break;
10152 set_errno(ENODEV); break;
10154 set_errno(EACCES); break;
10156 set_errno(EINVAL); break;
10157 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
10158 set_errno(E2BIG); break;
10159 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
10160 _ckvmssts(sts); /* fall through */
10161 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
10162 set_errno(EVMSERR);
10164 set_vaxc_errno(sts);
10165 if (ckWARN(WARN_EXEC)) {
10166 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't spawn: %s",
10174 fp = safe_popen(aTHX_ cmd, "nW", (int *)&sts);
10179 } /* end of do_spawn() */
10183 static unsigned int *sockflags, sockflagsize;
10186 * Shim fdopen to identify sockets for my_fwrite later, since the stdio
10187 * routines found in some versions of the CRTL can't deal with sockets.
10188 * We don't shim the other file open routines since a socket isn't
10189 * likely to be opened by a name.
10191 /*{{{ FILE *my_fdopen(int fd, const char *mode)*/
10192 FILE *my_fdopen(int fd, const char *mode)
10194 FILE *fp = fdopen(fd, mode);
10197 unsigned int fdoff = fd / sizeof(unsigned int);
10198 Stat_t sbuf; /* native stat; we don't need flex_stat */
10199 if (!sockflagsize || fdoff > sockflagsize) {
10200 if (sockflags) Renew( sockflags,fdoff+2,unsigned int);
10201 else Newx (sockflags,fdoff+2,unsigned int);
10202 memset(sockflags+sockflagsize,0,fdoff + 2 - sockflagsize);
10203 sockflagsize = fdoff + 2;
10205 if (fstat(fd, (struct stat *)&sbuf) == 0 && S_ISSOCK(sbuf.st_mode))
10206 sockflags[fdoff] |= 1 << (fd % sizeof(unsigned int));
10215 * Clear the corresponding bit when the (possibly) socket stream is closed.
10216 * There still a small hole: we miss an implicit close which might occur
10217 * via freopen(). >> Todo
10219 /*{{{ int my_fclose(FILE *fp)*/
10220 int my_fclose(FILE *fp) {
10222 unsigned int fd = fileno(fp);
10223 unsigned int fdoff = fd / sizeof(unsigned int);
10225 if (sockflagsize && fdoff <= sockflagsize)
10226 sockflags[fdoff] &= ~(1 << fd % sizeof(unsigned int));
10234 * A simple fwrite replacement which outputs itmsz*nitm chars without
10235 * introducing record boundaries every itmsz chars.
10236 * We are using fputs, which depends on a terminating null. We may
10237 * well be writing binary data, so we need to accommodate not only
10238 * data with nulls sprinkled in the middle but also data with no null
10241 /*{{{ int my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)*/
10243 my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)
10245 register char *cp, *end, *cpd, *data;
10246 register unsigned int fd = fileno(dest);
10247 register unsigned int fdoff = fd / sizeof(unsigned int);
10249 int bufsize = itmsz * nitm + 1;
10251 if (fdoff < sockflagsize &&
10252 (sockflags[fdoff] | 1 << (fd % sizeof(unsigned int)))) {
10253 if (write(fd, src, itmsz * nitm) == EOF) return EOF;
10257 _ckvmssts_noperl(lib$get_vm(&bufsize, &data));
10258 memcpy( data, src, itmsz*nitm );
10259 data[itmsz*nitm] = '\0';
10261 end = data + itmsz * nitm;
10262 retval = (int) nitm; /* on success return # items written */
10265 while (cpd <= end) {
10266 for (cp = cpd; cp <= end; cp++) if (!*cp) break;
10267 if (fputs(cpd,dest) == EOF) { retval = EOF; break; }
10269 if (fputc('\0',dest) == EOF) { retval = EOF; break; }
10273 if (data) _ckvmssts_noperl(lib$free_vm(&bufsize, &data));
10276 } /* end of my_fwrite() */
10279 /*{{{ int my_flush(FILE *fp)*/
10281 Perl_my_flush(pTHX_ FILE *fp)
10284 if ((res = fflush(fp)) == 0 && fp) {
10285 #ifdef VMS_DO_SOCKETS
10287 if (Fstat(fileno(fp), &s) == 0 && !S_ISSOCK(s.st_mode))
10289 res = fsync(fileno(fp));
10292 * If the flush succeeded but set end-of-file, we need to clear
10293 * the error because our caller may check ferror(). BTW, this
10294 * probably means we just flushed an empty file.
10296 if (res == 0 && vaxc$errno == RMS$_EOF) clearerr(fp);
10303 * Here are replacements for the following Unix routines in the VMS environment:
10304 * getpwuid Get information for a particular UIC or UID
10305 * getpwnam Get information for a named user
10306 * getpwent Get information for each user in the rights database
10307 * setpwent Reset search to the start of the rights database
10308 * endpwent Finish searching for users in the rights database
10310 * getpwuid, getpwnam, and getpwent return a pointer to the passwd structure
10311 * (defined in pwd.h), which contains the following fields:-
10313 * char *pw_name; Username (in lower case)
10314 * char *pw_passwd; Hashed password
10315 * unsigned int pw_uid; UIC
10316 * unsigned int pw_gid; UIC group number
10317 * char *pw_unixdir; Default device/directory (VMS-style)
10318 * char *pw_gecos; Owner name
10319 * char *pw_dir; Default device/directory (Unix-style)
10320 * char *pw_shell; Default CLI name (eg. DCL)
10322 * If the specified user does not exist, getpwuid and getpwnam return NULL.
10324 * pw_uid is the full UIC (eg. what's returned by stat() in st_uid).
10325 * not the UIC member number (eg. what's returned by getuid()),
10326 * getpwuid() can accept either as input (if uid is specified, the caller's
10327 * UIC group is used), though it won't recognise gid=0.
10329 * Note that in VMS it is necessary to have GRPPRV or SYSPRV to return
10330 * information about other users in your group or in other groups, respectively.
10331 * If the required privilege is not available, then these routines fill only
10332 * the pw_name, pw_uid, and pw_gid fields (the others point to an empty
10335 * By Tim Adye (T.J.Adye@rl.ac.uk), 10th February 1995.
10338 /* sizes of various UAF record fields */
10339 #define UAI$S_USERNAME 12
10340 #define UAI$S_IDENT 31
10341 #define UAI$S_OWNER 31
10342 #define UAI$S_DEFDEV 31
10343 #define UAI$S_DEFDIR 63
10344 #define UAI$S_DEFCLI 31
10345 #define UAI$S_PWD 8
10347 #define valid_uic(uic) ((uic).uic$v_format == UIC$K_UIC_FORMAT && \
10348 (uic).uic$v_member != UIC$K_WILD_MEMBER && \
10349 (uic).uic$v_group != UIC$K_WILD_GROUP)
10351 static char __empty[]= "";
10352 static struct passwd __passwd_empty=
10353 {(char *) __empty, (char *) __empty, 0, 0,
10354 (char *) __empty, (char *) __empty, (char *) __empty, (char *) __empty};
10355 static int contxt= 0;
10356 static struct passwd __pwdcache;
10357 static char __pw_namecache[UAI$S_IDENT+1];
10360 * This routine does most of the work extracting the user information.
10362 static int fillpasswd (pTHX_ const char *name, struct passwd *pwd)
10365 unsigned char length;
10366 char pw_gecos[UAI$S_OWNER+1];
10368 static union uicdef uic;
10370 unsigned char length;
10371 char pw_dir[UAI$S_DEFDEV+UAI$S_DEFDIR+1];
10374 unsigned char length;
10375 char unixdir[UAI$_DEFDEV+UAI$S_DEFDIR+1];
10378 unsigned char length;
10379 char pw_shell[UAI$S_DEFCLI+1];
10381 static char pw_passwd[UAI$S_PWD+1];
10383 static unsigned short lowner, luic, ldefdev, ldefdir, ldefcli, lpwd;
10384 struct dsc$descriptor_s name_desc;
10385 unsigned long int sts;
10387 static struct itmlst_3 itmlst[]= {
10388 {UAI$S_OWNER+1, UAI$_OWNER, &owner, &lowner},
10389 {sizeof(uic), UAI$_UIC, &uic, &luic},
10390 {UAI$S_DEFDEV+1, UAI$_DEFDEV, &defdev, &ldefdev},
10391 {UAI$S_DEFDIR+1, UAI$_DEFDIR, &defdir, &ldefdir},
10392 {UAI$S_DEFCLI+1, UAI$_DEFCLI, &defcli, &ldefcli},
10393 {UAI$S_PWD, UAI$_PWD, pw_passwd, &lpwd},
10394 {0, 0, NULL, NULL}};
10396 name_desc.dsc$w_length= strlen(name);
10397 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
10398 name_desc.dsc$b_class= DSC$K_CLASS_S;
10399 name_desc.dsc$a_pointer= (char *) name; /* read only pointer */
10401 /* Note that sys$getuai returns many fields as counted strings. */
10402 sts= sys$getuai(0, 0, &name_desc, &itmlst, 0, 0, 0);
10403 if (sts == SS$_NOSYSPRV || sts == SS$_NOGRPPRV || sts == RMS$_RNF) {
10404 set_vaxc_errno(sts); set_errno(sts == RMS$_RNF ? EINVAL : EACCES);
10406 else { _ckvmssts(sts); }
10407 if (!(sts & 1)) return 0; /* out here in case _ckvmssts() doesn't abort */
10409 if ((int) owner.length < lowner) lowner= (int) owner.length;
10410 if ((int) defdev.length < ldefdev) ldefdev= (int) defdev.length;
10411 if ((int) defdir.length < ldefdir) ldefdir= (int) defdir.length;
10412 if ((int) defcli.length < ldefcli) ldefcli= (int) defcli.length;
10413 memcpy(&defdev.pw_dir[ldefdev], &defdir.unixdir[0], ldefdir);
10414 owner.pw_gecos[lowner]= '\0';
10415 defdev.pw_dir[ldefdev+ldefdir]= '\0';
10416 defcli.pw_shell[ldefcli]= '\0';
10417 if (valid_uic(uic)) {
10418 pwd->pw_uid= uic.uic$l_uic;
10419 pwd->pw_gid= uic.uic$v_group;
10422 Perl_warn(aTHX_ "getpwnam returned invalid UIC %#o for user \"%s\"");
10423 pwd->pw_passwd= pw_passwd;
10424 pwd->pw_gecos= owner.pw_gecos;
10425 pwd->pw_dir= defdev.pw_dir;
10426 pwd->pw_unixdir= do_tounixpath(defdev.pw_dir, defdir.unixdir,1,NULL);
10427 pwd->pw_shell= defcli.pw_shell;
10428 if (pwd->pw_unixdir && pwd->pw_unixdir[0]) {
10430 ldir= strlen(pwd->pw_unixdir) - 1;
10431 if (pwd->pw_unixdir[ldir]=='/') pwd->pw_unixdir[ldir]= '\0';
10434 strcpy(pwd->pw_unixdir, pwd->pw_dir);
10435 if (!decc_efs_case_preserve)
10436 __mystrtolower(pwd->pw_unixdir);
10441 * Get information for a named user.
10443 /*{{{struct passwd *getpwnam(char *name)*/
10444 struct passwd *Perl_my_getpwnam(pTHX_ const char *name)
10446 struct dsc$descriptor_s name_desc;
10448 unsigned long int status, sts;
10450 __pwdcache = __passwd_empty;
10451 if (!fillpasswd(aTHX_ name, &__pwdcache)) {
10452 /* We still may be able to determine pw_uid and pw_gid */
10453 name_desc.dsc$w_length= strlen(name);
10454 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
10455 name_desc.dsc$b_class= DSC$K_CLASS_S;
10456 name_desc.dsc$a_pointer= (char *) name;
10457 if ((sts = sys$asctoid(&name_desc, &uic, 0)) == SS$_NORMAL) {
10458 __pwdcache.pw_uid= uic.uic$l_uic;
10459 __pwdcache.pw_gid= uic.uic$v_group;
10462 if (sts == SS$_NOSUCHID || sts == SS$_IVIDENT || sts == RMS$_PRV) {
10463 set_vaxc_errno(sts);
10464 set_errno(sts == RMS$_PRV ? EACCES : EINVAL);
10467 else { _ckvmssts(sts); }
10470 strncpy(__pw_namecache, name, sizeof(__pw_namecache));
10471 __pw_namecache[sizeof __pw_namecache - 1] = '\0';
10472 __pwdcache.pw_name= __pw_namecache;
10473 return &__pwdcache;
10474 } /* end of my_getpwnam() */
10478 * Get information for a particular UIC or UID.
10479 * Called by my_getpwent with uid=-1 to list all users.
10481 /*{{{struct passwd *my_getpwuid(Uid_t uid)*/
10482 struct passwd *Perl_my_getpwuid(pTHX_ Uid_t uid)
10484 const $DESCRIPTOR(name_desc,__pw_namecache);
10485 unsigned short lname;
10487 unsigned long int status;
10489 if (uid == (unsigned int) -1) {
10491 status = sys$idtoasc(-1, &lname, &name_desc, &uic, 0, &contxt);
10492 if (status == SS$_NOSUCHID || status == RMS$_PRV) {
10493 set_vaxc_errno(status);
10494 set_errno(status == RMS$_PRV ? EACCES : EINVAL);
10498 else { _ckvmssts(status); }
10499 } while (!valid_uic (uic));
10502 uic.uic$l_uic= uid;
10503 if (!uic.uic$v_group)
10504 uic.uic$v_group= PerlProc_getgid();
10505 if (valid_uic(uic))
10506 status = sys$idtoasc(uic.uic$l_uic, &lname, &name_desc, 0, 0, 0);
10507 else status = SS$_IVIDENT;
10508 if (status == SS$_IVIDENT || status == SS$_NOSUCHID ||
10509 status == RMS$_PRV) {
10510 set_vaxc_errno(status); set_errno(status == RMS$_PRV ? EACCES : EINVAL);
10513 else { _ckvmssts(status); }
10515 __pw_namecache[lname]= '\0';
10516 __mystrtolower(__pw_namecache);
10518 __pwdcache = __passwd_empty;
10519 __pwdcache.pw_name = __pw_namecache;
10521 /* Fill in the uid and gid in case fillpasswd can't (eg. no privilege).
10522 The identifier's value is usually the UIC, but it doesn't have to be,
10523 so if we can, we let fillpasswd update this. */
10524 __pwdcache.pw_uid = uic.uic$l_uic;
10525 __pwdcache.pw_gid = uic.uic$v_group;
10527 fillpasswd(aTHX_ __pw_namecache, &__pwdcache);
10528 return &__pwdcache;
10530 } /* end of my_getpwuid() */
10534 * Get information for next user.
10536 /*{{{struct passwd *my_getpwent()*/
10537 struct passwd *Perl_my_getpwent(pTHX)
10539 return (my_getpwuid((unsigned int) -1));
10544 * Finish searching rights database for users.
10546 /*{{{void my_endpwent()*/
10547 void Perl_my_endpwent(pTHX)
10550 _ckvmssts(sys$finish_rdb(&contxt));
10556 #ifdef HOMEGROWN_POSIX_SIGNALS
10557 /* Signal handling routines, pulled into the core from POSIX.xs.
10559 * We need these for threads, so they've been rolled into the core,
10560 * rather than left in POSIX.xs.
10562 * (DRS, Oct 23, 1997)
10565 /* sigset_t is atomic under VMS, so these routines are easy */
10566 /*{{{int my_sigemptyset(sigset_t *) */
10567 int my_sigemptyset(sigset_t *set) {
10568 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
10569 *set = 0; return 0;
10574 /*{{{int my_sigfillset(sigset_t *)*/
10575 int my_sigfillset(sigset_t *set) {
10577 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
10578 for (i = 0; i < NSIG; i++) *set |= (1 << i);
10584 /*{{{int my_sigaddset(sigset_t *set, int sig)*/
10585 int my_sigaddset(sigset_t *set, int sig) {
10586 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
10587 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
10588 *set |= (1 << (sig - 1));
10594 /*{{{int my_sigdelset(sigset_t *set, int sig)*/
10595 int my_sigdelset(sigset_t *set, int sig) {
10596 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
10597 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
10598 *set &= ~(1 << (sig - 1));
10604 /*{{{int my_sigismember(sigset_t *set, int sig)*/
10605 int my_sigismember(sigset_t *set, int sig) {
10606 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
10607 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
10608 return *set & (1 << (sig - 1));
10613 /*{{{int my_sigprocmask(int how, sigset_t *set, sigset_t *oset)*/
10614 int my_sigprocmask(int how, sigset_t *set, sigset_t *oset) {
10617 /* If set and oset are both null, then things are badly wrong. Bail out. */
10618 if ((oset == NULL) && (set == NULL)) {
10619 set_errno(EFAULT); set_vaxc_errno(SS$_ACCVIO);
10623 /* If set's null, then we're just handling a fetch. */
10625 tempmask = sigblock(0);
10630 tempmask = sigsetmask(*set);
10633 tempmask = sigblock(*set);
10636 tempmask = sigblock(0);
10637 sigsetmask(*oset & ~tempmask);
10640 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
10645 /* Did they pass us an oset? If so, stick our holding mask into it */
10652 #endif /* HOMEGROWN_POSIX_SIGNALS */
10655 /* Used for UTC calculation in my_gmtime(), my_localtime(), my_time(),
10656 * my_utime(), and flex_stat(), all of which operate on UTC unless
10657 * VMSISH_TIMES is true.
10659 /* method used to handle UTC conversions:
10660 * 1 == CRTL gmtime(); 2 == SYS$TIMEZONE_DIFFERENTIAL; 3 == no correction
10662 static int gmtime_emulation_type;
10663 /* number of secs to add to UTC POSIX-style time to get local time */
10664 static long int utc_offset_secs;
10666 /* We #defined 'gmtime', 'localtime', and 'time' as 'my_gmtime' etc.
10667 * in vmsish.h. #undef them here so we can call the CRTL routines
10676 * DEC C previous to 6.0 corrupts the behavior of the /prefix
10677 * qualifier with the extern prefix pragma. This provisional
10678 * hack circumvents this prefix pragma problem in previous
10681 #if defined(__VMS_VER) && __VMS_VER >= 70000000
10682 # if defined(VMS_WE_ARE_CASE_SENSITIVE) && (__DECC_VER < 60000000)
10683 # pragma __extern_prefix save
10684 # pragma __extern_prefix "" /* set to empty to prevent prefixing */
10685 # define gmtime decc$__utctz_gmtime
10686 # define localtime decc$__utctz_localtime
10687 # define time decc$__utc_time
10688 # pragma __extern_prefix restore
10690 struct tm *gmtime(), *localtime();
10696 static time_t toutc_dst(time_t loc) {
10699 if ((rsltmp = localtime(&loc)) == NULL) return -1;
10700 loc -= utc_offset_secs;
10701 if (rsltmp->tm_isdst) loc -= 3600;
10704 #define _toutc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
10705 ((gmtime_emulation_type || my_time(NULL)), \
10706 (gmtime_emulation_type == 1 ? toutc_dst(secs) : \
10707 ((secs) - utc_offset_secs))))
10709 static time_t toloc_dst(time_t utc) {
10712 utc += utc_offset_secs;
10713 if ((rsltmp = localtime(&utc)) == NULL) return -1;
10714 if (rsltmp->tm_isdst) utc += 3600;
10717 #define _toloc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
10718 ((gmtime_emulation_type || my_time(NULL)), \
10719 (gmtime_emulation_type == 1 ? toloc_dst(secs) : \
10720 ((secs) + utc_offset_secs))))
10722 #ifndef RTL_USES_UTC
10725 ucx$tz = "EST5EDT4,M4.1.0,M10.5.0" typical
10726 DST starts on 1st sun of april at 02:00 std time
10727 ends on last sun of october at 02:00 dst time
10728 see the UCX management command reference, SET CONFIG TIMEZONE
10729 for formatting info.
10731 No, it's not as general as it should be, but then again, NOTHING
10732 will handle UK times in a sensible way.
10737 parse the DST start/end info:
10738 (Jddd|ddd|Mmon.nth.dow)[/hh:mm:ss]
10742 tz_parse_startend(char *s, struct tm *w, int *past)
10744 int dinm[] = {31,28,31,30,31,30,31,31,30,31,30,31};
10745 int ly, dozjd, d, m, n, hour, min, sec, j, k;
10750 if (!past) return 0;
10753 if (w->tm_year % 4 == 0) ly = 1;
10754 if (w->tm_year % 100 == 0) ly = 0;
10755 if (w->tm_year+1900 % 400 == 0) ly = 1;
10758 dozjd = isdigit(*s);
10759 if (*s == 'J' || *s == 'j' || dozjd) {
10760 if (!dozjd && !isdigit(*++s)) return 0;
10763 d = d*10 + *s++ - '0';
10765 d = d*10 + *s++ - '0';
10768 if (d == 0) return 0;
10769 if (d > 366) return 0;
10771 if (!dozjd && d > 58 && ly) d++; /* after 28 feb */
10774 } else if (*s == 'M' || *s == 'm') {
10775 if (!isdigit(*++s)) return 0;
10777 if (isdigit(*s)) m = 10*m + *s++ - '0';
10778 if (*s != '.') return 0;
10779 if (!isdigit(*++s)) return 0;
10781 if (n < 1 || n > 5) return 0;
10782 if (*s != '.') return 0;
10783 if (!isdigit(*++s)) return 0;
10785 if (d > 6) return 0;
10789 if (!isdigit(*++s)) return 0;
10791 if (isdigit(*s)) hour = 10*hour + *s++ - '0';
10793 if (!isdigit(*++s)) return 0;
10795 if (isdigit(*s)) min = 10*min + *s++ - '0';
10797 if (!isdigit(*++s)) return 0;
10799 if (isdigit(*s)) sec = 10*sec + *s++ - '0';
10809 if (w->tm_yday < d) goto before;
10810 if (w->tm_yday > d) goto after;
10812 if (w->tm_mon+1 < m) goto before;
10813 if (w->tm_mon+1 > m) goto after;
10815 j = (42 + w->tm_wday - w->tm_mday)%7; /*dow of mday 0 */
10816 k = d - j; /* mday of first d */
10817 if (k <= 0) k += 7;
10818 k += 7 * ((n>4?4:n)-1); /* mday of n'th d */
10819 if (n == 5 && k+7 <= dinm[w->tm_mon]) k += 7;
10820 if (w->tm_mday < k) goto before;
10821 if (w->tm_mday > k) goto after;
10824 if (w->tm_hour < hour) goto before;
10825 if (w->tm_hour > hour) goto after;
10826 if (w->tm_min < min) goto before;
10827 if (w->tm_min > min) goto after;
10828 if (w->tm_sec < sec) goto before;
10842 /* parse the offset: (+|-)hh[:mm[:ss]] */
10845 tz_parse_offset(char *s, int *offset)
10847 int hour = 0, min = 0, sec = 0;
10850 if (!offset) return 0;
10852 if (*s == '-') {neg++; s++;}
10853 if (*s == '+') s++;
10854 if (!isdigit(*s)) return 0;
10856 if (isdigit(*s)) hour = hour*10+(*s++ - '0');
10857 if (hour > 24) return 0;
10859 if (!isdigit(*++s)) return 0;
10861 if (isdigit(*s)) min = min*10 + (*s++ - '0');
10862 if (min > 59) return 0;
10864 if (!isdigit(*++s)) return 0;
10866 if (isdigit(*s)) sec = sec*10 + (*s++ - '0');
10867 if (sec > 59) return 0;
10871 *offset = (hour*60+min)*60 + sec;
10872 if (neg) *offset = -*offset;
10877 input time is w, whatever type of time the CRTL localtime() uses.
10878 sets dst, the zone, and the gmtoff (seconds)
10880 caches the value of TZ and UCX$TZ env variables; note that
10881 my_setenv looks for these and sets a flag if they're changed
10884 We have to watch out for the "australian" case (dst starts in
10885 october, ends in april)...flagged by "reverse" and checked by
10886 scanning through the months of the previous year.
10891 tz_parse(pTHX_ time_t *w, int *dst, char *zone, int *gmtoff)
10896 char *dstzone, *tz, *s_start, *s_end;
10897 int std_off, dst_off, isdst;
10898 int y, dststart, dstend;
10899 static char envtz[1025]; /* longer than any logical, symbol, ... */
10900 static char ucxtz[1025];
10901 static char reversed = 0;
10907 reversed = -1; /* flag need to check */
10908 envtz[0] = ucxtz[0] = '\0';
10909 tz = my_getenv("TZ",0);
10910 if (tz) strcpy(envtz, tz);
10911 tz = my_getenv("UCX$TZ",0);
10912 if (tz) strcpy(ucxtz, tz);
10913 if (!envtz[0] && !ucxtz[0]) return 0; /* we give up */
10916 if (!*tz) tz = ucxtz;
10919 while (isalpha(*s)) s++;
10920 s = tz_parse_offset(s, &std_off);
10922 if (!*s) { /* no DST, hurray we're done! */
10928 while (isalpha(*s)) s++;
10929 s2 = tz_parse_offset(s, &dst_off);
10933 dst_off = std_off - 3600;
10936 if (!*s) { /* default dst start/end?? */
10937 if (tz != ucxtz) { /* if TZ tells zone only, UCX$TZ tells rule */
10938 s = strchr(ucxtz,',');
10940 if (!s || !*s) s = ",M4.1.0,M10.5.0"; /* we know we do dst, default rule */
10942 if (*s != ',') return 0;
10945 when = _toutc(when); /* convert to utc */
10946 when = when - std_off; /* convert to pseudolocal time*/
10948 w2 = localtime(&when);
10951 s = tz_parse_startend(s_start,w2,&dststart);
10953 if (*s != ',') return 0;
10956 when = _toutc(when); /* convert to utc */
10957 when = when - dst_off; /* convert to pseudolocal time*/
10958 w2 = localtime(&when);
10959 if (w2->tm_year != y) { /* spans a year, just check one time */
10960 when += dst_off - std_off;
10961 w2 = localtime(&when);
10964 s = tz_parse_startend(s_end,w2,&dstend);
10967 if (reversed == -1) { /* need to check if start later than end */
10971 if (when < 2*365*86400) {
10972 when += 2*365*86400;
10976 w2 =localtime(&when);
10977 when = when + (15 - w2->tm_yday) * 86400; /* jan 15 */
10979 for (j = 0; j < 12; j++) {
10980 w2 =localtime(&when);
10981 tz_parse_startend(s_start,w2,&ds);
10982 tz_parse_startend(s_end,w2,&de);
10983 if (ds != de) break;
10987 if (de && !ds) reversed = 1;
10990 isdst = dststart && !dstend;
10991 if (reversed) isdst = dststart || !dstend;
10994 if (dst) *dst = isdst;
10995 if (gmtoff) *gmtoff = isdst ? dst_off : std_off;
10996 if (isdst) tz = dstzone;
10998 while(isalpha(*tz)) *zone++ = *tz++;
11004 #endif /* !RTL_USES_UTC */
11006 /* my_time(), my_localtime(), my_gmtime()
11007 * By default traffic in UTC time values, using CRTL gmtime() or
11008 * SYS$TIMEZONE_DIFFERENTIAL to determine offset from local time zone.
11009 * Note: We need to use these functions even when the CRTL has working
11010 * UTC support, since they also handle C<use vmsish qw(times);>
11012 * Contributed by Chuck Lane <lane@duphy4.physics.drexel.edu>
11013 * Modified by Charles Bailey <bailey@newman.upenn.edu>
11016 /*{{{time_t my_time(time_t *timep)*/
11017 time_t Perl_my_time(pTHX_ time_t *timep)
11022 if (gmtime_emulation_type == 0) {
11024 time_t base = 15 * 86400; /* 15jan71; to avoid month/year ends between */
11025 /* results of calls to gmtime() and localtime() */
11026 /* for same &base */
11028 gmtime_emulation_type++;
11029 if ((tm_p = gmtime(&base)) == NULL) { /* CRTL gmtime() is a fake */
11030 char off[LNM$C_NAMLENGTH+1];;
11032 gmtime_emulation_type++;
11033 if (!vmstrnenv("SYS$TIMEZONE_DIFFERENTIAL",off,0,fildev,0)) {
11034 gmtime_emulation_type++;
11035 utc_offset_secs = 0;
11036 Perl_warn(aTHX_ "no UTC offset information; assuming local time is UTC");
11038 else { utc_offset_secs = atol(off); }
11040 else { /* We've got a working gmtime() */
11041 struct tm gmt, local;
11044 tm_p = localtime(&base);
11046 utc_offset_secs = (local.tm_mday - gmt.tm_mday) * 86400;
11047 utc_offset_secs += (local.tm_hour - gmt.tm_hour) * 3600;
11048 utc_offset_secs += (local.tm_min - gmt.tm_min) * 60;
11049 utc_offset_secs += (local.tm_sec - gmt.tm_sec);
11054 # ifdef VMSISH_TIME
11055 # ifdef RTL_USES_UTC
11056 if (VMSISH_TIME) when = _toloc(when);
11058 if (!VMSISH_TIME) when = _toutc(when);
11061 if (timep != NULL) *timep = when;
11064 } /* end of my_time() */
11068 /*{{{struct tm *my_gmtime(const time_t *timep)*/
11070 Perl_my_gmtime(pTHX_ const time_t *timep)
11076 if (timep == NULL) {
11077 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11080 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
11083 # ifdef VMSISH_TIME
11084 if (VMSISH_TIME) when = _toutc(when); /* Input was local time */
11086 # ifdef RTL_USES_UTC /* this implies that the CRTL has a working gmtime() */
11087 return gmtime(&when);
11089 /* CRTL localtime() wants local time as input, so does no tz correction */
11090 rsltmp = localtime(&when);
11091 if (rsltmp) rsltmp->tm_isdst = 0; /* We already took DST into account */
11094 } /* end of my_gmtime() */
11098 /*{{{struct tm *my_localtime(const time_t *timep)*/
11100 Perl_my_localtime(pTHX_ const time_t *timep)
11102 time_t when, whenutc;
11106 if (timep == NULL) {
11107 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11110 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
11111 if (gmtime_emulation_type == 0) my_time(NULL); /* Init UTC */
11114 # ifdef RTL_USES_UTC
11115 # ifdef VMSISH_TIME
11116 if (VMSISH_TIME) when = _toutc(when);
11118 /* CRTL localtime() wants UTC as input, does tz correction itself */
11119 return localtime(&when);
11121 # else /* !RTL_USES_UTC */
11123 # ifdef VMSISH_TIME
11124 if (!VMSISH_TIME) when = _toloc(whenutc); /* input was UTC */
11125 if (VMSISH_TIME) whenutc = _toutc(when); /* input was truelocal */
11128 #ifndef RTL_USES_UTC
11129 if (tz_parse(aTHX_ &when, &dst, 0, &offset)) { /* truelocal determines DST*/
11130 when = whenutc - offset; /* pseudolocal time*/
11133 /* CRTL localtime() wants local time as input, so does no tz correction */
11134 rsltmp = localtime(&when);
11135 if (rsltmp && gmtime_emulation_type != 1) rsltmp->tm_isdst = dst;
11139 } /* end of my_localtime() */
11142 /* Reset definitions for later calls */
11143 #define gmtime(t) my_gmtime(t)
11144 #define localtime(t) my_localtime(t)
11145 #define time(t) my_time(t)
11148 /* my_utime - update modification/access time of a file
11150 * VMS 7.3 and later implementation
11151 * Only the UTC translation is home-grown. The rest is handled by the
11152 * CRTL utime(), which will take into account the relevant feature
11153 * logicals and ODS-5 volume characteristics for true access times.
11155 * pre VMS 7.3 implementation:
11156 * The calling sequence is identical to POSIX utime(), but under
11157 * VMS with ODS-2, only the modification time is changed; ODS-2 does
11158 * not maintain access times. Restrictions differ from the POSIX
11159 * definition in that the time can be changed as long as the
11160 * caller has permission to execute the necessary IO$_MODIFY $QIO;
11161 * no separate checks are made to insure that the caller is the
11162 * owner of the file or has special privs enabled.
11163 * Code here is based on Joe Meadows' FILE utility.
11167 /* Adjustment from Unix epoch (01-JAN-1970 00:00:00.00)
11168 * to VMS epoch (01-JAN-1858 00:00:00.00)
11169 * in 100 ns intervals.
11171 static const long int utime_baseadjust[2] = { 0x4beb4000, 0x7c9567 };
11173 /*{{{int my_utime(const char *path, const struct utimbuf *utimes)*/
11174 int Perl_my_utime(pTHX_ const char *file, const struct utimbuf *utimes)
11176 #if __CRTL_VER >= 70300000
11177 struct utimbuf utc_utimes, *utc_utimesp;
11179 if (utimes != NULL) {
11180 utc_utimes.actime = utimes->actime;
11181 utc_utimes.modtime = utimes->modtime;
11182 # ifdef VMSISH_TIME
11183 /* If input was local; convert to UTC for sys svc */
11185 utc_utimes.actime = _toutc(utimes->actime);
11186 utc_utimes.modtime = _toutc(utimes->modtime);
11189 utc_utimesp = &utc_utimes;
11192 utc_utimesp = NULL;
11195 return utime(file, utc_utimesp);
11197 #else /* __CRTL_VER < 70300000 */
11201 long int bintime[2], len = 2, lowbit, unixtime,
11202 secscale = 10000000; /* seconds --> 100 ns intervals */
11203 unsigned long int chan, iosb[2], retsts;
11204 char vmsspec[NAM$C_MAXRSS+1], rsa[NAM$C_MAXRSS], esa[NAM$C_MAXRSS];
11205 struct FAB myfab = cc$rms_fab;
11206 struct NAM mynam = cc$rms_nam;
11207 #if defined (__DECC) && defined (__VAX)
11208 /* VAX DEC C atrdef.h has unsigned type for pointer member atr$l_addr,
11209 * at least through VMS V6.1, which causes a type-conversion warning.
11211 # pragma message save
11212 # pragma message disable cvtdiftypes
11214 struct atrdef myatr[2] = {{sizeof bintime, ATR$C_REVDATE, bintime}, {0,0,0}};
11215 struct fibdef myfib;
11216 #if defined (__DECC) && defined (__VAX)
11217 /* This should be right after the declaration of myatr, but due
11218 * to a bug in VAX DEC C, this takes effect a statement early.
11220 # pragma message restore
11222 /* cast ok for read only parameter */
11223 struct dsc$descriptor fibdsc = {sizeof(myfib), DSC$K_DTYPE_Z, DSC$K_CLASS_S,(char *) &myfib},
11224 devdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0},
11225 fnmdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0};
11227 if (file == NULL || *file == '\0') {
11228 SETERRNO(ENOENT, LIB$_INVARG);
11232 /* Convert to VMS format ensuring that it will fit in 255 characters */
11233 if (do_rmsexpand(file, vmsspec, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL) == NULL) {
11234 SETERRNO(ENOENT, LIB$_INVARG);
11237 if (utimes != NULL) {
11238 /* Convert Unix time (seconds since 01-JAN-1970 00:00:00.00)
11239 * to VMS quadword time (100 nsec intervals since 01-JAN-1858 00:00:00.00).
11240 * Since time_t is unsigned long int, and lib$emul takes a signed long int
11241 * as input, we force the sign bit to be clear by shifting unixtime right
11242 * one bit, then multiplying by an extra factor of 2 in lib$emul().
11244 lowbit = (utimes->modtime & 1) ? secscale : 0;
11245 unixtime = (long int) utimes->modtime;
11246 # ifdef VMSISH_TIME
11247 /* If input was UTC; convert to local for sys svc */
11248 if (!VMSISH_TIME) unixtime = _toloc(unixtime);
11250 unixtime >>= 1; secscale <<= 1;
11251 retsts = lib$emul(&secscale, &unixtime, &lowbit, bintime);
11252 if (!(retsts & 1)) {
11253 SETERRNO(EVMSERR, retsts);
11256 retsts = lib$addx(bintime,utime_baseadjust,bintime,&len);
11257 if (!(retsts & 1)) {
11258 SETERRNO(EVMSERR, retsts);
11263 /* Just get the current time in VMS format directly */
11264 retsts = sys$gettim(bintime);
11265 if (!(retsts & 1)) {
11266 SETERRNO(EVMSERR, retsts);
11271 myfab.fab$l_fna = vmsspec;
11272 myfab.fab$b_fns = (unsigned char) strlen(vmsspec);
11273 myfab.fab$l_nam = &mynam;
11274 mynam.nam$l_esa = esa;
11275 mynam.nam$b_ess = (unsigned char) sizeof esa;
11276 mynam.nam$l_rsa = rsa;
11277 mynam.nam$b_rss = (unsigned char) sizeof rsa;
11278 if (decc_efs_case_preserve)
11279 mynam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
11281 /* Look for the file to be affected, letting RMS parse the file
11282 * specification for us as well. I have set errno using only
11283 * values documented in the utime() man page for VMS POSIX.
11285 retsts = sys$parse(&myfab,0,0);
11286 if (!(retsts & 1)) {
11287 set_vaxc_errno(retsts);
11288 if (retsts == RMS$_PRV) set_errno(EACCES);
11289 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
11290 else set_errno(EVMSERR);
11293 retsts = sys$search(&myfab,0,0);
11294 if (!(retsts & 1)) {
11295 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
11296 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
11297 set_vaxc_errno(retsts);
11298 if (retsts == RMS$_PRV) set_errno(EACCES);
11299 else if (retsts == RMS$_FNF) set_errno(ENOENT);
11300 else set_errno(EVMSERR);
11304 devdsc.dsc$w_length = mynam.nam$b_dev;
11305 /* cast ok for read only parameter */
11306 devdsc.dsc$a_pointer = (char *) mynam.nam$l_dev;
11308 retsts = sys$assign(&devdsc,&chan,0,0);
11309 if (!(retsts & 1)) {
11310 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
11311 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
11312 set_vaxc_errno(retsts);
11313 if (retsts == SS$_IVDEVNAM) set_errno(ENOTDIR);
11314 else if (retsts == SS$_NOPRIV) set_errno(EACCES);
11315 else if (retsts == SS$_NOSUCHDEV) set_errno(ENOTDIR);
11316 else set_errno(EVMSERR);
11320 fnmdsc.dsc$a_pointer = mynam.nam$l_name;
11321 fnmdsc.dsc$w_length = mynam.nam$b_name + mynam.nam$b_type + mynam.nam$b_ver;
11323 memset((void *) &myfib, 0, sizeof myfib);
11324 #if defined(__DECC) || defined(__DECCXX)
11325 for (i=0;i<3;i++) myfib.fib$w_fid[i] = mynam.nam$w_fid[i];
11326 for (i=0;i<3;i++) myfib.fib$w_did[i] = mynam.nam$w_did[i];
11327 /* This prevents the revision time of the file being reset to the current
11328 * time as a result of our IO$_MODIFY $QIO. */
11329 myfib.fib$l_acctl = FIB$M_NORECORD;
11331 for (i=0;i<3;i++) myfib.fib$r_fid_overlay.fib$w_fid[i] = mynam.nam$w_fid[i];
11332 for (i=0;i<3;i++) myfib.fib$r_did_overlay.fib$w_did[i] = mynam.nam$w_did[i];
11333 myfib.fib$r_acctl_overlay.fib$l_acctl = FIB$M_NORECORD;
11335 retsts = sys$qiow(0,chan,IO$_MODIFY,iosb,0,0,&fibdsc,&fnmdsc,0,0,myatr,0);
11336 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
11337 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
11338 _ckvmssts(sys$dassgn(chan));
11339 if (retsts & 1) retsts = iosb[0];
11340 if (!(retsts & 1)) {
11341 set_vaxc_errno(retsts);
11342 if (retsts == SS$_NOPRIV) set_errno(EACCES);
11343 else set_errno(EVMSERR);
11349 #endif /* #if __CRTL_VER >= 70300000 */
11351 } /* end of my_utime() */
11355 * flex_stat, flex_lstat, flex_fstat
11356 * basic stat, but gets it right when asked to stat
11357 * a Unix-style path ending in a directory name (e.g. dir1/dir2/dir3)
11360 #ifndef _USE_STD_STAT
11361 /* encode_dev packs a VMS device name string into an integer to allow
11362 * simple comparisons. This can be used, for example, to check whether two
11363 * files are located on the same device, by comparing their encoded device
11364 * names. Even a string comparison would not do, because stat() reuses the
11365 * device name buffer for each call; so without encode_dev, it would be
11366 * necessary to save the buffer and use strcmp (this would mean a number of
11367 * changes to the standard Perl code, to say nothing of what a Perl script
11368 * would have to do.
11370 * The device lock id, if it exists, should be unique (unless perhaps compared
11371 * with lock ids transferred from other nodes). We have a lock id if the disk is
11372 * mounted cluster-wide, which is when we tend to get long (host-qualified)
11373 * device names. Thus we use the lock id in preference, and only if that isn't
11374 * available, do we try to pack the device name into an integer (flagged by
11375 * the sign bit (LOCKID_MASK) being set).
11377 * Note that encode_dev cannot guarantee an 1-to-1 correspondence twixt device
11378 * name and its encoded form, but it seems very unlikely that we will find
11379 * two files on different disks that share the same encoded device names,
11380 * and even more remote that they will share the same file id (if the test
11381 * is to check for the same file).
11383 * A better method might be to use sys$device_scan on the first call, and to
11384 * search for the device, returning an index into the cached array.
11385 * The number returned would be more intelligible.
11386 * This is probably not worth it, and anyway would take quite a bit longer
11387 * on the first call.
11389 #define LOCKID_MASK 0x80000000 /* Use 0 to force device name use only */
11390 static mydev_t encode_dev (pTHX_ const char *dev)
11393 unsigned long int f;
11398 if (!dev || !dev[0]) return 0;
11402 struct dsc$descriptor_s dev_desc;
11403 unsigned long int status, lockid = 0, item = DVI$_LOCKID;
11405 /* For cluster-mounted disks, the disk lock identifier is unique, so we
11406 can try that first. */
11407 dev_desc.dsc$w_length = strlen (dev);
11408 dev_desc.dsc$b_dtype = DSC$K_DTYPE_T;
11409 dev_desc.dsc$b_class = DSC$K_CLASS_S;
11410 dev_desc.dsc$a_pointer = (char *) dev; /* Read only parameter */
11411 status = lib$getdvi(&item, 0, &dev_desc, &lockid, 0, 0);
11412 if (!$VMS_STATUS_SUCCESS(status)) {
11414 case SS$_NOSUCHDEV:
11415 SETERRNO(ENODEV, status);
11421 if (lockid) return (lockid & ~LOCKID_MASK);
11425 /* Otherwise we try to encode the device name */
11429 for (q = dev + strlen(dev); q--; q >= dev) {
11434 else if (isalpha (toupper (*q)))
11435 c= toupper (*q) - 'A' + (char)10;
11437 continue; /* Skip '$'s */
11439 if (i>6) break; /* 36^7 is too large to fit in an unsigned long int */
11441 enc += f * (unsigned long int) c;
11443 return (enc | LOCKID_MASK); /* May have already overflowed into bit 31 */
11445 } /* end of encode_dev() */
11446 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
11447 device_no = encode_dev(aTHX_ devname)
11449 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
11450 device_no = new_dev_no
11454 is_null_device(name)
11457 if (decc_bug_devnull != 0) {
11458 if (strncmp("/dev/null", name, 9) == 0)
11461 /* The VMS null device is named "_NLA0:", usually abbreviated as "NL:".
11462 The underscore prefix, controller letter, and unit number are
11463 independently optional; for our purposes, the colon punctuation
11464 is not. The colon can be trailed by optional directory and/or
11465 filename, but two consecutive colons indicates a nodename rather
11466 than a device. [pr] */
11467 if (*name == '_') ++name;
11468 if (tolower(*name++) != 'n') return 0;
11469 if (tolower(*name++) != 'l') return 0;
11470 if (tolower(*name) == 'a') ++name;
11471 if (*name == '0') ++name;
11472 return (*name++ == ':') && (*name != ':');
11477 Perl_cando_by_name_int
11478 (pTHX_ I32 bit, bool effective, const char *fname, int opts)
11480 char usrname[L_cuserid];
11481 struct dsc$descriptor_s usrdsc =
11482 {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, usrname};
11483 char *vmsname = NULL, *fileified = NULL;
11484 unsigned long int objtyp = ACL$C_FILE, access, retsts, privused, iosb[2], flags;
11485 unsigned short int retlen, trnlnm_iter_count;
11486 struct dsc$descriptor_s namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
11487 union prvdef curprv;
11488 struct itmlst_3 armlst[4] = {{sizeof access, CHP$_ACCESS, &access, &retlen},
11489 {sizeof privused, CHP$_PRIVUSED, &privused, &retlen},
11490 {sizeof flags, CHP$_FLAGS, &flags, &retlen},{0,0,0,0}};
11491 struct itmlst_3 jpilst[3] = {{sizeof curprv, JPI$_CURPRIV, &curprv, &retlen},
11492 {sizeof usrname, JPI$_USERNAME, &usrname, &usrdsc.dsc$w_length},
11494 struct itmlst_3 usrprolst[2] = {{sizeof curprv, CHP$_PRIV, &curprv, &retlen},
11496 struct dsc$descriptor_s usrprodsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
11498 static int profile_context = -1;
11500 if (!fname || !*fname) return FALSE;
11502 /* Make sure we expand logical names, since sys$check_access doesn't */
11503 fileified = PerlMem_malloc(VMS_MAXRSS);
11504 if (fileified == NULL) _ckvmssts(SS$_INSFMEM);
11505 if (!strpbrk(fname,"/]>:")) {
11506 strcpy(fileified,fname);
11507 trnlnm_iter_count = 0;
11508 while (!strpbrk(fileified,"/]>:") && my_trnlnm(fileified,fileified,0)) {
11509 trnlnm_iter_count++;
11510 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
11515 vmsname = PerlMem_malloc(VMS_MAXRSS);
11516 if (vmsname == NULL) _ckvmssts(SS$_INSFMEM);
11517 if ( !(opts & PERL_RMSEXPAND_M_VMS_IN) ) {
11518 /* Don't know if already in VMS format, so make sure */
11519 if (!do_rmsexpand(fname, vmsname, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL)) {
11520 PerlMem_free(fileified);
11521 PerlMem_free(vmsname);
11526 strcpy(vmsname,fname);
11529 /* sys$check_access needs a file spec, not a directory spec.
11530 * Don't use flex_stat here, as that depends on thread context
11531 * having been initialized, and we may get here during startup.
11534 retlen = namdsc.dsc$w_length = strlen(vmsname);
11535 if (vmsname[retlen-1] == ']'
11536 || vmsname[retlen-1] == '>'
11537 || vmsname[retlen-1] == ':'
11538 || (!stat(vmsname, (stat_t *)&st) && S_ISDIR(st.st_mode))) {
11540 if (!do_fileify_dirspec(vmsname,fileified,1,NULL)) {
11541 PerlMem_free(fileified);
11542 PerlMem_free(vmsname);
11551 retlen = namdsc.dsc$w_length = strlen(fname);
11552 namdsc.dsc$a_pointer = (char *)fname;
11555 case S_IXUSR: case S_IXGRP: case S_IXOTH:
11556 access = ARM$M_EXECUTE;
11557 flags = CHP$M_READ;
11559 case S_IRUSR: case S_IRGRP: case S_IROTH:
11560 access = ARM$M_READ;
11561 flags = CHP$M_READ | CHP$M_USEREADALL;
11563 case S_IWUSR: case S_IWGRP: case S_IWOTH:
11564 access = ARM$M_WRITE;
11565 flags = CHP$M_READ | CHP$M_WRITE;
11567 case S_IDUSR: case S_IDGRP: case S_IDOTH:
11568 access = ARM$M_DELETE;
11569 flags = CHP$M_READ | CHP$M_WRITE;
11572 if (fileified != NULL)
11573 PerlMem_free(fileified);
11574 if (vmsname != NULL)
11575 PerlMem_free(vmsname);
11579 /* Before we call $check_access, create a user profile with the current
11580 * process privs since otherwise it just uses the default privs from the
11581 * UAF and might give false positives or negatives. This only works on
11582 * VMS versions v6.0 and later since that's when sys$create_user_profile
11583 * became available.
11586 /* get current process privs and username */
11587 _ckvmssts(sys$getjpiw(0,0,0,jpilst,iosb,0,0));
11588 _ckvmssts(iosb[0]);
11590 #if defined(__VMS_VER) && __VMS_VER >= 60000000
11592 /* find out the space required for the profile */
11593 _ckvmssts(sys$create_user_profile(&usrdsc,&usrprolst,0,0,
11594 &usrprodsc.dsc$w_length,&profile_context));
11596 /* allocate space for the profile and get it filled in */
11597 usrprodsc.dsc$a_pointer = PerlMem_malloc(usrprodsc.dsc$w_length);
11598 if (usrprodsc.dsc$a_pointer == NULL) _ckvmssts(SS$_INSFMEM);
11599 _ckvmssts(sys$create_user_profile(&usrdsc,&usrprolst,0,usrprodsc.dsc$a_pointer,
11600 &usrprodsc.dsc$w_length,&profile_context));
11602 /* use the profile to check access to the file; free profile & analyze results */
11603 retsts = sys$check_access(&objtyp,&namdsc,0,armlst,&profile_context,0,0,&usrprodsc);
11604 PerlMem_free(usrprodsc.dsc$a_pointer);
11605 if (retsts == SS$_NOCALLPRIV) retsts = SS$_NOPRIV; /* not really 3rd party */
11609 retsts = sys$check_access(&objtyp,&namdsc,&usrdsc,armlst);
11613 if (retsts == SS$_NOPRIV || retsts == SS$_NOSUCHOBJECT ||
11614 retsts == SS$_INVFILFOROP || retsts == RMS$_FNF || retsts == RMS$_SYN ||
11615 retsts == RMS$_DIR || retsts == RMS$_DEV || retsts == RMS$_DNF) {
11616 set_vaxc_errno(retsts);
11617 if (retsts == SS$_NOPRIV) set_errno(EACCES);
11618 else if (retsts == SS$_INVFILFOROP) set_errno(EINVAL);
11619 else set_errno(ENOENT);
11620 if (fileified != NULL)
11621 PerlMem_free(fileified);
11622 if (vmsname != NULL)
11623 PerlMem_free(vmsname);
11626 if (retsts == SS$_NORMAL || retsts == SS$_ACCONFLICT) {
11627 if (fileified != NULL)
11628 PerlMem_free(fileified);
11629 if (vmsname != NULL)
11630 PerlMem_free(vmsname);
11635 if (fileified != NULL)
11636 PerlMem_free(fileified);
11637 if (vmsname != NULL)
11638 PerlMem_free(vmsname);
11639 return FALSE; /* Should never get here */
11643 /* Do the permissions allow some operation? Assumes PL_statcache already set. */
11644 /* Do this via $Check_Access on VMS, since the CRTL stat() returns only a
11645 * subset of the applicable information.
11648 Perl_cando(pTHX_ Mode_t bit, bool effective, const Stat_t *statbufp)
11650 return cando_by_name_int
11651 (bit, effective, statbufp->st_devnam, PERL_RMSEXPAND_M_VMS_IN);
11652 } /* end of cando() */
11656 /*{{{I32 cando_by_name(I32 bit, bool effective, char *fname)*/
11658 Perl_cando_by_name(pTHX_ I32 bit, bool effective, const char *fname)
11660 return cando_by_name_int(bit, effective, fname, 0);
11662 } /* end of cando_by_name() */
11666 /*{{{ int flex_fstat(int fd, Stat_t *statbuf)*/
11668 Perl_flex_fstat(pTHX_ int fd, Stat_t *statbufp)
11670 if (!fstat(fd,(stat_t *) statbufp)) {
11672 char *vms_filename;
11673 vms_filename = PerlMem_malloc(VMS_MAXRSS);
11674 if (vms_filename == NULL) _ckvmssts(SS$_INSFMEM);
11676 /* Save name for cando by name in VMS format */
11677 cptr = getname(fd, vms_filename, 1);
11679 /* This should not happen, but just in case */
11680 if (cptr == NULL) {
11681 statbufp->st_devnam[0] = 0;
11684 /* Make sure that the saved name fits in 255 characters */
11685 cptr = do_rmsexpand
11687 statbufp->st_devnam,
11690 PERL_RMSEXPAND_M_VMS | PERL_RMSEXPAND_M_VMS_IN,
11694 statbufp->st_devnam[0] = 0;
11696 PerlMem_free(vms_filename);
11698 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
11700 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
11702 # ifdef RTL_USES_UTC
11703 # ifdef VMSISH_TIME
11705 statbufp->st_mtime = _toloc(statbufp->st_mtime);
11706 statbufp->st_atime = _toloc(statbufp->st_atime);
11707 statbufp->st_ctime = _toloc(statbufp->st_ctime);
11711 # ifdef VMSISH_TIME
11712 if (!VMSISH_TIME) { /* Return UTC instead of local time */
11716 statbufp->st_mtime = _toutc(statbufp->st_mtime);
11717 statbufp->st_atime = _toutc(statbufp->st_atime);
11718 statbufp->st_ctime = _toutc(statbufp->st_ctime);
11725 } /* end of flex_fstat() */
11728 #if !defined(__VAX) && __CRTL_VER >= 80200000
11736 #define lstat(_x, _y) stat(_x, _y)
11739 #define flex_stat_int(a,b,c) Perl_flex_stat_int(aTHX_ a,b,c)
11742 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag)
11744 char fileified[VMS_MAXRSS];
11745 char temp_fspec[VMS_MAXRSS];
11748 int saved_errno, saved_vaxc_errno;
11750 if (!fspec) return retval;
11751 saved_errno = errno; saved_vaxc_errno = vaxc$errno;
11752 strcpy(temp_fspec, fspec);
11754 if (decc_bug_devnull != 0) {
11755 if (is_null_device(temp_fspec)) { /* Fake a stat() for the null device */
11756 memset(statbufp,0,sizeof *statbufp);
11757 VMS_DEVICE_ENCODE(statbufp->st_dev, "_NLA0:", 0);
11758 statbufp->st_mode = S_IFBLK | S_IREAD | S_IWRITE | S_IEXEC;
11759 statbufp->st_uid = 0x00010001;
11760 statbufp->st_gid = 0x0001;
11761 time((time_t *)&statbufp->st_mtime);
11762 statbufp->st_atime = statbufp->st_ctime = statbufp->st_mtime;
11767 /* Try for a directory name first. If fspec contains a filename without
11768 * a type (e.g. sea:[wine.dark]water), and both sea:[wine.dark]water.dir
11769 * and sea:[wine.dark]water. exist, we prefer the directory here.
11770 * Similarly, sea:[wine.dark] returns the result for sea:[wine]dark.dir,
11771 * not sea:[wine.dark]., if the latter exists. If the intended target is
11772 * the file with null type, specify this by calling flex_stat() with
11773 * a '.' at the end of fspec.
11775 * If we are in Posix filespec mode, accept the filename as is.
11779 #if __CRTL_VER >= 70300000 && !defined(__VAX)
11780 /* The CRTL stat() falls down hard on multi-dot filenames in unix format unless
11781 * DECC$EFS_CHARSET is in effect, so temporarily enable it if it isn't already.
11783 if (!decc_efs_charset)
11784 decc$feature_set_value(decc$feature_get_index("DECC$EFS_CHARSET"),1,1);
11787 #if __CRTL_VER >= 80200000 && !defined(__VAX)
11788 if (decc_posix_compliant_pathnames == 0) {
11790 if (do_fileify_dirspec(temp_fspec,fileified,0,NULL) != NULL) {
11791 if (lstat_flag == 0)
11792 retval = stat(fileified,(stat_t *) statbufp);
11794 retval = lstat(fileified,(stat_t *) statbufp);
11795 save_spec = fileified;
11798 if (lstat_flag == 0)
11799 retval = stat(temp_fspec,(stat_t *) statbufp);
11801 retval = lstat(temp_fspec,(stat_t *) statbufp);
11802 save_spec = temp_fspec;
11805 * In debugging, on 8.3 Alpha, I found a case where stat was returning a
11806 * file not found error for a directory named foo:[bar.t] or /foo/bar/t
11807 * and lstat was working correctly for the same file.
11808 * The only syntax that was working for stat was "foo:[bar]t.dir".
11810 * Other directories with the same syntax worked fine.
11811 * So work around the problem when it shows up here.
11814 int save_errno = errno;
11815 if (do_tovmsspec(fspec, temp_fspec, 0, NULL) != NULL) {
11816 if (do_fileify_dirspec(temp_fspec, fileified, 0, NULL) != NULL) {
11817 retval = stat(fileified, (stat_t *) statbufp);
11818 save_spec = fileified;
11821 /* Restore the errno value if third stat does not succeed */
11823 errno = save_errno;
11825 #if __CRTL_VER >= 80200000 && !defined(__VAX)
11827 if (lstat_flag == 0)
11828 retval = stat(temp_fspec,(stat_t *) statbufp);
11830 retval = lstat(temp_fspec,(stat_t *) statbufp);
11831 save_spec = temp_fspec;
11835 #if __CRTL_VER >= 70300000 && !defined(__VAX)
11836 /* As you were... */
11837 if (!decc_efs_charset)
11838 decc$feature_set_value(decc$feature_get_index("DECC$EFS_CHARSET"),1,0);
11843 cptr = do_rmsexpand
11844 (save_spec, statbufp->st_devnam, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL);
11846 statbufp->st_devnam[0] = 0;
11848 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
11850 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
11851 # ifdef RTL_USES_UTC
11852 # ifdef VMSISH_TIME
11854 statbufp->st_mtime = _toloc(statbufp->st_mtime);
11855 statbufp->st_atime = _toloc(statbufp->st_atime);
11856 statbufp->st_ctime = _toloc(statbufp->st_ctime);
11860 # ifdef VMSISH_TIME
11861 if (!VMSISH_TIME) { /* Return UTC instead of local time */
11865 statbufp->st_mtime = _toutc(statbufp->st_mtime);
11866 statbufp->st_atime = _toutc(statbufp->st_atime);
11867 statbufp->st_ctime = _toutc(statbufp->st_ctime);
11871 /* If we were successful, leave errno where we found it */
11872 if (retval == 0) { errno = saved_errno; vaxc$errno = saved_vaxc_errno; }
11875 } /* end of flex_stat_int() */
11878 /*{{{ int flex_stat(const char *fspec, Stat_t *statbufp)*/
11880 Perl_flex_stat(pTHX_ const char *fspec, Stat_t *statbufp)
11882 return flex_stat_int(fspec, statbufp, 0);
11886 /*{{{ int flex_lstat(const char *fspec, Stat_t *statbufp)*/
11888 Perl_flex_lstat(pTHX_ const char *fspec, Stat_t *statbufp)
11890 return flex_stat_int(fspec, statbufp, 1);
11895 /*{{{char *my_getlogin()*/
11896 /* VMS cuserid == Unix getlogin, except calling sequence */
11900 static char user[L_cuserid];
11901 return cuserid(user);
11906 /* rmscopy - copy a file using VMS RMS routines
11908 * Copies contents and attributes of spec_in to spec_out, except owner
11909 * and protection information. Name and type of spec_in are used as
11910 * defaults for spec_out. The third parameter specifies whether rmscopy()
11911 * should try to propagate timestamps from the input file to the output file.
11912 * If it is less than 0, no timestamps are preserved. If it is 0, then
11913 * rmscopy() will behave similarly to the DCL COPY command: timestamps are
11914 * propagated to the output file at creation iff the output file specification
11915 * did not contain an explicit name or type, and the revision date is always
11916 * updated at the end of the copy operation. If it is greater than 0, then
11917 * it is interpreted as a bitmask, in which bit 0 indicates that timestamps
11918 * other than the revision date should be propagated, and bit 1 indicates
11919 * that the revision date should be propagated.
11921 * Returns 1 on success; returns 0 and sets errno and vaxc$errno on failure.
11923 * Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>.
11924 * Incorporates, with permission, some code from EZCOPY by Tim Adye
11925 * <T.J.Adye@rl.ac.uk>. Permission is given to distribute this code
11926 * as part of the Perl standard distribution under the terms of the
11927 * GNU General Public License or the Perl Artistic License. Copies
11928 * of each may be found in the Perl standard distribution.
11930 /*{{{int rmscopy(char *src, char *dst, int preserve_dates)*/
11932 Perl_rmscopy(pTHX_ const char *spec_in, const char *spec_out, int preserve_dates)
11934 char *vmsin, * vmsout, *esa, *esa_out,
11936 unsigned long int i, sts, sts2;
11938 struct FAB fab_in, fab_out;
11939 struct RAB rab_in, rab_out;
11940 rms_setup_nam(nam);
11941 rms_setup_nam(nam_out);
11942 struct XABDAT xabdat;
11943 struct XABFHC xabfhc;
11944 struct XABRDT xabrdt;
11945 struct XABSUM xabsum;
11947 vmsin = PerlMem_malloc(VMS_MAXRSS);
11948 if (vmsin == NULL) _ckvmssts(SS$_INSFMEM);
11949 vmsout = PerlMem_malloc(VMS_MAXRSS);
11950 if (vmsout == NULL) _ckvmssts(SS$_INSFMEM);
11951 if (!spec_in || !*spec_in || !do_tovmsspec(spec_in,vmsin,1,NULL) ||
11952 !spec_out || !*spec_out || !do_tovmsspec(spec_out,vmsout,1,NULL)) {
11953 PerlMem_free(vmsin);
11954 PerlMem_free(vmsout);
11955 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11959 esa = PerlMem_malloc(VMS_MAXRSS);
11960 if (esa == NULL) _ckvmssts(SS$_INSFMEM);
11961 fab_in = cc$rms_fab;
11962 rms_set_fna(fab_in, nam, vmsin, strlen(vmsin));
11963 fab_in.fab$b_shr = FAB$M_SHRPUT | FAB$M_UPI;
11964 fab_in.fab$b_fac = FAB$M_BIO | FAB$M_GET;
11965 fab_in.fab$l_fop = FAB$M_SQO;
11966 rms_bind_fab_nam(fab_in, nam);
11967 fab_in.fab$l_xab = (void *) &xabdat;
11969 rsa = PerlMem_malloc(VMS_MAXRSS);
11970 if (rsa == NULL) _ckvmssts(SS$_INSFMEM);
11971 rms_set_rsa(nam, rsa, (VMS_MAXRSS-1));
11972 rms_set_esa(fab_in, nam, esa, (VMS_MAXRSS-1));
11973 rms_nam_esl(nam) = 0;
11974 rms_nam_rsl(nam) = 0;
11975 rms_nam_esll(nam) = 0;
11976 rms_nam_rsll(nam) = 0;
11977 #ifdef NAM$M_NO_SHORT_UPCASE
11978 if (decc_efs_case_preserve)
11979 rms_set_nam_nop(nam, NAM$M_NO_SHORT_UPCASE);
11982 xabdat = cc$rms_xabdat; /* To get creation date */
11983 xabdat.xab$l_nxt = (void *) &xabfhc;
11985 xabfhc = cc$rms_xabfhc; /* To get record length */
11986 xabfhc.xab$l_nxt = (void *) &xabsum;
11988 xabsum = cc$rms_xabsum; /* To get key and area information */
11990 if (!((sts = sys$open(&fab_in)) & 1)) {
11991 PerlMem_free(vmsin);
11992 PerlMem_free(vmsout);
11995 set_vaxc_errno(sts);
11997 case RMS$_FNF: case RMS$_DNF:
11998 set_errno(ENOENT); break;
12000 set_errno(ENOTDIR); break;
12002 set_errno(ENODEV); break;
12004 set_errno(EINVAL); break;
12006 set_errno(EACCES); break;
12008 set_errno(EVMSERR);
12015 fab_out.fab$w_ifi = 0;
12016 fab_out.fab$b_fac = FAB$M_BIO | FAB$M_PUT;
12017 fab_out.fab$b_shr = FAB$M_SHRGET | FAB$M_UPI;
12018 fab_out.fab$l_fop = FAB$M_SQO;
12019 rms_bind_fab_nam(fab_out, nam_out);
12020 rms_set_fna(fab_out, nam_out, vmsout, strlen(vmsout));
12021 dna_len = rms_nam_namel(nam) ? rms_nam_name_type_l_size(nam) : 0;
12022 rms_set_dna(fab_out, nam_out, rms_nam_namel(nam), dna_len);
12023 esa_out = PerlMem_malloc(VMS_MAXRSS);
12024 if (esa_out == NULL) _ckvmssts(SS$_INSFMEM);
12025 rms_set_rsa(nam_out, NULL, 0);
12026 rms_set_esa(fab_out, nam_out, esa_out, (VMS_MAXRSS - 1));
12028 if (preserve_dates == 0) { /* Act like DCL COPY */
12029 rms_set_nam_nop(nam_out, NAM$M_SYNCHK);
12030 fab_out.fab$l_xab = NULL; /* Don't disturb data from input file */
12031 if (!((sts = sys$parse(&fab_out)) & STS$K_SUCCESS)) {
12032 PerlMem_free(vmsin);
12033 PerlMem_free(vmsout);
12036 PerlMem_free(esa_out);
12037 set_errno(sts == RMS$_SYN ? EINVAL : EVMSERR);
12038 set_vaxc_errno(sts);
12041 fab_out.fab$l_xab = (void *) &xabdat;
12042 if (rms_is_nam_fnb(nam, NAM$M_EXP_NAME | NAM$M_EXP_TYPE))
12043 preserve_dates = 1;
12045 if (preserve_dates < 0) /* Clear all bits; we'll use it as a */
12046 preserve_dates =0; /* bitmask from this point forward */
12048 if (!(preserve_dates & 1)) fab_out.fab$l_xab = (void *) &xabfhc;
12049 if (!((sts = sys$create(&fab_out)) & STS$K_SUCCESS)) {
12050 PerlMem_free(vmsin);
12051 PerlMem_free(vmsout);
12054 PerlMem_free(esa_out);
12055 set_vaxc_errno(sts);
12058 set_errno(ENOENT); break;
12060 set_errno(ENOTDIR); break;
12062 set_errno(ENODEV); break;
12064 set_errno(EINVAL); break;
12066 set_errno(EACCES); break;
12068 set_errno(EVMSERR);
12072 fab_out.fab$l_fop |= FAB$M_DLT; /* in case we have to bail out */
12073 if (preserve_dates & 2) {
12074 /* sys$close() will process xabrdt, not xabdat */
12075 xabrdt = cc$rms_xabrdt;
12077 xabrdt.xab$q_rdt = xabdat.xab$q_rdt;
12079 /* gcc doesn't like the assignment, since its prototype for xab$q_rdt
12080 * is unsigned long[2], while DECC & VAXC use a struct */
12081 memcpy(xabrdt.xab$q_rdt,xabdat.xab$q_rdt,sizeof xabrdt.xab$q_rdt);
12083 fab_out.fab$l_xab = (void *) &xabrdt;
12086 ubf = PerlMem_malloc(32256);
12087 if (ubf == NULL) _ckvmssts(SS$_INSFMEM);
12088 rab_in = cc$rms_rab;
12089 rab_in.rab$l_fab = &fab_in;
12090 rab_in.rab$l_rop = RAB$M_BIO;
12091 rab_in.rab$l_ubf = ubf;
12092 rab_in.rab$w_usz = 32256;
12093 if (!((sts = sys$connect(&rab_in)) & 1)) {
12094 sys$close(&fab_in); sys$close(&fab_out);
12095 PerlMem_free(vmsin);
12096 PerlMem_free(vmsout);
12100 PerlMem_free(esa_out);
12101 set_errno(EVMSERR); set_vaxc_errno(sts);
12105 rab_out = cc$rms_rab;
12106 rab_out.rab$l_fab = &fab_out;
12107 rab_out.rab$l_rbf = ubf;
12108 if (!((sts = sys$connect(&rab_out)) & 1)) {
12109 sys$close(&fab_in); sys$close(&fab_out);
12110 PerlMem_free(vmsin);
12111 PerlMem_free(vmsout);
12115 PerlMem_free(esa_out);
12116 set_errno(EVMSERR); set_vaxc_errno(sts);
12120 while ((sts = sys$read(&rab_in))) { /* always true */
12121 if (sts == RMS$_EOF) break;
12122 rab_out.rab$w_rsz = rab_in.rab$w_rsz;
12123 if (!(sts & 1) || !((sts = sys$write(&rab_out)) & 1)) {
12124 sys$close(&fab_in); sys$close(&fab_out);
12125 PerlMem_free(vmsin);
12126 PerlMem_free(vmsout);
12130 PerlMem_free(esa_out);
12131 set_errno(EVMSERR); set_vaxc_errno(sts);
12137 fab_out.fab$l_fop &= ~FAB$M_DLT; /* We got this far; keep the output */
12138 sys$close(&fab_in); sys$close(&fab_out);
12139 sts = (fab_in.fab$l_sts & 1) ? fab_out.fab$l_sts : fab_in.fab$l_sts;
12141 PerlMem_free(vmsin);
12142 PerlMem_free(vmsout);
12146 PerlMem_free(esa_out);
12147 set_errno(EVMSERR); set_vaxc_errno(sts);
12151 PerlMem_free(vmsin);
12152 PerlMem_free(vmsout);
12156 PerlMem_free(esa_out);
12159 } /* end of rmscopy() */
12163 /*** The following glue provides 'hooks' to make some of the routines
12164 * from this file available from Perl. These routines are sufficiently
12165 * basic, and are required sufficiently early in the build process,
12166 * that's it's nice to have them available to miniperl as well as the
12167 * full Perl, so they're set up here instead of in an extension. The
12168 * Perl code which handles importation of these names into a given
12169 * package lives in [.VMS]Filespec.pm in @INC.
12173 rmsexpand_fromperl(pTHX_ CV *cv)
12176 char *fspec, *defspec = NULL, *rslt;
12178 int fs_utf8, dfs_utf8;
12182 if (!items || items > 2)
12183 Perl_croak(aTHX_ "Usage: VMS::Filespec::rmsexpand(spec[,defspec])");
12184 fspec = SvPV(ST(0),n_a);
12185 fs_utf8 = SvUTF8(ST(0));
12186 if (!fspec || !*fspec) XSRETURN_UNDEF;
12188 defspec = SvPV(ST(1),n_a);
12189 dfs_utf8 = SvUTF8(ST(1));
12191 rslt = do_rmsexpand(fspec,NULL,1,defspec,0,&fs_utf8,&dfs_utf8);
12192 ST(0) = sv_newmortal();
12193 if (rslt != NULL) {
12194 sv_usepvn(ST(0),rslt,strlen(rslt));
12203 vmsify_fromperl(pTHX_ CV *cv)
12210 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsify(spec)");
12211 utf8_fl = SvUTF8(ST(0));
12212 vmsified = do_tovmsspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12213 ST(0) = sv_newmortal();
12214 if (vmsified != NULL) {
12215 sv_usepvn(ST(0),vmsified,strlen(vmsified));
12224 unixify_fromperl(pTHX_ CV *cv)
12231 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixify(spec)");
12232 utf8_fl = SvUTF8(ST(0));
12233 unixified = do_tounixspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12234 ST(0) = sv_newmortal();
12235 if (unixified != NULL) {
12236 sv_usepvn(ST(0),unixified,strlen(unixified));
12245 fileify_fromperl(pTHX_ CV *cv)
12252 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::fileify(spec)");
12253 utf8_fl = SvUTF8(ST(0));
12254 fileified = do_fileify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12255 ST(0) = sv_newmortal();
12256 if (fileified != NULL) {
12257 sv_usepvn(ST(0),fileified,strlen(fileified));
12266 pathify_fromperl(pTHX_ CV *cv)
12273 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::pathify(spec)");
12274 utf8_fl = SvUTF8(ST(0));
12275 pathified = do_pathify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12276 ST(0) = sv_newmortal();
12277 if (pathified != NULL) {
12278 sv_usepvn(ST(0),pathified,strlen(pathified));
12287 vmspath_fromperl(pTHX_ CV *cv)
12294 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmspath(spec)");
12295 utf8_fl = SvUTF8(ST(0));
12296 vmspath = do_tovmspath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12297 ST(0) = sv_newmortal();
12298 if (vmspath != NULL) {
12299 sv_usepvn(ST(0),vmspath,strlen(vmspath));
12308 unixpath_fromperl(pTHX_ CV *cv)
12315 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixpath(spec)");
12316 utf8_fl = SvUTF8(ST(0));
12317 unixpath = do_tounixpath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12318 ST(0) = sv_newmortal();
12319 if (unixpath != NULL) {
12320 sv_usepvn(ST(0),unixpath,strlen(unixpath));
12329 candelete_fromperl(pTHX_ CV *cv)
12337 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::candelete(spec)");
12339 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
12340 Newx(fspec, VMS_MAXRSS, char);
12341 if (fspec == NULL) _ckvmssts(SS$_INSFMEM);
12342 if (SvTYPE(mysv) == SVt_PVGV) {
12343 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),fspec)) {
12344 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12352 if (mysv != ST(0) || !(fsp = SvPV(mysv,n_a)) || !*fsp) {
12353 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12360 ST(0) = boolSV(cando_by_name(S_IDUSR,0,fsp));
12366 rmscopy_fromperl(pTHX_ CV *cv)
12369 char *inspec, *outspec, *inp, *outp;
12371 struct dsc$descriptor indsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
12372 outdsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
12373 unsigned long int sts;
12378 if (items < 2 || items > 3)
12379 Perl_croak(aTHX_ "Usage: File::Copy::rmscopy(from,to[,date_flag])");
12381 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
12382 Newx(inspec, VMS_MAXRSS, char);
12383 if (SvTYPE(mysv) == SVt_PVGV) {
12384 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),inspec)) {
12385 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12393 if (mysv != ST(0) || !(inp = SvPV(mysv,n_a)) || !*inp) {
12394 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12400 mysv = SvROK(ST(1)) ? SvRV(ST(1)) : ST(1);
12401 Newx(outspec, VMS_MAXRSS, char);
12402 if (SvTYPE(mysv) == SVt_PVGV) {
12403 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),outspec)) {
12404 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12413 if (mysv != ST(1) || !(outp = SvPV(mysv,n_a)) || !*outp) {
12414 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12421 date_flag = (items == 3) ? SvIV(ST(2)) : 0;
12423 ST(0) = boolSV(rmscopy(inp,outp,date_flag));
12429 /* The mod2fname is limited to shorter filenames by design, so it should
12430 * not be modified to support longer EFS pathnames
12433 mod2fname(pTHX_ CV *cv)
12436 char ultimate_name[NAM$C_MAXRSS+1], work_name[NAM$C_MAXRSS*8 + 1],
12437 workbuff[NAM$C_MAXRSS*1 + 1];
12438 int total_namelen = 3, counter, num_entries;
12439 /* ODS-5 ups this, but we want to be consistent, so... */
12440 int max_name_len = 39;
12441 AV *in_array = (AV *)SvRV(ST(0));
12443 num_entries = av_len(in_array);
12445 /* All the names start with PL_. */
12446 strcpy(ultimate_name, "PL_");
12448 /* Clean up our working buffer */
12449 Zero(work_name, sizeof(work_name), char);
12451 /* Run through the entries and build up a working name */
12452 for(counter = 0; counter <= num_entries; counter++) {
12453 /* If it's not the first name then tack on a __ */
12455 strcat(work_name, "__");
12457 strcat(work_name, SvPV(*av_fetch(in_array, counter, FALSE),
12461 /* Check to see if we actually have to bother...*/
12462 if (strlen(work_name) + 3 <= max_name_len) {
12463 strcat(ultimate_name, work_name);
12465 /* It's too darned big, so we need to go strip. We use the same */
12466 /* algorithm as xsubpp does. First, strip out doubled __ */
12467 char *source, *dest, last;
12470 for (source = work_name; *source; source++) {
12471 if (last == *source && last == '_') {
12477 /* Go put it back */
12478 strcpy(work_name, workbuff);
12479 /* Is it still too big? */
12480 if (strlen(work_name) + 3 > max_name_len) {
12481 /* Strip duplicate letters */
12484 for (source = work_name; *source; source++) {
12485 if (last == toupper(*source)) {
12489 last = toupper(*source);
12491 strcpy(work_name, workbuff);
12494 /* Is it *still* too big? */
12495 if (strlen(work_name) + 3 > max_name_len) {
12496 /* Too bad, we truncate */
12497 work_name[max_name_len - 2] = 0;
12499 strcat(ultimate_name, work_name);
12502 /* Okay, return it */
12503 ST(0) = sv_2mortal(newSVpv(ultimate_name, 0));
12508 hushexit_fromperl(pTHX_ CV *cv)
12513 VMSISH_HUSHED = SvTRUE(ST(0));
12515 ST(0) = boolSV(VMSISH_HUSHED);
12521 Perl_vms_start_glob
12522 (pTHX_ SV *tmpglob,
12526 struct vs_str_st *rslt;
12530 $DESCRIPTOR(dfltdsc,"SYS$DISK:[]*.*;");
12533 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
12534 struct dsc$descriptor_vs rsdsc;
12535 unsigned long int cxt = 0, sts = 0, ok = 1, hasdir = 0;
12536 unsigned long hasver = 0, isunix = 0;
12537 unsigned long int lff_flags = 0;
12540 #ifdef VMS_LONGNAME_SUPPORT
12541 lff_flags = LIB$M_FIL_LONG_NAMES;
12543 /* The Newx macro will not allow me to assign a smaller array
12544 * to the rslt pointer, so we will assign it to the begin char pointer
12545 * and then copy the value into the rslt pointer.
12547 Newx(begin, VMS_MAXRSS + sizeof(unsigned short int), char);
12548 rslt = (struct vs_str_st *)begin;
12550 rstr = &rslt->str[0];
12551 rsdsc.dsc$a_pointer = (char *) rslt; /* cast required */
12552 rsdsc.dsc$w_maxstrlen = VMS_MAXRSS + sizeof(unsigned short int);
12553 rsdsc.dsc$b_dtype = DSC$K_DTYPE_VT;
12554 rsdsc.dsc$b_class = DSC$K_CLASS_VS;
12556 Newx(vmsspec, VMS_MAXRSS, char);
12558 /* We could find out if there's an explicit dev/dir or version
12559 by peeking into lib$find_file's internal context at
12560 ((struct NAM *)((struct FAB *)cxt)->fab$l_nam)->nam$l_fnb
12561 but that's unsupported, so I don't want to do it now and
12562 have it bite someone in the future. */
12563 /* Fix-me: vms_split_path() is the only way to do this, the
12564 existing method will fail with many legal EFS or UNIX specifications
12567 cp = SvPV(tmpglob,i);
12570 if (cp[i] == ';') hasver = 1;
12571 if (cp[i] == '.') {
12572 if (sts) hasver = 1;
12575 if (cp[i] == '/') {
12576 hasdir = isunix = 1;
12579 if (cp[i] == ']' || cp[i] == '>' || cp[i] == ':') {
12584 if ((tmpfp = PerlIO_tmpfile()) != NULL) {
12588 stat_sts = PerlLIO_stat(SvPVX_const(tmpglob),&st);
12589 if (!stat_sts && S_ISDIR(st.st_mode)) {
12590 wilddsc.dsc$a_pointer = tovmspath_utf8(SvPVX(tmpglob),vmsspec,NULL);
12591 ok = (wilddsc.dsc$a_pointer != NULL);
12592 /* maybe passed 'foo' rather than '[.foo]', thus not detected above */
12596 wilddsc.dsc$a_pointer = tovmsspec_utf8(SvPVX(tmpglob),vmsspec,NULL);
12597 ok = (wilddsc.dsc$a_pointer != NULL);
12600 wilddsc.dsc$w_length = strlen(wilddsc.dsc$a_pointer);
12602 /* If not extended character set, replace ? with % */
12603 /* With extended character set, ? is a wildcard single character */
12604 if (!decc_efs_case_preserve) {
12605 for (cp=wilddsc.dsc$a_pointer; ok && cp && *cp; cp++)
12606 if (*cp == '?') *cp = '%';
12609 while (ok && $VMS_STATUS_SUCCESS(sts)) {
12610 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
12611 int v_sts, v_len, r_len, d_len, n_len, e_len, vs_len;
12613 sts = lib$find_file(&wilddsc,&rsdsc,&cxt,
12614 &dfltdsc,NULL,&rms_sts,&lff_flags);
12615 if (!$VMS_STATUS_SUCCESS(sts))
12620 /* with varying string, 1st word of buffer contains result length */
12621 rstr[rslt->length] = '\0';
12623 /* Find where all the components are */
12624 v_sts = vms_split_path
12639 /* If no version on input, truncate the version on output */
12640 if (!hasver && (vs_len > 0)) {
12644 /* No version & a null extension on UNIX handling */
12645 if (isunix && (e_len == 1) && decc_readdir_dropdotnotype) {
12651 if (!decc_efs_case_preserve) {
12652 for (cp = rstr; *cp; cp++) *cp = _tolower(*cp);
12656 if (isunix) trim_unixpath(rstr,SvPVX(tmpglob),1);
12660 /* Start with the name */
12663 strcat(begin,"\n");
12664 ok = (PerlIO_puts(tmpfp,begin) != EOF);
12666 if (cxt) (void)lib$find_file_end(&cxt);
12669 /* Be POSIXish: return the input pattern when no matches */
12670 begin = SvPVX(tmpglob);
12671 strcat(begin,"\n");
12672 ok = (PerlIO_puts(tmpfp,begin) != EOF);
12675 if (ok && sts != RMS$_NMF &&
12676 sts != RMS$_DNF && sts != RMS_FNF) ok = 0;
12679 SETERRNO((sts == RMS$_SYN ? EINVAL : EVMSERR),sts);
12681 PerlIO_close(tmpfp);
12685 PerlIO_rewind(tmpfp);
12686 IoTYPE(io) = IoTYPE_RDONLY;
12687 IoIFP(io) = fp = tmpfp;
12688 IoFLAGS(io) &= ~IOf_UNTAINT; /* maybe redundant */
12699 mp_do_vms_realpath(pTHX_ const char *filespec, char * rslt_spec,
12700 const int *utf8_fl);
12703 vms_realpath_fromperl(pTHX_ CV *cv)
12706 char *fspec, *rslt_spec, *rslt;
12709 if (!items || items != 1)
12710 Perl_croak(aTHX_ "Usage: VMS::Filespec::vms_realpath(spec)");
12712 fspec = SvPV(ST(0),n_a);
12713 if (!fspec || !*fspec) XSRETURN_UNDEF;
12715 Newx(rslt_spec, VMS_MAXRSS + 1, char);
12716 rslt = do_vms_realpath(fspec, rslt_spec, NULL);
12717 ST(0) = sv_newmortal();
12719 sv_usepvn(ST(0),rslt,strlen(rslt));
12721 Safefree(rslt_spec);
12726 #if __CRTL_VER >= 70301000 && !defined(__VAX)
12727 int do_vms_case_tolerant(void);
12730 vms_case_tolerant_fromperl(pTHX_ CV *cv)
12733 ST(0) = boolSV(do_vms_case_tolerant());
12739 Perl_sys_intern_dup(pTHX_ struct interp_intern *src,
12740 struct interp_intern *dst)
12742 memcpy(dst,src,sizeof(struct interp_intern));
12746 Perl_sys_intern_clear(pTHX)
12751 Perl_sys_intern_init(pTHX)
12753 unsigned int ix = RAND_MAX;
12758 /* fix me later to track running under GNV */
12759 /* this allows some limited testing */
12760 MY_POSIX_EXIT = decc_filename_unix_report;
12763 MY_INV_RAND_MAX = 1./x;
12767 init_os_extras(void)
12770 char* file = __FILE__;
12771 if (decc_disable_to_vms_logname_translation) {
12772 no_translate_barewords = TRUE;
12774 no_translate_barewords = FALSE;
12777 newXSproto("VMS::Filespec::rmsexpand",rmsexpand_fromperl,file,"$;$");
12778 newXSproto("VMS::Filespec::vmsify",vmsify_fromperl,file,"$");
12779 newXSproto("VMS::Filespec::unixify",unixify_fromperl,file,"$");
12780 newXSproto("VMS::Filespec::pathify",pathify_fromperl,file,"$");
12781 newXSproto("VMS::Filespec::fileify",fileify_fromperl,file,"$");
12782 newXSproto("VMS::Filespec::vmspath",vmspath_fromperl,file,"$");
12783 newXSproto("VMS::Filespec::unixpath",unixpath_fromperl,file,"$");
12784 newXSproto("VMS::Filespec::candelete",candelete_fromperl,file,"$");
12785 newXSproto("DynaLoader::mod2fname", mod2fname, file, "$");
12786 newXS("File::Copy::rmscopy",rmscopy_fromperl,file);
12787 newXSproto("vmsish::hushed",hushexit_fromperl,file,";$");
12789 newXSproto("VMS::Filespec::vms_realpath",vms_realpath_fromperl,file,"$;$");
12791 #if __CRTL_VER >= 70301000 && !defined(__VAX)
12792 newXSproto("VMS::Filespec::case_tolerant",vms_case_tolerant_fromperl,file,"$;$");
12795 store_pipelocs(aTHX); /* will redo any earlier attempts */
12802 #if __CRTL_VER == 80200000
12803 /* This missed getting in to the DECC SDK for 8.2 */
12804 char *realpath(const char *file_name, char * resolved_name, ...);
12807 /*{{{char *do_vms_realpath(const char *file_name, char *resolved_name)*/
12808 /* wrapper for the realpath() function added with 8.2 RMS SYMLINK SDK.
12809 * The perl fallback routine to provide realpath() is not as efficient
12813 mp_do_vms_realpath(pTHX_ const char *filespec, char *outbuf,
12814 const int *utf8_fl)
12816 return realpath(filespec, outbuf);
12820 /* External entry points */
12821 char *Perl_vms_realpath(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
12822 { return do_vms_realpath(filespec, outbuf, utf8_fl); }
12824 char *Perl_vms_realpath(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
12829 #if __CRTL_VER >= 70301000 && !defined(__VAX)
12830 /* case_tolerant */
12832 /*{{{int do_vms_case_tolerant(void)*/
12833 /* OpenVMS provides a case sensitive implementation of ODS-5 and this is
12834 * controlled by a process setting.
12836 int do_vms_case_tolerant(void)
12838 return vms_process_case_tolerant;
12841 /* External entry points */
12842 int Perl_vms_case_tolerant(void)
12843 { return do_vms_case_tolerant(); }
12845 int Perl_vms_case_tolerant(void)
12846 { return vms_process_case_tolerant; }
12850 /* Start of DECC RTL Feature handling */
12852 static int sys_trnlnm
12853 (const char * logname,
12857 const $DESCRIPTOR(table_dsc, "LNM$FILE_DEV");
12858 const unsigned long attr = LNM$M_CASE_BLIND;
12859 struct dsc$descriptor_s name_dsc;
12861 unsigned short result;
12862 struct itmlst_3 itlst[2] = {{value_len, LNM$_STRING, value, &result},
12865 name_dsc.dsc$w_length = strlen(logname);
12866 name_dsc.dsc$a_pointer = (char *)logname;
12867 name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
12868 name_dsc.dsc$b_class = DSC$K_CLASS_S;
12870 status = sys$trnlnm(&attr, &table_dsc, &name_dsc, 0, itlst);
12872 if ($VMS_STATUS_SUCCESS(status)) {
12874 /* Null terminate and return the string */
12875 /*--------------------------------------*/
12882 static int sys_crelnm
12883 (const char * logname,
12884 const char * value)
12887 const char * proc_table = "LNM$PROCESS_TABLE";
12888 struct dsc$descriptor_s proc_table_dsc;
12889 struct dsc$descriptor_s logname_dsc;
12890 struct itmlst_3 item_list[2];
12892 proc_table_dsc.dsc$a_pointer = (char *) proc_table;
12893 proc_table_dsc.dsc$w_length = strlen(proc_table);
12894 proc_table_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
12895 proc_table_dsc.dsc$b_class = DSC$K_CLASS_S;
12897 logname_dsc.dsc$a_pointer = (char *) logname;
12898 logname_dsc.dsc$w_length = strlen(logname);
12899 logname_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
12900 logname_dsc.dsc$b_class = DSC$K_CLASS_S;
12902 item_list[0].buflen = strlen(value);
12903 item_list[0].itmcode = LNM$_STRING;
12904 item_list[0].bufadr = (char *)value;
12905 item_list[0].retlen = NULL;
12907 item_list[1].buflen = 0;
12908 item_list[1].itmcode = 0;
12910 ret_val = sys$crelnm
12912 (const struct dsc$descriptor_s *)&proc_table_dsc,
12913 (const struct dsc$descriptor_s *)&logname_dsc,
12915 (const struct item_list_3 *) item_list);
12920 /* C RTL Feature settings */
12922 static int set_features
12923 (int (* init_coroutine)(int *, int *, void *), /* Needs casts if used */
12924 int (* cli_routine)(void), /* Not documented */
12925 void *image_info) /* Not documented */
12932 #if defined(JPI$_CASE_LOOKUP_PERM) && !defined(__VAX)
12933 const unsigned long int jpicode1 = JPI$_CASE_LOOKUP_PERM;
12934 const unsigned long int jpicode2 = JPI$_CASE_LOOKUP_IMAGE;
12935 unsigned long case_perm;
12936 unsigned long case_image;
12939 /* Allow an exception to bring Perl into the VMS debugger */
12940 vms_debug_on_exception = 0;
12941 status = sys_trnlnm("PERL_VMS_EXCEPTION_DEBUG", val_str, sizeof(val_str));
12942 if ($VMS_STATUS_SUCCESS(status)) {
12943 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
12944 vms_debug_on_exception = 1;
12946 vms_debug_on_exception = 0;
12949 /* Create VTF-7 filenames from Unicode instead of UTF-8 */
12950 vms_vtf7_filenames = 0;
12951 status = sys_trnlnm("PERL_VMS_VTF7_FILENAMES", val_str, sizeof(val_str));
12952 if ($VMS_STATUS_SUCCESS(status)) {
12953 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
12954 vms_vtf7_filenames = 1;
12956 vms_vtf7_filenames = 0;
12960 /* unlink all versions on unlink() or rename() */
12961 vms_vtf7_filenames = 0;
12962 status = sys_trnlnm
12963 ("PERL_VMS_UNLINK_ALL_VERSIONS", val_str, sizeof(val_str));
12964 if ($VMS_STATUS_SUCCESS(status)) {
12965 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
12966 vms_unlink_all_versions = 1;
12968 vms_unlink_all_versions = 0;
12971 /* Dectect running under GNV Bash or other UNIX like shell */
12972 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12973 gnv_unix_shell = 0;
12974 status = sys_trnlnm("GNV$UNIX_SHELL", val_str, sizeof(val_str));
12975 if ($VMS_STATUS_SUCCESS(status)) {
12976 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
12977 gnv_unix_shell = 1;
12978 set_feature_default("DECC$EFS_CASE_PRESERVE", 1);
12979 set_feature_default("DECC$EFS_CHARSET", 1);
12980 set_feature_default("DECC$FILENAME_UNIX_NO_VERSION", 1);
12981 set_feature_default("DECC$FILENAME_UNIX_REPORT", 1);
12982 set_feature_default("DECC$READDIR_DROPDOTNOTYPE", 1);
12983 set_feature_default("DECC$DISABLE_POSIX_ROOT", 0);
12984 vms_unlink_all_versions = 1;
12987 gnv_unix_shell = 0;
12991 /* hacks to see if known bugs are still present for testing */
12993 /* Readdir is returning filenames in VMS syntax always */
12994 decc_bug_readdir_efs1 = 1;
12995 status = sys_trnlnm("DECC_BUG_READDIR_EFS1", val_str, sizeof(val_str));
12996 if ($VMS_STATUS_SUCCESS(status)) {
12997 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
12998 decc_bug_readdir_efs1 = 1;
13000 decc_bug_readdir_efs1 = 0;
13003 /* PCP mode requires creating /dev/null special device file */
13004 decc_bug_devnull = 0;
13005 status = sys_trnlnm("DECC_BUG_DEVNULL", val_str, sizeof(val_str));
13006 if ($VMS_STATUS_SUCCESS(status)) {
13007 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13008 decc_bug_devnull = 1;
13010 decc_bug_devnull = 0;
13013 /* fgetname returning a VMS name in UNIX mode */
13014 decc_bug_fgetname = 1;
13015 status = sys_trnlnm("DECC_BUG_FGETNAME", val_str, sizeof(val_str));
13016 if ($VMS_STATUS_SUCCESS(status)) {
13017 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13018 decc_bug_fgetname = 1;
13020 decc_bug_fgetname = 0;
13023 /* UNIX directory names with no paths are broken in a lot of places */
13024 decc_dir_barename = 1;
13025 status = sys_trnlnm("DECC_DIR_BARENAME", val_str, sizeof(val_str));
13026 if ($VMS_STATUS_SUCCESS(status)) {
13027 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13028 decc_dir_barename = 1;
13030 decc_dir_barename = 0;
13033 #if __CRTL_VER >= 70300000 && !defined(__VAX)
13034 s = decc$feature_get_index("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION");
13036 decc_disable_to_vms_logname_translation = decc$feature_get_value(s, 1);
13037 if (decc_disable_to_vms_logname_translation < 0)
13038 decc_disable_to_vms_logname_translation = 0;
13041 s = decc$feature_get_index("DECC$EFS_CASE_PRESERVE");
13043 decc_efs_case_preserve = decc$feature_get_value(s, 1);
13044 if (decc_efs_case_preserve < 0)
13045 decc_efs_case_preserve = 0;
13048 s = decc$feature_get_index("DECC$EFS_CHARSET");
13050 decc_efs_charset = decc$feature_get_value(s, 1);
13051 if (decc_efs_charset < 0)
13052 decc_efs_charset = 0;
13055 s = decc$feature_get_index("DECC$FILENAME_UNIX_REPORT");
13057 decc_filename_unix_report = decc$feature_get_value(s, 1);
13058 if (decc_filename_unix_report > 0)
13059 decc_filename_unix_report = 1;
13061 decc_filename_unix_report = 0;
13064 s = decc$feature_get_index("DECC$FILENAME_UNIX_ONLY");
13066 decc_filename_unix_only = decc$feature_get_value(s, 1);
13067 if (decc_filename_unix_only > 0) {
13068 decc_filename_unix_only = 1;
13071 decc_filename_unix_only = 0;
13075 s = decc$feature_get_index("DECC$FILENAME_UNIX_NO_VERSION");
13077 decc_filename_unix_no_version = decc$feature_get_value(s, 1);
13078 if (decc_filename_unix_no_version < 0)
13079 decc_filename_unix_no_version = 0;
13082 s = decc$feature_get_index("DECC$READDIR_DROPDOTNOTYPE");
13084 decc_readdir_dropdotnotype = decc$feature_get_value(s, 1);
13085 if (decc_readdir_dropdotnotype < 0)
13086 decc_readdir_dropdotnotype = 0;
13089 status = sys_trnlnm("SYS$POSIX_ROOT", val_str, sizeof(val_str));
13090 if ($VMS_STATUS_SUCCESS(status)) {
13091 s = decc$feature_get_index("DECC$DISABLE_POSIX_ROOT");
13093 dflt = decc$feature_get_value(s, 4);
13095 decc_disable_posix_root = decc$feature_get_value(s, 1);
13096 if (decc_disable_posix_root <= 0) {
13097 decc$feature_set_value(s, 1, 1);
13098 decc_disable_posix_root = 1;
13102 /* Traditionally Perl assumes this is off */
13103 decc_disable_posix_root = 1;
13104 decc$feature_set_value(s, 1, 1);
13109 #if __CRTL_VER >= 80200000
13110 s = decc$feature_get_index("DECC$POSIX_COMPLIANT_PATHNAMES");
13112 decc_posix_compliant_pathnames = decc$feature_get_value(s, 1);
13113 if (decc_posix_compliant_pathnames < 0)
13114 decc_posix_compliant_pathnames = 0;
13115 if (decc_posix_compliant_pathnames > 4)
13116 decc_posix_compliant_pathnames = 0;
13121 status = sys_trnlnm
13122 ("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION", val_str, sizeof(val_str));
13123 if ($VMS_STATUS_SUCCESS(status)) {
13124 val_str[0] = _toupper(val_str[0]);
13125 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
13126 decc_disable_to_vms_logname_translation = 1;
13131 status = sys_trnlnm("DECC$EFS_CASE_PRESERVE", val_str, sizeof(val_str));
13132 if ($VMS_STATUS_SUCCESS(status)) {
13133 val_str[0] = _toupper(val_str[0]);
13134 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
13135 decc_efs_case_preserve = 1;
13140 status = sys_trnlnm("DECC$FILENAME_UNIX_REPORT", val_str, sizeof(val_str));
13141 if ($VMS_STATUS_SUCCESS(status)) {
13142 val_str[0] = _toupper(val_str[0]);
13143 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
13144 decc_filename_unix_report = 1;
13147 status = sys_trnlnm("DECC$FILENAME_UNIX_ONLY", val_str, sizeof(val_str));
13148 if ($VMS_STATUS_SUCCESS(status)) {
13149 val_str[0] = _toupper(val_str[0]);
13150 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
13151 decc_filename_unix_only = 1;
13152 decc_filename_unix_report = 1;
13155 status = sys_trnlnm("DECC$FILENAME_UNIX_NO_VERSION", val_str, sizeof(val_str));
13156 if ($VMS_STATUS_SUCCESS(status)) {
13157 val_str[0] = _toupper(val_str[0]);
13158 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
13159 decc_filename_unix_no_version = 1;
13162 status = sys_trnlnm("DECC$READDIR_DROPDOTNOTYPE", val_str, sizeof(val_str));
13163 if ($VMS_STATUS_SUCCESS(status)) {
13164 val_str[0] = _toupper(val_str[0]);
13165 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
13166 decc_readdir_dropdotnotype = 1;
13171 #if defined(JPI$_CASE_LOOKUP_PERM) && !defined(__VAX)
13173 /* Report true case tolerance */
13174 /*----------------------------*/
13175 status = lib$getjpi(&jpicode1, 0, 0, &case_perm, 0, 0);
13176 if (!$VMS_STATUS_SUCCESS(status))
13177 case_perm = PPROP$K_CASE_BLIND;
13178 status = lib$getjpi(&jpicode2, 0, 0, &case_image, 0, 0);
13179 if (!$VMS_STATUS_SUCCESS(status))
13180 case_image = PPROP$K_CASE_BLIND;
13181 if ((case_perm == PPROP$K_CASE_SENSITIVE) ||
13182 (case_image == PPROP$K_CASE_SENSITIVE))
13183 vms_process_case_tolerant = 0;
13188 /* CRTL can be initialized past this point, but not before. */
13189 /* DECC$CRTL_INIT(); */
13196 #pragma extern_model save
13197 #pragma extern_model strict_refdef "LIB$INITIALIZ" nowrt
13198 const __align (LONGWORD) int spare[8] = {0};
13200 /* .psect LIB$INITIALIZE, NOPIC, USR, CON, REL, GBL, NOSHR, NOEXE, RD, NOWRT, LONG */
13201 #if __DECC_VER >= 60560002
13202 #pragma extern_model strict_refdef "LIB$INITIALIZE" nopic, con, rel, gbl, noshr, noexe, nowrt, long
13204 #pragma extern_model strict_refdef "LIB$INITIALIZE" nopic, con, gbl, noshr, nowrt, long
13206 #endif /* __DECC */
13208 const long vms_cc_features = (const long)set_features;
13211 ** Force a reference to LIB$INITIALIZE to ensure it
13212 ** exists in the image.
13214 int lib$initialize(void);
13216 #pragma extern_model strict_refdef
13218 int lib_init_ref = (int) lib$initialize;
13221 #pragma extern_model restore