# HG changeset patch # User Bram Moolenaar # Date 1662583504 -7200 # Node ID 0fe61fa4e5d1cf0bce02333f9a251c9f73c53af4 # Parent 58dd17222e566de4bf068c16762229a1aef3f885 patch 9.0.0411: only created files can be cleaned up with one call Commit: https://github.com/vim/vim/commit/6f14da15ac900589f2f413d77898b9bff3b31ece Author: Bram Moolenaar Date: Wed Sep 7 21:30:44 2022 +0100 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/runtime/doc/builtin.txt b/runtime/doc/builtin.txt --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -6239,8 +6239,26 @@ min({expr}) Return the minimum value of mkdir({name} [, {path} [, {prot}]]) Create directory {name}. - If {path} is "p" then intermediate directories are created as - necessary. Otherwise it must be "". + 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 diff --git a/src/filepath.c b/src/filepath.c --- a/src/filepath.c +++ b/src/filepath.c @@ -1428,10 +1428,12 @@ f_isabsolutepath(typval_T *argvars, typv /* * 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 @@ mkdir_recurse(char_u *dir, int prot) 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 @@ f_mkdir(typval_T *argvars, typval_T *ret 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 @@ f_mkdir(typval_T *argvars, typval_T *ret 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 @@ f_mkdir(typval_T *argvars, typval_T *ret 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 @@ f_writefile(typval_T *argvars, typval_T 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 @@ f_writefile(typval_T *argvars, typval_T 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 --- a/src/proto/userfunc.pro +++ b/src/proto/userfunc.pro @@ -60,6 +60,7 @@ void func_ptr_unref(ufunc_T *fp); 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 --- a/src/testdir/test_autochdir.vim +++ b/src/testdir/test_autochdir.vim @@ -28,9 +28,9 @@ endfunc 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 @@ func Test_set_filename_other_window() 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 @@ func Test_acd_win_execute() 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 @@ func Test_acd_win_execute() bwipe! set noacd call chdir(cwd) - call delete('XacdDir', 'rf') endfunc func Test_verbose_pwd() @@ -78,7 +74,7 @@ func Test_verbose_pwd() 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 @@ func Test_verbose_pwd() 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 --- a/src/testdir/test_autocmd.vim +++ b/src/testdir/test_autocmd.vim @@ -707,14 +707,13 @@ func Test_BufEnter() 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 @@ func Test_BufWriteCmd() 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 @@ func Test_throw_in_BufWritePre() 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 @@ func Test_autocmd_in_try_block() 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 3669cfd40b24aad092ae59b3cf49bd8aef15d5fb..0081d89a52a488d644f570d314bce2336251c0b0 GIT binary patch literal 26019 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