]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 8.1.1378: delete() can not handle a file name that looks like a pattern v8.1.1378
authorBram Moolenaar <Bram@vim.org>
Fri, 24 May 2019 12:14:14 +0000 (14:14 +0200)
committerBram Moolenaar <Bram@vim.org>
Fri, 24 May 2019 12:14:14 +0000 (14:14 +0200)
Problem:    Delete() can not handle a file name that looks like a pattern.
Solution:   Use readdir() instead of appending "/*" and expanding wildcards.
            (Ken Takata, closes #4424, closes #696)

src/evalfunc.c
src/fileio.c
src/proto/fileio.pro
src/testdir/test_functions.vim
src/version.c

index 5631795e35c91c27f3b5f71f2dcb9ffc72cd0c33..6ffd1a1abfe3e6140a3a25f4a558052b7c90e7dc 100644 (file)
@@ -9349,17 +9349,21 @@ f_range(typval_T *argvars, typval_T *rettv)
 }
 
 /*
- * Evaluate "expr" for readdir().
+ * Evaluate "expr" (= "context") for readdir().
  */
     static int
-readdir_checkitem(typval_T *expr, char_u *name)
+readdir_checkitem(void *context, char_u *name)
 {
+    typval_T   *expr = (typval_T *)context;
     typval_T   save_val;
     typval_T   rettv;
     typval_T   argv[2];
     int                retval = 0;
     int                error = FALSE;
 
+    if (expr->v_type == VAR_UNKNOWN)
+       return 1;
+
     prepare_vimvar(VV_VAL, &save_val);
     set_vim_var_string(VV_VAL, name, -1);
     argv[0].v_type = VAR_STRING;
@@ -9386,136 +9390,20 @@ theend:
 f_readdir(typval_T *argvars, typval_T *rettv)
 {
     typval_T   *expr;
-    int                failed = FALSE;
+    int                ret;
     char_u     *path;
+    char_u     *p;
     garray_T   ga;
     int                i;
-#ifdef MSWIN
-    char_u             *buf, *p;
-    int                        ok;
-    HANDLE             hFind = INVALID_HANDLE_VALUE;
-    WIN32_FIND_DATAW    wfb;
-    WCHAR              *wn = NULL;     // UCS-2 name, NULL when not used.
-#endif
 
     if (rettv_list_alloc(rettv) == FAIL)
        return;
     path = tv_get_string(&argvars[0]);
     expr = &argvars[1];
-    ga_init2(&ga, (int)sizeof(char *), 20);
-
-#ifdef MSWIN
-    buf = alloc((int)MAXPATHL);
-    if (buf == NULL)
-       return;
-    STRNCPY(buf, path, MAXPATHL-5);
-    p = vim_strpbrk(path, (char_u *)"\\/");
-    if (p != NULL)
-       *p = NUL;
-    STRCAT(buf, "\\*");
-
-    wn = enc_to_utf16(buf, NULL);
-    if (wn != NULL)
-       hFind = FindFirstFileW(wn, &wfb);
-    ok = (hFind != INVALID_HANDLE_VALUE);
-    if (!ok)
-       smsg(_(e_notopen), path);
-    else
-    {
-       while (ok)
-       {
-           int ignore;
-
-           p = utf16_to_enc(wfb.cFileName, NULL);   // p is allocated here
-           if (p == NULL)
-               break;  // out of memory
-
-           ignore = p[0] == '.' && (p[1] == NUL
-                                             || (p[1] == '.' && p[2] == NUL));
-           if (!ignore && expr->v_type != VAR_UNKNOWN)
-           {
-               int r = readdir_checkitem(expr, p);
-
-               if (r < 0)
-               {
-                   vim_free(p);
-                   break;
-               }
-               if (r == 0)
-                   ignore = TRUE;
-           }
-
-           if (!ignore)
-           {
-               if (ga_grow(&ga, 1) == OK)
-                   ((char_u**)ga.ga_data)[ga.ga_len++] = vim_strsave(p);
-               else
-               {
-                   failed = TRUE;
-                   vim_free(p);
-                   break;
-               }
-           }
-
-           vim_free(p);
-           ok = FindNextFileW(hFind, &wfb);
-       }
-       FindClose(hFind);
-    }
-
-    vim_free(buf);
-    vim_free(wn);
-#else
-    DIR                *dirp;
-    struct dirent *dp;
-    char_u     *p;
-
-    dirp = opendir((char *)path);
-    if (dirp == NULL)
-       smsg(_(e_notopen), path);
-    else
-    {
-       for (;;)
-       {
-           int ignore;
-
-           dp = readdir(dirp);
-           if (dp == NULL)
-               break;
-           p = (char_u *)dp->d_name;
-
-           ignore = p[0] == '.' &&
-                   (p[1] == NUL ||
-                    (p[1] == '.' && p[2] == NUL));
-           if (!ignore && expr->v_type != VAR_UNKNOWN)
-           {
-               int r = readdir_checkitem(expr, p);
-
-               if (r < 0)
-                   break;
-               if (r == 0)
-                   ignore = TRUE;
-           }
-
-           if (!ignore)
-           {
-               if (ga_grow(&ga, 1) == OK)
-                   ((char_u**)ga.ga_data)[ga.ga_len++] = vim_strsave(p);
-               else
-               {
-                   failed = TRUE;
-                   break;
-               }
-           }
-       }
-
-       closedir(dirp);
-    }
-#endif
 
-    if (!failed && rettv->vval.v_list != NULL && ga.ga_len > 0)
+    ret = readdir_core(&ga, path, (void *)expr, readdir_checkitem);
+    if (ret == OK && rettv->vval.v_list != NULL && ga.ga_len > 0)
     {
-       sort_strings((char_u **)ga.ga_data, ga.ga_len);
        for (i = 0; i < ga.ga_len; i++)
        {
            p = ((char_u **)ga.ga_data)[i];
index c9387cecd127f8adc3808d7cfd932fc667756b4f..b6a37f522a56bf62d395a9a6d6c548588f84cc5b 100644 (file)
@@ -7171,21 +7171,165 @@ write_lnum_adjust(linenr_T offset)
 }
 
 #if defined(TEMPDIRNAMES) || defined(FEAT_EVAL) || defined(PROTO)
+/*
+ * Core part of "readdir()" function.
+ * Retrieve the list of files/directories of "path" into "gap".
+ * Return OK for success, FAIL for failure.
+ */
+    int
+readdir_core(
+    garray_T   *gap,
+    char_u     *path,
+    void       *context,
+    int                (*checkitem)(void *context, char_u *name))
+{
+    int                        failed = FALSE;
+#ifdef MSWIN
+    char_u             *buf, *p;
+    int                        ok;
+    HANDLE             hFind = INVALID_HANDLE_VALUE;
+    WIN32_FIND_DATAW    wfb;
+    WCHAR              *wn = NULL;     // UTF-16 name, NULL when not used.
+#endif
+
+    ga_init2(gap, (int)sizeof(char *), 20);
+
+#ifdef MSWIN
+    buf = alloc((int)MAXPATHL);
+    if (buf == NULL)
+       return FAIL;
+    STRNCPY(buf, path, MAXPATHL-5);
+    p = buf + strlen(buf);
+    MB_PTR_BACK(buf, p);
+    if (*p == '\\' || *p == '/')
+       *p = NUL;
+    STRCAT(buf, "\\*");
+
+    wn = enc_to_utf16(buf, NULL);
+    if (wn != NULL)
+       hFind = FindFirstFileW(wn, &wfb);
+    ok = (hFind != INVALID_HANDLE_VALUE);
+    if (!ok)
+    {
+       failed = TRUE;
+       smsg(_(e_notopen), path);
+    }
+    else
+    {
+       while (ok)
+       {
+           int ignore;
+
+           p = utf16_to_enc(wfb.cFileName, NULL);   // p is allocated here
+           if (p == NULL)
+               break;  // out of memory
+
+           ignore = p[0] == '.' && (p[1] == NUL
+                                             || (p[1] == '.' && p[2] == NUL));
+           if (!ignore && checkitem != NULL)
+           {
+               int r = checkitem(context, p);
+
+               if (r < 0)
+               {
+                   vim_free(p);
+                   break;
+               }
+               if (r == 0)
+                   ignore = TRUE;
+           }
+
+           if (!ignore)
+           {
+               if (ga_grow(gap, 1) == OK)
+                   ((char_u**)gap->ga_data)[gap->ga_len++] = vim_strsave(p);
+               else
+               {
+                   failed = TRUE;
+                   vim_free(p);
+                   break;
+               }
+           }
+
+           vim_free(p);
+           ok = FindNextFileW(hFind, &wfb);
+       }
+       FindClose(hFind);
+    }
+
+    vim_free(buf);
+    vim_free(wn);
+#else
+    DIR                *dirp;
+    struct dirent *dp;
+    char_u     *p;
+
+    dirp = opendir((char *)path);
+    if (dirp == NULL)
+    {
+       failed = TRUE;
+       smsg(_(e_notopen), path);
+    }
+    else
+    {
+       for (;;)
+       {
+           int ignore;
+
+           dp = readdir(dirp);
+           if (dp == NULL)
+               break;
+           p = (char_u *)dp->d_name;
+
+           ignore = p[0] == '.' &&
+                   (p[1] == NUL ||
+                    (p[1] == '.' && p[2] == NUL));
+           if (!ignore && checkitem != NULL)
+           {
+               int r = checkitem(context, p);
+
+               if (r < 0)
+                   break;
+               if (r == 0)
+                   ignore = TRUE;
+           }
+
+           if (!ignore)
+           {
+               if (ga_grow(gap, 1) == OK)
+                   ((char_u**)gap->ga_data)[gap->ga_len++] = vim_strsave(p);
+               else
+               {
+                   failed = TRUE;
+                   break;
+               }
+           }
+       }
+
+       closedir(dirp);
+    }
+#endif
+
+    if (!failed && gap->ga_len > 0)
+       sort_strings((char_u **)gap->ga_data, gap->ga_len);
+
+    return failed ? FAIL : OK;
+}
+
 /*
  * Delete "name" and everything in it, recursively.
- * return 0 for succes, -1 if some file was not deleted.
+ * return 0 for success, -1 if some file was not deleted.
  */
     int
 delete_recursive(char_u *name)
 {
     int result = 0;
-    char_u     **files;
-    int                file_count;
     int                i;
     char_u     *exp;
+    garray_T   ga;
 
-    /* A symbolic link to a directory itself is deleted, not the directory it
-     * points to. */
+    // A symbolic link to a directory itself is deleted, not the directory it
+    // points to.
     if (
 # if defined(UNIX) || defined(MSWIN)
         mch_isrealdir(name)
@@ -7194,22 +7338,24 @@ delete_recursive(char_u *name)
 # endif
            )
     {
-       vim_snprintf((char *)NameBuff, MAXPATHL, "%s/*", name);
-       exp = vim_strsave(NameBuff);
+       exp = vim_strsave(name);
        if (exp == NULL)
            return -1;
-       if (gen_expand_wildcards(1, &exp, &file_count, &files,
-             EW_DIR|EW_FILE|EW_SILENT|EW_ALLLINKS|EW_DODOT|EW_EMPTYOK) == OK)
+       if (readdir_core(&ga, exp, NULL, NULL) == OK)
        {
-           for (i = 0; i < file_count; ++i)
-               if (delete_recursive(files[i]) != 0)
+           for (i = 0; i < ga.ga_len; ++i)
+           {
+               vim_snprintf((char *)NameBuff, MAXPATHL, "%s/%s", exp,
+                                           ((char_u **)ga.ga_data)[i]);
+               if (delete_recursive(NameBuff) != 0)
                    result = -1;
-           FreeWild(file_count, files);
+           }
+           ga_clear_strings(&ga);
        }
        else
            result = -1;
+       (void)mch_rmdir(exp);
        vim_free(exp);
-       (void)mch_rmdir(name);
     }
     else
        result = mch_remove(name) == 0 ? 0 : -1;
index 1844924fdb3ff68c60e7b956b5469990c32d8fc6..bff6640730805bdde3394f316c5e4861d0b24326 100644 (file)
@@ -24,6 +24,7 @@ int buf_check_timestamp(buf_T *buf, int focus);
 void buf_reload(buf_T *buf, int orig_mode);
 void buf_store_time(buf_T *buf, stat_T *st, char_u *fname);
 void write_lnum_adjust(linenr_T offset);
+int readdir_core(garray_T *gap, char_u *path, void *context, int (*checkitem)(void *context, char_u *name));
 int delete_recursive(char_u *name);
 void vim_deltempdir(void);
 char_u *vim_tempname(int extra_char, int keep);
index b9520c7ee6a39e3a79d9df8cf804118d82b7c9af..3e4c03cdbe9523b99a0d1c0eec962419911800c0 100644 (file)
@@ -1436,6 +1436,21 @@ func Test_readdir()
   call delete('Xdir', 'rf')
 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
+
 func Test_call()
   call assert_equal(3, call('len', [123]))
   call assert_fails("call call('len', 123)", 'E714:')
index b2278ed02ac4eec7e52f4814f14203f8d7b5cdf5..e544f9bd34aba852bd98e3edcda07f0c8a378040 100644 (file)
@@ -767,6 +767,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1378,
 /**/
     1377,
 /**/