changeset 7615:228ff048db20 v7.4.1107

commit https://github.com/vim/vim/commit/da440d21a6b94d7f525fa7be9b1417c78dd9aa4c Author: Bram Moolenaar <Bram@vim.org> Date: Sat Jan 16 21:27:23 2016 +0100 patch 7.4.1107 Problem: Vim can create a directory but not delete it. Solution: Add an argument to delete() to make it possible to delete a directory, also recursively.
author Christian Brabandt <cb@256bit.org>
date Sat, 16 Jan 2016 21:30:05 +0100
parents 427b35579737
children 054887dabb63
files runtime/doc/eval.txt src/eval.c src/fileio.c src/proto/fileio.pro src/testdir/test_alot.vim src/testdir/test_delete.vim src/version.c
diffstat 7 files changed, 135 insertions(+), 28 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -1,4 +1,4 @@
-*eval.txt*	For Vim version 7.4.  Last change: 2016 Jan 15
+*eval.txt*	For Vim version 7.4.  Last change: 2016 Jan 16
 
 
 		  VIM REFERENCE MANUAL	  by Bram Moolenaar
@@ -919,6 +919,11 @@ just above, except that indexes out of r
 Using expr8[expr1] or expr8[expr1a : expr1b] on a |Funcref| results in an
 error.
 
+Watch out for confusion between a namespace and a variable followed by a colon
+for a sublist: >
+	mylist[n:]     " uses variable n
+	mylist[s:]     " uses namespace s:, error!
+
 
 expr8.name		entry in a |Dictionary|		*expr-entry*
 
@@ -1794,7 +1799,7 @@ cursor( {lnum}, {col} [, {off}])
 				Number	move cursor to {lnum}, {col}, {off}
 cursor( {list})			Number	move cursor to position in {list}
 deepcopy( {expr} [, {noref}])	any	make a full copy of {expr}
-delete( {fname})		Number	delete file {fname}
+delete( {fname} [, {flags}])	Number	delete the file or directory {fname}
 did_filetype()			Number	TRUE if FileType autocommand event used
 diff_filler( {lnum})		Number	diff filler lines about {lnum}
 diff_hlID( {lnum}, {col})	Number	diff highlighting at {lnum}/{col}
@@ -2748,10 +2753,19 @@ deepcopy({expr}[, {noref}])				*deepcopy
 		{noref} set to 1 will fail.
 		Also see |copy()|.
 
-delete({fname})							*delete()*
-		Deletes the file by the name {fname}.  The result is a Number,
-		which is 0 if the file was deleted successfully, and non-zero
-		when the deletion failed.
+delete({fname} [, {flags}])					*delete()*
+		Without {flags} or with {flags} empty: Deletes the file by the
+		name {fname}.
+
+		When {flags} is "d": Deletes the directory by the name
+		{fname}.  This fails when {fname} is not empty.
+		
+		When {flags} is "rf": Deletes the directory by the name
+		{fname} and everything in it, recursively.  Be careful!
+		
+		The result is a Number, which is 0 if the delete operation was
+		successful and -1 when the deletion failed or partly failed.
+
 		Use |remove()| to delete an item from a |List|.
 		To delete a line from the buffer use |:delete|.  Use |:exe|
 		when the line number is in a variable.
--- a/src/eval.c
+++ b/src/eval.c
@@ -8131,7 +8131,7 @@ static struct fst
     {"cscope_connection",0,3, f_cscope_connection},
     {"cursor",		1, 3, f_cursor},
     {"deepcopy",	1, 2, f_deepcopy},
-    {"delete",		1, 1, f_delete},
+    {"delete",		1, 2, f_delete},
     {"did_filetype",	0, 0, f_did_filetype},
     {"diff_filler",	1, 1, f_diff_filler},
     {"diff_hlID",	2, 2, f_diff_hlID},
@@ -10391,10 +10391,37 @@ f_delete(argvars, rettv)
     typval_T	*argvars;
     typval_T	*rettv;
 {
+    char_u	nbuf[NUMBUFLEN];
+    char_u	*name;
+    char_u	*flags;
+
+    rettv->vval.v_number = -1;
     if (check_restricted() || check_secure())
-	rettv->vval.v_number = -1;
-    else
-	rettv->vval.v_number = mch_remove(get_tv_string(&argvars[0]));
+	return;
+
+    name = get_tv_string(&argvars[0]);
+    if (name == NULL || *name == NUL)
+    {
+	EMSG(_(e_invarg));
+	return;
+    }
+
+    if (argvars[1].v_type != VAR_UNKNOWN)
+	flags = get_tv_string_buf(&argvars[1], nbuf);
+    else
+	flags = (char_u *)"";
+
+    if (*flags == NUL)
+	/* delete a file */
+	rettv->vval.v_number = mch_remove(name) == 0 ? 0 : -1;
+    else if (STRCMP(flags, "d") == 0)
+	/* delete an empty directory */
+	rettv->vval.v_number = mch_rmdir(name) == 0 ? 0 : -1;
+    else if (STRCMP(flags, "rf") == 0)
+	/* delete an directory recursively */
+	rettv->vval.v_number = delete_recursive(name);
+    else
+	EMSG2(_(e_invexpr2), flags);
 }
 
 /*
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -7280,6 +7280,46 @@ write_lnum_adjust(offset)
 	curbuf->b_no_eol_lnum += offset;
 }
 
+#if defined(TEMPDIRNAMES) || defined(FEAT_EVAL) || defined(PROTO)
+/*
+ * Delete "name" and everything in it, recursively.
+ * return 0 for succes, -1 if some file was not deleted.
+ */
+    int
+delete_recursive(char_u *name)
+{
+    int result = 0;
+    char_u	**files;
+    int		file_count;
+    int		i;
+    char_u	*exp;
+
+    if (mch_isdir(name))
+    {
+	vim_snprintf((char *)NameBuff, MAXPATHL, "%s/*", name);
+	exp = vim_strsave(NameBuff);
+	if (exp == NULL)
+	    return -1;
+	if (gen_expand_wildcards(1, &exp, &file_count, &files,
+					      EW_DIR|EW_FILE|EW_SILENT) == OK)
+	{
+	    for (i = 0; i < file_count; ++i)
+		if (delete_recursive(files[i]) != 0)
+		    result = -1;
+	    FreeWild(file_count, files);
+	}
+	else
+	    result = -1;
+	vim_free(exp);
+	(void)mch_rmdir(name);
+    }
+    else
+	result = mch_remove(name) == 0 ? 0 : -1;
+
+    return result;
+}
+#endif
+
 #if defined(TEMPDIRNAMES) || defined(PROTO)
 static long	temp_count = 0;		/* Temp filename counter. */
 
@@ -7289,30 +7329,16 @@ static long	temp_count = 0;		/* Temp fil
     void
 vim_deltempdir()
 {
-    char_u	**files;
-    int		file_count;
-    int		i;
-
     if (vim_tempdir != NULL)
     {
-	sprintf((char *)NameBuff, "%s*", vim_tempdir);
-	if (gen_expand_wildcards(1, &NameBuff, &file_count, &files,
-					      EW_DIR|EW_FILE|EW_SILENT) == OK)
-	{
-	    for (i = 0; i < file_count; ++i)
-		mch_remove(files[i]);
-	    FreeWild(file_count, files);
-	}
-	gettail(NameBuff)[-1] = NUL;
-	(void)mch_rmdir(NameBuff);
-
+	/* remove the trailing path separator */
+	gettail(vim_tempdir)[-1] = NUL;
+	delete_recursive(vim_tempdir);
 	vim_free(vim_tempdir);
 	vim_tempdir = NULL;
     }
 }
-#endif
-
-#ifdef TEMPDIRNAMES
+
 /*
  * Directory "tempdir" was created.  Expand this name to a full path and put
  * it in "vim_tempdir".  This avoids that using ":cd" would confuse us.
--- a/src/proto/fileio.pro
+++ b/src/proto/fileio.pro
@@ -22,6 +22,7 @@ int buf_check_timestamp __ARGS((buf_T *b
 void buf_reload __ARGS((buf_T *buf, int orig_mode));
 void buf_store_time __ARGS((buf_T *buf, struct stat *st, char_u *fname));
 void write_lnum_adjust __ARGS((linenr_T offset));
+int delete_recursive __ARGS((char_u *name));
 void vim_deltempdir __ARGS((void));
 char_u *vim_tempname __ARGS((int extra_char, int keep));
 void forward_slash __ARGS((char_u *fname));
--- a/src/testdir/test_alot.vim
+++ b/src/testdir/test_alot.vim
@@ -3,6 +3,7 @@
 
 source test_backspace_opt.vim
 source test_cursor_func.vim
+source test_delete.vim
 source test_lispwords.vim
 source test_menu.vim
 source test_searchpos.vim
new file mode 100644
--- /dev/null
+++ b/src/testdir/test_delete.vim
@@ -0,0 +1,36 @@
+" Test for delete().
+
+func Test_file_delete()
+  split Xfile
+  call setline(1, ['a', 'b'])
+  wq
+  call assert_equal(['a', 'b'], readfile('Xfile'))
+  call assert_equal(0, delete('Xfile'))
+  call assert_fails('call readfile("Xfile")', 'E484:')
+  call assert_equal(-1, delete('Xfile'))
+endfunc
+
+func Test_dir_delete()
+  call mkdir('Xdir1')
+  call assert_true(isdirectory('Xdir1'))
+  call assert_equal(0, delete('Xdir1', 'd'))
+  call assert_false(isdirectory('Xdir1'))
+  call assert_equal(-1, delete('Xdir1', 'd'))
+endfunc
+
+func Test_recursive_delete()
+  call mkdir('Xdir1')
+  call mkdir('Xdir1/subdir')
+  split Xdir1/Xfile
+  call setline(1, ['a', 'b'])
+  w
+  w Xdir1/subdir/Xfile
+  close
+  call assert_true(isdirectory('Xdir1'))
+  call assert_equal(['a', 'b'], readfile('Xdir1/Xfile'))
+  call assert_true(isdirectory('Xdir1/subdir'))
+  call assert_equal(['a', 'b'], readfile('Xdir1/subdir/Xfile'))
+  call assert_equal(0, delete('Xdir1', 'rf'))
+  call assert_false(isdirectory('Xdir1'))
+  call assert_equal(-1, delete('Xdir1', '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 */
 /**/
+    1107,
+/**/
     1106,
 /**/
     1105,