From 50a7517a6deb7d8eaa02bf718e273b7058066d89 Mon Sep 17 00:00:00 2001 From: Jurica Bradaric Date: Sun, 28 Feb 2016 10:32:46 +0100 Subject: [PATCH 1/9] vim-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. https://github.com/vim/vim/commit/da440d21a6b94d7f525fa7be9b1417c78dd9aa4c --- runtime/doc/eval.txt | 23 ++++++----- src/nvim/eval.c | 43 ++++++++++++++++---- src/nvim/tempfile.c | 55 +++++++++++++++++--------- src/nvim/version.c | 2 +- test/functional/legacy/delete_spec.lua | 41 +++++++++++++++++++ 5 files changed, 128 insertions(+), 36 deletions(-) create mode 100644 test/functional/legacy/delete_spec.lua diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 476ab71461..9437491623 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1,4 +1,4 @@ -*eval.txt* For Vim version 7.4. Last change: 2015 Sep 19 +*eval.txt* For Vim version 7.4. Last change: 2016 Jan 16 VIM REFERENCE MANUAL by Bram Moolenaar @@ -1830,7 +1830,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} dictwatcheradd( {dict}, {pattern}, {callback}) Start watching a dictionary dictwatcherdel( {dict}, {pattern}, {callback}) @@ -2770,13 +2770,18 @@ deepcopy({expr}[, {noref}]) *deepcopy()* *E698* {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. - 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. +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. dictwatcheradd({dict}, {pattern}, {callback}) *dictwatcheradd()* Adds a watcher to a dictionary. A dictionary watcher is diff --git a/src/nvim/eval.c b/src/nvim/eval.c index e9b1df6367..25201e400c 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -6702,7 +6702,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 }, { "dictwatcheradd", 3, 3, f_dictwatcheradd }, { "dictwatcherdel", 3, 3, f_dictwatcherdel }, { "did_filetype", 0, 0, f_did_filetype }, @@ -8374,15 +8374,42 @@ static void f_deepcopy(typval_T *argvars, typval_T *rettv) } } -/* - * "delete()" function - */ +// "delete()" function static void f_delete(typval_T *argvars, typval_T *rettv) { - if (check_restricted() || check_secure()) - rettv->vval.v_number = -1; - else - rettv->vval.v_number = os_remove((char *)get_tv_string(&argvars[0])); + char_u nbuf[NUMBUFLEN]; + char_u *name; + char_u *flags; + + rettv->vval.v_number = -1; + if (check_restricted() || check_secure()) { + 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 = os_remove((char *)name) == 0 ? 0 : -1; + } else if (STRCMP(flags, "d") == 0) { + // delete an empty directory + rettv->vval.v_number = os_rmdir((char *)name) == 0 ? 0 : -1; + } else if (STRCMP(flags, "rf") == 0) { + // delete a directory recursively + rettv->vval.v_number = delete_recursive(name); + } else { + EMSG2(_(e_invexpr2), flags); + } } // dictwatcheradd(dict, key, funcref) function diff --git a/src/nvim/tempfile.c b/src/nvim/tempfile.c index afe926b2ef..882dbe32f2 100644 --- a/src/nvim/tempfile.c +++ b/src/nvim/tempfile.c @@ -56,28 +56,47 @@ static void vim_maketempdir(void) } } +/// 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(char_u *name) +{ + int result = 0; + + if (os_isdir(name)) { + snprintf((char *)NameBuff, MAXPATHL, "%s/*", name); + + 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) == OK) { + for (int i = 0; i < file_count; i++) { + if (delete_recursive(files[i]) != 0) { + result = -1; + } + } + FreeWild(file_count, files); + } else { + result = -1; + } + + xfree(exp); + os_rmdir((char *)name); + } else { + result = os_remove((char *)name) == 0 ? 0 : -1; + } + + return result; +} + /// Delete the temp directory and all files it contains. void vim_deltempdir(void) { if (vim_tempdir != NULL) { - snprintf((char *)NameBuff, MAXPATHL, "%s*", vim_tempdir); - - char_u **files; - int file_count; - - // Note: We cannot just do `&NameBuff` because it is a statically - // sized array so `NameBuff == &NameBuff` according to C semantics. - char_u *buff_list[1] = {NameBuff}; - if (gen_expand_wildcards(1, buff_list, &file_count, &files, - EW_DIR|EW_FILE|EW_SILENT) == OK) { - for (int i = 0; i < file_count; ++i) { - os_remove((char *)files[i]); - } - FreeWild(file_count, files); - } - path_tail(NameBuff)[-1] = NUL; - os_rmdir((char *)NameBuff); - + // remove the trailing path separator + path_tail(vim_tempdir)[-1] = NUL; + delete_recursive(vim_tempdir); xfree(vim_tempdir); vim_tempdir = NULL; } diff --git a/src/nvim/version.c b/src/nvim/version.c index afd5639d30..2828442611 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -257,7 +257,7 @@ static int included_patches[] = { // 1110, // 1109 NA // 1108, - // 1107, + 1107, // 1106 NA 1105, // 1104 NA diff --git a/test/functional/legacy/delete_spec.lua b/test/functional/legacy/delete_spec.lua new file mode 100644 index 0000000000..01b06d606f --- /dev/null +++ b/test/functional/legacy/delete_spec.lua @@ -0,0 +1,41 @@ +local helpers = require('test.functional.helpers') +local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert +local eq, eval, execute, expect = helpers.eq, helpers.eval, helpers.execute, helpers.expect + +describe('Test for delete()', function() + before_each(clear) + + it('file delete', function() + execute('split Xfile') + execute("call setline(1, ['a', 'b'])") + execute('wq') + eq(eval("['a', 'b']"), eval("readfile('Xfile')")) + eq(0, eval("delete('Xfile')")) + eq(-1, eval("delete('Xfile')")) + end) + + it('directory delete', function() + execute("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() + execute("call mkdir('Xdir1')") + execute("call mkdir('Xdir1/subdir')") + execute('split Xdir1/Xfile') + execute("call setline(1, ['a', 'b'])") + execute('w') + execute('w Xdir1/subdir/Xfile') + execute('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(0, eval("delete('Xdir1', 'rf')")) + eq(0, eval("isdirectory('Xdir1')")) + eq(-1, eval("delete('Xdir1', 'd')")) + end) +end) From 88a735166b7ee1eadaf6d46be11804dc0e1a251a Mon Sep 17 00:00:00 2001 From: Jurica Bradaric Date: Sun, 28 Feb 2016 12:44:59 +0100 Subject: [PATCH 2/9] vim-patch:7.4.1114 Problem: delete() does not work well with symbolic links. Solution: Recognize symbolik links. https://github.com/vim/vim/commit/43a34f9f74fdce462fa250baab620264c28b6165 --- runtime/doc/eval.txt | 7 ++-- src/nvim/os/fs.c | 17 ++++++++ src/nvim/tempfile.c | 2 +- src/nvim/version.c | 2 +- test/functional/legacy/delete_spec.lua | 57 ++++++++++++++++++++++++++ 5 files changed, 80 insertions(+), 5 deletions(-) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 9437491623..fea9423df2 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2772,13 +2772,14 @@ deepcopy({expr}[, {noref}]) *deepcopy()* *E698* delete({fname} [, {flags}]) *delete()* Without {flags} or with {flags} empty: Deletes the file by the - name {fname}. + name {fname}. This also works when {fname} is a symbolic link. + A symbolic link itself is deleted, not what it points to. When {flags} is "d": Deletes the directory by the name - {fname}. This fails when {fname} is not empty. + {fname}. This fails when directory {fname} is not empty. When {flags} is "rf": Deletes the directory by the name - {fname} and everything in it, recursively. Be careful! + {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. diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index 34d8fde4f1..bc2d37764d 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -59,6 +59,23 @@ int os_dirname(char_u *buf, size_t len) return OK; } +/// Check if the given path is a directory and not a symlink to a directory. +/// @return `true` if `name` is a directory and NOT a symlink to a directory. +/// `false` if `name` is not a directory or if an error occurred. +bool os_isrealdir(const char_u *name) + FUNC_ATTR_NONNULL_ALL +{ + uv_fs_t request; + if (uv_fs_lstat(&fs_loop, &request, (char *)name, NULL) != kLibuvSuccess) { + return false; + } + if (S_ISLNK(request.statbuf.st_mode)) { + return false; + } else { + return S_ISDIR(request.statbuf.st_mode); + } +} + /// Check if the given path is a directory or not. /// /// @return `true` if `fname` is a directory. diff --git a/src/nvim/tempfile.c b/src/nvim/tempfile.c index 882dbe32f2..5b6268168d 100644 --- a/src/nvim/tempfile.c +++ b/src/nvim/tempfile.c @@ -63,7 +63,7 @@ int delete_recursive(char_u *name) { int result = 0; - if (os_isdir(name)) { + if (os_isrealdir(name)) { snprintf((char *)NameBuff, MAXPATHL, "%s/*", name); char_u **files; diff --git a/src/nvim/version.c b/src/nvim/version.c index 2828442611..235d74de76 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -250,7 +250,7 @@ static int included_patches[] = { // 1117, // 1116, // 1115 NA - // 1114, + 1114, 1113, 1112, // 1111, diff --git a/test/functional/legacy/delete_spec.lua b/test/functional/legacy/delete_spec.lua index 01b06d606f..b524af3338 100644 --- a/test/functional/legacy/delete_spec.lua +++ b/test/functional/legacy/delete_spec.lua @@ -1,5 +1,6 @@ local helpers = require('test.functional.helpers') local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert +local source = helpers.source local eq, eval, execute, expect = helpers.eq, helpers.eval, helpers.execute, helpers.expect describe('Test for delete()', function() @@ -38,4 +39,60 @@ describe('Test for delete()', function() eq(0, eval("isdirectory('Xdir1')")) eq(-1, eval("delete('Xdir1', 'd')")) end) + + it('symlink delete', function() + source([[ + split Xfile + call setline(1, ['a', 'b']) + wq + silent !ln -s Xfile Xlink + ]]) + -- Delete the link, not the file + eq(0, eval("delete('Xlink')")) + eq(-1, eval("delete('Xlink')")) + eq(0, eval("delete('Xfile')")) + end) + + it('symlink directory delete', function() + execute("call mkdir('Xdir1')") + execute("silent !ln -s Xdir1 Xlink") + 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 + silent !ln -s ../Xdir4 Xdir3/Xlink + ]]) + + 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) From 9e385404b3443f201f1092ba76bcdafd1f303976 Mon Sep 17 00:00:00 2001 From: Jurica Bradaric Date: Sun, 28 Feb 2016 12:59:13 +0100 Subject: [PATCH 3/9] vim-patch:7.4.1116 Problem: delete(x, 'rf') does not delete files starting with a dot. Solution: Also delete files starting with a dot. https://github.com/vim/vim/commit/b0967d587fc420fa02832533d4915c85d1a78c17 --- src/nvim/path.c | 8 +++++--- src/nvim/path.h | 1 + src/nvim/tempfile.c | 3 ++- src/nvim/version.c | 2 +- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/nvim/path.c b/src/nvim/path.c index aaf54bc5b4..8ba3fe3358 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -604,8 +604,8 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, starstar = true; // convert the file pattern to a regexp pattern - int starts_with_dot = (*s == '.'); - char_u *pat = file_pat_to_reg_pat(s, e, NULL, false); + int starts_with_dot = *s == '.' || (flags & EW_DODOT); + char_u *pat = file_pat_to_reg_pat(s, e, NULL, FALSE); if (pat == NULL) { xfree(buf); return 0; @@ -649,7 +649,9 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, char_u *name; scandir_next_with_dots(NULL /* initialize */); while((name = (char_u *) scandir_next_with_dots(&dir)) && name != NULL) { - if ((name[0] != '.' || starts_with_dot) + if ((name[0] != '.' || (starts_with_dot + && name[1] != NUL + && (name[1] != '.' || name[2] != NUL))) && ((regmatch.regprog != NULL && vim_regexec(®match, name, 0)) || ((flags & EW_NOTWILD) && fnamencmp(path + (s - buf), name, e - s) == 0))) { diff --git a/src/nvim/path.h b/src/nvim/path.h index eac367d0ac..9574d8131f 100644 --- a/src/nvim/path.h +++ b/src/nvim/path.h @@ -21,6 +21,7 @@ /* Note: mostly EW_NOTFOUND and EW_SILENT are mutually exclusive: EW_NOTFOUND * is used when executing commands and EW_SILENT for interactive expanding. */ #define EW_ALLLINKS 0x1000 // also links not pointing to existing file +#define EW_DODOT 0x4000 // also files starting with a dot /// Return value for the comparison of two files. Also @see path_full_compare. typedef enum file_comparison { diff --git a/src/nvim/tempfile.c b/src/nvim/tempfile.c index 5b6268168d..6566749f8a 100644 --- a/src/nvim/tempfile.c +++ b/src/nvim/tempfile.c @@ -70,7 +70,8 @@ int delete_recursive(char_u *name) int file_count; char_u *exp = vim_strsave(NameBuff); if (gen_expand_wildcards(1, &exp, &file_count, &files, - EW_DIR | EW_FILE | EW_SILENT) == OK) { + EW_DIR | EW_FILE | EW_SILENT + | EW_ALLLINKS | EW_DODOT) == OK) { for (int i = 0; i < file_count; i++) { if (delete_recursive(files[i]) != 0) { result = -1; diff --git a/src/nvim/version.c b/src/nvim/version.c index 235d74de76..75eb37c2c3 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -248,7 +248,7 @@ static int included_patches[] = { // 1119, // 1118, // 1117, - // 1116, + 1116, // 1115 NA 1114, 1113, From a252fca38e0e5bf8501b72c2a9cfd2acf1cf9fff Mon Sep 17 00:00:00 2001 From: Jurica Bradaric Date: Sun, 28 Feb 2016 13:12:12 +0100 Subject: [PATCH 4/9] Fix linter errors. --- src/nvim/path.c | 6 +++--- src/nvim/tempfile.c | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/nvim/path.c b/src/nvim/path.c index 8ba3fe3358..4b15afbfc1 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -605,7 +605,7 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, // convert the file pattern to a regexp pattern int starts_with_dot = *s == '.' || (flags & EW_DODOT); - char_u *pat = file_pat_to_reg_pat(s, e, NULL, FALSE); + char_u *pat = file_pat_to_reg_pat(s, e, NULL, false); if (pat == NULL) { xfree(buf); return 0; @@ -647,8 +647,8 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, if (os_file_is_readable(dirpath) && os_scandir(&dir, dirpath)) { // Find all matching entries. char_u *name; - scandir_next_with_dots(NULL /* initialize */); - while((name = (char_u *) scandir_next_with_dots(&dir)) && name != NULL) { + scandir_next_with_dots(NULL); // initialize + while ((name = (char_u *) scandir_next_with_dots(&dir)) && name != NULL) { if ((name[0] != '.' || (starts_with_dot && name[1] != NUL && (name[1] != '.' || name[2] != NUL))) diff --git a/src/nvim/tempfile.c b/src/nvim/tempfile.c index 6566749f8a..3b1a2ffca6 100644 --- a/src/nvim/tempfile.c +++ b/src/nvim/tempfile.c @@ -64,7 +64,7 @@ int delete_recursive(char_u *name) int result = 0; if (os_isrealdir(name)) { - snprintf((char *)NameBuff, MAXPATHL, "%s/*", name); + snprintf((char *)NameBuff, MAXPATHL, "%s/*", name); // NOLINT char_u **files; int file_count; From 29b737e92b5865918ca1e8c5a30e6c6f3a351915 Mon Sep 17 00:00:00 2001 From: Jurica Bradaric Date: Sun, 28 Feb 2016 13:21:01 +0100 Subject: [PATCH 5/9] vim-patch:7.4.1117 Problem: No longer get "." and ".." in directory list. Solution: Do not skip "." and ".." unless EW_DODOT is set. https://github.com/vim/vim/commit/d82103ed8534a1207742e9666ac7ef1e47dda12d --- src/nvim/path.c | 9 +++++---- src/nvim/version.c | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/nvim/path.c b/src/nvim/path.c index 4b15afbfc1..a6e8aa6236 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -604,7 +604,7 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, starstar = true; // convert the file pattern to a regexp pattern - int starts_with_dot = *s == '.' || (flags & EW_DODOT); + int starts_with_dot = *s == '.'; char_u *pat = file_pat_to_reg_pat(s, e, NULL, false); if (pat == NULL) { xfree(buf); @@ -649,9 +649,10 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, char_u *name; scandir_next_with_dots(NULL); // initialize while ((name = (char_u *) scandir_next_with_dots(&dir)) && name != NULL) { - if ((name[0] != '.' || (starts_with_dot - && name[1] != NUL - && (name[1] != '.' || name[2] != NUL))) + if ((name[0] != '.' + || starts_with_dot + || ((flags & EW_DODOT) + && name[1] != NUL && (name[1] != '.' || name[2] != NUL))) && ((regmatch.regprog != NULL && vim_regexec(®match, name, 0)) || ((flags & EW_NOTWILD) && fnamencmp(path + (s - buf), name, e - s) == 0))) { diff --git a/src/nvim/version.c b/src/nvim/version.c index 75eb37c2c3..3276439c50 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -247,7 +247,7 @@ static int included_patches[] = { // 1120, // 1119, // 1118, - // 1117, + 1117, 1116, // 1115 NA 1114, From 425fcdb5b4bc6dec05dafe2e2675c8a7cfe37187 Mon Sep 17 00:00:00 2001 From: Jurica Bradaric Date: Sun, 28 Feb 2016 13:26:10 +0100 Subject: [PATCH 6/9] vim-patch:7.4.1120 Problem: delete(x, 'rf') fails if a directory is empty. (Lcd) Solution: Ignore not finding matches in an empty directory. https://github.com/vim/vim/commit/336bd622c31e1805495c034e1a8cfadcc0bbabc7 --- src/nvim/path.c | 2 +- src/nvim/path.h | 1 + src/nvim/tempfile.c | 4 ++-- src/nvim/version.c | 2 +- test/functional/legacy/delete_spec.lua | 2 ++ 5 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/nvim/path.c b/src/nvim/path.c index a6e8aa6236..d3df9bc059 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -1223,7 +1223,7 @@ int gen_expand_wildcards(int num_pat, char_u **pat, int *num_file, recursive = false; - return (ga.ga_data != NULL) ? OK : FAIL; + return ((flags & EW_EMPTYOK) || ga.ga_data != NULL) ? OK : FAIL; } diff --git a/src/nvim/path.h b/src/nvim/path.h index 9574d8131f..88e5935c24 100644 --- a/src/nvim/path.h +++ b/src/nvim/path.h @@ -22,6 +22,7 @@ * is used when executing commands and EW_SILENT for interactive expanding. */ #define EW_ALLLINKS 0x1000 // also links not pointing to existing file #define EW_DODOT 0x4000 // also files starting with a dot +#define EW_EMPTYOK 0x8000 // no matches is not an error /// Return value for the comparison of two files. Also @see path_full_compare. typedef enum file_comparison { diff --git a/src/nvim/tempfile.c b/src/nvim/tempfile.c index 3b1a2ffca6..c7881fd9a5 100644 --- a/src/nvim/tempfile.c +++ b/src/nvim/tempfile.c @@ -70,8 +70,8 @@ int delete_recursive(char_u *name) 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) == OK) { + EW_DIR | EW_FILE | EW_SILENT | EW_ALLLINKS + | EW_DODOT | EW_EMPTYOK) == OK) { for (int i = 0; i < file_count; i++) { if (delete_recursive(files[i]) != 0) { result = -1; diff --git a/src/nvim/version.c b/src/nvim/version.c index 3276439c50..ca7438e708 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -244,7 +244,7 @@ static int included_patches[] = { // 1123, // 1122 NA // 1121, - // 1120, + 1120, // 1119, // 1118, 1117, diff --git a/test/functional/legacy/delete_spec.lua b/test/functional/legacy/delete_spec.lua index b524af3338..75c3fdf1a0 100644 --- a/test/functional/legacy/delete_spec.lua +++ b/test/functional/legacy/delete_spec.lua @@ -25,6 +25,7 @@ describe('Test for delete()', function() it('recursive delete', function() execute("call mkdir('Xdir1')") execute("call mkdir('Xdir1/subdir')") + execute("call mkdir('Xdir1/empty')") execute('split Xdir1/Xfile') execute("call setline(1, ['a', 'b'])") execute('w') @@ -35,6 +36,7 @@ describe('Test for delete()', function() 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')")) From 2dfc8de1cf59a2db2b873837946549f631d06d71 Mon Sep 17 00:00:00 2001 From: Jurica Bradaric Date: Mon, 29 Feb 2016 20:23:13 +0100 Subject: [PATCH 7/9] Merge tempfile.c back into fileio.c --- src/nvim/diff.c | 1 - src/nvim/eval.c | 1 - src/nvim/ex_cmds.c | 1 - src/nvim/fileio.c | 142 +++++++++++++++++++++++++++++- src/nvim/hardcopy.c | 1 - src/nvim/if_cscope.c | 1 - src/nvim/memline.c | 1 - src/nvim/misc1.c | 1 - src/nvim/msgpack_rpc/server.c | 2 +- src/nvim/os_unix.c | 1 - src/nvim/quickfix.c | 1 - src/nvim/spell.c | 1 - src/nvim/tempfile.c | 159 ---------------------------------- src/nvim/tempfile.h | 8 -- test/unit/formatc.lua | 2 +- test/unit/tempfile_spec.lua | 2 +- 16 files changed, 144 insertions(+), 181 deletions(-) delete mode 100644 src/nvim/tempfile.c delete mode 100644 src/nvim/tempfile.h diff --git a/src/nvim/diff.c b/src/nvim/diff.c index a3063de869..4826e70727 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -29,7 +29,6 @@ #include "nvim/path.h" #include "nvim/screen.h" #include "nvim/strings.h" -#include "nvim/tempfile.h" #include "nvim/undo.h" #include "nvim/window.h" #include "nvim/os/os.h" diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 25201e400c..f02eabfe9f 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -66,7 +66,6 @@ #include "nvim/strings.h" #include "nvim/syntax.h" #include "nvim/tag.h" -#include "nvim/tempfile.h" #include "nvim/ui.h" #include "nvim/mouse.h" #include "nvim/terminal.h" diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index d344daed11..69bf7abe6e 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -50,7 +50,6 @@ #include "nvim/strings.h" #include "nvim/syntax.h" #include "nvim/tag.h" -#include "nvim/tempfile.h" #include "nvim/ui.h" #include "nvim/undo.h" #include "nvim/window.h" diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 32e1b645d0..b280a8c54d 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -45,7 +45,6 @@ #include "nvim/search.h" #include "nvim/sha256.h" #include "nvim/strings.h" -#include "nvim/tempfile.h" #include "nvim/ui.h" #include "nvim/types.h" #include "nvim/undo.h" @@ -5116,6 +5115,147 @@ void forward_slash(char_u *fname) } #endif +/// Name of Vim's own temp dir. Ends in a slash. +static char_u *vim_tempdir = NULL; + +/// Create a directory for private use by this instance of Neovim. +/// This is done once, and the same directory is used for all temp files. +/// This method avoids security problems because of symlink attacks et al. +/// It's also a bit faster, because we only need to check for an existing +/// file when creating the directory and not for each temp file. +static void vim_maketempdir(void) +{ + static const char *temp_dirs[] = TEMP_DIR_NAMES; + // Try the entries in `TEMP_DIR_NAMES` to create the temp directory. + char_u template[TEMP_FILE_PATH_MAXLEN]; + char_u path[TEMP_FILE_PATH_MAXLEN]; + for (size_t i = 0; i < ARRAY_SIZE(temp_dirs); ++i) { + // Expand environment variables, leave room for "/nvimXXXXXX/999999999" + expand_env((char_u *)temp_dirs[i], template, TEMP_FILE_PATH_MAXLEN - 22); + if (!os_isdir(template)) { // directory doesn't exist + continue; + } + + add_pathsep((char *)template); + // Concatenate with temporary directory name pattern + STRCAT(template, "nvimXXXXXX"); + + if (os_mkdtemp((const char *)template, (char *)path) != 0) { + continue; + } + + if (vim_settempdir((char *)path)) { + // Successfully created and set temporary directory so stop trying. + break; + } else { + // Couldn't set `vim_tempdir` to `path` so remove created directory. + os_rmdir((char *)path); + } + } +} + +/// 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(char_u *name) +{ + int result = 0; + + if (os_isrealdir(name)) { + snprintf((char *)NameBuff, MAXPATHL, "%s/*", name); // NOLINT + + 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(files[i]) != 0) { + result = -1; + } + } + FreeWild(file_count, files); + } else { + result = -1; + } + + xfree(exp); + os_rmdir((char *)name); + } else { + result = os_remove((char *)name) == 0 ? 0 : -1; + } + + return result; +} + +/// Delete the temp directory and all files it contains. +void vim_deltempdir(void) +{ + if (vim_tempdir != NULL) { + // remove the trailing path separator + path_tail(vim_tempdir)[-1] = NUL; + delete_recursive(vim_tempdir); + xfree(vim_tempdir); + vim_tempdir = NULL; + } +} + +/// Get the name of temp directory. This directory would be created on the first +/// call to this function. +char_u *vim_gettempdir(void) +{ + if (vim_tempdir == NULL) { + vim_maketempdir(); + } + + return vim_tempdir; +} + +/// Set Neovim own temporary directory name to `tempdir`. This directory should +/// be already created. Expand this name to a full path and put it in +/// `vim_tempdir`. This avoids that using `:cd` would confuse us. +/// +/// @param tempdir must be no longer than MAXPATHL. +/// +/// @return false if we run out of memory. +static bool vim_settempdir(char *tempdir) +{ + char *buf = verbose_try_malloc(MAXPATHL + 2); + if (!buf) { + return false; + } + vim_FullName(tempdir, buf, MAXPATHL, false); + add_pathsep(buf); + vim_tempdir = (char_u *)xstrdup(buf); + xfree(buf); + return true; +} + +/// Return a unique name that can be used for a temp file. +/// +/// @note The temp file is NOT created. +/// +/// @return pointer to the temp file name or NULL if Neovim can't create +/// temporary directory for its own temporary files. +char_u *vim_tempname(void) +{ + // Temp filename counter. + static uint32_t temp_count; + + char_u *tempdir = vim_gettempdir(); + if (!tempdir) { + return NULL; + } + + // There is no need to check if the file exists, because we own the directory + // and nobody else creates a file in it. + char_u template[TEMP_FILE_PATH_MAXLEN]; + snprintf((char *)template, TEMP_FILE_PATH_MAXLEN, + "%s%" PRIu32, tempdir, temp_count++); + return vim_strsave(template); +} + /* * Code for automatic commands. diff --git a/src/nvim/hardcopy.c b/src/nvim/hardcopy.c index cc49bcd074..6ce8954fef 100644 --- a/src/nvim/hardcopy.c +++ b/src/nvim/hardcopy.c @@ -32,7 +32,6 @@ #include "nvim/syntax.h" #include "nvim/ui.h" #include "nvim/version.h" -#include "nvim/tempfile.h" #include "nvim/os/os.h" #include "nvim/os/input.h" diff --git a/src/nvim/if_cscope.c b/src/nvim/if_cscope.c index a143490356..daddd7cee4 100644 --- a/src/nvim/if_cscope.c +++ b/src/nvim/if_cscope.c @@ -27,7 +27,6 @@ #include "nvim/quickfix.h" #include "nvim/strings.h" #include "nvim/tag.h" -#include "nvim/tempfile.h" #include "nvim/window.h" #include "nvim/os/os.h" #include "nvim/os/input.h" diff --git a/src/nvim/memline.c b/src/nvim/memline.c index 10176752d5..f82f88a88f 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -65,7 +65,6 @@ #include "nvim/strings.h" #include "nvim/ui.h" #include "nvim/version.h" -#include "nvim/tempfile.h" #include "nvim/undo.h" #include "nvim/window.h" #include "nvim/os/os.h" diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c index a20ee562fa..43e0dd0c1a 100644 --- a/src/nvim/misc1.c +++ b/src/nvim/misc1.c @@ -43,7 +43,6 @@ #include "nvim/search.h" #include "nvim/strings.h" #include "nvim/tag.h" -#include "nvim/tempfile.h" #include "nvim/ui.h" #include "nvim/undo.h" #include "nvim/window.h" diff --git a/src/nvim/msgpack_rpc/server.c b/src/nvim/msgpack_rpc/server.c index bf384e3379..6cc56ba3dd 100644 --- a/src/nvim/msgpack_rpc/server.c +++ b/src/nvim/msgpack_rpc/server.c @@ -14,7 +14,7 @@ #include "nvim/vim.h" #include "nvim/memory.h" #include "nvim/log.h" -#include "nvim/tempfile.h" +#include "nvim/fileio.h" #include "nvim/path.h" #include "nvim/strings.h" diff --git a/src/nvim/os_unix.c b/src/nvim/os_unix.c index cb9a58cc77..2a859a0f7a 100644 --- a/src/nvim/os_unix.c +++ b/src/nvim/os_unix.c @@ -34,7 +34,6 @@ #include "nvim/screen.h" #include "nvim/strings.h" #include "nvim/syntax.h" -#include "nvim/tempfile.h" #include "nvim/ui.h" #include "nvim/types.h" #include "nvim/os/os.h" diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 28c0425e2c..97db69d3f3 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -39,7 +39,6 @@ #include "nvim/search.h" #include "nvim/strings.h" #include "nvim/ui.h" -#include "nvim/tempfile.h" #include "nvim/window.h" #include "nvim/os/os.h" #include "nvim/os/input.h" diff --git a/src/nvim/spell.c b/src/nvim/spell.c index fdae89b84c..0acaa9ae2b 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -319,7 +319,6 @@ #include "nvim/strings.h" #include "nvim/syntax.h" #include "nvim/ui.h" -#include "nvim/tempfile.h" #include "nvim/undo.h" #include "nvim/os/os.h" #include "nvim/os/input.h" diff --git a/src/nvim/tempfile.c b/src/nvim/tempfile.c deleted file mode 100644 index c7881fd9a5..0000000000 --- a/src/nvim/tempfile.c +++ /dev/null @@ -1,159 +0,0 @@ -#include -#include -#include -#include - -#include "nvim/ascii.h" -#include "nvim/memory.h" -#include "nvim/misc1.h" -#include "nvim/os/os.h" -#include "nvim/os/os_defs.h" -#include "nvim/path.h" -#include "nvim/strings.h" -#include "nvim/tempfile.h" - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "tempfile.c.generated.h" -#endif - -/// Name of Vim's own temp dir. Ends in a slash. -static char_u *vim_tempdir = NULL; - -/// Create a directory for private use by this instance of Neovim. -/// This is done once, and the same directory is used for all temp files. -/// This method avoids security problems because of symlink attacks et al. -/// It's also a bit faster, because we only need to check for an existing -/// file when creating the directory and not for each temp file. -static void vim_maketempdir(void) -{ - static const char *temp_dirs[] = TEMP_DIR_NAMES; - // Try the entries in `TEMP_DIR_NAMES` to create the temp directory. - char_u template[TEMP_FILE_PATH_MAXLEN]; - char_u path[TEMP_FILE_PATH_MAXLEN]; - for (size_t i = 0; i < ARRAY_SIZE(temp_dirs); ++i) { - // Expand environment variables, leave room for "/nvimXXXXXX/999999999" - // Skip the directory check if the expansion fails. - expand_env((char_u *)temp_dirs[i], template, TEMP_FILE_PATH_MAXLEN - 22); - if (template[0] == '$' || !os_isdir(template)) { - continue; - } - - add_pathsep((char *)template); - // Concatenate with temporary directory name pattern - STRCAT(template, "nvimXXXXXX"); - - if (os_mkdtemp((const char *)template, (char *)path) != 0) { - continue; - } - - if (vim_settempdir((char *)path)) { - // Successfully created and set temporary directory so stop trying. - break; - } else { - // Couldn't set `vim_tempdir` to `path` so remove created directory. - os_rmdir((char *)path); - } - } -} - -/// 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(char_u *name) -{ - int result = 0; - - if (os_isrealdir(name)) { - snprintf((char *)NameBuff, MAXPATHL, "%s/*", name); // NOLINT - - 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(files[i]) != 0) { - result = -1; - } - } - FreeWild(file_count, files); - } else { - result = -1; - } - - xfree(exp); - os_rmdir((char *)name); - } else { - result = os_remove((char *)name) == 0 ? 0 : -1; - } - - return result; -} - -/// Delete the temp directory and all files it contains. -void vim_deltempdir(void) -{ - if (vim_tempdir != NULL) { - // remove the trailing path separator - path_tail(vim_tempdir)[-1] = NUL; - delete_recursive(vim_tempdir); - xfree(vim_tempdir); - vim_tempdir = NULL; - } -} - -/// Get the name of temp directory. This directory would be created on the first -/// call to this function. -char_u *vim_gettempdir(void) -{ - if (vim_tempdir == NULL) { - vim_maketempdir(); - } - - return vim_tempdir; -} - -/// Set Neovim own temporary directory name to `tempdir`. This directory should -/// be already created. Expand this name to a full path and put it in -/// `vim_tempdir`. This avoids that using `:cd` would confuse us. -/// -/// @param tempdir must be no longer than MAXPATHL. -/// -/// @return false if we run out of memory. -static bool vim_settempdir(char *tempdir) -{ - char *buf = verbose_try_malloc(MAXPATHL + 2); - if (!buf) { - return false; - } - vim_FullName(tempdir, buf, MAXPATHL, false); - add_pathsep(buf); - vim_tempdir = (char_u *)xstrdup(buf); - xfree(buf); - return true; -} - -/// Return a unique name that can be used for a temp file. -/// -/// @note The temp file is NOT created. -/// -/// @return pointer to the temp file name or NULL if Neovim can't create -/// temporary directory for its own temporary files. -char_u *vim_tempname(void) -{ - // Temp filename counter. - static uint32_t temp_count; - - char_u *tempdir = vim_gettempdir(); - if (!tempdir) { - return NULL; - } - - // There is no need to check if the file exists, because we own the directory - // and nobody else creates a file in it. - char_u template[TEMP_FILE_PATH_MAXLEN]; - snprintf((char *)template, TEMP_FILE_PATH_MAXLEN, - "%s%" PRIu32, tempdir, temp_count++); - return vim_strsave(template); -} diff --git a/src/nvim/tempfile.h b/src/nvim/tempfile.h deleted file mode 100644 index c030a70eeb..0000000000 --- a/src/nvim/tempfile.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef NVIM_TEMPFILE_H -#define NVIM_TEMPFILE_H - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "tempfile.h.generated.h" -#endif - -#endif // NVIM_TEMPFILE_H diff --git a/test/unit/formatc.lua b/test/unit/formatc.lua index 3f86c5f1b1..00637e0b8d 100644 --- a/test/unit/formatc.lua +++ b/test/unit/formatc.lua @@ -238,7 +238,7 @@ local function standalone(...) -- luacheck: ignore end -- uncomment this line (and comment the `return`) for standalone debugging -- example usage: --- ../../.deps/usr/bin/luajit formatc.lua ../../include/tempfile.h.generated.h +-- ../../.deps/usr/bin/luajit formatc.lua ../../include/fileio.h.generated.h -- ../../.deps/usr/bin/luajit formatc.lua /usr/include/malloc.h -- standalone(...) return formatc diff --git a/test/unit/tempfile_spec.lua b/test/unit/tempfile_spec.lua index e558ff04c8..b3e84db132 100644 --- a/test/unit/tempfile_spec.lua +++ b/test/unit/tempfile_spec.lua @@ -2,7 +2,7 @@ local lfs = require 'lfs' local helpers = require 'test.unit.helpers' local os = helpers.cimport './src/nvim/os/os.h' -local tempfile = helpers.cimport './src/nvim/tempfile.h' +local tempfile = helpers.cimport './src/nvim/fileio.h' describe('tempfile related functions', function() after_each(function() From 5734e218738e7b76cef1e2acfdc79a17dad650ad Mon Sep 17 00:00:00 2001 From: Jurica Bradaric Date: Mon, 29 Feb 2016 20:27:48 +0100 Subject: [PATCH 8/9] delete_spec: Fix linter errors. --- test/functional/legacy/delete_spec.lua | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/functional/legacy/delete_spec.lua b/test/functional/legacy/delete_spec.lua index 75c3fdf1a0..cd18a8f750 100644 --- a/test/functional/legacy/delete_spec.lua +++ b/test/functional/legacy/delete_spec.lua @@ -1,7 +1,6 @@ local helpers = require('test.functional.helpers') -local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local source = helpers.source -local eq, eval, execute, expect = helpers.eq, helpers.eval, helpers.execute, helpers.expect +local clear, source = helpers.clear, helpers.source +local eq, eval, execute = helpers.eq, helpers.eval, helpers.execute describe('Test for delete()', function() before_each(clear) From 9e1cacecbe2938c45205fdb30465303dfe2af968 Mon Sep 17 00:00:00 2001 From: Jurica Bradaric Date: Wed, 20 Apr 2016 10:00:07 +0200 Subject: [PATCH 9/9] Fix another linter error. --- src/nvim/fileio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index b280a8c54d..c7870b9f69 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -5129,7 +5129,7 @@ static void vim_maketempdir(void) // Try the entries in `TEMP_DIR_NAMES` to create the temp directory. char_u template[TEMP_FILE_PATH_MAXLEN]; char_u path[TEMP_FILE_PATH_MAXLEN]; - for (size_t i = 0; i < ARRAY_SIZE(temp_dirs); ++i) { + for (size_t i = 0; i < ARRAY_SIZE(temp_dirs); i++) { // Expand environment variables, leave room for "/nvimXXXXXX/999999999" expand_env((char_u *)temp_dirs[i], template, TEMP_FILE_PATH_MAXLEN - 22); if (!os_isdir(template)) { // directory doesn't exist