Skip to content

Commit 43a34f9

Browse files
committed
patch 7.4.1114
Problem: delete() does not work well with symbolic links. Solution: Recognize symbolik links.
1 parent 4119cf8 commit 43a34f9

7 files changed

Lines changed: 105 additions & 6 deletions

File tree

runtime/doc/eval.txt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2755,13 +2755,14 @@ deepcopy({expr}[, {noref}]) *deepcopy()* *E698*
27552755

27562756
delete({fname} [, {flags}]) *delete()*
27572757
Without {flags} or with {flags} empty: Deletes the file by the
2758-
name {fname}.
2758+
name {fname}. This also works when {fname} is a symbolic link.
27592759

27602760
When {flags} is "d": Deletes the directory by the name
2761-
{fname}. This fails when {fname} is not empty.
2761+
{fname}. This fails when directory {fname} is not empty.
27622762

27632763
When {flags} is "rf": Deletes the directory by the name
2764-
{fname} and everything in it, recursively. Be careful!
2764+
{fname} and everything in it, recursively. BE CAREFUL!
2765+
A symbolic link itself is deleted, not what it points to.
27652766

27662767
The result is a Number, which is 0 if the delete operation was
27672768
successful and -1 when the deletion failed or partly failed.

src/eval.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10418,7 +10418,7 @@ f_delete(argvars, rettv)
1041810418
/* delete an empty directory */
1041910419
rettv->vval.v_number = mch_rmdir(name) == 0 ? 0 : -1;
1042010420
else if (STRCMP(flags, "rf") == 0)
10421-
/* delete an directory recursively */
10421+
/* delete a directory recursively */
1042210422
rettv->vval.v_number = delete_recursive(name);
1042310423
else
1042410424
EMSG2(_(e_invexpr2), flags);

src/fileio.c

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7294,7 +7294,19 @@ delete_recursive(char_u *name)
72947294
int i;
72957295
char_u *exp;
72967296

7297-
if (mch_isdir(name))
7297+
/* A symbolic link to a directory itself is deleted, not the directory it
7298+
* points to. */
7299+
if (
7300+
# if defined(WIN32)
7301+
mch_isdir(name) && !mch_is_symbolic_link(name)
7302+
# else
7303+
# ifdef UNIX
7304+
mch_isrealdir(name)
7305+
# else
7306+
mch_isdir(name)
7307+
# endif
7308+
# endif
7309+
)
72987310
{
72997311
vim_snprintf((char *)NameBuff, MAXPATHL, "%s/*", name);
73007312
exp = vim_strsave(NameBuff);

src/os_unix.c

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2994,7 +2994,7 @@ mch_hide(name)
29942994
}
29952995

29962996
/*
2997-
* return TRUE if "name" is a directory
2997+
* return TRUE if "name" is a directory or a symlink to a directory
29982998
* return FALSE if "name" is not a directory
29992999
* return FALSE for error
30003000
*/
@@ -3015,6 +3015,28 @@ mch_isdir(name)
30153015
#endif
30163016
}
30173017

3018+
/*
3019+
* return TRUE if "name" is a directory, NOT a symlink to a directory
3020+
* return FALSE if "name" is not a directory
3021+
* return FALSE for error
3022+
*/
3023+
int
3024+
mch_isrealdir(name)
3025+
char_u *name;
3026+
{
3027+
struct stat statb;
3028+
3029+
if (*name == NUL) /* Some stat()s don't flag "" as an error. */
3030+
return FALSE;
3031+
if (lstat((char *)name, &statb))
3032+
return FALSE;
3033+
#ifdef _POSIX_SOURCE
3034+
return (S_ISDIR(statb.st_mode) ? TRUE : FALSE);
3035+
#else
3036+
return ((statb.st_mode & S_IFMT) == S_IFDIR ? TRUE : FALSE);
3037+
#endif
3038+
}
3039+
30183040
static int executable_file __ARGS((char_u *name));
30193041

30203042
/*

src/proto/os_unix.pro

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ void mch_set_acl __ARGS((char_u *fname, vim_acl_T aclent));
4141
void mch_free_acl __ARGS((vim_acl_T aclent));
4242
void mch_hide __ARGS((char_u *name));
4343
int mch_isdir __ARGS((char_u *name));
44+
int mch_isrealdir __ARGS((char_u *name));
4445
int mch_can_exe __ARGS((char_u *name, char_u **path, int use_path));
4546
int mch_nodetype __ARGS((char_u *name));
4647
void mch_early_init __ARGS((void));

src/testdir/test_delete.vim

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,64 @@ func Test_recursive_delete()
3434
call assert_false(isdirectory('Xdir1'))
3535
call assert_equal(-1, delete('Xdir1', 'd'))
3636
endfunc
37+
38+
func Test_symlink_delete()
39+
if !has('unix')
40+
return
41+
endif
42+
split Xfile
43+
call setline(1, ['a', 'b'])
44+
wq
45+
silent !ln -s Xfile Xlink
46+
" Delete the link, not the file
47+
call assert_equal(0, delete('Xlink'))
48+
call assert_equal(-1, delete('Xlink'))
49+
call assert_equal(0, delete('Xfile'))
50+
endfunc
51+
52+
func Test_symlink_dir_delete()
53+
if !has('unix')
54+
return
55+
endif
56+
call mkdir('Xdir1')
57+
silent !ln -s Xdir1 Xlink
58+
call assert_true(isdirectory('Xdir1'))
59+
call assert_true(isdirectory('Xlink'))
60+
" Delete the link, not the directory
61+
call assert_equal(0, delete('Xlink'))
62+
call assert_equal(-1, delete('Xlink'))
63+
call assert_equal(0, delete('Xdir1', 'd'))
64+
endfunc
65+
66+
func Test_symlink_recursive_delete()
67+
if !has('unix')
68+
return
69+
endif
70+
call mkdir('Xdir3')
71+
call mkdir('Xdir3/subdir')
72+
call mkdir('Xdir4')
73+
split Xdir3/Xfile
74+
call setline(1, ['a', 'b'])
75+
w
76+
w Xdir3/subdir/Xfile
77+
w Xdir4/Xfile
78+
close
79+
silent !ln -s ../Xdir4 Xdir3/Xlink
80+
81+
call assert_true(isdirectory('Xdir3'))
82+
call assert_equal(['a', 'b'], readfile('Xdir3/Xfile'))
83+
call assert_true(isdirectory('Xdir3/subdir'))
84+
call assert_equal(['a', 'b'], readfile('Xdir3/subdir/Xfile'))
85+
call assert_true(isdirectory('Xdir4'))
86+
call assert_true(isdirectory('Xdir3/Xlink'))
87+
call assert_equal(['a', 'b'], readfile('Xdir4/Xfile'))
88+
89+
call assert_equal(0, delete('Xdir3', 'rf'))
90+
call assert_false(isdirectory('Xdir3'))
91+
call assert_equal(-1, delete('Xdir3', 'd'))
92+
" symlink is deleted, not the directory it points to
93+
call assert_true(isdirectory('Xdir4'))
94+
call assert_equal(['a', 'b'], readfile('Xdir4/Xfile'))
95+
call assert_equal(0, delete('Xdir4/Xfile'))
96+
call assert_equal(0, delete('Xdir4', 'd'))
97+
endfunc

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -741,6 +741,8 @@ static char *(features[]) =
741741

742742
static int included_patches[] =
743743
{ /* Add new patch number below this line */
744+
/**/
745+
1114,
744746
/**/
745747
1113,
746748
/**/

0 commit comments

Comments
 (0)