patch 9.0.0411: only created files can be cleaned up with one call

Problem:    Only created files can be cleaned up with one call.
Solution:   Add flags to mkdir() to delete with a deferred function.
            Expand the writefile() name to a full path to handle changing
            directory.
diff --git a/src/filepath.c b/src/filepath.c
index c72e428..373a784 100644
--- a/src/filepath.c
+++ b/src/filepath.c
@@ -1428,10 +1428,12 @@
 /*
  * Create the directory in which "dir" is located, and higher levels when
  * needed.
+ * Set "created" to the full name of the first created directory.  It will be
+ * NULL until that happens.
  * Return OK or FAIL.
  */
     static int
-mkdir_recurse(char_u *dir, int prot)
+mkdir_recurse(char_u *dir, int prot, char_u **created)
 {
     char_u	*p;
     char_u	*updir;
@@ -1449,8 +1451,12 @@
 	return FAIL;
     if (mch_isdir(updir))
 	r = OK;
-    else if (mkdir_recurse(updir, prot) == OK)
+    else if (mkdir_recurse(updir, prot, created) == OK)
+    {
 	r = vim_mkdir_emsg(updir, prot);
+	if (r == OK && created != NULL && *created == NULL)
+	    *created = FullName_save(updir, FALSE);
+    }
     vim_free(updir);
     return r;
 }
@@ -1464,6 +1470,9 @@
     char_u	*dir;
     char_u	buf[NUMBUFLEN];
     int		prot = 0755;
+    int		defer = FALSE;
+    int		defer_recurse = FALSE;
+    char_u	*created = NULL;
 
     rettv->vval.v_number = FAIL;
     if (check_restricted() || check_secure())
@@ -1486,13 +1495,21 @@
 
     if (argvars[1].v_type != VAR_UNKNOWN)
     {
+	char_u *arg2;
+
 	if (argvars[2].v_type != VAR_UNKNOWN)
 	{
 	    prot = (int)tv_get_number_chk(&argvars[2], NULL);
 	    if (prot == -1)
 		return;
 	}
-	if (STRCMP(tv_get_string(&argvars[1]), "p") == 0)
+	arg2 = tv_get_string(&argvars[1]);
+	defer = vim_strchr(arg2, 'D') != NULL;
+	defer_recurse = vim_strchr(arg2, 'R') != NULL;
+	if ((defer || defer_recurse) && !can_add_defer())
+	    return;
+
+	if (vim_strchr(arg2, 'p') != NULL)
 	{
 	    if (mch_isdir(dir))
 	    {
@@ -1500,10 +1517,33 @@
 		rettv->vval.v_number = OK;
 		return;
 	    }
-	    mkdir_recurse(dir, prot);
+	    mkdir_recurse(dir, prot, defer || defer_recurse ? &created : NULL);
 	}
     }
     rettv->vval.v_number = vim_mkdir_emsg(dir, prot);
+
+    // Handle "D" and "R": deferred deletion of the created directory.
+    if (rettv->vval.v_number == OK
+				&& created == NULL && (defer || defer_recurse))
+	created = FullName_save(dir, FALSE);
+    if (created != NULL)
+    {
+	typval_T tv[2];
+
+	tv[0].v_type = VAR_STRING;
+	tv[0].v_lock = 0;
+	tv[0].vval.v_string = created;
+	tv[1].v_type = VAR_STRING;
+	tv[1].v_lock = 0;
+	tv[1].vval.v_string = vim_strsave(
+				       (char_u *)(defer_recurse ? "rf" : "d"));
+	if (tv[0].vval.v_string == NULL || tv[1].vval.v_string == NULL
+		|| add_defer((char_u *)"delete", 2, tv) == FAIL)
+	{
+	    vim_free(tv[0].vval.v_string);
+	    vim_free(tv[1].vval.v_string);
+	}
+    }
 }
 
 /*
@@ -2300,11 +2340,8 @@
     if (fname == NULL)
 	return;
 
-    if (defer && !in_def_function() && get_current_funccal() == NULL)
-    {
-	semsg(_(e_str_not_inside_function), "defer");
+    if (defer && !can_add_defer())
 	return;
-    }
 
     // Always open the file in binary mode, library functions have a mind of
     // their own about CR-LF conversion.
@@ -2323,7 +2360,7 @@
 
 	    tv.v_type = VAR_STRING;
 	    tv.v_lock = 0;
-	    tv.vval.v_string = vim_strsave(fname);
+	    tv.vval.v_string = FullName_save(fname, FALSE);
 	    if (tv.vval.v_string == NULL
 		    || add_defer((char_u *)"delete", 1, &tv) == FAIL)
 	    {
diff --git a/src/proto/userfunc.pro b/src/proto/userfunc.pro
index 555830e..e5543f3 100644
--- a/src/proto/userfunc.pro
+++ b/src/proto/userfunc.pro
@@ -60,6 +60,7 @@
 void func_ref(char_u *name);
 void func_ptr_ref(ufunc_T *fp);
 void ex_return(exarg_T *eap);
+int can_add_defer(void);
 int add_defer(char_u *name, int argcount_arg, typval_T *argvars);
 void invoke_all_defer(void);
 void ex_call(exarg_T *eap);
diff --git a/src/testdir/test_autochdir.vim b/src/testdir/test_autochdir.vim
index 332de8f..eb40253 100644
--- a/src/testdir/test_autochdir.vim
+++ b/src/testdir/test_autochdir.vim
@@ -28,9 +28,9 @@
 func Test_set_filename_other_window()
   let cwd = getcwd()
   call test_autochdir()
-  call mkdir('Xa')
-  call mkdir('Xb')
-  call mkdir('Xc')
+  call mkdir('Xa', 'R')
+  call mkdir('Xb', 'R')
+  call mkdir('Xc', 'R')
   try
     args Xa/aaa.txt Xb/bbb.txt
     set acd
@@ -45,9 +45,6 @@
     bwipe! aaa.txt
     bwipe! bbb.txt
     bwipe! ccc.txt
-    call delete('Xa', 'rf')
-    call delete('Xb', 'rf')
-    call delete('Xc', 'rf')
   endtry
 endfunc
 
@@ -56,7 +53,7 @@
   set acd
   call test_autochdir()
 
-  call mkdir('XacdDir')
+  call mkdir('XacdDir', 'R')
   let winid = win_getid()
   new XacdDir/file
   call assert_match('testdir.XacdDir$', getcwd())
@@ -68,7 +65,6 @@
   bwipe!
   set noacd
   call chdir(cwd)
-  call delete('XacdDir', 'rf')
 endfunc
 
 func Test_verbose_pwd()
@@ -78,7 +74,7 @@
   edit global.txt
   call assert_match('\[global\].*testdir$', execute('verbose pwd'))
 
-  call mkdir('Xautodir')
+  call mkdir('Xautodir', 'R')
   split Xautodir/local.txt
   lcd Xautodir
   call assert_match('\[window\].*testdir[/\\]Xautodir', execute('verbose pwd'))
@@ -112,7 +108,6 @@
 
   bwipe!
   call chdir(cwd)
-  call delete('Xautodir', 'rf')
 endfunc
 
 func Test_multibyte()
diff --git a/src/testdir/test_autocmd.vim b/src/testdir/test_autocmd.vim
index 028e3d2..669089d 100644
--- a/src/testdir/test_autocmd.vim
+++ b/src/testdir/test_autocmd.vim
@@ -707,14 +707,13 @@
   call assert_equal('++', g:val)
 
   " Also get BufEnter when editing a directory
-  call mkdir('Xbufenterdir')
+  call mkdir('Xbufenterdir', 'D')
   split Xbufenterdir
   call assert_equal('+++', g:val)
 
   " On MS-Windows we can't edit the directory, make sure we wipe the right
   " buffer.
   bwipe! Xbufenterdir
-  call delete('Xbufenterdir', 'd')
   au! BufEnter
 
   " Editing a "nofile" buffer doesn't read the file but does trigger BufEnter
@@ -1902,11 +1901,10 @@
   new
   file Xbufwritecmd
   set buftype=acwrite
-  call mkdir('Xbufwritecmd')
+  call mkdir('Xbufwritecmd', 'D')
   write
   " BufWriteCmd should be triggered even if a directory has the same name
   call assert_equal(1, g:written)
-  call delete('Xbufwritecmd', 'd')
   unlet g:written
   au! BufWriteCmd
   bwipe!
@@ -2710,7 +2708,7 @@
 endfunc
 
 func Test_autocmd_in_try_block()
-  call mkdir('Xintrydir')
+  call mkdir('Xintrydir', 'R')
   au BufEnter * let g:fname = expand('%')
   try
     edit Xintrydir/
@@ -2719,7 +2717,6 @@
 
   unlet g:fname
   au! BufEnter
-  call delete('Xintrydir', 'rf')
 endfunc
 
 func Test_autocmd_SafeState()
diff --git a/src/testdir/test_eval_stuff.vim b/src/testdir/test_eval_stuff.vim
index 3669cfd..0081d89 100644
--- a/src/testdir/test_eval_stuff.vim
+++ b/src/testdir/test_eval_stuff.vim
Binary files differ
diff --git a/src/testdir/test_writefile.vim b/src/testdir/test_writefile.vim
index dddc5c9..f2b8aba 100644
--- a/src/testdir/test_writefile.vim
+++ b/src/testdir/test_writefile.vim
@@ -950,6 +950,19 @@
   call assert_equal('', glob('XdefdeferDelete'))
 endfunc
 
+func DoWriteFile()
+  call writefile(['text'], 'Xthefile', 'D')
+  cd ..
+endfunc
+
+func Test_write_defer_delete_chdir()
+  let dir = getcwd()
+  call DoWriteFile()
+  call assert_notequal(dir, getcwd())
+  call chdir(dir)
+  call assert_equal('', glob('Xthefile'))
+endfunc
+
 " Check that buffer is written before triggering QuitPre
 func Test_wq_quitpre_autocommand()
   edit Xsomefile
diff --git a/src/userfunc.c b/src/userfunc.c
index fea5b79..7b6034a 100644
--- a/src/userfunc.c
+++ b/src/userfunc.c
@@ -5650,6 +5650,21 @@
 }
 
 /*
+ * Return TRUE if currently inside a function call.
+ * Give an error message and return FALSE when not.
+ */
+    int
+can_add_defer(void)
+{
+    if (!in_def_function() && get_current_funccal() == NULL)
+    {
+	semsg(_(e_str_not_inside_function), "defer");
+	return FALSE;
+    }
+    return TRUE;
+}
+
+/*
  * Add a deferred call for "name" with arguments "argvars[argcount]".
  * Consumes "argvars[]".
  * Caller must check that in_def_function() returns TRUE or current_funccal is
diff --git a/src/version.c b/src/version.c
index 0d6cc89..78a1ae0 100644
--- a/src/version.c
+++ b/src/version.c
@@ -704,6 +704,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    411,
+/**/
     410,
 /**/
     409,