]> granicus.if.org Git - vim/commitdiff
patch 8.1.1120: cannot easily get directory entry matches v8.1.1120
authorBram Moolenaar <Bram@vim.org>
Fri, 5 Apr 2019 20:50:40 +0000 (22:50 +0200)
committerBram Moolenaar <Bram@vim.org>
Fri, 5 Apr 2019 20:50:40 +0000 (22:50 +0200)
Problem:    Cannot easily get directory entry matches.
Solution:   Add the readdir() function. (Yasuhiro Matsumoto, closes #2439)

runtime/doc/eval.txt
src/eval.c
src/evalfunc.c
src/misc1.c
src/proto/eval.pro
src/testdir/test_functions.vim
src/version.c

index 30fd05c0bf6476bf9f5ea3d384a272524c42b6a3..0b9fb542597223f05e48e74f964fbcc70acc4346 100644 (file)
@@ -2507,6 +2507,9 @@ py3eval({expr})                   any     evaluate |python3| expression
 pyxeval({expr})                        any     evaluate |python_x| expression
 range({expr} [, {max} [, {stride}]])
                                List    items from {expr} to {max}
+readdir({directory} [, {expr}])
+                               List    file names on {dir} with evalating
+                                       {expr}
 readfile({fname} [, {type} [, {max}]])
                                List    get list of lines from file {fname}
 reg_executing()                        String  get the executing register name
@@ -7255,6 +7258,33 @@ range({expr} [, {max} [, {stride}]])                             *range()*
                        range(2, -2, -1)        " [2, 1, 0, -1, -2]
                        range(0)                " []
                        range(2, 0)             " error!
+<
+                                                       *readdir()*
+readdir({directory} [, {expr}])
+               Return a list with file and directory names in {directory}.
+
+               When {expr} is omitted all entries are included.
+               When {expr} is given, it is evaluated to check what to do:
+                       If {expr} results in -1 then no further entries will
+                       be handled.
+                       If {expr} results in 0 then this entry will not be
+                       added to the list.
+                       If {expr} results in 1 then this entry will be added
+                       to the list.
+               Each time {expr} is evaluated |v:val| is set to the entry name.
+               When {expr} is a function the name is passed as the argument.
+               For example, to get a list of files ending in ".txt": >
+                 readdir(dirname, {n -> n =~ '.txt$'})
+<              To skip hidden and backup files: >
+                 readdir(dirname, {n -> n !~ '^\.\|\~$'})
+
+<              If you want to get a directory tree: >
+                  function! s:tree(dir)
+                      return {a:dir : map(readdir(a:dir),
+                     \ {_, x -> isdirectory(x) ?
+                     \          {x : s:tree(a:dir . '/' . x)} : x})}
+                  endfunction
+                  echo s:tree(".")
 <
                                                        *readfile()*
 readfile({fname} [, {type} [, {max}]])
index 9e93dcf2701880ebb107791e1de63822c8f945dc..5f45d6900e44a62f792c00227870338d3194c74a 100644 (file)
@@ -753,7 +753,7 @@ eval1_emsg(char_u **arg, typval_T *rettv, int evaluate)
     return ret;
 }
 
-    static int
+    int
 eval_expr_typval(typval_T *expr, typval_T *argv, int argc, typval_T *rettv)
 {
     char_u     *s;
@@ -966,7 +966,7 @@ eval_to_number(char_u *expr)
  * Save the current typeval in "save_tv".
  * When not used yet add the variable to the v: hashtable.
  */
-    static void
+    void
 prepare_vimvar(int idx, typval_T *save_tv)
 {
     *save_tv = vimvars[idx].vv_tv;
@@ -978,7 +978,7 @@ prepare_vimvar(int idx, typval_T *save_tv)
  * Restore v: variable "idx" to typeval "save_tv".
  * When no longer defined, remove the variable from the v: hashtable.
  */
-    static void
+    void
 restore_vimvar(int idx, typval_T *save_tv)
 {
     hashitem_T *hi;
index ea2ec3c9fc5663eac2d50be0033220eadbe977eb..0e00a0a41219e1af2fd28feaa80ac659d94a74ff 100644 (file)
@@ -319,6 +319,7 @@ static void f_pyeval(typval_T *argvars, typval_T *rettv);
 static void f_pyxeval(typval_T *argvars, typval_T *rettv);
 #endif
 static void f_range(typval_T *argvars, typval_T *rettv);
+static void f_readdir(typval_T *argvars, typval_T *rettv);
 static void f_readfile(typval_T *argvars, typval_T *rettv);
 static void f_reg_executing(typval_T *argvars, typval_T *rettv);
 static void f_reg_recording(typval_T *argvars, typval_T *rettv);
@@ -819,6 +820,7 @@ static struct fst
     {"pyxeval",                1, 1, f_pyxeval},
 #endif
     {"range",          1, 3, f_range},
+    {"readdir",                1, 2, f_readdir},
     {"readfile",       1, 3, f_readfile},
     {"reg_executing",  0, 0, f_reg_executing},
     {"reg_recording",  0, 0, f_reg_recording},
@@ -9117,6 +9119,189 @@ f_range(typval_T *argvars, typval_T *rettv)
     }
 }
 
+/*
+ * Evaluate "expr" for readdir().
+ */
+    static int
+readdir_checkitem(typval_T *expr, char_u *name)
+{
+    typval_T   save_val;
+    typval_T   rettv;
+    typval_T   argv[2];
+    int                retval = 0;
+    int                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 = name;
+
+    if (eval_expr_typval(expr, argv, 1, &rettv) == FAIL)
+       goto theend;
+
+    retval = tv_get_number_chk(&rettv, &error);
+    if (error)
+       retval = -1;
+    clear_tv(&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)
+{
+    typval_T   *expr;
+    int                failed = FALSE;
+    char_u     *path;
+    garray_T   ga;
+    int                i;
+#ifdef MSWIN
+    char_u             *buf, *p;
+    WIN32_FIND_DATA    fb;
+    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
+
+    rettv->vval.v_list = list_alloc();
+    if (!failed && rettv->vval.v_list != NULL)
+    {
+       ++rettv->vval.v_list->lv_refcount;
+       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];
+           list_append_string(rettv->vval.v_list, p, -1);
+       }
+    }
+    for (i = 0; i < ga.ga_len; i++)
+       vim_free(((char_u **)ga.ga_data)[i]);
+
+    ga_clear(&ga);
+}
+
 /*
  * "readfile()" function
  */
index f6d6c18179857bf798d3b0e73fb520ba219381b8..54da8900e3be8bed70a5907570fbabfca9297079 100644 (file)
@@ -5790,6 +5790,9 @@ dos_expandpath(
     while (ok)
     {
        p = utf16_to_enc(wfb.cFileName, NULL);   // p is allocated here
+       if (p == NULL)
+           break;  // out of memory
+
        // Ignore entries starting with a dot, unless when asked for.  Accept
        // all entries found with "matchname".
        if ((p[0] != '.' || starts_with_dot
index ba75f07986af3ba6a0d34cf416fd188aef5506f7..0e6182f0168c7c6ed1dd1d406f8ddb5a743ef656 100644 (file)
@@ -10,12 +10,15 @@ int eval_printexpr(char_u *fname, char_u *args);
 void eval_diff(char_u *origfile, char_u *newfile, char_u *outfile);
 void eval_patch(char_u *origfile, char_u *difffile, char_u *outfile);
 int eval_to_bool(char_u *arg, int *error, char_u **nextcmd, int skip);
+int eval_expr_typval(typval_T *expr, typval_T *argv, int argc, typval_T *rettv);
 int eval_expr_to_bool(typval_T *expr, int *error);
 char_u *eval_to_string_skip(char_u *arg, char_u **nextcmd, int skip);
 int skip_expr(char_u **pp);
 char_u *eval_to_string(char_u *arg, char_u **nextcmd, int convert);
 char_u *eval_to_string_safe(char_u *arg, char_u **nextcmd, int use_sandbox);
 varnumber_T eval_to_number(char_u *expr);
+void prepare_vimvar(int idx, typval_T *save_tv);
+void restore_vimvar(int idx, typval_T *save_tv);
 list_T *eval_spell_expr(char_u *badword, char_u *expr);
 int get_spellword(list_T *list, char_u **pp);
 typval_T *eval_expr(char_u *arg, char_u **nextcmd);
index e273e8aa799cb791b5b5d79ae4f9eccd52c01788..7bd3ef488e189e5aebaee19e4ec4a1ad2c4472fe 100644 (file)
@@ -1401,3 +1401,33 @@ func Test_platform_name()
     call assert_equal(uname =~? 'CYGWIN\|MSYS', has('win32unix'))
   endif
 endfunc
+
+func Test_readdir()
+  call mkdir('Xdir')
+  call writefile([], 'Xdir/foo.txt')
+  call writefile([], 'Xdir/bar.txt')
+  call mkdir('Xdir/dir')
+
+  " All results
+  let files = readdir('Xdir')
+  call assert_equal(['bar.txt', 'dir', 'foo.txt'], sort(files))
+
+  " Only results containing "f"
+  let files = readdir('Xdir', { x -> stridx(x, 'f') !=- 1 })
+  call assert_equal(['foo.txt'], sort(files))
+
+  " Only .txt files
+  let files = readdir('Xdir', { x -> x =~ '.txt$' })
+  call assert_equal(['bar.txt', 'foo.txt'], sort(files))
+
+  " Only .txt files with string
+  let files = readdir('Xdir', 'v:val =~ ".txt$"')
+  call assert_equal(['bar.txt', 'foo.txt'], sort(files))
+
+  " Limit to 1 result.
+  let l = []
+  let files = readdir('Xdir', {x -> len(add(l, x)) == 2 ? -1 : 1})
+  call assert_equal(1, len(files))
+
+  call delete('Xdir', 'rf')
+endfunc
index 40d802a6004a2799b33f7007843f2ee00a5ee287..b3d7abc74c681adeb4554574d2be752a34205023 100644 (file)
@@ -771,6 +771,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1120,
 /**/
     1119,
 /**/