From 4ba3d059bfcd5b08b0831e7d71de26284db6dcb9 Mon Sep 17 00:00:00 2001 From: Marco Hinz Date: Wed, 12 May 2021 00:44:39 +0200 Subject: [PATCH] Revert "vim-patch:8.1.1378: delete() can not handle a file name that looks like a pattern (#12784)" This reverts commit 4be0e92db01a502863ac4bb26dd0fee16d833145. Unfortunately, that commit stalled the Windows builds on GHA and likely requires other patches to work properly. --- src/nvim/eval/funcs.c | 62 +++++++++- src/nvim/fileio.c | 107 +++--------------- src/nvim/testdir/test_arglist.vim | 18 ++- src/nvim/testdir/test_command_count.vim | 2 - src/nvim/testdir/test_functions.vim | 19 ---- src/nvim/testdir/test_quickfix.vim | 10 +- src/nvim/testdir/test_registers.vim | 3 - .../legacy/011_autocommands_spec.lua | 2 +- test/functional/legacy/delete_spec.lua | 78 +++++++++++++ test/functional/legacy/packadd_spec.lua | 1 - 10 files changed, 168 insertions(+), 134 deletions(-) diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 911b3c2c2f..9d5707e649 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -6661,6 +6661,37 @@ static void f_range(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } +// Evaluate "expr" for readdir(). +static varnumber_T readdir_checkitem(typval_T *expr, const char *name) +{ + typval_T save_val; + typval_T rettv; + typval_T argv[2]; + varnumber_T retval = 0; + bool error = false; + + prepare_vimvar(VV_VAL, &save_val); + set_vim_var_string(VV_VAL, name, -1); + argv[0].v_type = VAR_STRING; + argv[0].vval.v_string = (char_u *)name; + + if (eval_expr_typval(expr, argv, 1, &rettv) == FAIL) { + goto theend; + } + + retval = tv_get_number_chk(&rettv, &error); + if (error) { + retval = -1; + } + + tv_clear(&rettv); + +theend: + set_vim_var_string(VV_VAL, NULL, 0); + restore_vimvar(VV_VAL, &save_val); + return retval; +} + // "readdir()" function static void f_readdir(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -6672,14 +6703,43 @@ static void f_readdir(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_list_alloc_ret(rettv, kListLenUnknown); path = tv_get_string(&argvars[0]); expr = &argvars[1]; + ga_init(&ga, (int)sizeof(char *), 20); if (!os_scandir(&dir, path)) { smsg(_(e_notopen), path); } else { - readdir_core(&ga, &dir, expr, true); + for (;;) { + bool ignore; + + path = os_scandir_next(&dir); + if (path == NULL) { + break; + } + + ignore = (path[0] == '.' + && (path[1] == NUL || (path[1] == '.' && path[2] == NUL))); + if (!ignore && expr->v_type != VAR_UNKNOWN) { + varnumber_T r = readdir_checkitem(expr, path); + + if (r < 0) { + break; + } + if (r == 0) { + ignore = true; + } + } + + if (!ignore) { + ga_grow(&ga, 1); + ((char **)ga.ga_data)[ga.ga_len++] = xstrdup(path); + } + } + + os_closedir(&dir); } if (rettv->vval.v_list != NULL && ga.ga_len > 0) { + sort_strings((char_u **)ga.ga_data, ga.ga_len); for (int i = 0; i < ga.ga_len; i++) { path = ((const char **)ga.ga_data)[i]; tv_list_append_string(rettv->vval.v_list, path, -1); diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 2037ba5f19..29c29a2884 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -5204,113 +5204,38 @@ static void vim_maketempdir(void) (void)umask(umask_save); } -// Evaluate "expr" for readdir(). -static varnumber_T readdir_checkitem(typval_T *expr, const char *name) -{ - typval_T save_val; - typval_T rettv; - typval_T argv[2]; - varnumber_T retval = 0; - bool error = false; - - prepare_vimvar(VV_VAL, &save_val); - set_vim_var_string(VV_VAL, name, -1); - argv[0].v_type = VAR_STRING; - argv[0].vval.v_string = (char_u *)name; - - if (eval_expr_typval(expr, argv, 1, &rettv) == FAIL) { - goto theend; - } - - retval = tv_get_number_chk(&rettv, &error); - if (error) { - retval = -1; - } - - tv_clear(&rettv); - -theend: - set_vim_var_string(VV_VAL, NULL, 0); - restore_vimvar(VV_VAL, &save_val); - return retval; -} - -/// Core part of "readdir()" function. -/// Retrieve the list of files/directories of "dirp" into "gap". -void readdir_core( - garray_T *gap, - Directory *dirp, - typval_T *expr, - bool is_checkitem) -{ - ga_init(gap, (int)sizeof(char *), 20); - - for (;;) { - bool ignore; - - const char *path = os_scandir_next(dirp); - if (path == NULL) { - break; - } - - ignore = (path[0] == '.' - && (path[1] == NUL || (path[1] == '.' && path[2] == NUL))); - if (!ignore && expr != NULL && expr->v_type != VAR_UNKNOWN - && is_checkitem) { - varnumber_T r = readdir_checkitem(expr, path); - - if (r < 0) { - break; - } - if (r == 0) { - ignore = true; - } - } - - if (!ignore) { - ga_grow(gap, 1); - ((char **)gap->ga_data)[gap->ga_len++] = xstrdup(path); - } - } - - if (gap->ga_len > 0) { - sort_strings((char_u **)gap->ga_data, gap->ga_len); - } - - os_closedir(dirp); -} - /// Delete "name" and everything in it, recursively. /// @param name The path which should be deleted. /// @return 0 for success, -1 if some file was not deleted. int delete_recursive(const char *name) { int result = 0; - char *exp = (char *)vim_strsave((char_u *)name); - Directory dir; - if (os_isrealdir(name) && os_scandir(&dir, exp)) { - garray_T ga; + if (os_isrealdir(name)) { + snprintf((char *)NameBuff, MAXPATHL, "%s/*", name); // NOLINT - readdir_core(&ga, &dir, NULL, false); - - for (int i = 0; i < ga.ga_len; i++) { - vim_snprintf((char *)NameBuff, MAXPATHL, "%s/%s", exp, - ((char_u **)ga.ga_data)[i]); - if (delete_recursive((const char *)NameBuff) != 0) { - result = -1; + char_u **files; + int file_count; + char_u *exp = vim_strsave(NameBuff); + if (gen_expand_wildcards(1, &exp, &file_count, &files, + EW_DIR | EW_FILE | EW_SILENT | EW_ALLLINKS + | EW_DODOT | EW_EMPTYOK) == OK) { + for (int i = 0; i < file_count; i++) { + if (delete_recursive((const char *)files[i]) != 0) { + result = -1; + } } + FreeWild(file_count, files); + } else { + result = -1; } - ga_clear_strings(&ga); - + xfree(exp); os_rmdir(name); } else { result = os_remove(name) == 0 ? 0 : -1; } - xfree(exp); - return result; } diff --git a/src/nvim/testdir/test_arglist.vim b/src/nvim/testdir/test_arglist.vim index 08e578a226..a1ef8325ec 100644 --- a/src/nvim/testdir/test_arglist.vim +++ b/src/nvim/testdir/test_arglist.vim @@ -26,8 +26,6 @@ func Test_argidx() endfunc func Test_argadd() - call Reset_arglist() - %argdelete argadd a b c call assert_equal(0, argidx()) @@ -105,11 +103,6 @@ func Init_abc() next endfunc -func Reset_arglist() - cd - args a | %argd -endfunc - func Assert_argc(l) call assert_equal(len(a:l), argc()) let i = 0 @@ -122,7 +115,8 @@ endfunc " Test for [count]argument and [count]argdelete commands " Ported from the test_argument_count.in test script func Test_argument() - call Reset_arglist() + " Clean the argument list + arga a | %argd let save_hidden = &hidden set hidden @@ -250,7 +244,8 @@ endfunc " Test for 0argadd and 0argedit " Ported from the test_argument_0count.in test script func Test_zero_argadd() - call Reset_arglist() + " Clean the argument list + arga a | %argd arga a b c d 2argu @@ -277,6 +272,10 @@ func Test_zero_argadd() call assert_equal('file with spaces', expand('%')) endfunc +func Reset_arglist() + args a | %argd +endfunc + " Test for argc() func Test_argc() call Reset_arglist() @@ -409,7 +408,6 @@ endfunc " Test for the :argdelete command func Test_argdelete() call Reset_arglist() - args aa a aaa b bb argdelete a* call assert_equal(['b', 'bb'], argv()) diff --git a/src/nvim/testdir/test_command_count.vim b/src/nvim/testdir/test_command_count.vim index 36ff4cd1bb..55b230373f 100644 --- a/src/nvim/testdir/test_command_count.vim +++ b/src/nvim/testdir/test_command_count.vim @@ -103,8 +103,6 @@ endfunc func Test_command_count_2() silent! %argd - cd - arga a b c d call assert_fails('5argu', 'E16:') diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index c280aedffb..93f567b3a0 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -1402,10 +1402,6 @@ func Test_bufadd_bufload() endfunc func Test_readdir() - if isdirectory('Xdir') - call delete('Xdir', 'rf') - endif - call mkdir('Xdir') call writefile([], 'Xdir/foo.txt') call writefile([], 'Xdir/bar.txt') @@ -1460,19 +1456,4 @@ func Test_default_arg_value() call assert_equal('msg', HasDefault()) endfunc -func Test_delete_rf() - call mkdir('Xdir') - call writefile([], 'Xdir/foo.txt') - call writefile([], 'Xdir/bar.txt') - call mkdir('Xdir/[a-1]') " issue #696 - call writefile([], 'Xdir/[a-1]/foo.txt') - call writefile([], 'Xdir/[a-1]/bar.txt') - call assert_true(filereadable('Xdir/foo.txt')) - call assert_true(filereadable('Xdir/[a-1]/foo.txt')) - - call assert_equal(0, delete('Xdir', 'rf')) - call assert_false(filereadable('Xdir/foo.txt')) - call assert_false(filereadable('Xdir/[a-1]/foo.txt')) -endfunc - " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index 16b6a5f464..da949f5940 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -1011,9 +1011,10 @@ endfunc " Tests for %D and %X errorformat options func Test_efm_dirstack() " Create the directory stack and files - call mkdir('dir1/a', 'p') - call mkdir('dir1/a/b', 'p') - call mkdir('dir1/c', 'p') + call mkdir('dir1') + call mkdir('dir1/a') + call mkdir('dir1/a/b') + call mkdir('dir1/c') call mkdir('dir2') let lines =<< trim [DATA] @@ -3484,9 +3485,6 @@ func Xqftick_tests(cchar) \ {'filename' : 'F7', 'lnum' : 11, 'text' : 'L11'}], 'r') call assert_equal(2, g:Xgetlist({'changedtick' : 0}).changedtick) - if isdirectory("Xone") - call delete("Xone", 'rf') - endif call writefile(["F8:80:L80", "F8:81:L81"], "Xone") Xfile Xone call assert_equal(1, g:Xgetlist({'changedtick' : 0}).changedtick) diff --git a/src/nvim/testdir/test_registers.vim b/src/nvim/testdir/test_registers.vim index bcadb84ced..53069b3d31 100644 --- a/src/nvim/testdir/test_registers.vim +++ b/src/nvim/testdir/test_registers.vim @@ -43,9 +43,6 @@ func Test_yank_shows_register() endfunc func Test_display_registers() - " Disable clipboard - let g:clipboard = {} - e file1 e file2 call setline(1, ['foo', 'bar']) diff --git a/test/functional/legacy/011_autocommands_spec.lua b/test/functional/legacy/011_autocommands_spec.lua index c9c004eec1..7cc31dc787 100644 --- a/test/functional/legacy/011_autocommands_spec.lua +++ b/test/functional/legacy/011_autocommands_spec.lua @@ -70,7 +70,7 @@ describe('file reading, writing and bufnew and filter autocommands', function() feed_command('let $GZIP = ""') --execute('au FileChangedShell * echo "caught FileChangedShell"') feed_command('set bin') - feed_command("au FileReadPost *.gz '[,']!GZIP= gzip -d") + feed_command("au FileReadPost *.gz '[,']!gzip -d") -- Read and decompress the testfile. feed_command('$r Xtestfile.gz') expect('\n'..text1) diff --git a/test/functional/legacy/delete_spec.lua b/test/functional/legacy/delete_spec.lua index 3d2c4a7d91..f2ced8942d 100644 --- a/test/functional/legacy/delete_spec.lua +++ b/test/functional/legacy/delete_spec.lua @@ -17,6 +17,33 @@ describe('Test for delete()', function() eq(-1, eval("delete('Xfile')")) end) + it('directory delete', function() + command("call mkdir('Xdir1')") + eq(1, eval("isdirectory('Xdir1')")) + eq(0, eval("delete('Xdir1', 'd')")) + eq(0, eval("isdirectory('Xdir1')")) + eq(-1, eval("delete('Xdir1', 'd')")) + end) + it('recursive delete', function() + command("call mkdir('Xdir1')") + command("call mkdir('Xdir1/subdir')") + command("call mkdir('Xdir1/empty')") + command('split Xdir1/Xfile') + command("call setline(1, ['a', 'b'])") + command('w') + command('w Xdir1/subdir/Xfile') + command('close') + + eq(1, eval("isdirectory('Xdir1')")) + eq(eval("['a', 'b']"), eval("readfile('Xdir1/Xfile')")) + eq(1, eval("isdirectory('Xdir1/subdir')")) + eq(eval("['a', 'b']"), eval("readfile('Xdir1/subdir/Xfile')")) + eq(1, eval("isdirectory('Xdir1/empty')")) + eq(0, eval("delete('Xdir1', 'rf')")) + eq(0, eval("isdirectory('Xdir1')")) + eq(-1, eval("delete('Xdir1', 'd')")) + end) + it('symlink delete', function() source([[ split Xfile @@ -36,4 +63,55 @@ describe('Test for delete()', function() eq(-1, eval("delete('Xlink')")) eq(0, eval("delete('Xfile')")) end) + + it('symlink directory delete', function() + command("call mkdir('Xdir1')") + if helpers.iswin() then + command("silent !mklink /j Xlink Xdir1") + else + command("silent !ln -s Xdir1 Xlink") + end + eq(1, eval("isdirectory('Xdir1')")) + eq(1, eval("isdirectory('Xlink')")) + -- Delete the link, not the directory + eq(0, eval("delete('Xlink')")) + eq(-1, eval("delete('Xlink')")) + eq(0, eval("delete('Xdir1', 'd')")) + end) + + it('symlink recursive delete', function() + source([[ + call mkdir('Xdir3') + call mkdir('Xdir3/subdir') + call mkdir('Xdir4') + split Xdir3/Xfile + call setline(1, ['a', 'b']) + w + w Xdir3/subdir/Xfile + w Xdir4/Xfile + close + if has('win32') + silent !mklink /j Xdir3\Xlink Xdir4 + else + silent !ln -s ../Xdir4 Xdir3/Xlink + endif + ]]) + + eq(1, eval("isdirectory('Xdir3')")) + eq(eval("['a', 'b']"), eval("readfile('Xdir3/Xfile')")) + eq(1, eval("isdirectory('Xdir3/subdir')")) + eq(eval("['a', 'b']"), eval("readfile('Xdir3/subdir/Xfile')")) + eq(1, eval("isdirectory('Xdir4')")) + eq(1, eval("isdirectory('Xdir3/Xlink')")) + eq(eval("['a', 'b']"), eval("readfile('Xdir4/Xfile')")) + + eq(0, eval("delete('Xdir3', 'rf')")) + eq(0, eval("isdirectory('Xdir3')")) + eq(-1, eval("delete('Xdir3', 'd')")) + -- symlink is deleted, not the directory it points to + eq(1, eval("isdirectory('Xdir4')")) + eq(eval("['a', 'b']"), eval("readfile('Xdir4/Xfile')")) + eq(0, eval("delete('Xdir4/Xfile')")) + eq(0, eval("delete('Xdir4', 'd')")) + end) end) diff --git a/test/functional/legacy/packadd_spec.lua b/test/functional/legacy/packadd_spec.lua index d92409059e..486a1fe471 100644 --- a/test/functional/legacy/packadd_spec.lua +++ b/test/functional/legacy/packadd_spec.lua @@ -20,7 +20,6 @@ describe('packadd', function() func SetUp() let s:topdir = expand(getcwd() . '/Xdir') - call delete(s:topdir, 'rf') exe 'set packpath=' . s:topdir let s:plugdir = expand(s:topdir . '/pack/mine/opt/mytest') endfunc