changeset 7629:befbed72da87 v7.4.1114

commit https://github.com/vim/vim/commit/43a34f9f74fdce462fa250baab620264c28b6165 Author: Bram Moolenaar <Bram@vim.org> Date: Sun Jan 17 15:56:34 2016 +0100 patch 7.4.1114 Problem: delete() does not work well with symbolic links. Solution: Recognize symbolik links.
author Christian Brabandt <cb@256bit.org>
date Sun, 17 Jan 2016 16:00:05 +0100
parents d74db83fa956
children 29deea838e21
files runtime/doc/eval.txt src/eval.c src/fileio.c src/os_unix.c src/proto/os_unix.pro src/testdir/test_delete.vim src/version.c
diffstat 7 files changed, 105 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -2755,13 +2755,14 @@ deepcopy({expr}[, {noref}])				*deepcopy
 
 delete({fname} [, {flags}])					*delete()*
 		Without {flags} or with {flags} empty: Deletes the file by the
-		name {fname}.
+		name {fname}.  This also works when {fname} is a symbolic link.
 
 		When {flags} is "d": Deletes the directory by the name
-		{fname}.  This fails when {fname} is not empty.
+		{fname}.  This fails when directory {fname} is not empty.
 		
 		When {flags} is "rf": Deletes the directory by the name
-		{fname} and everything in it, recursively.  Be careful!
+		{fname} and everything in it, recursively.  BE CAREFUL!
+		A symbolic link itself is deleted, not what it points to.
 		
 		The result is a Number, which is 0 if the delete operation was
 		successful and -1 when the deletion failed or partly failed.
--- a/src/eval.c
+++ b/src/eval.c
@@ -10418,7 +10418,7 @@ f_delete(argvars, rettv)
 	/* delete an empty directory */
 	rettv->vval.v_number = mch_rmdir(name) == 0 ? 0 : -1;
     else if (STRCMP(flags, "rf") == 0)
-	/* delete an directory recursively */
+	/* delete a directory recursively */
 	rettv->vval.v_number = delete_recursive(name);
     else
 	EMSG2(_(e_invexpr2), flags);
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -7294,7 +7294,19 @@ delete_recursive(char_u *name)
     int		i;
     char_u	*exp;
 
-    if (mch_isdir(name))
+    /* A symbolic link to a directory itself is deleted, not the directory it
+     * points to. */
+    if (
+# if defined(WIN32)
+	 mch_isdir(name) && !mch_is_symbolic_link(name)
+# else
+#  ifdef UNIX
+	 mch_isrealdir(name)
+#  else
+	 mch_isdir(name)
+#  endif
+# endif
+	    )
     {
 	vim_snprintf((char *)NameBuff, MAXPATHL, "%s/*", name);
 	exp = vim_strsave(NameBuff);
--- a/src/os_unix.c
+++ b/src/os_unix.c
@@ -2994,7 +2994,7 @@ mch_hide(name)
 }
 
 /*
- * return TRUE if "name" is a directory
+ * return TRUE if "name" is a directory or a symlink to a directory
  * return FALSE if "name" is not a directory
  * return FALSE for error
  */
@@ -3015,6 +3015,28 @@ mch_isdir(name)
 #endif
 }
 
+/*
+ * return TRUE if "name" is a directory, NOT a symlink to a directory
+ * return FALSE if "name" is not a directory
+ * return FALSE for error
+ */
+    int
+mch_isrealdir(name)
+    char_u *name;
+{
+    struct stat statb;
+
+    if (*name == NUL)	    /* Some stat()s don't flag "" as an error. */
+	return FALSE;
+    if (lstat((char *)name, &statb))
+	return FALSE;
+#ifdef _POSIX_SOURCE
+    return (S_ISDIR(statb.st_mode) ? TRUE : FALSE);
+#else
+    return ((statb.st_mode & S_IFMT) == S_IFDIR ? TRUE : FALSE);
+#endif
+}
+
 static int executable_file __ARGS((char_u *name));
 
 /*
--- a/src/proto/os_unix.pro
+++ b/src/proto/os_unix.pro
@@ -41,6 +41,7 @@ void mch_set_acl __ARGS((char_u *fname, 
 void mch_free_acl __ARGS((vim_acl_T aclent));
 void mch_hide __ARGS((char_u *name));
 int mch_isdir __ARGS((char_u *name));
+int mch_isrealdir __ARGS((char_u *name));
 int mch_can_exe __ARGS((char_u *name, char_u **path, int use_path));
 int mch_nodetype __ARGS((char_u *name));
 void mch_early_init __ARGS((void));
--- a/src/testdir/test_delete.vim
+++ b/src/testdir/test_delete.vim
@@ -34,3 +34,64 @@ func Test_recursive_delete()
   call assert_false(isdirectory('Xdir1'))
   call assert_equal(-1, delete('Xdir1', 'd'))
 endfunc
+
+func Test_symlink_delete()
+  if !has('unix')
+    return
+  endif
+  split Xfile
+  call setline(1, ['a', 'b'])
+  wq
+  silent !ln -s Xfile Xlink
+  " Delete the link, not the file
+  call assert_equal(0, delete('Xlink'))
+  call assert_equal(-1, delete('Xlink'))
+  call assert_equal(0, delete('Xfile'))
+endfunc
+
+func Test_symlink_dir_delete()
+  if !has('unix')
+    return
+  endif
+  call mkdir('Xdir1')
+  silent !ln -s Xdir1 Xlink
+  call assert_true(isdirectory('Xdir1'))
+  call assert_true(isdirectory('Xlink'))
+  " Delete the link, not the directory
+  call assert_equal(0, delete('Xlink'))
+  call assert_equal(-1, delete('Xlink'))
+  call assert_equal(0, delete('Xdir1', 'd'))
+endfunc
+
+func Test_symlink_recursive_delete()
+  if !has('unix')
+    return
+  endif
+  call mkdir('Xdir3')
+  call mkdir('Xdir3/subdir')
+  call mkdir('Xdir4')
+  split Xdir3/Xfile
+  call setline(1, ['a', 'b'])
+  w
+  w Xdir3/subdir/Xfile
+  w Xdir4/Xfile
+  close
+  silent !ln -s ../Xdir4 Xdir3/Xlink
+
+  call assert_true(isdirectory('Xdir3'))
+  call assert_equal(['a', 'b'], readfile('Xdir3/Xfile'))
+  call assert_true(isdirectory('Xdir3/subdir'))
+  call assert_equal(['a', 'b'], readfile('Xdir3/subdir/Xfile'))
+  call assert_true(isdirectory('Xdir4'))
+  call assert_true(isdirectory('Xdir3/Xlink'))
+  call assert_equal(['a', 'b'], readfile('Xdir4/Xfile'))
+
+  call assert_equal(0, delete('Xdir3', 'rf'))
+  call assert_false(isdirectory('Xdir3'))
+  call assert_equal(-1, delete('Xdir3', 'd'))
+  " symlink is deleted, not the directory it points to
+  call assert_true(isdirectory('Xdir4'))
+  call assert_equal(['a', 'b'], readfile('Xdir4/Xfile'))
+  call assert_equal(0, delete('Xdir4/Xfile'))
+  call assert_equal(0, delete('Xdir4', 'd'))
+endfunc
--- a/src/version.c
+++ b/src/version.c
@@ -742,6 +742,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1114,
+/**/
     1113,
 /**/
     1112,