Patch 9.0.0411
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.
Files: runtime/doc/builtin.txt, src/filepath.c, src/userfunc.c,
src/proto/userfunc.pro, src/testdir/test_autochdir.vim,
src/testdir/test_autocmd.vim, src/testdir/test_eval_stuff.vim,
src/testdir/test_writefile.vim
*** ../vim-9.0.0410/runtime/doc/builtin.txt 2022-09-04 15:40:31.812188115
+0100
--- runtime/doc/builtin.txt 2022-09-07 20:49:25.656972204 +0100
***************
*** 6229,6236 ****
mkdir({name} [, {path} [, {prot}]])
Create directory {name}.
! If {path} is "p" then intermediate directories are created as
! necessary. Otherwise it must be "".
If {prot} is given it is used to set the protection bits of
the new directory. The default is 0o755 (rwxr-xr-x: r/w for
--- 6239,6264 ----
mkdir({name} [, {path} [, {prot}]])
Create directory {name}.
! If {path} contains "p" then intermediate directories are
! created as necessary. Otherwise it must be "".
!
! If {path} contains "D" then {name} is deleted at the end of
! the current function, as with: >
! defer delete({name}, 'd')
! <
! If {path} contains "R" then {name} is deleted recursively at
! the end of the current function, as with: >
! defer delete({name}, 'rf')
! < Note that when {name} has more than one part and "p" is used
! some directories may already exist. Only the first one that
! is created and what it contains is scheduled to be deleted.
! E.g. when using: >
! call mkdir('subdir/tmp/autoload', 'pR')
! < and "subdir" already exists then "subdir/tmp" will be
! scheduled for deletion, like with: >
! defer delete('subdir/tmp', 'rf')
! < Note that if scheduling the defer fails the directory is not
! deleted. This should only happen when out of memory.
If {prot} is given it is used to set the protection bits of
the new directory. The default is 0o755 (rwxr-xr-x: r/w for
*** ../vim-9.0.0410/src/filepath.c 2022-09-04 15:40:31.812188115 +0100
--- src/filepath.c 2022-09-07 20:51:23.008694815 +0100
***************
*** 1428,1437 ****
/*
* Create the directory in which "dir" is located, and higher levels when
* needed.
* Return OK or FAIL.
*/
static int
! mkdir_recurse(char_u *dir, int prot)
{
char_u *p;
char_u *updir;
--- 1428,1439 ----
/*
* 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, char_u **created)
{
char_u *p;
char_u *updir;
***************
*** 1449,1456 ****
return FAIL;
if (mch_isdir(updir))
r = OK;
! else if (mkdir_recurse(updir, prot) == OK)
r = vim_mkdir_emsg(updir, prot);
vim_free(updir);
return r;
}
--- 1451,1462 ----
return FAIL;
if (mch_isdir(updir))
r = 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,1469 ****
--- 1470,1478 ----
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,1498 ****
if (argvars[1].v_type != VAR_UNKNOWN)
{
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)
{
if (mch_isdir(dir))
{
--- 1495,1515 ----
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;
}
! 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,1509 ****
rettv->vval.v_number = OK;
return;
}
! mkdir_recurse(dir, prot);
}
}
rettv->vval.v_number = vim_mkdir_emsg(dir, prot);
}
/*
--- 1517,1549 ----
rettv->vval.v_number = OK;
return;
}
! 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,2310 ****
if (fname == NULL)
return;
! if (defer && !in_def_function() && get_current_funccal() == NULL)
! {
! semsg(_(e_str_not_inside_function), "defer");
return;
- }
// Always open the file in binary mode, library functions have a mind of
// their own about CR-LF conversion.
--- 2340,2347 ----
if (fname == NULL)
return;
! 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,2329 ****
tv.v_type = VAR_STRING;
tv.v_lock = 0;
! tv.vval.v_string = vim_strsave(fname);
if (tv.vval.v_string == NULL
|| add_defer((char_u *)"delete", 1, &tv) == FAIL)
{
--- 2360,2366 ----
tv.v_type = VAR_STRING;
tv.v_lock = 0;
! tv.vval.v_string = FullName_save(fname, FALSE);
if (tv.vval.v_string == NULL
|| add_defer((char_u *)"delete", 1, &tv) == FAIL)
{
*** ../vim-9.0.0410/src/userfunc.c 2022-09-07 17:28:05.849865176 +0100
--- src/userfunc.c 2022-09-07 20:43:14.877880964 +0100
***************
*** 5650,5655 ****
--- 5650,5670 ----
}
/*
+ * 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
*** ../vim-9.0.0410/src/proto/userfunc.pro 2022-09-07 17:28:05.849865176
+0100
--- src/proto/userfunc.pro 2022-09-07 20:45:11.961587496 +0100
***************
*** 60,65 ****
--- 60,66 ----
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);
*** ../vim-9.0.0410/src/testdir/test_autochdir.vim 2022-08-29
11:02:55.227279554 +0100
--- src/testdir/test_autochdir.vim 2022-09-07 20:59:10.559616186 +0100
***************
*** 28,36 ****
func Test_set_filename_other_window()
let cwd = getcwd()
call test_autochdir()
! call mkdir('Xa')
! call mkdir('Xb')
! call mkdir('Xc')
try
args Xa/aaa.txt Xb/bbb.txt
set acd
--- 28,36 ----
func Test_set_filename_other_window()
let cwd = getcwd()
call test_autochdir()
! call mkdir('Xa', 'R')
! call mkdir('Xb', 'R')
! call mkdir('Xc', 'R')
try
args Xa/aaa.txt Xb/bbb.txt
set acd
***************
*** 45,53 ****
bwipe! aaa.txt
bwipe! bbb.txt
bwipe! ccc.txt
- call delete('Xa', 'rf')
- call delete('Xb', 'rf')
- call delete('Xc', 'rf')
endtry
endfunc
--- 45,50 ----
***************
*** 56,62 ****
set acd
call test_autochdir()
! call mkdir('XacdDir')
let winid = win_getid()
new XacdDir/file
call assert_match('testdir.XacdDir$', getcwd())
--- 53,59 ----
set acd
call test_autochdir()
! call mkdir('XacdDir', 'R')
let winid = win_getid()
new XacdDir/file
call assert_match('testdir.XacdDir$', getcwd())
***************
*** 68,74 ****
bwipe!
set noacd
call chdir(cwd)
- call delete('XacdDir', 'rf')
endfunc
func Test_verbose_pwd()
--- 65,70 ----
***************
*** 78,84 ****
edit global.txt
call assert_match('\[global\].*testdir$', execute('verbose pwd'))
! call mkdir('Xautodir')
split Xautodir/local.txt
lcd Xautodir
call assert_match('\[window\].*testdir[/\\]Xautodir', execute('verbose
pwd'))
--- 74,80 ----
edit global.txt
call assert_match('\[global\].*testdir$', execute('verbose pwd'))
! call mkdir('Xautodir', 'R')
split Xautodir/local.txt
lcd Xautodir
call assert_match('\[window\].*testdir[/\\]Xautodir', execute('verbose
pwd'))
***************
*** 112,118 ****
bwipe!
call chdir(cwd)
- call delete('Xautodir', 'rf')
endfunc
func Test_multibyte()
--- 108,113 ----
*** ../vim-9.0.0410/src/testdir/test_autocmd.vim 2022-09-04
21:28:15.192614789 +0100
--- src/testdir/test_autocmd.vim 2022-09-07 21:01:38.499280388 +0100
***************
*** 707,720 ****
call assert_equal('++', g:val)
" Also get BufEnter when editing a directory
! call mkdir('Xbufenterdir')
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
--- 707,719 ----
call assert_equal('++', g:val)
" Also get BufEnter when editing a directory
! 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
au! BufEnter
" Editing a "nofile" buffer doesn't read the file but does trigger BufEnter
***************
*** 1902,1912 ****
new
file Xbufwritecmd
set buftype=acwrite
! call mkdir('Xbufwritecmd')
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!
--- 1901,1910 ----
new
file Xbufwritecmd
set buftype=acwrite
! call mkdir('Xbufwritecmd', 'D')
write
" BufWriteCmd should be triggered even if a directory has the same name
call assert_equal(1, g:written)
unlet g:written
au! BufWriteCmd
bwipe!
***************
*** 2710,2716 ****
endfunc
func Test_autocmd_in_try_block()
! call mkdir('Xintrydir')
au BufEnter * let g:fname = expand('%')
try
edit Xintrydir/
--- 2708,2714 ----
endfunc
func Test_autocmd_in_try_block()
! call mkdir('Xintrydir', 'R')
au BufEnter * let g:fname = expand('%')
try
edit Xintrydir/
***************
*** 2719,2725 ****
unlet g:fname
au! BufEnter
- call delete('Xintrydir', 'rf')
endfunc
func Test_autocmd_SafeState()
--- 2717,2722 ----
*** ../vim-9.0.0410/src/testdir/test_eval_stuff.vim 2022-09-02
21:55:45.499049444 +0100
--- src/testdir/test_eval_stuff.vim 2022-09-07 21:25:00.072137930 +0100
***************
*** 44,49 ****
--- 44,107 ----
call assert_fails('call mkdir("abc", [], [])', 'E745:')
endfunc
+ func DoMkdirDel(name)
+ call mkdir(a:name, 'pD')
+ call assert_true(isdirectory(a:name))
+ endfunc
+
+ func DoMkdirDelAddFile(name)
+ call mkdir(a:name, 'pD')
+ call assert_true(isdirectory(a:name))
+ call writefile(['text'], a:name .. '/file')
+ endfunc
+
+ func DoMkdirDelRec(name)
+ call mkdir(a:name, 'pR')
+ call assert_true(isdirectory(a:name))
+ endfunc
+
+ func DoMkdirDelRecAddFile(name)
+ call mkdir(a:name, 'pR')
+ call assert_true(isdirectory(a:name))
+ call writefile(['text'], a:name .. '/file')
+ endfunc
+
+ func Test_mkdir_defer_del()
+ " Xtopdir/tmp is created thus deleted, not Xtopdir itself
+ call mkdir('Xtopdir', 'R')
+ call DoMkdirDel('Xtopdir/tmp')
+ call assert_true(isdirectory('Xtopdir'))
+ call assert_false(isdirectory('Xtopdir/tmp'))
+
+ " Deletion fails because "tmp" contains "sub"
+ call DoMkdirDel('Xtopdir/tmp/sub')
+ call assert_true(isdirectory('Xtopdir'))
+ call assert_true(isdirectory('Xtopdir/tmp'))
+ call delete('Xtopdir/tmp', 'rf')
+
+ " Deletion fails because "tmp" contains "file"
+ call DoMkdirDelAddFile('Xtopdir/tmp')
+ call assert_true(isdirectory('Xtopdir'))
+ call assert_true(isdirectory('Xtopdir/tmp'))
+ call assert_true(filereadable('Xtopdir/tmp/file'))
+ call delete('Xtopdir/tmp', 'rf')
+
+ " Xtopdir/tmp is created thus deleted, not Xtopdir itself
+ call DoMkdirDelRec('Xtopdir/tmp')
+ call assert_true(isdirectory('Xtopdir'))
+ call assert_false(isdirectory('Xtopdir/tmp'))
+
+ " Deletion works even though "tmp" contains "sub"
+ call DoMkdirDelRec('Xtopdir/tmp/sub')
+ call assert_true(isdirectory('Xtopdir'))
+ call assert_false(isdirectory('Xtopdir/tmp'))
+
+ " Deletion works even though "tmp" contains "file"
+ call DoMkdirDelRecAddFile('Xtopdir/tmp')
+ call assert_true(isdirectory('Xtopdir'))
+ call assert_false(isdirectory('Xtopdir/tmp'))
+ endfunc
+
func Test_line_continuation()
let array = [5,
"\ ignore this
*** ../vim-9.0.0410/src/testdir/test_writefile.vim 2022-09-04
21:29:41.556536007 +0100
--- src/testdir/test_writefile.vim 2022-09-07 21:10:21.038104305 +0100
***************
*** 950,955 ****
--- 950,968 ----
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
*** ../vim-9.0.0410/src/version.c 2022-09-07 20:01:13.323141838 +0100
--- src/version.c 2022-09-07 21:28:56.075611230 +0100
***************
*** 705,706 ****
--- 705,708 ----
{ /* Add new patch number below this line */
+ /**/
+ 411,
/**/
--
How To Keep A Healthy Level Of Insanity:
1. At lunch time, sit in your parked car with sunglasses on and point
a hair dryer at passing cars. See if they slow down.
/// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net \\\
/// \\\
\\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
\\\ help me help AIDS victims -- http://ICCF-Holland.org ///
--
--
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php
---
You received this message because you are subscribed to the Google Groups
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/vim_dev/20220907203344.D776C1C04B1%40moolenaar.net.