X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=vms%2Fvms.c;h=40e80a2f7d2e313cdfc3d8714924933b054f8004;hb=53c40a8fd46e24a1d1e4bce188f973172eb1a279;hp=73f13f56da318eb2ae1a0a2eddeb7cb0c2fde260;hpb=f617045b1dc288a3a5c423f1bc2d20a0b872e39e;p=p5sagit%2Fp5-mst-13.2.git diff --git a/vms/vms.c b/vms/vms.c index 73f13f5..40e80a2 100644 --- a/vms/vms.c +++ b/vms/vms.c @@ -32,6 +32,7 @@ #include #include #include +#include #if __CRTL_VER >= 70301000 && !defined(__VAX) #include #endif @@ -91,6 +92,67 @@ int sys$getdviw void * nullarg); #endif +#ifdef sys$get_security +#undef sys$get_security +int sys$get_security + (const struct dsc$descriptor_s * clsnam, + const struct dsc$descriptor_s * objnam, + const unsigned int *objhan, + unsigned int flags, + const struct item_list_3 * itmlst, + unsigned int * contxt, + const unsigned int * acmode); +#endif + +#ifdef sys$set_security +#undef sys$set_security +int sys$set_security + (const struct dsc$descriptor_s * clsnam, + const struct dsc$descriptor_s * objnam, + const unsigned int *objhan, + unsigned int flags, + const struct item_list_3 * itmlst, + unsigned int * contxt, + const unsigned int * acmode); +#endif + +#ifdef lib$find_image_symbol +#undef lib$find_image_symbol +int lib$find_image_symbol + (const struct dsc$descriptor_s * imgname, + const struct dsc$descriptor_s * symname, + void * symval, + const struct dsc$descriptor_s * defspec, + unsigned long flag); +#endif + +#ifdef lib$rename_file +#undef lib$rename_file +int lib$rename_file + (const struct dsc$descriptor_s * old_file_dsc, + const struct dsc$descriptor_s * new_file_dsc, + const struct dsc$descriptor_s * default_file_dsc, + const struct dsc$descriptor_s * related_file_dsc, + const unsigned long * flags, + void * (success)(const struct dsc$descriptor_s * old_dsc, + const struct dsc$descriptor_s * new_dsc, + const void *), + void * (error)(const struct dsc$descriptor_s * old_dsc, + const struct dsc$descriptor_s * new_dsc, + const int * rms_sts, + const int * rms_stv, + const int * error_src, + const void * usr_arg), + int (confirm)(const struct dsc$descriptor_s * old_dsc, + const struct dsc$descriptor_s * new_dsc, + const void * old_fab, + const void * usr_arg), + void * user_arg, + struct dsc$descriptor_s * old_result_name_dsc, + struct dsc$descriptor_s * new_result_name_dsc, + unsigned long * file_scan_context); +#endif + #if __CRTL_VER >= 70300000 && !defined(__VAX) static int set_feature_default(const char *name, int value) @@ -144,12 +206,10 @@ return 0; # define RTL_USES_UTC 1 #endif -#ifdef USE_VMS_DECTERM - /* Routine to create a decterm for use with the Perl debugger */ /* No headers, this information was found in the Programming Concepts Manual */ -int decw$term_port +static int (*decw_term_port) (const struct dsc$descriptor_s * display, const struct dsc$descriptor_s * setup_file, const struct dsc$descriptor_s * customization, @@ -157,8 +217,7 @@ int decw$term_port unsigned short * result_device_name_length, void * controller, void * char_buffer, - void * char_change_buffer); -#endif + void * char_change_buffer) = 0; /* gcc's header files don't #define direct access macros * corresponding to VAXC's variant structs */ @@ -279,6 +338,7 @@ int decc_readdir_dropdotnotype = 0; static int vms_process_case_tolerant = 1; int vms_vtf7_filenames = 0; int gnv_unix_shell = 0; +static int vms_unlink_all_versions = 0; /* bug workarounds if needed */ int decc_bug_readdir_efs1 = 0; @@ -419,7 +479,7 @@ int utf8_flag; } } - /* High bit set, but not a unicode character! */ + /* High bit set, but not a Unicode character! */ /* Non printing DECMCS or ISO Latin-1 character? */ if (*inspec <= 0x9F) { @@ -521,6 +581,16 @@ int utf8_flag; case ']': case '%': case '^': + /* Don't escape again if following character is + * already something we escape. + */ + if (strchr(".~!#&\'`()+@{},;[]%^=_", *(inspec+1))) { + *outspec = *inspec; + *output_cnt = 1; + return 1; + break; + } + /* But otherwise fall through and escape it. */ case '=': /* Assume that this is to be escaped */ outspec[0] = '^'; @@ -564,17 +634,26 @@ int scnt; if (*inspec == '^') { inspec++; switch (*inspec) { + /* Spaces and non-trailing dots should just be passed through, + * but eat the escape character. + */ case '.': - /* Non trailing dots should just be passed through, but eat the escape */ *outspec = *inspec; - count++; + count += 2; + (*output_cnt)++; break; case '_': /* space */ *outspec = ' '; - inspec++; - count++; + count += 2; (*output_cnt)++; break; + case '^': + /* Hmm. Better leave the escape escaped. */ + outspec[0] = '^'; + outspec[1] = '^'; + count += 2; + (*output_cnt) += 2; + break; case 'U': /* Unicode - FIX-ME this is wrong. */ inspec++; count++; @@ -1729,6 +1808,10 @@ static char * fixup_bare_dirnames(const char * name) return NULL; } +/* 8.3, remove() is now broken on symbolic links */ +static int rms_erase(const char * vmsname); + + /* mp_do_kill_file * A little hack to get around a bug in some implemenation of remove() * that do not know how to delete a directory @@ -1744,8 +1827,8 @@ static char * fixup_bare_dirnames(const char * name) static int mp_do_kill_file(pTHX_ const char *name, int dirflag) { - char *vmsname, *rspec; - char *remove_name; + char *vmsname; + char *rslt; unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE; unsigned long int cxt = 0, aclsts, fndsts, rmsts = -1; struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; @@ -1772,59 +1855,31 @@ mp_do_kill_file(pTHX_ const char *name, int dirflag) vmsname = PerlMem_malloc(NAM$C_MAXRSS+1); if (vmsname == NULL) _ckvmssts(SS$_INSFMEM); - if (do_rmsexpand(name, vmsname, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL) == NULL) { - PerlMem_free(vmsname); - return -1; - } - - if (decc_posix_compliant_pathnames) { - /* In POSIX mode, we prefer to remove the UNIX name */ - rspec = vmsname; - remove_name = (char *)name; - } - else { - rspec = PerlMem_malloc(NAM$C_MAXRSS+1); - if (rspec == NULL) _ckvmssts(SS$_INSFMEM); - if (do_rmsexpand(vmsname, rspec, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL) == NULL) { - PerlMem_free(rspec); + rslt = do_rmsexpand(name, + vmsname, + 0, + NULL, + PERL_RMSEXPAND_M_VMS | PERL_RMSEXPAND_M_SYMLINK, + NULL, + NULL); + if (rslt == NULL) { PerlMem_free(vmsname); return -1; } - PerlMem_free(vmsname); - remove_name = rspec; - } - -#if defined(__CRTL_VER) && __CRTL_VER >= 70000000 - if (dirflag != 0) { - if (decc_dir_barename && decc_posix_compliant_pathnames) { - remove_name = PerlMem_malloc(NAM$C_MAXRSS+1); - if (remove_name == NULL) _ckvmssts(SS$_INSFMEM); - do_pathify_dirspec(name, remove_name, 0, NULL); - if (!rmdir(remove_name)) { + /* Erase the file */ + rmsts = rms_erase(vmsname); - PerlMem_free(remove_name); - PerlMem_free(rspec); - return 0; /* Can we just get rid of it? */ - } - } - else { - if (!rmdir(remove_name)) { - PerlMem_free(rspec); - return 0; /* Can we just get rid of it? */ - } - } - } - else -#endif - if (!remove(remove_name)) { - PerlMem_free(rspec); - return 0; /* Can we just get rid of it? */ + /* Did it succeed */ + if ($VMS_STATUS_SUCCESS(rmsts)) { + PerlMem_free(vmsname); + return 0; } /* If not, can changing protections help? */ - if (vaxc$errno != RMS$_PRV) { - PerlMem_free(rspec); + if (rmsts != RMS$_PRV) { + set_vaxc_errno(rmsts); + PerlMem_free(vmsname); return -1; } @@ -1833,10 +1888,11 @@ mp_do_kill_file(pTHX_ const char *name, int dirflag) * to delete the file. */ _ckvmssts(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0)); - fildsc.dsc$w_length = strlen(rspec); - fildsc.dsc$a_pointer = rspec; + fildsc.dsc$w_length = strlen(vmsname); + fildsc.dsc$a_pointer = vmsname; cxt = 0; newace.myace$l_ident = oldace.myace$l_ident; + rmsts = -1; if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) { switch (aclsts) { case RMS$_FNF: case RMS$_DNF: case SS$_NOSUCHOBJECT: @@ -1853,7 +1909,7 @@ mp_do_kill_file(pTHX_ const char *name, int dirflag) _ckvmssts(aclsts); } set_vaxc_errno(aclsts); - PerlMem_free(rspec); + PerlMem_free(vmsname); return -1; } /* Grab any existing ACEs with this identifier in case we fail */ @@ -1864,23 +1920,12 @@ mp_do_kill_file(pTHX_ const char *name, int dirflag) if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1)) goto yourroom; -#if defined(__CRTL_VER) && __CRTL_VER >= 70000000 - if (dirflag != 0) - if (decc_dir_barename && decc_posix_compliant_pathnames) { - remove_name = PerlMem_malloc(NAM$C_MAXRSS+1); - if (remove_name == NULL) _ckvmssts(SS$_INSFMEM); - - do_pathify_dirspec(name, remove_name, 0, NULL); - rmsts = rmdir(remove_name); - PerlMem_free(remove_name); + rmsts = rms_erase(vmsname); + if ($VMS_STATUS_SUCCESS(rmsts)) { + rmsts = 0; } else { - rmsts = rmdir(remove_name); - } - else -#endif - rmsts = remove(remove_name); - if (rmsts) { + rmsts = -1; /* We blew it - dir with files in it, no write priv for * parent directory, etc. Put things back the way they were. */ if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1)) @@ -1904,11 +1949,9 @@ mp_do_kill_file(pTHX_ const char *name, int dirflag) if (!(aclsts & 1)) { set_errno(EVMSERR); set_vaxc_errno(aclsts); - PerlMem_free(rspec); - return -1; } - PerlMem_free(rspec); + PerlMem_free(vmsname); return rmsts; } /* end of kill_file() */ @@ -1919,13 +1962,27 @@ mp_do_kill_file(pTHX_ const char *name, int dirflag) int Perl_do_rmdir(pTHX_ const char *name) { - char dirfile[NAM$C_MAXRSS+1]; + char * dirfile; int retval; Stat_t st; - if (do_fileify_dirspec(name,dirfile,0,NULL) == NULL) return -1; - if (flex_stat(dirfile,&st) || !S_ISDIR(st.st_mode)) retval = -1; - else retval = mp_do_kill_file(aTHX_ dirfile, 1); + dirfile = PerlMem_malloc(VMS_MAXRSS + 1); + if (dirfile == NULL) + _ckvmssts(SS$_INSFMEM); + + /* Force to a directory specification */ + if (do_fileify_dirspec(name, dirfile, 0, NULL) == NULL) { + PerlMem_free(dirfile); + return -1; + } + if (Perl_flex_lstat(aTHX_ dirfile, &st) || !S_ISDIR(st.st_mode)) { + errno = ENOTDIR; + retval = -1; + } + else + retval = mp_do_kill_file(aTHX_ dirfile, 1); + + PerlMem_free(dirfile); return retval; } /* end of do_rmdir */ @@ -1945,95 +2002,19 @@ Perl_kill_file(pTHX_ const char *name) { char rspec[NAM$C_MAXRSS+1]; char *tspec; - unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE; - unsigned long int cxt = 0, aclsts, fndsts, rmsts = -1; - struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; - struct myacedef { - unsigned char myace$b_length; - unsigned char myace$b_type; - unsigned short int myace$w_flags; - unsigned long int myace$l_access; - unsigned long int myace$l_ident; - } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0, - ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0}, - oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0}; - struct itmlst_3 - findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0}, - {sizeof oldace, ACL$C_READACE, &oldace, 0},{0,0,0,0}}, - addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}}, - dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}}, - lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}}, - ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}}; - - /* Expand the input spec using RMS, since the CRTL remove() and - * system services won't do this by themselves, so we may miss - * a file "hiding" behind a logical name or search list. */ - tspec = do_rmsexpand(name, rspec, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL); - if (tspec == NULL) return -1; - if (!remove(rspec)) return 0; /* Can we just get rid of it? */ - /* If not, can changing protections help? */ - if (vaxc$errno != RMS$_PRV) return -1; + Stat_t st; + int rmsts; - /* No, so we get our own UIC to use as a rights identifier, - * and the insert an ACE at the head of the ACL which allows us - * to delete the file. + /* Remove() is allowed to delete directories, according to the X/Open + * specifications. + * This may need special handling to work with the ACL hacks. */ - _ckvmssts(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0)); - fildsc.dsc$w_length = strlen(rspec); - fildsc.dsc$a_pointer = rspec; - cxt = 0; - newace.myace$l_ident = oldace.myace$l_ident; - if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) { - switch (aclsts) { - case RMS$_FNF: case RMS$_DNF: case SS$_NOSUCHOBJECT: - set_errno(ENOENT); break; - case RMS$_DIR: - set_errno(ENOTDIR); break; - case RMS$_DEV: - set_errno(ENODEV); break; - case RMS$_SYN: case SS$_INVFILFOROP: - set_errno(EINVAL); break; - case RMS$_PRV: - set_errno(EACCES); break; - default: - _ckvmssts(aclsts); - } - set_vaxc_errno(aclsts); - return -1; - } - /* Grab any existing ACEs with this identifier in case we fail */ - aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt); - if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY - || fndsts == SS$_NOMOREACE ) { - /* Add the new ACE . . . */ - if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1)) - goto yourroom; - if ((rmsts = remove(name))) { - /* We blew it - dir with files in it, no write priv for - * parent directory, etc. Put things back the way they were. */ - if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1)) - goto yourroom; - if (fndsts & 1) { - addlst[0].bufadr = &oldace; - if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1)) - goto yourroom; - } - } + if ((flex_lstat(name, &st) == 0) && S_ISDIR(st.st_mode)) { + rmsts = Perl_do_rmdir(aTHX_ name); + return rmsts; } - yourroom: - fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0); - /* We just deleted it, so of course it's not there. Some versions of - * VMS seem to return success on the unlock operation anyhow (after all - * the unlock is successful), but others don't. - */ - if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL; - if (aclsts & 1) aclsts = fndsts; - if (!(aclsts & 1)) { - set_errno(EVMSERR); - set_vaxc_errno(aclsts); - return -1; - } + rmsts = mp_do_kill_file(aTHX_ name, 0); return rmsts; @@ -2102,6 +2083,61 @@ Perl_my_chdir(pTHX_ const char *dir) /*}}}*/ +/*{{{int my_chmod(char *, mode_t)*/ +int +Perl_my_chmod(pTHX_ const char *file_spec, mode_t mode) +{ + STRLEN speclen = strlen(file_spec); + + /* zero length string sometimes gives ACCVIO */ + if (speclen == 0) return -1; + + /* some versions of CRTL chmod() doesn't tolerate trailing /, since + * that implies null file name/type. However, it's commonplace under Unix, + * so we'll allow it for a gain in portability. + * + * Tests are showing that chmod() on VMS 8.3 is only accepting directories + * in VMS file.dir notation. + */ + if ((speclen > 1) && (file_spec[speclen-1] == '/')) { + char *vms_src, *vms_dir, *rslt; + int ret = -1; + errno = EIO; + + /* First convert this to a VMS format specification */ + vms_src = PerlMem_malloc(VMS_MAXRSS); + if (vms_src == NULL) + _ckvmssts(SS$_INSFMEM); + + rslt = do_tovmsspec(file_spec, vms_src, 0, NULL); + if (rslt == NULL) { + /* If we fail, then not a file specification */ + PerlMem_free(vms_src); + errno = EIO; + return -1; + } + + /* Now make it a directory spec so chmod is happy */ + vms_dir = PerlMem_malloc(VMS_MAXRSS + 1); + if (vms_dir == NULL) + _ckvmssts(SS$_INSFMEM); + rslt = do_fileify_dirspec(vms_src, vms_dir, 0, NULL); + PerlMem_free(vms_src); + + /* Now do it */ + if (rslt != NULL) { + ret = chmod(vms_dir, mode); + } else { + errno = EIO; + } + PerlMem_free(vms_dir); + return ret; + } + else return chmod(file_spec, mode); +} /* end of my_chmod */ +/*}}}*/ + + /*{{{FILE *my_tmpfile()*/ FILE * my_tmpfile(void) @@ -2823,14 +2859,20 @@ pipe_exit_routine(pTHX) unsigned long int retsts = SS$_NORMAL, abort = SS$_TIMEOUT; int sts, did_stuff, need_eof, j; - /* - flush any pending i/o + /* + * Flush any pending i/o, but since we are in process run-down, be + * careful about referencing PerlIO structures that may already have + * been deallocated. We may not even have an interpreter anymore. */ info = open_pipes; while (info) { if (info->fp) { - if (!info->useFILE) - PerlIO_flush(info->fp); /* first, flush data */ + if (!info->useFILE +#if defined(USE_ITHREADS) + && my_perl +#endif + && PL_perlio_fd_refcnt) + PerlIO_flush(info->fp); else fflush((FILE *)info->fp); } @@ -3744,8 +3786,6 @@ vmspipe_tempfile(pTHX) } -#ifdef USE_VMS_DECTERM - static int vms_is_syscommand_xterm(void) { const static struct dsc$descriptor_s syscommand_dsc = @@ -3836,6 +3876,12 @@ static PerlIO * create_forked_xterm(pTHX_ const char *cmd, const char *mode) struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T, DSC$K_CLASS_S, mbx1}; + /* LIB$FIND_IMAGE_SIGNAL needs a handler */ + /*---------------------------------------*/ + VAXC$ESTABLISH((__vms_handler)LIB$SIG_TO_RET); + + + /* Make sure that this is from the Perl debugger */ ret_char = strstr(cmd," xterm "); if (ret_char == NULL) return NULL; @@ -3847,6 +3893,37 @@ static PerlIO * create_forked_xterm(pTHX_ const char *cmd, const char *mode) if (ret_char == NULL) return NULL; + if (decw_term_port == 0) { + $DESCRIPTOR(filename1_dsc, "DECW$TERMINALSHR12"); + $DESCRIPTOR(filename2_dsc, "DECW$TERMINALSHR"); + $DESCRIPTOR(decw_term_port_dsc, "DECW$TERM_PORT"); + + status = LIB$FIND_IMAGE_SYMBOL + (&filename1_dsc, + &decw_term_port_dsc, + (void *)&decw_term_port, + NULL, + 0); + + /* Try again with the other image name */ + if (!$VMS_STATUS_SUCCESS(status)) { + + status = LIB$FIND_IMAGE_SYMBOL + (&filename2_dsc, + &decw_term_port_dsc, + (void *)&decw_term_port, + NULL, + 0); + + } + + } + + + /* No decw$term_port, give it up */ + if (!$VMS_STATUS_SUCCESS(status)) + return NULL; + /* Are we on a workstation? */ /* to do: capture the rows / columns and pass their properties */ ret_stat = vms_is_syscommand_xterm(); @@ -3892,7 +3969,7 @@ static PerlIO * create_forked_xterm(pTHX_ const char *cmd, const char *mode) device_name_len = 0; /* Try to create the window */ - status = decw$term_port + status = (*decw_term_port) (NULL, NULL, &customization_dsc, @@ -3971,7 +4048,6 @@ static PerlIO * create_forked_xterm(pTHX_ const char *cmd, const char *mode) /* All done */ return info->fp; } -#endif static PerlIO * safe_popen(pTHX_ const char *cmd, const char *in_mode, int *psts) @@ -4001,7 +4077,6 @@ safe_popen(pTHX_ const char *cmd, const char *in_mode, int *psts) $DESCRIPTOR(d_sym_out,"PERL_POPEN_OUT"); $DESCRIPTOR(d_sym_err,"PERL_POPEN_ERR"); -#ifdef USE_VMS_DECTERM /* Check here for Xterm create request. This means looking for * "3>&1 xterm\b" and "\btty 1>&3\b$" in the command, and that it * is possible to create an xterm. @@ -4013,7 +4088,6 @@ safe_popen(pTHX_ const char *cmd, const char *in_mode, int *psts) if (xterm_fd != Nullfp) return xterm_fd; } -#endif if (!head_PLOC) store_pipelocs(aTHX); /* at least TRY to use a static vmspipe file */ @@ -4377,8 +4451,12 @@ I32 Perl_my_pclose(pTHX_ PerlIO *fp) * the first EOF closing the pipe (and DASSGN'ing the channel)... */ if (info->fp) { - if (!info->useFILE) - PerlIO_flush(info->fp); /* first, flush data */ + if (!info->useFILE +#if defined(USE_ITHREADS) + && my_perl +#endif + && PL_perlio_fd_refcnt) + PerlIO_flush(info->fp); else fflush((FILE *)info->fp); } @@ -4400,7 +4478,11 @@ I32 Perl_my_pclose(pTHX_ PerlIO *fp) 0, 0, 0, 0, 0, 0)); _ckvmssts(sys$setast(1)); if (info->fp) { - if (!info->useFILE) + if (!info->useFILE +#if defined(USE_ITHREADS) + && my_perl +#endif + && PL_perlio_fd_refcnt) PerlIO_close(info->fp); else fclose((FILE *)info->fp); @@ -4729,6 +4811,461 @@ struct NAML * nam; #endif +/* rms_erase + * The CRTL for 8.3 and later can create symbolic links in any mode, + * however in 8.3 the unlink/remove/delete routines will only properly handle + * them if one of the PCP modes is active. + */ +static int rms_erase(const char * vmsname) +{ + int status; + struct FAB myfab = cc$rms_fab; + rms_setup_nam(mynam); + + rms_set_fna(myfab, mynam, (char *)vmsname, strlen(vmsname)); /* cast ok */ + rms_bind_fab_nam(myfab, mynam); + + /* Are we removing all versions? */ + if (vms_unlink_all_versions == 1) { + const char * defspec = ";*"; + rms_set_dna(myfab, mynam, (char *)defspec, strlen(defspec)); /* cast ok */ + } + +#ifdef NAML$M_OPEN_SPECIAL + rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL); +#endif + + status = SYS$ERASE(&myfab, 0, 0); + + return status; +} + + +static int +vms_rename_with_acl(pTHX_ const struct dsc$descriptor_s * vms_src_dsc, + const struct dsc$descriptor_s * vms_dst_dsc, + unsigned long flags) +{ + /* VMS and UNIX handle file permissions differently and the + * the same ACL trick may be needed for renaming files, + * especially if they are directories. + */ + + /* todo: get kill_file and rename to share common code */ + /* I can not find online documentation for $change_acl + * it appears to be replaced by $set_security some time ago */ + +const unsigned int access_mode = 0; +$DESCRIPTOR(obj_file_dsc,"FILE"); +char *vmsname; +char *rslt; +unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE; +int aclsts, fndsts, rnsts = -1; +unsigned int ctx = 0; +struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; +struct dsc$descriptor_s * clean_dsc; + +struct myacedef { + unsigned char myace$b_length; + unsigned char myace$b_type; + unsigned short int myace$w_flags; + unsigned long int myace$l_access; + unsigned long int myace$l_ident; +} newace = { sizeof(struct myacedef), ACE$C_KEYID, 0, + ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, + 0}, + oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0}; + +struct item_list_3 + findlst[3] = {{sizeof oldace, OSS$_ACL_FIND_ENTRY, &oldace, 0}, + {sizeof oldace, OSS$_ACL_READ_ENTRY, &oldace, 0}, + {0,0,0,0}}, + addlst[2] = {{sizeof newace, OSS$_ACL_ADD_ENTRY, &newace, 0},{0,0,0,0}}, + dellst[2] = {{sizeof newace, OSS$_ACL_DELETE_ENTRY, &newace, 0}, + {0,0,0,0}}; + + + /* Expand the input spec using RMS, since we do not want to put + * ACLs on the target of a symbolic link */ + vmsname = PerlMem_malloc(NAM$C_MAXRSS+1); + if (vmsname == NULL) + return SS$_INSFMEM; + + rslt = do_rmsexpand(vms_src_dsc->dsc$a_pointer, + vmsname, + 0, + NULL, + PERL_RMSEXPAND_M_VMS | PERL_RMSEXPAND_M_SYMLINK, + NULL, + NULL); + if (rslt == NULL) { + PerlMem_free(vmsname); + return SS$_INSFMEM; + } + + /* So we get our own UIC to use as a rights identifier, + * and the insert an ACE at the head of the ACL which allows us + * to delete the file. + */ + _ckvmssts(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0)); + + fildsc.dsc$w_length = strlen(vmsname); + fildsc.dsc$a_pointer = vmsname; + ctx = 0; + newace.myace$l_ident = oldace.myace$l_ident; + rnsts = SS$_ABORT; + + /* Grab any existing ACEs with this identifier in case we fail */ + clean_dsc = &fildsc; + aclsts = fndsts = sys$get_security(&obj_file_dsc, + &fildsc, + NULL, + OSS$M_WLOCK, + findlst, + &ctx, + &access_mode); + + if ($VMS_STATUS_SUCCESS(fndsts) || (fndsts == SS$_ACLEMPTY)) { + /* Add the new ACE . . . */ + + /* if the sys$get_security succeeded, then ctx is valid, and the + * object/file descriptors will be ignored. But otherwise they + * are needed + */ + aclsts = sys$set_security(&obj_file_dsc, &fildsc, NULL, + OSS$M_RELCTX, addlst, &ctx, &access_mode); + if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) { + set_errno(EVMSERR); + set_vaxc_errno(aclsts); + PerlMem_free(vmsname); + return aclsts; + } + + rnsts = lib$rename_file(vms_src_dsc, vms_dst_dsc, + NULL, NULL, + &flags, + NULL, NULL, NULL, NULL, NULL, NULL, NULL); + + if ($VMS_STATUS_SUCCESS(rnsts)) { + clean_dsc = (struct dsc$descriptor_s *)vms_dst_dsc; + } + + /* Put things back the way they were. */ + ctx = 0; + aclsts = sys$get_security(&obj_file_dsc, + clean_dsc, + NULL, + OSS$M_WLOCK, + findlst, + &ctx, + &access_mode); + + if ($VMS_STATUS_SUCCESS(aclsts)) { + int sec_flags; + + sec_flags = 0; + if (!$VMS_STATUS_SUCCESS(fndsts)) + sec_flags = OSS$M_RELCTX; + + /* Get rid of the new ACE */ + aclsts = sys$set_security(NULL, NULL, NULL, + sec_flags, dellst, &ctx, &access_mode); + + /* If there was an old ACE, put it back */ + if ($VMS_STATUS_SUCCESS(aclsts) && $VMS_STATUS_SUCCESS(fndsts)) { + addlst[0].bufadr = &oldace; + aclsts = sys$set_security(NULL, NULL, NULL, + OSS$M_RELCTX, addlst, &ctx, &access_mode); + if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) { + set_errno(EVMSERR); + set_vaxc_errno(aclsts); + rnsts = aclsts; + } + } else { + int aclsts2; + + /* Try to clear the lock on the ACL list */ + aclsts2 = sys$set_security(NULL, NULL, NULL, + OSS$M_RELCTX, NULL, &ctx, &access_mode); + + /* Rename errors are most important */ + if (!$VMS_STATUS_SUCCESS(rnsts)) + aclsts = rnsts; + set_errno(EVMSERR); + set_vaxc_errno(aclsts); + rnsts = aclsts; + } + } + else { + if (aclsts != SS$_ACLEMPTY) + rnsts = aclsts; + } + } + else + rnsts = fndsts; + + PerlMem_free(vmsname); + return rnsts; +} + + +/*{{{int rename(const char *, const char * */ +/* Not exactly what X/Open says to do, but doing it absolutely right + * and efficiently would require a lot more work. This should be close + * enough to pass all but the most strict X/Open compliance test. + */ +int +Perl_rename(pTHX_ const char *src, const char * dst) +{ +int retval; +int pre_delete = 0; +int src_sts; +int dst_sts; +Stat_t src_st; +Stat_t dst_st; + + /* Validate the source file */ + src_sts = flex_lstat(src, &src_st); + if (src_sts != 0) { + + /* No source file or other problem */ + return src_sts; + } + + dst_sts = flex_lstat(dst, &dst_st); + if (dst_sts == 0) { + + if (dst_st.st_dev != src_st.st_dev) { + /* Must be on the same device */ + errno = EXDEV; + return -1; + } + + /* VMS_INO_T_COMPARE is true if the inodes are different + * to match the output of memcmp + */ + + if (!VMS_INO_T_COMPARE(src_st.st_ino, dst_st.st_ino)) { + /* That was easy, the files are the same! */ + return 0; + } + + if (S_ISDIR(src_st.st_mode) && !S_ISDIR(dst_st.st_mode)) { + /* If source is a directory, so must be dest */ + errno = EISDIR; + return -1; + } + + } + + + if ((dst_sts == 0) && + (vms_unlink_all_versions || S_ISDIR(dst_st.st_mode))) { + + /* We have issues here if vms_unlink_all_versions is set + * If the destination exists, and is not a directory, then + * we must delete in advance. + * + * If the src is a directory, then we must always pre-delete + * the destination. + * + * If we successfully delete the dst in advance, and the rename fails + * X/Open requires that errno be EIO. + * + */ + + if (!S_ISDIR(dst_st.st_mode) || S_ISDIR(src_st.st_mode)) { + int d_sts; + d_sts = mp_do_kill_file(aTHX_ dst, S_ISDIR(dst_st.st_mode)); + if (d_sts != 0) + return d_sts; + + /* We killed the destination, so only errno now is EIO */ + pre_delete = 1; + } + } + + /* Originally the idea was to call the CRTL rename() and only + * try the lib$rename_file if it failed. + * It turns out that there are too many variants in what the + * the CRTL rename might do, so only use lib$rename_file + */ + retval = -1; + + { + /* Is the source and dest both in VMS format */ + /* if the source is a directory, then need to fileify */ + /* and dest must be a directory or non-existant. */ + + char * vms_src; + char * vms_dst; + int sts; + char * ret_str; + unsigned long flags; + struct dsc$descriptor_s old_file_dsc; + struct dsc$descriptor_s new_file_dsc; + + /* We need to modify the src and dst depending + * on if one or more of them are directories. + */ + + vms_src = PerlMem_malloc(VMS_MAXRSS); + if (vms_src == NULL) + _ckvmssts(SS$_INSFMEM); + + /* Source is always a VMS format file */ + ret_str = do_tovmsspec(src, vms_src, 0, NULL); + if (ret_str == NULL) { + PerlMem_free(vms_src); + errno = EIO; + return -1; + } + + vms_dst = PerlMem_malloc(VMS_MAXRSS); + if (vms_dst == NULL) + _ckvmssts(SS$_INSFMEM); + + if (S_ISDIR(src_st.st_mode)) { + char * ret_str; + char * vms_dir_file; + + vms_dir_file = PerlMem_malloc(VMS_MAXRSS); + if (vms_dir_file == NULL) + _ckvmssts(SS$_INSFMEM); + + /* The source must be a file specification */ + ret_str = do_fileify_dirspec(vms_src, vms_dir_file, 0, NULL); + if (ret_str == NULL) { + PerlMem_free(vms_src); + PerlMem_free(vms_dst); + PerlMem_free(vms_dir_file); + errno = EIO; + return -1; + } + PerlMem_free(vms_src); + vms_src = vms_dir_file; + + /* If the dest is a directory, we must remove it + if (dst_sts == 0) { + int d_sts; + d_sts = mp_do_kill_file(aTHX_ dst, 1); + if (d_sts != 0) { + PerlMem_free(vms_src); + PerlMem_free(vms_dst); + errno = EIO; + return sts; + } + + pre_delete = 1; + } + + /* The dest must be a VMS file specification */ + ret_str = do_tovmsspec(dst, vms_dst, 0, NULL); + if (ret_str == NULL) { + PerlMem_free(vms_src); + PerlMem_free(vms_dst); + errno = EIO; + return -1; + } + + /* The source must be a file specification */ + vms_dir_file = PerlMem_malloc(VMS_MAXRSS); + if (vms_dir_file == NULL) + _ckvmssts(SS$_INSFMEM); + + ret_str = do_fileify_dirspec(vms_dst, vms_dir_file, 0, NULL); + if (ret_str == NULL) { + PerlMem_free(vms_src); + PerlMem_free(vms_dst); + PerlMem_free(vms_dir_file); + errno = EIO; + return -1; + } + PerlMem_free(vms_dst); + vms_dst = vms_dir_file; + + } else { + /* File to file or file to new dir */ + + if ((dst_sts == 0) && S_ISDIR(dst_st.st_mode)) { + /* VMS pathify a dir target */ + ret_str = do_tovmspath(dst, vms_dst, 0, NULL); + if (ret_str == NULL) { + PerlMem_free(vms_src); + PerlMem_free(vms_dst); + errno = EIO; + return -1; + } + } else { + + /* fileify a target VMS file specification */ + ret_str = do_tovmsspec(dst, vms_dst, 0, NULL); + if (ret_str == NULL) { + PerlMem_free(vms_src); + PerlMem_free(vms_dst); + errno = EIO; + return -1; + } + } + } + + old_file_dsc.dsc$a_pointer = vms_src; + old_file_dsc.dsc$w_length = strlen(vms_src); + old_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T; + old_file_dsc.dsc$b_class = DSC$K_CLASS_S; + + new_file_dsc.dsc$a_pointer = vms_dst; + new_file_dsc.dsc$w_length = strlen(vms_dst); + new_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T; + new_file_dsc.dsc$b_class = DSC$K_CLASS_S; + + flags = 0; +#if !defined(__VAX) && defined(NAML$C_MAXRSS) + flags |= 2; /* LIB$M_FIL_LONG_NAMES */ +#endif + + sts = lib$rename_file(&old_file_dsc, + &new_file_dsc, + NULL, NULL, + &flags, + NULL, NULL, NULL, NULL, NULL, NULL, NULL); + if (!$VMS_STATUS_SUCCESS(sts)) { + + /* We could have failed because VMS style permissions do not + * permit renames that UNIX will allow. Just like the hack + * in for kill_file. + */ + sts = vms_rename_with_acl(aTHX_ &old_file_dsc, &new_file_dsc, flags); + } + + PerlMem_free(vms_src); + PerlMem_free(vms_dst); + if (!$VMS_STATUS_SUCCESS(sts)) { + errno = EIO; + return -1; + } + retval = 0; + } + + if (vms_unlink_all_versions) { + /* Now get rid of any previous versions of the source file that + * might still exist + */ + int save_errno; + save_errno = errno; + src_sts = mp_do_kill_file(aTHX_ src, S_ISDIR(src_st.st_mode)); + errno = save_errno; + } + + /* We deleted the destination, so must force the error to be EIO */ + if ((retval != 0) && (pre_delete != 0)) + errno = EIO; + + return retval; +} +/*}}}*/ + + /*{{{char *do_rmsexpand(char *fspec, char *out, int ts, char *def, unsigned opts)*/ /* Shortcut for common case of simple calls to $PARSE and $SEARCH * to expand file specification. Allows for a single default file @@ -4745,6 +5282,7 @@ struct NAML * nam; * PERL_RMSEXPAND_M_VMS - Force output file specification to VMS format. * PERL_RMSEXPAND_M_LONG - Want output in long formst * PERL_RMSEXPAND_M_VMS_IN - Input is already in VMS, so do not vmsify + * PERL_RMSEXPAND_M_SYMLINK - Use symbolic link, not target */ static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *); @@ -4860,6 +5398,12 @@ mp_do_rmsexpand rms_set_nam_nop(mynam, NAM$M_NO_SHORT_UPCASE); #endif + /* We may not want to follow symbolic links */ +#ifdef NAML$M_OPEN_SPECIAL + if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0) + rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL); +#endif + /* First attempt to parse as an existing file */ retsts = sys$parse(&myfab,0,0); if (!(retsts & STS$K_SUCCESS)) { @@ -4977,6 +5521,10 @@ mp_do_rmsexpand if (decc_efs_case_preserve) rms_set_nam_nop(defnam, NAM$M_NO_SHORT_UPCASE); #endif +#ifdef NAML$M_OPEN_SPECIAL + if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0) + rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL); +#endif if (sys$parse(&deffab,0,0) & STS$K_SUCCESS) { if (trimver) { trimver = !rms_is_nam_fnb(defnam, NAM$M_EXP_VER); @@ -5058,7 +5606,7 @@ mp_do_rmsexpand if (!rms_nam_rsll(mynam)) { if (isunix) { - if (do_tounixspec(esa,outbuf,0,fs_utf8) == NULL) { + if (do_tounixspec(tbuf, outbuf ,0 , fs_utf8) == NULL) { if (out) Safefree(out); if (esal != NULL) PerlMem_free(esal); @@ -5068,7 +5616,7 @@ mp_do_rmsexpand return NULL; } } - else strcpy(outbuf,esa); + else strcpy(outbuf, tbuf); } else if (isunix) { tmpfspec = PerlMem_malloc(VMS_MAXRSS); @@ -5182,7 +5730,7 @@ static char *mp_do_fileify_dirspec(pTHX_ const char *dir,char *buf,int ts, int * (!decc_posix_compliant_pathnames && decc_disable_posix_root)) { strcpy(trndir,*dir == '/' ? dir + 1: dir); trnlnm_iter_count = 0; - while (!strpbrk(trndir,"/]>:>") && my_trnlnm(trndir,trndir,0)) { + while (!strpbrk(trndir,"/]>:") && my_trnlnm(trndir,trndir,0)) { trnlnm_iter_count++; if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break; } @@ -6077,7 +6625,7 @@ static char *mp_do_tounixspec(pTHX_ const char *spec, char *buf, int ts, int * u } if ((*cp2 == '^')) { /* EFS file escape, pass the next character as is */ - /* Fix me: HEX encoding for UNICODE not implemented */ + /* Fix me: HEX encoding for Unicode not implemented */ cp2++; } else if ( *cp2 == '.') { @@ -6092,7 +6640,7 @@ static char *mp_do_tounixspec(pTHX_ const char *spec, char *buf, int ts, int * u for (; cp2 <= dirend; cp2++) { if ((*cp2 == '^')) { /* EFS file escape, pass the next character as is */ - /* Fix me: HEX encoding for UNICODE not implemented */ + /* Fix me: HEX encoding for Unicode not implemented */ *(cp1++) = *(++cp2); /* An escaped dot stays as is -- don't convert to slash */ if (*cp2 == '.') cp2++; @@ -7545,6 +8093,14 @@ static char *mp_do_tovmsspec case '#': case '%': case '^': + /* Don't escape again if following character is + * already something we escape. + */ + if (strchr("\"~`!#%^&()=+\'@[]{}:\\|<>_.", *(cp2+1))) { + *(cp1++) = *(cp2++); + break; + } + /* But otherwise fall through and escape it. */ case '&': case '(': case ')': @@ -8745,9 +9301,6 @@ Perl_opendir(pTHX_ const char *name) DIR *dd; char *dir; Stat_t sb; - int unix_flag = 0; - - unix_flag = is_unix_filespec(name); Newx(dir, VMS_MAXRSS, char); if (do_tovmspath(name,dir,0,NULL) == NULL) { @@ -8778,8 +9331,12 @@ Perl_opendir(pTHX_ const char *name) dd->context = 0; dd->count = 0; dd->flags = 0; - if (unix_flag) - dd->flags = PERL_VMSDIR_M_UNIXSPECS; + /* By saying we always want the result of readdir() in unix format, we + * are really saying we want all the escapes removed. Otherwise the caller, + * having no way to know whether it's already in VMS format, might send it + * through tovmsspec again, thus double escaping. + */ + dd->flags = PERL_VMSDIR_M_UNIXSPECS; dd->pat.dsc$a_pointer = dd->pattern; dd->pat.dsc$w_length = strlen(dd->pattern); dd->pat.dsc$b_dtype = DSC$K_DTYPE_T; @@ -8979,7 +9536,7 @@ Perl_readdir(pTHX_ DIR *dd) if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) { /* Translate the encoded characters. */ - /* Fixme: unicode handling could result in embedded 0 characters */ + /* Fixme: Unicode handling could result in embedded 0 characters */ if (strchr(dd->entry.d_name, '^') != NULL) { char new_name[256]; char * q; @@ -8993,7 +9550,7 @@ Perl_readdir(pTHX_ DIR *dd) /* fix-me */ /* if outchars_added > 1, then this is a wide file specification */ /* Wide file specifications need to be passed in Perl */ - /* counted strings apparently with a unicode flag */ + /* counted strings apparently with a Unicode flag */ } *q = 0; strcpy(dd->entry.d_name, new_name); @@ -10920,11 +11477,10 @@ static I32 Perl_cando_by_name_int (pTHX_ I32 bit, bool effective, const char *fname, int opts) { - static char usrname[L_cuserid]; - static struct dsc$descriptor_s usrdsc = + char usrname[L_cuserid]; + struct dsc$descriptor_s usrdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, usrname}; - char vmsname[NAM$C_MAXRSS+1]; - char *fileified; + char *vmsname = NULL, *fileified = NULL; unsigned long int objtyp = ACL$C_FILE, access, retsts, privused, iosb[2], flags; unsigned short int retlen, trnlnm_iter_count; struct dsc$descriptor_s namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; @@ -10938,40 +11494,63 @@ Perl_cando_by_name_int struct itmlst_3 usrprolst[2] = {{sizeof curprv, CHP$_PRIV, &curprv, &retlen}, {0,0,0,0}}; struct dsc$descriptor_s usrprodsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; + Stat_t st; + static int profile_context = -1; if (!fname || !*fname) return FALSE; - /* Make sure we expand logical names, since sys$check_access doesn't */ - fileified = NULL; - if ((opts & PERL_RMSEXPAND_M_VMS_IN) != 0) { - fileified = PerlMem_malloc(VMS_MAXRSS); - if (!strpbrk(fname,"/]>:")) { + /* Make sure we expand logical names, since sys$check_access doesn't */ + fileified = PerlMem_malloc(VMS_MAXRSS); + if (fileified == NULL) _ckvmssts(SS$_INSFMEM); + if (!strpbrk(fname,"/]>:")) { strcpy(fileified,fname); trnlnm_iter_count = 0; - while (!strpbrk(fileified,"/]>:>") && my_trnlnm(fileified,fileified,0)) { + while (!strpbrk(fileified,"/]>:") && my_trnlnm(fileified,fileified,0)) { trnlnm_iter_count++; if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break; } fname = fileified; - } + } + + vmsname = PerlMem_malloc(VMS_MAXRSS); + if (vmsname == NULL) _ckvmssts(SS$_INSFMEM); + if ( !(opts & PERL_RMSEXPAND_M_VMS_IN) ) { + /* Don't know if already in VMS format, so make sure */ if (!do_rmsexpand(fname, vmsname, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL)) { PerlMem_free(fileified); + PerlMem_free(vmsname); return FALSE; } - retlen = namdsc.dsc$w_length = strlen(vmsname); - namdsc.dsc$a_pointer = vmsname; - if (vmsname[retlen-1] == ']' || vmsname[retlen-1] == '>' || - vmsname[retlen-1] == ':') { - if (!do_fileify_dirspec(vmsname,fileified,1,NULL)) return FALSE; - namdsc.dsc$w_length = strlen(fileified); - namdsc.dsc$a_pointer = fileified; - } } else { - retlen = namdsc.dsc$w_length = strlen(fname); - namdsc.dsc$a_pointer = (char *)fname; /* cast ok */ + strcpy(vmsname,fname); } + /* sys$check_access needs a file spec, not a directory spec. + * Don't use flex_stat here, as that depends on thread context + * having been initialized, and we may get here during startup. + */ + + retlen = namdsc.dsc$w_length = strlen(vmsname); + if (vmsname[retlen-1] == ']' + || vmsname[retlen-1] == '>' + || vmsname[retlen-1] == ':' + || (!stat(vmsname, (stat_t *)&st) && S_ISDIR(st.st_mode))) { + + if (!do_fileify_dirspec(vmsname,fileified,1,NULL)) { + PerlMem_free(fileified); + PerlMem_free(vmsname); + return FALSE; + } + fname = fileified; + } + else { + fname = vmsname; + } + + retlen = namdsc.dsc$w_length = strlen(fname); + namdsc.dsc$a_pointer = (char *)fname; + switch (bit) { case S_IXUSR: case S_IXGRP: case S_IXOTH: access = ARM$M_EXECUTE; @@ -10992,6 +11571,8 @@ Perl_cando_by_name_int default: if (fileified != NULL) PerlMem_free(fileified); + if (vmsname != NULL) + PerlMem_free(vmsname); return FALSE; } @@ -11010,16 +11591,16 @@ Perl_cando_by_name_int /* find out the space required for the profile */ _ckvmssts(sys$create_user_profile(&usrdsc,&usrprolst,0,0, - &usrprodsc.dsc$w_length,0)); + &usrprodsc.dsc$w_length,&profile_context)); /* allocate space for the profile and get it filled in */ usrprodsc.dsc$a_pointer = PerlMem_malloc(usrprodsc.dsc$w_length); if (usrprodsc.dsc$a_pointer == NULL) _ckvmssts(SS$_INSFMEM); _ckvmssts(sys$create_user_profile(&usrdsc,&usrprolst,0,usrprodsc.dsc$a_pointer, - &usrprodsc.dsc$w_length,0)); + &usrprodsc.dsc$w_length,&profile_context)); /* use the profile to check access to the file; free profile & analyze results */ - retsts = sys$check_access(&objtyp,&namdsc,0,armlst,0,0,0,&usrprodsc); + retsts = sys$check_access(&objtyp,&namdsc,0,armlst,&profile_context,0,0,&usrprodsc); PerlMem_free(usrprodsc.dsc$a_pointer); if (retsts == SS$_NOCALLPRIV) retsts = SS$_NOPRIV; /* not really 3rd party */ @@ -11038,17 +11619,23 @@ Perl_cando_by_name_int else set_errno(ENOENT); if (fileified != NULL) PerlMem_free(fileified); + if (vmsname != NULL) + PerlMem_free(vmsname); return FALSE; } if (retsts == SS$_NORMAL || retsts == SS$_ACCONFLICT) { if (fileified != NULL) PerlMem_free(fileified); + if (vmsname != NULL) + PerlMem_free(vmsname); return TRUE; } _ckvmssts(retsts); if (fileified != NULL) PerlMem_free(fileified); + if (vmsname != NULL) + PerlMem_free(vmsname); return FALSE; /* Should never get here */ } @@ -11187,6 +11774,16 @@ Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag) * * If we are in Posix filespec mode, accept the filename as is. */ + + +#if __CRTL_VER >= 70300000 && !defined(__VAX) + /* The CRTL stat() falls down hard on multi-dot filenames in unix format unless + * DECC$EFS_CHARSET is in effect, so temporarily enable it if it isn't already. + */ + if (!decc_efs_charset) + decc$feature_set_value(decc$feature_get_index("DECC$EFS_CHARSET"),1,1); +#endif + #if __CRTL_VER >= 80200000 && !defined(__VAX) if (decc_posix_compliant_pathnames == 0) { #endif @@ -11204,6 +11801,27 @@ Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag) retval = lstat(temp_fspec,(stat_t *) statbufp); save_spec = temp_fspec; } +/* + * In debugging, on 8.3 Alpha, I found a case where stat was returning a + * file not found error for a directory named foo:[bar.t] or /foo/bar/t + * and lstat was working correctly for the same file. + * The only syntax that was working for stat was "foo:[bar]t.dir". + * + * Other directories with the same syntax worked fine. + * So work around the problem when it shows up here. + */ + if (retval) { + int save_errno = errno; + if (do_tovmsspec(fspec, temp_fspec, 0, NULL) != NULL) { + if (do_fileify_dirspec(temp_fspec, fileified, 0, NULL) != NULL) { + retval = stat(fileified, (stat_t *) statbufp); + save_spec = fileified; + } + } + /* Restore the errno value if third stat does not succeed */ + if (retval != 0) + errno = save_errno; + } #if __CRTL_VER >= 80200000 && !defined(__VAX) } else { if (lstat_flag == 0) @@ -11213,6 +11831,13 @@ Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag) save_spec = temp_fspec; } #endif + +#if __CRTL_VER >= 70300000 && !defined(__VAX) + /* As you were... */ + if (!decc_efs_charset) + decc$feature_set_value(decc$feature_get_index("DECC$EFS_CHARSET"),1,0); +#endif + if (!retval) { char * cptr; cptr = do_rmsexpand @@ -12071,7 +12696,8 @@ Perl_vms_start_glob #ifdef HAS_SYMLINK static char * -mp_do_vms_realpath(pTHX_ const char *filespec, char * rslt_spec, const int *utf8_fl); +mp_do_vms_realpath(pTHX_ const char *filespec, char * rslt_spec, + const int *utf8_fl); void vms_realpath_fromperl(pTHX_ CV *cv) @@ -12184,7 +12810,8 @@ char *realpath(const char *file_name, char * resolved_name, ...); * on OpenVMS. */ static char * -mp_do_vms_realpath(pTHX_ const char *filespec, char *outbuf, int *utf8_fl) +mp_do_vms_realpath(pTHX_ const char *filespec, char *outbuf, + const int *utf8_fl) { return realpath(filespec, outbuf); } @@ -12319,7 +12946,7 @@ static int set_features vms_debug_on_exception = 0; } - /* Create VTF-7 filenames from UNICODE instead of UTF-8 */ + /* Create VTF-7 filenames from Unicode instead of UTF-8 */ vms_vtf7_filenames = 0; status = sys_trnlnm("PERL_VMS_VTF7_FILENAMES", val_str, sizeof(val_str)); if ($VMS_STATUS_SUCCESS(status)) { @@ -12329,6 +12956,18 @@ static int set_features vms_vtf7_filenames = 0; } + + /* unlink all versions on unlink() or rename() */ + vms_vtf7_filenames = 0; + status = sys_trnlnm + ("PERL_VMS_UNLINK_ALL_VERSIONS", val_str, sizeof(val_str)); + if ($VMS_STATUS_SUCCESS(status)) { + if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) + vms_unlink_all_versions = 1; + else + vms_unlink_all_versions = 0; + } + /* Dectect running under GNV Bash or other UNIX like shell */ #if __CRTL_VER >= 70300000 && !defined(__VAX) gnv_unix_shell = 0; @@ -12342,6 +12981,7 @@ static int set_features set_feature_default("DECC$FILENAME_UNIX_REPORT", 1); set_feature_default("DECC$READDIR_DROPDOTNOTYPE", 1); set_feature_default("DECC$DISABLE_POSIX_ROOT", 0); + vms_unlink_all_versions = 1; } else gnv_unix_shell = 0;