From 43a34f9f74fdce462fa250baab620264c28b6165 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 17 Jan 2016 15:56:34 +0100 Subject: patch 7.4.1114 Problem: delete() does not work well with symbolic links. Solution: Recognize symbolik links. --- runtime/doc/eval.txt | 7 +++--- src/eval.c | 2 +- src/fileio.c | 14 ++++++++++- src/os_unix.c | 24 +++++++++++++++++- src/proto/os_unix.pro | 1 + src/testdir/test_delete.vim | 61 +++++++++++++++++++++++++++++++++++++++++++++ src/version.c | 2 ++ 7 files changed, 105 insertions(+), 6 deletions(-) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 6edd3f5411..fb6a851447 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2755,13 +2755,14 @@ deepcopy({expr}[, {noref}]) *deepcopy()* *E698* 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. diff --git a/src/eval.c b/src/eval.c index fb2cbe79e5..aec1ea9873 100644 --- 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); diff --git a/src/fileio.c b/src/fileio.c index 6dbdea21e6..f809eb5bc3 100644 --- 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); diff --git a/src/os_unix.c b/src/os_unix.c index 36196369cb..d2e1c79bd9 100644 --- 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)); /* diff --git a/src/proto/os_unix.pro b/src/proto/os_unix.pro index 89dff7791d..be6eb6f103 100644 --- a/src/proto/os_unix.pro +++ b/src/proto/os_unix.pro @@ -41,6 +41,7 @@ void mch_set_acl __ARGS((char_u *fname, vim_acl_T aclent)); 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)); diff --git a/src/testdir/test_delete.vim b/src/testdir/test_delete.vim index 6e2b9c86bb..13c87a152f 100644 --- 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 diff --git a/src/version.c b/src/version.c index 8d9fda8153..571cb682cf 100644 --- a/src/version.c +++ b/src/version.c @@ -741,6 +741,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1114, /**/ 1113, /**/ -- cgit v1.2.3