3 * VMS-specific routines for perl5
5 * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
6 * 2002, 2003, 2004, 2005, 2006, 2007 by Charles Bailey and others.
8 * You may distribute under the terms of either the GNU General Public
9 * License or the Artistic License, as specified in the README file.
11 * Please see Changes*.* or the Perl Repository Browser for revision history.
15 * Yet small as was their hunted band
16 * still fell and fearless was each hand,
17 * and strong deeds they wrought yet oft,
18 * and loved the woods, whose ways more soft
19 * them seemed than thralls of that black throne
20 * to live and languish in halls of stone.
21 * "The Lay of Leithian", Canto II, lines 135-40
23 * [p.162 of _The Lays of Beleriand_]
32 #include <climsgdef.h>
43 #include <libclidef.h>
45 #include <lib$routines.h>
49 #if __CRTL_VER >= 70301000 && !defined(__VAX)
59 #include <str$routines.h>
66 #if __CRTL_VER >= 70000000 /* FIXME to earliest version */
68 #define NO_EFN EFN$C_ENF
73 #if __CRTL_VER < 70301000 && __CRTL_VER >= 70300000
74 int decc$feature_get_index(const char *name);
75 char* decc$feature_get_name(int index);
76 int decc$feature_get_value(int index, int mode);
77 int decc$feature_set_value(int index, int mode, int value);
82 #pragma member_alignment save
83 #pragma nomember_alignment longword
88 unsigned short * retadr;
90 #pragma member_alignment restore
92 /* More specific prototype than in starlet_c.h makes programming errors
100 const struct dsc$descriptor_s * devnam,
101 const struct item_list_3 * itmlst,
103 void * (astadr)(unsigned long),
108 #ifdef sys$get_security
109 #undef sys$get_security
111 (const struct dsc$descriptor_s * clsnam,
112 const struct dsc$descriptor_s * objnam,
113 const unsigned int *objhan,
115 const struct item_list_3 * itmlst,
116 unsigned int * contxt,
117 const unsigned int * acmode);
120 #ifdef sys$set_security
121 #undef sys$set_security
123 (const struct dsc$descriptor_s * clsnam,
124 const struct dsc$descriptor_s * objnam,
125 const unsigned int *objhan,
127 const struct item_list_3 * itmlst,
128 unsigned int * contxt,
129 const unsigned int * acmode);
132 #ifdef lib$find_image_symbol
133 #undef lib$find_image_symbol
134 int lib$find_image_symbol
135 (const struct dsc$descriptor_s * imgname,
136 const struct dsc$descriptor_s * symname,
138 const struct dsc$descriptor_s * defspec,
142 #ifdef lib$rename_file
143 #undef lib$rename_file
145 (const struct dsc$descriptor_s * old_file_dsc,
146 const struct dsc$descriptor_s * new_file_dsc,
147 const struct dsc$descriptor_s * default_file_dsc,
148 const struct dsc$descriptor_s * related_file_dsc,
149 const unsigned long * flags,
150 void * (success)(const struct dsc$descriptor_s * old_dsc,
151 const struct dsc$descriptor_s * new_dsc,
153 void * (error)(const struct dsc$descriptor_s * old_dsc,
154 const struct dsc$descriptor_s * new_dsc,
157 const int * error_src,
158 const void * usr_arg),
159 int (confirm)(const struct dsc$descriptor_s * old_dsc,
160 const struct dsc$descriptor_s * new_dsc,
161 const void * old_fab,
162 const void * usr_arg),
164 struct dsc$descriptor_s * old_result_name_dsc,
165 struct dsc$descriptor_s * new_result_name_dsc,
166 unsigned long * file_scan_context);
169 #if __CRTL_VER >= 70300000 && !defined(__VAX)
171 static int set_feature_default(const char *name, int value)
176 index = decc$feature_get_index(name);
178 status = decc$feature_set_value(index, 1, value);
179 if (index == -1 || (status == -1)) {
183 status = decc$feature_get_value(index, 1);
184 if (status != value) {
192 /* Older versions of ssdef.h don't have these */
193 #ifndef SS$_INVFILFOROP
194 # define SS$_INVFILFOROP 3930
196 #ifndef SS$_NOSUCHOBJECT
197 # define SS$_NOSUCHOBJECT 2696
200 /* We implement I/O here, so we will be mixing PerlIO and stdio calls. */
201 #define PERLIO_NOT_STDIO 0
203 /* Don't replace system definitions of vfork, getenv, lstat, and stat,
204 * code below needs to get to the underlying CRTL routines. */
205 #define DONT_MASK_RTL_CALLS
209 /* Anticipating future expansion in lexical warnings . . . */
210 #ifndef WARN_INTERNAL
211 # define WARN_INTERNAL WARN_MISC
214 #ifdef VMS_LONGNAME_SUPPORT
215 #include <libfildef.h>
218 #if defined(__VMS_VER) && __VMS_VER >= 70000000 && __DECC_VER >= 50200000
219 # define RTL_USES_UTC 1
222 /* Routine to create a decterm for use with the Perl debugger */
223 /* No headers, this information was found in the Programming Concepts Manual */
225 static int (*decw_term_port)
226 (const struct dsc$descriptor_s * display,
227 const struct dsc$descriptor_s * setup_file,
228 const struct dsc$descriptor_s * customization,
229 struct dsc$descriptor_s * result_device_name,
230 unsigned short * result_device_name_length,
233 void * char_change_buffer) = 0;
235 /* gcc's header files don't #define direct access macros
236 * corresponding to VAXC's variant structs */
238 # define uic$v_format uic$r_uic_form.uic$v_format
239 # define uic$v_group uic$r_uic_form.uic$v_group
240 # define uic$v_member uic$r_uic_form.uic$v_member
241 # define prv$v_bypass prv$r_prvdef_bits0.prv$v_bypass
242 # define prv$v_grpprv prv$r_prvdef_bits0.prv$v_grpprv
243 # define prv$v_readall prv$r_prvdef_bits0.prv$v_readall
244 # define prv$v_sysprv prv$r_prvdef_bits0.prv$v_sysprv
247 #if defined(NEED_AN_H_ERRNO)
252 #pragma message disable pragma
253 #pragma member_alignment save
254 #pragma nomember_alignment longword
256 #pragma message disable misalgndmem
259 unsigned short int buflen;
260 unsigned short int itmcode;
262 unsigned short int *retlen;
265 struct filescan_itmlst_2 {
266 unsigned short length;
267 unsigned short itmcode;
272 unsigned short length;
277 #pragma message restore
278 #pragma member_alignment restore
281 #define do_fileify_dirspec(a,b,c,d) mp_do_fileify_dirspec(aTHX_ a,b,c,d)
282 #define do_pathify_dirspec(a,b,c,d) mp_do_pathify_dirspec(aTHX_ a,b,c,d)
283 #define do_tovmsspec(a,b,c,d) mp_do_tovmsspec(aTHX_ a,b,c,0,d)
284 #define do_tovmspath(a,b,c,d) mp_do_tovmspath(aTHX_ a,b,c,d)
285 #define do_rmsexpand(a,b,c,d,e,f,g) mp_do_rmsexpand(aTHX_ a,b,c,d,e,f,g)
286 #define do_vms_realpath(a,b,c) mp_do_vms_realpath(aTHX_ a,b,c)
287 #define do_vms_realname(a,b,c) mp_do_vms_realname(aTHX_ a,b,c)
288 #define do_tounixspec(a,b,c,d) mp_do_tounixspec(aTHX_ a,b,c,d)
289 #define do_tounixpath(a,b,c,d) mp_do_tounixpath(aTHX_ a,b,c,d)
290 #define do_vms_case_tolerant(a) mp_do_vms_case_tolerant(a)
291 #define expand_wild_cards(a,b,c,d) mp_expand_wild_cards(aTHX_ a,b,c,d)
292 #define getredirection(a,b) mp_getredirection(aTHX_ a,b)
294 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int *);
295 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int *);
296 static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *);
297 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int *);
299 static char * int_rmsexpand_vms(
300 const char * filespec, char * outbuf, unsigned opts);
301 static char * int_rmsexpand_tovms(
302 const char * filespec, char * outbuf, unsigned opts);
303 static char *int_tovmsspec
304 (const char *path, char *buf, int dir_flag, int * utf8_flag);
305 static char * int_tounixspec(const char *spec, char *buf, int * utf8_fl);
307 /* see system service docs for $TRNLNM -- NOT the same as LNM$_MAX_INDEX */
308 #define PERL_LNM_MAX_ALLOWED_INDEX 127
310 /* OpenVMS User's Guide says at least 9 iterative translations will be performed,
311 * depending on the facility. SHOW LOGICAL does 10, so we'll imitate that for
314 #define PERL_LNM_MAX_ITER 10
316 /* New values with 7.3-2*/ /* why does max DCL have 4 byte subtracted from it? */
317 #if __CRTL_VER >= 70302000 && !defined(__VAX)
318 #define MAX_DCL_SYMBOL (8192)
319 #define MAX_DCL_LINE_LENGTH (4096 - 4)
321 #define MAX_DCL_SYMBOL (1024)
322 #define MAX_DCL_LINE_LENGTH (1024 - 4)
325 static char *__mystrtolower(char *str)
327 if (str) for (; *str; ++str) *str= tolower(*str);
331 static struct dsc$descriptor_s fildevdsc =
332 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "LNM$FILE_DEV" };
333 static struct dsc$descriptor_s crtlenvdsc =
334 { 8, DSC$K_DTYPE_T, DSC$K_CLASS_S, "CRTL_ENV" };
335 static struct dsc$descriptor_s *fildev[] = { &fildevdsc, NULL };
336 static struct dsc$descriptor_s *defenv[] = { &fildevdsc, &crtlenvdsc, NULL };
337 static struct dsc$descriptor_s **env_tables = defenv;
338 static bool will_taint = FALSE; /* tainting active, but no PL_curinterp yet */
340 /* True if we shouldn't treat barewords as logicals during directory */
342 static int no_translate_barewords;
345 static int tz_updated = 1;
348 /* DECC Features that may need to affect how Perl interprets
349 * displays filename information
351 static int decc_disable_to_vms_logname_translation = 1;
352 static int decc_disable_posix_root = 1;
353 int decc_efs_case_preserve = 0;
354 static int decc_efs_charset = 0;
355 static int decc_efs_charset_index = -1;
356 static int decc_filename_unix_no_version = 0;
357 static int decc_filename_unix_only = 0;
358 int decc_filename_unix_report = 0;
359 int decc_posix_compliant_pathnames = 0;
360 int decc_readdir_dropdotnotype = 0;
361 static int vms_process_case_tolerant = 1;
362 int vms_vtf7_filenames = 0;
363 int gnv_unix_shell = 0;
364 static int vms_unlink_all_versions = 0;
365 static int vms_posix_exit = 0;
367 /* bug workarounds if needed */
368 int decc_bug_devnull = 1;
369 int decc_dir_barename = 0;
370 int vms_bug_stat_filename = 0;
372 static int vms_debug_on_exception = 0;
373 static int vms_debug_fileify = 0;
375 /* Simple logical name translation */
376 static int simple_trnlnm
377 (const char * logname,
381 const $DESCRIPTOR(table_dsc, "LNM$FILE_DEV");
382 const unsigned long attr = LNM$M_CASE_BLIND;
383 struct dsc$descriptor_s name_dsc;
385 unsigned short result;
386 struct itmlst_3 itlst[2] = {{value_len, LNM$_STRING, value, &result},
389 name_dsc.dsc$w_length = strlen(logname);
390 name_dsc.dsc$a_pointer = (char *)logname;
391 name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
392 name_dsc.dsc$b_class = DSC$K_CLASS_S;
394 status = sys$trnlnm(&attr, &table_dsc, &name_dsc, 0, itlst);
396 if ($VMS_STATUS_SUCCESS(status)) {
398 /* Null terminate and return the string */
399 /*--------------------------------------*/
408 /* Is this a UNIX file specification?
409 * No longer a simple check with EFS file specs
410 * For now, not a full check, but need to
411 * handle POSIX ^UP^ specifications
412 * Fixing to handle ^/ cases would require
413 * changes to many other conversion routines.
416 static int is_unix_filespec(const char *path)
422 if (strncmp(path,"\"^UP^",5) != 0) {
423 pch1 = strchr(path, '/');
428 /* If the user wants UNIX files, "." needs to be treated as in UNIX */
429 if (decc_filename_unix_report || decc_filename_unix_only) {
430 if (strcmp(path,".") == 0)
438 /* This routine converts a UCS-2 character to be VTF-7 encoded.
441 static void ucs2_to_vtf7
443 unsigned long ucs2_char,
446 unsigned char * ucs_ptr;
449 ucs_ptr = (unsigned char *)&ucs2_char;
453 hex = (ucs_ptr[1] >> 4) & 0xf;
455 outspec[2] = hex + '0';
457 outspec[2] = (hex - 9) + 'A';
458 hex = ucs_ptr[1] & 0xF;
460 outspec[3] = hex + '0';
462 outspec[3] = (hex - 9) + 'A';
464 hex = (ucs_ptr[0] >> 4) & 0xf;
466 outspec[4] = hex + '0';
468 outspec[4] = (hex - 9) + 'A';
469 hex = ucs_ptr[1] & 0xF;
471 outspec[5] = hex + '0';
473 outspec[5] = (hex - 9) + 'A';
479 /* This handles the conversion of a UNIX extended character set to a ^
480 * escaped VMS character.
481 * in a UNIX file specification.
483 * The output count variable contains the number of characters added
484 * to the output string.
486 * The return value is the number of characters read from the input string
488 static int copy_expand_unix_filename_escape
489 (char *outspec, const char *inspec, int *output_cnt, const int * utf8_fl)
497 utf8_flag = *utf8_fl;
501 if (*inspec >= 0x80) {
502 if (utf8_fl && vms_vtf7_filenames) {
503 unsigned long ucs_char;
507 if ((*inspec & 0xE0) == 0xC0) {
509 ucs_char = ((inspec[0] & 0x1F) << 6) + (inspec[1] & 0x3f);
510 if (ucs_char >= 0x80) {
511 ucs2_to_vtf7(outspec, ucs_char, output_cnt);
514 } else if ((*inspec & 0xF0) == 0xE0) {
516 ucs_char = ((inspec[0] & 0xF) << 12) +
517 ((inspec[1] & 0x3f) << 6) +
519 if (ucs_char >= 0x800) {
520 ucs2_to_vtf7(outspec, ucs_char, output_cnt);
524 #if 0 /* I do not see longer sequences supported by OpenVMS */
525 /* Maybe some one can fix this later */
526 } else if ((*inspec & 0xF8) == 0xF0) {
529 } else if ((*inspec & 0xFC) == 0xF8) {
532 } else if ((*inspec & 0xFE) == 0xFC) {
539 /* High bit set, but not a Unicode character! */
541 /* Non printing DECMCS or ISO Latin-1 character? */
542 if (*inspec <= 0x9F) {
546 hex = (*inspec >> 4) & 0xF;
548 outspec[1] = hex + '0';
550 outspec[1] = (hex - 9) + 'A';
554 outspec[2] = hex + '0';
556 outspec[2] = (hex - 9) + 'A';
560 } else if (*inspec == 0xA0) {
566 } else if (*inspec == 0xFF) {
578 /* Is this a macro that needs to be passed through?
579 * Macros start with $( and an alpha character, followed
580 * by a string of alpha numeric characters ending with a )
581 * If this does not match, then encode it as ODS-5.
583 if ((inspec[0] == '$') && (inspec[1] == '(')) {
586 if (isalnum(inspec[2]) || (inspec[2] == '.') || (inspec[2] == '_')) {
588 outspec[0] = inspec[0];
589 outspec[1] = inspec[1];
590 outspec[2] = inspec[2];
592 while(isalnum(inspec[tcnt]) ||
593 (inspec[2] == '.') || (inspec[2] == '_')) {
594 outspec[tcnt] = inspec[tcnt];
597 if (inspec[tcnt] == ')') {
598 outspec[tcnt] = inspec[tcnt];
615 if (decc_efs_charset == 0)
642 /* Don't escape again if following character is
643 * already something we escape.
645 if (strchr(".~!#&\'`()+@{},;[]%^=_\\", *(inspec+1))) {
651 /* But otherwise fall through and escape it. */
653 /* Assume that this is to be escaped */
655 outspec[1] = *inspec;
659 case ' ': /* space */
660 /* Assume that this is to be escaped */
675 /* This handles the expansion of a '^' prefix to the proper character
676 * in a UNIX file specification.
678 * The output count variable contains the number of characters added
679 * to the output string.
681 * The return value is the number of characters read from the input
684 static int copy_expand_vms_filename_escape
685 (char *outspec, const char *inspec, int *output_cnt)
692 if (*inspec == '^') {
695 /* Spaces and non-trailing dots should just be passed through,
696 * but eat the escape character.
703 case '_': /* space */
709 /* Hmm. Better leave the escape escaped. */
715 case 'U': /* Unicode - FIX-ME this is wrong. */
718 scnt = strspn(inspec, "0123456789ABCDEFabcdef");
721 scnt = sscanf(inspec, "%2x%2x", &c1, &c2);
722 outspec[0] == c1 & 0xff;
723 outspec[1] == c2 & 0xff;
730 /* Error - do best we can to continue */
740 scnt = strspn(inspec, "0123456789ABCDEFabcdef");
744 scnt = sscanf(inspec, "%2x", &c1);
745 outspec[0] = c1 & 0xff;
769 (const struct dsc$descriptor_s * srcstr,
770 struct filescan_itmlst_2 * valuelist,
771 unsigned long * fldflags,
772 struct dsc$descriptor_s *auxout,
773 unsigned short * retlen);
776 /* vms_split_path - Verify that the input file specification is a
777 * VMS format file specification, and provide pointers to the components of
778 * it. With EFS format filenames, this is virtually the only way to
779 * parse a VMS path specification into components.
781 * If the sum of the components do not add up to the length of the
782 * string, then the passed file specification is probably a UNIX style
785 static int vms_split_path
800 struct dsc$descriptor path_desc;
804 struct filescan_itmlst_2 item_list[9];
805 const int filespec = 0;
806 const int nodespec = 1;
807 const int devspec = 2;
808 const int rootspec = 3;
809 const int dirspec = 4;
810 const int namespec = 5;
811 const int typespec = 6;
812 const int verspec = 7;
814 /* Assume the worst for an easy exit */
829 path_desc.dsc$a_pointer = (char *)path; /* cast ok */
830 path_desc.dsc$w_length = strlen(path);
831 path_desc.dsc$b_dtype = DSC$K_DTYPE_T;
832 path_desc.dsc$b_class = DSC$K_CLASS_S;
834 /* Get the total length, if it is shorter than the string passed
835 * then this was probably not a VMS formatted file specification
837 item_list[filespec].itmcode = FSCN$_FILESPEC;
838 item_list[filespec].length = 0;
839 item_list[filespec].component = NULL;
841 /* If the node is present, then it gets considered as part of the
842 * volume name to hopefully make things simple.
844 item_list[nodespec].itmcode = FSCN$_NODE;
845 item_list[nodespec].length = 0;
846 item_list[nodespec].component = NULL;
848 item_list[devspec].itmcode = FSCN$_DEVICE;
849 item_list[devspec].length = 0;
850 item_list[devspec].component = NULL;
852 /* root is a special case, adding it to either the directory or
853 * the device components will probalby complicate things for the
854 * callers of this routine, so leave it separate.
856 item_list[rootspec].itmcode = FSCN$_ROOT;
857 item_list[rootspec].length = 0;
858 item_list[rootspec].component = NULL;
860 item_list[dirspec].itmcode = FSCN$_DIRECTORY;
861 item_list[dirspec].length = 0;
862 item_list[dirspec].component = NULL;
864 item_list[namespec].itmcode = FSCN$_NAME;
865 item_list[namespec].length = 0;
866 item_list[namespec].component = NULL;
868 item_list[typespec].itmcode = FSCN$_TYPE;
869 item_list[typespec].length = 0;
870 item_list[typespec].component = NULL;
872 item_list[verspec].itmcode = FSCN$_VERSION;
873 item_list[verspec].length = 0;
874 item_list[verspec].component = NULL;
876 item_list[8].itmcode = 0;
877 item_list[8].length = 0;
878 item_list[8].component = NULL;
880 status = sys$filescan
881 ((const struct dsc$descriptor_s *)&path_desc, item_list,
883 _ckvmssts_noperl(status); /* All failure status values indicate a coding error */
885 /* If we parsed it successfully these two lengths should be the same */
886 if (path_desc.dsc$w_length != item_list[filespec].length)
889 /* If we got here, then it is a VMS file specification */
892 /* set the volume name */
893 if (item_list[nodespec].length > 0) {
894 *volume = item_list[nodespec].component;
895 *vol_len = item_list[nodespec].length + item_list[devspec].length;
898 *volume = item_list[devspec].component;
899 *vol_len = item_list[devspec].length;
902 *root = item_list[rootspec].component;
903 *root_len = item_list[rootspec].length;
905 *dir = item_list[dirspec].component;
906 *dir_len = item_list[dirspec].length;
908 /* Now fun with versions and EFS file specifications
909 * The parser can not tell the difference when a "." is a version
910 * delimiter or a part of the file specification.
912 if ((decc_efs_charset) &&
913 (item_list[verspec].length > 0) &&
914 (item_list[verspec].component[0] == '.')) {
915 *name = item_list[namespec].component;
916 *name_len = item_list[namespec].length + item_list[typespec].length;
917 *ext = item_list[verspec].component;
918 *ext_len = item_list[verspec].length;
923 *name = item_list[namespec].component;
924 *name_len = item_list[namespec].length;
925 *ext = item_list[typespec].component;
926 *ext_len = item_list[typespec].length;
927 *version = item_list[verspec].component;
928 *ver_len = item_list[verspec].length;
933 /* Routine to determine if the file specification ends with .dir */
934 static int is_dir_ext(char * e_spec, int e_len, char * vs_spec, int vs_len) {
936 /* e_len must be 4, and version must be <= 2 characters */
937 if (e_len != 4 || vs_len > 2)
940 /* If a version number is present, it needs to be one */
941 if ((vs_len == 2) && (vs_spec[1] != '1'))
944 /* Look for the DIR on the extension */
945 if (vms_process_case_tolerant) {
946 if ((toupper(e_spec[1]) == 'D') &&
947 (toupper(e_spec[2]) == 'I') &&
948 (toupper(e_spec[3]) == 'R')) {
952 /* Directory extensions are supposed to be in upper case only */
953 /* I would not be surprised if this rule can not be enforced */
954 /* if and when someone fully debugs the case sensitive mode */
955 if ((e_spec[1] == 'D') &&
956 (e_spec[2] == 'I') &&
957 (e_spec[3] == 'R')) {
966 * Routine to retrieve the maximum equivalence index for an input
967 * logical name. Some calls to this routine have no knowledge if
968 * the variable is a logical or not. So on error we return a max
971 /*{{{int my_maxidx(const char *lnm) */
973 my_maxidx(const char *lnm)
977 int attr = LNM$M_CASE_BLIND;
978 struct dsc$descriptor lnmdsc;
979 struct itmlst_3 itlst[2] = {{sizeof(midx), LNM$_MAX_INDEX, &midx, 0},
982 lnmdsc.dsc$w_length = strlen(lnm);
983 lnmdsc.dsc$b_dtype = DSC$K_DTYPE_T;
984 lnmdsc.dsc$b_class = DSC$K_CLASS_S;
985 lnmdsc.dsc$a_pointer = (char *) lnm; /* Cast ok for read only parameter */
987 status = sys$trnlnm(&attr, &fildevdsc, &lnmdsc, 0, itlst);
988 if ((status & 1) == 0)
995 /*{{{int vmstrnenv(const char *lnm, char *eqv, unsigned long int idx, struct dsc$descriptor_s **tabvec, unsigned long int flags) */
997 Perl_vmstrnenv(const char *lnm, char *eqv, unsigned long int idx,
998 struct dsc$descriptor_s **tabvec, unsigned long int flags)
1001 char uplnm[LNM$C_NAMLENGTH+1], *cp2;
1002 unsigned short int eqvlen, curtab, ivlnm = 0, ivsym = 0, ivenv = 0, secure;
1003 unsigned long int retsts, attr = LNM$M_CASE_BLIND;
1005 unsigned char acmode;
1006 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
1007 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1008 struct itmlst_3 lnmlst[3] = {{sizeof idx, LNM$_INDEX, &idx, 0},
1009 {LNM$C_NAMLENGTH, LNM$_STRING, eqv, &eqvlen},
1011 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
1012 #if defined(PERL_IMPLICIT_CONTEXT)
1015 aTHX = PERL_GET_INTERP;
1021 if (!lnm || !eqv || ((idx != 0) && ((idx-1) > PERL_LNM_MAX_ALLOWED_INDEX))) {
1022 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return 0;
1024 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
1025 *cp2 = _toupper(*cp1);
1026 if (cp1 - lnm > LNM$C_NAMLENGTH) {
1027 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1031 lnmdsc.dsc$w_length = cp1 - lnm;
1032 lnmdsc.dsc$a_pointer = uplnm;
1033 uplnm[lnmdsc.dsc$w_length] = '\0';
1034 secure = flags & PERL__TRNENV_SECURE;
1035 acmode = secure ? PSL$C_EXEC : PSL$C_USER;
1036 if (!tabvec || !*tabvec) tabvec = env_tables;
1038 for (curtab = 0; tabvec[curtab]; curtab++) {
1039 if (!str$case_blind_compare(tabvec[curtab],&crtlenv)) {
1040 if (!ivenv && !secure) {
1045 #if defined(PERL_IMPLICIT_CONTEXT)
1048 "%%PERL-W-VMS_INIT Can't read CRTL environ\n");
1051 Perl_warn(aTHX_ "Can't read CRTL environ\n");
1054 retsts = SS$_NOLOGNAM;
1055 for (i = 0; environ[i]; i++) {
1056 if ((eq = strchr(environ[i],'=')) &&
1057 lnmdsc.dsc$w_length == (eq - environ[i]) &&
1058 !strncmp(environ[i],uplnm,eq - environ[i])) {
1060 for (eqvlen = 0; eq[eqvlen]; eqvlen++) eqv[eqvlen] = eq[eqvlen];
1061 if (!eqvlen) continue;
1062 retsts = SS$_NORMAL;
1066 if (retsts != SS$_NOLOGNAM) break;
1069 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
1070 !str$case_blind_compare(&tmpdsc,&clisym)) {
1071 if (!ivsym && !secure) {
1072 unsigned short int deflen = LNM$C_NAMLENGTH;
1073 struct dsc$descriptor_d eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
1074 /* dynamic dsc to accomodate possible long value */
1075 _ckvmssts_noperl(lib$sget1_dd(&deflen,&eqvdsc));
1076 retsts = lib$get_symbol(&lnmdsc,&eqvdsc,&eqvlen,0);
1078 if (eqvlen > MAX_DCL_SYMBOL) {
1079 set_errno(EVMSERR); set_vaxc_errno(LIB$_STRTRU);
1080 eqvlen = MAX_DCL_SYMBOL;
1081 /* Special hack--we might be called before the interpreter's */
1082 /* fully initialized, in which case either thr or PL_curcop */
1083 /* might be bogus. We have to check, since ckWARN needs them */
1084 /* both to be valid if running threaded */
1085 #if defined(PERL_IMPLICIT_CONTEXT)
1088 "%Perl-VMS-Init, Value of CLI symbol \"%s\" too long",lnm);
1091 if (ckWARN(WARN_MISC)) {
1092 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of CLI symbol \"%s\" too long",lnm);
1095 strncpy(eqv,eqvdsc.dsc$a_pointer,eqvlen);
1097 _ckvmssts_noperl(lib$sfree1_dd(&eqvdsc));
1098 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
1099 if (retsts == LIB$_NOSUCHSYM) continue;
1104 if ( (idx == 0) && (flags & PERL__TRNENV_JOIN_SEARCHLIST) ) {
1105 midx = my_maxidx(lnm);
1106 for (idx = 0, cp2 = eqv; idx <= midx; idx++) {
1107 lnmlst[1].bufadr = cp2;
1109 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
1110 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; break; }
1111 if (retsts == SS$_NOLOGNAM) break;
1112 /* PPFs have a prefix */
1115 *((int *)uplnm) == *((int *)"SYS$") &&
1117 eqvlen >= 4 && eqv[0] == 0x1b && eqv[1] == 0x00 &&
1118 ( (uplnm[4] == 'O' && !strcmp(uplnm,"SYS$OUTPUT")) ||
1119 (uplnm[4] == 'I' && !strcmp(uplnm,"SYS$INPUT")) ||
1120 (uplnm[4] == 'E' && !strcmp(uplnm,"SYS$ERROR")) ||
1121 (uplnm[4] == 'C' && !strcmp(uplnm,"SYS$COMMAND")) ) ) {
1122 memmove(eqv,eqv+4,eqvlen-4);
1128 if ((retsts == SS$_IVLOGNAM) ||
1129 (retsts == SS$_NOLOGNAM)) { continue; }
1132 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
1133 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
1134 if (retsts == SS$_NOLOGNAM) continue;
1137 eqvlen = strlen(eqv);
1141 if (retsts & 1) { eqv[eqvlen] = '\0'; return eqvlen; }
1142 else if (retsts == LIB$_NOSUCHSYM || retsts == LIB$_INVSYMNAM ||
1143 retsts == SS$_IVLOGNAM || retsts == SS$_IVLOGTAB ||
1144 retsts == SS$_NOLOGNAM) {
1145 set_errno(EINVAL); set_vaxc_errno(retsts);
1147 else _ckvmssts_noperl(retsts);
1149 } /* end of vmstrnenv */
1152 /*{{{ int my_trnlnm(const char *lnm, char *eqv, unsigned long int idx)*/
1153 /* Define as a function so we can access statics. */
1154 int Perl_my_trnlnm(pTHX_ const char *lnm, char *eqv, unsigned long int idx)
1158 #if defined(PERL_IMPLICIT_CONTEXT)
1161 #ifdef SECURE_INTERNAL_GETENV
1162 flags = (PL_curinterp ? PL_tainting : will_taint) ?
1163 PERL__TRNENV_SECURE : 0;
1166 return vmstrnenv(lnm, eqv, idx, fildev, flags);
1171 * Note: Uses Perl temp to store result so char * can be returned to
1172 * caller; this pointer will be invalidated at next Perl statement
1174 * We define this as a function rather than a macro in terms of my_getenv_len()
1175 * so that it'll work when PL_curinterp is undefined (and we therefore can't
1178 /*{{{ char *my_getenv(const char *lnm, bool sys)*/
1180 Perl_my_getenv(pTHX_ const char *lnm, bool sys)
1183 static char *__my_getenv_eqv = NULL;
1184 char uplnm[LNM$C_NAMLENGTH+1], *cp2, *eqv;
1185 unsigned long int idx = 0;
1186 int trnsuccess, success, secure, saverr, savvmserr;
1190 midx = my_maxidx(lnm) + 1;
1192 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
1193 /* Set up a temporary buffer for the return value; Perl will
1194 * clean it up at the next statement transition */
1195 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
1196 if (!tmpsv) return NULL;
1200 /* Assume no interpreter ==> single thread */
1201 if (__my_getenv_eqv != NULL) {
1202 Renew(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
1205 Newx(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
1207 eqv = __my_getenv_eqv;
1210 for (cp1 = lnm, cp2 = eqv; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
1211 if (cp1 - lnm == 7 && !strncmp(eqv,"DEFAULT",7)) {
1213 getcwd(eqv,LNM$C_NAMLENGTH);
1217 /* Get rid of "000000/ in rooted filespecs */
1220 zeros = strstr(eqv, "/000000/");
1221 if (zeros != NULL) {
1223 mlen = len - (zeros - eqv) - 7;
1224 memmove(zeros, &zeros[7], mlen);
1232 /* Impose security constraints only if tainting */
1234 /* Impose security constraints only if tainting */
1235 secure = PL_curinterp ? PL_tainting : will_taint;
1236 saverr = errno; savvmserr = vaxc$errno;
1243 #ifdef SECURE_INTERNAL_GETENV
1244 secure ? PERL__TRNENV_SECURE : 0
1250 /* For the getenv interface we combine all the equivalence names
1251 * of a search list logical into one value to acquire a maximum
1252 * value length of 255*128 (assuming %ENV is using logicals).
1254 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
1256 /* If the name contains a semicolon-delimited index, parse it
1257 * off and make sure we only retrieve the equivalence name for
1259 if ((cp2 = strchr(lnm,';')) != NULL) {
1261 uplnm[cp2-lnm] = '\0';
1262 idx = strtoul(cp2+1,NULL,0);
1264 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
1267 success = vmstrnenv(lnm,eqv,idx,secure ? fildev : NULL,flags);
1269 /* Discard NOLOGNAM on internal calls since we're often looking
1270 * for an optional name, and this "error" often shows up as the
1271 * (bogus) exit status for a die() call later on. */
1272 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
1273 return success ? eqv : NULL;
1276 } /* end of my_getenv() */
1280 /*{{{ SV *my_getenv_len(const char *lnm, bool sys)*/
1282 Perl_my_getenv_len(pTHX_ const char *lnm, unsigned long *len, bool sys)
1286 unsigned long idx = 0;
1288 static char *__my_getenv_len_eqv = NULL;
1289 int secure, saverr, savvmserr;
1292 midx = my_maxidx(lnm) + 1;
1294 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
1295 /* Set up a temporary buffer for the return value; Perl will
1296 * clean it up at the next statement transition */
1297 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
1298 if (!tmpsv) return NULL;
1302 /* Assume no interpreter ==> single thread */
1303 if (__my_getenv_len_eqv != NULL) {
1304 Renew(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
1307 Newx(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
1309 buf = __my_getenv_len_eqv;
1312 for (cp1 = lnm, cp2 = buf; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
1313 if (cp1 - lnm == 7 && !strncmp(buf,"DEFAULT",7)) {
1316 getcwd(buf,LNM$C_NAMLENGTH);
1319 /* Get rid of "000000/ in rooted filespecs */
1321 zeros = strstr(buf, "/000000/");
1322 if (zeros != NULL) {
1324 mlen = *len - (zeros - buf) - 7;
1325 memmove(zeros, &zeros[7], mlen);
1334 /* Impose security constraints only if tainting */
1335 secure = PL_curinterp ? PL_tainting : will_taint;
1336 saverr = errno; savvmserr = vaxc$errno;
1343 #ifdef SECURE_INTERNAL_GETENV
1344 secure ? PERL__TRNENV_SECURE : 0
1350 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
1352 if ((cp2 = strchr(lnm,';')) != NULL) {
1354 buf[cp2-lnm] = '\0';
1355 idx = strtoul(cp2+1,NULL,0);
1357 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
1360 *len = vmstrnenv(lnm,buf,idx,secure ? fildev : NULL,flags);
1362 /* Get rid of "000000/ in rooted filespecs */
1365 zeros = strstr(buf, "/000000/");
1366 if (zeros != NULL) {
1368 mlen = *len - (zeros - buf) - 7;
1369 memmove(zeros, &zeros[7], mlen);
1375 /* Discard NOLOGNAM on internal calls since we're often looking
1376 * for an optional name, and this "error" often shows up as the
1377 * (bogus) exit status for a die() call later on. */
1378 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
1379 return *len ? buf : NULL;
1382 } /* end of my_getenv_len() */
1385 static void create_mbx(unsigned short int *, struct dsc$descriptor_s *);
1387 static void riseandshine(unsigned long int dummy) { sys$wake(0,0); }
1389 /*{{{ void prime_env_iter() */
1391 prime_env_iter(void)
1392 /* Fill the %ENV associative array with all logical names we can
1393 * find, in preparation for iterating over it.
1396 static int primed = 0;
1397 HV *seenhv = NULL, *envhv;
1399 char cmd[LNM$C_NAMLENGTH+24], mbxnam[LNM$C_NAMLENGTH], *buf = NULL;
1400 unsigned short int chan;
1401 #ifndef CLI$M_TRUSTED
1402 # define CLI$M_TRUSTED 0x40 /* Missing from VAXC headers */
1404 unsigned long int defflags = CLI$M_NOWAIT | CLI$M_NOKEYPAD | CLI$M_TRUSTED;
1405 unsigned long int mbxbufsiz, flags, retsts, subpid = 0, substs = 0, wakect = 0;
1407 bool have_sym = FALSE, have_lnm = FALSE;
1408 struct dsc$descriptor_s tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1409 $DESCRIPTOR(cmddsc,cmd); $DESCRIPTOR(nldsc,"_NLA0:");
1410 $DESCRIPTOR(clidsc,"DCL"); $DESCRIPTOR(clitabdsc,"DCLTABLES");
1411 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
1412 $DESCRIPTOR(local,"_LOCAL"); $DESCRIPTOR(mbxdsc,mbxnam);
1413 #if defined(PERL_IMPLICIT_CONTEXT)
1416 #if defined(USE_ITHREADS)
1417 static perl_mutex primenv_mutex;
1418 MUTEX_INIT(&primenv_mutex);
1421 #if defined(PERL_IMPLICIT_CONTEXT)
1422 /* We jump through these hoops because we can be called at */
1423 /* platform-specific initialization time, which is before anything is */
1424 /* set up--we can't even do a plain dTHX since that relies on the */
1425 /* interpreter structure to be initialized */
1427 aTHX = PERL_GET_INTERP;
1429 /* we never get here because the NULL pointer will cause the */
1430 /* several of the routines called by this routine to access violate */
1432 /* This routine is only called by hv.c/hv_iterinit which has a */
1433 /* context, so the real fix may be to pass it through instead of */
1434 /* the hoops above */
1439 if (primed || !PL_envgv) return;
1440 MUTEX_LOCK(&primenv_mutex);
1441 if (primed) { MUTEX_UNLOCK(&primenv_mutex); return; }
1442 envhv = GvHVn(PL_envgv);
1443 /* Perform a dummy fetch as an lval to insure that the hash table is
1444 * set up. Otherwise, the hv_store() will turn into a nullop. */
1445 (void) hv_fetch(envhv,"DEFAULT",7,TRUE);
1447 for (i = 0; env_tables[i]; i++) {
1448 if (!have_sym && (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
1449 !str$case_blind_compare(&tmpdsc,&clisym)) have_sym = 1;
1450 if (!have_lnm && str$case_blind_compare(env_tables[i],&crtlenv)) have_lnm = 1;
1452 if (have_sym || have_lnm) {
1453 long int syiitm = SYI$_MAXBUF, dviitm = DVI$_DEVNAM;
1454 _ckvmssts(lib$getsyi(&syiitm, &mbxbufsiz, 0, 0, 0, 0));
1455 _ckvmssts(sys$crembx(0,&chan,mbxbufsiz,mbxbufsiz,0xff0f,0,0));
1456 _ckvmssts(lib$getdvi(&dviitm, &chan, NULL, NULL, &mbxdsc, &mbxdsc.dsc$w_length));
1459 for (i--; i >= 0; i--) {
1460 if (!str$case_blind_compare(env_tables[i],&crtlenv)) {
1463 for (j = 0; environ[j]; j++) {
1464 if (!(start = strchr(environ[j],'='))) {
1465 if (ckWARN(WARN_INTERNAL))
1466 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed CRTL environ value \"%s\"\n",environ[j]);
1470 sv = newSVpv(start,0);
1472 (void) hv_store(envhv,environ[j],start - environ[j] - 1,sv,0);
1477 else if ((tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
1478 !str$case_blind_compare(&tmpdsc,&clisym)) {
1479 strcpy(cmd,"Show Symbol/Global *");
1480 cmddsc.dsc$w_length = 20;
1481 if (env_tables[i]->dsc$w_length == 12 &&
1482 (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer + 6) &&
1483 !str$case_blind_compare(&tmpdsc,&local)) strcpy(cmd+12,"Local *");
1484 flags = defflags | CLI$M_NOLOGNAM;
1487 strcpy(cmd,"Show Logical *");
1488 if (str$case_blind_compare(env_tables[i],&fildevdsc)) {
1489 strcat(cmd," /Table=");
1490 strncat(cmd,env_tables[i]->dsc$a_pointer,env_tables[i]->dsc$w_length);
1491 cmddsc.dsc$w_length = strlen(cmd);
1493 else cmddsc.dsc$w_length = 14; /* N.B. We test this below */
1494 flags = defflags | CLI$M_NOCLISYM;
1497 /* Create a new subprocess to execute each command, to exclude the
1498 * remote possibility that someone could subvert a mbx or file used
1499 * to write multiple commands to a single subprocess.
1502 retsts = lib$spawn(&cmddsc,&nldsc,&mbxdsc,&flags,0,&subpid,&substs,
1503 0,&riseandshine,0,0,&clidsc,&clitabdsc);
1504 flags &= ~CLI$M_TRUSTED; /* Just in case we hit a really old version */
1505 defflags &= ~CLI$M_TRUSTED;
1506 } while (retsts == LIB$_INVARG && (flags | CLI$M_TRUSTED));
1508 if (!buf) Newx(buf,mbxbufsiz + 1,char);
1509 if (seenhv) SvREFCNT_dec(seenhv);
1512 char *cp1, *cp2, *key;
1513 unsigned long int sts, iosb[2], retlen, keylen;
1516 sts = sys$qiow(0,chan,IO$_READVBLK,iosb,0,0,buf,mbxbufsiz,0,0,0,0);
1517 if (sts & 1) sts = iosb[0] & 0xffff;
1518 if (sts == SS$_ENDOFFILE) {
1520 while (substs == 0) { sys$hiber(); wakect++;}
1521 if (wakect > 1) sys$wake(0,0); /* Stole someone else's wake */
1526 retlen = iosb[0] >> 16;
1527 if (!retlen) continue; /* blank line */
1529 if (iosb[1] != subpid) {
1531 Perl_croak(aTHX_ "Unknown process %x sent message to prime_env_iter: %s",buf);
1535 if (sts == SS$_BUFFEROVF && ckWARN(WARN_INTERNAL))
1536 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Buffer overflow in prime_env_iter: %s",buf);
1538 for (cp1 = buf; *cp1 && isspace(*cp1); cp1++) ;
1539 if (*cp1 == '(' || /* Logical name table name */
1540 *cp1 == '=' /* Next eqv of searchlist */) continue;
1541 if (*cp1 == '"') cp1++;
1542 for (cp2 = cp1; *cp2 && *cp2 != '"' && *cp2 != ' '; cp2++) ;
1543 key = cp1; keylen = cp2 - cp1;
1544 if (keylen && hv_exists(seenhv,key,keylen)) continue;
1545 while (*cp2 && *cp2 != '=') cp2++;
1546 while (*cp2 && *cp2 == '=') cp2++;
1547 while (*cp2 && *cp2 == ' ') cp2++;
1548 if (*cp2 == '"') { /* String translation; may embed "" */
1549 for (cp1 = buf + retlen; *cp1 != '"'; cp1--) ;
1550 cp2++; cp1--; /* Skip "" surrounding translation */
1552 else { /* Numeric translation */
1553 for (cp1 = cp2; *cp1 && *cp1 != ' '; cp1++) ;
1554 cp1--; /* stop on last non-space char */
1556 if ((!keylen || (cp1 - cp2 < -1)) && ckWARN(WARN_INTERNAL)) {
1557 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed message in prime_env_iter: |%s|",buf);
1560 PERL_HASH(hash,key,keylen);
1562 if (cp1 == cp2 && *cp2 == '.') {
1563 /* A single dot usually means an unprintable character, such as a null
1564 * to indicate a zero-length value. Get the actual value to make sure.
1566 char lnm[LNM$C_NAMLENGTH+1];
1567 char eqv[MAX_DCL_SYMBOL+1];
1569 strncpy(lnm, key, keylen);
1570 trnlen = vmstrnenv(lnm, eqv, 0, fildev, 0);
1571 sv = newSVpvn(eqv, strlen(eqv));
1574 sv = newSVpvn(cp2,cp1 - cp2 + 1);
1578 hv_store(envhv,key,keylen,sv,hash);
1579 hv_store(seenhv,key,keylen,&PL_sv_yes,hash);
1581 if (cmddsc.dsc$w_length == 14) { /* We just read LNM$FILE_DEV */
1582 /* get the PPFs for this process, not the subprocess */
1583 const char *ppfs[] = {"SYS$COMMAND", "SYS$INPUT", "SYS$OUTPUT", "SYS$ERROR", NULL};
1584 char eqv[LNM$C_NAMLENGTH+1];
1586 for (i = 0; ppfs[i]; i++) {
1587 trnlen = vmstrnenv(ppfs[i],eqv,0,fildev,0);
1588 sv = newSVpv(eqv,trnlen);
1590 hv_store(envhv,ppfs[i],strlen(ppfs[i]),sv,0);
1595 if (have_sym || have_lnm) _ckvmssts(sys$dassgn(chan));
1596 if (buf) Safefree(buf);
1597 if (seenhv) SvREFCNT_dec(seenhv);
1598 MUTEX_UNLOCK(&primenv_mutex);
1601 } /* end of prime_env_iter */
1605 /*{{{ int vmssetenv(const char *lnm, const char *eqv)*/
1606 /* Define or delete an element in the same "environment" as
1607 * vmstrnenv(). If an element is to be deleted, it's removed from
1608 * the first place it's found. If it's to be set, it's set in the
1609 * place designated by the first element of the table vector.
1610 * Like setenv() returns 0 for success, non-zero on error.
1613 Perl_vmssetenv(pTHX_ const char *lnm, const char *eqv, struct dsc$descriptor_s **tabvec)
1616 char uplnm[LNM$C_NAMLENGTH], *cp2, *c;
1617 unsigned short int curtab, ivlnm = 0, ivsym = 0, ivenv = 0;
1619 unsigned long int retsts, usermode = PSL$C_USER;
1620 struct itmlst_3 *ile, *ilist;
1621 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,uplnm},
1622 eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
1623 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1624 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
1625 $DESCRIPTOR(local,"_LOCAL");
1628 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1629 return SS$_IVLOGNAM;
1632 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
1633 *cp2 = _toupper(*cp1);
1634 if (cp1 - lnm > LNM$C_NAMLENGTH) {
1635 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1636 return SS$_IVLOGNAM;
1639 lnmdsc.dsc$w_length = cp1 - lnm;
1640 if (!tabvec || !*tabvec) tabvec = env_tables;
1642 if (!eqv) { /* we're deleting n element */
1643 for (curtab = 0; tabvec[curtab]; curtab++) {
1644 if (!ivenv && !str$case_blind_compare(tabvec[curtab],&crtlenv)) {
1646 for (i = 0; environ[i]; i++) { /* If it's an environ elt, reset */
1647 if ((cp1 = strchr(environ[i],'=')) &&
1648 lnmdsc.dsc$w_length == (cp1 - environ[i]) &&
1649 !strncmp(environ[i],lnm,cp1 - environ[i])) {
1651 return setenv(lnm,"",1) ? vaxc$errno : 0;
1654 ivenv = 1; retsts = SS$_NOLOGNAM;
1656 if (ckWARN(WARN_INTERNAL))
1657 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't reset CRTL environ elements (%s)",lnm);
1658 ivenv = 1; retsts = SS$_NOSUCHPGM;
1664 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
1665 !str$case_blind_compare(&tmpdsc,&clisym)) {
1666 unsigned int symtype;
1667 if (tabvec[curtab]->dsc$w_length == 12 &&
1668 (tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer + 6) &&
1669 !str$case_blind_compare(&tmpdsc,&local))
1670 symtype = LIB$K_CLI_LOCAL_SYM;
1671 else symtype = LIB$K_CLI_GLOBAL_SYM;
1672 retsts = lib$delete_symbol(&lnmdsc,&symtype);
1673 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
1674 if (retsts == LIB$_NOSUCHSYM) continue;
1678 retsts = sys$dellnm(tabvec[curtab],&lnmdsc,&usermode); /* try user mode first */
1679 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
1680 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
1681 retsts = lib$delete_logical(&lnmdsc,tabvec[curtab]); /* then supervisor mode */
1682 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
1686 else { /* we're defining a value */
1687 if (!ivenv && !str$case_blind_compare(tabvec[0],&crtlenv)) {
1689 return setenv(lnm,eqv,1) ? vaxc$errno : 0;
1691 if (ckWARN(WARN_INTERNAL))
1692 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't set CRTL environ elements (%s=%s)",lnm,eqv);
1693 retsts = SS$_NOSUCHPGM;
1697 eqvdsc.dsc$a_pointer = (char *) eqv; /* cast ok to readonly parameter */
1698 eqvdsc.dsc$w_length = strlen(eqv);
1699 if ((tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer) &&
1700 !str$case_blind_compare(&tmpdsc,&clisym)) {
1701 unsigned int symtype;
1702 if (tabvec[0]->dsc$w_length == 12 &&
1703 (tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer + 6) &&
1704 !str$case_blind_compare(&tmpdsc,&local))
1705 symtype = LIB$K_CLI_LOCAL_SYM;
1706 else symtype = LIB$K_CLI_GLOBAL_SYM;
1707 retsts = lib$set_symbol(&lnmdsc,&eqvdsc,&symtype);
1710 if (!*eqv) eqvdsc.dsc$w_length = 1;
1711 if (eqvdsc.dsc$w_length > LNM$C_NAMLENGTH) {
1713 nseg = (eqvdsc.dsc$w_length + LNM$C_NAMLENGTH - 1) / LNM$C_NAMLENGTH;
1714 if (nseg > PERL_LNM_MAX_ALLOWED_INDEX + 1) {
1715 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of logical \"%s\" too long. Truncating to %i bytes",
1716 lnm, LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1));
1717 eqvdsc.dsc$w_length = LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1);
1718 nseg = PERL_LNM_MAX_ALLOWED_INDEX + 1;
1721 Newx(ilist,nseg+1,struct itmlst_3);
1724 set_errno(ENOMEM); set_vaxc_errno(SS$_INSFMEM);
1727 memset(ilist, 0, (sizeof(struct itmlst_3) * (nseg+1)));
1729 for (j = 0, c = eqvdsc.dsc$a_pointer; j < nseg; j++, ile++, c += LNM$C_NAMLENGTH) {
1730 ile->itmcode = LNM$_STRING;
1732 if ((j+1) == nseg) {
1733 ile->buflen = strlen(c);
1734 /* in case we are truncating one that's too long */
1735 if (ile->buflen > LNM$C_NAMLENGTH) ile->buflen = LNM$C_NAMLENGTH;
1738 ile->buflen = LNM$C_NAMLENGTH;
1742 retsts = lib$set_logical(&lnmdsc,0,tabvec[0],0,ilist);
1746 retsts = lib$set_logical(&lnmdsc,&eqvdsc,tabvec[0],0,0);
1751 if (!(retsts & 1)) {
1753 case LIB$_AMBSYMDEF: case LIB$_INSCLIMEM:
1754 case SS$_NOLOGTAB: case SS$_TOOMANYLNAM: case SS$_IVLOGTAB:
1755 set_errno(EVMSERR); break;
1756 case LIB$_INVARG: case LIB$_INVSYMNAM: case SS$_IVLOGNAM:
1757 case LIB$_NOSUCHSYM: case SS$_NOLOGNAM:
1758 set_errno(EINVAL); break;
1760 set_errno(EACCES); break;
1765 set_vaxc_errno(retsts);
1766 return (int) retsts || 44; /* retsts should never be 0, but just in case */
1769 /* We reset error values on success because Perl does an hv_fetch()
1770 * before each hv_store(), and if the thing we're setting didn't
1771 * previously exist, we've got a leftover error message. (Of course,
1772 * this fails in the face of
1773 * $foo = $ENV{nonexistent}; $ENV{existent} = 'foo';
1774 * in that the error reported in $! isn't spurious,
1775 * but it's right more often than not.)
1777 set_errno(0); set_vaxc_errno(retsts);
1781 } /* end of vmssetenv() */
1784 /*{{{ void my_setenv(const char *lnm, const char *eqv)*/
1785 /* This has to be a function since there's a prototype for it in proto.h */
1787 Perl_my_setenv(pTHX_ const char *lnm, const char *eqv)
1790 int len = strlen(lnm);
1794 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
1795 if (!strcmp(uplnm,"DEFAULT")) {
1796 if (eqv && *eqv) my_chdir(eqv);
1800 #ifndef RTL_USES_UTC
1801 if (len == 6 || len == 2) {
1804 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
1806 if (!strcmp(uplnm,"UCX$TZ")) tz_updated = 1;
1807 if (!strcmp(uplnm,"TZ")) tz_updated = 1;
1811 (void) vmssetenv(lnm,eqv,NULL);
1815 /*{{{static void vmssetuserlnm(char *name, char *eqv); */
1817 * sets a user-mode logical in the process logical name table
1818 * used for redirection of sys$error
1821 Perl_vmssetuserlnm(pTHX_ const char *name, const char *eqv)
1823 $DESCRIPTOR(d_tab, "LNM$PROCESS");
1824 struct dsc$descriptor_d d_name = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
1825 unsigned long int iss, attr = LNM$M_CONFINE;
1826 unsigned char acmode = PSL$C_USER;
1827 struct itmlst_3 lnmlst[2] = {{0, LNM$_STRING, 0, 0},
1829 d_name.dsc$a_pointer = (char *)name; /* Cast OK for read only parameter */
1830 d_name.dsc$w_length = strlen(name);
1832 lnmlst[0].buflen = strlen(eqv);
1833 lnmlst[0].bufadr = (char *)eqv; /* Cast OK for read only parameter */
1835 iss = sys$crelnm(&attr,&d_tab,&d_name,&acmode,lnmlst);
1836 if (!(iss&1)) lib$signal(iss);
1841 /*{{{ char *my_crypt(const char *textpasswd, const char *usrname)*/
1842 /* my_crypt - VMS password hashing
1843 * my_crypt() provides an interface compatible with the Unix crypt()
1844 * C library function, and uses sys$hash_password() to perform VMS
1845 * password hashing. The quadword hashed password value is returned
1846 * as a NUL-terminated 8 character string. my_crypt() does not change
1847 * the case of its string arguments; in order to match the behavior
1848 * of LOGINOUT et al., alphabetic characters in both arguments must
1849 * be upcased by the caller.
1851 * - fix me to call ACM services when available
1854 Perl_my_crypt(pTHX_ const char *textpasswd, const char *usrname)
1856 # ifndef UAI$C_PREFERRED_ALGORITHM
1857 # define UAI$C_PREFERRED_ALGORITHM 127
1859 unsigned char alg = UAI$C_PREFERRED_ALGORITHM;
1860 unsigned short int salt = 0;
1861 unsigned long int sts;
1863 unsigned short int dsc$w_length;
1864 unsigned char dsc$b_type;
1865 unsigned char dsc$b_class;
1866 const char * dsc$a_pointer;
1867 } usrdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
1868 txtdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1869 struct itmlst_3 uailst[3] = {
1870 { sizeof alg, UAI$_ENCRYPT, &alg, 0},
1871 { sizeof salt, UAI$_SALT, &salt, 0},
1872 { 0, 0, NULL, NULL}};
1873 static char hash[9];
1875 usrdsc.dsc$w_length = strlen(usrname);
1876 usrdsc.dsc$a_pointer = usrname;
1877 if (!((sts = sys$getuai(0, 0, &usrdsc, uailst, 0, 0, 0)) & 1)) {
1879 case SS$_NOGRPPRV: case SS$_NOSYSPRV:
1883 set_errno(ESRCH); /* There isn't a Unix no-such-user error */
1888 set_vaxc_errno(sts);
1889 if (sts != RMS$_RNF) return NULL;
1892 txtdsc.dsc$w_length = strlen(textpasswd);
1893 txtdsc.dsc$a_pointer = textpasswd;
1894 if (!((sts = sys$hash_password(&txtdsc, alg, salt, &usrdsc, &hash)) & 1)) {
1895 set_errno(EVMSERR); set_vaxc_errno(sts); return NULL;
1898 return (char *) hash;
1900 } /* end of my_crypt() */
1904 static char *mp_do_rmsexpand(pTHX_ const char *, char *, int, const char *, unsigned, int *, int *);
1905 static char *mp_do_fileify_dirspec(pTHX_ const char *, char *, int, int *);
1906 static char *mp_do_tovmsspec(pTHX_ const char *, char *, int, int, int *);
1908 /* fixup barenames that are directories for internal use.
1909 * There have been problems with the consistent handling of UNIX
1910 * style directory names when routines are presented with a name that
1911 * has no directory delimitors at all. So this routine will eventually
1914 static char * fixup_bare_dirnames(const char * name)
1916 if (decc_disable_to_vms_logname_translation) {
1922 /* 8.3, remove() is now broken on symbolic links */
1923 static int rms_erase(const char * vmsname);
1927 * A little hack to get around a bug in some implemenation of remove()
1928 * that do not know how to delete a directory
1930 * Delete any file to which user has control access, regardless of whether
1931 * delete access is explicitly allowed.
1932 * Limitations: User must have write access to parent directory.
1933 * Does not block signals or ASTs; if interrupted in midstream
1934 * may leave file with an altered ACL.
1937 /*{{{int mp_do_kill_file(const char *name, int dirflag)*/
1939 mp_do_kill_file(pTHX_ const char *name, int dirflag)
1943 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
1944 unsigned long int cxt = 0, aclsts, fndsts, rmsts = -1;
1945 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1947 unsigned char myace$b_length;
1948 unsigned char myace$b_type;
1949 unsigned short int myace$w_flags;
1950 unsigned long int myace$l_access;
1951 unsigned long int myace$l_ident;
1952 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
1953 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0},
1954 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
1956 findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0},
1957 {sizeof oldace, ACL$C_READACE, &oldace, 0},{0,0,0,0}},
1958 addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}},
1959 dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}},
1960 lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}},
1961 ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}};
1963 /* Expand the input spec using RMS, since the CRTL remove() and
1964 * system services won't do this by themselves, so we may miss
1965 * a file "hiding" behind a logical name or search list. */
1966 vmsname = PerlMem_malloc(NAM$C_MAXRSS+1);
1967 if (vmsname == NULL) _ckvmssts_noperl(SS$_INSFMEM);
1969 rslt = int_rmsexpand_tovms(name, vmsname, PERL_RMSEXPAND_M_SYMLINK);
1971 PerlMem_free(vmsname);
1975 /* Erase the file */
1976 rmsts = rms_erase(vmsname);
1978 /* Did it succeed */
1979 if ($VMS_STATUS_SUCCESS(rmsts)) {
1980 PerlMem_free(vmsname);
1984 /* If not, can changing protections help? */
1985 if (rmsts != RMS$_PRV) {
1986 set_vaxc_errno(rmsts);
1987 PerlMem_free(vmsname);
1991 /* No, so we get our own UIC to use as a rights identifier,
1992 * and the insert an ACE at the head of the ACL which allows us
1993 * to delete the file.
1995 _ckvmssts_noperl(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
1996 fildsc.dsc$w_length = strlen(vmsname);
1997 fildsc.dsc$a_pointer = vmsname;
1999 newace.myace$l_ident = oldace.myace$l_ident;
2001 if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) {
2003 case RMS$_FNF: case RMS$_DNF: case SS$_NOSUCHOBJECT:
2004 set_errno(ENOENT); break;
2006 set_errno(ENOTDIR); break;
2008 set_errno(ENODEV); break;
2009 case RMS$_SYN: case SS$_INVFILFOROP:
2010 set_errno(EINVAL); break;
2012 set_errno(EACCES); break;
2014 _ckvmssts_noperl(aclsts);
2016 set_vaxc_errno(aclsts);
2017 PerlMem_free(vmsname);
2020 /* Grab any existing ACEs with this identifier in case we fail */
2021 aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt);
2022 if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY
2023 || fndsts == SS$_NOMOREACE ) {
2024 /* Add the new ACE . . . */
2025 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1))
2028 rmsts = rms_erase(vmsname);
2029 if ($VMS_STATUS_SUCCESS(rmsts)) {
2034 /* We blew it - dir with files in it, no write priv for
2035 * parent directory, etc. Put things back the way they were. */
2036 if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1))
2039 addlst[0].bufadr = &oldace;
2040 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1))
2047 fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0);
2048 /* We just deleted it, so of course it's not there. Some versions of
2049 * VMS seem to return success on the unlock operation anyhow (after all
2050 * the unlock is successful), but others don't.
2052 if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL;
2053 if (aclsts & 1) aclsts = fndsts;
2054 if (!(aclsts & 1)) {
2056 set_vaxc_errno(aclsts);
2059 PerlMem_free(vmsname);
2062 } /* end of kill_file() */
2066 /*{{{int do_rmdir(char *name)*/
2068 Perl_do_rmdir(pTHX_ const char *name)
2074 dirfile = PerlMem_malloc(VMS_MAXRSS + 1);
2075 if (dirfile == NULL)
2076 _ckvmssts(SS$_INSFMEM);
2078 /* Force to a directory specification */
2079 if (do_fileify_dirspec(name, dirfile, 0, NULL) == NULL) {
2080 PerlMem_free(dirfile);
2083 if (Perl_flex_lstat(aTHX_ dirfile, &st) || !S_ISDIR(st.st_mode)) {
2088 retval = mp_do_kill_file(aTHX_ dirfile, 1);
2090 PerlMem_free(dirfile);
2093 } /* end of do_rmdir */
2097 * Delete any file to which user has control access, regardless of whether
2098 * delete access is explicitly allowed.
2099 * Limitations: User must have write access to parent directory.
2100 * Does not block signals or ASTs; if interrupted in midstream
2101 * may leave file with an altered ACL.
2104 /*{{{int kill_file(char *name)*/
2106 Perl_kill_file(pTHX_ const char *name)
2108 char rspec[NAM$C_MAXRSS+1];
2113 /* Remove() is allowed to delete directories, according to the X/Open
2115 * This may need special handling to work with the ACL hacks.
2117 if ((flex_lstat(name, &st) == 0) && S_ISDIR(st.st_mode)) {
2118 rmsts = Perl_do_rmdir(aTHX_ name);
2122 rmsts = mp_do_kill_file(aTHX_ name, 0);
2126 } /* end of kill_file() */
2130 /*{{{int my_mkdir(char *,Mode_t)*/
2132 Perl_my_mkdir(pTHX_ const char *dir, Mode_t mode)
2134 STRLEN dirlen = strlen(dir);
2136 /* zero length string sometimes gives ACCVIO */
2137 if (dirlen == 0) return -1;
2139 /* CRTL mkdir() doesn't tolerate trailing /, since that implies
2140 * null file name/type. However, it's commonplace under Unix,
2141 * so we'll allow it for a gain in portability.
2143 if (dir[dirlen-1] == '/') {
2144 char *newdir = savepvn(dir,dirlen-1);
2145 int ret = mkdir(newdir,mode);
2149 else return mkdir(dir,mode);
2150 } /* end of my_mkdir */
2153 /*{{{int my_chdir(char *)*/
2155 Perl_my_chdir(pTHX_ const char *dir)
2157 STRLEN dirlen = strlen(dir);
2159 /* zero length string sometimes gives ACCVIO */
2160 if (dirlen == 0) return -1;
2163 /* Perl is passing the output of the DCL SHOW DEFAULT with leading spaces.
2164 * This does not work if DECC$EFS_CHARSET is active. Hack it here
2165 * so that existing scripts do not need to be changed.
2168 while ((dirlen > 0) && (*dir1 == ' ')) {
2173 /* some versions of CRTL chdir() doesn't tolerate trailing /, since
2175 * null file name/type. However, it's commonplace under Unix,
2176 * so we'll allow it for a gain in portability.
2178 * - Preview- '/' will be valid soon on VMS
2180 if ((dirlen > 1) && (dir1[dirlen-1] == '/')) {
2181 char *newdir = savepvn(dir1,dirlen-1);
2182 int ret = chdir(newdir);
2186 else return chdir(dir1);
2187 } /* end of my_chdir */
2191 /*{{{int my_chmod(char *, mode_t)*/
2193 Perl_my_chmod(pTHX_ const char *file_spec, mode_t mode)
2195 STRLEN speclen = strlen(file_spec);
2197 /* zero length string sometimes gives ACCVIO */
2198 if (speclen == 0) return -1;
2200 /* some versions of CRTL chmod() doesn't tolerate trailing /, since
2201 * that implies null file name/type. However, it's commonplace under Unix,
2202 * so we'll allow it for a gain in portability.
2204 * Tests are showing that chmod() on VMS 8.3 is only accepting directories
2205 * in VMS file.dir notation.
2207 if ((speclen > 1) && (file_spec[speclen-1] == '/')) {
2208 char *vms_src, *vms_dir, *rslt;
2212 /* First convert this to a VMS format specification */
2213 vms_src = PerlMem_malloc(VMS_MAXRSS);
2214 if (vms_src == NULL)
2215 _ckvmssts_noperl(SS$_INSFMEM);
2217 rslt = do_tovmsspec(file_spec, vms_src, 0, NULL);
2219 /* If we fail, then not a file specification */
2220 PerlMem_free(vms_src);
2225 /* Now make it a directory spec so chmod is happy */
2226 vms_dir = PerlMem_malloc(VMS_MAXRSS + 1);
2227 if (vms_dir == NULL)
2228 _ckvmssts_noperl(SS$_INSFMEM);
2229 rslt = do_fileify_dirspec(vms_src, vms_dir, 0, NULL);
2230 PerlMem_free(vms_src);
2234 ret = chmod(vms_dir, mode);
2238 PerlMem_free(vms_dir);
2241 else return chmod(file_spec, mode);
2242 } /* end of my_chmod */
2246 /*{{{FILE *my_tmpfile()*/
2253 if ((fp = tmpfile())) return fp;
2255 cp = PerlMem_malloc(L_tmpnam+24);
2256 if (cp == NULL) _ckvmssts_noperl(SS$_INSFMEM);
2258 if (decc_filename_unix_only == 0)
2259 strcpy(cp,"Sys$Scratch:");
2262 tmpnam(cp+strlen(cp));
2263 strcat(cp,".Perltmp");
2264 fp = fopen(cp,"w+","fop=dlt");
2271 #ifndef HOMEGROWN_POSIX_SIGNALS
2273 * The C RTL's sigaction fails to check for invalid signal numbers so we
2274 * help it out a bit. The docs are correct, but the actual routine doesn't
2275 * do what the docs say it will.
2277 /*{{{int Perl_my_sigaction (pTHX_ int, const struct sigaction*, struct sigaction*);*/
2279 Perl_my_sigaction (pTHX_ int sig, const struct sigaction* act,
2280 struct sigaction* oact)
2282 if (sig == SIGKILL || sig == SIGSTOP || sig == SIGCONT) {
2283 SETERRNO(EINVAL, SS$_INVARG);
2286 return sigaction(sig, act, oact);
2291 #ifdef KILL_BY_SIGPRC
2292 #include <errnodef.h>
2294 /* We implement our own kill() using the undocumented system service
2295 sys$sigprc for one of two reasons:
2297 1.) If the kill() in an older CRTL uses sys$forcex, causing the
2298 target process to do a sys$exit, which usually can't be handled
2299 gracefully...certainly not by Perl and the %SIG{} mechanism.
2301 2.) If the kill() in the CRTL can't be called from a signal
2302 handler without disappearing into the ether, i.e., the signal
2303 it purportedly sends is never trapped. Still true as of VMS 7.3.
2305 sys$sigprc has the same parameters as sys$forcex, but throws an exception
2306 in the target process rather than calling sys$exit.
2308 Note that distinguishing SIGSEGV from SIGBUS requires an extra arg
2309 on the ACCVIO condition, which sys$sigprc (and sys$forcex) don't
2310 provide. On VMS 7.0+ this is taken care of by doing sys$sigprc
2311 with condition codes C$_SIG0+nsig*8, catching the exception on the
2312 target process and resignaling with appropriate arguments.
2314 But we don't have that VMS 7.0+ exception handler, so if you
2315 Perl_my_kill(.., SIGSEGV) it will show up as a SIGBUS. Oh well.
2317 Also note that SIGTERM is listed in the docs as being "unimplemented",
2318 yet always seems to be signaled with a VMS condition code of 4 (and
2319 correctly handled for that code). So we hardwire it in.
2321 Unlike the VMS 7.0+ CRTL kill() function, we actually check the signal
2322 number to see if it's valid. So Perl_my_kill(pid,0) returns -1 rather
2323 than signalling with an unrecognized (and unhandled by CRTL) code.
2326 #define _MY_SIG_MAX 28
2329 Perl_sig_to_vmscondition_int(int sig)
2331 static unsigned int sig_code[_MY_SIG_MAX+1] =
2334 SS$_HANGUP, /* 1 SIGHUP */
2335 SS$_CONTROLC, /* 2 SIGINT */
2336 SS$_CONTROLY, /* 3 SIGQUIT */
2337 SS$_RADRMOD, /* 4 SIGILL */
2338 SS$_BREAK, /* 5 SIGTRAP */
2339 SS$_OPCCUS, /* 6 SIGABRT */
2340 SS$_COMPAT, /* 7 SIGEMT */
2342 SS$_FLTOVF, /* 8 SIGFPE VAX */
2344 SS$_HPARITH, /* 8 SIGFPE AXP */
2346 SS$_ABORT, /* 9 SIGKILL */
2347 SS$_ACCVIO, /* 10 SIGBUS */
2348 SS$_ACCVIO, /* 11 SIGSEGV */
2349 SS$_BADPARAM, /* 12 SIGSYS */
2350 SS$_NOMBX, /* 13 SIGPIPE */
2351 SS$_ASTFLT, /* 14 SIGALRM */
2368 #if __VMS_VER >= 60200000
2369 static int initted = 0;
2372 sig_code[16] = C$_SIGUSR1;
2373 sig_code[17] = C$_SIGUSR2;
2374 #if __CRTL_VER >= 70000000
2375 sig_code[20] = C$_SIGCHLD;
2377 #if __CRTL_VER >= 70300000
2378 sig_code[28] = C$_SIGWINCH;
2383 if (sig < _SIG_MIN) return 0;
2384 if (sig > _MY_SIG_MAX) return 0;
2385 return sig_code[sig];
2389 Perl_sig_to_vmscondition(int sig)
2392 if (vms_debug_on_exception != 0)
2393 lib$signal(SS$_DEBUG);
2395 return Perl_sig_to_vmscondition_int(sig);
2400 Perl_my_kill(int pid, int sig)
2405 int sys$sigprc(unsigned int *pidadr,
2406 struct dsc$descriptor_s *prcname,
2409 /* sig 0 means validate the PID */
2410 /*------------------------------*/
2412 const unsigned long int jpicode = JPI$_PID;
2415 status = lib$getjpi(&jpicode, &pid, NULL, &ret_pid, NULL, NULL);
2416 if ($VMS_STATUS_SUCCESS(status))
2419 case SS$_NOSUCHNODE:
2420 case SS$_UNREACHABLE:
2434 code = Perl_sig_to_vmscondition_int(sig);
2437 SETERRNO(EINVAL, SS$_BADPARAM);
2441 /* Fixme: Per official UNIX specification: If pid = 0, or negative then
2442 * signals are to be sent to multiple processes.
2443 * pid = 0 - all processes in group except ones that the system exempts
2444 * pid = -1 - all processes except ones that the system exempts
2445 * pid = -n - all processes in group (abs(n)) except ...
2446 * For now, just report as not supported.
2450 SETERRNO(ENOTSUP, SS$_UNSUPPORTED);
2454 iss = sys$sigprc((unsigned int *)&pid,0,code);
2455 if (iss&1) return 0;
2459 set_errno(EPERM); break;
2461 case SS$_NOSUCHNODE:
2462 case SS$_UNREACHABLE:
2463 set_errno(ESRCH); break;
2465 set_errno(ENOMEM); break;
2467 _ckvmssts_noperl(iss);
2470 set_vaxc_errno(iss);
2476 /* Routine to convert a VMS status code to a UNIX status code.
2477 ** More tricky than it appears because of conflicting conventions with
2480 ** VMS status codes are a bit mask, with the least significant bit set for
2483 ** Special UNIX status of EVMSERR indicates that no translation is currently
2484 ** available, and programs should check the VMS status code.
2486 ** Programs compiled with _POSIX_EXIT have a special encoding that requires
2490 #ifndef C_FACILITY_NO
2491 #define C_FACILITY_NO 0x350000
2494 #define DCL_IVVERB 0x38090
2497 int Perl_vms_status_to_unix(int vms_status, int child_flag)
2505 /* Assume the best or the worst */
2506 if (vms_status & STS$M_SUCCESS)
2509 unix_status = EVMSERR;
2511 msg_status = vms_status & ~STS$M_CONTROL;
2513 facility = vms_status & STS$M_FAC_NO;
2514 fac_sp = vms_status & STS$M_FAC_SP;
2515 msg_no = vms_status & (STS$M_MSG_NO | STS$M_SEVERITY);
2517 if (((facility == 0) || (fac_sp == 0)) && (child_flag == 0)) {
2523 unix_status = EFAULT;
2525 case SS$_DEVOFFLINE:
2526 unix_status = EBUSY;
2529 unix_status = ENOTCONN;
2537 case SS$_INVFILFOROP:
2541 unix_status = EINVAL;
2543 case SS$_UNSUPPORTED:
2544 unix_status = ENOTSUP;
2549 unix_status = EACCES;
2551 case SS$_DEVICEFULL:
2552 unix_status = ENOSPC;
2555 unix_status = ENODEV;
2557 case SS$_NOSUCHFILE:
2558 case SS$_NOSUCHOBJECT:
2559 unix_status = ENOENT;
2561 case SS$_ABORT: /* Fatal case */
2562 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_ERROR): /* Error case */
2563 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_WARNING): /* Warning case */
2564 unix_status = EINTR;
2567 unix_status = E2BIG;
2570 unix_status = ENOMEM;
2573 unix_status = EPERM;
2575 case SS$_NOSUCHNODE:
2576 case SS$_UNREACHABLE:
2577 unix_status = ESRCH;
2580 unix_status = ECHILD;
2583 if ((facility == 0) && (msg_no < 8)) {
2584 /* These are not real VMS status codes so assume that they are
2585 ** already UNIX status codes
2587 unix_status = msg_no;
2593 /* Translate a POSIX exit code to a UNIX exit code */
2594 if ((facility == C_FACILITY_NO) && ((msg_no & 0xA000) == 0xA000)) {
2595 unix_status = (msg_no & 0x07F8) >> 3;
2599 /* Documented traditional behavior for handling VMS child exits */
2600 /*--------------------------------------------------------------*/
2601 if (child_flag != 0) {
2603 /* Success / Informational return 0 */
2604 /*----------------------------------*/
2605 if (msg_no & STS$K_SUCCESS)
2608 /* Warning returns 1 */
2609 /*-------------------*/
2610 if ((msg_no & (STS$K_ERROR | STS$K_SEVERE)) == 0)
2613 /* Everything else pass through the severity bits */
2614 /*------------------------------------------------*/
2615 return (msg_no & STS$M_SEVERITY);
2618 /* Normal VMS status to ERRNO mapping attempt */
2619 /*--------------------------------------------*/
2620 switch(msg_status) {
2621 /* case RMS$_EOF: */ /* End of File */
2622 case RMS$_FNF: /* File Not Found */
2623 case RMS$_DNF: /* Dir Not Found */
2624 unix_status = ENOENT;
2626 case RMS$_RNF: /* Record Not Found */
2627 unix_status = ESRCH;
2630 unix_status = ENOTDIR;
2633 unix_status = ENODEV;
2638 unix_status = EBADF;
2641 unix_status = EEXIST;
2645 case LIB$_INVSTRDES:
2647 case LIB$_NOSUCHSYM:
2648 case LIB$_INVSYMNAM:
2650 unix_status = EINVAL;
2656 unix_status = E2BIG;
2658 case RMS$_PRV: /* No privilege */
2659 case RMS$_ACC: /* ACP file access failed */
2660 case RMS$_WLK: /* Device write locked */
2661 unix_status = EACCES;
2663 case RMS$_MKD: /* Failed to mark for delete */
2664 unix_status = EPERM;
2666 /* case RMS$_NMF: */ /* No more files */
2674 /* Try to guess at what VMS error status should go with a UNIX errno
2675 * value. This is hard to do as there could be many possible VMS
2676 * error statuses that caused the errno value to be set.
2679 int Perl_unix_status_to_vms(int unix_status)
2681 int test_unix_status;
2683 /* Trivial cases first */
2684 /*---------------------*/
2685 if (unix_status == EVMSERR)
2688 /* Is vaxc$errno sane? */
2689 /*---------------------*/
2690 test_unix_status = Perl_vms_status_to_unix(vaxc$errno, 0);
2691 if (test_unix_status == unix_status)
2694 /* If way out of range, must be VMS code already */
2695 /*-----------------------------------------------*/
2696 if (unix_status > EVMSERR)
2699 /* If out of range, punt */
2700 /*-----------------------*/
2701 if (unix_status > __ERRNO_MAX)
2705 /* Ok, now we have to do it the hard way. */
2706 /*----------------------------------------*/
2707 switch(unix_status) {
2708 case 0: return SS$_NORMAL;
2709 case EPERM: return SS$_NOPRIV;
2710 case ENOENT: return SS$_NOSUCHOBJECT;
2711 case ESRCH: return SS$_UNREACHABLE;
2712 case EINTR: return SS$_ABORT;
2715 case E2BIG: return SS$_BUFFEROVF;
2717 case EBADF: return RMS$_IFI;
2718 case ECHILD: return SS$_NONEXPR;
2720 case ENOMEM: return SS$_INSFMEM;
2721 case EACCES: return SS$_FILACCERR;
2722 case EFAULT: return SS$_ACCVIO;
2724 case EBUSY: return SS$_DEVOFFLINE;
2725 case EEXIST: return RMS$_FEX;
2727 case ENODEV: return SS$_NOSUCHDEV;
2728 case ENOTDIR: return RMS$_DIR;
2730 case EINVAL: return SS$_INVARG;
2736 case ENOSPC: return SS$_DEVICEFULL;
2737 case ESPIPE: return LIB$_INVARG;
2742 case ERANGE: return LIB$_INVARG;
2743 /* case EWOULDBLOCK */
2744 /* case EINPROGRESS */
2747 /* case EDESTADDRREQ */
2749 /* case EPROTOTYPE */
2750 /* case ENOPROTOOPT */
2751 /* case EPROTONOSUPPORT */
2752 /* case ESOCKTNOSUPPORT */
2753 /* case EOPNOTSUPP */
2754 /* case EPFNOSUPPORT */
2755 /* case EAFNOSUPPORT */
2756 /* case EADDRINUSE */
2757 /* case EADDRNOTAVAIL */
2759 /* case ENETUNREACH */
2760 /* case ENETRESET */
2761 /* case ECONNABORTED */
2762 /* case ECONNRESET */
2765 case ENOTCONN: return SS$_CLEARED;
2766 /* case ESHUTDOWN */
2767 /* case ETOOMANYREFS */
2768 /* case ETIMEDOUT */
2769 /* case ECONNREFUSED */
2771 /* case ENAMETOOLONG */
2772 /* case EHOSTDOWN */
2773 /* case EHOSTUNREACH */
2774 /* case ENOTEMPTY */
2786 /* case ECANCELED */
2790 return SS$_UNSUPPORTED;
2796 /* case EABANDONED */
2798 return SS$_ABORT; /* punt */
2801 return SS$_ABORT; /* Should not get here */
2805 /* default piping mailbox size */
2806 #define PERL_BUFSIZ 512
2810 create_mbx(unsigned short int *chan, struct dsc$descriptor_s *namdsc)
2812 unsigned long int mbxbufsiz;
2813 static unsigned long int syssize = 0;
2814 unsigned long int dviitm = DVI$_DEVNAM;
2815 char csize[LNM$C_NAMLENGTH+1];
2819 unsigned long syiitm = SYI$_MAXBUF;
2821 * Get the SYSGEN parameter MAXBUF
2823 * If the logical 'PERL_MBX_SIZE' is defined
2824 * use the value of the logical instead of PERL_BUFSIZ, but
2825 * keep the size between 128 and MAXBUF.
2828 _ckvmssts_noperl(lib$getsyi(&syiitm, &syssize, 0, 0, 0, 0));
2831 if (vmstrnenv("PERL_MBX_SIZE", csize, 0, fildev, 0)) {
2832 mbxbufsiz = atoi(csize);
2834 mbxbufsiz = PERL_BUFSIZ;
2836 if (mbxbufsiz < 128) mbxbufsiz = 128;
2837 if (mbxbufsiz > syssize) mbxbufsiz = syssize;
2839 _ckvmssts_noperl(sts = sys$crembx(0,chan,mbxbufsiz,mbxbufsiz,0,0,0));
2841 sts = lib$getdvi(&dviitm, chan, NULL, NULL, namdsc, &namdsc->dsc$w_length);
2842 _ckvmssts_noperl(sts);
2843 namdsc->dsc$a_pointer[namdsc->dsc$w_length] = '\0';
2845 } /* end of create_mbx() */
2848 /*{{{ my_popen and my_pclose*/
2850 typedef struct _iosb IOSB;
2851 typedef struct _iosb* pIOSB;
2852 typedef struct _pipe Pipe;
2853 typedef struct _pipe* pPipe;
2854 typedef struct pipe_details Info;
2855 typedef struct pipe_details* pInfo;
2856 typedef struct _srqp RQE;
2857 typedef struct _srqp* pRQE;
2858 typedef struct _tochildbuf CBuf;
2859 typedef struct _tochildbuf* pCBuf;
2862 unsigned short status;
2863 unsigned short count;
2864 unsigned long dvispec;
2867 #pragma member_alignment save
2868 #pragma nomember_alignment quadword
2869 struct _srqp { /* VMS self-relative queue entry */
2870 unsigned long qptr[2];
2872 #pragma member_alignment restore
2873 static RQE RQE_ZERO = {0,0};
2875 struct _tochildbuf {
2878 unsigned short size;
2886 unsigned short chan_in;
2887 unsigned short chan_out;
2889 unsigned int bufsize;
2901 #if defined(PERL_IMPLICIT_CONTEXT)
2902 void *thx; /* Either a thread or an interpreter */
2903 /* pointer, depending on how we're built */
2911 PerlIO *fp; /* file pointer to pipe mailbox */
2912 int useFILE; /* using stdio, not perlio */
2913 int pid; /* PID of subprocess */
2914 int mode; /* == 'r' if pipe open for reading */
2915 int done; /* subprocess has completed */
2916 int waiting; /* waiting for completion/closure */
2917 int closing; /* my_pclose is closing this pipe */
2918 unsigned long completion; /* termination status of subprocess */
2919 pPipe in; /* pipe in to sub */
2920 pPipe out; /* pipe out of sub */
2921 pPipe err; /* pipe of sub's sys$error */
2922 int in_done; /* true when in pipe finished */
2925 unsigned short xchan; /* channel to debug xterm */
2926 unsigned short xchan_valid; /* channel is assigned */
2929 struct exit_control_block
2931 struct exit_control_block *flink;
2932 unsigned long int (*exit_routine)();
2933 unsigned long int arg_count;
2934 unsigned long int *status_address;
2935 unsigned long int exit_status;
2938 typedef struct _closed_pipes Xpipe;
2939 typedef struct _closed_pipes* pXpipe;
2941 struct _closed_pipes {
2942 int pid; /* PID of subprocess */
2943 unsigned long completion; /* termination status of subprocess */
2945 #define NKEEPCLOSED 50
2946 static Xpipe closed_list[NKEEPCLOSED];
2947 static int closed_index = 0;
2948 static int closed_num = 0;
2950 #define RETRY_DELAY "0 ::0.20"
2951 #define MAX_RETRY 50
2953 static int pipe_ef = 0; /* first call to safe_popen inits these*/
2954 static unsigned long mypid;
2955 static unsigned long delaytime[2];
2957 static pInfo open_pipes = NULL;
2958 static $DESCRIPTOR(nl_desc, "NL:");
2960 #define PIPE_COMPLETION_WAIT 30 /* seconds, for EOF/FORCEX wait */
2964 static unsigned long int
2968 unsigned long int retsts = SS$_NORMAL, abort = SS$_TIMEOUT;
2969 int sts, did_stuff, need_eof, j;
2972 * Flush any pending i/o, but since we are in process run-down, be
2973 * careful about referencing PerlIO structures that may already have
2974 * been deallocated. We may not even have an interpreter anymore.
2979 #if defined(PERL_IMPLICIT_CONTEXT)
2980 /* We need to use the Perl context of the thread that created */
2984 aTHX = info->err->thx;
2986 aTHX = info->out->thx;
2988 aTHX = info->in->thx;
2991 #if defined(USE_ITHREADS)
2994 && PL_perlio_fd_refcnt)
2995 PerlIO_flush(info->fp);
2997 fflush((FILE *)info->fp);
3003 next we try sending an EOF...ignore if doesn't work, make sure we
3011 _ckvmssts_noperl(sys$setast(0));
3012 if (info->in && !info->in->shut_on_empty) {
3013 _ckvmssts_noperl(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
3018 _ckvmssts_noperl(sys$setast(1));
3022 /* wait for EOF to have effect, up to ~ 30 sec [default] */
3024 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
3029 _ckvmssts_noperl(sys$setast(0));
3030 if (info->waiting && info->done)
3032 nwait += info->waiting;
3033 _ckvmssts_noperl(sys$setast(1));
3043 _ckvmssts_noperl(sys$setast(0));
3044 if (!info->done) { /* Tap them gently on the shoulder . . .*/
3045 sts = sys$forcex(&info->pid,0,&abort);
3046 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
3049 _ckvmssts_noperl(sys$setast(1));
3053 /* again, wait for effect */
3055 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
3060 _ckvmssts_noperl(sys$setast(0));
3061 if (info->waiting && info->done)
3063 nwait += info->waiting;
3064 _ckvmssts_noperl(sys$setast(1));
3073 _ckvmssts_noperl(sys$setast(0));
3074 if (!info->done) { /* We tried to be nice . . . */
3075 sts = sys$delprc(&info->pid,0);
3076 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
3077 info->done = 1; /* sys$delprc is as done as we're going to get. */
3079 _ckvmssts_noperl(sys$setast(1));
3085 #if defined(PERL_IMPLICIT_CONTEXT)
3086 /* We need to use the Perl context of the thread that created */
3089 if (open_pipes->err)
3090 aTHX = open_pipes->err->thx;
3091 else if (open_pipes->out)
3092 aTHX = open_pipes->out->thx;
3093 else if (open_pipes->in)
3094 aTHX = open_pipes->in->thx;
3096 if ((sts = my_pclose(open_pipes->fp)) == -1) retsts = vaxc$errno;
3097 else if (!(sts & 1)) retsts = sts;
3102 static struct exit_control_block pipe_exitblock =
3103 {(struct exit_control_block *) 0,
3104 pipe_exit_routine, 0, &pipe_exitblock.exit_status, 0};
3106 static void pipe_mbxtofd_ast(pPipe p);
3107 static void pipe_tochild1_ast(pPipe p);
3108 static void pipe_tochild2_ast(pPipe p);
3111 popen_completion_ast(pInfo info)
3113 pInfo i = open_pipes;
3118 info->completion &= 0x0FFFFFFF; /* strip off "control" field */
3119 closed_list[closed_index].pid = info->pid;
3120 closed_list[closed_index].completion = info->completion;
3122 if (closed_index == NKEEPCLOSED)
3127 if (i == info) break;
3130 if (!i) return; /* unlinked, probably freed too */
3135 Writing to subprocess ...
3136 if my_pclose'd: EOF already sent, should shutdown chan_in part of pipe
3138 chan_out may be waiting for "done" flag, or hung waiting
3139 for i/o completion to child...cancel the i/o. This will
3140 put it into "snarf mode" (done but no EOF yet) that discards
3143 Output from subprocess (stdout, stderr) needs to be flushed and
3144 shut down. We try sending an EOF, but if the mbx is full the pipe
3145 routine should still catch the "shut_on_empty" flag, telling it to
3146 use immediate-style reads so that "mbx empty" -> EOF.
3150 if (info->in && !info->in_done) { /* only for mode=w */
3151 if (info->in->shut_on_empty && info->in->need_wake) {
3152 info->in->need_wake = FALSE;
3153 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,info->in,0));
3155 _ckvmssts_noperl(sys$cancel(info->in->chan_out));
3159 if (info->out && !info->out_done) { /* were we also piping output? */
3160 info->out->shut_on_empty = TRUE;
3161 iss = sys$qio(0,info->out->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
3162 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
3163 _ckvmssts_noperl(iss);
3166 if (info->err && !info->err_done) { /* we were piping stderr */
3167 info->err->shut_on_empty = TRUE;
3168 iss = sys$qio(0,info->err->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
3169 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
3170 _ckvmssts_noperl(iss);
3172 _ckvmssts_noperl(sys$setef(pipe_ef));
3176 static unsigned long int setup_cmddsc(pTHX_ const char *cmd, int check_img, int *suggest_quote, struct dsc$descriptor_s **pvmscmd);
3177 static void vms_execfree(struct dsc$descriptor_s *vmscmd);
3180 we actually differ from vmstrnenv since we use this to
3181 get the RMS IFI to check if SYS$OUTPUT and SYS$ERROR *really*
3182 are pointing to the same thing
3185 static unsigned short
3186 popen_translate(pTHX_ char *logical, char *result)
3189 $DESCRIPTOR(d_table,"LNM$PROCESS_TABLE");
3190 $DESCRIPTOR(d_log,"");
3192 unsigned short length;
3193 unsigned short code;
3195 unsigned short *retlenaddr;
3197 unsigned short l, ifi;
3199 d_log.dsc$a_pointer = logical;
3200 d_log.dsc$w_length = strlen(logical);
3202 itmlst[0].code = LNM$_STRING;
3203 itmlst[0].length = 255;
3204 itmlst[0].buffer_addr = result;
3205 itmlst[0].retlenaddr = &l;
3208 itmlst[1].length = 0;
3209 itmlst[1].buffer_addr = 0;
3210 itmlst[1].retlenaddr = 0;
3212 iss = sys$trnlnm(0, &d_table, &d_log, 0, itmlst);
3213 if (iss == SS$_NOLOGNAM) {
3217 if (!(iss&1)) lib$signal(iss);
3220 logicals for PPFs have a 4 byte prefix ESC+NUL+(RMS IFI)
3221 strip it off and return the ifi, if any
3224 if (result[0] == 0x1b && result[1] == 0x00) {
3225 memmove(&ifi,result+2,2);
3226 strcpy(result,result+4);
3228 return ifi; /* this is the RMS internal file id */
3231 static void pipe_infromchild_ast(pPipe p);
3234 I'm using LIB$(GET|FREE)_VM here so that we can allocate and deallocate
3235 inside an AST routine without worrying about reentrancy and which Perl
3236 memory allocator is being used.
3238 We read data and queue up the buffers, then spit them out one at a
3239 time to the output mailbox when the output mailbox is ready for one.
3242 #define INITIAL_TOCHILDQUEUE 2
3245 pipe_tochild_setup(pTHX_ char *rmbx, char *wmbx)
3249 char mbx1[64], mbx2[64];
3250 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3251 DSC$K_CLASS_S, mbx1},
3252 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
3253 DSC$K_CLASS_S, mbx2};
3254 unsigned int dviitm = DVI$_DEVBUFSIZ;
3258 _ckvmssts_noperl(lib$get_vm(&n, &p));
3260 create_mbx(&p->chan_in , &d_mbx1);
3261 create_mbx(&p->chan_out, &d_mbx2);
3262 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3265 p->shut_on_empty = FALSE;
3266 p->need_wake = FALSE;
3269 p->iosb.status = SS$_NORMAL;
3270 p->iosb2.status = SS$_NORMAL;
3276 #ifdef PERL_IMPLICIT_CONTEXT
3280 n = sizeof(CBuf) + p->bufsize;
3282 for (j = 0; j < INITIAL_TOCHILDQUEUE; j++) {
3283 _ckvmssts_noperl(lib$get_vm(&n, &b));
3284 b->buf = (char *) b + sizeof(CBuf);
3285 _ckvmssts_noperl(lib$insqhi(b, &p->free));
3288 pipe_tochild2_ast(p);
3289 pipe_tochild1_ast(p);
3295 /* reads the MBX Perl is writing, and queues */
3298 pipe_tochild1_ast(pPipe p)
3301 int iss = p->iosb.status;
3302 int eof = (iss == SS$_ENDOFFILE);
3304 #ifdef PERL_IMPLICIT_CONTEXT
3310 p->shut_on_empty = TRUE;
3312 _ckvmssts_noperl(sys$dassgn(p->chan_in));
3314 _ckvmssts_noperl(iss);
3318 b->size = p->iosb.count;
3319 _ckvmssts_noperl(sts = lib$insqhi(b, &p->wait));
3321 p->need_wake = FALSE;
3322 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,p,0));
3325 p->retry = 1; /* initial call */
3328 if (eof) { /* flush the free queue, return when done */
3329 int n = sizeof(CBuf) + p->bufsize;
3331 iss = lib$remqti(&p->free, &b);
3332 if (iss == LIB$_QUEWASEMP) return;
3333 _ckvmssts_noperl(iss);
3334 _ckvmssts_noperl(lib$free_vm(&n, &b));
3338 iss = lib$remqti(&p->free, &b);
3339 if (iss == LIB$_QUEWASEMP) {
3340 int n = sizeof(CBuf) + p->bufsize;
3341 _ckvmssts_noperl(lib$get_vm(&n, &b));
3342 b->buf = (char *) b + sizeof(CBuf);
3344 _ckvmssts_noperl(iss);
3348 iss = sys$qio(0,p->chan_in,
3349 IO$_READVBLK|(p->shut_on_empty ? IO$M_NOWAIT : 0),
3351 pipe_tochild1_ast, p, b->buf, p->bufsize, 0, 0, 0, 0);
3352 if (iss == SS$_ENDOFFILE && p->shut_on_empty) iss = SS$_NORMAL;
3353 _ckvmssts_noperl(iss);
3357 /* writes queued buffers to output, waits for each to complete before
3361 pipe_tochild2_ast(pPipe p)
3364 int iss = p->iosb2.status;
3365 int n = sizeof(CBuf) + p->bufsize;
3366 int done = (p->info && p->info->done) ||
3367 iss == SS$_CANCEL || iss == SS$_ABORT;
3368 #if defined(PERL_IMPLICIT_CONTEXT)
3373 if (p->type) { /* type=1 has old buffer, dispose */
3374 if (p->shut_on_empty) {
3375 _ckvmssts_noperl(lib$free_vm(&n, &b));
3377 _ckvmssts_noperl(lib$insqhi(b, &p->free));
3382 iss = lib$remqti(&p->wait, &b);
3383 if (iss == LIB$_QUEWASEMP) {
3384 if (p->shut_on_empty) {
3386 _ckvmssts_noperl(sys$dassgn(p->chan_out));
3387 *p->pipe_done = TRUE;
3388 _ckvmssts_noperl(sys$setef(pipe_ef));
3390 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF,
3391 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3395 p->need_wake = TRUE;
3398 _ckvmssts_noperl(iss);
3405 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF,
3406 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3408 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEVBLK,
3409 &p->iosb2, pipe_tochild2_ast, p, b->buf, b->size, 0, 0, 0, 0));
3418 pipe_infromchild_setup(pTHX_ char *rmbx, char *wmbx)
3421 char mbx1[64], mbx2[64];
3422 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3423 DSC$K_CLASS_S, mbx1},
3424 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
3425 DSC$K_CLASS_S, mbx2};
3426 unsigned int dviitm = DVI$_DEVBUFSIZ;
3428 int n = sizeof(Pipe);
3429 _ckvmssts_noperl(lib$get_vm(&n, &p));
3430 create_mbx(&p->chan_in , &d_mbx1);
3431 create_mbx(&p->chan_out, &d_mbx2);
3433 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3434 n = p->bufsize * sizeof(char);
3435 _ckvmssts_noperl(lib$get_vm(&n, &p->buf));
3436 p->shut_on_empty = FALSE;
3439 p->iosb.status = SS$_NORMAL;
3440 #if defined(PERL_IMPLICIT_CONTEXT)
3443 pipe_infromchild_ast(p);
3451 pipe_infromchild_ast(pPipe p)
3453 int iss = p->iosb.status;
3454 int eof = (iss == SS$_ENDOFFILE);
3455 int myeof = (eof && (p->iosb.dvispec == mypid || p->iosb.dvispec == 0));
3456 int kideof = (eof && (p->iosb.dvispec == p->info->pid));
3457 #if defined(PERL_IMPLICIT_CONTEXT)
3461 if (p->info && p->info->closing && p->chan_out) { /* output shutdown */
3462 _ckvmssts_noperl(sys$dassgn(p->chan_out));
3467 input shutdown if EOF from self (done or shut_on_empty)
3468 output shutdown if closing flag set (my_pclose)
3469 send data/eof from child or eof from self
3470 otherwise, re-read (snarf of data from child)
3475 if (myeof && p->chan_in) { /* input shutdown */
3476 _ckvmssts_noperl(sys$dassgn(p->chan_in));
3481 if (myeof || kideof) { /* pass EOF to parent */
3482 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF, &p->iosb,
3483 pipe_infromchild_ast, p,
3486 } else if (eof) { /* eat EOF --- fall through to read*/
3488 } else { /* transmit data */
3489 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEVBLK,&p->iosb,
3490 pipe_infromchild_ast,p,
3491 p->buf, p->iosb.count, 0, 0, 0, 0));
3497 /* everything shut? flag as done */
3499 if (!p->chan_in && !p->chan_out) {
3500 *p->pipe_done = TRUE;
3501 _ckvmssts_noperl(sys$setef(pipe_ef));
3505 /* write completed (or read, if snarfing from child)
3506 if still have input active,
3507 queue read...immediate mode if shut_on_empty so we get EOF if empty
3509 check if Perl reading, generate EOFs as needed
3515 iss = sys$qio(0,p->chan_in,IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0),&p->iosb,
3516 pipe_infromchild_ast,p,
3517 p->buf, p->bufsize, 0, 0, 0, 0);
3518 if (p->shut_on_empty && iss == SS$_ENDOFFILE) iss = SS$_NORMAL;
3519 _ckvmssts_noperl(iss);
3520 } else { /* send EOFs for extra reads */
3521 p->iosb.status = SS$_ENDOFFILE;
3522 p->iosb.dvispec = 0;
3523 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_SETMODE|IO$M_READATTN,
3525 pipe_infromchild_ast, p, 0, 0, 0, 0));
3531 pipe_mbxtofd_setup(pTHX_ int fd, char *out)
3535 unsigned long dviitm = DVI$_DEVBUFSIZ;
3537 struct dsc$descriptor_s d_mbx = {sizeof mbx, DSC$K_DTYPE_T,
3538 DSC$K_CLASS_S, mbx};
3539 int n = sizeof(Pipe);
3541 /* things like terminals and mbx's don't need this filter */
3542 if (fd && fstat(fd,&s) == 0) {
3543 unsigned long dviitm = DVI$_DEVCHAR, devchar;
3545 unsigned short dev_len;
3546 struct dsc$descriptor_s d_dev;
3548 struct item_list_3 items[3];
3550 unsigned short dvi_iosb[4];
3552 cptr = getname(fd, out, 1);
3553 if (cptr == NULL) _ckvmssts_noperl(SS$_NOSUCHDEV);
3554 d_dev.dsc$a_pointer = out;
3555 d_dev.dsc$w_length = strlen(out);
3556 d_dev.dsc$b_dtype = DSC$K_DTYPE_T;
3557 d_dev.dsc$b_class = DSC$K_CLASS_S;
3560 items[0].code = DVI$_DEVCHAR;
3561 items[0].bufadr = &devchar;
3562 items[0].retadr = NULL;
3564 items[1].code = DVI$_FULLDEVNAM;
3565 items[1].bufadr = device;
3566 items[1].retadr = &dev_len;
3570 status = sys$getdviw
3571 (NO_EFN, 0, &d_dev, items, dvi_iosb, NULL, NULL, NULL);
3572 _ckvmssts_noperl(status);
3573 if ($VMS_STATUS_SUCCESS(dvi_iosb[0])) {
3574 device[dev_len] = 0;
3576 if (!(devchar & DEV$M_DIR)) {
3577 strcpy(out, device);
3583 _ckvmssts_noperl(lib$get_vm(&n, &p));
3584 p->fd_out = dup(fd);
3585 create_mbx(&p->chan_in, &d_mbx);
3586 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3587 n = (p->bufsize+1) * sizeof(char);
3588 _ckvmssts_noperl(lib$get_vm(&n, &p->buf));
3589 p->shut_on_empty = FALSE;
3594 _ckvmssts_noperl(sys$qio(0, p->chan_in, IO$_READVBLK, &p->iosb,
3595 pipe_mbxtofd_ast, p,
3596 p->buf, p->bufsize, 0, 0, 0, 0));
3602 pipe_mbxtofd_ast(pPipe p)
3604 int iss = p->iosb.status;
3605 int done = p->info->done;
3607 int eof = (iss == SS$_ENDOFFILE);
3608 int myeof = eof && ((p->iosb.dvispec == mypid)||(p->iosb.dvispec == 0));
3609 int err = !(iss&1) && !eof;
3610 #if defined(PERL_IMPLICIT_CONTEXT)
3614 if (done && myeof) { /* end piping */
3616 sys$dassgn(p->chan_in);
3617 *p->pipe_done = TRUE;
3618 _ckvmssts_noperl(sys$setef(pipe_ef));
3622 if (!err && !eof) { /* good data to send to file */
3623 p->buf[p->iosb.count] = '\n';
3624 iss2 = write(p->fd_out, p->buf, p->iosb.count+1);
3627 if (p->retry < MAX_RETRY) {
3628 _ckvmssts_noperl(sys$setimr(0,delaytime,pipe_mbxtofd_ast,p));
3634 _ckvmssts_noperl(iss);
3638 iss = sys$qio(0, p->chan_in, IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0), &p->iosb,
3639 pipe_mbxtofd_ast, p,
3640 p->buf, p->bufsize, 0, 0, 0, 0);
3641 if (p->shut_on_empty && (iss == SS$_ENDOFFILE)) iss = SS$_NORMAL;
3642 _ckvmssts_noperl(iss);
3646 typedef struct _pipeloc PLOC;
3647 typedef struct _pipeloc* pPLOC;
3651 char dir[NAM$C_MAXRSS+1];
3653 static pPLOC head_PLOC = 0;
3656 free_pipelocs(pTHX_ void *head)
3659 pPLOC *pHead = (pPLOC *)head;
3671 store_pipelocs(pTHX)
3680 char temp[NAM$C_MAXRSS+1];
3684 free_pipelocs(aTHX_ &head_PLOC);
3686 /* the . directory from @INC comes last */
3688 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3689 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3690 p->next = head_PLOC;
3692 strcpy(p->dir,"./");
3694 /* get the directory from $^X */
3696 unixdir = PerlMem_malloc(VMS_MAXRSS);
3697 if (unixdir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3699 #ifdef PERL_IMPLICIT_CONTEXT
3700 if (aTHX && PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
3702 if (PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
3704 strcpy(temp, PL_origargv[0]);
3705 x = strrchr(temp,']');
3707 x = strrchr(temp,'>');
3709 /* It could be a UNIX path */
3710 x = strrchr(temp,'/');
3716 /* Got a bare name, so use default directory */
3721 if ((tounixpath_utf8(temp, unixdir, NULL)) != NULL) {
3722 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3723 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3724 p->next = head_PLOC;
3726 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3727 p->dir[NAM$C_MAXRSS] = '\0';
3731 /* reverse order of @INC entries, skip "." since entered above */
3733 #ifdef PERL_IMPLICIT_CONTEXT
3736 if (PL_incgv) av = GvAVn(PL_incgv);
3738 for (i = 0; av && i <= AvFILL(av); i++) {
3739 dirsv = *av_fetch(av,i,TRUE);
3741 if (SvROK(dirsv)) continue;
3742 dir = SvPVx(dirsv,n_a);
3743 if (strcmp(dir,".") == 0) continue;
3744 if ((tounixpath_utf8(dir, unixdir, NULL)) == NULL)
3747 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3748 p->next = head_PLOC;
3750 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3751 p->dir[NAM$C_MAXRSS] = '\0';
3754 /* most likely spot (ARCHLIB) put first in the list */
3757 if ((tounixpath_utf8(ARCHLIB_EXP, unixdir, NULL)) != NULL) {
3758 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3759 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3760 p->next = head_PLOC;
3762 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3763 p->dir[NAM$C_MAXRSS] = '\0';
3766 PerlMem_free(unixdir);
3770 Perl_cando_by_name_int
3771 (pTHX_ I32 bit, bool effective, const char *fname, int opts);
3772 #if !defined(PERL_IMPLICIT_CONTEXT)
3773 #define cando_by_name_int Perl_cando_by_name_int
3775 #define cando_by_name_int(a,b,c,d) Perl_cando_by_name_int(aTHX_ a,b,c,d)
3781 static int vmspipe_file_status = 0;
3782 static char vmspipe_file[NAM$C_MAXRSS+1];
3784 /* already found? Check and use ... need read+execute permission */
3786 if (vmspipe_file_status == 1) {
3787 if (cando_by_name_int(S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)
3788 && cando_by_name_int
3789 (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) {
3790 return vmspipe_file;
3792 vmspipe_file_status = 0;
3795 /* scan through stored @INC, $^X */
3797 if (vmspipe_file_status == 0) {
3798 char file[NAM$C_MAXRSS+1];
3799 pPLOC p = head_PLOC;
3804 strcpy(file, p->dir);
3805 dirlen = strlen(file);
3806 strncat(file, "vmspipe.com",NAM$C_MAXRSS - dirlen);
3807 file[NAM$C_MAXRSS] = '\0';
3810 exp_res = int_rmsexpand_tovms(file, vmspipe_file, 0);
3811 if (!exp_res) continue;
3813 if (cando_by_name_int
3814 (S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)
3815 && cando_by_name_int
3816 (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) {
3817 vmspipe_file_status = 1;
3818 return vmspipe_file;
3821 vmspipe_file_status = -1; /* failed, use tempfiles */
3828 vmspipe_tempfile(pTHX)
3830 char file[NAM$C_MAXRSS+1];
3832 static int index = 0;
3836 /* create a tempfile */
3838 /* we can't go from W, shr=get to R, shr=get without
3839 an intermediate vulnerable state, so don't bother trying...
3841 and lib$spawn doesn't shr=put, so have to close the write
3843 So... match up the creation date/time and the FID to
3844 make sure we're dealing with the same file
3849 if (!decc_filename_unix_only) {
3850 sprintf(file,"sys$scratch:perlpipe_%08.8x_%d.com",mypid,index);
3851 fp = fopen(file,"w");
3853 sprintf(file,"sys$login:perlpipe_%08.8x_%d.com",mypid,index);
3854 fp = fopen(file,"w");
3856 sprintf(file,"sys$disk:[]perlpipe_%08.8x_%d.com",mypid,index);
3857 fp = fopen(file,"w");
3862 sprintf(file,"/tmp/perlpipe_%08.8x_%d.com",mypid,index);
3863 fp = fopen(file,"w");
3865 sprintf(file,"/sys$login/perlpipe_%08.8x_%d.com",mypid,index);
3866 fp = fopen(file,"w");
3868 sprintf(file,"./perlpipe_%08.8x_%d.com",mypid,index);
3869 fp = fopen(file,"w");
3873 if (!fp) return 0; /* we're hosed */
3875 fprintf(fp,"$! 'f$verify(0)'\n");
3876 fprintf(fp,"$! --- protect against nonstandard definitions ---\n");
3877 fprintf(fp,"$ perl_cfile = f$environment(\"procedure\")\n");
3878 fprintf(fp,"$ perl_define = \"define/nolog\"\n");
3879 fprintf(fp,"$ perl_on = \"set noon\"\n");
3880 fprintf(fp,"$ perl_exit = \"exit\"\n");
3881 fprintf(fp,"$ perl_del = \"delete\"\n");
3882 fprintf(fp,"$ pif = \"if\"\n");
3883 fprintf(fp,"$! --- define i/o redirection (sys$output set by lib$spawn)\n");
3884 fprintf(fp,"$ pif perl_popen_in .nes. \"\" then perl_define/user/name_attributes=confine sys$input 'perl_popen_in'\n");
3885 fprintf(fp,"$ pif perl_popen_err .nes. \"\" then perl_define/user/name_attributes=confine sys$error 'perl_popen_err'\n");
3886 fprintf(fp,"$ pif perl_popen_out .nes. \"\" then perl_define sys$output 'perl_popen_out'\n");
3887 fprintf(fp,"$! --- build command line to get max possible length\n");
3888 fprintf(fp,"$c=perl_popen_cmd0\n");
3889 fprintf(fp,"$c=c+perl_popen_cmd1\n");
3890 fprintf(fp,"$c=c+perl_popen_cmd2\n");
3891 fprintf(fp,"$x=perl_popen_cmd3\n");
3892 fprintf(fp,"$c=c+x\n");
3893 fprintf(fp,"$ perl_on\n");
3894 fprintf(fp,"$ 'c'\n");
3895 fprintf(fp,"$ perl_status = $STATUS\n");
3896 fprintf(fp,"$ perl_del 'perl_cfile'\n");
3897 fprintf(fp,"$ perl_exit 'perl_status'\n");
3900 fgetname(fp, file, 1);
3901 fstat(fileno(fp), (struct stat *)&s0);
3904 if (decc_filename_unix_only)
3905 int_tounixspec(file, file, NULL);
3906 fp = fopen(file,"r","shr=get");
3908 fstat(fileno(fp), (struct stat *)&s1);
3910 cmp_result = VMS_INO_T_COMPARE(s0.crtl_stat.st_ino, s1.crtl_stat.st_ino);
3911 if ((cmp_result != 0) && (s0.st_ctime != s1.st_ctime)) {
3920 static int vms_is_syscommand_xterm(void)
3922 const static struct dsc$descriptor_s syscommand_dsc =
3923 { 11, DSC$K_DTYPE_T, DSC$K_CLASS_S, "SYS$COMMAND" };
3925 const static struct dsc$descriptor_s decwdisplay_dsc =
3926 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "DECW$DISPLAY" };
3928 struct item_list_3 items[2];
3929 unsigned short dvi_iosb[4];
3930 unsigned long devchar;
3931 unsigned long devclass;
3934 /* Very simple check to guess if sys$command is a decterm? */
3935 /* First see if the DECW$DISPLAY: device exists */
3937 items[0].code = DVI$_DEVCHAR;
3938 items[0].bufadr = &devchar;
3939 items[0].retadr = NULL;
3943 status = sys$getdviw
3944 (NO_EFN, 0, &decwdisplay_dsc, items, dvi_iosb, NULL, NULL, NULL);
3946 if ($VMS_STATUS_SUCCESS(status)) {
3947 status = dvi_iosb[0];
3950 if (!$VMS_STATUS_SUCCESS(status)) {
3951 SETERRNO(EVMSERR, status);
3955 /* If it does, then for now assume that we are on a workstation */
3956 /* Now verify that SYS$COMMAND is a terminal */
3957 /* for creating the debugger DECTerm */
3960 items[0].code = DVI$_DEVCLASS;
3961 items[0].bufadr = &devclass;
3962 items[0].retadr = NULL;
3966 status = sys$getdviw
3967 (NO_EFN, 0, &syscommand_dsc, items, dvi_iosb, NULL, NULL, NULL);
3969 if ($VMS_STATUS_SUCCESS(status)) {
3970 status = dvi_iosb[0];
3973 if (!$VMS_STATUS_SUCCESS(status)) {
3974 SETERRNO(EVMSERR, status);
3978 if (devclass == DC$_TERM) {
3985 /* If we are on a DECTerm, we can pretend to fork xterms when requested */
3986 static PerlIO * create_forked_xterm(pTHX_ const char *cmd, const char *mode)
3991 char device_name[65];
3992 unsigned short device_name_len;
3993 struct dsc$descriptor_s customization_dsc;
3994 struct dsc$descriptor_s device_name_dsc;
3997 char customization[200];
4001 unsigned short p_chan;
4003 unsigned short iosb[4];
4004 struct item_list_3 items[2];
4005 const char * cust_str =
4006 "DECW$TERMINAL.iconName:\tPerl Dbg\nDECW$TERMINAL.title:\t%40s\n";
4007 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
4008 DSC$K_CLASS_S, mbx1};
4010 /* LIB$FIND_IMAGE_SIGNAL needs a handler */
4011 /*---------------------------------------*/
4012 VAXC$ESTABLISH((__vms_handler)lib$sig_to_ret);
4015 /* Make sure that this is from the Perl debugger */
4016 ret_char = strstr(cmd," xterm ");
4017 if (ret_char == NULL)
4019 cptr = ret_char + 7;
4020 ret_char = strstr(cmd,"tty");
4021 if (ret_char == NULL)
4023 ret_char = strstr(cmd,"sleep");
4024 if (ret_char == NULL)
4027 if (decw_term_port == 0) {
4028 $DESCRIPTOR(filename1_dsc, "DECW$TERMINALSHR12");
4029 $DESCRIPTOR(filename2_dsc, "DECW$TERMINALSHR");
4030 $DESCRIPTOR(decw_term_port_dsc, "DECW$TERM_PORT");
4032 status = lib$find_image_symbol
4034 &decw_term_port_dsc,
4035 (void *)&decw_term_port,
4039 /* Try again with the other image name */
4040 if (!$VMS_STATUS_SUCCESS(status)) {
4042 status = lib$find_image_symbol
4044 &decw_term_port_dsc,
4045 (void *)&decw_term_port,
4054 /* No decw$term_port, give it up */
4055 if (!$VMS_STATUS_SUCCESS(status))
4058 /* Are we on a workstation? */
4059 /* to do: capture the rows / columns and pass their properties */
4060 ret_stat = vms_is_syscommand_xterm();
4064 /* Make the title: */
4065 ret_char = strstr(cptr,"-title");
4066 if (ret_char != NULL) {
4067 while ((*cptr != 0) && (*cptr != '\"')) {
4073 while ((*cptr != 0) && (*cptr != '\"')) {
4086 strcpy(title,"Perl Debug DECTerm");
4088 sprintf(customization, cust_str, title);
4090 customization_dsc.dsc$a_pointer = customization;
4091 customization_dsc.dsc$w_length = strlen(customization);
4092 customization_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
4093 customization_dsc.dsc$b_class = DSC$K_CLASS_S;
4095 device_name_dsc.dsc$a_pointer = device_name;
4096 device_name_dsc.dsc$w_length = sizeof device_name -1;
4097 device_name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
4098 device_name_dsc.dsc$b_class = DSC$K_CLASS_S;
4100 device_name_len = 0;
4102 /* Try to create the window */
4103 status = (*decw_term_port)
4112 if (!$VMS_STATUS_SUCCESS(status)) {
4113 SETERRNO(EVMSERR, status);
4117 device_name[device_name_len] = '\0';
4119 /* Need to set this up to look like a pipe for cleanup */
4121 status = lib$get_vm(&n, &info);
4122 if (!$VMS_STATUS_SUCCESS(status)) {
4123 SETERRNO(ENOMEM, status);
4129 info->completion = 0;
4130 info->closing = FALSE;
4137 info->in_done = TRUE;
4138 info->out_done = TRUE;
4139 info->err_done = TRUE;
4141 /* Assign a channel on this so that it will persist, and not login */
4142 /* We stash this channel in the info structure for reference. */
4143 /* The created xterm self destructs when the last channel is removed */
4144 /* and it appears that perl5db.pl (perl debugger) does this routinely */
4145 /* So leave this assigned. */
4146 device_name_dsc.dsc$w_length = device_name_len;
4147 status = sys$assign(&device_name_dsc,&info->xchan,0,0);
4148 if (!$VMS_STATUS_SUCCESS(status)) {
4149 SETERRNO(EVMSERR, status);
4152 info->xchan_valid = 1;
4154 /* Now create a mailbox to be read by the application */
4156 create_mbx(&p_chan, &d_mbx1);
4158 /* write the name of the created terminal to the mailbox */
4159 status = sys$qiow(NO_EFN, p_chan, IO$_WRITEVBLK|IO$M_NOW,
4160 iosb, NULL, NULL, device_name, device_name_len, 0, 0, 0, 0);
4162 if (!$VMS_STATUS_SUCCESS(status)) {
4163 SETERRNO(EVMSERR, status);
4167 info->fp = PerlIO_open(mbx1, mode);
4169 /* Done with this channel */
4172 /* If any errors, then clean up */
4175 _ckvmssts_noperl(lib$free_vm(&n, &info));
4183 static I32 my_pclose_pinfo(pTHX_ pInfo info);
4186 safe_popen(pTHX_ const char *cmd, const char *in_mode, int *psts)
4188 static int handler_set_up = FALSE;
4190 unsigned long int sts, flags = CLI$M_NOWAIT;
4191 /* The use of a GLOBAL table (as was done previously) rendered
4192 * Perl's qx() or `` unusable from a C<$ SET SYMBOL/SCOPE=NOGLOBAL> DCL
4193 * environment. Hence we've switched to LOCAL symbol table.
4195 unsigned int table = LIB$K_CLI_LOCAL_SYM;
4197 char *p, mode[10], symbol[MAX_DCL_SYMBOL+1], *vmspipe;
4198 char *in, *out, *err, mbx[512];
4200 char tfilebuf[NAM$C_MAXRSS+1];
4202 char cmd_sym_name[20];
4203 struct dsc$descriptor_s d_symbol= {0, DSC$K_DTYPE_T,
4204 DSC$K_CLASS_S, symbol};
4205 struct dsc$descriptor_s vmspipedsc = {0, DSC$K_DTYPE_T,
4207 struct dsc$descriptor_s d_sym_cmd = {0, DSC$K_DTYPE_T,
4208 DSC$K_CLASS_S, cmd_sym_name};
4209 struct dsc$descriptor_s *vmscmd;
4210 $DESCRIPTOR(d_sym_in ,"PERL_POPEN_IN");
4211 $DESCRIPTOR(d_sym_out,"PERL_POPEN_OUT");
4212 $DESCRIPTOR(d_sym_err,"PERL_POPEN_ERR");
4214 /* Check here for Xterm create request. This means looking for
4215 * "3>&1 xterm\b" and "\btty 1>&3\b$" in the command, and that it
4216 * is possible to create an xterm.
4218 if (*in_mode == 'r') {
4221 xterm_fd = create_forked_xterm(aTHX_ cmd, in_mode);
4222 if (xterm_fd != NULL)
4226 if (!head_PLOC) store_pipelocs(aTHX); /* at least TRY to use a static vmspipe file */
4228 /* once-per-program initialization...
4229 note that the SETAST calls and the dual test of pipe_ef
4230 makes sure that only the FIRST thread through here does
4231 the initialization...all other threads wait until it's
4234 Yeah, uglier than a pthread call, it's got all the stuff inline
4235 rather than in a separate routine.
4239 _ckvmssts_noperl(sys$setast(0));
4241 unsigned long int pidcode = JPI$_PID;
4242 $DESCRIPTOR(d_delay, RETRY_DELAY);
4243 _ckvmssts_noperl(lib$get_ef(&pipe_ef));
4244 _ckvmssts_noperl(lib$getjpi(&pidcode,0,0,&mypid,0,0));
4245 _ckvmssts_noperl(sys$bintim(&d_delay, delaytime));
4247 if (!handler_set_up) {
4248 _ckvmssts_noperl(sys$dclexh(&pipe_exitblock));
4249 handler_set_up = TRUE;
4251 _ckvmssts_noperl(sys$setast(1));
4254 /* see if we can find a VMSPIPE.COM */
4257 vmspipe = find_vmspipe(aTHX);
4259 strcpy(tfilebuf+1,vmspipe);
4260 } else { /* uh, oh...we're in tempfile hell */
4261 tpipe = vmspipe_tempfile(aTHX);
4262 if (!tpipe) { /* a fish popular in Boston */
4263 if (ckWARN(WARN_PIPE)) {
4264 Perl_warner(aTHX_ packWARN(WARN_PIPE),"unable to find VMSPIPE.COM for i/o piping");
4268 fgetname(tpipe,tfilebuf+1,1);
4270 vmspipedsc.dsc$a_pointer = tfilebuf;
4271 vmspipedsc.dsc$w_length = strlen(tfilebuf);
4273 sts = setup_cmddsc(aTHX_ cmd,0,0,&vmscmd);
4276 case RMS$_FNF: case RMS$_DNF:
4277 set_errno(ENOENT); break;
4279 set_errno(ENOTDIR); break;
4281 set_errno(ENODEV); break;
4283 set_errno(EACCES); break;
4285 set_errno(EINVAL); break;
4286 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
4287 set_errno(E2BIG); break;
4288 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
4289 _ckvmssts_noperl(sts); /* fall through */
4290 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
4293 set_vaxc_errno(sts);
4294 if (*in_mode != 'n' && ckWARN(WARN_PIPE)) {
4295 Perl_warner(aTHX_ packWARN(WARN_PIPE),"Can't pipe \"%*s\": %s", strlen(cmd), cmd, Strerror(errno));
4301 _ckvmssts_noperl(lib$get_vm(&n, &info));
4303 strcpy(mode,in_mode);
4306 info->completion = 0;
4307 info->closing = FALSE;
4314 info->in_done = TRUE;
4315 info->out_done = TRUE;
4316 info->err_done = TRUE;
4318 info->xchan_valid = 0;
4320 in = PerlMem_malloc(VMS_MAXRSS);
4321 if (in == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4322 out = PerlMem_malloc(VMS_MAXRSS);
4323 if (out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4324 err = PerlMem_malloc(VMS_MAXRSS);
4325 if (err == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4327 in[0] = out[0] = err[0] = '\0';
4329 if ((p = strchr(mode,'F')) != NULL) { /* F -> use FILE* */
4333 if ((p = strchr(mode,'W')) != NULL) { /* W -> wait for completion */
4338 if (*mode == 'r') { /* piping from subroutine */
4340 info->out = pipe_infromchild_setup(aTHX_ mbx,out);
4342 info->out->pipe_done = &info->out_done;
4343 info->out_done = FALSE;
4344 info->out->info = info;
4346 if (!info->useFILE) {
4347 info->fp = PerlIO_open(mbx, mode);
4349 info->fp = (PerlIO *) freopen(mbx, mode, stdin);
4350 Perl_vmssetuserlnm(aTHX_ "SYS$INPUT",mbx);
4353 if (!info->fp && info->out) {
4354 sys$cancel(info->out->chan_out);
4356 while (!info->out_done) {
4358 _ckvmssts_noperl(sys$setast(0));
4359 done = info->out_done;
4360 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4361 _ckvmssts_noperl(sys$setast(1));
4362 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4365 if (info->out->buf) {
4366 n = info->out->bufsize * sizeof(char);
4367 _ckvmssts_noperl(lib$free_vm(&n, &info->out->buf));
4370 _ckvmssts_noperl(lib$free_vm(&n, &info->out));
4372 _ckvmssts_noperl(lib$free_vm(&n, &info));
4377 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4379 info->err->pipe_done = &info->err_done;
4380 info->err_done = FALSE;
4381 info->err->info = info;
4384 } else if (*mode == 'w') { /* piping to subroutine */
4386 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
4388 info->out->pipe_done = &info->out_done;
4389 info->out_done = FALSE;
4390 info->out->info = info;
4393 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4395 info->err->pipe_done = &info->err_done;
4396 info->err_done = FALSE;
4397 info->err->info = info;
4400 info->in = pipe_tochild_setup(aTHX_ in,mbx);
4401 if (!info->useFILE) {
4402 info->fp = PerlIO_open(mbx, mode);
4404 info->fp = (PerlIO *) freopen(mbx, mode, stdout);
4405 Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",mbx);
4409 info->in->pipe_done = &info->in_done;
4410 info->in_done = FALSE;
4411 info->in->info = info;
4415 if (!info->fp && info->in) {
4417 _ckvmssts_noperl(sys$qiow(0,info->in->chan_in, IO$_WRITEOF, 0,
4418 0, 0, 0, 0, 0, 0, 0, 0));
4420 while (!info->in_done) {
4422 _ckvmssts_noperl(sys$setast(0));
4423 done = info->in_done;
4424 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4425 _ckvmssts_noperl(sys$setast(1));
4426 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4429 if (info->in->buf) {
4430 n = info->in->bufsize * sizeof(char);
4431 _ckvmssts_noperl(lib$free_vm(&n, &info->in->buf));
4434 _ckvmssts_noperl(lib$free_vm(&n, &info->in));
4436 _ckvmssts_noperl(lib$free_vm(&n, &info));
4442 } else if (*mode == 'n') { /* separate subprocess, no Perl i/o */
4443 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
4445 info->out->pipe_done = &info->out_done;
4446 info->out_done = FALSE;
4447 info->out->info = info;
4450 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4452 info->err->pipe_done = &info->err_done;
4453 info->err_done = FALSE;
4454 info->err->info = info;
4458 symbol[MAX_DCL_SYMBOL] = '\0';
4460 strncpy(symbol, in, MAX_DCL_SYMBOL);
4461 d_symbol.dsc$w_length = strlen(symbol);
4462 _ckvmssts_noperl(lib$set_symbol(&d_sym_in, &d_symbol, &table));
4464 strncpy(symbol, err, MAX_DCL_SYMBOL);
4465 d_symbol.dsc$w_length = strlen(symbol);
4466 _ckvmssts_noperl(lib$set_symbol(&d_sym_err, &d_symbol, &table));
4468 strncpy(symbol, out, MAX_DCL_SYMBOL);
4469 d_symbol.dsc$w_length = strlen(symbol);
4470 _ckvmssts_noperl(lib$set_symbol(&d_sym_out, &d_symbol, &table));
4472 /* Done with the names for the pipes */
4477 p = vmscmd->dsc$a_pointer;
4478 while (*p == ' ' || *p == '\t') p++; /* remove leading whitespace */
4479 if (*p == '$') p++; /* remove leading $ */
4480 while (*p == ' ' || *p == '\t') p++;
4482 for (j = 0; j < 4; j++) {
4483 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
4484 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
4486 strncpy(symbol, p, MAX_DCL_SYMBOL);
4487 d_symbol.dsc$w_length = strlen(symbol);
4488 _ckvmssts_noperl(lib$set_symbol(&d_sym_cmd, &d_symbol, &table));
4490 if (strlen(p) > MAX_DCL_SYMBOL) {
4491 p += MAX_DCL_SYMBOL;
4496 _ckvmssts_noperl(sys$setast(0));
4497 info->next=open_pipes; /* prepend to list */
4499 _ckvmssts_noperl(sys$setast(1));
4500 /* Omit arg 2 (input file) so the child will get the parent's SYS$INPUT
4501 * and SYS$COMMAND. vmspipe.com will redefine SYS$INPUT, but we'll still
4502 * have SYS$COMMAND if we need it.
4504 _ckvmssts_noperl(lib$spawn(&vmspipedsc, 0, &nl_desc, &flags,
4505 0, &info->pid, &info->completion,
4506 0, popen_completion_ast,info,0,0,0));
4508 /* if we were using a tempfile, close it now */
4510 if (tpipe) fclose(tpipe);
4512 /* once the subprocess is spawned, it has copied the symbols and
4513 we can get rid of ours */
4515 for (j = 0; j < 4; j++) {
4516 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
4517 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
4518 _ckvmssts_noperl(lib$delete_symbol(&d_sym_cmd, &table));
4520 _ckvmssts_noperl(lib$delete_symbol(&d_sym_in, &table));
4521 _ckvmssts_noperl(lib$delete_symbol(&d_sym_err, &table));
4522 _ckvmssts_noperl(lib$delete_symbol(&d_sym_out, &table));
4523 vms_execfree(vmscmd);
4525 #ifdef PERL_IMPLICIT_CONTEXT
4528 PL_forkprocess = info->pid;
4535 _ckvmssts_noperl(sys$setast(0));
4537 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4538 _ckvmssts_noperl(sys$setast(1));
4539 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4541 *psts = info->completion;
4542 /* Caller thinks it is open and tries to close it. */
4543 /* This causes some problems, as it changes the error status */
4544 /* my_pclose(info->fp); */
4546 /* If we did not have a file pointer open, then we have to */
4547 /* clean up here or eventually we will run out of something */
4549 if (info->fp == NULL) {
4550 my_pclose_pinfo(aTHX_ info);
4558 } /* end of safe_popen */
4561 /*{{{ PerlIO *my_popen(char *cmd, char *mode)*/
4563 Perl_my_popen(pTHX_ const char *cmd, const char *mode)
4567 TAINT_PROPER("popen");
4568 PERL_FLUSHALL_FOR_CHILD;
4569 return safe_popen(aTHX_ cmd,mode,&sts);
4575 /* Routine to close and cleanup a pipe info structure */
4577 static I32 my_pclose_pinfo(pTHX_ pInfo info) {
4579 unsigned long int retsts;
4584 /* If we were writing to a subprocess, insure that someone reading from
4585 * the mailbox gets an EOF. It looks like a simple fclose() doesn't
4586 * produce an EOF record in the mailbox.
4588 * well, at least sometimes it *does*, so we have to watch out for
4589 * the first EOF closing the pipe (and DASSGN'ing the channel)...
4593 #if defined(USE_ITHREADS)
4596 && PL_perlio_fd_refcnt)
4597 PerlIO_flush(info->fp);
4599 fflush((FILE *)info->fp);
4602 _ckvmssts(sys$setast(0));
4603 info->closing = TRUE;
4604 done = info->done && info->in_done && info->out_done && info->err_done;
4605 /* hanging on write to Perl's input? cancel it */
4606 if (info->mode == 'r' && info->out && !info->out_done) {
4607 if (info->out->chan_out) {
4608 _ckvmssts(sys$cancel(info->out->chan_out));
4609 if (!info->out->chan_in) { /* EOF generation, need AST */
4610 _ckvmssts(sys$dclast(pipe_infromchild_ast,info->out,0));
4614 if (info->in && !info->in_done && !info->in->shut_on_empty) /* EOF if hasn't had one yet */
4615 _ckvmssts(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
4617 _ckvmssts(sys$setast(1));
4620 #if defined(USE_ITHREADS)
4623 && PL_perlio_fd_refcnt)
4624 PerlIO_close(info->fp);
4626 fclose((FILE *)info->fp);
4629 we have to wait until subprocess completes, but ALSO wait until all
4630 the i/o completes...otherwise we'll be freeing the "info" structure
4631 that the i/o ASTs could still be using...
4635 _ckvmssts(sys$setast(0));
4636 done = info->done && info->in_done && info->out_done && info->err_done;
4637 if (!done) _ckvmssts(sys$clref(pipe_ef));
4638 _ckvmssts(sys$setast(1));
4639 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4641 retsts = info->completion;
4643 /* remove from list of open pipes */
4644 _ckvmssts(sys$setast(0));
4646 for (next = open_pipes; next != NULL; last = next, next = next->next) {
4652 last->next = info->next;
4654 open_pipes = info->next;
4655 _ckvmssts(sys$setast(1));
4657 /* free buffers and structures */
4660 if (info->in->buf) {
4661 n = info->in->bufsize * sizeof(char);
4662 _ckvmssts(lib$free_vm(&n, &info->in->buf));
4665 _ckvmssts(lib$free_vm(&n, &info->in));
4668 if (info->out->buf) {
4669 n = info->out->bufsize * sizeof(char);
4670 _ckvmssts(lib$free_vm(&n, &info->out->buf));
4673 _ckvmssts(lib$free_vm(&n, &info->out));
4676 if (info->err->buf) {
4677 n = info->err->bufsize * sizeof(char);
4678 _ckvmssts(lib$free_vm(&n, &info->err->buf));
4681 _ckvmssts(lib$free_vm(&n, &info->err));
4684 _ckvmssts(lib$free_vm(&n, &info));
4690 /*{{{ I32 my_pclose(PerlIO *fp)*/
4691 I32 Perl_my_pclose(pTHX_ PerlIO *fp)
4693 pInfo info, last = NULL;
4696 /* Fixme - need ast and mutex protection here */
4697 for (info = open_pipes; info != NULL; last = info, info = info->next)
4698 if (info->fp == fp) break;
4700 if (info == NULL) { /* no such pipe open */
4701 set_errno(ECHILD); /* quoth POSIX */
4702 set_vaxc_errno(SS$_NONEXPR);
4706 ret_status = my_pclose_pinfo(aTHX_ info);
4710 } /* end of my_pclose() */
4712 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
4713 /* Roll our own prototype because we want this regardless of whether
4714 * _VMS_WAIT is defined.
4716 __pid_t __vms_waitpid( __pid_t __pid, int *__stat_loc, int __options );
4718 /* sort-of waitpid; special handling of pipe clean-up for subprocesses
4719 created with popen(); otherwise partially emulate waitpid() unless
4720 we have a suitable one from the CRTL that came with VMS 7.2 and later.
4721 Also check processes not considered by the CRTL waitpid().
4723 /*{{{Pid_t my_waitpid(Pid_t pid, int *statusp, int flags)*/
4725 Perl_my_waitpid(pTHX_ Pid_t pid, int *statusp, int flags)
4732 if (statusp) *statusp = 0;
4734 for (info = open_pipes; info != NULL; info = info->next)
4735 if (info->pid == pid) break;
4737 if (info != NULL) { /* we know about this child */
4738 while (!info->done) {
4739 _ckvmssts(sys$setast(0));
4741 if (!done) _ckvmssts(sys$clref(pipe_ef));
4742 _ckvmssts(sys$setast(1));
4743 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4746 if (statusp) *statusp = info->completion;
4750 /* child that already terminated? */
4752 for (j = 0; j < NKEEPCLOSED && j < closed_num; j++) {
4753 if (closed_list[j].pid == pid) {
4754 if (statusp) *statusp = closed_list[j].completion;
4759 /* fall through if this child is not one of our own pipe children */
4761 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
4763 /* waitpid() became available in the CRTL as of VMS 7.0, but only
4764 * in 7.2 did we get a version that fills in the VMS completion
4765 * status as Perl has always tried to do.
4768 sts = __vms_waitpid( pid, statusp, flags );
4770 if ( sts == 0 || !(sts == -1 && errno == ECHILD) )
4773 /* If the real waitpid tells us the child does not exist, we
4774 * fall through here to implement waiting for a child that
4775 * was created by some means other than exec() (say, spawned
4776 * from DCL) or to wait for a process that is not a subprocess
4777 * of the current process.
4780 #endif /* defined(__CRTL_VER) && __CRTL_VER >= 70200000 */
4783 $DESCRIPTOR(intdsc,"0 00:00:01");
4784 unsigned long int ownercode = JPI$_OWNER, ownerpid;
4785 unsigned long int pidcode = JPI$_PID, mypid;
4786 unsigned long int interval[2];
4787 unsigned int jpi_iosb[2];
4788 struct itmlst_3 jpilist[2] = {
4789 {sizeof(ownerpid), JPI$_OWNER, &ownerpid, 0},
4794 /* Sorry folks, we don't presently implement rooting around for
4795 the first child we can find, and we definitely don't want to
4796 pass a pid of -1 to $getjpi, where it is a wildcard operation.
4802 /* Get the owner of the child so I can warn if it's not mine. If the
4803 * process doesn't exist or I don't have the privs to look at it,
4804 * I can go home early.
4806 sts = sys$getjpiw(0,&pid,NULL,&jpilist,&jpi_iosb,NULL,NULL);
4807 if (sts & 1) sts = jpi_iosb[0];
4819 set_vaxc_errno(sts);
4823 if (ckWARN(WARN_EXEC)) {
4824 /* remind folks they are asking for non-standard waitpid behavior */
4825 _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
4826 if (ownerpid != mypid)
4827 Perl_warner(aTHX_ packWARN(WARN_EXEC),
4828 "waitpid: process %x is not a child of process %x",
4832 /* simply check on it once a second until it's not there anymore. */
4834 _ckvmssts(sys$bintim(&intdsc,interval));
4835 while ((sts=lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0)) & 1) {
4836 _ckvmssts(sys$schdwk(0,0,interval,0));
4837 _ckvmssts(sys$hiber());
4839 if (sts == SS$_NONEXPR) sts = SS$_NORMAL;
4844 } /* end of waitpid() */
4849 /*{{{ char *my_gconvert(double val, int ndig, int trail, char *buf) */
4851 my_gconvert(double val, int ndig, int trail, char *buf)
4853 static char __gcvtbuf[DBL_DIG+1];
4856 loc = buf ? buf : __gcvtbuf;
4858 #ifndef __DECC /* VAXCRTL gcvt uses E format for numbers < 1 */
4860 sprintf(loc,"%.*g",ndig,val);
4866 if (!buf && ndig > DBL_DIG) ndig = DBL_DIG;
4867 return gcvt(val,ndig,loc);
4870 loc[0] = '0'; loc[1] = '\0';
4877 #if defined(__VAX) || !defined(NAML$C_MAXRSS)
4878 static int rms_free_search_context(struct FAB * fab)
4882 nam = fab->fab$l_nam;
4883 nam->nam$b_nop |= NAM$M_SYNCHK;
4884 nam->nam$l_rlf = NULL;
4886 return sys$parse(fab, NULL, NULL);
4889 #define rms_setup_nam(nam) struct NAM nam = cc$rms_nam
4890 #define rms_clear_nam_nop(nam) nam.nam$b_nop = 0;
4891 #define rms_set_nam_nop(nam, opt) nam.nam$b_nop |= (opt)
4892 #define rms_set_nam_fnb(nam, opt) nam.nam$l_fnb |= (opt)
4893 #define rms_is_nam_fnb(nam, opt) (nam.nam$l_fnb & (opt))
4894 #define rms_nam_esll(nam) nam.nam$b_esl
4895 #define rms_nam_esl(nam) nam.nam$b_esl
4896 #define rms_nam_name(nam) nam.nam$l_name
4897 #define rms_nam_namel(nam) nam.nam$l_name
4898 #define rms_nam_type(nam) nam.nam$l_type
4899 #define rms_nam_typel(nam) nam.nam$l_type
4900 #define rms_nam_ver(nam) nam.nam$l_ver
4901 #define rms_nam_verl(nam) nam.nam$l_ver
4902 #define rms_nam_rsll(nam) nam.nam$b_rsl
4903 #define rms_nam_rsl(nam) nam.nam$b_rsl
4904 #define rms_bind_fab_nam(fab, nam) fab.fab$l_nam = &nam
4905 #define rms_set_fna(fab, nam, name, size) \
4906 { fab.fab$b_fns = size; fab.fab$l_fna = name; }
4907 #define rms_get_fna(fab, nam) fab.fab$l_fna
4908 #define rms_set_dna(fab, nam, name, size) \
4909 { fab.fab$b_dns = size; fab.fab$l_dna = name; }
4910 #define rms_nam_dns(fab, nam) fab.fab$b_dns
4911 #define rms_set_esa(nam, name, size) \
4912 { nam.nam$b_ess = size; nam.nam$l_esa = name; }
4913 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
4914 { nam.nam$l_esa = s_name; nam.nam$b_ess = s_size;}
4915 #define rms_set_rsa(nam, name, size) \
4916 { nam.nam$l_rsa = name; nam.nam$b_rss = size; }
4917 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
4918 { nam.nam$l_rsa = s_name; nam.nam$b_rss = s_size; }
4919 #define rms_nam_name_type_l_size(nam) \
4920 (nam.nam$b_name + nam.nam$b_type)
4922 static int rms_free_search_context(struct FAB * fab)
4926 nam = fab->fab$l_naml;
4927 nam->naml$b_nop |= NAM$M_SYNCHK;
4928 nam->naml$l_rlf = NULL;
4929 nam->naml$l_long_defname_size = 0;
4932 return sys$parse(fab, NULL, NULL);
4935 #define rms_setup_nam(nam) struct NAML nam = cc$rms_naml
4936 #define rms_clear_nam_nop(nam) nam.naml$b_nop = 0;
4937 #define rms_set_nam_nop(nam, opt) nam.naml$b_nop |= (opt)
4938 #define rms_set_nam_fnb(nam, opt) nam.naml$l_fnb |= (opt)
4939 #define rms_is_nam_fnb(nam, opt) (nam.naml$l_fnb & (opt))
4940 #define rms_nam_esll(nam) nam.naml$l_long_expand_size
4941 #define rms_nam_esl(nam) nam.naml$b_esl
4942 #define rms_nam_name(nam) nam.naml$l_name
4943 #define rms_nam_namel(nam) nam.naml$l_long_name
4944 #define rms_nam_type(nam) nam.naml$l_type
4945 #define rms_nam_typel(nam) nam.naml$l_long_type
4946 #define rms_nam_ver(nam) nam.naml$l_ver
4947 #define rms_nam_verl(nam) nam.naml$l_long_ver
4948 #define rms_nam_rsll(nam) nam.naml$l_long_result_size
4949 #define rms_nam_rsl(nam) nam.naml$b_rsl
4950 #define rms_bind_fab_nam(fab, nam) fab.fab$l_naml = &nam
4951 #define rms_set_fna(fab, nam, name, size) \
4952 { fab.fab$b_fns = 0; fab.fab$l_fna = (char *) -1; \
4953 nam.naml$l_long_filename_size = size; \
4954 nam.naml$l_long_filename = name;}
4955 #define rms_get_fna(fab, nam) nam.naml$l_long_filename
4956 #define rms_set_dna(fab, nam, name, size) \
4957 { fab.fab$b_dns = 0; fab.fab$l_dna = (char *) -1; \
4958 nam.naml$l_long_defname_size = size; \
4959 nam.naml$l_long_defname = name; }
4960 #define rms_nam_dns(fab, nam) nam.naml$l_long_defname_size
4961 #define rms_set_esa(nam, name, size) \
4962 { nam.naml$b_ess = 0; nam.naml$l_esa = (char *) -1; \
4963 nam.naml$l_long_expand_alloc = size; \
4964 nam.naml$l_long_expand = name; }
4965 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
4966 { nam.naml$l_esa = s_name; nam.naml$b_ess = s_size; \
4967 nam.naml$l_long_expand = l_name; \
4968 nam.naml$l_long_expand_alloc = l_size; }
4969 #define rms_set_rsa(nam, name, size) \
4970 { nam.naml$l_rsa = NULL; nam.naml$b_rss = 0; \
4971 nam.naml$l_long_result = name; \
4972 nam.naml$l_long_result_alloc = size; }
4973 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
4974 { nam.naml$l_rsa = s_name; nam.naml$b_rss = s_size; \
4975 nam.naml$l_long_result = l_name; \
4976 nam.naml$l_long_result_alloc = l_size; }
4977 #define rms_nam_name_type_l_size(nam) \
4978 (nam.naml$l_long_name_size + nam.naml$l_long_type_size)
4983 * The CRTL for 8.3 and later can create symbolic links in any mode,
4984 * however in 8.3 the unlink/remove/delete routines will only properly handle
4985 * them if one of the PCP modes is active.
4987 static int rms_erase(const char * vmsname)
4990 struct FAB myfab = cc$rms_fab;
4991 rms_setup_nam(mynam);
4993 rms_set_fna(myfab, mynam, (char *)vmsname, strlen(vmsname)); /* cast ok */
4994 rms_bind_fab_nam(myfab, mynam);
4996 /* Are we removing all versions? */
4997 if (vms_unlink_all_versions == 1) {
4998 const char * defspec = ";*";
4999 rms_set_dna(myfab, mynam, (char *)defspec, strlen(defspec)); /* cast ok */
5002 #ifdef NAML$M_OPEN_SPECIAL
5003 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5006 status = sys$erase(&myfab, 0, 0);
5013 vms_rename_with_acl(pTHX_ const struct dsc$descriptor_s * vms_src_dsc,
5014 const struct dsc$descriptor_s * vms_dst_dsc,
5015 unsigned long flags)
5017 /* VMS and UNIX handle file permissions differently and the
5018 * the same ACL trick may be needed for renaming files,
5019 * especially if they are directories.
5022 /* todo: get kill_file and rename to share common code */
5023 /* I can not find online documentation for $change_acl
5024 * it appears to be replaced by $set_security some time ago */
5026 const unsigned int access_mode = 0;
5027 $DESCRIPTOR(obj_file_dsc,"FILE");
5030 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
5031 int aclsts, fndsts, rnsts = -1;
5032 unsigned int ctx = 0;
5033 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
5034 struct dsc$descriptor_s * clean_dsc;
5037 unsigned char myace$b_length;
5038 unsigned char myace$b_type;
5039 unsigned short int myace$w_flags;
5040 unsigned long int myace$l_access;
5041 unsigned long int myace$l_ident;
5042 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
5043 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL,
5045 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
5048 findlst[3] = {{sizeof oldace, OSS$_ACL_FIND_ENTRY, &oldace, 0},
5049 {sizeof oldace, OSS$_ACL_READ_ENTRY, &oldace, 0},
5051 addlst[2] = {{sizeof newace, OSS$_ACL_ADD_ENTRY, &newace, 0},{0,0,0,0}},
5052 dellst[2] = {{sizeof newace, OSS$_ACL_DELETE_ENTRY, &newace, 0},
5056 /* Expand the input spec using RMS, since we do not want to put
5057 * ACLs on the target of a symbolic link */
5058 vmsname = PerlMem_malloc(NAM$C_MAXRSS+1);
5059 if (vmsname == NULL)
5062 rslt = int_rmsexpand_tovms(vms_src_dsc->dsc$a_pointer,
5064 PERL_RMSEXPAND_M_SYMLINK);
5066 PerlMem_free(vmsname);
5070 /* So we get our own UIC to use as a rights identifier,
5071 * and the insert an ACE at the head of the ACL which allows us
5072 * to delete the file.
5074 _ckvmssts_noperl(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
5076 fildsc.dsc$w_length = strlen(vmsname);
5077 fildsc.dsc$a_pointer = vmsname;
5079 newace.myace$l_ident = oldace.myace$l_ident;
5082 /* Grab any existing ACEs with this identifier in case we fail */
5083 clean_dsc = &fildsc;
5084 aclsts = fndsts = sys$get_security(&obj_file_dsc,
5092 if ($VMS_STATUS_SUCCESS(fndsts) || (fndsts == SS$_ACLEMPTY)) {
5093 /* Add the new ACE . . . */
5095 /* if the sys$get_security succeeded, then ctx is valid, and the
5096 * object/file descriptors will be ignored. But otherwise they
5099 aclsts = sys$set_security(&obj_file_dsc, &fildsc, NULL,
5100 OSS$M_RELCTX, addlst, &ctx, &access_mode);
5101 if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) {
5103 set_vaxc_errno(aclsts);
5104 PerlMem_free(vmsname);
5108 rnsts = lib$rename_file(vms_src_dsc, vms_dst_dsc,
5111 NULL, NULL, NULL, NULL, NULL, NULL, NULL);
5113 if ($VMS_STATUS_SUCCESS(rnsts)) {
5114 clean_dsc = (struct dsc$descriptor_s *)vms_dst_dsc;
5117 /* Put things back the way they were. */
5119 aclsts = sys$get_security(&obj_file_dsc,
5127 if ($VMS_STATUS_SUCCESS(aclsts)) {
5131 if (!$VMS_STATUS_SUCCESS(fndsts))
5132 sec_flags = OSS$M_RELCTX;
5134 /* Get rid of the new ACE */
5135 aclsts = sys$set_security(NULL, NULL, NULL,
5136 sec_flags, dellst, &ctx, &access_mode);
5138 /* If there was an old ACE, put it back */
5139 if ($VMS_STATUS_SUCCESS(aclsts) && $VMS_STATUS_SUCCESS(fndsts)) {
5140 addlst[0].bufadr = &oldace;
5141 aclsts = sys$set_security(NULL, NULL, NULL,
5142 OSS$M_RELCTX, addlst, &ctx, &access_mode);
5143 if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) {
5145 set_vaxc_errno(aclsts);
5151 /* Try to clear the lock on the ACL list */
5152 aclsts2 = sys$set_security(NULL, NULL, NULL,
5153 OSS$M_RELCTX, NULL, &ctx, &access_mode);
5155 /* Rename errors are most important */
5156 if (!$VMS_STATUS_SUCCESS(rnsts))
5159 set_vaxc_errno(aclsts);
5164 if (aclsts != SS$_ACLEMPTY)
5171 PerlMem_free(vmsname);
5176 /*{{{int rename(const char *, const char * */
5177 /* Not exactly what X/Open says to do, but doing it absolutely right
5178 * and efficiently would require a lot more work. This should be close
5179 * enough to pass all but the most strict X/Open compliance test.
5182 Perl_rename(pTHX_ const char *src, const char * dst)
5191 /* Validate the source file */
5192 src_sts = flex_lstat(src, &src_st);
5195 /* No source file or other problem */
5199 dst_sts = flex_lstat(dst, &dst_st);
5202 if (dst_st.st_dev != src_st.st_dev) {
5203 /* Must be on the same device */
5208 /* VMS_INO_T_COMPARE is true if the inodes are different
5209 * to match the output of memcmp
5212 if (!VMS_INO_T_COMPARE(src_st.st_ino, dst_st.st_ino)) {
5213 /* That was easy, the files are the same! */
5217 if (S_ISDIR(src_st.st_mode) && !S_ISDIR(dst_st.st_mode)) {
5218 /* If source is a directory, so must be dest */
5226 if ((dst_sts == 0) &&
5227 (vms_unlink_all_versions || S_ISDIR(dst_st.st_mode))) {
5229 /* We have issues here if vms_unlink_all_versions is set
5230 * If the destination exists, and is not a directory, then
5231 * we must delete in advance.
5233 * If the src is a directory, then we must always pre-delete
5236 * If we successfully delete the dst in advance, and the rename fails
5237 * X/Open requires that errno be EIO.
5241 if (!S_ISDIR(dst_st.st_mode) || S_ISDIR(src_st.st_mode)) {
5243 d_sts = mp_do_kill_file(aTHX_ dst, S_ISDIR(dst_st.st_mode));
5247 /* We killed the destination, so only errno now is EIO */
5252 /* Originally the idea was to call the CRTL rename() and only
5253 * try the lib$rename_file if it failed.
5254 * It turns out that there are too many variants in what the
5255 * the CRTL rename might do, so only use lib$rename_file
5260 /* Is the source and dest both in VMS format */
5261 /* if the source is a directory, then need to fileify */
5262 /* and dest must be a directory or non-existant. */
5268 unsigned long flags;
5269 struct dsc$descriptor_s old_file_dsc;
5270 struct dsc$descriptor_s new_file_dsc;
5272 /* We need to modify the src and dst depending
5273 * on if one or more of them are directories.
5276 vms_src = PerlMem_malloc(VMS_MAXRSS);
5277 if (vms_src == NULL)
5278 _ckvmssts_noperl(SS$_INSFMEM);
5280 /* Source is always a VMS format file */
5281 ret_str = do_tovmsspec(src, vms_src, 0, NULL);
5282 if (ret_str == NULL) {
5283 PerlMem_free(vms_src);
5288 vms_dst = PerlMem_malloc(VMS_MAXRSS);
5289 if (vms_dst == NULL)
5290 _ckvmssts_noperl(SS$_INSFMEM);
5292 if (S_ISDIR(src_st.st_mode)) {
5294 char * vms_dir_file;
5296 vms_dir_file = PerlMem_malloc(VMS_MAXRSS);
5297 if (vms_dir_file == NULL)
5298 _ckvmssts_noperl(SS$_INSFMEM);
5300 /* The source must be a file specification */
5301 ret_str = do_fileify_dirspec(vms_src, vms_dir_file, 0, NULL);
5302 if (ret_str == NULL) {
5303 PerlMem_free(vms_src);
5304 PerlMem_free(vms_dst);
5305 PerlMem_free(vms_dir_file);
5309 PerlMem_free(vms_src);
5310 vms_src = vms_dir_file;
5312 /* If the dest is a directory, we must remove it
5315 d_sts = mp_do_kill_file(aTHX_ dst, 1);
5317 PerlMem_free(vms_src);
5318 PerlMem_free(vms_dst);
5326 /* The dest must be a VMS file specification */
5327 ret_str = int_tovmsspec(dst, vms_dst, 0, NULL);
5328 if (ret_str == NULL) {
5329 PerlMem_free(vms_src);
5330 PerlMem_free(vms_dst);
5335 /* The source must be a file specification */
5336 vms_dir_file = PerlMem_malloc(VMS_MAXRSS);
5337 if (vms_dir_file == NULL)
5338 _ckvmssts_noperl(SS$_INSFMEM);
5340 ret_str = do_fileify_dirspec(vms_dst, vms_dir_file, 0, NULL);
5341 if (ret_str == NULL) {
5342 PerlMem_free(vms_src);
5343 PerlMem_free(vms_dst);
5344 PerlMem_free(vms_dir_file);
5348 PerlMem_free(vms_dst);
5349 vms_dst = vms_dir_file;
5352 /* File to file or file to new dir */
5354 if ((dst_sts == 0) && S_ISDIR(dst_st.st_mode)) {
5355 /* VMS pathify a dir target */
5356 ret_str = do_tovmspath(dst, vms_dst, 0, NULL);
5357 if (ret_str == NULL) {
5358 PerlMem_free(vms_src);
5359 PerlMem_free(vms_dst);
5365 /* fileify a target VMS file specification */
5366 ret_str = int_tovmsspec(dst, vms_dst, 0, NULL);
5367 if (ret_str == NULL) {
5368 PerlMem_free(vms_src);
5369 PerlMem_free(vms_dst);
5376 old_file_dsc.dsc$a_pointer = vms_src;
5377 old_file_dsc.dsc$w_length = strlen(vms_src);
5378 old_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
5379 old_file_dsc.dsc$b_class = DSC$K_CLASS_S;
5381 new_file_dsc.dsc$a_pointer = vms_dst;
5382 new_file_dsc.dsc$w_length = strlen(vms_dst);
5383 new_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
5384 new_file_dsc.dsc$b_class = DSC$K_CLASS_S;
5387 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5388 flags |= 4; /* LIB$M_FIL_LONG_NAMES (bit 2) */
5391 sts = lib$rename_file(&old_file_dsc,
5395 NULL, NULL, NULL, NULL, NULL, NULL, NULL);
5396 if (!$VMS_STATUS_SUCCESS(sts)) {
5398 /* We could have failed because VMS style permissions do not
5399 * permit renames that UNIX will allow. Just like the hack
5402 sts = vms_rename_with_acl(aTHX_ &old_file_dsc, &new_file_dsc, flags);
5405 PerlMem_free(vms_src);
5406 PerlMem_free(vms_dst);
5407 if (!$VMS_STATUS_SUCCESS(sts)) {
5414 if (vms_unlink_all_versions) {
5415 /* Now get rid of any previous versions of the source file that
5420 src_sts = mp_do_kill_file(aTHX_ src, S_ISDIR(src_st.st_mode));
5424 /* We deleted the destination, so must force the error to be EIO */
5425 if ((retval != 0) && (pre_delete != 0))
5433 /*{{{char *do_rmsexpand(char *fspec, char *out, int ts, char *def, unsigned opts)*/
5434 /* Shortcut for common case of simple calls to $PARSE and $SEARCH
5435 * to expand file specification. Allows for a single default file
5436 * specification and a simple mask of options. If outbuf is non-NULL,
5437 * it must point to a buffer at least NAM$C_MAXRSS bytes long, into which
5438 * the resultant file specification is placed. If outbuf is NULL, the
5439 * resultant file specification is placed into a static buffer.
5440 * The third argument, if non-NULL, is taken to be a default file
5441 * specification string. The fourth argument is unused at present.
5442 * rmesexpand() returns the address of the resultant string if
5443 * successful, and NULL on error.
5445 * New functionality for previously unused opts value:
5446 * PERL_RMSEXPAND_M_VMS - Force output file specification to VMS format.
5447 * PERL_RMSEXPAND_M_LONG - Want output in long formst
5448 * PERL_RMSEXPAND_M_VMS_IN - Input is already in VMS, so do not vmsify
5449 * PERL_RMSEXPAND_M_SYMLINK - Use symbolic link, not target
5451 static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *);
5455 (const char *filespec,
5457 const char *defspec,
5463 const char * in_spec;
5465 const char * def_spec;
5466 char * vmsfspec, *vmsdefspec;
5470 struct FAB myfab = cc$rms_fab;
5471 rms_setup_nam(mynam);
5473 unsigned long int retsts, trimver, trimtype, haslower = 0, isunix = 0;
5476 /* temp hack until UTF8 is actually implemented */
5477 if (fs_utf8 != NULL)
5480 if (!filespec || !*filespec) {
5481 set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL);
5491 if ((opts & PERL_RMSEXPAND_M_VMS_IN) == 0) {
5492 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
5493 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
5495 /* If this is a UNIX file spec, convert it to VMS */
5496 sts = vms_split_path(filespec, &v_spec, &v_len, &r_spec, &r_len,
5497 &d_spec, &d_len, &n_spec, &n_len, &e_spec,
5498 &e_len, &vs_spec, &vs_len);
5503 vmsfspec = PerlMem_malloc(VMS_MAXRSS);
5504 if (vmsfspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5505 ret_spec = int_tovmsspec(filespec, vmsfspec, 0, fs_utf8);
5506 if (ret_spec == NULL) {
5507 PerlMem_free(vmsfspec);
5510 in_spec = (const char *)vmsfspec;
5512 /* Unless we are forcing to VMS format, a UNIX input means
5513 * UNIX output, and that requires long names to be used
5515 if ((opts & PERL_RMSEXPAND_M_VMS) == 0)
5516 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5517 opts |= PERL_RMSEXPAND_M_LONG;
5525 rms_set_fna(myfab, mynam, (char *)in_spec, strlen(in_spec)); /* cast ok */
5526 rms_bind_fab_nam(myfab, mynam);
5528 /* Process the default file specification if present */
5530 if (defspec && *defspec) {
5532 t_isunix = is_unix_filespec(defspec);
5534 vmsdefspec = PerlMem_malloc(VMS_MAXRSS);
5535 if (vmsdefspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5536 ret_spec = int_tovmsspec(defspec, vmsdefspec, 0, dfs_utf8);
5538 if (ret_spec == NULL) {
5539 /* Clean up and bail */
5540 PerlMem_free(vmsdefspec);
5541 if (vmsfspec != NULL)
5542 PerlMem_free(vmsfspec);
5545 def_spec = (const char *)vmsdefspec;
5547 rms_set_dna(myfab, mynam,
5548 (char *)def_spec, strlen(def_spec)); /* cast ok */
5551 /* Now we need the expansion buffers */
5552 esa = PerlMem_malloc(NAM$C_MAXRSS + 1);
5553 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5554 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5555 esal = PerlMem_malloc(VMS_MAXRSS);
5556 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5558 rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS-1);
5560 /* If a NAML block is used RMS always writes to the long and short
5561 * addresses unless you suppress the short name.
5563 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5564 outbufl = PerlMem_malloc(VMS_MAXRSS);
5565 if (outbufl == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5567 rms_set_rsal(mynam, outbuf, NAM$C_MAXRSS, outbufl, (VMS_MAXRSS - 1));
5569 #ifdef NAM$M_NO_SHORT_UPCASE
5570 if (decc_efs_case_preserve)
5571 rms_set_nam_nop(mynam, NAM$M_NO_SHORT_UPCASE);
5574 /* We may not want to follow symbolic links */
5575 #ifdef NAML$M_OPEN_SPECIAL
5576 if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0)
5577 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5580 /* First attempt to parse as an existing file */
5581 retsts = sys$parse(&myfab,0,0);
5582 if (!(retsts & STS$K_SUCCESS)) {
5584 /* Could not find the file, try as syntax only if error is not fatal */
5585 rms_set_nam_nop(mynam, NAM$M_SYNCHK);
5586 if (retsts == RMS$_DNF ||
5587 retsts == RMS$_DIR ||
5588 retsts == RMS$_DEV ||
5589 retsts == RMS$_PRV) {
5590 retsts = sys$parse(&myfab,0,0);
5591 if (retsts & STS$K_SUCCESS) goto int_expanded;
5594 /* Still could not parse the file specification */
5595 /*----------------------------------------------*/
5596 sts = rms_free_search_context(&myfab); /* Free search context */
5597 if (vmsdefspec != NULL)
5598 PerlMem_free(vmsdefspec);
5599 if (vmsfspec != NULL)
5600 PerlMem_free(vmsfspec);
5601 if (outbufl != NULL)
5602 PerlMem_free(outbufl);
5606 set_vaxc_errno(retsts);
5607 if (retsts == RMS$_PRV) set_errno(EACCES);
5608 else if (retsts == RMS$_DEV) set_errno(ENODEV);
5609 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
5610 else set_errno(EVMSERR);
5613 retsts = sys$search(&myfab,0,0);
5614 if (!(retsts & STS$K_SUCCESS) && retsts != RMS$_FNF) {
5615 sts = rms_free_search_context(&myfab); /* Free search context */
5616 if (vmsdefspec != NULL)
5617 PerlMem_free(vmsdefspec);
5618 if (vmsfspec != NULL)
5619 PerlMem_free(vmsfspec);
5620 if (outbufl != NULL)
5621 PerlMem_free(outbufl);
5625 set_vaxc_errno(retsts);
5626 if (retsts == RMS$_PRV) set_errno(EACCES);
5627 else set_errno(EVMSERR);
5631 /* If the input filespec contained any lowercase characters,
5632 * downcase the result for compatibility with Unix-minded code. */
5634 if (!decc_efs_case_preserve) {
5636 for (tbuf = rms_get_fna(myfab, mynam); *tbuf; tbuf++)
5637 if (islower(*tbuf)) { haslower = 1; break; }
5640 /* Is a long or a short name expected */
5641 /*------------------------------------*/
5643 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5644 if (rms_nam_rsll(mynam)) {
5646 speclen = rms_nam_rsll(mynam);
5649 spec_buf = esal; /* Not esa */
5650 speclen = rms_nam_esll(mynam);
5654 if (rms_nam_rsl(mynam)) {
5656 speclen = rms_nam_rsl(mynam);
5659 spec_buf = esa; /* Not esal */
5660 speclen = rms_nam_esl(mynam);
5663 spec_buf[speclen] = '\0';
5665 /* Trim off null fields added by $PARSE
5666 * If type > 1 char, must have been specified in original or default spec
5667 * (not true for version; $SEARCH may have added version of existing file).
5669 trimver = !rms_is_nam_fnb(mynam, NAM$M_EXP_VER);
5670 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5671 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
5672 ((rms_nam_verl(mynam) - rms_nam_typel(mynam)) == 1);
5675 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
5676 ((rms_nam_ver(mynam) - rms_nam_type(mynam)) == 1);
5678 if (trimver || trimtype) {
5679 if (defspec && *defspec) {
5680 char *defesal = NULL;
5681 char *defesa = NULL;
5682 defesa = PerlMem_malloc(VMS_MAXRSS + 1);
5683 if (defesa != NULL) {
5684 struct FAB deffab = cc$rms_fab;
5685 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5686 defesal = PerlMem_malloc(VMS_MAXRSS + 1);
5687 if (defesal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5689 rms_setup_nam(defnam);
5691 rms_bind_fab_nam(deffab, defnam);
5695 (deffab, defnam, (char *)defspec, rms_nam_dns(myfab, mynam));
5697 /* RMS needs the esa/esal as a work area if wildcards are involved */
5698 rms_set_esal(defnam, defesa, NAM$C_MAXRSS, defesal, VMS_MAXRSS - 1);
5700 rms_clear_nam_nop(defnam);
5701 rms_set_nam_nop(defnam, NAM$M_SYNCHK);
5702 #ifdef NAM$M_NO_SHORT_UPCASE
5703 if (decc_efs_case_preserve)
5704 rms_set_nam_nop(defnam, NAM$M_NO_SHORT_UPCASE);
5706 #ifdef NAML$M_OPEN_SPECIAL
5707 if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0)
5708 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5710 if (sys$parse(&deffab,0,0) & STS$K_SUCCESS) {
5712 trimver = !rms_is_nam_fnb(defnam, NAM$M_EXP_VER);
5715 trimtype = !rms_is_nam_fnb(defnam, NAM$M_EXP_TYPE);
5718 if (defesal != NULL)
5719 PerlMem_free(defesal);
5720 PerlMem_free(defesa);
5722 _ckvmssts_noperl(SS$_INSFMEM);
5726 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5727 if (*(rms_nam_verl(mynam)) != '\"')
5728 speclen = rms_nam_verl(mynam) - spec_buf;
5731 if (*(rms_nam_ver(mynam)) != '\"')
5732 speclen = rms_nam_ver(mynam) - spec_buf;
5736 /* If we didn't already trim version, copy down */
5737 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5738 if (speclen > rms_nam_verl(mynam) - spec_buf)
5740 (rms_nam_typel(mynam),
5741 rms_nam_verl(mynam),
5742 speclen - (rms_nam_verl(mynam) - spec_buf));
5743 speclen -= rms_nam_verl(mynam) - rms_nam_typel(mynam);
5746 if (speclen > rms_nam_ver(mynam) - spec_buf)
5748 (rms_nam_type(mynam),
5750 speclen - (rms_nam_ver(mynam) - spec_buf));
5751 speclen -= rms_nam_ver(mynam) - rms_nam_type(mynam);
5756 /* Done with these copies of the input files */
5757 /*-------------------------------------------*/
5758 if (vmsfspec != NULL)
5759 PerlMem_free(vmsfspec);
5760 if (vmsdefspec != NULL)
5761 PerlMem_free(vmsdefspec);
5763 /* If we just had a directory spec on input, $PARSE "helpfully"
5764 * adds an empty name and type for us */
5765 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5766 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5767 if (rms_nam_namel(mynam) == rms_nam_typel(mynam) &&
5768 rms_nam_verl(mynam) == rms_nam_typel(mynam) + 1 &&
5769 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
5770 speclen = rms_nam_namel(mynam) - spec_buf;
5775 if (rms_nam_name(mynam) == rms_nam_type(mynam) &&
5776 rms_nam_ver(mynam) == rms_nam_ver(mynam) + 1 &&
5777 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
5778 speclen = rms_nam_name(mynam) - spec_buf;
5781 /* Posix format specifications must have matching quotes */
5782 if (speclen < (VMS_MAXRSS - 1)) {
5783 if (decc_posix_compliant_pathnames && (spec_buf[0] == '\"')) {
5784 if ((speclen > 1) && (spec_buf[speclen-1] != '\"')) {
5785 spec_buf[speclen] = '\"';
5790 spec_buf[speclen] = '\0';
5791 if (haslower && !decc_efs_case_preserve) __mystrtolower(spec_buf);
5793 /* Have we been working with an expanded, but not resultant, spec? */
5794 /* Also, convert back to Unix syntax if necessary. */
5798 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5799 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5800 rsl = rms_nam_rsll(mynam);
5804 rsl = rms_nam_rsl(mynam);
5807 /* rsl is not present, it means that spec_buf is either */
5808 /* esa or esal, and needs to be copied to outbuf */
5809 /* convert to Unix if desired */
5811 ret_spec = int_tounixspec(spec_buf, outbuf, fs_utf8);
5813 /* VMS file specs are not in UTF-8 */
5814 if (fs_utf8 != NULL)
5816 strcpy(outbuf, spec_buf);
5821 /* Now spec_buf is either outbuf or outbufl */
5822 /* We need the result into outbuf */
5824 /* If we need this in UNIX, then we need another buffer */
5825 /* to keep things in order */
5827 char * new_src = NULL;
5828 if (spec_buf == outbuf) {
5829 new_src = PerlMem_malloc(VMS_MAXRSS);
5830 strcpy(new_src, spec_buf);
5834 ret_spec = int_tounixspec(src, outbuf, fs_utf8);
5836 PerlMem_free(new_src);
5839 /* VMS file specs are not in UTF-8 */
5840 if (fs_utf8 != NULL)
5843 /* Copy the buffer if needed */
5844 if (outbuf != spec_buf)
5845 strcpy(outbuf, spec_buf);
5851 /* Need to clean up the search context */
5852 rms_set_rsal(mynam, NULL, 0, NULL, 0);
5853 sts = rms_free_search_context(&myfab); /* Free search context */
5855 /* Clean up the extra buffers */
5859 if (outbufl != NULL)
5860 PerlMem_free(outbufl);
5862 /* Return the result */
5866 /* Common simple case - Expand an already VMS spec */
5868 int_rmsexpand_vms(const char * filespec, char * outbuf, unsigned opts) {
5869 opts |= PERL_RMSEXPAND_M_VMS_IN;
5870 return int_rmsexpand(filespec, outbuf, NULL, opts, NULL, NULL);
5873 /* Common simple case - Expand to a VMS spec */
5875 int_rmsexpand_tovms(const char * filespec, char * outbuf, unsigned opts) {
5876 opts |= PERL_RMSEXPAND_M_VMS;
5877 return int_rmsexpand(filespec, outbuf, NULL, opts, NULL, NULL);
5881 /* Entry point used by perl routines */
5884 (pTHX_ const char *filespec,
5887 const char *defspec,
5892 static char __rmsexpand_retbuf[VMS_MAXRSS];
5893 char * expanded, *ret_spec, *ret_buf;
5897 if (ret_buf == NULL) {
5899 Newx(expanded, VMS_MAXRSS, char);
5900 if (expanded == NULL)
5901 _ckvmssts(SS$_INSFMEM);
5904 ret_buf = __rmsexpand_retbuf;
5909 ret_spec = int_rmsexpand(filespec, ret_buf, defspec,
5910 opts, fs_utf8, dfs_utf8);
5912 if (ret_spec == NULL) {
5913 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
5921 /* External entry points */
5922 char *Perl_rmsexpand(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
5923 { return do_rmsexpand(spec,buf,0,def,opt,NULL,NULL); }
5924 char *Perl_rmsexpand_ts(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
5925 { return do_rmsexpand(spec,buf,1,def,opt,NULL,NULL); }
5926 char *Perl_rmsexpand_utf8
5927 (pTHX_ const char *spec, char *buf, const char *def,
5928 unsigned opt, int * fs_utf8, int * dfs_utf8)
5929 { return do_rmsexpand(spec,buf,0,def,opt, fs_utf8, dfs_utf8); }
5930 char *Perl_rmsexpand_utf8_ts
5931 (pTHX_ const char *spec, char *buf, const char *def,
5932 unsigned opt, int * fs_utf8, int * dfs_utf8)
5933 { return do_rmsexpand(spec,buf,1,def,opt, fs_utf8, dfs_utf8); }
5937 ** The following routines are provided to make life easier when
5938 ** converting among VMS-style and Unix-style directory specifications.
5939 ** All will take input specifications in either VMS or Unix syntax. On
5940 ** failure, all return NULL. If successful, the routines listed below
5941 ** return a pointer to a buffer containing the appropriately
5942 ** reformatted spec (and, therefore, subsequent calls to that routine
5943 ** will clobber the result), while the routines of the same names with
5944 ** a _ts suffix appended will return a pointer to a mallocd string
5945 ** containing the appropriately reformatted spec.
5946 ** In all cases, only explicit syntax is altered; no check is made that
5947 ** the resulting string is valid or that the directory in question
5950 ** fileify_dirspec() - convert a directory spec into the name of the
5951 ** directory file (i.e. what you can stat() to see if it's a dir).
5952 ** The style (VMS or Unix) of the result is the same as the style
5953 ** of the parameter passed in.
5954 ** pathify_dirspec() - convert a directory spec into a path (i.e.
5955 ** what you prepend to a filename to indicate what directory it's in).
5956 ** The style (VMS or Unix) of the result is the same as the style
5957 ** of the parameter passed in.
5958 ** tounixpath() - convert a directory spec into a Unix-style path.
5959 ** tovmspath() - convert a directory spec into a VMS-style path.
5960 ** tounixspec() - convert any file spec into a Unix-style file spec.
5961 ** tovmsspec() - convert any file spec into a VMS-style spec.
5962 ** xxxxx_utf8() - Variants that support UTF8 encoding of Unix-Style file spec.
5964 ** Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>
5965 ** Permission is given to distribute this code as part of the Perl
5966 ** standard distribution under the terms of the GNU General Public
5967 ** License or the Perl Artistic License. Copies of each may be
5968 ** found in the Perl standard distribution.
5971 /*{{{ char *fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/
5972 static char *mp_do_fileify_dirspec(pTHX_ const char *dir,char *buf,int ts, int *utf8_fl)
5974 static char __fileify_retbuf[VMS_MAXRSS];
5975 unsigned long int dirlen, retlen, addmfd = 0, hasfilename = 0;
5976 char *retspec, *cp1, *cp2, *lastdir;
5977 char *trndir, *vmsdir;
5978 unsigned short int trnlnm_iter_count;
5982 if (utf8_fl != NULL)
5985 if (!dir || !*dir) {
5986 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
5988 dirlen = strlen(dir);
5989 while (dirlen && dir[dirlen-1] == '/') --dirlen;
5990 if (!dirlen) { /* We had Unixish '/' -- substitute top of current tree */
5991 if (!decc_posix_compliant_pathnames && decc_disable_posix_root) {
5998 if (dirlen > (VMS_MAXRSS - 1)) {
5999 set_errno(ENAMETOOLONG); set_vaxc_errno(RMS$_SYN);
6002 trndir = PerlMem_malloc(VMS_MAXRSS + 1);
6003 if (trndir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6004 if (!strpbrk(dir+1,"/]>:") &&
6005 (!decc_posix_compliant_pathnames && decc_disable_posix_root)) {
6006 strcpy(trndir,*dir == '/' ? dir + 1: dir);
6007 trnlnm_iter_count = 0;
6008 while (!strpbrk(trndir,"/]>:") && simple_trnlnm(trndir,trndir,VMS_MAXRSS-1)) {
6009 trnlnm_iter_count++;
6010 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
6012 dirlen = strlen(trndir);
6015 strncpy(trndir,dir,dirlen);
6016 trndir[dirlen] = '\0';
6019 /* At this point we are done with *dir and use *trndir which is a
6020 * copy that can be modified. *dir must not be modified.
6023 /* If we were handed a rooted logical name or spec, treat it like a
6024 * simple directory, so that
6025 * $ Define myroot dev:[dir.]
6026 * ... do_fileify_dirspec("myroot",buf,1) ...
6027 * does something useful.
6029 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".]")) {
6030 trndir[--dirlen] = '\0';
6031 trndir[dirlen-1] = ']';
6033 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".>")) {
6034 trndir[--dirlen] = '\0';
6035 trndir[dirlen-1] = '>';
6038 if ((cp1 = strrchr(trndir,']')) != NULL || (cp1 = strrchr(trndir,'>')) != NULL) {
6039 /* If we've got an explicit filename, we can just shuffle the string. */
6040 if (*(cp1+1)) hasfilename = 1;
6041 /* Similarly, we can just back up a level if we've got multiple levels
6042 of explicit directories in a VMS spec which ends with directories. */
6044 for (cp2 = cp1; cp2 > trndir; cp2--) {
6046 if ((cp2 - 1 > trndir) && (*(cp2 - 1) != '^')) {
6047 /* fix-me, can not scan EFS file specs backward like this */
6048 *cp2 = *cp1; *cp1 = '\0';
6053 if (*cp2 == '[' || *cp2 == '<') break;
6058 vmsdir = PerlMem_malloc(VMS_MAXRSS + 1);
6059 if (vmsdir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6060 cp1 = strpbrk(trndir,"]:>");
6061 if (hasfilename || !cp1) { /* Unix-style path or filename */
6062 if (trndir[0] == '.') {
6063 if (trndir[1] == '\0' || (trndir[1] == '/' && trndir[2] == '\0')) {
6064 PerlMem_free(trndir);
6065 PerlMem_free(vmsdir);
6066 return do_fileify_dirspec("[]",buf,ts,NULL);
6068 else if (trndir[1] == '.' &&
6069 (trndir[2] == '\0' || (trndir[2] == '/' && trndir[3] == '\0'))) {
6070 PerlMem_free(trndir);
6071 PerlMem_free(vmsdir);
6072 return do_fileify_dirspec("[-]",buf,ts,NULL);
6075 if (dirlen && trndir[dirlen-1] == '/') { /* path ends with '/'; just add .dir;1 */
6076 dirlen -= 1; /* to last element */
6077 lastdir = strrchr(trndir,'/');
6079 else if ((cp1 = strstr(trndir,"/.")) != NULL) {
6080 /* If we have "/." or "/..", VMSify it and let the VMS code
6081 * below expand it, rather than repeating the code to handle
6082 * relative components of a filespec here */
6084 if (*(cp1+2) == '.') cp1++;
6085 if (*(cp1+2) == '/' || *(cp1+2) == '\0') {
6087 if (int_tovmsspec(trndir, vmsdir, 0, utf8_fl) == NULL) {
6088 PerlMem_free(trndir);
6089 PerlMem_free(vmsdir);
6092 if (strchr(vmsdir,'/') != NULL) {
6093 /* If int_tovmsspec() returned it, it must have VMS syntax
6094 * delimiters in it, so it's a mixed VMS/Unix spec. We take
6095 * the time to check this here only so we avoid a recursion
6096 * loop; otherwise, gigo.
6098 PerlMem_free(trndir);
6099 PerlMem_free(vmsdir);
6100 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN);
6103 if (do_fileify_dirspec(vmsdir,trndir,0,NULL) == NULL) {
6104 PerlMem_free(trndir);
6105 PerlMem_free(vmsdir);
6108 ret_chr = int_tounixspec(trndir, buf, utf8_fl);
6109 PerlMem_free(trndir);
6110 PerlMem_free(vmsdir);
6114 } while ((cp1 = strstr(cp1,"/.")) != NULL);
6115 lastdir = strrchr(trndir,'/');
6117 else if (dirlen >= 7 && !strcmp(&trndir[dirlen-7],"/000000")) {
6119 /* Ditto for specs that end in an MFD -- let the VMS code
6120 * figure out whether it's a real device or a rooted logical. */
6122 /* This should not happen any more. Allowing the fake /000000
6123 * in a UNIX pathname causes all sorts of problems when trying
6124 * to run in UNIX emulation. So the VMS to UNIX conversions
6125 * now remove the fake /000000 directories.
6128 trndir[dirlen] = '/'; trndir[dirlen+1] = '\0';
6129 if (int_tovmsspec(trndir, vmsdir, 0, NULL) == NULL) {
6130 PerlMem_free(trndir);
6131 PerlMem_free(vmsdir);
6134 if (do_fileify_dirspec(vmsdir,trndir,0,NULL) == NULL) {
6135 PerlMem_free(trndir);
6136 PerlMem_free(vmsdir);
6139 ret_chr = int_tounixspec(trndir, buf, utf8_fl);
6140 PerlMem_free(trndir);
6141 PerlMem_free(vmsdir);
6146 if ( !(lastdir = cp1 = strrchr(trndir,'/')) &&
6147 !(lastdir = cp1 = strrchr(trndir,']')) &&
6148 !(lastdir = cp1 = strrchr(trndir,'>'))) cp1 = trndir;
6149 if ((cp2 = strchr(cp1,'.'))) { /* look for explicit type */
6152 /* For EFS or ODS-5 look for the last dot */
6153 if (decc_efs_charset) {
6154 cp2 = strrchr(cp1,'.');
6156 if (vms_process_case_tolerant) {
6157 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
6158 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
6159 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
6160 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
6161 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
6162 (ver || *cp3)))))) {
6163 PerlMem_free(trndir);
6164 PerlMem_free(vmsdir);
6166 set_vaxc_errno(RMS$_DIR);
6171 if (!*(cp2+1) || *(cp2+1) != 'D' || /* Wrong type. */
6172 !*(cp2+2) || *(cp2+2) != 'I' || /* Bzzt. */
6173 !*(cp2+3) || *(cp2+3) != 'R' ||
6174 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
6175 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
6176 (ver || *cp3)))))) {
6177 PerlMem_free(trndir);
6178 PerlMem_free(vmsdir);
6180 set_vaxc_errno(RMS$_DIR);
6184 dirlen = cp2 - trndir;
6188 retlen = dirlen + 6;
6189 if (buf) retspec = buf;
6190 else if (ts) Newx(retspec,retlen+1,char);
6191 else retspec = __fileify_retbuf;
6192 memcpy(retspec,trndir,dirlen);
6193 retspec[dirlen] = '\0';
6195 /* We've picked up everything up to the directory file name.
6196 Now just add the type and version, and we're set. */
6198 /* We should only add type for VMS syntax, but historically Perl
6199 has added it for UNIX style also */
6201 /* Fix me - we should not be using the same routine for VMS and
6202 UNIX format files. Things are too tangled so we need to lookup
6203 what syntax the output is */
6207 lastdir = strrchr(trndir,'/');
6211 lastdir = strpbrk(trndir,"]:>");
6217 if ((is_vms == 0) && (is_unix == 0)) {
6218 /* We still do not know? */
6219 is_unix = decc_filename_unix_report;
6224 if ((is_unix && !decc_efs_charset) || is_vms) {
6226 /* It is a bug to add a .dir to a UNIX format directory spec */
6227 /* However Perl on VMS may have programs that expect this so */
6228 /* If not using EFS character specifications allow it. */
6230 if ((!decc_efs_case_preserve) && vms_process_case_tolerant) {
6231 /* Traditionally Perl expects filenames in lower case */
6232 strcat(retspec, ".dir");
6234 /* VMS expects the .DIR to be in upper case */
6235 strcat(retspec, ".DIR");
6238 /* It is also a bug to put a VMS format version on a UNIX file */
6239 /* specification. Perl self tests are looking for this */
6240 if (is_vms || !(decc_efs_charset || decc_filename_unix_report))
6241 strcat(retspec, ";1");
6243 PerlMem_free(trndir);
6244 PerlMem_free(vmsdir);
6247 else { /* VMS-style directory spec */
6249 char *esa, *esal, term, *cp;
6252 unsigned long int sts, cmplen, haslower = 0;
6253 unsigned int nam_fnb;
6255 struct FAB dirfab = cc$rms_fab;
6256 rms_setup_nam(savnam);
6257 rms_setup_nam(dirnam);
6259 esa = PerlMem_malloc(NAM$C_MAXRSS + 1);
6260 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6262 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
6263 esal = PerlMem_malloc(VMS_MAXRSS);
6264 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6266 rms_set_fna(dirfab, dirnam, trndir, strlen(trndir));
6267 rms_bind_fab_nam(dirfab, dirnam);
6268 rms_set_dna(dirfab, dirnam, ".DIR;1", 6);
6269 rms_set_esal(dirnam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1));
6270 #ifdef NAM$M_NO_SHORT_UPCASE
6271 if (decc_efs_case_preserve)
6272 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
6275 for (cp = trndir; *cp; cp++)
6276 if (islower(*cp)) { haslower = 1; break; }
6277 if (!((sts = sys$parse(&dirfab)) & STS$K_SUCCESS)) {
6278 if ((dirfab.fab$l_sts == RMS$_DIR) || (dirfab.fab$l_sts == RMS$_DNF)) {
6279 rms_set_nam_nop(dirnam, NAM$M_SYNCHK);
6280 sts = sys$parse(&dirfab) & STS$K_SUCCESS;
6286 PerlMem_free(trndir);
6287 PerlMem_free(vmsdir);
6289 set_vaxc_errno(dirfab.fab$l_sts);
6295 /* Does the file really exist? */
6296 if (sys$search(&dirfab)& STS$K_SUCCESS) {
6297 /* Yes; fake the fnb bits so we'll check type below */
6298 rms_set_nam_fnb(dirnam, (NAM$M_EXP_TYPE | NAM$M_EXP_VER));
6300 else { /* No; just work with potential name */
6301 if (dirfab.fab$l_sts == RMS$_FNF) dirnam = savnam;
6304 fab_sts = dirfab.fab$l_sts;
6305 sts = rms_free_search_context(&dirfab);
6309 PerlMem_free(trndir);
6310 PerlMem_free(vmsdir);
6311 set_errno(EVMSERR); set_vaxc_errno(fab_sts);
6317 /* Make sure we are using the right buffer */
6320 my_esa_len = rms_nam_esll(dirnam);
6323 my_esa_len = rms_nam_esl(dirnam);
6325 my_esa[my_esa_len] = '\0';
6326 if (!rms_is_nam_fnb(dirnam, (NAM$M_EXP_DEV | NAM$M_EXP_DIR))) {
6327 cp1 = strchr(my_esa,']');
6328 if (!cp1) cp1 = strchr(my_esa,'>');
6329 if (cp1) { /* Should always be true */
6330 my_esa_len -= cp1 - my_esa - 1;
6331 memmove(my_esa, cp1 + 1, my_esa_len);
6334 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_TYPE)) { /* Was type specified? */
6335 /* Yep; check version while we're at it, if it's there. */
6336 cmplen = rms_is_nam_fnb(dirnam, NAM$M_EXP_VER) ? 6 : 4;
6337 if (strncmp(rms_nam_typel(dirnam), ".DIR;1", cmplen)) {
6338 /* Something other than .DIR[;1]. Bzzt. */
6339 sts = rms_free_search_context(&dirfab);
6343 PerlMem_free(trndir);
6344 PerlMem_free(vmsdir);
6346 set_vaxc_errno(RMS$_DIR);
6351 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_NAME)) {
6352 /* They provided at least the name; we added the type, if necessary, */
6353 if (buf) retspec = buf; /* in sys$parse() */
6354 else if (ts) Newx(retspec, my_esa_len + 1, char);
6355 else retspec = __fileify_retbuf;
6356 strcpy(retspec,my_esa);
6357 sts = rms_free_search_context(&dirfab);
6358 PerlMem_free(trndir);
6362 PerlMem_free(vmsdir);
6365 if ((cp1 = strstr(esa,".][000000]")) != NULL) {
6366 for (cp2 = cp1 + 9; *cp2; cp1++,cp2++) *cp1 = *cp2;
6370 if ((cp1 = strrchr(my_esa,']')) == NULL) cp1 = strrchr(my_esa,'>');
6371 if (cp1 == NULL) { /* should never happen */
6372 sts = rms_free_search_context(&dirfab);
6373 PerlMem_free(trndir);
6377 PerlMem_free(vmsdir);
6382 retlen = strlen(my_esa);
6383 cp1 = strrchr(my_esa,'.');
6384 /* ODS-5 directory specifications can have extra "." in them. */
6385 /* Fix-me, can not scan EFS file specifications backwards */
6386 while (cp1 != NULL) {
6387 if ((cp1-1 == my_esa) || (*(cp1-1) != '^'))
6391 while ((cp1 > my_esa) && (*cp1 != '.'))
6398 if ((cp1) != NULL) {
6399 /* There's more than one directory in the path. Just roll back. */
6401 if (buf) retspec = buf;
6402 else if (ts) Newx(retspec,retlen+7,char);
6403 else retspec = __fileify_retbuf;
6404 strcpy(retspec,my_esa);
6407 if (rms_is_nam_fnb(dirnam, NAM$M_ROOT_DIR)) {
6408 /* Go back and expand rooted logical name */
6409 rms_set_nam_nop(dirnam, NAM$M_SYNCHK | NAM$M_NOCONCEAL);
6410 #ifdef NAM$M_NO_SHORT_UPCASE
6411 if (decc_efs_case_preserve)
6412 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
6414 if (!(sys$parse(&dirfab) & STS$K_SUCCESS)) {
6415 sts = rms_free_search_context(&dirfab);
6419 PerlMem_free(trndir);
6420 PerlMem_free(vmsdir);
6422 set_vaxc_errno(dirfab.fab$l_sts);
6426 /* This changes the length of the string of course */
6428 my_esa_len = rms_nam_esll(dirnam);
6430 my_esa_len = rms_nam_esl(dirnam);
6433 retlen = my_esa_len - 9; /* esa - '][' - '].DIR;1' */
6434 if (buf) retspec = buf;
6435 else if (ts) Newx(retspec,retlen+16,char);
6436 else retspec = __fileify_retbuf;
6437 cp1 = strstr(my_esa,"][");
6438 if (!cp1) cp1 = strstr(my_esa,"]<");
6439 dirlen = cp1 - my_esa;
6440 memcpy(retspec,my_esa,dirlen);
6441 if (!strncmp(cp1+2,"000000]",7)) {
6442 retspec[dirlen-1] = '\0';
6443 /* fix-me Not full ODS-5, just extra dots in directories for now */
6444 cp1 = retspec + dirlen - 1;
6445 while (cp1 > retspec)
6450 if (*(cp1-1) != '^')
6455 if (*cp1 == '.') *cp1 = ']';
6457 memmove(cp1+8,cp1+1,retspec+dirlen-cp1);
6458 memmove(cp1+1,"000000]",7);
6462 memmove(retspec+dirlen,cp1+2,retlen-dirlen);
6463 retspec[retlen] = '\0';
6464 /* Convert last '.' to ']' */
6465 cp1 = retspec+retlen-1;
6466 while (*cp != '[') {
6469 /* Do not trip on extra dots in ODS-5 directories */
6470 if ((cp1 == retspec) || (*(cp1-1) != '^'))
6474 if (*cp1 == '.') *cp1 = ']';
6476 memmove(cp1+8,cp1+1,retspec+dirlen-cp1);
6477 memmove(cp1+1,"000000]",7);
6481 else { /* This is a top-level dir. Add the MFD to the path. */
6482 if (buf) retspec = buf;
6483 else if (ts) Newx(retspec,retlen+16,char);
6484 else retspec = __fileify_retbuf;
6487 while ((*cp1 != ':') && (*cp1 != '\0')) *(cp2++) = *(cp1++);
6488 strcpy(cp2,":[000000]");
6493 sts = rms_free_search_context(&dirfab);
6494 /* We've set up the string up through the filename. Add the
6495 type and version, and we're done. */
6496 strcat(retspec,".DIR;1");
6498 /* $PARSE may have upcased filespec, so convert output to lower
6499 * case if input contained any lowercase characters. */
6500 if (haslower && !decc_efs_case_preserve) __mystrtolower(retspec);
6501 PerlMem_free(trndir);
6505 PerlMem_free(vmsdir);
6508 } /* end of do_fileify_dirspec() */
6510 /* External entry points */
6511 char *Perl_fileify_dirspec(pTHX_ const char *dir, char *buf)
6512 { return do_fileify_dirspec(dir,buf,0,NULL); }
6513 char *Perl_fileify_dirspec_ts(pTHX_ const char *dir, char *buf)
6514 { return do_fileify_dirspec(dir,buf,1,NULL); }
6515 char *Perl_fileify_dirspec_utf8(pTHX_ const char *dir, char *buf, int * utf8_fl)
6516 { return do_fileify_dirspec(dir,buf,0,utf8_fl); }
6517 char *Perl_fileify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int * utf8_fl)
6518 { return do_fileify_dirspec(dir,buf,1,utf8_fl); }
6520 /*{{{ char *pathify_dirspec[_ts](char *path, char *buf)*/
6521 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int * utf8_fl)
6523 static char __pathify_retbuf[VMS_MAXRSS];
6524 unsigned long int retlen;
6525 char *retpath, *cp1, *cp2, *trndir;
6526 unsigned short int trnlnm_iter_count;
6529 if (utf8_fl != NULL)
6532 if (!dir || !*dir) {
6533 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
6536 trndir = PerlMem_malloc(VMS_MAXRSS);
6537 if (trndir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6538 if (*dir) strcpy(trndir,dir);
6539 else getcwd(trndir,VMS_MAXRSS - 1);
6541 trnlnm_iter_count = 0;
6542 while (!strpbrk(trndir,"/]:>") && !no_translate_barewords
6543 && simple_trnlnm(trndir,trndir,VMS_MAXRSS-1)) {
6544 trnlnm_iter_count++;
6545 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
6546 trnlen = strlen(trndir);
6548 /* Trap simple rooted lnms, and return lnm:[000000] */
6549 if (!strcmp(trndir+trnlen-2,".]")) {
6550 if (buf) retpath = buf;
6551 else if (ts) Newx(retpath,strlen(dir)+10,char);
6552 else retpath = __pathify_retbuf;
6553 strcpy(retpath,dir);
6554 strcat(retpath,":[000000]");
6555 PerlMem_free(trndir);
6560 /* At this point we do not work with *dir, but the copy in
6561 * *trndir that is modifiable.
6564 if (!strpbrk(trndir,"]:>")) { /* Unix-style path or plain name */
6565 if (*trndir == '.' && (*(trndir+1) == '\0' ||
6566 (*(trndir+1) == '.' && *(trndir+2) == '\0')))
6567 retlen = 2 + (*(trndir+1) != '\0');
6569 if ( !(cp1 = strrchr(trndir,'/')) &&
6570 !(cp1 = strrchr(trndir,']')) &&
6571 !(cp1 = strrchr(trndir,'>')) ) cp1 = trndir;
6572 if ((cp2 = strchr(cp1,'.')) != NULL &&
6573 (*(cp2-1) != '/' || /* Trailing '.', '..', */
6574 !(*(cp2+1) == '\0' || /* or '...' are dirs. */
6575 (*(cp2+1) == '.' && *(cp2+2) == '\0') ||
6576 (*(cp2+1) == '.' && *(cp2+2) == '.' && *(cp2+3) == '\0')))) {
6579 /* For EFS or ODS-5 look for the last dot */
6580 if (decc_efs_charset) {
6581 cp2 = strrchr(cp1,'.');
6583 if (vms_process_case_tolerant) {
6584 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
6585 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
6586 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
6587 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
6588 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
6589 (ver || *cp3)))))) {
6590 PerlMem_free(trndir);
6592 set_vaxc_errno(RMS$_DIR);
6597 if (!*(cp2+1) || *(cp2+1) != 'D' || /* Wrong type. */
6598 !*(cp2+2) || *(cp2+2) != 'I' || /* Bzzt. */
6599 !*(cp2+3) || *(cp2+3) != 'R' ||
6600 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
6601 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
6602 (ver || *cp3)))))) {
6603 PerlMem_free(trndir);
6605 set_vaxc_errno(RMS$_DIR);
6609 retlen = cp2 - trndir + 1;
6611 else { /* No file type present. Treat the filename as a directory. */
6612 retlen = strlen(trndir) + 1;
6615 if (buf) retpath = buf;
6616 else if (ts) Newx(retpath,retlen+1,char);
6617 else retpath = __pathify_retbuf;
6618 strncpy(retpath, trndir, retlen-1);
6619 if (retpath[retlen-2] != '/') { /* If the path doesn't already end */
6620 retpath[retlen-1] = '/'; /* with '/', add it. */
6621 retpath[retlen] = '\0';
6623 else retpath[retlen-1] = '\0';
6625 else { /* VMS-style directory spec */
6626 char *esa, *esal, *cp;
6629 unsigned long int sts, cmplen, haslower;
6630 struct FAB dirfab = cc$rms_fab;
6632 rms_setup_nam(savnam);
6633 rms_setup_nam(dirnam);
6635 /* If we've got an explicit filename, we can just shuffle the string. */
6636 if ( ( (cp1 = strrchr(trndir,']')) != NULL ||
6637 (cp1 = strrchr(trndir,'>')) != NULL ) && *(cp1+1)) {
6638 if ((cp2 = strchr(cp1,'.')) != NULL) {
6640 if (vms_process_case_tolerant) {
6641 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
6642 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
6643 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
6644 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
6645 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
6646 (ver || *cp3)))))) {
6647 PerlMem_free(trndir);
6649 set_vaxc_errno(RMS$_DIR);
6654 if (!*(cp2+1) || *(cp2+1) != 'D' || /* Wrong type. */
6655 !*(cp2+2) || *(cp2+2) != 'I' || /* Bzzt. */
6656 !*(cp2+3) || *(cp2+3) != 'R' ||
6657 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
6658 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
6659 (ver || *cp3)))))) {
6660 PerlMem_free(trndir);
6662 set_vaxc_errno(RMS$_DIR);
6667 else { /* No file type, so just draw name into directory part */
6668 for (cp2 = cp1; *cp2; cp2++) ;
6671 *(cp2+1) = '\0'; /* OK; trndir is guaranteed to be long enough */
6673 /* We've now got a VMS 'path'; fall through */
6676 dirlen = strlen(trndir);
6677 if (trndir[dirlen-1] == ']' ||
6678 trndir[dirlen-1] == '>' ||
6679 trndir[dirlen-1] == ':') { /* It's already a VMS 'path' */
6680 if (buf) retpath = buf;
6681 else if (ts) Newx(retpath,strlen(trndir)+1,char);
6682 else retpath = __pathify_retbuf;
6683 strcpy(retpath,trndir);
6684 PerlMem_free(trndir);
6687 rms_set_fna(dirfab, dirnam, trndir, dirlen);
6688 esa = PerlMem_malloc(VMS_MAXRSS);
6689 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6691 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
6692 esal = PerlMem_malloc(VMS_MAXRSS);
6693 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6695 rms_set_dna(dirfab, dirnam, ".DIR;1", 6);
6696 rms_bind_fab_nam(dirfab, dirnam);
6697 rms_set_esal(dirnam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS-1);
6698 #ifdef NAM$M_NO_SHORT_UPCASE
6699 if (decc_efs_case_preserve)
6700 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
6703 for (cp = trndir; *cp; cp++)
6704 if (islower(*cp)) { haslower = 1; break; }
6706 if (!(sts = (sys$parse(&dirfab)& STS$K_SUCCESS))) {
6707 if ((dirfab.fab$l_sts == RMS$_DIR) || (dirfab.fab$l_sts == RMS$_DNF)) {
6708 rms_set_nam_nop(dirnam, NAM$M_SYNCHK);
6709 sts = sys$parse(&dirfab) & STS$K_SUCCESS;
6712 PerlMem_free(trndir);
6717 set_vaxc_errno(dirfab.fab$l_sts);
6723 /* Does the file really exist? */
6724 if (!(sys$search(&dirfab)&STS$K_SUCCESS)) {
6725 if (dirfab.fab$l_sts != RMS$_FNF) {
6727 sts1 = rms_free_search_context(&dirfab);
6728 PerlMem_free(trndir);
6733 set_vaxc_errno(dirfab.fab$l_sts);
6736 dirnam = savnam; /* No; just work with potential name */
6739 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_TYPE)) { /* Was type specified? */
6740 /* Yep; check version while we're at it, if it's there. */
6741 cmplen = rms_is_nam_fnb(dirnam, NAM$M_EXP_VER) ? 6 : 4;
6742 if (strncmp(rms_nam_typel(dirnam),".DIR;1",cmplen)) {
6744 /* Something other than .DIR[;1]. Bzzt. */
6745 sts2 = rms_free_search_context(&dirfab);
6746 PerlMem_free(trndir);
6751 set_vaxc_errno(RMS$_DIR);
6755 /* Make sure we are using the right buffer */
6757 /* We only need one, clean up the other */
6759 my_esa_len = rms_nam_esll(dirnam);
6762 my_esa_len = rms_nam_esl(dirnam);
6765 /* Null terminate the buffer */
6766 my_esa[my_esa_len] = '\0';
6768 /* OK, the type was fine. Now pull any file name into the
6770 if ((cp1 = strrchr(my_esa,']'))) *(rms_nam_typel(dirnam)) = ']';
6772 cp1 = strrchr(my_esa,'>');
6773 *(rms_nam_typel(dirnam)) = '>';
6776 *(rms_nam_typel(dirnam) + 1) = '\0';
6777 retlen = (rms_nam_typel(dirnam)) - my_esa + 2;
6778 if (buf) retpath = buf;
6779 else if (ts) Newx(retpath,retlen,char);
6780 else retpath = __pathify_retbuf;
6781 strcpy(retpath,my_esa);
6785 sts = rms_free_search_context(&dirfab);
6786 /* $PARSE may have upcased filespec, so convert output to lower
6787 * case if input contained any lowercase characters. */
6788 if (haslower && !decc_efs_case_preserve) __mystrtolower(retpath);
6791 PerlMem_free(trndir);
6793 } /* end of do_pathify_dirspec() */
6795 /* External entry points */
6796 char *Perl_pathify_dirspec(pTHX_ const char *dir, char *buf)
6797 { return do_pathify_dirspec(dir,buf,0,NULL); }
6798 char *Perl_pathify_dirspec_ts(pTHX_ const char *dir, char *buf)
6799 { return do_pathify_dirspec(dir,buf,1,NULL); }
6800 char *Perl_pathify_dirspec_utf8(pTHX_ const char *dir, char *buf, int *utf8_fl)
6801 { return do_pathify_dirspec(dir,buf,0,utf8_fl); }
6802 char *Perl_pathify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int *utf8_fl)
6803 { return do_pathify_dirspec(dir,buf,1,utf8_fl); }
6805 /* Internal tounixspec routine that does not use a thread context */
6806 /*{{{ char *int_tounixspec[_ts](char *spec, char *buf, int *)*/
6807 static char *int_tounixspec(const char *spec, char *rslt, int * utf8_fl)
6809 char *dirend, *cp1, *cp3, *tmp;
6811 int devlen, dirlen, retlen = VMS_MAXRSS;
6812 int expand = 1; /* guarantee room for leading and trailing slashes */
6813 unsigned short int trnlnm_iter_count;
6815 if (utf8_fl != NULL)
6818 if (vms_debug_fileify) {
6820 fprintf(stderr, "int_tounixspec: spec = NULL\n");
6822 fprintf(stderr, "int_tounixspec: spec = %s\n", spec);
6828 set_vaxc_errno(SS$_BADPARAM);
6831 if (strlen(spec) > (VMS_MAXRSS-1)) {
6833 set_vaxc_errno(SS$_BUFFEROVF);
6837 /* New VMS specific format needs translation
6838 * glob passes filenames with trailing '\n' and expects this preserved.
6840 if (decc_posix_compliant_pathnames) {
6841 if (strncmp(spec, "\"^UP^", 5) == 0) {
6847 tunix = PerlMem_malloc(VMS_MAXRSS);
6848 if (tunix == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6849 strcpy(tunix, spec);
6850 tunix_len = strlen(tunix);
6852 if (tunix[tunix_len - 1] == '\n') {
6853 tunix[tunix_len - 1] = '\"';
6854 tunix[tunix_len] = '\0';
6858 uspec = decc$translate_vms(tunix);
6859 PerlMem_free(tunix);
6860 if ((int)uspec > 0) {
6866 /* If we can not translate it, makemaker wants as-is */
6874 cmp_rslt = 0; /* Presume VMS */
6875 cp1 = strchr(spec, '/');
6879 /* Look for EFS ^/ */
6880 if (decc_efs_charset) {
6881 while (cp1 != NULL) {
6884 /* Found illegal VMS, assume UNIX */
6889 cp1 = strchr(cp1, '/');
6893 /* Look for "." and ".." */
6894 if (decc_filename_unix_report) {
6895 if (spec[0] == '.') {
6896 if ((spec[1] == '\0') || (spec[1] == '\n')) {
6900 if ((spec[1] == '.') && ((spec[2] == '\0') || (spec[2] == '\n'))) {
6906 /* This is already UNIX or at least nothing VMS understands */
6909 if (vms_debug_fileify) {
6910 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
6917 dirend = strrchr(spec,']');
6918 if (dirend == NULL) dirend = strrchr(spec,'>');
6919 if (dirend == NULL) dirend = strchr(spec,':');
6920 if (dirend == NULL) {
6922 if (vms_debug_fileify) {
6923 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
6928 /* Special case 1 - sys$posix_root = / */
6929 #if __CRTL_VER >= 70000000
6930 if (!decc_disable_posix_root) {
6931 if (strncasecmp(spec, "SYS$POSIX_ROOT:", 15) == 0) {
6939 /* Special case 2 - Convert NLA0: to /dev/null */
6940 #if __CRTL_VER < 70000000
6941 cmp_rslt = strncmp(spec,"NLA0:", 5);
6943 cmp_rslt = strncmp(spec,"nla0:", 5);
6945 cmp_rslt = strncasecmp(spec,"NLA0:", 5);
6947 if (cmp_rslt == 0) {
6948 strcpy(rslt, "/dev/null");
6951 if (spec[6] != '\0') {
6958 /* Also handle special case "SYS$SCRATCH:" */
6959 #if __CRTL_VER < 70000000
6960 cmp_rslt = strncmp(spec,"SYS$SCRATCH:", 12);
6962 cmp_rslt = strncmp(spec,"sys$scratch:", 12);
6964 cmp_rslt = strncasecmp(spec,"SYS$SCRATCH:", 12);
6966 tmp = PerlMem_malloc(VMS_MAXRSS);
6967 if (tmp == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6968 if (cmp_rslt == 0) {
6971 islnm = simple_trnlnm("TMP", tmp, VMS_MAXRSS-1);
6973 strcpy(rslt, "/tmp");
6976 if (spec[12] != '\0') {
6984 if (*cp2 != '[' && *cp2 != '<') {
6987 else { /* the VMS spec begins with directories */
6989 if (*cp2 == ']' || *cp2 == '>') {
6990 *(cp1++) = '.'; *(cp1++) = '/'; *(cp1++) = '\0';
6994 else if ( *cp2 != '^' && *cp2 != '.' && *cp2 != '-') { /* add the implied device */
6995 if (getcwd(tmp, VMS_MAXRSS-1 ,1) == NULL) {
6997 if (vms_debug_fileify) {
6998 fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7002 trnlnm_iter_count = 0;
7005 while (*cp3 != ':' && *cp3) cp3++;
7007 if (strchr(cp3,']') != NULL) break;
7008 trnlnm_iter_count++;
7009 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER+1) break;
7010 } while (vmstrnenv(tmp,tmp,0,fildev,0));
7015 *(cp1++) = *(cp3++);
7016 if (cp1 - rslt > (VMS_MAXRSS - 1)) {
7018 set_errno(ENAMETOOLONG);
7019 set_vaxc_errno(SS$_BUFFEROVF);
7020 if (vms_debug_fileify) {
7021 fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7023 return NULL; /* No room */
7028 if ((*cp2 == '^')) {
7029 /* EFS file escape, pass the next character as is */
7030 /* Fix me: HEX encoding for Unicode not implemented */
7033 else if ( *cp2 == '.') {
7034 if (*(cp2+1) == '.' && *(cp2+2) == '.') {
7035 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
7042 for (; cp2 <= dirend; cp2++) {
7043 if ((*cp2 == '^')) {
7044 /* EFS file escape, pass the next character as is */
7045 /* Fix me: HEX encoding for Unicode not implemented */
7046 *(cp1++) = *(++cp2);
7047 /* An escaped dot stays as is -- don't convert to slash */
7048 if (*cp2 == '.') cp2++;
7052 if (*(cp2+1) == '[') cp2++;
7054 else if (*cp2 == ']' || *cp2 == '>') {
7055 if (*(cp1-1) != '/') *(cp1++) = '/'; /* Don't double after ellipsis */
7057 else if ((*cp2 == '.') && (*cp2-1 != '^')) {
7059 if (*(cp2+1) == ']' || *(cp2+1) == '>') {
7060 while (*(cp2+1) == ']' || *(cp2+1) == '>' ||
7061 *(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
7062 if (!strncmp(cp2,"[000000",7) && (*(cp2+7) == ']' ||
7063 *(cp2+7) == '>' || *(cp2+7) == '.')) cp2 += 7;
7065 else if ( *(cp2+1) == '.' && *(cp2+2) == '.') {
7066 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) ='/';
7070 else if (*cp2 == '-') {
7071 if (*(cp2-1) == '[' || *(cp2-1) == '<' || *(cp2-1) == '.') {
7072 while (*cp2 == '-') {
7074 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
7076 if (*cp2 != '.' && *cp2 != ']' && *cp2 != '>') { /* we don't allow */
7077 /* filespecs like */
7078 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); /* [fred.--foo.bar] */
7079 if (vms_debug_fileify) {
7080 fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7085 else *(cp1++) = *cp2;
7087 else *(cp1++) = *cp2;
7089 /* Translate the rest of the filename. */
7094 /* Fixme - for compatibility with the CRTL we should be removing */
7095 /* spaces from the file specifications, but this may show that */
7096 /* some tests that were appearing to pass are not really passing */
7102 /* Fix me hex expansions not implemented */
7103 cp2++; /* '^.' --> '.' and other. */
7109 *(cp1++) = *(cp2++);
7114 if (decc_filename_unix_no_version) {
7115 /* Easy, drop the version */
7120 /* Punt - passing the version as a dot will probably */
7121 /* break perl in weird ways, but so did passing */
7122 /* through the ; as a version. Follow the CRTL and */
7123 /* hope for the best. */
7130 /* We will need to fix this properly later */
7131 /* As Perl may be installed on an ODS-5 volume, but not */
7132 /* have the EFS_CHARSET enabled, it still may encounter */
7133 /* filenames with extra dots in them, and a precedent got */
7134 /* set which allowed them to work, that we will uphold here */
7135 /* If extra dots are present in a name and no ^ is on them */
7136 /* VMS assumes that the first one is the extension delimiter */
7137 /* the rest have an implied ^. */
7139 /* this is also a conflict as the . is also a version */
7140 /* delimiter in VMS, */
7142 *(cp1++) = *(cp2++);
7146 /* This is an extension */
7147 if (decc_readdir_dropdotnotype) {
7149 if ((!*cp2) || (*cp2 == ';') || (*cp2 == '.')) {
7150 /* Drop the dot for the extension */
7158 *(cp1++) = *(cp2++);
7163 /* This still leaves /000000/ when working with a
7164 * VMS device root or concealed root.
7170 ulen = strlen(rslt);
7172 /* Get rid of "000000/ in rooted filespecs */
7174 zeros = strstr(rslt, "/000000/");
7175 if (zeros != NULL) {
7177 mlen = ulen - (zeros - rslt) - 7;
7178 memmove(zeros, &zeros[7], mlen);
7185 if (vms_debug_fileify) {
7186 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
7190 } /* end of int_tounixspec() */
7193 /*{{{ char *tounixspec[_ts](char *spec, char *buf, int *)*/
7194 static char *mp_do_tounixspec(pTHX_ const char *spec, char *buf, int ts, int * utf8_fl)
7196 static char __tounixspec_retbuf[VMS_MAXRSS];
7197 char * unixspec, *ret_spec, *ret_buf;
7201 if (ret_buf == NULL) {
7203 Newx(unixspec, VMS_MAXRSS, char);
7204 if (unixspec == NULL)
7205 _ckvmssts(SS$_INSFMEM);
7208 ret_buf = __tounixspec_retbuf;
7212 ret_spec = int_tounixspec(spec, ret_buf, utf8_fl);
7214 if (ret_spec == NULL) {
7215 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
7222 } /* end of do_tounixspec() */
7224 /* External entry points */
7225 char *Perl_tounixspec(pTHX_ const char *spec, char *buf)
7226 { return do_tounixspec(spec,buf,0, NULL); }
7227 char *Perl_tounixspec_ts(pTHX_ const char *spec, char *buf)
7228 { return do_tounixspec(spec,buf,1, NULL); }
7229 char *Perl_tounixspec_utf8(pTHX_ const char *spec, char *buf, int * utf8_fl)
7230 { return do_tounixspec(spec,buf,0, utf8_fl); }
7231 char *Perl_tounixspec_utf8_ts(pTHX_ const char *spec, char *buf, int * utf8_fl)
7232 { return do_tounixspec(spec,buf,1, utf8_fl); }
7234 #if __CRTL_VER >= 70200000 && !defined(__VAX)
7237 This procedure is used to identify if a path is based in either
7238 the old SYS$POSIX_ROOT: or the new 8.3 RMS based POSIX root, and
7239 it returns the OpenVMS format directory for it.
7241 It is expecting specifications of only '/' or '/xxxx/'
7243 If a posix root does not exist, or 'xxxx' is not a directory
7244 in the posix root, it returns a failure.
7246 FIX-ME: xxxx could be in UTF-8 and needs to be returned in VTF-7.
7248 It is used only internally by posix_to_vmsspec_hardway().
7251 static int posix_root_to_vms
7252 (char *vmspath, int vmspath_len,
7253 const char *unixpath,
7254 const int * utf8_fl)
7257 struct FAB myfab = cc$rms_fab;
7258 rms_setup_nam(mynam);
7259 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7260 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7261 char * esa, * esal, * rsa, * rsal;
7268 unixlen = strlen(unixpath);
7273 #if __CRTL_VER >= 80200000
7274 /* If not a posix spec already, convert it */
7275 if (decc_posix_compliant_pathnames) {
7276 if (strncmp(unixpath,"\"^UP^",5) != 0) {
7277 sprintf(vmspath,"\"^UP^%s\"",unixpath);
7280 /* This is already a VMS specification, no conversion */
7282 strncpy(vmspath,unixpath, vmspath_len);
7291 /* Check to see if this is under the POSIX root */
7292 if (decc_disable_posix_root) {
7296 /* Skip leading / */
7297 if (unixpath[0] == '/') {
7303 strcpy(vmspath,"SYS$POSIX_ROOT:");
7305 /* If this is only the / , or blank, then... */
7306 if (unixpath[0] == '\0') {
7307 /* by definition, this is the answer */
7311 /* Need to look up a directory */
7315 /* Copy and add '^' escape characters as needed */
7318 while (unixpath[i] != 0) {
7321 j += copy_expand_unix_filename_escape
7322 (&vmspath[j], &unixpath[i], &k, utf8_fl);
7326 path_len = strlen(vmspath);
7327 if (vmspath[path_len - 1] == '/')
7329 vmspath[path_len] = ']';
7331 vmspath[path_len] = '\0';
7334 vmspath[vmspath_len] = 0;
7335 if (unixpath[unixlen - 1] == '/')
7337 esal = PerlMem_malloc(VMS_MAXRSS);
7338 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7339 esa = PerlMem_malloc(NAM$C_MAXRSS + 1);
7340 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7341 rsal = PerlMem_malloc(VMS_MAXRSS);
7342 if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7343 rsa = PerlMem_malloc(NAM$C_MAXRSS + 1);
7344 if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7345 rms_set_fna(myfab, mynam, (char *) vmspath, strlen(vmspath)); /* cast ok */
7346 rms_bind_fab_nam(myfab, mynam);
7347 rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS - 1);
7348 rms_set_rsal(mynam, rsa, NAM$C_MAXRSS, rsal, VMS_MAXRSS - 1);
7349 if (decc_efs_case_preserve)
7350 mynam.naml$b_nop |= NAM$M_NO_SHORT_UPCASE;
7351 #ifdef NAML$M_OPEN_SPECIAL
7352 mynam.naml$l_input_flags |= NAML$M_OPEN_SPECIAL;
7355 /* Set up the remaining naml fields */
7356 sts = sys$parse(&myfab);
7358 /* It failed! Try again as a UNIX filespec */
7367 /* get the Device ID and the FID */
7368 sts = sys$search(&myfab);
7370 /* These are no longer needed */
7375 /* on any failure, returned the POSIX ^UP^ filespec */
7380 specdsc.dsc$a_pointer = vmspath;
7381 specdsc.dsc$w_length = vmspath_len;
7383 dvidsc.dsc$a_pointer = &mynam.naml$t_dvi[1];
7384 dvidsc.dsc$w_length = mynam.naml$t_dvi[0];
7385 sts = lib$fid_to_name
7386 (&dvidsc, mynam.naml$w_fid, &specdsc, &specdsc.dsc$w_length);
7388 /* on any failure, returned the POSIX ^UP^ filespec */
7390 /* This can happen if user does not have permission to read directories */
7391 if (strncmp(unixpath,"\"^UP^",5) != 0)
7392 sprintf(vmspath,"\"^UP^%s\"",unixpath);
7394 strcpy(vmspath, unixpath);
7397 vmspath[specdsc.dsc$w_length] = 0;
7399 /* Are we expecting a directory? */
7400 if (dir_flag != 0) {
7406 i = specdsc.dsc$w_length - 1;
7410 /* Version must be '1' */
7411 if (vmspath[i--] != '1')
7413 /* Version delimiter is one of ".;" */
7414 if ((vmspath[i] != '.') && (vmspath[i] != ';'))
7417 if (vmspath[i--] != 'R')
7419 if (vmspath[i--] != 'I')
7421 if (vmspath[i--] != 'D')
7423 if (vmspath[i--] != '.')
7425 eptr = &vmspath[i+1];
7427 if ((vmspath[i] == ']') || (vmspath[i] == '>')) {
7428 if (vmspath[i-1] != '^') {
7436 /* Get rid of 6 imaginary zero directory filename */
7437 vmspath[i+1] = '\0';
7441 if (vmspath[i] == '0')
7455 /* /dev/mumble needs to be handled special.
7456 /dev/null becomes NLA0:, And there is the potential for other stuff
7457 like /dev/tty which may need to be mapped to something.
7461 slash_dev_special_to_vms
7462 (const char * unixptr,
7472 nextslash = strchr(unixptr, '/');
7473 len = strlen(unixptr);
7474 if (nextslash != NULL)
7475 len = nextslash - unixptr;
7476 cmp = strncmp("null", unixptr, 5);
7478 if (vmspath_len >= 6) {
7479 strcpy(vmspath, "_NLA0:");
7486 /* The built in routines do not understand perl's special needs, so
7487 doing a manual conversion from UNIX to VMS
7489 If the utf8_fl is not null and points to a non-zero value, then
7490 treat 8 bit characters as UTF-8.
7492 The sequence starting with '$(' and ending with ')' will be passed
7493 through with out interpretation instead of being escaped.
7496 static int posix_to_vmsspec_hardway
7497 (char *vmspath, int vmspath_len,
7498 const char *unixpath,
7503 const char *unixptr;
7504 const char *unixend;
7506 const char *lastslash;
7507 const char *lastdot;
7513 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
7514 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
7516 if (utf8_fl != NULL)
7522 /* Ignore leading "/" characters */
7523 while((unixptr[0] == '/') && (unixptr[1] == '/')) {
7526 unixlen = strlen(unixptr);
7528 /* Do nothing with blank paths */
7535 /* This could have a "^UP^ on the front */
7536 if (strncmp(unixptr,"\"^UP^",5) == 0) {
7542 lastslash = strrchr(unixptr,'/');
7543 lastdot = strrchr(unixptr,'.');
7544 unixend = strrchr(unixptr,'\"');
7545 if (!quoted || !((unixend != NULL) && (unixend[1] == '\0'))) {
7546 unixend = unixptr + unixlen;
7549 /* last dot is last dot or past end of string */
7550 if (lastdot == NULL)
7551 lastdot = unixptr + unixlen;
7553 /* if no directories, set last slash to beginning of string */
7554 if (lastslash == NULL) {
7555 lastslash = unixptr;
7558 /* Watch out for trailing "." after last slash, still a directory */
7559 if ((lastslash[1] == '.') && (lastslash[2] == '\0')) {
7560 lastslash = unixptr + unixlen;
7563 /* Watch out for traiing ".." after last slash, still a directory */
7564 if ((lastslash[1] == '.')&&(lastslash[2] == '.')&&(lastslash[3] == '\0')) {
7565 lastslash = unixptr + unixlen;
7568 /* dots in directories are aways escaped */
7569 if (lastdot < lastslash)
7570 lastdot = unixptr + unixlen;
7573 /* if (unixptr < lastslash) then we are in a directory */
7580 /* Start with the UNIX path */
7581 if (*unixptr != '/') {
7582 /* relative paths */
7584 /* If allowing logical names on relative pathnames, then handle here */
7585 if ((unixptr[0] != '.') && !decc_disable_to_vms_logname_translation &&
7586 !decc_posix_compliant_pathnames) {
7592 /* Find the next slash */
7593 nextslash = strchr(unixptr,'/');
7595 esa = PerlMem_malloc(vmspath_len);
7596 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7598 trn = PerlMem_malloc(VMS_MAXRSS);
7599 if (trn == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7601 if (nextslash != NULL) {
7603 seg_len = nextslash - unixptr;
7604 strncpy(esa, unixptr, seg_len);
7608 strcpy(esa, unixptr);
7609 seg_len = strlen(unixptr);
7611 /* trnlnm(section) */
7612 islnm = vmstrnenv(esa, trn, 0, fildev, 0);
7615 /* Now fix up the directory */
7617 /* Split up the path to find the components */
7618 sts = vms_split_path
7637 /* A logical name must be a directory or the full
7638 specification. It is only a full specification if
7639 it is the only component */
7640 if ((unixptr[seg_len] == '\0') ||
7641 (unixptr[seg_len+1] == '\0')) {
7643 /* Is a directory being required? */
7644 if (((n_len + e_len) != 0) && (dir_flag !=0)) {
7645 /* Not a logical name */
7650 if ((unixptr[seg_len] == '/') || (dir_flag != 0)) {
7651 /* This must be a directory */
7652 if (((n_len + e_len) == 0)&&(seg_len <= vmspath_len)) {
7653 strcpy(vmsptr, esa);
7654 vmslen=strlen(vmsptr);
7655 vmsptr[vmslen] = ':';
7657 vmsptr[vmslen] = '\0';
7665 /* must be dev/directory - ignore version */
7666 if ((n_len + e_len) != 0)
7669 /* transfer the volume */
7670 if (v_len > 0 && ((v_len + vmslen) < vmspath_len)) {
7671 strncpy(vmsptr, v_spec, v_len);
7677 /* unroot the rooted directory */
7678 if ((r_len > 0) && ((r_len + d_len + vmslen) < vmspath_len)) {
7680 r_spec[r_len - 1] = ']';
7682 /* This should not be there, but nothing is perfect */
7684 cmp = strcmp(&r_spec[1], "000000.");
7694 strncpy(vmsptr, r_spec, r_len);
7700 /* Bring over the directory. */
7702 ((d_len + vmslen) < vmspath_len)) {
7704 d_spec[d_len - 1] = ']';
7706 cmp = strcmp(&d_spec[1], "000000.");
7717 /* Remove the redundant root */
7725 strncpy(vmsptr, d_spec, d_len);
7739 if (lastslash > unixptr) {
7742 /* skip leading ./ */
7744 while ((unixptr[0] == '.') && (unixptr[1] == '/')) {
7750 /* Are we still in a directory? */
7751 if (unixptr <= lastslash) {
7756 /* if not backing up, then it is relative forward. */
7757 if (!((*unixptr == '.') && (unixptr[1] == '.') &&
7758 ((unixptr[2] == '/') || (&unixptr[2] == unixend)))) {
7766 /* Perl wants an empty directory here to tell the difference
7767 * between a DCL commmand and a filename
7776 /* Handle two special files . and .. */
7777 if (unixptr[0] == '.') {
7778 if (&unixptr[1] == unixend) {
7785 if ((unixptr[1] == '.') && (&unixptr[2] == unixend)) {
7796 else { /* Absolute PATH handling */
7800 /* Need to find out where root is */
7802 /* In theory, this procedure should never get an absolute POSIX pathname
7803 * that can not be found on the POSIX root.
7804 * In practice, that can not be relied on, and things will show up
7805 * here that are a VMS device name or concealed logical name instead.
7806 * So to make things work, this procedure must be tolerant.
7808 esa = PerlMem_malloc(vmspath_len);
7809 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7812 nextslash = strchr(&unixptr[1],'/');
7814 if (nextslash != NULL) {
7816 seg_len = nextslash - &unixptr[1];
7817 strncpy(vmspath, unixptr, seg_len + 1);
7818 vmspath[seg_len+1] = 0;
7821 cmp = strncmp(vmspath, "dev", 4);
7823 sts = slash_dev_special_to_vms(unixptr, vmspath, vmspath_len);
7824 if (sts = SS$_NORMAL)
7828 sts = posix_root_to_vms(esa, vmspath_len, vmspath, utf8_fl);
7831 if ($VMS_STATUS_SUCCESS(sts)) {
7832 /* This is verified to be a real path */
7834 sts = posix_root_to_vms(esa, vmspath_len, "/", NULL);
7835 if ($VMS_STATUS_SUCCESS(sts)) {
7836 strcpy(vmspath, esa);
7837 vmslen = strlen(vmspath);
7838 vmsptr = vmspath + vmslen;
7840 if (unixptr < lastslash) {
7849 cmp = strcmp(rptr,"000000.");
7854 } /* removing 6 zeros */
7855 } /* vmslen < 7, no 6 zeros possible */
7856 } /* Not in a directory */
7857 } /* Posix root found */
7859 /* No posix root, fall back to default directory */
7860 strcpy(vmspath, "SYS$DISK:[");
7861 vmsptr = &vmspath[10];
7863 if (unixptr > lastslash) {
7872 } /* end of verified real path handling */
7877 /* Ok, we have a device or a concealed root that is not in POSIX
7878 * or we have garbage. Make the best of it.
7881 /* Posix to VMS destroyed this, so copy it again */
7882 strncpy(vmspath, &unixptr[1], seg_len);
7883 vmspath[seg_len] = 0;
7885 vmsptr = &vmsptr[vmslen];
7888 /* Now do we need to add the fake 6 zero directory to it? */
7890 if ((*lastslash == '/') && (nextslash < lastslash)) {
7891 /* No there is another directory */
7898 /* now we have foo:bar or foo:[000000]bar to decide from */
7899 islnm = vmstrnenv(vmspath, esa, 0, fildev, 0);
7901 if (!islnm && !decc_posix_compliant_pathnames) {
7903 cmp = strncmp("bin", vmspath, 4);
7905 /* bin => SYS$SYSTEM: */
7906 islnm = vmstrnenv("SYS$SYSTEM:", esa, 0, fildev, 0);
7909 /* tmp => SYS$SCRATCH: */
7910 cmp = strncmp("tmp", vmspath, 4);
7912 islnm = vmstrnenv("SYS$SCRATCH:", esa, 0, fildev, 0);
7917 trnend = islnm ? islnm - 1 : 0;
7919 /* if this was a logical name, ']' or '>' must be present */
7920 /* if not a logical name, then assume a device and hope. */
7921 islnm = trnend ? (esa[trnend] == ']' || esa[trnend] == '>') : 0;
7923 /* if log name and trailing '.' then rooted - treat as device */
7924 add_6zero = islnm ? (esa[trnend-1] == '.') : 0;
7926 /* Fix me, if not a logical name, a device lookup should be
7927 * done to see if the device is file structured. If the device
7928 * is not file structured, the 6 zeros should not be put on.
7930 * As it is, perl is occasionally looking for dev:[000000]tty.
7931 * which looks a little strange.
7933 * Not that easy to detect as "/dev" may be file structured with
7934 * special device files.
7937 if ((add_6zero == 0) && (*nextslash == '/') &&
7938 (&nextslash[1] == unixend)) {
7939 /* No real directory present */
7944 /* Put the device delimiter on */
7947 unixptr = nextslash;
7950 /* Start directory if needed */
7951 if (!islnm || add_6zero) {
7957 /* add fake 000000] if needed */
7970 } /* non-POSIX translation */
7972 } /* End of relative/absolute path handling */
7974 while ((unixptr <= unixend) && (vmslen < vmspath_len)){
7981 if (dir_start != 0) {
7983 /* First characters in a directory are handled special */
7984 while ((*unixptr == '/') ||
7985 ((*unixptr == '.') &&
7986 ((unixptr[1]=='.') || (unixptr[1]=='/') ||
7987 (&unixptr[1]==unixend)))) {
7992 /* Skip redundant / in specification */
7993 while ((*unixptr == '/') && (dir_start != 0)) {
7996 if (unixptr == lastslash)
7999 if (unixptr == lastslash)
8002 /* Skip redundant ./ characters */
8003 while ((*unixptr == '.') &&
8004 ((unixptr[1] == '/')||(&unixptr[1] == unixend))) {
8007 if (unixptr == lastslash)
8009 if (*unixptr == '/')
8012 if (unixptr == lastslash)
8015 /* Skip redundant ../ characters */
8016 while ((*unixptr == '.') && (unixptr[1] == '.') &&
8017 ((unixptr[2] == '/') || (&unixptr[2] == unixend))) {
8018 /* Set the backing up flag */
8024 unixptr++; /* first . */
8025 unixptr++; /* second . */
8026 if (unixptr == lastslash)
8028 if (*unixptr == '/') /* The slash */
8031 if (unixptr == lastslash)
8034 /* To do: Perl expects /.../ to be translated to [...] on VMS */
8035 /* Not needed when VMS is pretending to be UNIX. */
8037 /* Is this loop stuck because of too many dots? */
8038 if (loop_flag == 0) {
8039 /* Exit the loop and pass the rest through */
8044 /* Are we done with directories yet? */
8045 if (unixptr >= lastslash) {
8047 /* Watch out for trailing dots */
8056 if (*unixptr == '/')
8060 /* Have we stopped backing up? */
8065 /* dir_start continues to be = 1 */
8067 if (*unixptr == '-') {
8069 *vmsptr++ = *unixptr++;
8073 /* Now are we done with directories yet? */
8074 if (unixptr >= lastslash) {
8076 /* Watch out for trailing dots */
8092 if (unixptr >= unixend)
8095 /* Normal characters - More EFS work probably needed */
8101 /* remove multiple / */
8102 while (unixptr[1] == '/') {
8105 if (unixptr == lastslash) {
8106 /* Watch out for trailing dots */
8118 /* To do: Perl expects /.../ to be translated to [...] on VMS */
8119 /* Not needed when VMS is pretending to be UNIX. */
8123 if (unixptr != unixend)
8128 if ((unixptr < lastdot) || (unixptr < lastslash) ||
8129 (&unixptr[1] == unixend)) {
8135 /* trailing dot ==> '^..' on VMS */
8136 if (unixptr == unixend) {
8144 *vmsptr++ = *unixptr++;
8148 if (quoted && (&unixptr[1] == unixend)) {
8152 in_cnt = copy_expand_unix_filename_escape
8153 (vmsptr, unixptr, &out_cnt, utf8_fl);
8163 in_cnt = copy_expand_unix_filename_escape
8164 (vmsptr, unixptr, &out_cnt, utf8_fl);
8171 /* Make sure directory is closed */
8172 if (unixptr == lastslash) {
8174 vmsptr2 = vmsptr - 1;
8176 if (*vmsptr2 != ']') {
8179 /* directories do not end in a dot bracket */
8180 if (*vmsptr2 == '.') {
8184 if (*vmsptr2 != '^') {
8185 vmsptr--; /* back up over the dot */
8193 /* Add a trailing dot if a file with no extension */
8194 vmsptr2 = vmsptr - 1;
8196 (*vmsptr2 != ']') && (*vmsptr2 != '*') && (*vmsptr2 != '%') &&
8197 (*vmsptr2 != ')') && (*lastdot != '.')) {
8208 /* Eventual routine to convert a UTF-8 specification to VTF-7. */
8209 static char * utf8_to_vtf7(char * rslt, const char * path, int *utf8_fl)
8214 /* If a UTF8 flag is being passed, honor it */
8216 if (utf8_fl != NULL) {
8217 utf8_flag = *utf8_fl;
8222 /* If there is a possibility of UTF8, then if any UTF8 characters
8223 are present, then they must be converted to VTF-7
8225 result = strcpy(rslt, path); /* FIX-ME */
8228 result = strcpy(rslt, path);
8235 /*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/
8236 static char *int_tovmsspec
8237 (const char *path, char *rslt, int dir_flag, int * utf8_flag) {
8243 unsigned long int infront = 0, hasdir = 1;
8246 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
8247 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
8249 if (vms_debug_fileify) {
8251 fprintf(stderr, "int_tovmsspec: path = NULL\n");
8253 fprintf(stderr, "int_tovmsspec: path = %s\n", path);
8257 /* If we fail, we should be setting errno */
8259 set_vaxc_errno(SS$_BADPARAM);
8262 rslt_len = VMS_MAXRSS-1;
8264 /* '.' and '..' are "[]" and "[-]" for a quick check */
8265 if (path[0] == '.') {
8266 if (path[1] == '\0') {
8268 if (utf8_flag != NULL)
8273 if (path[1] == '.' && path[2] == '\0') {
8275 if (utf8_flag != NULL)
8282 /* Posix specifications are now a native VMS format */
8283 /*--------------------------------------------------*/
8284 #if __CRTL_VER >= 80200000 && !defined(__VAX)
8285 if (decc_posix_compliant_pathnames) {
8286 if (strncmp(path,"\"^UP^",5) == 0) {
8287 posix_to_vmsspec_hardway(rslt, rslt_len, path, dir_flag, utf8_flag);
8293 /* This is really the only way to see if this is already in VMS format */
8294 sts = vms_split_path
8309 /* FIX-ME - If dir_flag is non-zero, then this is a mp_do_vmspath()
8310 replacement, because the above parse just took care of most of
8311 what is needed to do vmspath when the specification is already
8314 And if it is not already, it is easier to do the conversion as
8315 part of this routine than to call this routine and then work on
8319 /* If VMS punctuation was found, it is already VMS format */
8320 if ((v_len != 0) || (r_len != 0) || (d_len != 0) || (vs_len != 0)) {
8321 if (utf8_flag != NULL)
8324 if (vms_debug_fileify) {
8325 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8329 /* Now, what to do with trailing "." cases where there is no
8330 extension? If this is a UNIX specification, and EFS characters
8331 are enabled, then the trailing "." should be converted to a "^.".
8332 But if this was already a VMS specification, then it should be
8335 So in the case of ambiguity, leave the specification alone.
8339 /* If there is a possibility of UTF8, then if any UTF8 characters
8340 are present, then they must be converted to VTF-7
8342 if (utf8_flag != NULL)
8345 if (vms_debug_fileify) {
8346 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8351 dirend = strrchr(path,'/');
8353 if (dirend == NULL) {
8357 /* If we get here with no UNIX directory delimiters, then this is
8358 not a complete file specification, either garbage a UNIX glob
8359 specification that can not be converted to a VMS wildcard, or
8360 it a UNIX shell macro. MakeMaker wants shell macros passed
8363 utf8 flag setting needs to be preserved.
8368 macro_start = strchr(path,'$');
8369 if (macro_start != NULL) {
8370 if (macro_start[1] == '(') {
8374 if ((decc_efs_charset == 0) || (has_macro)) {
8376 if (vms_debug_fileify) {
8377 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8383 /* If POSIX mode active, handle the conversion */
8384 #if __CRTL_VER >= 80200000 && !defined(__VAX)
8385 if (decc_efs_charset) {
8386 posix_to_vmsspec_hardway(rslt, rslt_len, path, dir_flag, utf8_flag);
8387 if (vms_debug_fileify) {
8388 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8394 if (*(dirend+1) == '.') { /* do we have trailing "/." or "/.." or "/..."? */
8395 if (!*(dirend+2)) dirend +=2;
8396 if (*(dirend+2) == '.' && !*(dirend+3)) dirend += 3;
8397 if (decc_efs_charset == 0) {
8398 if (*(dirend+2) == '.' && *(dirend+3) == '.' && !*(dirend+4)) dirend += 4;
8404 lastdot = strrchr(cp2,'.');
8410 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
8412 if (decc_disable_posix_root) {
8413 strcpy(rslt,"sys$disk:[000000]");
8416 strcpy(rslt,"sys$posix_root:[000000]");
8418 if (utf8_flag != NULL)
8420 if (vms_debug_fileify) {
8421 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8425 while (*(++cp2) != '/' && *cp2) *(cp1++) = *cp2;
8427 trndev = PerlMem_malloc(VMS_MAXRSS);
8428 if (trndev == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8429 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8431 /* DECC special handling */
8433 if (strcmp(rslt,"bin") == 0) {
8434 strcpy(rslt,"sys$system");
8437 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8439 else if (strcmp(rslt,"tmp") == 0) {
8440 strcpy(rslt,"sys$scratch");
8443 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8445 else if (!decc_disable_posix_root) {
8446 strcpy(rslt, "sys$posix_root");
8450 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
8451 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8453 else if (strcmp(rslt,"dev") == 0) {
8454 if (strncmp(cp2,"/null", 5) == 0) {
8455 if ((cp2[5] == 0) || (cp2[5] == '/')) {
8456 strcpy(rslt,"NLA0");
8460 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8466 trnend = islnm ? strlen(trndev) - 1 : 0;
8467 islnm = trnend ? (trndev[trnend] == ']' || trndev[trnend] == '>') : 0;
8468 rooted = islnm ? (trndev[trnend-1] == '.') : 0;
8469 /* If the first element of the path is a logical name, determine
8470 * whether it has to be translated so we can add more directories. */
8471 if (!islnm || rooted) {
8474 if (cp2 == dirend) while (infront++ < 6) *(cp1++) = '0';
8478 if (cp2 != dirend) {
8479 strcpy(rslt,trndev);
8480 cp1 = rslt + trnend;
8487 if (decc_disable_posix_root) {
8493 PerlMem_free(trndev);
8498 if (*(cp2+1) == '/' || *(cp2+1) == '\0') {
8499 cp2 += 2; /* skip over "./" - it's redundant */
8500 *(cp1++) = '.'; /* but it does indicate a relative dirspec */
8502 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
8503 *(cp1++) = '-'; /* "../" --> "-" */
8506 else if (*(cp2+1) == '.' && *(cp2+2) == '.' &&
8507 (*(cp2+3) == '/' || *(cp2+3) == '\0')) {
8508 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
8509 if (!*(cp2+4)) *(cp1++) = '.'; /* Simulate trailing '/' for later */
8512 else if ((cp2 != lastdot) || (lastdot < dirend)) {
8513 /* Escape the extra dots in EFS file specifications */
8516 if (cp2 > dirend) cp2 = dirend;
8518 else *(cp1++) = '.';
8520 for (; cp2 < dirend; cp2++) {
8522 if (*(cp2-1) == '/') continue;
8523 if (*(cp1-1) != '.') *(cp1++) = '.';
8526 else if (!infront && *cp2 == '.') {
8527 if (cp2+1 == dirend || *(cp2+1) == '\0') { cp2++; break; }
8528 else if (*(cp2+1) == '/') cp2++; /* skip over "./" - it's redundant */
8529 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
8530 if (*(cp1-1) == '-' || *(cp1-1) == '[') *(cp1++) = '-'; /* handle "../" */
8531 else if (*(cp1-2) == '[') *(cp1-1) = '-';
8532 else { /* back up over previous directory name */
8534 while (*(cp1-1) != '.' && *(cp1-1) != '[') cp1--;
8535 if (*(cp1-1) == '[') {
8536 memcpy(cp1,"000000.",7);
8541 if (cp2 == dirend) break;
8543 else if ( *(cp2+1) == '.' && *(cp2+2) == '.' &&
8544 (*(cp2+3) == '/' || *(cp2+3) == '\0') ) {
8545 if (*(cp1-1) != '.') *(cp1++) = '.'; /* May already have 1 from '/' */
8546 *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
8548 *(cp1++) = '.'; /* Simulate trailing '/' */
8549 cp2 += 2; /* for loop will incr this to == dirend */
8551 else cp2 += 3; /* Trailing '/' was there, so skip it, too */
8554 if (decc_efs_charset == 0)
8555 *(cp1++) = '_'; /* fix up syntax - '.' in name not allowed */
8557 *(cp1++) = '^'; /* fix up syntax - '.' in name is allowed */
8563 if (!infront && *(cp1-1) == '-') *(cp1++) = '.';
8565 if (decc_efs_charset == 0)
8572 else *(cp1++) = *cp2;
8576 if (*(cp1-1) == '.') cp1--; /* Unix spec ending in '/' ==> trailing '.' */
8577 if (hasdir) *(cp1++) = ']';
8578 if (*cp2) cp2++; /* check in case we ended with trailing '..' */
8579 /* fixme for ODS5 */
8586 if (decc_efs_charset == 0)
8597 if (((cp2 < lastdot) || (cp2[1] == '\0')) &&
8598 decc_readdir_dropdotnotype) {
8603 /* trailing dot ==> '^..' on VMS */
8610 *(cp1++) = *(cp2++);
8615 /* This could be a macro to be passed through */
8616 *(cp1++) = *(cp2++);
8618 const char * save_cp2;
8622 /* paranoid check */
8628 *(cp1++) = *(cp2++);
8629 if (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
8630 *(cp1++) = *(cp2++);
8631 while (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
8632 *(cp1++) = *(cp2++);
8635 *(cp1++) = *(cp2++);
8639 if (is_macro == 0) {
8640 /* Not really a macro - never mind */
8653 /* Don't escape again if following character is
8654 * already something we escape.
8656 if (strchr("\"~`!#%^&()=+\'@[]{}:\\|<>_.", *(cp2+1))) {
8657 *(cp1++) = *(cp2++);
8660 /* But otherwise fall through and escape it. */
8678 *(cp1++) = *(cp2++);
8681 /* FIXME: This needs fixing as Perl is putting ".dir;" on UNIX filespecs
8682 * which is wrong. UNIX notation should be ".dir." unless
8683 * the DECC$FILENAME_UNIX_NO_VERSION is enabled.
8684 * changing this behavior could break more things at this time.
8685 * efs character set effectively does not allow "." to be a version
8686 * delimiter as a further complication about changing this.
8688 if (decc_filename_unix_report != 0) {
8691 *(cp1++) = *(cp2++);
8694 *(cp1++) = *(cp2++);
8697 if ((no_type_seen == 1) && decc_readdir_dropdotnotype) {
8701 /* Fix me for "^]", but that requires making sure that you do
8702 * not back up past the start of the filename
8704 if ((*lcp1 != ']') && (*lcp1 != '*') && (*lcp1 != '%'))
8709 if (utf8_flag != NULL)
8711 if (vms_debug_fileify) {
8712 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8716 } /* end of int_tovmsspec() */
8719 /*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/
8720 static char *mp_do_tovmsspec
8721 (pTHX_ const char *path, char *buf, int ts, int dir_flag, int * utf8_flag) {
8722 static char __tovmsspec_retbuf[VMS_MAXRSS];
8723 char * vmsspec, *ret_spec, *ret_buf;
8727 if (ret_buf == NULL) {
8729 Newx(vmsspec, VMS_MAXRSS, char);
8730 if (vmsspec == NULL)
8731 _ckvmssts(SS$_INSFMEM);
8734 ret_buf = __tovmsspec_retbuf;
8738 ret_spec = int_tovmsspec(path, ret_buf, 0, utf8_flag);
8740 if (ret_spec == NULL) {
8741 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
8748 } /* end of mp_do_tovmsspec() */
8750 /* External entry points */
8751 char *Perl_tovmsspec(pTHX_ const char *path, char *buf)
8752 { return do_tovmsspec(path,buf,0,NULL); }
8753 char *Perl_tovmsspec_ts(pTHX_ const char *path, char *buf)
8754 { return do_tovmsspec(path,buf,1,NULL); }
8755 char *Perl_tovmsspec_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
8756 { return do_tovmsspec(path,buf,0,utf8_fl); }
8757 char *Perl_tovmsspec_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
8758 { return do_tovmsspec(path,buf,1,utf8_fl); }
8760 /*{{{ char *tovmspath[_ts](char *path, char *buf, const int *)*/
8761 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) {
8762 static char __tovmspath_retbuf[VMS_MAXRSS];
8764 char *pathified, *vmsified, *cp;
8766 if (path == NULL) return NULL;
8767 pathified = PerlMem_malloc(VMS_MAXRSS);
8768 if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
8769 if (do_pathify_dirspec(path,pathified,0,NULL) == NULL) {
8770 PerlMem_free(pathified);
8776 Newx(vmsified, VMS_MAXRSS, char);
8777 if (do_tovmsspec(pathified, buf ? buf : vmsified, 0, NULL) == NULL) {
8778 PerlMem_free(pathified);
8779 if (vmsified) Safefree(vmsified);
8782 PerlMem_free(pathified);
8787 vmslen = strlen(vmsified);
8788 Newx(cp,vmslen+1,char);
8789 memcpy(cp,vmsified,vmslen);
8795 strcpy(__tovmspath_retbuf,vmsified);
8797 return __tovmspath_retbuf;
8800 } /* end of do_tovmspath() */
8802 /* External entry points */
8803 char *Perl_tovmspath(pTHX_ const char *path, char *buf)
8804 { return do_tovmspath(path,buf,0, NULL); }
8805 char *Perl_tovmspath_ts(pTHX_ const char *path, char *buf)
8806 { return do_tovmspath(path,buf,1, NULL); }
8807 char *Perl_tovmspath_utf8(pTHX_ const char *path, char *buf, int *utf8_fl)
8808 { return do_tovmspath(path,buf,0,utf8_fl); }
8809 char *Perl_tovmspath_utf8_ts(pTHX_ const char *path, char *buf, int *utf8_fl)
8810 { return do_tovmspath(path,buf,1,utf8_fl); }
8813 /*{{{ char *tounixpath[_ts](char *path, char *buf, int * utf8_fl)*/
8814 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) {
8815 static char __tounixpath_retbuf[VMS_MAXRSS];
8817 char *pathified, *unixified, *cp;
8819 if (path == NULL) return NULL;
8820 pathified = PerlMem_malloc(VMS_MAXRSS);
8821 if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
8822 if (do_pathify_dirspec(path,pathified,0,NULL) == NULL) {
8823 PerlMem_free(pathified);
8829 Newx(unixified, VMS_MAXRSS, char);
8831 if (do_tounixspec(pathified,buf ? buf : unixified,0,NULL) == NULL) {
8832 PerlMem_free(pathified);
8833 if (unixified) Safefree(unixified);
8836 PerlMem_free(pathified);
8841 unixlen = strlen(unixified);
8842 Newx(cp,unixlen+1,char);
8843 memcpy(cp,unixified,unixlen);
8845 Safefree(unixified);
8849 strcpy(__tounixpath_retbuf,unixified);
8850 Safefree(unixified);
8851 return __tounixpath_retbuf;
8854 } /* end of do_tounixpath() */
8856 /* External entry points */
8857 char *Perl_tounixpath(pTHX_ const char *path, char *buf)
8858 { return do_tounixpath(path,buf,0,NULL); }
8859 char *Perl_tounixpath_ts(pTHX_ const char *path, char *buf)
8860 { return do_tounixpath(path,buf,1,NULL); }
8861 char *Perl_tounixpath_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
8862 { return do_tounixpath(path,buf,0,utf8_fl); }
8863 char *Perl_tounixpath_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
8864 { return do_tounixpath(path,buf,1,utf8_fl); }
8867 * @(#)argproc.c 2.2 94/08/16 Mark Pizzolato (mark AT infocomm DOT com)
8869 *****************************************************************************
8871 * Copyright (C) 1989-1994, 2007 by *
8872 * Mark Pizzolato - INFO COMM, Danville, California (510) 837-5600 *
8874 * Permission is hereby granted for the reproduction of this software *
8875 * on condition that this copyright notice is included in source *
8876 * distributions of the software. The code may be modified and *
8877 * distributed under the same terms as Perl itself. *
8879 * 27-Aug-1994 Modified for inclusion in perl5 *
8880 * by Charles Bailey (bailey AT newman DOT upenn DOT edu) *
8881 *****************************************************************************
8885 * getredirection() is intended to aid in porting C programs
8886 * to VMS (Vax-11 C). The native VMS environment does not support
8887 * '>' and '<' I/O redirection, or command line wild card expansion,
8888 * or a command line pipe mechanism using the '|' AND background
8889 * command execution '&'. All of these capabilities are provided to any
8890 * C program which calls this procedure as the first thing in the
8892 * The piping mechanism will probably work with almost any 'filter' type
8893 * of program. With suitable modification, it may useful for other
8894 * portability problems as well.
8896 * Author: Mark Pizzolato (mark AT infocomm DOT com)
8900 struct list_item *next;
8904 static void add_item(struct list_item **head,
8905 struct list_item **tail,
8909 static void mp_expand_wild_cards(pTHX_ char *item,
8910 struct list_item **head,
8911 struct list_item **tail,
8914 static int background_process(pTHX_ int argc, char **argv);
8916 static void pipe_and_fork(pTHX_ char **cmargv);
8918 /*{{{ void getredirection(int *ac, char ***av)*/
8920 mp_getredirection(pTHX_ int *ac, char ***av)
8922 * Process vms redirection arg's. Exit if any error is seen.
8923 * If getredirection() processes an argument, it is erased
8924 * from the vector. getredirection() returns a new argc and argv value.
8925 * In the event that a background command is requested (by a trailing "&"),
8926 * this routine creates a background subprocess, and simply exits the program.
8928 * Warning: do not try to simplify the code for vms. The code
8929 * presupposes that getredirection() is called before any data is
8930 * read from stdin or written to stdout.
8932 * Normal usage is as follows:
8938 * getredirection(&argc, &argv);
8942 int argc = *ac; /* Argument Count */
8943 char **argv = *av; /* Argument Vector */
8944 char *ap; /* Argument pointer */
8945 int j; /* argv[] index */
8946 int item_count = 0; /* Count of Items in List */
8947 struct list_item *list_head = 0; /* First Item in List */
8948 struct list_item *list_tail; /* Last Item in List */
8949 char *in = NULL; /* Input File Name */
8950 char *out = NULL; /* Output File Name */
8951 char *outmode = "w"; /* Mode to Open Output File */
8952 char *err = NULL; /* Error File Name */
8953 char *errmode = "w"; /* Mode to Open Error File */
8954 int cmargc = 0; /* Piped Command Arg Count */
8955 char **cmargv = NULL;/* Piped Command Arg Vector */
8958 * First handle the case where the last thing on the line ends with
8959 * a '&'. This indicates the desire for the command to be run in a
8960 * subprocess, so we satisfy that desire.
8963 if (0 == strcmp("&", ap))
8964 exit(background_process(aTHX_ --argc, argv));
8965 if (*ap && '&' == ap[strlen(ap)-1])
8967 ap[strlen(ap)-1] = '\0';
8968 exit(background_process(aTHX_ argc, argv));
8971 * Now we handle the general redirection cases that involve '>', '>>',
8972 * '<', and pipes '|'.
8974 for (j = 0; j < argc; ++j)
8976 if (0 == strcmp("<", argv[j]))
8980 fprintf(stderr,"No input file after < on command line");
8981 exit(LIB$_WRONUMARG);
8986 if ('<' == *(ap = argv[j]))
8991 if (0 == strcmp(">", ap))
8995 fprintf(stderr,"No output file after > on command line");
8996 exit(LIB$_WRONUMARG);
9015 fprintf(stderr,"No output file after > or >> on command line");
9016 exit(LIB$_WRONUMARG);
9020 if (('2' == *ap) && ('>' == ap[1]))
9037 fprintf(stderr,"No output file after 2> or 2>> on command line");
9038 exit(LIB$_WRONUMARG);
9042 if (0 == strcmp("|", argv[j]))
9046 fprintf(stderr,"No command into which to pipe on command line");
9047 exit(LIB$_WRONUMARG);
9049 cmargc = argc-(j+1);
9050 cmargv = &argv[j+1];
9054 if ('|' == *(ap = argv[j]))
9062 expand_wild_cards(ap, &list_head, &list_tail, &item_count);
9065 * Allocate and fill in the new argument vector, Some Unix's terminate
9066 * the list with an extra null pointer.
9068 argv = (char **) PerlMem_malloc((item_count+1) * sizeof(char *));
9069 if (argv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9071 for (j = 0; j < item_count; ++j, list_head = list_head->next)
9072 argv[j] = list_head->value;
9078 fprintf(stderr,"'|' and '>' may not both be specified on command line");
9079 exit(LIB$_INVARGORD);
9081 pipe_and_fork(aTHX_ cmargv);
9084 /* Check for input from a pipe (mailbox) */
9086 if (in == NULL && 1 == isapipe(0))
9088 char mbxname[L_tmpnam];
9090 long int dvi_item = DVI$_DEVBUFSIZ;
9091 $DESCRIPTOR(mbxnam, "");
9092 $DESCRIPTOR(mbxdevnam, "");
9094 /* Input from a pipe, reopen it in binary mode to disable */
9095 /* carriage control processing. */
9097 fgetname(stdin, mbxname);
9098 mbxnam.dsc$a_pointer = mbxname;
9099 mbxnam.dsc$w_length = strlen(mbxnam.dsc$a_pointer);
9100 lib$getdvi(&dvi_item, 0, &mbxnam, &bufsize, 0, 0);
9101 mbxdevnam.dsc$a_pointer = mbxname;
9102 mbxdevnam.dsc$w_length = sizeof(mbxname);
9103 dvi_item = DVI$_DEVNAM;
9104 lib$getdvi(&dvi_item, 0, &mbxnam, 0, &mbxdevnam, &mbxdevnam.dsc$w_length);
9105 mbxdevnam.dsc$a_pointer[mbxdevnam.dsc$w_length] = '\0';
9108 freopen(mbxname, "rb", stdin);
9111 fprintf(stderr,"Can't reopen input pipe (name: %s) in binary mode",mbxname);
9115 if ((in != NULL) && (NULL == freopen(in, "r", stdin, "mbc=32", "mbf=2")))
9117 fprintf(stderr,"Can't open input file %s as stdin",in);
9120 if ((out != NULL) && (NULL == freopen(out, outmode, stdout, "mbc=32", "mbf=2")))
9122 fprintf(stderr,"Can't open output file %s as stdout",out);
9125 if (out != NULL) Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",out);
9128 if (strcmp(err,"&1") == 0) {
9129 dup2(fileno(stdout), fileno(stderr));
9130 Perl_vmssetuserlnm(aTHX_ "SYS$ERROR","SYS$OUTPUT");
9133 if (NULL == (tmperr = fopen(err, errmode, "mbc=32", "mbf=2")))
9135 fprintf(stderr,"Can't open error file %s as stderr",err);
9139 if (NULL == freopen(err, "a", stderr, "mbc=32", "mbf=2"))
9143 Perl_vmssetuserlnm(aTHX_ "SYS$ERROR",err);
9146 #ifdef ARGPROC_DEBUG
9147 PerlIO_printf(Perl_debug_log, "Arglist:\n");
9148 for (j = 0; j < *ac; ++j)
9149 PerlIO_printf(Perl_debug_log, "argv[%d] = '%s'\n", j, argv[j]);
9151 /* Clear errors we may have hit expanding wildcards, so they don't
9152 show up in Perl's $! later */
9153 set_errno(0); set_vaxc_errno(1);
9154 } /* end of getredirection() */
9157 static void add_item(struct list_item **head,
9158 struct list_item **tail,
9164 *head = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
9165 if (head == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9169 (*tail)->next = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
9170 if ((*tail)->next == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9171 *tail = (*tail)->next;
9173 (*tail)->value = value;
9177 static void mp_expand_wild_cards(pTHX_ char *item,
9178 struct list_item **head,
9179 struct list_item **tail,
9183 unsigned long int context = 0;
9191 $DESCRIPTOR(filespec, "");
9192 $DESCRIPTOR(defaultspec, "SYS$DISK:[]");
9193 $DESCRIPTOR(resultspec, "");
9194 unsigned long int lff_flags = 0;
9198 #ifdef VMS_LONGNAME_SUPPORT
9199 lff_flags = LIB$M_FIL_LONG_NAMES;
9202 for (cp = item; *cp; cp++) {
9203 if (*cp == '*' || *cp == '%' || isspace(*cp)) break;
9204 if (*cp == '.' && *(cp-1) == '.' && *(cp-2) =='.') break;
9206 if (!*cp || isspace(*cp))
9208 add_item(head, tail, item, count);
9213 /* "double quoted" wild card expressions pass as is */
9214 /* From DCL that means using e.g.: */
9215 /* perl program """perl.*""" */
9216 item_len = strlen(item);
9217 if ( '"' == *item && '"' == item[item_len-1] )
9220 item[item_len-2] = '\0';
9221 add_item(head, tail, item, count);
9225 resultspec.dsc$b_dtype = DSC$K_DTYPE_T;
9226 resultspec.dsc$b_class = DSC$K_CLASS_D;
9227 resultspec.dsc$a_pointer = NULL;
9228 vmsspec = PerlMem_malloc(VMS_MAXRSS);
9229 if (vmsspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9230 if ((isunix = (int) strchr(item,'/')) != (int) NULL)
9231 filespec.dsc$a_pointer = int_tovmsspec(item, vmsspec, 0, NULL);
9232 if (!isunix || !filespec.dsc$a_pointer)
9233 filespec.dsc$a_pointer = item;
9234 filespec.dsc$w_length = strlen(filespec.dsc$a_pointer);
9236 * Only return version specs, if the caller specified a version
9238 had_version = strchr(item, ';');
9240 * Only return device and directory specs, if the caller specifed either.
9242 had_device = strchr(item, ':');
9243 had_directory = (isunix || NULL != strchr(item, '[')) || (NULL != strchr(item, '<'));
9245 while ($VMS_STATUS_SUCCESS(sts = lib$find_file
9246 (&filespec, &resultspec, &context,
9247 &defaultspec, 0, &rms_sts, &lff_flags)))
9252 string = PerlMem_malloc(resultspec.dsc$w_length+1);
9253 if (string == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9254 strncpy(string, resultspec.dsc$a_pointer, resultspec.dsc$w_length);
9255 string[resultspec.dsc$w_length] = '\0';
9256 if (NULL == had_version)
9257 *(strrchr(string, ';')) = '\0';
9258 if ((!had_directory) && (had_device == NULL))
9260 if (NULL == (devdir = strrchr(string, ']')))
9261 devdir = strrchr(string, '>');
9262 strcpy(string, devdir + 1);
9265 * Be consistent with what the C RTL has already done to the rest of
9266 * the argv items and lowercase all of these names.
9268 if (!decc_efs_case_preserve) {
9269 for (c = string; *c; ++c)
9273 if (isunix) trim_unixpath(string,item,1);
9274 add_item(head, tail, string, count);
9277 PerlMem_free(vmsspec);
9278 if (sts != RMS$_NMF)
9280 set_vaxc_errno(sts);
9283 case RMS$_FNF: case RMS$_DNF:
9284 set_errno(ENOENT); break;
9286 set_errno(ENOTDIR); break;
9288 set_errno(ENODEV); break;
9289 case RMS$_FNM: case RMS$_SYN:
9290 set_errno(EINVAL); break;
9292 set_errno(EACCES); break;
9294 _ckvmssts_noperl(sts);
9298 add_item(head, tail, item, count);
9299 _ckvmssts_noperl(lib$sfree1_dd(&resultspec));
9300 _ckvmssts_noperl(lib$find_file_end(&context));
9303 static int child_st[2];/* Event Flag set when child process completes */
9305 static unsigned short child_chan;/* I/O Channel for Pipe Mailbox */
9307 static unsigned long int exit_handler(int *status)
9311 if (0 == child_st[0])
9313 #ifdef ARGPROC_DEBUG
9314 PerlIO_printf(Perl_debug_log, "Waiting for Child Process to Finish . . .\n");
9316 fflush(stdout); /* Have to flush pipe for binary data to */
9317 /* terminate properly -- <tp@mccall.com> */
9318 sys$qiow(0, child_chan, IO$_WRITEOF, iosb, 0, 0, 0, 0, 0, 0, 0, 0);
9319 sys$dassgn(child_chan);
9321 sys$synch(0, child_st);
9326 static void sig_child(int chan)
9328 #ifdef ARGPROC_DEBUG
9329 PerlIO_printf(Perl_debug_log, "Child Completion AST\n");
9331 if (child_st[0] == 0)
9335 static struct exit_control_block exit_block =
9340 &exit_block.exit_status,
9345 pipe_and_fork(pTHX_ char **cmargv)
9348 struct dsc$descriptor_s *vmscmd;
9349 char subcmd[2*MAX_DCL_LINE_LENGTH], *p, *q;
9350 int sts, j, l, ismcr, quote, tquote = 0;
9352 sts = setup_cmddsc(aTHX_ cmargv[0],0,"e,&vmscmd);
9353 vms_execfree(vmscmd);
9358 ismcr = q && toupper(*q) == 'M' && toupper(*(q+1)) == 'C'
9359 && toupper(*(q+2)) == 'R' && !*(q+3);
9361 while (q && l < MAX_DCL_LINE_LENGTH) {
9363 if (j > 0 && quote) {
9369 if (ismcr && j > 1) quote = 1;
9370 tquote = (strchr(q,' ')) != NULL || *q == '\0';
9373 if (quote || tquote) {
9379 if ((quote||tquote) && *q == '"') {
9389 fp = safe_popen(aTHX_ subcmd,"wbF",&sts);
9391 PerlIO_printf(Perl_debug_log,"Can't open output pipe (status %d)",sts);
9395 static int background_process(pTHX_ int argc, char **argv)
9397 char command[MAX_DCL_SYMBOL + 1] = "$";
9398 $DESCRIPTOR(value, "");
9399 static $DESCRIPTOR(cmd, "BACKGROUND$COMMAND");
9400 static $DESCRIPTOR(null, "NLA0:");
9401 static $DESCRIPTOR(pidsymbol, "SHELL_BACKGROUND_PID");
9403 $DESCRIPTOR(pidstr, "");
9405 unsigned long int flags = 17, one = 1, retsts;
9408 strcat(command, argv[0]);
9409 len = strlen(command);
9410 while (--argc && (len < MAX_DCL_SYMBOL))
9412 strcat(command, " \"");
9413 strcat(command, *(++argv));
9414 strcat(command, "\"");
9415 len = strlen(command);
9417 value.dsc$a_pointer = command;
9418 value.dsc$w_length = strlen(value.dsc$a_pointer);
9419 _ckvmssts_noperl(lib$set_symbol(&cmd, &value));
9420 retsts = lib$spawn(&cmd, &null, 0, &flags, 0, &pid);
9421 if (retsts == 0x38250) { /* DCL-W-NOTIFY - We must be BATCH, so retry */
9422 _ckvmssts_noperl(lib$spawn(&cmd, &null, 0, &one, 0, &pid));
9425 _ckvmssts_noperl(retsts);
9427 #ifdef ARGPROC_DEBUG
9428 PerlIO_printf(Perl_debug_log, "%s\n", command);
9430 sprintf(pidstring, "%08X", pid);
9431 PerlIO_printf(Perl_debug_log, "%s\n", pidstring);
9432 pidstr.dsc$a_pointer = pidstring;
9433 pidstr.dsc$w_length = strlen(pidstr.dsc$a_pointer);
9434 lib$set_symbol(&pidsymbol, &pidstr);
9438 /***** End of code taken from Mark Pizzolato's argproc.c package *****/
9441 /* OS-specific initialization at image activation (not thread startup) */
9442 /* Older VAXC header files lack these constants */
9443 #ifndef JPI$_RIGHTS_SIZE
9444 # define JPI$_RIGHTS_SIZE 817
9446 #ifndef KGB$M_SUBSYSTEM
9447 # define KGB$M_SUBSYSTEM 0x8
9450 /* Avoid Newx() in vms_image_init as thread context has not been initialized. */
9452 /*{{{void vms_image_init(int *, char ***)*/
9454 vms_image_init(int *argcp, char ***argvp)
9457 char eqv[LNM$C_NAMLENGTH+1] = "";
9458 unsigned int len, tabct = 8, tabidx = 0;
9459 unsigned long int *mask, iosb[2], i, rlst[128], rsz;
9460 unsigned long int iprv[(sizeof(union prvdef) + sizeof(unsigned long int) - 1) / sizeof(unsigned long int)];
9461 unsigned short int dummy, rlen;
9462 struct dsc$descriptor_s **tabvec;
9463 #if defined(PERL_IMPLICIT_CONTEXT)
9466 struct itmlst_3 jpilist[4] = { {sizeof iprv, JPI$_IMAGPRIV, iprv, &dummy},
9467 {sizeof rlst, JPI$_RIGHTSLIST, rlst, &rlen},
9468 { sizeof rsz, JPI$_RIGHTS_SIZE, &rsz, &dummy},
9471 #ifdef KILL_BY_SIGPRC
9472 Perl_csighandler_init();
9475 /* This was moved from the pre-image init handler because on threaded */
9476 /* Perl it was always returning 0 for the default value. */
9477 status = simple_trnlnm("SYS$POSIX_ROOT", eqv, LNM$C_NAMLENGTH);
9480 s = decc$feature_get_index("DECC$DISABLE_POSIX_ROOT");
9483 initial = decc$feature_get_value(s, 4);
9485 /* initial is: 0 if nothing has set the feature */
9486 /* -1 if initialized to default */
9487 /* 1 if set by logical name */
9488 /* 2 if set by decc$feature_set_value */
9489 decc_disable_posix_root = decc$feature_get_value(s, 1);
9491 /* If the value is not valid, force the feature off */
9492 if (decc_disable_posix_root < 0) {
9493 decc$feature_set_value(s, 1, 1);
9494 decc_disable_posix_root = 1;
9498 /* Nothing has asked for it explicitly, so use our own default. */
9499 decc_disable_posix_root = 1;
9500 decc$feature_set_value(s, 1, 1);
9506 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,jpilist,iosb,NULL,NULL));
9507 _ckvmssts_noperl(iosb[0]);
9508 for (i = 0; i < sizeof iprv / sizeof(unsigned long int); i++) {
9509 if (iprv[i]) { /* Running image installed with privs? */
9510 _ckvmssts_noperl(sys$setprv(0,iprv,0,NULL)); /* Turn 'em off. */
9515 /* Rights identifiers might trigger tainting as well. */
9516 if (!will_taint && (rlen || rsz)) {
9517 while (rlen < rsz) {
9518 /* We didn't get all the identifiers on the first pass. Allocate a
9519 * buffer much larger than $GETJPI wants (rsz is size in bytes that
9520 * were needed to hold all identifiers at time of last call; we'll
9521 * allocate that many unsigned long ints), and go back and get 'em.
9522 * If it gave us less than it wanted to despite ample buffer space,
9523 * something's broken. Is your system missing a system identifier?
9525 if (rsz <= jpilist[1].buflen) {
9526 /* Perl_croak accvios when used this early in startup. */
9527 fprintf(stderr, "vms_image_init: $getjpiw refuses to store RIGHTSLIST of %u bytes in buffer of %u bytes.\n%s",
9528 rsz, (unsigned long) jpilist[1].buflen,
9529 "Check your rights database for corruption.\n");
9532 if (jpilist[1].bufadr != rlst) PerlMem_free(jpilist[1].bufadr);
9533 jpilist[1].bufadr = mask = (unsigned long int *) PerlMem_malloc(rsz * sizeof(unsigned long int));
9534 if (mask == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9535 jpilist[1].buflen = rsz * sizeof(unsigned long int);
9536 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,&jpilist[1],iosb,NULL,NULL));
9537 _ckvmssts_noperl(iosb[0]);
9539 mask = jpilist[1].bufadr;
9540 /* Check attribute flags for each identifier (2nd longword); protected
9541 * subsystem identifiers trigger tainting.
9543 for (i = 1; i < (rlen + sizeof(unsigned long int) - 1) / sizeof(unsigned long int); i += 2) {
9544 if (mask[i] & KGB$M_SUBSYSTEM) {
9549 if (mask != rlst) PerlMem_free(mask);
9552 /* When Perl is in decc_filename_unix_report mode and is run from a concealed
9553 * logical, some versions of the CRTL will add a phanthom /000000/
9554 * directory. This needs to be removed.
9556 if (decc_filename_unix_report) {
9559 ulen = strlen(argvp[0][0]);
9561 zeros = strstr(argvp[0][0], "/000000/");
9562 if (zeros != NULL) {
9564 mlen = ulen - (zeros - argvp[0][0]) - 7;
9565 memmove(zeros, &zeros[7], mlen);
9567 argvp[0][0][ulen] = '\0';
9570 /* It also may have a trailing dot that needs to be removed otherwise
9571 * it will be converted to VMS mode incorrectly.
9574 if ((argvp[0][0][ulen] == '.') && (decc_readdir_dropdotnotype))
9575 argvp[0][0][ulen] = '\0';
9578 /* We need to use this hack to tell Perl it should run with tainting,
9579 * since its tainting flag may be part of the PL_curinterp struct, which
9580 * hasn't been allocated when vms_image_init() is called.
9583 char **newargv, **oldargv;
9585 newargv = (char **) PerlMem_malloc(((*argcp)+2) * sizeof(char *));
9586 if (newargv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9587 newargv[0] = oldargv[0];
9588 newargv[1] = PerlMem_malloc(3 * sizeof(char));
9589 if (newargv[1] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9590 strcpy(newargv[1], "-T");
9591 Copy(&oldargv[1],&newargv[2],(*argcp)-1,char **);
9593 newargv[*argcp] = NULL;
9594 /* We orphan the old argv, since we don't know where it's come from,
9595 * so we don't know how to free it.
9599 else { /* Did user explicitly request tainting? */
9601 char *cp, **av = *argvp;
9602 for (i = 1; i < *argcp; i++) {
9603 if (*av[i] != '-') break;
9604 for (cp = av[i]+1; *cp; cp++) {
9605 if (*cp == 'T') { will_taint = 1; break; }
9606 else if ( (*cp == 'd' || *cp == 'V') && *(cp+1) == ':' ||
9607 strchr("DFIiMmx",*cp)) break;
9609 if (will_taint) break;
9614 len = my_trnlnm("PERL_ENV_TABLES",eqv,tabidx);
9617 tabvec = (struct dsc$descriptor_s **)
9618 PerlMem_malloc(tabct * sizeof(struct dsc$descriptor_s *));
9619 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9621 else if (tabidx >= tabct) {
9623 tabvec = (struct dsc$descriptor_s **) PerlMem_realloc(tabvec, tabct * sizeof(struct dsc$descriptor_s *));
9624 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9626 tabvec[tabidx] = (struct dsc$descriptor_s *) PerlMem_malloc(sizeof(struct dsc$descriptor_s));
9627 if (tabvec[tabidx] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9628 tabvec[tabidx]->dsc$w_length = 0;
9629 tabvec[tabidx]->dsc$b_dtype = DSC$K_DTYPE_T;
9630 tabvec[tabidx]->dsc$b_class = DSC$K_CLASS_D;
9631 tabvec[tabidx]->dsc$a_pointer = NULL;
9632 _ckvmssts_noperl(lib$scopy_r_dx(&len,eqv,tabvec[tabidx]));
9634 if (tabidx) { tabvec[tabidx] = NULL; env_tables = tabvec; }
9636 getredirection(argcp,argvp);
9637 #if defined(USE_ITHREADS) && ( defined(__DECC) || defined(__DECCXX) )
9639 # include <reentrancy.h>
9640 decc$set_reentrancy(C$C_MULTITHREAD);
9649 * Trim Unix-style prefix off filespec, so it looks like what a shell
9650 * glob expansion would return (i.e. from specified prefix on, not
9651 * full path). Note that returned filespec is Unix-style, regardless
9652 * of whether input filespec was VMS-style or Unix-style.
9654 * fspec is filespec to be trimmed, and wildspec is wildcard spec used to
9655 * determine prefix (both may be in VMS or Unix syntax). opts is a bit
9656 * vector of options; at present, only bit 0 is used, and if set tells
9657 * trim unixpath to try the current default directory as a prefix when
9658 * presented with a possibly ambiguous ... wildcard.
9660 * Returns !=0 on success, with trimmed filespec replacing contents of
9661 * fspec, and 0 on failure, with contents of fpsec unchanged.
9663 /*{{{int trim_unixpath(char *fspec, char *wildspec, int opts)*/
9665 Perl_trim_unixpath(pTHX_ char *fspec, const char *wildspec, int opts)
9667 char *unixified, *unixwild,
9668 *template, *base, *end, *cp1, *cp2;
9669 register int tmplen, reslen = 0, dirs = 0;
9671 if (!wildspec || !fspec) return 0;
9673 unixwild = PerlMem_malloc(VMS_MAXRSS);
9674 if (unixwild == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9675 template = unixwild;
9676 if (strpbrk(wildspec,"]>:") != NULL) {
9677 if (int_tounixspec(wildspec, unixwild, NULL) == NULL) {
9678 PerlMem_free(unixwild);
9683 strncpy(unixwild, wildspec, VMS_MAXRSS-1);
9684 unixwild[VMS_MAXRSS-1] = 0;
9686 unixified = PerlMem_malloc(VMS_MAXRSS);
9687 if (unixified == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9688 if (strpbrk(fspec,"]>:") != NULL) {
9689 if (int_tounixspec(fspec, unixified, NULL) == NULL) {
9690 PerlMem_free(unixwild);
9691 PerlMem_free(unixified);
9694 else base = unixified;
9695 /* reslen != 0 ==> we had to unixify resultant filespec, so we must
9696 * check to see that final result fits into (isn't longer than) fspec */
9697 reslen = strlen(fspec);
9701 /* No prefix or absolute path on wildcard, so nothing to remove */
9702 if (!*template || *template == '/') {
9703 PerlMem_free(unixwild);
9704 if (base == fspec) {
9705 PerlMem_free(unixified);
9708 tmplen = strlen(unixified);
9709 if (tmplen > reslen) {
9710 PerlMem_free(unixified);
9711 return 0; /* not enough space */
9713 /* Copy unixified resultant, including trailing NUL */
9714 memmove(fspec,unixified,tmplen+1);
9715 PerlMem_free(unixified);
9719 for (end = base; *end; end++) ; /* Find end of resultant filespec */
9720 if ((cp1 = strstr(template,".../")) == NULL) { /* No ...; just count elts */
9721 for (cp1 = template; *cp1; cp1++) if (*cp1 == '/') dirs++;
9722 for (cp1 = end ;cp1 >= base; cp1--)
9723 if ((*cp1 == '/') && !dirs--) /* postdec so we get front of rel path */
9725 if (cp1 != fspec) memmove(fspec,cp1, end - cp1 + 1);
9726 PerlMem_free(unixified);
9727 PerlMem_free(unixwild);
9732 char *front, *nextell, *lcend, *lcfront, *ellipsis = cp1;
9733 int ells = 1, totells, segdirs, match;
9734 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL},
9735 resdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
9737 while ((cp1 = strstr(ellipsis+4,".../")) != NULL) {ellipsis = cp1; ells++;}
9739 for (cp1 = ellipsis+4; *cp1; cp1++) if (*cp1 == '/') dirs++;
9740 tpl = PerlMem_malloc(VMS_MAXRSS);
9741 if (tpl == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9742 if (ellipsis == template && opts & 1) {
9743 /* Template begins with an ellipsis. Since we can't tell how many
9744 * directory names at the front of the resultant to keep for an
9745 * arbitrary starting point, we arbitrarily choose the current
9746 * default directory as a starting point. If it's there as a prefix,
9747 * clip it off. If not, fall through and act as if the leading
9748 * ellipsis weren't there (i.e. return shortest possible path that
9749 * could match template).
9751 if (getcwd(tpl, (VMS_MAXRSS - 1),0) == NULL) {
9753 PerlMem_free(unixified);
9754 PerlMem_free(unixwild);
9757 if (!decc_efs_case_preserve) {
9758 for (cp1 = tpl, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
9759 if (_tolower(*cp1) != _tolower(*cp2)) break;
9761 segdirs = dirs - totells; /* Min # of dirs we must have left */
9762 for (front = cp2+1; *front; front++) if (*front == '/') segdirs--;
9763 if (*cp1 == '\0' && *cp2 == '/' && segdirs < 1) {
9764 memmove(fspec,cp2+1,end - cp2);
9766 PerlMem_free(unixified);
9767 PerlMem_free(unixwild);
9771 /* First off, back up over constant elements at end of path */
9773 for (front = end ; front >= base; front--)
9774 if (*front == '/' && !dirs--) { front++; break; }
9776 lcres = PerlMem_malloc(VMS_MAXRSS);
9777 if (lcres == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9778 for (cp1=template,cp2=lcres; *cp1 && cp2 <= lcres + (VMS_MAXRSS - 1);
9780 if (!decc_efs_case_preserve) {
9781 *cp2 = _tolower(*cp1); /* Make lc copy for match */
9789 PerlMem_free(unixified);
9790 PerlMem_free(unixwild);
9791 PerlMem_free(lcres);
9792 return 0; /* Path too long. */
9795 *cp2 = '\0'; /* Pick up with memcpy later */
9796 lcfront = lcres + (front - base);
9797 /* Now skip over each ellipsis and try to match the path in front of it. */
9799 for (cp1 = ellipsis - 2; cp1 >= template; cp1--)
9800 if (*(cp1) == '.' && *(cp1+1) == '.' &&
9801 *(cp1+2) == '.' && *(cp1+3) == '/' ) break;
9802 if (cp1 < template) break; /* template started with an ellipsis */
9803 if (cp1 + 4 == ellipsis) { /* Consecutive ellipses */
9804 ellipsis = cp1; continue;
9806 wilddsc.dsc$a_pointer = tpl;
9807 wilddsc.dsc$w_length = resdsc.dsc$w_length = ellipsis - 1 - cp1;
9809 for (segdirs = 0, cp2 = tpl;
9810 cp1 <= ellipsis - 1 && cp2 <= tpl + (VMS_MAXRSS-1);
9812 if (*cp1 == '?') *cp2 = '%'; /* Substitute VMS' wildcard for Unix' */
9814 if (!decc_efs_case_preserve) {
9815 *cp2 = _tolower(*cp1); /* else lowercase for match */
9818 *cp2 = *cp1; /* else preserve case for match */
9821 if (*cp2 == '/') segdirs++;
9823 if (cp1 != ellipsis - 1) {
9825 PerlMem_free(unixified);
9826 PerlMem_free(unixwild);
9827 PerlMem_free(lcres);
9828 return 0; /* Path too long */
9830 /* Back up at least as many dirs as in template before matching */
9831 for (cp1 = lcfront - 1; segdirs && cp1 >= lcres; cp1--)
9832 if (*cp1 == '/' && !segdirs--) { cp1++; break; }
9833 for (match = 0; cp1 > lcres;) {
9834 resdsc.dsc$a_pointer = cp1;
9835 if (str$match_wild(&wilddsc,&resdsc) == STR$_MATCH) {
9837 if (match == 1) lcfront = cp1;
9839 for ( ; cp1 >= lcres; cp1--) if (*cp1 == '/') { cp1++; break; }
9843 PerlMem_free(unixified);
9844 PerlMem_free(unixwild);
9845 PerlMem_free(lcres);
9846 return 0; /* Can't find prefix ??? */
9848 if (match > 1 && opts & 1) {
9849 /* This ... wildcard could cover more than one set of dirs (i.e.
9850 * a set of similar dir names is repeated). If the template
9851 * contains more than 1 ..., upstream elements could resolve the
9852 * ambiguity, but it's not worth a full backtracking setup here.
9853 * As a quick heuristic, clip off the current default directory
9854 * if it's present to find the trimmed spec, else use the
9855 * shortest string that this ... could cover.
9857 char def[NAM$C_MAXRSS+1], *st;
9859 if (getcwd(def, sizeof def,0) == NULL) {
9860 PerlMem_free(unixified);
9861 PerlMem_free(unixwild);
9862 PerlMem_free(lcres);
9866 if (!decc_efs_case_preserve) {
9867 for (cp1 = def, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
9868 if (_tolower(*cp1) != _tolower(*cp2)) break;
9870 segdirs = dirs - totells; /* Min # of dirs we must have left */
9871 for (st = cp2+1; *st; st++) if (*st == '/') segdirs--;
9872 if (*cp1 == '\0' && *cp2 == '/') {
9873 memmove(fspec,cp2+1,end - cp2);
9875 PerlMem_free(unixified);
9876 PerlMem_free(unixwild);
9877 PerlMem_free(lcres);
9880 /* Nope -- stick with lcfront from above and keep going. */
9883 memmove(fspec,base + (lcfront - lcres), lcend - lcfront + 1);
9885 PerlMem_free(unixified);
9886 PerlMem_free(unixwild);
9887 PerlMem_free(lcres);
9892 } /* end of trim_unixpath() */
9897 * VMS readdir() routines.
9898 * Written by Rich $alz, <rsalz@bbn.com> in August, 1990.
9900 * 21-Jul-1994 Charles Bailey bailey@newman.upenn.edu
9901 * Minor modifications to original routines.
9904 /* readdir may have been redefined by reentr.h, so make sure we get
9905 * the local version for what we do here.
9910 #if !defined(PERL_IMPLICIT_CONTEXT)
9911 # define readdir Perl_readdir
9913 # define readdir(a) Perl_readdir(aTHX_ a)
9916 /* Number of elements in vms_versions array */
9917 #define VERSIZE(e) (sizeof e->vms_versions / sizeof e->vms_versions[0])
9920 * Open a directory, return a handle for later use.
9922 /*{{{ DIR *opendir(char*name) */
9924 Perl_opendir(pTHX_ const char *name)
9930 Newx(dir, VMS_MAXRSS, char);
9931 if (do_tovmspath(name,dir,0,NULL) == NULL) {
9935 /* Check access before stat; otherwise stat does not
9936 * accurately report whether it's a directory.
9938 if (!cando_by_name_int(S_IRUSR,0,dir,PERL_RMSEXPAND_M_VMS_IN)) {
9939 /* cando_by_name has already set errno */
9943 if (flex_stat(dir,&sb) == -1) return NULL;
9944 if (!S_ISDIR(sb.st_mode)) {
9946 set_errno(ENOTDIR); set_vaxc_errno(RMS$_DIR);
9949 /* Get memory for the handle, and the pattern. */
9951 Newx(dd->pattern,strlen(dir)+sizeof "*.*" + 1,char);
9953 /* Fill in the fields; mainly playing with the descriptor. */
9954 sprintf(dd->pattern, "%s*.*",dir);
9959 /* By saying we always want the result of readdir() in unix format, we
9960 * are really saying we want all the escapes removed. Otherwise the caller,
9961 * having no way to know whether it's already in VMS format, might send it
9962 * through tovmsspec again, thus double escaping.
9964 dd->flags = PERL_VMSDIR_M_UNIXSPECS;
9965 dd->pat.dsc$a_pointer = dd->pattern;
9966 dd->pat.dsc$w_length = strlen(dd->pattern);
9967 dd->pat.dsc$b_dtype = DSC$K_DTYPE_T;
9968 dd->pat.dsc$b_class = DSC$K_CLASS_S;
9969 #if defined(USE_ITHREADS)
9970 Newx(dd->mutex,1,perl_mutex);
9971 MUTEX_INIT( (perl_mutex *) dd->mutex );
9977 } /* end of opendir() */
9981 * Set the flag to indicate we want versions or not.
9983 /*{{{ void vmsreaddirversions(DIR *dd, int flag)*/
9985 vmsreaddirversions(DIR *dd, int flag)
9988 dd->flags |= PERL_VMSDIR_M_VERSIONS;
9990 dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
9995 * Free up an opened directory.
9997 /*{{{ void closedir(DIR *dd)*/
9999 Perl_closedir(DIR *dd)
10003 sts = lib$find_file_end(&dd->context);
10004 Safefree(dd->pattern);
10005 #if defined(USE_ITHREADS)
10006 MUTEX_DESTROY( (perl_mutex *) dd->mutex );
10007 Safefree(dd->mutex);
10014 * Collect all the version numbers for the current file.
10017 collectversions(pTHX_ DIR *dd)
10019 struct dsc$descriptor_s pat;
10020 struct dsc$descriptor_s res;
10022 char *p, *text, *buff;
10024 unsigned long context, tmpsts;
10026 /* Convenient shorthand. */
10029 /* Add the version wildcard, ignoring the "*.*" put on before */
10030 i = strlen(dd->pattern);
10031 Newx(text,i + e->d_namlen + 3,char);
10032 strcpy(text, dd->pattern);
10033 sprintf(&text[i - 3], "%s;*", e->d_name);
10035 /* Set up the pattern descriptor. */
10036 pat.dsc$a_pointer = text;
10037 pat.dsc$w_length = i + e->d_namlen - 1;
10038 pat.dsc$b_dtype = DSC$K_DTYPE_T;
10039 pat.dsc$b_class = DSC$K_CLASS_S;
10041 /* Set up result descriptor. */
10042 Newx(buff, VMS_MAXRSS, char);
10043 res.dsc$a_pointer = buff;
10044 res.dsc$w_length = VMS_MAXRSS - 1;
10045 res.dsc$b_dtype = DSC$K_DTYPE_T;
10046 res.dsc$b_class = DSC$K_CLASS_S;
10048 /* Read files, collecting versions. */
10049 for (context = 0, e->vms_verscount = 0;
10050 e->vms_verscount < VERSIZE(e);
10051 e->vms_verscount++) {
10052 unsigned long rsts;
10053 unsigned long flags = 0;
10055 #ifdef VMS_LONGNAME_SUPPORT
10056 flags = LIB$M_FIL_LONG_NAMES;
10058 tmpsts = lib$find_file(&pat, &res, &context, NULL, NULL, &rsts, &flags);
10059 if (tmpsts == RMS$_NMF || context == 0) break;
10061 buff[VMS_MAXRSS - 1] = '\0';
10062 if ((p = strchr(buff, ';')))
10063 e->vms_versions[e->vms_verscount] = atoi(p + 1);
10065 e->vms_versions[e->vms_verscount] = -1;
10068 _ckvmssts(lib$find_file_end(&context));
10072 } /* end of collectversions() */
10075 * Read the next entry from the directory.
10077 /*{{{ struct dirent *readdir(DIR *dd)*/
10079 Perl_readdir(pTHX_ DIR *dd)
10081 struct dsc$descriptor_s res;
10083 unsigned long int tmpsts;
10084 unsigned long rsts;
10085 unsigned long flags = 0;
10086 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
10087 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
10089 /* Set up result descriptor, and get next file. */
10090 Newx(buff, VMS_MAXRSS, char);
10091 res.dsc$a_pointer = buff;
10092 res.dsc$w_length = VMS_MAXRSS - 1;
10093 res.dsc$b_dtype = DSC$K_DTYPE_T;
10094 res.dsc$b_class = DSC$K_CLASS_S;
10096 #ifdef VMS_LONGNAME_SUPPORT
10097 flags = LIB$M_FIL_LONG_NAMES;
10100 tmpsts = lib$find_file
10101 (&dd->pat, &res, &dd->context, NULL, NULL, &rsts, &flags);
10102 if ( tmpsts == RMS$_NMF || dd->context == 0) return NULL; /* None left. */
10103 if (!(tmpsts & 1)) {
10104 set_vaxc_errno(tmpsts);
10107 set_errno(EACCES); break;
10109 set_errno(ENODEV); break;
10111 set_errno(ENOTDIR); break;
10112 case RMS$_FNF: case RMS$_DNF:
10113 set_errno(ENOENT); break;
10115 set_errno(EVMSERR);
10121 /* Force the buffer to end with a NUL, and downcase name to match C convention. */
10122 buff[res.dsc$w_length] = '\0';
10123 p = buff + res.dsc$w_length;
10124 while (--p >= buff) if (!isspace(*p)) break;
10126 if (!decc_efs_case_preserve) {
10127 for (p = buff; *p; p++) *p = _tolower(*p);
10130 /* Skip any directory component and just copy the name. */
10131 sts = vms_split_path
10146 if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
10148 /* In Unix report mode, remove the ".dir;1" from the name */
10149 /* if it is a real directory. */
10150 if (decc_filename_unix_report || decc_efs_charset) {
10151 if ((e_len == 4) && (vs_len == 2) && (vs_spec[1] == '1')) {
10152 if ((toupper(e_spec[1]) == 'D') &&
10153 (toupper(e_spec[2]) == 'I') &&
10154 (toupper(e_spec[3]) == 'R')) {
10158 ret_sts = stat(buff, (stat_t *)&statbuf);
10159 if ((ret_sts == 0) && S_ISDIR(statbuf.st_mode)) {
10167 /* Drop NULL extensions on UNIX file specification */
10168 if ((e_len == 1) && decc_readdir_dropdotnotype) {
10174 strncpy(dd->entry.d_name, n_spec, n_len + e_len);
10175 dd->entry.d_name[n_len + e_len] = '\0';
10176 dd->entry.d_namlen = strlen(dd->entry.d_name);
10178 /* Convert the filename to UNIX format if needed */
10179 if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
10181 /* Translate the encoded characters. */
10182 /* Fixme: Unicode handling could result in embedded 0 characters */
10183 if (strchr(dd->entry.d_name, '^') != NULL) {
10184 char new_name[256];
10186 p = dd->entry.d_name;
10189 int inchars_read, outchars_added;
10190 inchars_read = copy_expand_vms_filename_escape(q, p, &outchars_added);
10192 q += outchars_added;
10194 /* if outchars_added > 1, then this is a wide file specification */
10195 /* Wide file specifications need to be passed in Perl */
10196 /* counted strings apparently with a Unicode flag */
10199 strcpy(dd->entry.d_name, new_name);
10200 dd->entry.d_namlen = strlen(dd->entry.d_name);
10204 dd->entry.vms_verscount = 0;
10205 if (dd->flags & PERL_VMSDIR_M_VERSIONS) collectversions(aTHX_ dd);
10209 } /* end of readdir() */
10213 * Read the next entry from the directory -- thread-safe version.
10215 /*{{{ int readdir_r(DIR *dd, struct dirent *entry, struct dirent **result)*/
10217 Perl_readdir_r(pTHX_ DIR *dd, struct dirent *entry, struct dirent **result)
10221 MUTEX_LOCK( (perl_mutex *) dd->mutex );
10223 entry = readdir(dd);
10225 retval = ( *result == NULL ? errno : 0 );
10227 MUTEX_UNLOCK( (perl_mutex *) dd->mutex );
10231 } /* end of readdir_r() */
10235 * Return something that can be used in a seekdir later.
10237 /*{{{ long telldir(DIR *dd)*/
10239 Perl_telldir(DIR *dd)
10246 * Return to a spot where we used to be. Brute force.
10248 /*{{{ void seekdir(DIR *dd,long count)*/
10250 Perl_seekdir(pTHX_ DIR *dd, long count)
10254 /* If we haven't done anything yet... */
10255 if (dd->count == 0)
10258 /* Remember some state, and clear it. */
10259 old_flags = dd->flags;
10260 dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
10261 _ckvmssts(lib$find_file_end(&dd->context));
10264 /* The increment is in readdir(). */
10265 for (dd->count = 0; dd->count < count; )
10268 dd->flags = old_flags;
10270 } /* end of seekdir() */
10273 /* VMS subprocess management
10275 * my_vfork() - just a vfork(), after setting a flag to record that
10276 * the current script is trying a Unix-style fork/exec.
10278 * vms_do_aexec() and vms_do_exec() are called in response to the
10279 * perl 'exec' function. If this follows a vfork call, then they
10280 * call out the regular perl routines in doio.c which do an
10281 * execvp (for those who really want to try this under VMS).
10282 * Otherwise, they do exactly what the perl docs say exec should
10283 * do - terminate the current script and invoke a new command
10284 * (See below for notes on command syntax.)
10286 * do_aspawn() and do_spawn() implement the VMS side of the perl
10287 * 'system' function.
10289 * Note on command arguments to perl 'exec' and 'system': When handled
10290 * in 'VMSish fashion' (i.e. not after a call to vfork) The args
10291 * are concatenated to form a DCL command string. If the first non-numeric
10292 * arg begins with '$' (i.e. the perl script had "\$ Type" or some such),
10293 * the command string is handed off to DCL directly. Otherwise,
10294 * the first token of the command is taken as the filespec of an image
10295 * to run. The filespec is expanded using a default type of '.EXE' and
10296 * the process defaults for device, directory, etc., and if found, the resultant
10297 * filespec is invoked using the DCL verb 'MCR', and passed the rest of
10298 * the command string as parameters. This is perhaps a bit complicated,
10299 * but I hope it will form a happy medium between what VMS folks expect
10300 * from lib$spawn and what Unix folks expect from exec.
10303 static int vfork_called;
10305 /*{{{int my_vfork()*/
10316 vms_execfree(struct dsc$descriptor_s *vmscmd)
10319 if (vmscmd->dsc$a_pointer) {
10320 PerlMem_free(vmscmd->dsc$a_pointer);
10322 PerlMem_free(vmscmd);
10327 setup_argstr(pTHX_ SV *really, SV **mark, SV **sp)
10329 char *junk, *tmps = NULL;
10330 register size_t cmdlen = 0;
10337 tmps = SvPV(really,rlen);
10339 cmdlen += rlen + 1;
10344 for (idx++; idx <= sp; idx++) {
10346 junk = SvPVx(*idx,rlen);
10347 cmdlen += rlen ? rlen + 1 : 0;
10350 Newx(PL_Cmd, cmdlen+1, char);
10352 if (tmps && *tmps) {
10353 strcpy(PL_Cmd,tmps);
10356 else *PL_Cmd = '\0';
10357 while (++mark <= sp) {
10359 char *s = SvPVx(*mark,n_a);
10361 if (*PL_Cmd) strcat(PL_Cmd," ");
10367 } /* end of setup_argstr() */
10370 static unsigned long int
10371 setup_cmddsc(pTHX_ const char *incmd, int check_img, int *suggest_quote,
10372 struct dsc$descriptor_s **pvmscmd)
10376 char image_name[NAM$C_MAXRSS+1];
10377 char image_argv[NAM$C_MAXRSS+1];
10378 $DESCRIPTOR(defdsc,".EXE");
10379 $DESCRIPTOR(defdsc2,".");
10380 struct dsc$descriptor_s resdsc;
10381 struct dsc$descriptor_s *vmscmd;
10382 struct dsc$descriptor_s imgdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10383 unsigned long int cxt = 0, flags = 1, retsts = SS$_NORMAL;
10384 register char *s, *rest, *cp, *wordbreak;
10387 register int isdcl;
10389 vmscmd = PerlMem_malloc(sizeof(struct dsc$descriptor_s));
10390 if (vmscmd == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10392 /* vmsspec is a DCL command buffer, not just a filename */
10393 vmsspec = PerlMem_malloc(MAX_DCL_LINE_LENGTH + 1);
10394 if (vmsspec == NULL)
10395 _ckvmssts_noperl(SS$_INSFMEM);
10397 resspec = PerlMem_malloc(VMS_MAXRSS);
10398 if (resspec == NULL)
10399 _ckvmssts_noperl(SS$_INSFMEM);
10401 /* Make a copy for modification */
10402 cmdlen = strlen(incmd);
10403 cmd = PerlMem_malloc(cmdlen+1);
10404 if (cmd == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10405 strncpy(cmd, incmd, cmdlen);
10410 resdsc.dsc$a_pointer = resspec;
10411 resdsc.dsc$b_dtype = DSC$K_DTYPE_T;
10412 resdsc.dsc$b_class = DSC$K_CLASS_S;
10413 resdsc.dsc$w_length = VMS_MAXRSS - 1;
10415 vmscmd->dsc$a_pointer = NULL;
10416 vmscmd->dsc$b_dtype = DSC$K_DTYPE_T;
10417 vmscmd->dsc$b_class = DSC$K_CLASS_S;
10418 vmscmd->dsc$w_length = 0;
10419 if (pvmscmd) *pvmscmd = vmscmd;
10421 if (suggest_quote) *suggest_quote = 0;
10423 if (strlen(cmd) > MAX_DCL_LINE_LENGTH) {
10425 PerlMem_free(vmsspec);
10426 PerlMem_free(resspec);
10427 return CLI$_BUFOVF; /* continuation lines currently unsupported */
10432 while (*s && isspace(*s)) s++;
10434 if (*s == '@' || *s == '$') {
10435 vmsspec[0] = *s; rest = s + 1;
10436 for (cp = &vmsspec[1]; *rest && isspace(*rest); rest++,cp++) *cp = *rest;
10438 else { cp = vmsspec; rest = s; }
10439 if (*rest == '.' || *rest == '/') {
10441 for (cp2 = resspec;
10442 *rest && !isspace(*rest) && cp2 - resspec < (VMS_MAXRSS - 1);
10443 rest++, cp2++) *cp2 = *rest;
10445 if (int_tovmsspec(resspec, cp, 0, NULL)) {
10448 /* When a UNIX spec with no file type is translated to VMS, */
10449 /* A trailing '.' is appended under ODS-5 rules. */
10450 /* Here we do not want that trailing "." as it prevents */
10451 /* Looking for a implied ".exe" type. */
10452 if (decc_efs_charset) {
10454 i = strlen(vmsspec);
10455 if (vmsspec[i-1] == '.') {
10456 vmsspec[i-1] = '\0';
10461 for (cp2 = vmsspec + strlen(vmsspec);
10462 *rest && cp2 - vmsspec < MAX_DCL_LINE_LENGTH;
10463 rest++, cp2++) *cp2 = *rest;
10468 /* Intuit whether verb (first word of cmd) is a DCL command:
10469 * - if first nonspace char is '@', it's a DCL indirection
10471 * - if verb contains a filespec separator, it's not a DCL command
10472 * - if it doesn't, caller tells us whether to default to a DCL
10473 * command, or to a local image unless told it's DCL (by leading '$')
10477 if (suggest_quote) *suggest_quote = 1;
10479 register char *filespec = strpbrk(s,":<[.;");
10480 rest = wordbreak = strpbrk(s," \"\t/");
10481 if (!wordbreak) wordbreak = s + strlen(s);
10482 if (*s == '$') check_img = 0;
10483 if (filespec && (filespec < wordbreak)) isdcl = 0;
10484 else isdcl = !check_img;
10489 imgdsc.dsc$a_pointer = s;
10490 imgdsc.dsc$w_length = wordbreak - s;
10491 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
10493 _ckvmssts_noperl(lib$find_file_end(&cxt));
10494 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
10495 if (!(retsts & 1) && *s == '$') {
10496 _ckvmssts_noperl(lib$find_file_end(&cxt));
10497 imgdsc.dsc$a_pointer++; imgdsc.dsc$w_length--;
10498 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
10500 _ckvmssts_noperl(lib$find_file_end(&cxt));
10501 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
10505 _ckvmssts_noperl(lib$find_file_end(&cxt));
10510 while (*s && !isspace(*s)) s++;
10513 /* check that it's really not DCL with no file extension */
10514 fp = fopen(resspec,"r","ctx=bin","ctx=rec","shr=get");
10516 char b[256] = {0,0,0,0};
10517 read(fileno(fp), b, 256);
10518 isdcl = isprint(b[0]) && isprint(b[1]) && isprint(b[2]) && isprint(b[3]);
10522 /* Check for script */
10524 if ((b[0] == '#') && (b[1] == '!'))
10526 #ifdef ALTERNATE_SHEBANG
10528 shebang_len = strlen(ALTERNATE_SHEBANG);
10529 if (strncmp(b, ALTERNATE_SHEBANG, shebang_len) == 0) {
10531 perlstr = strstr("perl",b);
10532 if (perlstr == NULL)
10540 if (shebang_len > 0) {
10543 char tmpspec[NAM$C_MAXRSS + 1];
10546 /* Image is following after white space */
10547 /*--------------------------------------*/
10548 while (isprint(b[i]) && isspace(b[i]))
10552 while (isprint(b[i]) && !isspace(b[i])) {
10553 tmpspec[j++] = b[i++];
10554 if (j >= NAM$C_MAXRSS)
10559 /* There may be some default parameters to the image */
10560 /*---------------------------------------------------*/
10562 while (isprint(b[i])) {
10563 image_argv[j++] = b[i++];
10564 if (j >= NAM$C_MAXRSS)
10567 while ((j > 0) && !isprint(image_argv[j-1]))
10571 /* It will need to be converted to VMS format and validated */
10572 if (tmpspec[0] != '\0') {
10575 /* Try to find the exact program requested to be run */
10576 /*---------------------------------------------------*/
10577 iname = int_rmsexpand
10578 (tmpspec, image_name, ".exe",
10579 PERL_RMSEXPAND_M_VMS, NULL, NULL);
10580 if (iname != NULL) {
10581 if (cando_by_name_int
10582 (S_IXUSR,0,image_name,PERL_RMSEXPAND_M_VMS_IN)) {
10583 /* MCR prefix needed */
10587 /* Try again with a null type */
10588 /*----------------------------*/
10589 iname = int_rmsexpand
10590 (tmpspec, image_name, ".",
10591 PERL_RMSEXPAND_M_VMS, NULL, NULL);
10592 if (iname != NULL) {
10593 if (cando_by_name_int
10594 (S_IXUSR,0,image_name, PERL_RMSEXPAND_M_VMS_IN)) {
10595 /* MCR prefix needed */
10601 /* Did we find the image to run the script? */
10602 /*------------------------------------------*/
10606 /* Assume DCL or foreign command exists */
10607 /*--------------------------------------*/
10608 tchr = strrchr(tmpspec, '/');
10609 if (tchr != NULL) {
10615 strcpy(image_name, tchr);
10623 if (check_img && isdcl) {
10625 PerlMem_free(resspec);
10626 PerlMem_free(vmsspec);
10630 if (cando_by_name(S_IXUSR,0,resspec)) {
10631 vmscmd->dsc$a_pointer = PerlMem_malloc(MAX_DCL_LINE_LENGTH);
10632 if (vmscmd->dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10634 strcpy(vmscmd->dsc$a_pointer,"$ MCR ");
10635 if (image_name[0] != 0) {
10636 strcat(vmscmd->dsc$a_pointer, image_name);
10637 strcat(vmscmd->dsc$a_pointer, " ");
10639 } else if (image_name[0] != 0) {
10640 strcpy(vmscmd->dsc$a_pointer, image_name);
10641 strcat(vmscmd->dsc$a_pointer, " ");
10643 strcpy(vmscmd->dsc$a_pointer,"@");
10645 if (suggest_quote) *suggest_quote = 1;
10647 /* If there is an image name, use original command */
10648 if (image_name[0] == 0)
10649 strcat(vmscmd->dsc$a_pointer,resspec);
10652 while (*rest && isspace(*rest)) rest++;
10655 if (image_argv[0] != 0) {
10656 strcat(vmscmd->dsc$a_pointer,image_argv);
10657 strcat(vmscmd->dsc$a_pointer, " ");
10663 rest_len = strlen(rest);
10664 vmscmd_len = strlen(vmscmd->dsc$a_pointer);
10665 if ((rest_len + vmscmd_len) < MAX_DCL_LINE_LENGTH)
10666 strcat(vmscmd->dsc$a_pointer,rest);
10668 retsts = CLI$_BUFOVF;
10670 vmscmd->dsc$w_length = strlen(vmscmd->dsc$a_pointer);
10672 PerlMem_free(vmsspec);
10673 PerlMem_free(resspec);
10674 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
10680 /* It's either a DCL command or we couldn't find a suitable image */
10681 vmscmd->dsc$w_length = strlen(cmd);
10683 vmscmd->dsc$a_pointer = PerlMem_malloc(vmscmd->dsc$w_length + 1);
10684 strncpy(vmscmd->dsc$a_pointer,cmd,vmscmd->dsc$w_length);
10685 vmscmd->dsc$a_pointer[vmscmd->dsc$w_length] = 0;
10688 PerlMem_free(resspec);
10689 PerlMem_free(vmsspec);
10691 /* check if it's a symbol (for quoting purposes) */
10692 if (suggest_quote && !*suggest_quote) {
10694 char equiv[LNM$C_NAMLENGTH];
10695 struct dsc$descriptor_s eqvdsc = {sizeof(equiv), DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10696 eqvdsc.dsc$a_pointer = equiv;
10698 iss = lib$get_symbol(vmscmd,&eqvdsc);
10699 if (iss&1 && (*equiv == '$' || *equiv == '@')) *suggest_quote = 1;
10701 if (!(retsts & 1)) {
10702 /* just hand off status values likely to be due to user error */
10703 if (retsts == RMS$_FNF || retsts == RMS$_DNF || retsts == RMS$_PRV ||
10704 retsts == RMS$_DEV || retsts == RMS$_DIR || retsts == RMS$_SYN ||
10705 (retsts & STS$M_CODE) == (SHR$_NOWILD & STS$M_CODE)) return retsts;
10706 else { _ckvmssts_noperl(retsts); }
10709 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
10711 } /* end of setup_cmddsc() */
10714 /* {{{ bool vms_do_aexec(SV *really,SV **mark,SV **sp) */
10716 Perl_vms_do_aexec(pTHX_ SV *really,SV **mark,SV **sp)
10722 if (vfork_called) { /* this follows a vfork - act Unixish */
10724 if (vfork_called < 0) {
10725 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
10728 else return do_aexec(really,mark,sp);
10730 /* no vfork - act VMSish */
10731 cmd = setup_argstr(aTHX_ really,mark,sp);
10732 exec_sts = vms_do_exec(cmd);
10733 Safefree(cmd); /* Clean up from setup_argstr() */
10738 } /* end of vms_do_aexec() */
10741 /* {{{bool vms_do_exec(char *cmd) */
10743 Perl_vms_do_exec(pTHX_ const char *cmd)
10745 struct dsc$descriptor_s *vmscmd;
10747 if (vfork_called) { /* this follows a vfork - act Unixish */
10749 if (vfork_called < 0) {
10750 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
10753 else return do_exec(cmd);
10756 { /* no vfork - act VMSish */
10757 unsigned long int retsts;
10760 TAINT_PROPER("exec");
10761 if ((retsts = setup_cmddsc(aTHX_ cmd,1,0,&vmscmd)) & 1)
10762 retsts = lib$do_command(vmscmd);
10765 case RMS$_FNF: case RMS$_DNF:
10766 set_errno(ENOENT); break;
10768 set_errno(ENOTDIR); break;
10770 set_errno(ENODEV); break;
10772 set_errno(EACCES); break;
10774 set_errno(EINVAL); break;
10775 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
10776 set_errno(E2BIG); break;
10777 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
10778 _ckvmssts_noperl(retsts); /* fall through */
10779 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
10780 set_errno(EVMSERR);
10782 set_vaxc_errno(retsts);
10783 if (ckWARN(WARN_EXEC)) {
10784 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't exec \"%*s\": %s",
10785 vmscmd->dsc$w_length, vmscmd->dsc$a_pointer, Strerror(errno));
10787 vms_execfree(vmscmd);
10792 } /* end of vms_do_exec() */
10795 int do_spawn2(pTHX_ const char *, int);
10798 Perl_do_aspawn(pTHX_ SV* really, SV** mark, SV** sp)
10800 unsigned long int sts;
10806 /* We'll copy the (undocumented?) Win32 behavior and allow a
10807 * numeric first argument. But the only value we'll support
10808 * through do_aspawn is a value of 1, which means spawn without
10809 * waiting for completion -- other values are ignored.
10811 if (SvNIOKp(*(mark+1)) && !SvPOKp(*(mark+1))) {
10813 flags = SvIVx(*mark);
10816 if (flags && flags == 1) /* the Win32 P_NOWAIT value */
10817 flags = CLI$M_NOWAIT;
10821 cmd = setup_argstr(aTHX_ really, mark, sp);
10822 sts = do_spawn2(aTHX_ cmd, flags);
10823 /* pp_sys will clean up cmd */
10827 } /* end of do_aspawn() */
10831 /* {{{int do_spawn(char* cmd) */
10833 Perl_do_spawn(pTHX_ char* cmd)
10835 PERL_ARGS_ASSERT_DO_SPAWN;
10837 return do_spawn2(aTHX_ cmd, 0);
10841 /* {{{int do_spawn_nowait(char* cmd) */
10843 Perl_do_spawn_nowait(pTHX_ char* cmd)
10845 PERL_ARGS_ASSERT_DO_SPAWN_NOWAIT;
10847 return do_spawn2(aTHX_ cmd, CLI$M_NOWAIT);
10851 /* {{{int do_spawn2(char *cmd) */
10853 do_spawn2(pTHX_ const char *cmd, int flags)
10855 unsigned long int sts, substs;
10857 /* The caller of this routine expects to Safefree(PL_Cmd) */
10858 Newx(PL_Cmd,10,char);
10861 TAINT_PROPER("spawn");
10862 if (!cmd || !*cmd) {
10863 sts = lib$spawn(0,0,0,&flags,0,0,&substs,0,0,0,0,0,0);
10866 case RMS$_FNF: case RMS$_DNF:
10867 set_errno(ENOENT); break;
10869 set_errno(ENOTDIR); break;
10871 set_errno(ENODEV); break;
10873 set_errno(EACCES); break;
10875 set_errno(EINVAL); break;
10876 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
10877 set_errno(E2BIG); break;
10878 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
10879 _ckvmssts_noperl(sts); /* fall through */
10880 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
10881 set_errno(EVMSERR);
10883 set_vaxc_errno(sts);
10884 if (ckWARN(WARN_EXEC)) {
10885 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't spawn: %s",
10894 if (flags & CLI$M_NOWAIT)
10897 strcpy(mode, "nW");
10899 fp = safe_popen(aTHX_ cmd, mode, (int *)&sts);
10902 /* sts will be the pid in the nowait case */
10905 } /* end of do_spawn2() */
10909 static unsigned int *sockflags, sockflagsize;
10912 * Shim fdopen to identify sockets for my_fwrite later, since the stdio
10913 * routines found in some versions of the CRTL can't deal with sockets.
10914 * We don't shim the other file open routines since a socket isn't
10915 * likely to be opened by a name.
10917 /*{{{ FILE *my_fdopen(int fd, const char *mode)*/
10918 FILE *my_fdopen(int fd, const char *mode)
10920 FILE *fp = fdopen(fd, mode);
10923 unsigned int fdoff = fd / sizeof(unsigned int);
10924 Stat_t sbuf; /* native stat; we don't need flex_stat */
10925 if (!sockflagsize || fdoff > sockflagsize) {
10926 if (sockflags) Renew( sockflags,fdoff+2,unsigned int);
10927 else Newx (sockflags,fdoff+2,unsigned int);
10928 memset(sockflags+sockflagsize,0,fdoff + 2 - sockflagsize);
10929 sockflagsize = fdoff + 2;
10931 if (fstat(fd, (struct stat *)&sbuf) == 0 && S_ISSOCK(sbuf.st_mode))
10932 sockflags[fdoff] |= 1 << (fd % sizeof(unsigned int));
10941 * Clear the corresponding bit when the (possibly) socket stream is closed.
10942 * There still a small hole: we miss an implicit close which might occur
10943 * via freopen(). >> Todo
10945 /*{{{ int my_fclose(FILE *fp)*/
10946 int my_fclose(FILE *fp) {
10948 unsigned int fd = fileno(fp);
10949 unsigned int fdoff = fd / sizeof(unsigned int);
10951 if (sockflagsize && fdoff < sockflagsize)
10952 sockflags[fdoff] &= ~(1 << fd % sizeof(unsigned int));
10960 * A simple fwrite replacement which outputs itmsz*nitm chars without
10961 * introducing record boundaries every itmsz chars.
10962 * We are using fputs, which depends on a terminating null. We may
10963 * well be writing binary data, so we need to accommodate not only
10964 * data with nulls sprinkled in the middle but also data with no null
10967 /*{{{ int my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)*/
10969 my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)
10971 register char *cp, *end, *cpd, *data;
10972 register unsigned int fd = fileno(dest);
10973 register unsigned int fdoff = fd / sizeof(unsigned int);
10975 int bufsize = itmsz * nitm + 1;
10977 if (fdoff < sockflagsize &&
10978 (sockflags[fdoff] | 1 << (fd % sizeof(unsigned int)))) {
10979 if (write(fd, src, itmsz * nitm) == EOF) return EOF;
10983 _ckvmssts_noperl(lib$get_vm(&bufsize, &data));
10984 memcpy( data, src, itmsz*nitm );
10985 data[itmsz*nitm] = '\0';
10987 end = data + itmsz * nitm;
10988 retval = (int) nitm; /* on success return # items written */
10991 while (cpd <= end) {
10992 for (cp = cpd; cp <= end; cp++) if (!*cp) break;
10993 if (fputs(cpd,dest) == EOF) { retval = EOF; break; }
10995 if (fputc('\0',dest) == EOF) { retval = EOF; break; }
10999 if (data) _ckvmssts_noperl(lib$free_vm(&bufsize, &data));
11002 } /* end of my_fwrite() */
11005 /*{{{ int my_flush(FILE *fp)*/
11007 Perl_my_flush(pTHX_ FILE *fp)
11010 if ((res = fflush(fp)) == 0 && fp) {
11011 #ifdef VMS_DO_SOCKETS
11013 if (fstat(fileno(fp), &s.crtl_stat) == 0 && !S_ISSOCK(s.st_mode))
11015 res = fsync(fileno(fp));
11018 * If the flush succeeded but set end-of-file, we need to clear
11019 * the error because our caller may check ferror(). BTW, this
11020 * probably means we just flushed an empty file.
11022 if (res == 0 && vaxc$errno == RMS$_EOF) clearerr(fp);
11029 * Here are replacements for the following Unix routines in the VMS environment:
11030 * getpwuid Get information for a particular UIC or UID
11031 * getpwnam Get information for a named user
11032 * getpwent Get information for each user in the rights database
11033 * setpwent Reset search to the start of the rights database
11034 * endpwent Finish searching for users in the rights database
11036 * getpwuid, getpwnam, and getpwent return a pointer to the passwd structure
11037 * (defined in pwd.h), which contains the following fields:-
11039 * char *pw_name; Username (in lower case)
11040 * char *pw_passwd; Hashed password
11041 * unsigned int pw_uid; UIC
11042 * unsigned int pw_gid; UIC group number
11043 * char *pw_unixdir; Default device/directory (VMS-style)
11044 * char *pw_gecos; Owner name
11045 * char *pw_dir; Default device/directory (Unix-style)
11046 * char *pw_shell; Default CLI name (eg. DCL)
11048 * If the specified user does not exist, getpwuid and getpwnam return NULL.
11050 * pw_uid is the full UIC (eg. what's returned by stat() in st_uid).
11051 * not the UIC member number (eg. what's returned by getuid()),
11052 * getpwuid() can accept either as input (if uid is specified, the caller's
11053 * UIC group is used), though it won't recognise gid=0.
11055 * Note that in VMS it is necessary to have GRPPRV or SYSPRV to return
11056 * information about other users in your group or in other groups, respectively.
11057 * If the required privilege is not available, then these routines fill only
11058 * the pw_name, pw_uid, and pw_gid fields (the others point to an empty
11061 * By Tim Adye (T.J.Adye@rl.ac.uk), 10th February 1995.
11064 /* sizes of various UAF record fields */
11065 #define UAI$S_USERNAME 12
11066 #define UAI$S_IDENT 31
11067 #define UAI$S_OWNER 31
11068 #define UAI$S_DEFDEV 31
11069 #define UAI$S_DEFDIR 63
11070 #define UAI$S_DEFCLI 31
11071 #define UAI$S_PWD 8
11073 #define valid_uic(uic) ((uic).uic$v_format == UIC$K_UIC_FORMAT && \
11074 (uic).uic$v_member != UIC$K_WILD_MEMBER && \
11075 (uic).uic$v_group != UIC$K_WILD_GROUP)
11077 static char __empty[]= "";
11078 static struct passwd __passwd_empty=
11079 {(char *) __empty, (char *) __empty, 0, 0,
11080 (char *) __empty, (char *) __empty, (char *) __empty, (char *) __empty};
11081 static int contxt= 0;
11082 static struct passwd __pwdcache;
11083 static char __pw_namecache[UAI$S_IDENT+1];
11086 * This routine does most of the work extracting the user information.
11088 static int fillpasswd (pTHX_ const char *name, struct passwd *pwd)
11091 unsigned char length;
11092 char pw_gecos[UAI$S_OWNER+1];
11094 static union uicdef uic;
11096 unsigned char length;
11097 char pw_dir[UAI$S_DEFDEV+UAI$S_DEFDIR+1];
11100 unsigned char length;
11101 char unixdir[UAI$_DEFDEV+UAI$S_DEFDIR+1];
11104 unsigned char length;
11105 char pw_shell[UAI$S_DEFCLI+1];
11107 static char pw_passwd[UAI$S_PWD+1];
11109 static unsigned short lowner, luic, ldefdev, ldefdir, ldefcli, lpwd;
11110 struct dsc$descriptor_s name_desc;
11111 unsigned long int sts;
11113 static struct itmlst_3 itmlst[]= {
11114 {UAI$S_OWNER+1, UAI$_OWNER, &owner, &lowner},
11115 {sizeof(uic), UAI$_UIC, &uic, &luic},
11116 {UAI$S_DEFDEV+1, UAI$_DEFDEV, &defdev, &ldefdev},
11117 {UAI$S_DEFDIR+1, UAI$_DEFDIR, &defdir, &ldefdir},
11118 {UAI$S_DEFCLI+1, UAI$_DEFCLI, &defcli, &ldefcli},
11119 {UAI$S_PWD, UAI$_PWD, pw_passwd, &lpwd},
11120 {0, 0, NULL, NULL}};
11122 name_desc.dsc$w_length= strlen(name);
11123 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
11124 name_desc.dsc$b_class= DSC$K_CLASS_S;
11125 name_desc.dsc$a_pointer= (char *) name; /* read only pointer */
11127 /* Note that sys$getuai returns many fields as counted strings. */
11128 sts= sys$getuai(0, 0, &name_desc, &itmlst, 0, 0, 0);
11129 if (sts == SS$_NOSYSPRV || sts == SS$_NOGRPPRV || sts == RMS$_RNF) {
11130 set_vaxc_errno(sts); set_errno(sts == RMS$_RNF ? EINVAL : EACCES);
11132 else { _ckvmssts(sts); }
11133 if (!(sts & 1)) return 0; /* out here in case _ckvmssts() doesn't abort */
11135 if ((int) owner.length < lowner) lowner= (int) owner.length;
11136 if ((int) defdev.length < ldefdev) ldefdev= (int) defdev.length;
11137 if ((int) defdir.length < ldefdir) ldefdir= (int) defdir.length;
11138 if ((int) defcli.length < ldefcli) ldefcli= (int) defcli.length;
11139 memcpy(&defdev.pw_dir[ldefdev], &defdir.unixdir[0], ldefdir);
11140 owner.pw_gecos[lowner]= '\0';
11141 defdev.pw_dir[ldefdev+ldefdir]= '\0';
11142 defcli.pw_shell[ldefcli]= '\0';
11143 if (valid_uic(uic)) {
11144 pwd->pw_uid= uic.uic$l_uic;
11145 pwd->pw_gid= uic.uic$v_group;
11148 Perl_warn(aTHX_ "getpwnam returned invalid UIC %#o for user \"%s\"");
11149 pwd->pw_passwd= pw_passwd;
11150 pwd->pw_gecos= owner.pw_gecos;
11151 pwd->pw_dir= defdev.pw_dir;
11152 pwd->pw_unixdir= do_tounixpath(defdev.pw_dir, defdir.unixdir,1,NULL);
11153 pwd->pw_shell= defcli.pw_shell;
11154 if (pwd->pw_unixdir && pwd->pw_unixdir[0]) {
11156 ldir= strlen(pwd->pw_unixdir) - 1;
11157 if (pwd->pw_unixdir[ldir]=='/') pwd->pw_unixdir[ldir]= '\0';
11160 strcpy(pwd->pw_unixdir, pwd->pw_dir);
11161 if (!decc_efs_case_preserve)
11162 __mystrtolower(pwd->pw_unixdir);
11167 * Get information for a named user.
11169 /*{{{struct passwd *getpwnam(char *name)*/
11170 struct passwd *Perl_my_getpwnam(pTHX_ const char *name)
11172 struct dsc$descriptor_s name_desc;
11174 unsigned long int status, sts;
11176 __pwdcache = __passwd_empty;
11177 if (!fillpasswd(aTHX_ name, &__pwdcache)) {
11178 /* We still may be able to determine pw_uid and pw_gid */
11179 name_desc.dsc$w_length= strlen(name);
11180 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
11181 name_desc.dsc$b_class= DSC$K_CLASS_S;
11182 name_desc.dsc$a_pointer= (char *) name;
11183 if ((sts = sys$asctoid(&name_desc, &uic, 0)) == SS$_NORMAL) {
11184 __pwdcache.pw_uid= uic.uic$l_uic;
11185 __pwdcache.pw_gid= uic.uic$v_group;
11188 if (sts == SS$_NOSUCHID || sts == SS$_IVIDENT || sts == RMS$_PRV) {
11189 set_vaxc_errno(sts);
11190 set_errno(sts == RMS$_PRV ? EACCES : EINVAL);
11193 else { _ckvmssts(sts); }
11196 strncpy(__pw_namecache, name, sizeof(__pw_namecache));
11197 __pw_namecache[sizeof __pw_namecache - 1] = '\0';
11198 __pwdcache.pw_name= __pw_namecache;
11199 return &__pwdcache;
11200 } /* end of my_getpwnam() */
11204 * Get information for a particular UIC or UID.
11205 * Called by my_getpwent with uid=-1 to list all users.
11207 /*{{{struct passwd *my_getpwuid(Uid_t uid)*/
11208 struct passwd *Perl_my_getpwuid(pTHX_ Uid_t uid)
11210 const $DESCRIPTOR(name_desc,__pw_namecache);
11211 unsigned short lname;
11213 unsigned long int status;
11215 if (uid == (unsigned int) -1) {
11217 status = sys$idtoasc(-1, &lname, &name_desc, &uic, 0, &contxt);
11218 if (status == SS$_NOSUCHID || status == RMS$_PRV) {
11219 set_vaxc_errno(status);
11220 set_errno(status == RMS$_PRV ? EACCES : EINVAL);
11224 else { _ckvmssts(status); }
11225 } while (!valid_uic (uic));
11228 uic.uic$l_uic= uid;
11229 if (!uic.uic$v_group)
11230 uic.uic$v_group= PerlProc_getgid();
11231 if (valid_uic(uic))
11232 status = sys$idtoasc(uic.uic$l_uic, &lname, &name_desc, 0, 0, 0);
11233 else status = SS$_IVIDENT;
11234 if (status == SS$_IVIDENT || status == SS$_NOSUCHID ||
11235 status == RMS$_PRV) {
11236 set_vaxc_errno(status); set_errno(status == RMS$_PRV ? EACCES : EINVAL);
11239 else { _ckvmssts(status); }
11241 __pw_namecache[lname]= '\0';
11242 __mystrtolower(__pw_namecache);
11244 __pwdcache = __passwd_empty;
11245 __pwdcache.pw_name = __pw_namecache;
11247 /* Fill in the uid and gid in case fillpasswd can't (eg. no privilege).
11248 The identifier's value is usually the UIC, but it doesn't have to be,
11249 so if we can, we let fillpasswd update this. */
11250 __pwdcache.pw_uid = uic.uic$l_uic;
11251 __pwdcache.pw_gid = uic.uic$v_group;
11253 fillpasswd(aTHX_ __pw_namecache, &__pwdcache);
11254 return &__pwdcache;
11256 } /* end of my_getpwuid() */
11260 * Get information for next user.
11262 /*{{{struct passwd *my_getpwent()*/
11263 struct passwd *Perl_my_getpwent(pTHX)
11265 return (my_getpwuid((unsigned int) -1));
11270 * Finish searching rights database for users.
11272 /*{{{void my_endpwent()*/
11273 void Perl_my_endpwent(pTHX)
11276 _ckvmssts(sys$finish_rdb(&contxt));
11282 #ifdef HOMEGROWN_POSIX_SIGNALS
11283 /* Signal handling routines, pulled into the core from POSIX.xs.
11285 * We need these for threads, so they've been rolled into the core,
11286 * rather than left in POSIX.xs.
11288 * (DRS, Oct 23, 1997)
11291 /* sigset_t is atomic under VMS, so these routines are easy */
11292 /*{{{int my_sigemptyset(sigset_t *) */
11293 int my_sigemptyset(sigset_t *set) {
11294 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
11295 *set = 0; return 0;
11300 /*{{{int my_sigfillset(sigset_t *)*/
11301 int my_sigfillset(sigset_t *set) {
11303 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
11304 for (i = 0; i < NSIG; i++) *set |= (1 << i);
11310 /*{{{int my_sigaddset(sigset_t *set, int sig)*/
11311 int my_sigaddset(sigset_t *set, int sig) {
11312 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
11313 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
11314 *set |= (1 << (sig - 1));
11320 /*{{{int my_sigdelset(sigset_t *set, int sig)*/
11321 int my_sigdelset(sigset_t *set, int sig) {
11322 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
11323 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
11324 *set &= ~(1 << (sig - 1));
11330 /*{{{int my_sigismember(sigset_t *set, int sig)*/
11331 int my_sigismember(sigset_t *set, int sig) {
11332 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
11333 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
11334 return *set & (1 << (sig - 1));
11339 /*{{{int my_sigprocmask(int how, sigset_t *set, sigset_t *oset)*/
11340 int my_sigprocmask(int how, sigset_t *set, sigset_t *oset) {
11343 /* If set and oset are both null, then things are badly wrong. Bail out. */
11344 if ((oset == NULL) && (set == NULL)) {
11345 set_errno(EFAULT); set_vaxc_errno(SS$_ACCVIO);
11349 /* If set's null, then we're just handling a fetch. */
11351 tempmask = sigblock(0);
11356 tempmask = sigsetmask(*set);
11359 tempmask = sigblock(*set);
11362 tempmask = sigblock(0);
11363 sigsetmask(*oset & ~tempmask);
11366 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11371 /* Did they pass us an oset? If so, stick our holding mask into it */
11378 #endif /* HOMEGROWN_POSIX_SIGNALS */
11381 /* Used for UTC calculation in my_gmtime(), my_localtime(), my_time(),
11382 * my_utime(), and flex_stat(), all of which operate on UTC unless
11383 * VMSISH_TIMES is true.
11385 /* method used to handle UTC conversions:
11386 * 1 == CRTL gmtime(); 2 == SYS$TIMEZONE_DIFFERENTIAL; 3 == no correction
11388 static int gmtime_emulation_type;
11389 /* number of secs to add to UTC POSIX-style time to get local time */
11390 static long int utc_offset_secs;
11392 /* We #defined 'gmtime', 'localtime', and 'time' as 'my_gmtime' etc.
11393 * in vmsish.h. #undef them here so we can call the CRTL routines
11402 * DEC C previous to 6.0 corrupts the behavior of the /prefix
11403 * qualifier with the extern prefix pragma. This provisional
11404 * hack circumvents this prefix pragma problem in previous
11407 #if defined(__VMS_VER) && __VMS_VER >= 70000000
11408 # if defined(VMS_WE_ARE_CASE_SENSITIVE) && (__DECC_VER < 60000000)
11409 # pragma __extern_prefix save
11410 # pragma __extern_prefix "" /* set to empty to prevent prefixing */
11411 # define gmtime decc$__utctz_gmtime
11412 # define localtime decc$__utctz_localtime
11413 # define time decc$__utc_time
11414 # pragma __extern_prefix restore
11416 struct tm *gmtime(), *localtime();
11422 static time_t toutc_dst(time_t loc) {
11425 if ((rsltmp = localtime(&loc)) == NULL) return -1;
11426 loc -= utc_offset_secs;
11427 if (rsltmp->tm_isdst) loc -= 3600;
11430 #define _toutc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
11431 ((gmtime_emulation_type || my_time(NULL)), \
11432 (gmtime_emulation_type == 1 ? toutc_dst(secs) : \
11433 ((secs) - utc_offset_secs))))
11435 static time_t toloc_dst(time_t utc) {
11438 utc += utc_offset_secs;
11439 if ((rsltmp = localtime(&utc)) == NULL) return -1;
11440 if (rsltmp->tm_isdst) utc += 3600;
11443 #define _toloc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
11444 ((gmtime_emulation_type || my_time(NULL)), \
11445 (gmtime_emulation_type == 1 ? toloc_dst(secs) : \
11446 ((secs) + utc_offset_secs))))
11448 #ifndef RTL_USES_UTC
11451 ucx$tz = "EST5EDT4,M4.1.0,M10.5.0" typical
11452 DST starts on 1st sun of april at 02:00 std time
11453 ends on last sun of october at 02:00 dst time
11454 see the UCX management command reference, SET CONFIG TIMEZONE
11455 for formatting info.
11457 No, it's not as general as it should be, but then again, NOTHING
11458 will handle UK times in a sensible way.
11463 parse the DST start/end info:
11464 (Jddd|ddd|Mmon.nth.dow)[/hh:mm:ss]
11468 tz_parse_startend(char *s, struct tm *w, int *past)
11470 int dinm[] = {31,28,31,30,31,30,31,31,30,31,30,31};
11471 int ly, dozjd, d, m, n, hour, min, sec, j, k;
11476 if (!past) return 0;
11479 if (w->tm_year % 4 == 0) ly = 1;
11480 if (w->tm_year % 100 == 0) ly = 0;
11481 if (w->tm_year+1900 % 400 == 0) ly = 1;
11484 dozjd = isdigit(*s);
11485 if (*s == 'J' || *s == 'j' || dozjd) {
11486 if (!dozjd && !isdigit(*++s)) return 0;
11489 d = d*10 + *s++ - '0';
11491 d = d*10 + *s++ - '0';
11494 if (d == 0) return 0;
11495 if (d > 366) return 0;
11497 if (!dozjd && d > 58 && ly) d++; /* after 28 feb */
11500 } else if (*s == 'M' || *s == 'm') {
11501 if (!isdigit(*++s)) return 0;
11503 if (isdigit(*s)) m = 10*m + *s++ - '0';
11504 if (*s != '.') return 0;
11505 if (!isdigit(*++s)) return 0;
11507 if (n < 1 || n > 5) return 0;
11508 if (*s != '.') return 0;
11509 if (!isdigit(*++s)) return 0;
11511 if (d > 6) return 0;
11515 if (!isdigit(*++s)) return 0;
11517 if (isdigit(*s)) hour = 10*hour + *s++ - '0';
11519 if (!isdigit(*++s)) return 0;
11521 if (isdigit(*s)) min = 10*min + *s++ - '0';
11523 if (!isdigit(*++s)) return 0;
11525 if (isdigit(*s)) sec = 10*sec + *s++ - '0';
11535 if (w->tm_yday < d) goto before;
11536 if (w->tm_yday > d) goto after;
11538 if (w->tm_mon+1 < m) goto before;
11539 if (w->tm_mon+1 > m) goto after;
11541 j = (42 + w->tm_wday - w->tm_mday)%7; /*dow of mday 0 */
11542 k = d - j; /* mday of first d */
11543 if (k <= 0) k += 7;
11544 k += 7 * ((n>4?4:n)-1); /* mday of n'th d */
11545 if (n == 5 && k+7 <= dinm[w->tm_mon]) k += 7;
11546 if (w->tm_mday < k) goto before;
11547 if (w->tm_mday > k) goto after;
11550 if (w->tm_hour < hour) goto before;
11551 if (w->tm_hour > hour) goto after;
11552 if (w->tm_min < min) goto before;
11553 if (w->tm_min > min) goto after;
11554 if (w->tm_sec < sec) goto before;
11568 /* parse the offset: (+|-)hh[:mm[:ss]] */
11571 tz_parse_offset(char *s, int *offset)
11573 int hour = 0, min = 0, sec = 0;
11576 if (!offset) return 0;
11578 if (*s == '-') {neg++; s++;}
11579 if (*s == '+') s++;
11580 if (!isdigit(*s)) return 0;
11582 if (isdigit(*s)) hour = hour*10+(*s++ - '0');
11583 if (hour > 24) return 0;
11585 if (!isdigit(*++s)) return 0;
11587 if (isdigit(*s)) min = min*10 + (*s++ - '0');
11588 if (min > 59) return 0;
11590 if (!isdigit(*++s)) return 0;
11592 if (isdigit(*s)) sec = sec*10 + (*s++ - '0');
11593 if (sec > 59) return 0;
11597 *offset = (hour*60+min)*60 + sec;
11598 if (neg) *offset = -*offset;
11603 input time is w, whatever type of time the CRTL localtime() uses.
11604 sets dst, the zone, and the gmtoff (seconds)
11606 caches the value of TZ and UCX$TZ env variables; note that
11607 my_setenv looks for these and sets a flag if they're changed
11610 We have to watch out for the "australian" case (dst starts in
11611 october, ends in april)...flagged by "reverse" and checked by
11612 scanning through the months of the previous year.
11617 tz_parse(pTHX_ time_t *w, int *dst, char *zone, int *gmtoff)
11622 char *dstzone, *tz, *s_start, *s_end;
11623 int std_off, dst_off, isdst;
11624 int y, dststart, dstend;
11625 static char envtz[1025]; /* longer than any logical, symbol, ... */
11626 static char ucxtz[1025];
11627 static char reversed = 0;
11633 reversed = -1; /* flag need to check */
11634 envtz[0] = ucxtz[0] = '\0';
11635 tz = my_getenv("TZ",0);
11636 if (tz) strcpy(envtz, tz);
11637 tz = my_getenv("UCX$TZ",0);
11638 if (tz) strcpy(ucxtz, tz);
11639 if (!envtz[0] && !ucxtz[0]) return 0; /* we give up */
11642 if (!*tz) tz = ucxtz;
11645 while (isalpha(*s)) s++;
11646 s = tz_parse_offset(s, &std_off);
11648 if (!*s) { /* no DST, hurray we're done! */
11654 while (isalpha(*s)) s++;
11655 s2 = tz_parse_offset(s, &dst_off);
11659 dst_off = std_off - 3600;
11662 if (!*s) { /* default dst start/end?? */
11663 if (tz != ucxtz) { /* if TZ tells zone only, UCX$TZ tells rule */
11664 s = strchr(ucxtz,',');
11666 if (!s || !*s) s = ",M4.1.0,M10.5.0"; /* we know we do dst, default rule */
11668 if (*s != ',') return 0;
11671 when = _toutc(when); /* convert to utc */
11672 when = when - std_off; /* convert to pseudolocal time*/
11674 w2 = localtime(&when);
11677 s = tz_parse_startend(s_start,w2,&dststart);
11679 if (*s != ',') return 0;
11682 when = _toutc(when); /* convert to utc */
11683 when = when - dst_off; /* convert to pseudolocal time*/
11684 w2 = localtime(&when);
11685 if (w2->tm_year != y) { /* spans a year, just check one time */
11686 when += dst_off - std_off;
11687 w2 = localtime(&when);
11690 s = tz_parse_startend(s_end,w2,&dstend);
11693 if (reversed == -1) { /* need to check if start later than end */
11697 if (when < 2*365*86400) {
11698 when += 2*365*86400;
11702 w2 =localtime(&when);
11703 when = when + (15 - w2->tm_yday) * 86400; /* jan 15 */
11705 for (j = 0; j < 12; j++) {
11706 w2 =localtime(&when);
11707 tz_parse_startend(s_start,w2,&ds);
11708 tz_parse_startend(s_end,w2,&de);
11709 if (ds != de) break;
11713 if (de && !ds) reversed = 1;
11716 isdst = dststart && !dstend;
11717 if (reversed) isdst = dststart || !dstend;
11720 if (dst) *dst = isdst;
11721 if (gmtoff) *gmtoff = isdst ? dst_off : std_off;
11722 if (isdst) tz = dstzone;
11724 while(isalpha(*tz)) *zone++ = *tz++;
11730 #endif /* !RTL_USES_UTC */
11732 /* my_time(), my_localtime(), my_gmtime()
11733 * By default traffic in UTC time values, using CRTL gmtime() or
11734 * SYS$TIMEZONE_DIFFERENTIAL to determine offset from local time zone.
11735 * Note: We need to use these functions even when the CRTL has working
11736 * UTC support, since they also handle C<use vmsish qw(times);>
11738 * Contributed by Chuck Lane <lane@duphy4.physics.drexel.edu>
11739 * Modified by Charles Bailey <bailey@newman.upenn.edu>
11742 /*{{{time_t my_time(time_t *timep)*/
11743 time_t Perl_my_time(pTHX_ time_t *timep)
11748 if (gmtime_emulation_type == 0) {
11750 time_t base = 15 * 86400; /* 15jan71; to avoid month/year ends between */
11751 /* results of calls to gmtime() and localtime() */
11752 /* for same &base */
11754 gmtime_emulation_type++;
11755 if ((tm_p = gmtime(&base)) == NULL) { /* CRTL gmtime() is a fake */
11756 char off[LNM$C_NAMLENGTH+1];;
11758 gmtime_emulation_type++;
11759 if (!vmstrnenv("SYS$TIMEZONE_DIFFERENTIAL",off,0,fildev,0)) {
11760 gmtime_emulation_type++;
11761 utc_offset_secs = 0;
11762 Perl_warn(aTHX_ "no UTC offset information; assuming local time is UTC");
11764 else { utc_offset_secs = atol(off); }
11766 else { /* We've got a working gmtime() */
11767 struct tm gmt, local;
11770 tm_p = localtime(&base);
11772 utc_offset_secs = (local.tm_mday - gmt.tm_mday) * 86400;
11773 utc_offset_secs += (local.tm_hour - gmt.tm_hour) * 3600;
11774 utc_offset_secs += (local.tm_min - gmt.tm_min) * 60;
11775 utc_offset_secs += (local.tm_sec - gmt.tm_sec);
11780 # ifdef VMSISH_TIME
11781 # ifdef RTL_USES_UTC
11782 if (VMSISH_TIME) when = _toloc(when);
11784 if (!VMSISH_TIME) when = _toutc(when);
11787 if (timep != NULL) *timep = when;
11790 } /* end of my_time() */
11794 /*{{{struct tm *my_gmtime(const time_t *timep)*/
11796 Perl_my_gmtime(pTHX_ const time_t *timep)
11802 if (timep == NULL) {
11803 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11806 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
11809 # ifdef VMSISH_TIME
11810 if (VMSISH_TIME) when = _toutc(when); /* Input was local time */
11812 # ifdef RTL_USES_UTC /* this implies that the CRTL has a working gmtime() */
11813 return gmtime(&when);
11815 /* CRTL localtime() wants local time as input, so does no tz correction */
11816 rsltmp = localtime(&when);
11817 if (rsltmp) rsltmp->tm_isdst = 0; /* We already took DST into account */
11820 } /* end of my_gmtime() */
11824 /*{{{struct tm *my_localtime(const time_t *timep)*/
11826 Perl_my_localtime(pTHX_ const time_t *timep)
11828 time_t when, whenutc;
11832 if (timep == NULL) {
11833 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11836 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
11837 if (gmtime_emulation_type == 0) my_time(NULL); /* Init UTC */
11840 # ifdef RTL_USES_UTC
11841 # ifdef VMSISH_TIME
11842 if (VMSISH_TIME) when = _toutc(when);
11844 /* CRTL localtime() wants UTC as input, does tz correction itself */
11845 return localtime(&when);
11847 # else /* !RTL_USES_UTC */
11849 # ifdef VMSISH_TIME
11850 if (!VMSISH_TIME) when = _toloc(whenutc); /* input was UTC */
11851 if (VMSISH_TIME) whenutc = _toutc(when); /* input was truelocal */
11854 #ifndef RTL_USES_UTC
11855 if (tz_parse(aTHX_ &when, &dst, 0, &offset)) { /* truelocal determines DST*/
11856 when = whenutc - offset; /* pseudolocal time*/
11859 /* CRTL localtime() wants local time as input, so does no tz correction */
11860 rsltmp = localtime(&when);
11861 if (rsltmp && gmtime_emulation_type != 1) rsltmp->tm_isdst = dst;
11865 } /* end of my_localtime() */
11868 /* Reset definitions for later calls */
11869 #define gmtime(t) my_gmtime(t)
11870 #define localtime(t) my_localtime(t)
11871 #define time(t) my_time(t)
11874 /* my_utime - update modification/access time of a file
11876 * VMS 7.3 and later implementation
11877 * Only the UTC translation is home-grown. The rest is handled by the
11878 * CRTL utime(), which will take into account the relevant feature
11879 * logicals and ODS-5 volume characteristics for true access times.
11881 * pre VMS 7.3 implementation:
11882 * The calling sequence is identical to POSIX utime(), but under
11883 * VMS with ODS-2, only the modification time is changed; ODS-2 does
11884 * not maintain access times. Restrictions differ from the POSIX
11885 * definition in that the time can be changed as long as the
11886 * caller has permission to execute the necessary IO$_MODIFY $QIO;
11887 * no separate checks are made to insure that the caller is the
11888 * owner of the file or has special privs enabled.
11889 * Code here is based on Joe Meadows' FILE utility.
11893 /* Adjustment from Unix epoch (01-JAN-1970 00:00:00.00)
11894 * to VMS epoch (01-JAN-1858 00:00:00.00)
11895 * in 100 ns intervals.
11897 static const long int utime_baseadjust[2] = { 0x4beb4000, 0x7c9567 };
11899 /*{{{int my_utime(const char *path, const struct utimbuf *utimes)*/
11900 int Perl_my_utime(pTHX_ const char *file, const struct utimbuf *utimes)
11902 #if __CRTL_VER >= 70300000
11903 struct utimbuf utc_utimes, *utc_utimesp;
11905 if (utimes != NULL) {
11906 utc_utimes.actime = utimes->actime;
11907 utc_utimes.modtime = utimes->modtime;
11908 # ifdef VMSISH_TIME
11909 /* If input was local; convert to UTC for sys svc */
11911 utc_utimes.actime = _toutc(utimes->actime);
11912 utc_utimes.modtime = _toutc(utimes->modtime);
11915 utc_utimesp = &utc_utimes;
11918 utc_utimesp = NULL;
11921 return utime(file, utc_utimesp);
11923 #else /* __CRTL_VER < 70300000 */
11927 long int bintime[2], len = 2, lowbit, unixtime,
11928 secscale = 10000000; /* seconds --> 100 ns intervals */
11929 unsigned long int chan, iosb[2], retsts;
11930 char vmsspec[NAM$C_MAXRSS+1], rsa[NAM$C_MAXRSS], esa[NAM$C_MAXRSS];
11931 struct FAB myfab = cc$rms_fab;
11932 struct NAM mynam = cc$rms_nam;
11933 #if defined (__DECC) && defined (__VAX)
11934 /* VAX DEC C atrdef.h has unsigned type for pointer member atr$l_addr,
11935 * at least through VMS V6.1, which causes a type-conversion warning.
11937 # pragma message save
11938 # pragma message disable cvtdiftypes
11940 struct atrdef myatr[2] = {{sizeof bintime, ATR$C_REVDATE, bintime}, {0,0,0}};
11941 struct fibdef myfib;
11942 #if defined (__DECC) && defined (__VAX)
11943 /* This should be right after the declaration of myatr, but due
11944 * to a bug in VAX DEC C, this takes effect a statement early.
11946 # pragma message restore
11948 /* cast ok for read only parameter */
11949 struct dsc$descriptor fibdsc = {sizeof(myfib), DSC$K_DTYPE_Z, DSC$K_CLASS_S,(char *) &myfib},
11950 devdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0},
11951 fnmdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0};
11953 if (file == NULL || *file == '\0') {
11954 SETERRNO(ENOENT, LIB$_INVARG);
11958 /* Convert to VMS format ensuring that it will fit in 255 characters */
11959 if (int_rmsexpand_tovms(file, vmsspec, 0) == NULL) {
11960 SETERRNO(ENOENT, LIB$_INVARG);
11963 if (utimes != NULL) {
11964 /* Convert Unix time (seconds since 01-JAN-1970 00:00:00.00)
11965 * to VMS quadword time (100 nsec intervals since 01-JAN-1858 00:00:00.00).
11966 * Since time_t is unsigned long int, and lib$emul takes a signed long int
11967 * as input, we force the sign bit to be clear by shifting unixtime right
11968 * one bit, then multiplying by an extra factor of 2 in lib$emul().
11970 lowbit = (utimes->modtime & 1) ? secscale : 0;
11971 unixtime = (long int) utimes->modtime;
11972 # ifdef VMSISH_TIME
11973 /* If input was UTC; convert to local for sys svc */
11974 if (!VMSISH_TIME) unixtime = _toloc(unixtime);
11976 unixtime >>= 1; secscale <<= 1;
11977 retsts = lib$emul(&secscale, &unixtime, &lowbit, bintime);
11978 if (!(retsts & 1)) {
11979 SETERRNO(EVMSERR, retsts);
11982 retsts = lib$addx(bintime,utime_baseadjust,bintime,&len);
11983 if (!(retsts & 1)) {
11984 SETERRNO(EVMSERR, retsts);
11989 /* Just get the current time in VMS format directly */
11990 retsts = sys$gettim(bintime);
11991 if (!(retsts & 1)) {
11992 SETERRNO(EVMSERR, retsts);
11997 myfab.fab$l_fna = vmsspec;
11998 myfab.fab$b_fns = (unsigned char) strlen(vmsspec);
11999 myfab.fab$l_nam = &mynam;
12000 mynam.nam$l_esa = esa;
12001 mynam.nam$b_ess = (unsigned char) sizeof esa;
12002 mynam.nam$l_rsa = rsa;
12003 mynam.nam$b_rss = (unsigned char) sizeof rsa;
12004 if (decc_efs_case_preserve)
12005 mynam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
12007 /* Look for the file to be affected, letting RMS parse the file
12008 * specification for us as well. I have set errno using only
12009 * values documented in the utime() man page for VMS POSIX.
12011 retsts = sys$parse(&myfab,0,0);
12012 if (!(retsts & 1)) {
12013 set_vaxc_errno(retsts);
12014 if (retsts == RMS$_PRV) set_errno(EACCES);
12015 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
12016 else set_errno(EVMSERR);
12019 retsts = sys$search(&myfab,0,0);
12020 if (!(retsts & 1)) {
12021 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
12022 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
12023 set_vaxc_errno(retsts);
12024 if (retsts == RMS$_PRV) set_errno(EACCES);
12025 else if (retsts == RMS$_FNF) set_errno(ENOENT);
12026 else set_errno(EVMSERR);
12030 devdsc.dsc$w_length = mynam.nam$b_dev;
12031 /* cast ok for read only parameter */
12032 devdsc.dsc$a_pointer = (char *) mynam.nam$l_dev;
12034 retsts = sys$assign(&devdsc,&chan,0,0);
12035 if (!(retsts & 1)) {
12036 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
12037 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
12038 set_vaxc_errno(retsts);
12039 if (retsts == SS$_IVDEVNAM) set_errno(ENOTDIR);
12040 else if (retsts == SS$_NOPRIV) set_errno(EACCES);
12041 else if (retsts == SS$_NOSUCHDEV) set_errno(ENOTDIR);
12042 else set_errno(EVMSERR);
12046 fnmdsc.dsc$a_pointer = mynam.nam$l_name;
12047 fnmdsc.dsc$w_length = mynam.nam$b_name + mynam.nam$b_type + mynam.nam$b_ver;
12049 memset((void *) &myfib, 0, sizeof myfib);
12050 #if defined(__DECC) || defined(__DECCXX)
12051 for (i=0;i<3;i++) myfib.fib$w_fid[i] = mynam.nam$w_fid[i];
12052 for (i=0;i<3;i++) myfib.fib$w_did[i] = mynam.nam$w_did[i];
12053 /* This prevents the revision time of the file being reset to the current
12054 * time as a result of our IO$_MODIFY $QIO. */
12055 myfib.fib$l_acctl = FIB$M_NORECORD;
12057 for (i=0;i<3;i++) myfib.fib$r_fid_overlay.fib$w_fid[i] = mynam.nam$w_fid[i];
12058 for (i=0;i<3;i++) myfib.fib$r_did_overlay.fib$w_did[i] = mynam.nam$w_did[i];
12059 myfib.fib$r_acctl_overlay.fib$l_acctl = FIB$M_NORECORD;
12061 retsts = sys$qiow(0,chan,IO$_MODIFY,iosb,0,0,&fibdsc,&fnmdsc,0,0,myatr,0);
12062 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
12063 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
12064 _ckvmssts(sys$dassgn(chan));
12065 if (retsts & 1) retsts = iosb[0];
12066 if (!(retsts & 1)) {
12067 set_vaxc_errno(retsts);
12068 if (retsts == SS$_NOPRIV) set_errno(EACCES);
12069 else set_errno(EVMSERR);
12075 #endif /* #if __CRTL_VER >= 70300000 */
12077 } /* end of my_utime() */
12081 * flex_stat, flex_lstat, flex_fstat
12082 * basic stat, but gets it right when asked to stat
12083 * a Unix-style path ending in a directory name (e.g. dir1/dir2/dir3)
12086 #ifndef _USE_STD_STAT
12087 /* encode_dev packs a VMS device name string into an integer to allow
12088 * simple comparisons. This can be used, for example, to check whether two
12089 * files are located on the same device, by comparing their encoded device
12090 * names. Even a string comparison would not do, because stat() reuses the
12091 * device name buffer for each call; so without encode_dev, it would be
12092 * necessary to save the buffer and use strcmp (this would mean a number of
12093 * changes to the standard Perl code, to say nothing of what a Perl script
12094 * would have to do.
12096 * The device lock id, if it exists, should be unique (unless perhaps compared
12097 * with lock ids transferred from other nodes). We have a lock id if the disk is
12098 * mounted cluster-wide, which is when we tend to get long (host-qualified)
12099 * device names. Thus we use the lock id in preference, and only if that isn't
12100 * available, do we try to pack the device name into an integer (flagged by
12101 * the sign bit (LOCKID_MASK) being set).
12103 * Note that encode_dev cannot guarantee an 1-to-1 correspondence twixt device
12104 * name and its encoded form, but it seems very unlikely that we will find
12105 * two files on different disks that share the same encoded device names,
12106 * and even more remote that they will share the same file id (if the test
12107 * is to check for the same file).
12109 * A better method might be to use sys$device_scan on the first call, and to
12110 * search for the device, returning an index into the cached array.
12111 * The number returned would be more intelligible.
12112 * This is probably not worth it, and anyway would take quite a bit longer
12113 * on the first call.
12115 #define LOCKID_MASK 0x80000000 /* Use 0 to force device name use only */
12116 static mydev_t encode_dev (pTHX_ const char *dev)
12119 unsigned long int f;
12124 if (!dev || !dev[0]) return 0;
12128 struct dsc$descriptor_s dev_desc;
12129 unsigned long int status, lockid = 0, item = DVI$_LOCKID;
12131 /* For cluster-mounted disks, the disk lock identifier is unique, so we
12132 can try that first. */
12133 dev_desc.dsc$w_length = strlen (dev);
12134 dev_desc.dsc$b_dtype = DSC$K_DTYPE_T;
12135 dev_desc.dsc$b_class = DSC$K_CLASS_S;
12136 dev_desc.dsc$a_pointer = (char *) dev; /* Read only parameter */
12137 status = lib$getdvi(&item, 0, &dev_desc, &lockid, 0, 0);
12138 if (!$VMS_STATUS_SUCCESS(status)) {
12140 case SS$_NOSUCHDEV:
12141 SETERRNO(ENODEV, status);
12147 if (lockid) return (lockid & ~LOCKID_MASK);
12151 /* Otherwise we try to encode the device name */
12155 for (q = dev + strlen(dev); q--; q >= dev) {
12160 else if (isalpha (toupper (*q)))
12161 c= toupper (*q) - 'A' + (char)10;
12163 continue; /* Skip '$'s */
12165 if (i>6) break; /* 36^7 is too large to fit in an unsigned long int */
12167 enc += f * (unsigned long int) c;
12169 return (enc | LOCKID_MASK); /* May have already overflowed into bit 31 */
12171 } /* end of encode_dev() */
12172 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
12173 device_no = encode_dev(aTHX_ devname)
12175 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
12176 device_no = new_dev_no
12180 is_null_device(name)
12183 if (decc_bug_devnull != 0) {
12184 if (strncmp("/dev/null", name, 9) == 0)
12187 /* The VMS null device is named "_NLA0:", usually abbreviated as "NL:".
12188 The underscore prefix, controller letter, and unit number are
12189 independently optional; for our purposes, the colon punctuation
12190 is not. The colon can be trailed by optional directory and/or
12191 filename, but two consecutive colons indicates a nodename rather
12192 than a device. [pr] */
12193 if (*name == '_') ++name;
12194 if (tolower(*name++) != 'n') return 0;
12195 if (tolower(*name++) != 'l') return 0;
12196 if (tolower(*name) == 'a') ++name;
12197 if (*name == '0') ++name;
12198 return (*name++ == ':') && (*name != ':');
12203 Perl_cando_by_name_int
12204 (pTHX_ I32 bit, bool effective, const char *fname, int opts)
12206 char usrname[L_cuserid];
12207 struct dsc$descriptor_s usrdsc =
12208 {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, usrname};
12209 char *vmsname = NULL, *fileified = NULL;
12210 unsigned long int objtyp = ACL$C_FILE, access, retsts, privused, iosb[2], flags;
12211 unsigned short int retlen, trnlnm_iter_count;
12212 struct dsc$descriptor_s namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
12213 union prvdef curprv;
12214 struct itmlst_3 armlst[4] = {{sizeof access, CHP$_ACCESS, &access, &retlen},
12215 {sizeof privused, CHP$_PRIVUSED, &privused, &retlen},
12216 {sizeof flags, CHP$_FLAGS, &flags, &retlen},{0,0,0,0}};
12217 struct itmlst_3 jpilst[3] = {{sizeof curprv, JPI$_CURPRIV, &curprv, &retlen},
12218 {sizeof usrname, JPI$_USERNAME, &usrname, &usrdsc.dsc$w_length},
12220 struct itmlst_3 usrprolst[2] = {{sizeof curprv, CHP$_PRIV, &curprv, &retlen},
12222 struct dsc$descriptor_s usrprodsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
12224 static int profile_context = -1;
12226 if (!fname || !*fname) return FALSE;
12228 /* Make sure we expand logical names, since sys$check_access doesn't */
12229 fileified = PerlMem_malloc(VMS_MAXRSS);
12230 if (fileified == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12231 if (!strpbrk(fname,"/]>:")) {
12232 strcpy(fileified,fname);
12233 trnlnm_iter_count = 0;
12234 while (!strpbrk(fileified,"/]>:") && my_trnlnm(fileified,fileified,0)) {
12235 trnlnm_iter_count++;
12236 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
12241 vmsname = PerlMem_malloc(VMS_MAXRSS);
12242 if (vmsname == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12243 if ( !(opts & PERL_RMSEXPAND_M_VMS_IN) ) {
12244 /* Don't know if already in VMS format, so make sure */
12245 if (!do_rmsexpand(fname, vmsname, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL)) {
12246 PerlMem_free(fileified);
12247 PerlMem_free(vmsname);
12252 strcpy(vmsname,fname);
12255 /* sys$check_access needs a file spec, not a directory spec.
12256 * Don't use flex_stat here, as that depends on thread context
12257 * having been initialized, and we may get here during startup.
12260 retlen = namdsc.dsc$w_length = strlen(vmsname);
12261 if (vmsname[retlen-1] == ']'
12262 || vmsname[retlen-1] == '>'
12263 || vmsname[retlen-1] == ':'
12264 || (!stat(vmsname, (stat_t *)&st) && S_ISDIR(st.st_mode))) {
12266 if (!do_fileify_dirspec(vmsname,fileified,1,NULL)) {
12267 PerlMem_free(fileified);
12268 PerlMem_free(vmsname);
12277 retlen = namdsc.dsc$w_length = strlen(fname);
12278 namdsc.dsc$a_pointer = (char *)fname;
12281 case S_IXUSR: case S_IXGRP: case S_IXOTH:
12282 access = ARM$M_EXECUTE;
12283 flags = CHP$M_READ;
12285 case S_IRUSR: case S_IRGRP: case S_IROTH:
12286 access = ARM$M_READ;
12287 flags = CHP$M_READ | CHP$M_USEREADALL;
12289 case S_IWUSR: case S_IWGRP: case S_IWOTH:
12290 access = ARM$M_WRITE;
12291 flags = CHP$M_READ | CHP$M_WRITE;
12293 case S_IDUSR: case S_IDGRP: case S_IDOTH:
12294 access = ARM$M_DELETE;
12295 flags = CHP$M_READ | CHP$M_WRITE;
12298 if (fileified != NULL)
12299 PerlMem_free(fileified);
12300 if (vmsname != NULL)
12301 PerlMem_free(vmsname);
12305 /* Before we call $check_access, create a user profile with the current
12306 * process privs since otherwise it just uses the default privs from the
12307 * UAF and might give false positives or negatives. This only works on
12308 * VMS versions v6.0 and later since that's when sys$create_user_profile
12309 * became available.
12312 /* get current process privs and username */
12313 _ckvmssts_noperl(sys$getjpiw(0,0,0,jpilst,iosb,0,0));
12314 _ckvmssts_noperl(iosb[0]);
12316 #if defined(__VMS_VER) && __VMS_VER >= 60000000
12318 /* find out the space required for the profile */
12319 _ckvmssts_noperl(sys$create_user_profile(&usrdsc,&usrprolst,0,0,
12320 &usrprodsc.dsc$w_length,&profile_context));
12322 /* allocate space for the profile and get it filled in */
12323 usrprodsc.dsc$a_pointer = PerlMem_malloc(usrprodsc.dsc$w_length);
12324 if (usrprodsc.dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12325 _ckvmssts_noperl(sys$create_user_profile(&usrdsc,&usrprolst,0,usrprodsc.dsc$a_pointer,
12326 &usrprodsc.dsc$w_length,&profile_context));
12328 /* use the profile to check access to the file; free profile & analyze results */
12329 retsts = sys$check_access(&objtyp,&namdsc,0,armlst,&profile_context,0,0,&usrprodsc);
12330 PerlMem_free(usrprodsc.dsc$a_pointer);
12331 if (retsts == SS$_NOCALLPRIV) retsts = SS$_NOPRIV; /* not really 3rd party */
12335 retsts = sys$check_access(&objtyp,&namdsc,&usrdsc,armlst);
12339 if (retsts == SS$_NOPRIV || retsts == SS$_NOSUCHOBJECT ||
12340 retsts == SS$_INVFILFOROP || retsts == RMS$_FNF || retsts == RMS$_SYN ||
12341 retsts == RMS$_DIR || retsts == RMS$_DEV || retsts == RMS$_DNF) {
12342 set_vaxc_errno(retsts);
12343 if (retsts == SS$_NOPRIV) set_errno(EACCES);
12344 else if (retsts == SS$_INVFILFOROP) set_errno(EINVAL);
12345 else set_errno(ENOENT);
12346 if (fileified != NULL)
12347 PerlMem_free(fileified);
12348 if (vmsname != NULL)
12349 PerlMem_free(vmsname);
12352 if (retsts == SS$_NORMAL || retsts == SS$_ACCONFLICT) {
12353 if (fileified != NULL)
12354 PerlMem_free(fileified);
12355 if (vmsname != NULL)
12356 PerlMem_free(vmsname);
12359 _ckvmssts_noperl(retsts);
12361 if (fileified != NULL)
12362 PerlMem_free(fileified);
12363 if (vmsname != NULL)
12364 PerlMem_free(vmsname);
12365 return FALSE; /* Should never get here */
12369 /* Do the permissions allow some operation? Assumes PL_statcache already set. */
12370 /* Do this via $Check_Access on VMS, since the CRTL stat() returns only a
12371 * subset of the applicable information.
12374 Perl_cando(pTHX_ Mode_t bit, bool effective, const Stat_t *statbufp)
12376 return cando_by_name_int
12377 (bit, effective, statbufp->st_devnam, PERL_RMSEXPAND_M_VMS_IN);
12378 } /* end of cando() */
12382 /*{{{I32 cando_by_name(I32 bit, bool effective, char *fname)*/
12384 Perl_cando_by_name(pTHX_ I32 bit, bool effective, const char *fname)
12386 return cando_by_name_int(bit, effective, fname, 0);
12388 } /* end of cando_by_name() */
12392 /*{{{ int flex_fstat(int fd, Stat_t *statbuf)*/
12394 Perl_flex_fstat(pTHX_ int fd, Stat_t *statbufp)
12396 if (!fstat(fd,(stat_t *) statbufp)) {
12398 char *vms_filename;
12399 vms_filename = PerlMem_malloc(VMS_MAXRSS);
12400 if (vms_filename == NULL) _ckvmssts(SS$_INSFMEM);
12402 /* Save name for cando by name in VMS format */
12403 cptr = getname(fd, vms_filename, 1);
12405 /* This should not happen, but just in case */
12406 if (cptr == NULL) {
12407 statbufp->st_devnam[0] = 0;
12410 /* Make sure that the saved name fits in 255 characters */
12411 cptr = int_rmsexpand_vms
12413 statbufp->st_devnam,
12416 statbufp->st_devnam[0] = 0;
12418 PerlMem_free(vms_filename);
12420 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
12422 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
12424 # ifdef RTL_USES_UTC
12425 # ifdef VMSISH_TIME
12427 statbufp->st_mtime = _toloc(statbufp->st_mtime);
12428 statbufp->st_atime = _toloc(statbufp->st_atime);
12429 statbufp->st_ctime = _toloc(statbufp->st_ctime);
12433 # ifdef VMSISH_TIME
12434 if (!VMSISH_TIME) { /* Return UTC instead of local time */
12438 statbufp->st_mtime = _toutc(statbufp->st_mtime);
12439 statbufp->st_atime = _toutc(statbufp->st_atime);
12440 statbufp->st_ctime = _toutc(statbufp->st_ctime);
12447 } /* end of flex_fstat() */
12450 #if !defined(__VAX) && __CRTL_VER >= 80200000
12458 #define lstat(_x, _y) stat(_x, _y)
12461 #define flex_stat_int(a,b,c) Perl_flex_stat_int(aTHX_ a,b,c)
12464 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag)
12466 char fileified[VMS_MAXRSS];
12467 char temp_fspec[VMS_MAXRSS];
12472 if (!fspec) return retval;
12474 strcpy(temp_fspec, fspec);
12476 if (decc_bug_devnull != 0) {
12477 if (is_null_device(temp_fspec)) { /* Fake a stat() for the null device */
12478 memset(statbufp,0,sizeof *statbufp);
12479 VMS_DEVICE_ENCODE(statbufp->st_dev, "_NLA0:", 0);
12480 statbufp->st_mode = S_IFBLK | S_IREAD | S_IWRITE | S_IEXEC;
12481 statbufp->st_uid = 0x00010001;
12482 statbufp->st_gid = 0x0001;
12483 time((time_t *)&statbufp->st_mtime);
12484 statbufp->st_atime = statbufp->st_ctime = statbufp->st_mtime;
12489 /* Try for a directory name first. If fspec contains a filename without
12490 * a type (e.g. sea:[wine.dark]water), and both sea:[wine.dark]water.dir
12491 * and sea:[wine.dark]water. exist, we prefer the directory here.
12492 * Similarly, sea:[wine.dark] returns the result for sea:[wine]dark.dir,
12493 * not sea:[wine.dark]., if the latter exists. If the intended target is
12494 * the file with null type, specify this by calling flex_stat() with
12495 * a '.' at the end of fspec.
12497 * If we are in Posix filespec mode, accept the filename as is.
12501 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12502 /* The CRTL stat() falls down hard on multi-dot filenames in unix format unless
12503 * DECC$EFS_CHARSET is in effect, so temporarily enable it if it isn't already.
12505 if (!decc_efs_charset)
12506 decc$feature_set_value(decc$feature_get_index("DECC$EFS_CHARSET"),1,1);
12509 #if __CRTL_VER >= 80200000 && !defined(__VAX)
12510 if (decc_posix_compliant_pathnames == 0) {
12512 if (do_fileify_dirspec(temp_fspec,fileified,0,NULL) != NULL) {
12513 if (lstat_flag == 0)
12514 retval = stat(fileified,(stat_t *) statbufp);
12516 retval = lstat(fileified,(stat_t *) statbufp);
12517 save_spec = fileified;
12520 if (lstat_flag == 0)
12521 retval = stat(temp_fspec,(stat_t *) statbufp);
12523 retval = lstat(temp_fspec,(stat_t *) statbufp);
12524 save_spec = temp_fspec;
12527 * In debugging, on 8.3 Alpha, I found a case where stat was returning a
12528 * file not found error for a directory named foo:[bar.t] or /foo/bar/t
12529 * and lstat was working correctly for the same file.
12530 * The only syntax that was working for stat was "foo:[bar]t.dir".
12532 * Other directories with the same syntax worked fine.
12533 * So work around the problem when it shows up here.
12536 int save_errno = errno;
12537 if (do_tovmsspec(fspec, temp_fspec, 0, NULL) != NULL) {
12538 if (do_fileify_dirspec(temp_fspec, fileified, 0, NULL) != NULL) {
12539 retval = stat(fileified, (stat_t *) statbufp);
12540 save_spec = fileified;
12543 /* Restore the errno value if third stat does not succeed */
12545 errno = save_errno;
12547 #if __CRTL_VER >= 80200000 && !defined(__VAX)
12549 if (lstat_flag == 0)
12550 retval = stat(temp_fspec,(stat_t *) statbufp);
12552 retval = lstat(temp_fspec,(stat_t *) statbufp);
12553 save_spec = temp_fspec;
12557 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12558 /* As you were... */
12559 if (!decc_efs_charset)
12560 decc$feature_set_value(decc$feature_get_index("DECC$EFS_CHARSET"),1,0);
12565 int rmsex_flags = PERL_RMSEXPAND_M_VMS;
12567 /* If this is an lstat, do not follow the link */
12569 rmsex_flags |= PERL_RMSEXPAND_M_SYMLINK;
12571 cptr = int_rmsexpand_tovms(save_spec, statbufp->st_devnam, rmsex_flags);
12573 statbufp->st_devnam[0] = 0;
12575 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
12577 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
12578 # ifdef RTL_USES_UTC
12579 # ifdef VMSISH_TIME
12581 statbufp->st_mtime = _toloc(statbufp->st_mtime);
12582 statbufp->st_atime = _toloc(statbufp->st_atime);
12583 statbufp->st_ctime = _toloc(statbufp->st_ctime);
12587 # ifdef VMSISH_TIME
12588 if (!VMSISH_TIME) { /* Return UTC instead of local time */
12592 statbufp->st_mtime = _toutc(statbufp->st_mtime);
12593 statbufp->st_atime = _toutc(statbufp->st_atime);
12594 statbufp->st_ctime = _toutc(statbufp->st_ctime);
12598 /* If we were successful, leave errno where we found it */
12599 if (retval == 0) RESTORE_ERRNO;
12602 } /* end of flex_stat_int() */
12605 /*{{{ int flex_stat(const char *fspec, Stat_t *statbufp)*/
12607 Perl_flex_stat(pTHX_ const char *fspec, Stat_t *statbufp)
12609 return flex_stat_int(fspec, statbufp, 0);
12613 /*{{{ int flex_lstat(const char *fspec, Stat_t *statbufp)*/
12615 Perl_flex_lstat(pTHX_ const char *fspec, Stat_t *statbufp)
12617 return flex_stat_int(fspec, statbufp, 1);
12622 /*{{{char *my_getlogin()*/
12623 /* VMS cuserid == Unix getlogin, except calling sequence */
12627 static char user[L_cuserid];
12628 return cuserid(user);
12633 /* rmscopy - copy a file using VMS RMS routines
12635 * Copies contents and attributes of spec_in to spec_out, except owner
12636 * and protection information. Name and type of spec_in are used as
12637 * defaults for spec_out. The third parameter specifies whether rmscopy()
12638 * should try to propagate timestamps from the input file to the output file.
12639 * If it is less than 0, no timestamps are preserved. If it is 0, then
12640 * rmscopy() will behave similarly to the DCL COPY command: timestamps are
12641 * propagated to the output file at creation iff the output file specification
12642 * did not contain an explicit name or type, and the revision date is always
12643 * updated at the end of the copy operation. If it is greater than 0, then
12644 * it is interpreted as a bitmask, in which bit 0 indicates that timestamps
12645 * other than the revision date should be propagated, and bit 1 indicates
12646 * that the revision date should be propagated.
12648 * Returns 1 on success; returns 0 and sets errno and vaxc$errno on failure.
12650 * Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>.
12651 * Incorporates, with permission, some code from EZCOPY by Tim Adye
12652 * <T.J.Adye@rl.ac.uk>. Permission is given to distribute this code
12653 * as part of the Perl standard distribution under the terms of the
12654 * GNU General Public License or the Perl Artistic License. Copies
12655 * of each may be found in the Perl standard distribution.
12657 /*{{{int rmscopy(char *src, char *dst, int preserve_dates)*/
12659 Perl_rmscopy(pTHX_ const char *spec_in, const char *spec_out, int preserve_dates)
12661 char *vmsin, * vmsout, *esa, *esal, *esa_out, *esal_out,
12662 *rsa, *rsal, *rsa_out, *rsal_out, *ubf;
12663 unsigned long int i, sts, sts2;
12665 struct FAB fab_in, fab_out;
12666 struct RAB rab_in, rab_out;
12667 rms_setup_nam(nam);
12668 rms_setup_nam(nam_out);
12669 struct XABDAT xabdat;
12670 struct XABFHC xabfhc;
12671 struct XABRDT xabrdt;
12672 struct XABSUM xabsum;
12674 vmsin = PerlMem_malloc(VMS_MAXRSS);
12675 if (vmsin == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12676 vmsout = PerlMem_malloc(VMS_MAXRSS);
12677 if (vmsout == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12678 if (!spec_in || !*spec_in || !int_tovmsspec(spec_in, vmsin, 1, NULL) ||
12679 !spec_out || !*spec_out || !int_tovmsspec(spec_out, vmsout, 1, NULL)) {
12680 PerlMem_free(vmsin);
12681 PerlMem_free(vmsout);
12682 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12686 esa = PerlMem_malloc(VMS_MAXRSS);
12687 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12689 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
12690 esal = PerlMem_malloc(VMS_MAXRSS);
12691 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12693 fab_in = cc$rms_fab;
12694 rms_set_fna(fab_in, nam, vmsin, strlen(vmsin));
12695 fab_in.fab$b_shr = FAB$M_SHRPUT | FAB$M_UPI;
12696 fab_in.fab$b_fac = FAB$M_BIO | FAB$M_GET;
12697 fab_in.fab$l_fop = FAB$M_SQO;
12698 rms_bind_fab_nam(fab_in, nam);
12699 fab_in.fab$l_xab = (void *) &xabdat;
12701 rsa = PerlMem_malloc(VMS_MAXRSS);
12702 if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12704 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
12705 rsal = PerlMem_malloc(VMS_MAXRSS);
12706 if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12708 rms_set_rsal(nam, rsa, NAM$C_MAXRSS, rsal, (VMS_MAXRSS - 1));
12709 rms_set_esal(nam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1));
12710 rms_nam_esl(nam) = 0;
12711 rms_nam_rsl(nam) = 0;
12712 rms_nam_esll(nam) = 0;
12713 rms_nam_rsll(nam) = 0;
12714 #ifdef NAM$M_NO_SHORT_UPCASE
12715 if (decc_efs_case_preserve)
12716 rms_set_nam_nop(nam, NAM$M_NO_SHORT_UPCASE);
12719 xabdat = cc$rms_xabdat; /* To get creation date */
12720 xabdat.xab$l_nxt = (void *) &xabfhc;
12722 xabfhc = cc$rms_xabfhc; /* To get record length */
12723 xabfhc.xab$l_nxt = (void *) &xabsum;
12725 xabsum = cc$rms_xabsum; /* To get key and area information */
12727 if (!((sts = sys$open(&fab_in)) & 1)) {
12728 PerlMem_free(vmsin);
12729 PerlMem_free(vmsout);
12732 PerlMem_free(esal);
12735 PerlMem_free(rsal);
12736 set_vaxc_errno(sts);
12738 case RMS$_FNF: case RMS$_DNF:
12739 set_errno(ENOENT); break;
12741 set_errno(ENOTDIR); break;
12743 set_errno(ENODEV); break;
12745 set_errno(EINVAL); break;
12747 set_errno(EACCES); break;
12749 set_errno(EVMSERR);
12756 fab_out.fab$w_ifi = 0;
12757 fab_out.fab$b_fac = FAB$M_BIO | FAB$M_PUT;
12758 fab_out.fab$b_shr = FAB$M_SHRGET | FAB$M_UPI;
12759 fab_out.fab$l_fop = FAB$M_SQO;
12760 rms_bind_fab_nam(fab_out, nam_out);
12761 rms_set_fna(fab_out, nam_out, vmsout, strlen(vmsout));
12762 dna_len = rms_nam_namel(nam) ? rms_nam_name_type_l_size(nam) : 0;
12763 rms_set_dna(fab_out, nam_out, rms_nam_namel(nam), dna_len);
12764 esa_out = PerlMem_malloc(NAM$C_MAXRSS + 1);
12765 if (esa_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12766 rsa_out = PerlMem_malloc(NAM$C_MAXRSS + 1);
12767 if (rsa_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12770 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
12771 esal_out = PerlMem_malloc(VMS_MAXRSS);
12772 if (esal_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12773 rsal_out = PerlMem_malloc(VMS_MAXRSS);
12774 if (rsal_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12776 rms_set_rsal(nam_out, rsa_out, NAM$C_MAXRSS, rsal_out, (VMS_MAXRSS - 1));
12777 rms_set_esal(nam_out, esa_out, NAM$C_MAXRSS, esal_out, (VMS_MAXRSS - 1));
12779 if (preserve_dates == 0) { /* Act like DCL COPY */
12780 rms_set_nam_nop(nam_out, NAM$M_SYNCHK);
12781 fab_out.fab$l_xab = NULL; /* Don't disturb data from input file */
12782 if (!((sts = sys$parse(&fab_out)) & STS$K_SUCCESS)) {
12783 PerlMem_free(vmsin);
12784 PerlMem_free(vmsout);
12787 PerlMem_free(esal);
12790 PerlMem_free(rsal);
12791 PerlMem_free(esa_out);
12792 if (esal_out != NULL)
12793 PerlMem_free(esal_out);
12794 PerlMem_free(rsa_out);
12795 if (rsal_out != NULL)
12796 PerlMem_free(rsal_out);
12797 set_errno(sts == RMS$_SYN ? EINVAL : EVMSERR);
12798 set_vaxc_errno(sts);
12801 fab_out.fab$l_xab = (void *) &xabdat;
12802 if (rms_is_nam_fnb(nam, NAM$M_EXP_NAME | NAM$M_EXP_TYPE))
12803 preserve_dates = 1;
12805 if (preserve_dates < 0) /* Clear all bits; we'll use it as a */
12806 preserve_dates =0; /* bitmask from this point forward */
12808 if (!(preserve_dates & 1)) fab_out.fab$l_xab = (void *) &xabfhc;
12809 if (!((sts = sys$create(&fab_out)) & STS$K_SUCCESS)) {
12810 PerlMem_free(vmsin);
12811 PerlMem_free(vmsout);
12814 PerlMem_free(esal);
12817 PerlMem_free(rsal);
12818 PerlMem_free(esa_out);
12819 if (esal_out != NULL)
12820 PerlMem_free(esal_out);
12821 PerlMem_free(rsa_out);
12822 if (rsal_out != NULL)
12823 PerlMem_free(rsal_out);
12824 set_vaxc_errno(sts);
12827 set_errno(ENOENT); break;
12829 set_errno(ENOTDIR); break;
12831 set_errno(ENODEV); break;
12833 set_errno(EINVAL); break;
12835 set_errno(EACCES); break;
12837 set_errno(EVMSERR);
12841 fab_out.fab$l_fop |= FAB$M_DLT; /* in case we have to bail out */
12842 if (preserve_dates & 2) {
12843 /* sys$close() will process xabrdt, not xabdat */
12844 xabrdt = cc$rms_xabrdt;
12846 xabrdt.xab$q_rdt = xabdat.xab$q_rdt;
12848 /* gcc doesn't like the assignment, since its prototype for xab$q_rdt
12849 * is unsigned long[2], while DECC & VAXC use a struct */
12850 memcpy(xabrdt.xab$q_rdt,xabdat.xab$q_rdt,sizeof xabrdt.xab$q_rdt);
12852 fab_out.fab$l_xab = (void *) &xabrdt;
12855 ubf = PerlMem_malloc(32256);
12856 if (ubf == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12857 rab_in = cc$rms_rab;
12858 rab_in.rab$l_fab = &fab_in;
12859 rab_in.rab$l_rop = RAB$M_BIO;
12860 rab_in.rab$l_ubf = ubf;
12861 rab_in.rab$w_usz = 32256;
12862 if (!((sts = sys$connect(&rab_in)) & 1)) {
12863 sys$close(&fab_in); sys$close(&fab_out);
12864 PerlMem_free(vmsin);
12865 PerlMem_free(vmsout);
12869 PerlMem_free(esal);
12872 PerlMem_free(rsal);
12873 PerlMem_free(esa_out);
12874 if (esal_out != NULL)
12875 PerlMem_free(esal_out);
12876 PerlMem_free(rsa_out);
12877 if (rsal_out != NULL)
12878 PerlMem_free(rsal_out);
12879 set_errno(EVMSERR); set_vaxc_errno(sts);
12883 rab_out = cc$rms_rab;
12884 rab_out.rab$l_fab = &fab_out;
12885 rab_out.rab$l_rbf = ubf;
12886 if (!((sts = sys$connect(&rab_out)) & 1)) {
12887 sys$close(&fab_in); sys$close(&fab_out);
12888 PerlMem_free(vmsin);
12889 PerlMem_free(vmsout);
12893 PerlMem_free(esal);
12896 PerlMem_free(rsal);
12897 PerlMem_free(esa_out);
12898 if (esal_out != NULL)
12899 PerlMem_free(esal_out);
12900 PerlMem_free(rsa_out);
12901 if (rsal_out != NULL)
12902 PerlMem_free(rsal_out);
12903 set_errno(EVMSERR); set_vaxc_errno(sts);
12907 while ((sts = sys$read(&rab_in))) { /* always true */
12908 if (sts == RMS$_EOF) break;
12909 rab_out.rab$w_rsz = rab_in.rab$w_rsz;
12910 if (!(sts & 1) || !((sts = sys$write(&rab_out)) & 1)) {
12911 sys$close(&fab_in); sys$close(&fab_out);
12912 PerlMem_free(vmsin);
12913 PerlMem_free(vmsout);
12917 PerlMem_free(esal);
12920 PerlMem_free(rsal);
12921 PerlMem_free(esa_out);
12922 if (esal_out != NULL)
12923 PerlMem_free(esal_out);
12924 PerlMem_free(rsa_out);
12925 if (rsal_out != NULL)
12926 PerlMem_free(rsal_out);
12927 set_errno(EVMSERR); set_vaxc_errno(sts);
12933 fab_out.fab$l_fop &= ~FAB$M_DLT; /* We got this far; keep the output */
12934 sys$close(&fab_in); sys$close(&fab_out);
12935 sts = (fab_in.fab$l_sts & 1) ? fab_out.fab$l_sts : fab_in.fab$l_sts;
12937 PerlMem_free(vmsin);
12938 PerlMem_free(vmsout);
12942 PerlMem_free(esal);
12945 PerlMem_free(rsal);
12946 PerlMem_free(esa_out);
12947 if (esal_out != NULL)
12948 PerlMem_free(esal_out);
12949 PerlMem_free(rsa_out);
12950 if (rsal_out != NULL)
12951 PerlMem_free(rsal_out);
12954 set_errno(EVMSERR); set_vaxc_errno(sts);
12960 } /* end of rmscopy() */
12964 /*** The following glue provides 'hooks' to make some of the routines
12965 * from this file available from Perl. These routines are sufficiently
12966 * basic, and are required sufficiently early in the build process,
12967 * that's it's nice to have them available to miniperl as well as the
12968 * full Perl, so they're set up here instead of in an extension. The
12969 * Perl code which handles importation of these names into a given
12970 * package lives in [.VMS]Filespec.pm in @INC.
12974 rmsexpand_fromperl(pTHX_ CV *cv)
12977 char *fspec, *defspec = NULL, *rslt;
12979 int fs_utf8, dfs_utf8;
12983 if (!items || items > 2)
12984 Perl_croak(aTHX_ "Usage: VMS::Filespec::rmsexpand(spec[,defspec])");
12985 fspec = SvPV(ST(0),n_a);
12986 fs_utf8 = SvUTF8(ST(0));
12987 if (!fspec || !*fspec) XSRETURN_UNDEF;
12989 defspec = SvPV(ST(1),n_a);
12990 dfs_utf8 = SvUTF8(ST(1));
12992 rslt = do_rmsexpand(fspec,NULL,1,defspec,0,&fs_utf8,&dfs_utf8);
12993 ST(0) = sv_newmortal();
12994 if (rslt != NULL) {
12995 sv_usepvn(ST(0),rslt,strlen(rslt));
13004 vmsify_fromperl(pTHX_ CV *cv)
13011 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsify(spec)");
13012 utf8_fl = SvUTF8(ST(0));
13013 vmsified = do_tovmsspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
13014 ST(0) = sv_newmortal();
13015 if (vmsified != NULL) {
13016 sv_usepvn(ST(0),vmsified,strlen(vmsified));
13025 unixify_fromperl(pTHX_ CV *cv)
13032 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixify(spec)");
13033 utf8_fl = SvUTF8(ST(0));
13034 unixified = do_tounixspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
13035 ST(0) = sv_newmortal();
13036 if (unixified != NULL) {
13037 sv_usepvn(ST(0),unixified,strlen(unixified));
13046 fileify_fromperl(pTHX_ CV *cv)
13053 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::fileify(spec)");
13054 utf8_fl = SvUTF8(ST(0));
13055 fileified = do_fileify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
13056 ST(0) = sv_newmortal();
13057 if (fileified != NULL) {
13058 sv_usepvn(ST(0),fileified,strlen(fileified));
13067 pathify_fromperl(pTHX_ CV *cv)
13074 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::pathify(spec)");
13075 utf8_fl = SvUTF8(ST(0));
13076 pathified = do_pathify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
13077 ST(0) = sv_newmortal();
13078 if (pathified != NULL) {
13079 sv_usepvn(ST(0),pathified,strlen(pathified));
13088 vmspath_fromperl(pTHX_ CV *cv)
13095 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmspath(spec)");
13096 utf8_fl = SvUTF8(ST(0));
13097 vmspath = do_tovmspath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
13098 ST(0) = sv_newmortal();
13099 if (vmspath != NULL) {
13100 sv_usepvn(ST(0),vmspath,strlen(vmspath));
13109 unixpath_fromperl(pTHX_ CV *cv)
13116 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixpath(spec)");
13117 utf8_fl = SvUTF8(ST(0));
13118 unixpath = do_tounixpath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
13119 ST(0) = sv_newmortal();
13120 if (unixpath != NULL) {
13121 sv_usepvn(ST(0),unixpath,strlen(unixpath));
13130 candelete_fromperl(pTHX_ CV *cv)
13138 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::candelete(spec)");
13140 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
13141 Newx(fspec, VMS_MAXRSS, char);
13142 if (fspec == NULL) _ckvmssts(SS$_INSFMEM);
13143 if (SvTYPE(mysv) == SVt_PVGV) {
13144 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),fspec)) {
13145 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13153 if (mysv != ST(0) || !(fsp = SvPV(mysv,n_a)) || !*fsp) {
13154 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13161 ST(0) = boolSV(cando_by_name(S_IDUSR,0,fsp));
13167 rmscopy_fromperl(pTHX_ CV *cv)
13170 char *inspec, *outspec, *inp, *outp;
13172 struct dsc$descriptor indsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
13173 outdsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13174 unsigned long int sts;
13179 if (items < 2 || items > 3)
13180 Perl_croak(aTHX_ "Usage: File::Copy::rmscopy(from,to[,date_flag])");
13182 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
13183 Newx(inspec, VMS_MAXRSS, char);
13184 if (SvTYPE(mysv) == SVt_PVGV) {
13185 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),inspec)) {
13186 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13194 if (mysv != ST(0) || !(inp = SvPV(mysv,n_a)) || !*inp) {
13195 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13201 mysv = SvROK(ST(1)) ? SvRV(ST(1)) : ST(1);
13202 Newx(outspec, VMS_MAXRSS, char);
13203 if (SvTYPE(mysv) == SVt_PVGV) {
13204 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),outspec)) {
13205 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13214 if (mysv != ST(1) || !(outp = SvPV(mysv,n_a)) || !*outp) {
13215 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13222 date_flag = (items == 3) ? SvIV(ST(2)) : 0;
13224 ST(0) = boolSV(rmscopy(inp,outp,date_flag));
13230 /* The mod2fname is limited to shorter filenames by design, so it should
13231 * not be modified to support longer EFS pathnames
13234 mod2fname(pTHX_ CV *cv)
13237 char ultimate_name[NAM$C_MAXRSS+1], work_name[NAM$C_MAXRSS*8 + 1],
13238 workbuff[NAM$C_MAXRSS*1 + 1];
13239 int total_namelen = 3, counter, num_entries;
13240 /* ODS-5 ups this, but we want to be consistent, so... */
13241 int max_name_len = 39;
13242 AV *in_array = (AV *)SvRV(ST(0));
13244 num_entries = av_len(in_array);
13246 /* All the names start with PL_. */
13247 strcpy(ultimate_name, "PL_");
13249 /* Clean up our working buffer */
13250 Zero(work_name, sizeof(work_name), char);
13252 /* Run through the entries and build up a working name */
13253 for(counter = 0; counter <= num_entries; counter++) {
13254 /* If it's not the first name then tack on a __ */
13256 strcat(work_name, "__");
13258 strcat(work_name, SvPV_nolen(*av_fetch(in_array, counter, FALSE)));
13261 /* Check to see if we actually have to bother...*/
13262 if (strlen(work_name) + 3 <= max_name_len) {
13263 strcat(ultimate_name, work_name);
13265 /* It's too darned big, so we need to go strip. We use the same */
13266 /* algorithm as xsubpp does. First, strip out doubled __ */
13267 char *source, *dest, last;
13270 for (source = work_name; *source; source++) {
13271 if (last == *source && last == '_') {
13277 /* Go put it back */
13278 strcpy(work_name, workbuff);
13279 /* Is it still too big? */
13280 if (strlen(work_name) + 3 > max_name_len) {
13281 /* Strip duplicate letters */
13284 for (source = work_name; *source; source++) {
13285 if (last == toupper(*source)) {
13289 last = toupper(*source);
13291 strcpy(work_name, workbuff);
13294 /* Is it *still* too big? */
13295 if (strlen(work_name) + 3 > max_name_len) {
13296 /* Too bad, we truncate */
13297 work_name[max_name_len - 2] = 0;
13299 strcat(ultimate_name, work_name);
13302 /* Okay, return it */
13303 ST(0) = sv_2mortal(newSVpv(ultimate_name, 0));
13308 hushexit_fromperl(pTHX_ CV *cv)
13313 VMSISH_HUSHED = SvTRUE(ST(0));
13315 ST(0) = boolSV(VMSISH_HUSHED);
13321 Perl_vms_start_glob
13322 (pTHX_ SV *tmpglob,
13326 struct vs_str_st *rslt;
13330 $DESCRIPTOR(dfltdsc,"SYS$DISK:[]*.*;");
13333 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13334 struct dsc$descriptor_vs rsdsc;
13335 unsigned long int cxt = 0, sts = 0, ok = 1, hasdir = 0;
13336 unsigned long hasver = 0, isunix = 0;
13337 unsigned long int lff_flags = 0;
13340 if (!SvOK(tmpglob)) {
13341 SETERRNO(ENOENT,RMS$_FNF);
13345 #ifdef VMS_LONGNAME_SUPPORT
13346 lff_flags = LIB$M_FIL_LONG_NAMES;
13348 /* The Newx macro will not allow me to assign a smaller array
13349 * to the rslt pointer, so we will assign it to the begin char pointer
13350 * and then copy the value into the rslt pointer.
13352 Newx(begin, VMS_MAXRSS + sizeof(unsigned short int), char);
13353 rslt = (struct vs_str_st *)begin;
13355 rstr = &rslt->str[0];
13356 rsdsc.dsc$a_pointer = (char *) rslt; /* cast required */
13357 rsdsc.dsc$w_maxstrlen = VMS_MAXRSS + sizeof(unsigned short int);
13358 rsdsc.dsc$b_dtype = DSC$K_DTYPE_VT;
13359 rsdsc.dsc$b_class = DSC$K_CLASS_VS;
13361 Newx(vmsspec, VMS_MAXRSS, char);
13363 /* We could find out if there's an explicit dev/dir or version
13364 by peeking into lib$find_file's internal context at
13365 ((struct NAM *)((struct FAB *)cxt)->fab$l_nam)->nam$l_fnb
13366 but that's unsupported, so I don't want to do it now and
13367 have it bite someone in the future. */
13368 /* Fix-me: vms_split_path() is the only way to do this, the
13369 existing method will fail with many legal EFS or UNIX specifications
13372 cp = SvPV(tmpglob,i);
13375 if (cp[i] == ';') hasver = 1;
13376 if (cp[i] == '.') {
13377 if (sts) hasver = 1;
13380 if (cp[i] == '/') {
13381 hasdir = isunix = 1;
13384 if (cp[i] == ']' || cp[i] == '>' || cp[i] == ':') {
13389 if ((tmpfp = PerlIO_tmpfile()) != NULL) {
13393 stat_sts = PerlLIO_stat(SvPVX_const(tmpglob),&st);
13394 if (!stat_sts && S_ISDIR(st.st_mode)) {
13395 wilddsc.dsc$a_pointer = tovmspath_utf8(SvPVX(tmpglob),vmsspec,NULL);
13396 ok = (wilddsc.dsc$a_pointer != NULL);
13397 /* maybe passed 'foo' rather than '[.foo]', thus not detected above */
13401 wilddsc.dsc$a_pointer = tovmsspec_utf8(SvPVX(tmpglob),vmsspec,NULL);
13402 ok = (wilddsc.dsc$a_pointer != NULL);
13405 wilddsc.dsc$w_length = strlen(wilddsc.dsc$a_pointer);
13407 /* If not extended character set, replace ? with % */
13408 /* With extended character set, ? is a wildcard single character */
13409 if (!decc_efs_case_preserve) {
13410 for (cp=wilddsc.dsc$a_pointer; ok && cp && *cp; cp++)
13411 if (*cp == '?') *cp = '%';
13414 while (ok && $VMS_STATUS_SUCCESS(sts)) {
13415 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
13416 int v_sts, v_len, r_len, d_len, n_len, e_len, vs_len;
13418 sts = lib$find_file(&wilddsc,&rsdsc,&cxt,
13419 &dfltdsc,NULL,&rms_sts,&lff_flags);
13420 if (!$VMS_STATUS_SUCCESS(sts))
13425 /* with varying string, 1st word of buffer contains result length */
13426 rstr[rslt->length] = '\0';
13428 /* Find where all the components are */
13429 v_sts = vms_split_path
13444 /* If no version on input, truncate the version on output */
13445 if (!hasver && (vs_len > 0)) {
13449 /* No version & a null extension on UNIX handling */
13450 if (isunix && (e_len == 1) && decc_readdir_dropdotnotype) {
13456 if (!decc_efs_case_preserve) {
13457 for (cp = rstr; *cp; cp++) *cp = _tolower(*cp);
13461 if (isunix) trim_unixpath(rstr,SvPVX(tmpglob),1);
13465 /* Start with the name */
13468 strcat(begin,"\n");
13469 ok = (PerlIO_puts(tmpfp,begin) != EOF);
13471 if (cxt) (void)lib$find_file_end(&cxt);
13474 /* Be POSIXish: return the input pattern when no matches */
13475 strcpy(rstr,SvPVX(tmpglob));
13477 ok = (PerlIO_puts(tmpfp,rstr) != EOF);
13480 if (ok && sts != RMS$_NMF &&
13481 sts != RMS$_DNF && sts != RMS_FNF) ok = 0;
13484 SETERRNO((sts == RMS$_SYN ? EINVAL : EVMSERR),sts);
13486 PerlIO_close(tmpfp);
13490 PerlIO_rewind(tmpfp);
13491 IoTYPE(io) = IoTYPE_RDONLY;
13492 IoIFP(io) = fp = tmpfp;
13493 IoFLAGS(io) &= ~IOf_UNTAINT; /* maybe redundant */
13503 mp_do_vms_realpath(pTHX_ const char *filespec, char * rslt_spec,
13507 unixrealpath_fromperl(pTHX_ CV *cv)
13510 char *fspec, *rslt_spec, *rslt;
13513 if (!items || items != 1)
13514 Perl_croak(aTHX_ "Usage: VMS::Filespec::unixrealpath(spec)");
13516 fspec = SvPV(ST(0),n_a);
13517 if (!fspec || !*fspec) XSRETURN_UNDEF;
13519 Newx(rslt_spec, VMS_MAXRSS + 1, char);
13520 rslt = do_vms_realpath(fspec, rslt_spec, NULL);
13522 ST(0) = sv_newmortal();
13524 sv_usepvn(ST(0),rslt,strlen(rslt));
13526 Safefree(rslt_spec);
13531 mp_do_vms_realname(pTHX_ const char *filespec, char * rslt_spec,
13535 vmsrealpath_fromperl(pTHX_ CV *cv)
13538 char *fspec, *rslt_spec, *rslt;
13541 if (!items || items != 1)
13542 Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsrealpath(spec)");
13544 fspec = SvPV(ST(0),n_a);
13545 if (!fspec || !*fspec) XSRETURN_UNDEF;
13547 Newx(rslt_spec, VMS_MAXRSS + 1, char);
13548 rslt = do_vms_realname(fspec, rslt_spec, NULL);
13550 ST(0) = sv_newmortal();
13552 sv_usepvn(ST(0),rslt,strlen(rslt));
13554 Safefree(rslt_spec);
13560 * A thin wrapper around decc$symlink to make sure we follow the
13561 * standard and do not create a symlink with a zero-length name.
13563 * Also in ODS-2 mode, existing tests assume that the link target
13564 * will be converted to UNIX format.
13566 /*{{{ int my_symlink(pTHX_ const char *contents, const char *link_name)*/
13567 int Perl_my_symlink(pTHX_ const char *contents, const char *link_name) {
13568 if (!link_name || !*link_name) {
13569 SETERRNO(ENOENT, SS$_NOSUCHFILE);
13573 if (decc_efs_charset) {
13574 return symlink(contents, link_name);
13579 /* Unless we are in ODS-5 mode, convert the symlink target to UNIX */
13580 /* because in order to work, the symlink target must be in UNIX format */
13582 /* As symbolic links can hold things other than files, we will only do */
13583 /* the conversion in in ODS-2 mode */
13585 Newx(utarget, VMS_MAXRSS + 1, char);
13586 if (int_tounixspec(contents, utarget, NULL) == NULL) {
13588 /* This should not fail, as an untranslatable filename */
13589 /* should be passed through */
13590 utarget = (char *)contents;
13592 sts = symlink(utarget, link_name);
13600 #endif /* HAS_SYMLINK */
13602 int do_vms_case_tolerant(void);
13605 case_tolerant_process_fromperl(pTHX_ CV *cv)
13608 ST(0) = boolSV(do_vms_case_tolerant());
13612 #ifdef USE_ITHREADS
13615 Perl_sys_intern_dup(pTHX_ struct interp_intern *src,
13616 struct interp_intern *dst)
13618 PERL_ARGS_ASSERT_SYS_INTERN_DUP;
13620 memcpy(dst,src,sizeof(struct interp_intern));
13626 Perl_sys_intern_clear(pTHX)
13631 Perl_sys_intern_init(pTHX)
13633 unsigned int ix = RAND_MAX;
13638 MY_POSIX_EXIT = vms_posix_exit;
13641 MY_INV_RAND_MAX = 1./x;
13645 init_os_extras(void)
13648 char* file = __FILE__;
13649 if (decc_disable_to_vms_logname_translation) {
13650 no_translate_barewords = TRUE;
13652 no_translate_barewords = FALSE;
13655 newXSproto("VMS::Filespec::rmsexpand",rmsexpand_fromperl,file,"$;$");
13656 newXSproto("VMS::Filespec::vmsify",vmsify_fromperl,file,"$");
13657 newXSproto("VMS::Filespec::unixify",unixify_fromperl,file,"$");
13658 newXSproto("VMS::Filespec::pathify",pathify_fromperl,file,"$");
13659 newXSproto("VMS::Filespec::fileify",fileify_fromperl,file,"$");
13660 newXSproto("VMS::Filespec::vmspath",vmspath_fromperl,file,"$");
13661 newXSproto("VMS::Filespec::unixpath",unixpath_fromperl,file,"$");
13662 newXSproto("VMS::Filespec::candelete",candelete_fromperl,file,"$");
13663 newXSproto("DynaLoader::mod2fname", mod2fname, file, "$");
13664 newXS("File::Copy::rmscopy",rmscopy_fromperl,file);
13665 newXSproto("vmsish::hushed",hushexit_fromperl,file,";$");
13666 newXSproto("VMS::Filespec::unixrealpath",unixrealpath_fromperl,file,"$;$");
13667 newXSproto("VMS::Filespec::vmsrealpath",vmsrealpath_fromperl,file,"$;$");
13668 newXSproto("VMS::Filespec::case_tolerant_process",
13669 case_tolerant_process_fromperl,file,"");
13671 store_pipelocs(aTHX); /* will redo any earlier attempts */
13676 #if __CRTL_VER == 80200000
13677 /* This missed getting in to the DECC SDK for 8.2 */
13678 char *realpath(const char *file_name, char * resolved_name, ...);
13681 /*{{{char *do_vms_realpath(const char *file_name, char *resolved_name)*/
13682 /* wrapper for the realpath() function added with 8.2 RMS SYMLINK SDK.
13683 * The perl fallback routine to provide realpath() is not as efficient
13687 /* Hack, use old stat() as fastest way of getting ino_t and device */
13688 int decc$stat(const char *name, void * statbuf);
13691 /* Realpath is fragile. In 8.3 it does not work if the feature
13692 * DECC$POSIX_COMPLIANT_PATHNAMES is not enabled, even though symbolic
13693 * links are implemented in RMS, not the CRTL. It also can fail if the
13694 * user does not have read/execute access to some of the directories.
13695 * So in order for Do What I Mean mode to work, if realpath() fails,
13696 * fall back to looking up the filename by the device name and FID.
13699 int vms_fid_to_name(char * outname, int outlen, const char * name)
13703 unsigned short st_ino[3];
13704 unsigned short padw;
13705 unsigned long padl[30]; /* plenty of room */
13708 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13709 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13711 sts = decc$stat(name, &statbuf);
13714 dvidsc.dsc$a_pointer=statbuf.st_dev;
13715 dvidsc.dsc$w_length=strlen(statbuf.st_dev);
13717 specdsc.dsc$a_pointer = outname;
13718 specdsc.dsc$w_length = outlen-1;
13720 sts = lib$fid_to_name
13721 (&dvidsc, statbuf.st_ino, &specdsc, &specdsc.dsc$w_length);
13722 if ($VMS_STATUS_SUCCESS(sts)) {
13723 outname[specdsc.dsc$w_length] = 0;
13733 mp_do_vms_realpath(pTHX_ const char *filespec, char *outbuf,
13736 char * rslt = NULL;
13739 if (decc_posix_compliant_pathnames > 0 ) {
13740 /* realpath currently only works if posix compliant pathnames are
13741 * enabled. It may start working when they are not, but in that
13742 * case we still want the fallback behavior for backwards compatibility
13744 rslt = realpath(filespec, outbuf);
13748 if (rslt == NULL) {
13750 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
13751 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
13754 /* Fall back to fid_to_name */
13756 Newx(vms_spec, VMS_MAXRSS + 1, char);
13758 sts = vms_fid_to_name(vms_spec, VMS_MAXRSS + 1, filespec);
13762 /* Now need to trim the version off */
13763 sts = vms_split_path
13783 /* Trim off the version */
13784 int file_len = v_len + r_len + d_len + n_len + e_len;
13785 vms_spec[file_len] = 0;
13787 /* The result is expected to be in UNIX format */
13788 rslt = int_tounixspec(vms_spec, outbuf, utf8_fl);
13790 /* Downcase if input had any lower case letters and
13791 * case preservation is not in effect.
13793 if (!decc_efs_case_preserve) {
13794 for (cp = filespec; *cp; cp++)
13795 if (islower(*cp)) { haslower = 1; break; }
13797 if (haslower) __mystrtolower(rslt);
13802 /* Now for some hacks to deal with backwards and forward */
13804 if (!decc_efs_charset) {
13806 /* 1. ODS-2 mode wants to do a syntax only translation */
13807 rslt = int_rmsexpand(filespec, outbuf,
13808 NULL, 0, NULL, utf8_fl);
13811 if (decc_filename_unix_report) {
13813 char * vms_dir_name;
13816 /* 2. ODS-5 / UNIX report mode should return a failure */
13817 /* if the parent directory also does not exist */
13818 /* Otherwise, get the real path for the parent */
13819 /* and add the child to it.
13821 /* basename / dirname only available for VMS 7.0+ */
13822 /* So we may need to implement them as common routines */
13824 Newx(dir_name, VMS_MAXRSS + 1, char);
13825 Newx(vms_dir_name, VMS_MAXRSS + 1, char);
13826 dir_name[0] = '\0';
13829 /* First try a VMS parse */
13830 sts = vms_split_path
13848 int dir_len = v_len + r_len + d_len + n_len;
13850 strncpy(dir_name, filespec, dir_len);
13851 dir_name[dir_len] = '\0';
13852 file_name = (char *)&filespec[dir_len + 1];
13855 /* This must be UNIX */
13858 tchar = strrchr(filespec, '/');
13860 if (tchar != NULL) {
13861 int dir_len = tchar - filespec;
13862 strncpy(dir_name, filespec, dir_len);
13863 dir_name[dir_len] = '\0';
13864 file_name = (char *) &filespec[dir_len + 1];
13868 /* Dir name is defaulted */
13869 if (dir_name[0] == 0) {
13871 dir_name[1] = '\0';
13874 /* Need realpath for the directory */
13875 sts = vms_fid_to_name(vms_dir_name,
13880 /* Now need to pathify it.
13881 char *tdir = do_pathify_dirspec(vms_dir_name,
13884 /* And now add the original filespec to it */
13885 if (file_name != NULL) {
13886 strcat(outbuf, file_name);
13890 Safefree(vms_dir_name);
13891 Safefree(dir_name);
13895 Safefree(vms_spec);
13901 mp_do_vms_realname(pTHX_ const char *filespec, char *outbuf,
13904 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
13905 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
13908 /* Fall back to fid_to_name */
13910 sts = vms_fid_to_name(outbuf, VMS_MAXRSS + 1, filespec);
13917 /* Now need to trim the version off */
13918 sts = vms_split_path
13938 /* Trim off the version */
13939 int file_len = v_len + r_len + d_len + n_len + e_len;
13940 outbuf[file_len] = 0;
13942 /* Downcase if input had any lower case letters and
13943 * case preservation is not in effect.
13945 if (!decc_efs_case_preserve) {
13946 for (cp = filespec; *cp; cp++)
13947 if (islower(*cp)) { haslower = 1; break; }
13949 if (haslower) __mystrtolower(outbuf);
13958 /* External entry points */
13959 char *Perl_vms_realpath(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
13960 { return do_vms_realpath(filespec, outbuf, utf8_fl); }
13962 char *Perl_vms_realname(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
13963 { return do_vms_realname(filespec, outbuf, utf8_fl); }
13965 /* case_tolerant */
13967 /*{{{int do_vms_case_tolerant(void)*/
13968 /* OpenVMS provides a case sensitive implementation of ODS-5 and this is
13969 * controlled by a process setting.
13971 int do_vms_case_tolerant(void)
13973 return vms_process_case_tolerant;
13976 /* External entry points */
13977 #if __CRTL_VER >= 70301000 && !defined(__VAX)
13978 int Perl_vms_case_tolerant(void)
13979 { return do_vms_case_tolerant(); }
13981 int Perl_vms_case_tolerant(void)
13982 { return vms_process_case_tolerant; }
13986 /* Start of DECC RTL Feature handling */
13988 static int sys_trnlnm
13989 (const char * logname,
13993 const $DESCRIPTOR(table_dsc, "LNM$FILE_DEV");
13994 const unsigned long attr = LNM$M_CASE_BLIND;
13995 struct dsc$descriptor_s name_dsc;
13997 unsigned short result;
13998 struct itmlst_3 itlst[2] = {{value_len, LNM$_STRING, value, &result},
14001 name_dsc.dsc$w_length = strlen(logname);
14002 name_dsc.dsc$a_pointer = (char *)logname;
14003 name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
14004 name_dsc.dsc$b_class = DSC$K_CLASS_S;
14006 status = sys$trnlnm(&attr, &table_dsc, &name_dsc, 0, itlst);
14008 if ($VMS_STATUS_SUCCESS(status)) {
14010 /* Null terminate and return the string */
14011 /*--------------------------------------*/
14018 static int sys_crelnm
14019 (const char * logname,
14020 const char * value)
14023 const char * proc_table = "LNM$PROCESS_TABLE";
14024 struct dsc$descriptor_s proc_table_dsc;
14025 struct dsc$descriptor_s logname_dsc;
14026 struct itmlst_3 item_list[2];
14028 proc_table_dsc.dsc$a_pointer = (char *) proc_table;
14029 proc_table_dsc.dsc$w_length = strlen(proc_table);
14030 proc_table_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
14031 proc_table_dsc.dsc$b_class = DSC$K_CLASS_S;
14033 logname_dsc.dsc$a_pointer = (char *) logname;
14034 logname_dsc.dsc$w_length = strlen(logname);
14035 logname_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
14036 logname_dsc.dsc$b_class = DSC$K_CLASS_S;
14038 item_list[0].buflen = strlen(value);
14039 item_list[0].itmcode = LNM$_STRING;
14040 item_list[0].bufadr = (char *)value;
14041 item_list[0].retlen = NULL;
14043 item_list[1].buflen = 0;
14044 item_list[1].itmcode = 0;
14046 ret_val = sys$crelnm
14048 (const struct dsc$descriptor_s *)&proc_table_dsc,
14049 (const struct dsc$descriptor_s *)&logname_dsc,
14051 (const struct item_list_3 *) item_list);
14056 /* C RTL Feature settings */
14058 static int set_features
14059 (int (* init_coroutine)(int *, int *, void *), /* Needs casts if used */
14060 int (* cli_routine)(void), /* Not documented */
14061 void *image_info) /* Not documented */
14067 #if defined(JPI$_CASE_LOOKUP_PERM) && !defined(__VAX)
14068 const unsigned long int jpicode1 = JPI$_CASE_LOOKUP_PERM;
14069 const unsigned long int jpicode2 = JPI$_CASE_LOOKUP_IMAGE;
14070 unsigned long case_perm;
14071 unsigned long case_image;
14074 /* Allow an exception to bring Perl into the VMS debugger */
14075 vms_debug_on_exception = 0;
14076 status = sys_trnlnm("PERL_VMS_EXCEPTION_DEBUG", val_str, sizeof(val_str));
14077 if ($VMS_STATUS_SUCCESS(status)) {
14078 val_str[0] = _toupper(val_str[0]);
14079 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14080 vms_debug_on_exception = 1;
14082 vms_debug_on_exception = 0;
14085 /* Debug unix/vms file translation routines */
14086 vms_debug_fileify = 0;
14087 status = sys_trnlnm("PERL_VMS_FILEIFY_DEBUG", val_str, sizeof(val_str));
14088 if ($VMS_STATUS_SUCCESS(status)) {
14089 val_str[0] = _toupper(val_str[0]);
14090 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14091 vms_debug_fileify = 1;
14093 vms_debug_fileify = 0;
14097 /* Historically PERL has been doing vmsify / stat differently than */
14098 /* the CRTL. In particular, under some conditions the CRTL will */
14099 /* remove some illegal characters like spaces from filenames */
14100 /* resulting in some differences. The stat()/lstat() wrapper has */
14101 /* been reporting such file names as invalid and fails to stat them */
14102 /* fixing this bug so that stat()/lstat() accept these like the */
14103 /* CRTL does will result in several tests failing. */
14104 /* This should really be fixed, but for now, set up a feature to */
14105 /* enable it so that the impact can be studied. */
14106 vms_bug_stat_filename = 0;
14107 status = sys_trnlnm("PERL_VMS_BUG_STAT_FILENAME", val_str, sizeof(val_str));
14108 if ($VMS_STATUS_SUCCESS(status)) {
14109 val_str[0] = _toupper(val_str[0]);
14110 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14111 vms_bug_stat_filename = 1;
14113 vms_bug_stat_filename = 0;
14117 /* Create VTF-7 filenames from Unicode instead of UTF-8 */
14118 vms_vtf7_filenames = 0;
14119 status = sys_trnlnm("PERL_VMS_VTF7_FILENAMES", val_str, sizeof(val_str));
14120 if ($VMS_STATUS_SUCCESS(status)) {
14121 val_str[0] = _toupper(val_str[0]);
14122 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14123 vms_vtf7_filenames = 1;
14125 vms_vtf7_filenames = 0;
14128 /* unlink all versions on unlink() or rename() */
14129 vms_unlink_all_versions = 0;
14130 status = sys_trnlnm
14131 ("PERL_VMS_UNLINK_ALL_VERSIONS", val_str, sizeof(val_str));
14132 if ($VMS_STATUS_SUCCESS(status)) {
14133 val_str[0] = _toupper(val_str[0]);
14134 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14135 vms_unlink_all_versions = 1;
14137 vms_unlink_all_versions = 0;
14140 /* Dectect running under GNV Bash or other UNIX like shell */
14141 #if __CRTL_VER >= 70300000 && !defined(__VAX)
14142 gnv_unix_shell = 0;
14143 status = sys_trnlnm("GNV$UNIX_SHELL", val_str, sizeof(val_str));
14144 if ($VMS_STATUS_SUCCESS(status)) {
14145 gnv_unix_shell = 1;
14146 set_feature_default("DECC$EFS_CASE_PRESERVE", 1);
14147 set_feature_default("DECC$EFS_CHARSET", 1);
14148 set_feature_default("DECC$FILENAME_UNIX_NO_VERSION", 1);
14149 set_feature_default("DECC$FILENAME_UNIX_REPORT", 1);
14150 set_feature_default("DECC$READDIR_DROPDOTNOTYPE", 1);
14151 set_feature_default("DECC$DISABLE_POSIX_ROOT", 0);
14152 vms_unlink_all_versions = 1;
14153 vms_posix_exit = 1;
14157 /* hacks to see if known bugs are still present for testing */
14159 /* PCP mode requires creating /dev/null special device file */
14160 decc_bug_devnull = 0;
14161 status = sys_trnlnm("DECC_BUG_DEVNULL", val_str, sizeof(val_str));
14162 if ($VMS_STATUS_SUCCESS(status)) {
14163 val_str[0] = _toupper(val_str[0]);
14164 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14165 decc_bug_devnull = 1;
14167 decc_bug_devnull = 0;
14170 /* UNIX directory names with no paths are broken in a lot of places */
14171 decc_dir_barename = 1;
14172 status = sys_trnlnm("DECC_DIR_BARENAME", val_str, sizeof(val_str));
14173 if ($VMS_STATUS_SUCCESS(status)) {
14174 val_str[0] = _toupper(val_str[0]);
14175 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14176 decc_dir_barename = 1;
14178 decc_dir_barename = 0;
14181 #if __CRTL_VER >= 70300000 && !defined(__VAX)
14182 s = decc$feature_get_index("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION");
14184 decc_disable_to_vms_logname_translation = decc$feature_get_value(s, 1);
14185 if (decc_disable_to_vms_logname_translation < 0)
14186 decc_disable_to_vms_logname_translation = 0;
14189 s = decc$feature_get_index("DECC$EFS_CASE_PRESERVE");
14191 decc_efs_case_preserve = decc$feature_get_value(s, 1);
14192 if (decc_efs_case_preserve < 0)
14193 decc_efs_case_preserve = 0;
14196 s = decc$feature_get_index("DECC$EFS_CHARSET");
14197 decc_efs_charset_index = s;
14199 decc_efs_charset = decc$feature_get_value(s, 1);
14200 if (decc_efs_charset < 0)
14201 decc_efs_charset = 0;
14204 s = decc$feature_get_index("DECC$FILENAME_UNIX_REPORT");
14206 decc_filename_unix_report = decc$feature_get_value(s, 1);
14207 if (decc_filename_unix_report > 0) {
14208 decc_filename_unix_report = 1;
14209 vms_posix_exit = 1;
14212 decc_filename_unix_report = 0;
14215 s = decc$feature_get_index("DECC$FILENAME_UNIX_ONLY");
14217 decc_filename_unix_only = decc$feature_get_value(s, 1);
14218 if (decc_filename_unix_only > 0) {
14219 decc_filename_unix_only = 1;
14222 decc_filename_unix_only = 0;
14226 s = decc$feature_get_index("DECC$FILENAME_UNIX_NO_VERSION");
14228 decc_filename_unix_no_version = decc$feature_get_value(s, 1);
14229 if (decc_filename_unix_no_version < 0)
14230 decc_filename_unix_no_version = 0;
14233 s = decc$feature_get_index("DECC$READDIR_DROPDOTNOTYPE");
14235 decc_readdir_dropdotnotype = decc$feature_get_value(s, 1);
14236 if (decc_readdir_dropdotnotype < 0)
14237 decc_readdir_dropdotnotype = 0;
14240 #if __CRTL_VER >= 80200000
14241 s = decc$feature_get_index("DECC$POSIX_COMPLIANT_PATHNAMES");
14243 decc_posix_compliant_pathnames = decc$feature_get_value(s, 1);
14244 if (decc_posix_compliant_pathnames < 0)
14245 decc_posix_compliant_pathnames = 0;
14246 if (decc_posix_compliant_pathnames > 4)
14247 decc_posix_compliant_pathnames = 0;
14252 status = sys_trnlnm
14253 ("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION", val_str, sizeof(val_str));
14254 if ($VMS_STATUS_SUCCESS(status)) {
14255 val_str[0] = _toupper(val_str[0]);
14256 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14257 decc_disable_to_vms_logname_translation = 1;
14262 status = sys_trnlnm("DECC$EFS_CASE_PRESERVE", val_str, sizeof(val_str));
14263 if ($VMS_STATUS_SUCCESS(status)) {
14264 val_str[0] = _toupper(val_str[0]);
14265 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14266 decc_efs_case_preserve = 1;
14271 status = sys_trnlnm("DECC$FILENAME_UNIX_REPORT", val_str, sizeof(val_str));
14272 if ($VMS_STATUS_SUCCESS(status)) {
14273 val_str[0] = _toupper(val_str[0]);
14274 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14275 decc_filename_unix_report = 1;
14278 status = sys_trnlnm("DECC$FILENAME_UNIX_ONLY", val_str, sizeof(val_str));
14279 if ($VMS_STATUS_SUCCESS(status)) {
14280 val_str[0] = _toupper(val_str[0]);
14281 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14282 decc_filename_unix_only = 1;
14283 decc_filename_unix_report = 1;
14286 status = sys_trnlnm("DECC$FILENAME_UNIX_NO_VERSION", val_str, sizeof(val_str));
14287 if ($VMS_STATUS_SUCCESS(status)) {
14288 val_str[0] = _toupper(val_str[0]);
14289 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14290 decc_filename_unix_no_version = 1;
14293 status = sys_trnlnm("DECC$READDIR_DROPDOTNOTYPE", val_str, sizeof(val_str));
14294 if ($VMS_STATUS_SUCCESS(status)) {
14295 val_str[0] = _toupper(val_str[0]);
14296 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14297 decc_readdir_dropdotnotype = 1;
14302 #if defined(JPI$_CASE_LOOKUP_PERM) && defined(PPROP$K_CASE_BLIND) && !defined(__VAX)
14304 /* Report true case tolerance */
14305 /*----------------------------*/
14306 status = lib$getjpi(&jpicode1, 0, 0, &case_perm, 0, 0);
14307 if (!$VMS_STATUS_SUCCESS(status))
14308 case_perm = PPROP$K_CASE_BLIND;
14309 status = lib$getjpi(&jpicode2, 0, 0, &case_image, 0, 0);
14310 if (!$VMS_STATUS_SUCCESS(status))
14311 case_image = PPROP$K_CASE_BLIND;
14312 if ((case_perm == PPROP$K_CASE_SENSITIVE) ||
14313 (case_image == PPROP$K_CASE_SENSITIVE))
14314 vms_process_case_tolerant = 0;
14318 /* USE POSIX/DCL Exit codes - Recommended, but needs to default to */
14319 /* for strict backward compatibilty */
14320 status = sys_trnlnm
14321 ("PERL_VMS_POSIX_EXIT", val_str, sizeof(val_str));
14322 if ($VMS_STATUS_SUCCESS(status)) {
14323 val_str[0] = _toupper(val_str[0]);
14324 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14325 vms_posix_exit = 1;
14327 vms_posix_exit = 0;
14331 /* CRTL can be initialized past this point, but not before. */
14332 /* DECC$CRTL_INIT(); */
14339 #pragma extern_model save
14340 #pragma extern_model strict_refdef "LIB$INITIALIZ" nowrt
14341 const __align (LONGWORD) int spare[8] = {0};
14343 /* .psect LIB$INITIALIZE, NOPIC, USR, CON, REL, GBL, NOSHR, NOEXE, RD, NOWRT, LONG */
14344 #if __DECC_VER >= 60560002
14345 #pragma extern_model strict_refdef "LIB$INITIALIZE" nopic, con, rel, gbl, noshr, noexe, nowrt, long
14347 #pragma extern_model strict_refdef "LIB$INITIALIZE" nopic, con, gbl, noshr, nowrt, long
14349 #endif /* __DECC */
14351 const long vms_cc_features = (const long)set_features;
14354 ** Force a reference to LIB$INITIALIZE to ensure it
14355 ** exists in the image.
14357 int lib$initialize(void);
14359 #pragma extern_model strict_refdef
14361 int lib_init_ref = (int) lib$initialize;
14364 #pragma extern_model restore