Patch 8.2.0875
Problem: Getting attributes for directory entries is slow.
Solution: Add readdirex(). (Ken Takata, closes #5619)
Files: runtime/doc/eval.txt, runtime/doc/usr_41.txt, src/evalfunc.c,
src/fileio.c, src/filepath.c src/proto/
fileio.pro,
src/proto/
filepath.pro, src/testdir/test_functions.vim
*** ../vim-8.2.0874/runtime/doc/eval.txt 2020-06-01 14:14:40.691899742 +0200
--- runtime/doc/eval.txt 2020-06-01 15:56:35.576460287 +0200
***************
*** 2670,2675 ****
--- 2676,2682 ----
range({expr} [, {max} [, {stride}]])
List items from {expr} to {max}
readdir({dir} [, {expr}]) List file names in {dir} selected by {expr}
+ readdirex({dir} [, {expr}]) List file info in {dir} selected by {expr}
readfile({fname} [, {type} [, {max}]])
List get list of lines from file {fname}
reg_executing() String get the executing register name
***************
*** 7824,7834 ****
:echo rand(seed)
:echo rand(seed) % 16 " random number 0 - 15
<
! *readdir()*
! readdir({directory} [, {expr}])
Return a list with file and directory names in {directory}.
You can also use |glob()| if you don't need to do complicated
things, such as limiting the number of matches.
When {expr} is omitted all entries are included.
When {expr} is given, it is evaluated to check what to do:
--- 7841,7851 ----
:echo rand(seed)
:echo rand(seed) % 16 " random number 0 - 15
<
! readdir({directory} [, {expr}]) *readdir()*
Return a list with file and directory names in {directory}.
You can also use |glob()| if you don't need to do complicated
things, such as limiting the number of matches.
+ The list will be sorted (case sensitive).
When {expr} is omitted all entries are included.
When {expr} is given, it is evaluated to check what to do:
***************
*** 7838,7843 ****
--- 7855,7861 ----
added to the list.
If {expr} results in 1 then this entry will be added
to the list.
+ The entries "." and ".." are always excluded.
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": >
***************
*** 7856,7861 ****
--- 7874,7932 ----
Can also be used as a |method|: >
GetDirName()->readdir()
<
+ readdirex({directory} [, {expr}]) *readdirex()*
+ Extended version of |readdir()|.
+ Return a list of Dictionaries with file and directory
+ information in {directory}.
+ This is useful if you want to get the attributes of file and
+ directory at the same time as getting a list of a directory.
+ This is much faster than calling |readdir()| then calling
+ |getfperm()|, |getfsize()|, |getftime()| and |getftype()| for
+ each file and directory especially on MS-Windows.
+ The list will be sorted by name (case sensitive).
+
+ The Dictionary for file and directory information has the
+ following items:
+ group Group name of the entry. (Only on Unix)
+ name Name of the entry.
+ perm Permissions of the entry. See |getfperm()|.
+ size Size of the entry. See |getfsize()|.
+ time Timestamp of the entry. See |getftime()|.
+ type Type of the entry.
+ On Unix, almost same as |getftype()| except:
+ Symlink to a dir "linkd"
+ Other symlink "link"
+ On MS-Windows:
+ Normal file "file"
+ Directory "dir"
+ Junction "junction"
+ Symlink to a dir "linkd"
+ Other symlink "link"
+ Other reparse point "reparse"
+ user User name of the entry's owner. (Only on Unix)
+ On Unix, if the entry is a symlink, the Dictionary includes
+ the information of the target (except the "type" item).
+ On MS-Windows, it includes the information of the symlink
+ itself because of performance reasons.
+
+ 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.
+ The entries "." and ".." are always excluded.
+ Each time {expr} is evaluated |v:val| is set to a Dictionary
+ of the entry.
+ When {expr} is a function the entry is passed as the argument.
+ For example, to get a list of files ending in ".txt": >
+ readdirex(dirname, {e ->
e.name =~ '.txt$'})
+ <
+ Can also be used as a |method|: >
+ GetDirName()->readdirex()
+ <
*readfile()*
readfile({fname} [, {type} [, {max}]])
Read file {fname} and return a |List|, each line of the file
*** ../vim-8.2.0874/runtime/doc/usr_41.txt 2020-05-31 15:41:53.159138610 +0200
--- runtime/doc/usr_41.txt 2020-06-01 15:56:35.576460287 +0200
***************
*** 791,796 ****
--- 799,805 ----
hostname() name of the system
readfile() read a file into a List of lines
readdir() get a List of file names in a directory
+ readdirex() get a List of file information in a directory
writefile() write a List of lines or Blob into a file
Date and Time: *date-functions* *time-functions*
*** ../vim-8.2.0874/src/evalfunc.c 2020-06-01 14:14:40.691899742 +0200
--- src/evalfunc.c 2020-06-01 15:56:35.576460287 +0200
***************
*** 767,772 ****
--- 767,773 ----
{"rand", 0, 1, FEARG_1, ret_number, f_rand},
{"range", 1, 3, FEARG_1, ret_list_number, f_range},
{"readdir", 1, 2, FEARG_1, ret_list_string, f_readdir},
+ {"readdirex", 1, 2, FEARG_1, ret_list_dict_any, f_readdirex},
{"readfile", 1, 3, FEARG_1, ret_any, f_readfile},
{"reg_executing", 0, 0, 0, ret_string, f_reg_executing},
{"reg_recording", 0, 0, 0, ret_string, f_reg_recording},
*** ../vim-8.2.0874/src/fileio.c 2020-05-30 20:30:42.892816571 +0200
--- src/fileio.c 2020-06-01 15:56:35.576460287 +0200
***************
*** 16,21 ****
--- 16,25 ----
#if defined(__TANDEM) || defined(__MINT__)
# include <limits.h> // for SSIZE_MAX
#endif
+ #if defined(UNIX) && defined(FEAT_EVAL)
+ # include <pwd.h>
+ # include <grp.h>
+ #endif
// Is there any system that doesn't have access()?
#define USE_MCH_ACCESS
***************
*** 4420,4570 ****
curbuf->b_no_eol_lnum += 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;
! char_u *p;
! ga_init2(gap, (int)sizeof(char *), 20);
# ifdef MSWIN
{
! char_u *buf;
! int ok;
! HANDLE hFind = INVALID_HANDLE_VALUE;
! WIN32_FIND_DATAW wfb;
! WCHAR *wn = NULL; // UTF-16 name, NULL when not used.
!
! buf = alloc(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;
!
! 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;
}
--- 4424,4848 ----
curbuf->b_no_eol_lnum += offset;
}
+ // Subfuncions for readdirex()
+ #ifdef FEAT_EVAL
+ # ifdef MSWIN
+ static char_u *
+ getfpermwfd(WIN32_FIND_DATAW *wfd, char_u *perm)
+ {
+ stat_T st;
+ unsigned short st_mode;
+ DWORD flag = wfd->dwFileAttributes;
+ WCHAR *wp;
+
+ st_mode = (flag & FILE_ATTRIBUTE_DIRECTORY)
+ ? (_S_IFDIR | _S_IEXEC) : _S_IFREG;
+ st_mode |= (flag & FILE_ATTRIBUTE_READONLY)
+ ? _S_IREAD : (_S_IREAD | _S_IWRITE);
+
+ wp = wcsrchr(wfd->cFileName, L'.');
+ if (wp != NULL)
+ {
+ if (_wcsicmp(wp, L".exe") == 0 ||
+ _wcsicmp(wp, L".com") == 0 ||
+ _wcsicmp(wp, L".cmd") == 0 ||
+ _wcsicmp(wp, L".bat") == 0)
+ st_mode |= _S_IEXEC;
+ }
+
+ // Copy user bits to group/other.
+ st_mode |= (st_mode & 0700) >> 3;
+ st_mode |= (st_mode & 0700) >> 6;
+
+ st.st_mode = st_mode;
+ return getfpermst(&st, perm);
+ }
+
+ static char_u *
+ getftypewfd(WIN32_FIND_DATAW *wfd)
+ {
+ DWORD flag = wfd->dwFileAttributes;
+ DWORD tag = wfd->dwReserved0;
+
+ if (flag & FILE_ATTRIBUTE_REPARSE_POINT)
+ {
+ if (tag == IO_REPARSE_TAG_MOUNT_POINT)
+ return (char_u*)"junction";
+ else if (tag == IO_REPARSE_TAG_SYMLINK)
+ {
+ if (flag & FILE_ATTRIBUTE_DIRECTORY)
+ return (char_u*)"linkd";
+ else
+ return (char_u*)"link";
+ }
+ return (char_u*)"reparse"; // unknown reparse point type
+ }
+ if (flag & FILE_ATTRIBUTE_DIRECTORY)
+ return (char_u*)"dir";
+ else
+ return (char_u*)"file";
+ }
+
+ static dict_T *
+ create_readdirex_item(WIN32_FIND_DATAW *wfd)
+ {
+ dict_T *item;
+ char_u *p;
+ varnumber_T size, time;
+ char_u permbuf[] = "---------";
+
+ item = dict_alloc();
+ if (item == NULL)
+ return NULL;
+ item->dv_refcount++;
+
+ p = utf16_to_enc(wfd->cFileName, NULL);
+ if (p == NULL)
+ goto theend;
+ if (dict_add_string(item, "name", p) == FAIL)
+ {
+ vim_free(p);
+ goto theend;
+ }
+ vim_free(p);
+
+ size = (((varnumber_T)wfd->nFileSizeHigh) << 32) | wfd->nFileSizeLow;
+ if (dict_add_number(item, "size", size) == FAIL)
+ goto theend;
+
+ // Convert FILETIME to unix time.
+ time = (((((varnumber_T)wfd->ftLastWriteTime.dwHighDateTime) << 32) |
+ wfd->ftLastWriteTime.dwLowDateTime)
+ - 116444736000000000) / 10000000;
+ if (dict_add_number(item, "time", time) == FAIL)
+ goto theend;
+
+ if (dict_add_string(item, "type", getftypewfd(wfd)) == FAIL)
+ goto theend;
+ if (dict_add_string(item, "perm", getfpermwfd(wfd, permbuf)) == FAIL)
+ goto theend;
+
+ if (dict_add_string(item, "user", (char_u*)"") == FAIL)
+ goto theend;
+ if (dict_add_string(item, "group", (char_u*)"") == FAIL)
+ goto theend;
+
+ return item;
+
+ theend:
+ dict_unref(item);
+ return NULL;
+ }
+ # else
+ static dict_T *
+ create_readdirex_item(char_u *path, char_u *name)
+ {
+ dict_T *item;
+ char *p;
+ size_t len;
+ stat_T st;
+ int ret, link = FALSE;
+ varnumber_T size;
+ char_u permbuf[] = "---------";
+ char_u *q;
+ struct passwd *pw;
+ struct group *gr;
+
+ item = dict_alloc();
+ if (item == NULL)
+ return NULL;
+ item->dv_refcount++;
+
+ len = STRLEN(path) + 1 + STRLEN(name) + 1;
+ p = alloc(len);
+ if (p == NULL)
+ goto theend;
+ vim_snprintf(p, len, "%s/%s", path, name);
+ ret = mch_lstat(p, &st);
+ if (ret >= 0 && S_ISLNK(st.st_mode))
+ {
+ link = TRUE;
+ ret = mch_stat(p, &st);
+ }
+ vim_free(p);
+
+ if (dict_add_string(item, "name", name) == FAIL)
+ goto theend;
+
+ if (ret >= 0)
+ {
+ size = (varnumber_T)st.st_size;
+ if (S_ISDIR(st.st_mode))
+ size = 0;
+ // non-perfect check for overflow
+ if ((off_T)size != (off_T)st.st_size)
+ size = -2;
+ if (dict_add_number(item, "size", size) == FAIL)
+ goto theend;
+ if (dict_add_number(item, "time", (varnumber_T)st.st_mtime) == FAIL)
+ goto theend;
+
+ if (link)
+ {
+ if (S_ISDIR(st.st_mode))
+ q = (char_u*)"linkd";
+ else
+ q = (char_u*)"link";
+ }
+ else
+ q = getftypest(&st);
+ if (dict_add_string(item, "type", q) == FAIL)
+ goto theend;
+ if (dict_add_string(item, "perm", getfpermst(&st, permbuf)) == FAIL)
+ goto theend;
+
+ pw = getpwuid(st.st_uid);
+ if (pw == NULL)
+ q = (char_u*)"";
+ else
+ q = (char_u*)pw->pw_name;
+ if (dict_add_string(item, "user", q) == FAIL)
+ goto theend;
+ gr = getgrgid(st.st_gid);
+ if (gr == NULL)
+ q = (char_u*)"";
+ else
+ q = (char_u*)gr->gr_name;
+ if (dict_add_string(item, "group", q) == FAIL)
+ goto theend;
+ }
+ else
+ {
+ if (dict_add_number(item, "size", -1) == FAIL)
+ goto theend;
+ if (dict_add_number(item, "time", -1) == FAIL)
+ goto theend;
+ if (dict_add_string(item, "type", (char_u*)"") == FAIL)
+ goto theend;
+ if (dict_add_string(item, "perm", (char_u*)"") == FAIL)
+ goto theend;
+ if (dict_add_string(item, "user", (char_u*)"") == FAIL)
+ goto theend;
+ if (dict_add_string(item, "group", (char_u*)"") == FAIL)
+ goto theend;
+ }
+ return item;
+
+ theend:
+ dict_unref(item);
+ return NULL;
+ }
+ # endif
+
+ static int
+ compare_readdirex_item(const void *p1, const void *p2)
+ {
+ char_u *name1, *name2;
+
+ name1 = dict_get_string(*(dict_T**)p1, (char_u*)"name", FALSE);
+ name2 = dict_get_string(*(dict_T**)p2, (char_u*)"name", FALSE);
+ return STRCMP(name1, name2);
+ }
+ #endif
+
#if defined(TEMPDIRNAMES) || defined(FEAT_EVAL) || defined(PROTO)
/*
! * Core part of "readdir()" and "readdirex()" function.
* Retrieve the list of files/directories of "path" into "gap".
+ * If "withattr" is TRUE, retrieve the names and their attributes.
+ * If "withattr" is FALSE, retrieve the names only.
* Return OK for success, FAIL for failure.
*/
int
readdir_core(
garray_T *gap,
char_u *path,
+ int withattr UNUSED,
void *context,
! int (*checkitem)(void *context, void *item))
{
! int failed = FALSE;
! char_u *p;
! # ifdef MSWIN
! char_u *buf;
! int ok;
! HANDLE hFind = INVALID_HANDLE_VALUE;
! WIN32_FIND_DATAW wfd;
! WCHAR *wn = NULL; // UTF-16 name, NULL when not used.
! # else
! DIR *dirp;
! struct dirent *dp;
! # endif
! ga_init2(gap, (int)sizeof(void *), 20);
!
! # ifdef FEAT_EVAL
! # define FREE_ITEM(item) do { \
! if (withattr) \
! dict_unref((dict_T*)item); \
! else \
! vim_free(item); \
! } while (0)
! # else
! # define FREE_ITEM(item) vim_free(item)
! # endif
# ifdef MSWIN
+ buf = alloc(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(p, "\\*");
+
+ wn = enc_to_utf16(buf, NULL);
+ if (wn != NULL)
+ hFind = FindFirstFileW(wn, &wfd);
+ ok = (hFind != INVALID_HANDLE_VALUE);
+ if (!ok)
{
! failed = TRUE;
! smsg(_(e_notopen), path);
! }
! else
! {
! while (ok)
{
! int ignore;
! void *item;
! WCHAR *wp;
!
! wp = wfd.cFileName;
! ignore = wp[0] == L'.' &&
! (wp[1] == NUL ||
! (wp[1] == L'.' && wp[2] == NUL));
! # ifdef FEAT_EVAL
! if (withattr)
! item = (void*)create_readdirex_item(&wfd);
! else
! # endif
! item = (void*)utf16_to_enc(wfd.cFileName, NULL);
! if (item == NULL)
{
! failed = TRUE;
! break;
! }
! if (!ignore && checkitem != NULL)
! {
! int r = checkitem(context, item);
! if (r < 0)
{
! FREE_ITEM(item);
! break;
}
+ if (r == 0)
+ ignore = TRUE;
+ }
! if (!ignore)
! {
! if (ga_grow(gap, 1) == OK)
! ((void**)gap->ga_data)[gap->ga_len++] = item;
! else
{
! failed = TRUE;
! FREE_ITEM(item);
! break;
}
}
! else
! FREE_ITEM(item);
!
! ok = FindNextFileW(hFind, &wfd);
}
+ FindClose(hFind);
+ }
! vim_free(buf);
! vim_free(wn);
! # else // MSWIN
! dirp = opendir((char *)path);
! if (dirp == NULL)
! {
! failed = TRUE;
! smsg(_(e_notopen), path);
}
! else
{
! for (;;)
{
! int ignore;
! void *item;
!
! dp = readdir(dirp);
! if (dp == NULL)
! break;
! p = (char_u *)dp->d_name;
!
! ignore = p[0] == '.' &&
! (p[1] == NUL ||
! (p[1] == '.' && p[2] == NUL));
! # ifdef FEAT_EVAL
! if (withattr)
! item = (void*)create_readdirex_item(path, p);
! else
! # endif
! item = (void*)vim_strsave(p);
! if (item == NULL)
{
! failed = TRUE;
! break;
! }
! if (!ignore && checkitem != NULL)
! {
! int r = checkitem(context, item);
! if (r < 0)
{
! FREE_ITEM(item);
! break;
}
+ if (r == 0)
+ ignore = TRUE;
+ }
! if (!ignore)
! {
! if (ga_grow(gap, 1) == OK)
! ((void**)gap->ga_data)[gap->ga_len++] = item;
! else
{
! failed = TRUE;
! FREE_ITEM(item);
! break;
}
}
! else
! FREE_ITEM(item);
}
+
+ closedir(dirp);
}
! # endif // MSWIN
!
! # undef FREE_ITEM
if (!failed && gap->ga_len > 0)
! {
! # ifdef FEAT_EVAL
! if (withattr)
! qsort((void*)gap->ga_data, (size_t)gap->ga_len, sizeof(dict_T*),
! compare_readdirex_item);
! else
! # endif
! sort_strings((char_u **)gap->ga_data, gap->ga_len);
! }
return failed ? FAIL : OK;
}
***************
*** 4594,4600 ****
exp = vim_strsave(name);
if (exp == NULL)
return -1;
! if (readdir_core(&ga, exp, NULL, NULL) == OK)
{
for (i = 0; i < ga.ga_len; ++i)
{
--- 4872,4878 ----
exp = vim_strsave(name);
if (exp == NULL)
return -1;
! if (readdir_core(&ga, exp, FALSE, NULL, NULL) == OK)
{
for (i = 0; i < ga.ga_len; ++i)
{
*** ../vim-8.2.0874/src/filepath.c 2020-05-13 22:44:18.142288807 +0200
--- src/filepath.c 2020-06-01 15:56:35.576460287 +0200
***************
*** 1029,1034 ****
--- 1029,1053 ----
}
/*
+ * Convert "st" to file permission string.
+ */
+ char_u *
+ getfpermst(stat_T *st, char_u *perm)
+ {
+ char_u flags[] = "rwx";
+ int i;
+
+ for (i = 0; i < 9; i++)
+ {
+ if (st->st_mode & (1 << (8 - i)))
+ perm[i] = flags[i % 3];
+ else
+ perm[i] = '-';
+ }
+ return perm;
+ }
+
+ /*
* "getfperm({fname})" function
*/
void
***************
*** 1037,1060 ****
char_u *fname;
stat_T st;
char_u *perm = NULL;
! char_u flags[] = "rwx";
! int i;
fname = tv_get_string(&argvars[0]);
rettv->v_type = VAR_STRING;
if (mch_stat((char *)fname, &st) >= 0)
! {
! perm = vim_strsave((char_u *)"---------");
! if (perm != NULL)
! {
! for (i = 0; i < 9; i++)
! {
! if (st.st_mode & (1 << (8 - i)))
! perm[i] = flags[i % 3];
! }
! }
! }
rettv->vval.v_string = perm;
}
--- 1056,1068 ----
char_u *fname;
stat_T st;
char_u *perm = NULL;
! char_u permbuf[] = "---------";
fname = tv_get_string(&argvars[0]);
rettv->v_type = VAR_STRING;
if (mch_stat((char *)fname, &st) >= 0)
! perm = vim_strsave(getfpermst(&st, permbuf));
rettv->vval.v_string = perm;
}
***************
*** 1106,1111 ****
--- 1114,1146 ----
}
/*
+ * Convert "st" to file type string.
+ */
+ char_u *
+ getftypest(stat_T *st)
+ {
+ char *t;
+
+ if (S_ISREG(st->st_mode))
+ t = "file";
+ else if (S_ISDIR(st->st_mode))
+ t = "dir";
+ else if (S_ISLNK(st->st_mode))
+ t = "link";
+ else if (S_ISBLK(st->st_mode))
+ t = "bdev";
+ else if (S_ISCHR(st->st_mode))
+ t = "cdev";
+ else if (S_ISFIFO(st->st_mode))
+ t = "fifo";
+ else if (S_ISSOCK(st->st_mode))
+ t = "socket";
+ else
+ t = "other";
+ return (char_u*)t;
+ }
+
+ /*
* "getftype({fname})" function
*/
void
***************
*** 1114,1144 ****
char_u *fname;
stat_T st;
char_u *type = NULL;
- char *t;
fname = tv_get_string(&argvars[0]);
rettv->v_type = VAR_STRING;
if (mch_lstat((char *)fname, &st) >= 0)
! {
! if (S_ISREG(st.st_mode))
! t = "file";
! else if (S_ISDIR(st.st_mode))
! t = "dir";
! else if (S_ISLNK(st.st_mode))
! t = "link";
! else if (S_ISBLK(st.st_mode))
! t = "bdev";
! else if (S_ISCHR(st.st_mode))
! t = "cdev";
! else if (S_ISFIFO(st.st_mode))
! t = "fifo";
! else if (S_ISSOCK(st.st_mode))
! t = "socket";
! else
! t = "other";
! type = vim_strsave((char_u *)t);
! }
rettv->vval.v_string = type;
}
--- 1149,1160 ----
char_u *fname;
stat_T st;
char_u *type = NULL;
fname = tv_get_string(&argvars[0]);
rettv->v_type = VAR_STRING;
if (mch_lstat((char *)fname, &st) >= 0)
! type = vim_strsave(getftypest(&st));
rettv->vval.v_string = type;
}
***************
*** 1359,1365 ****
* Evaluate "expr" (= "context") for readdir().
*/
static int
! readdir_checkitem(void *context, char_u *name)
{
typval_T *expr = (typval_T *)context;
typval_T save_val;
--- 1375,1381 ----
* Evaluate "expr" (= "context") for readdir().
*/
static int
! readdir_checkitem(void *context, void *item)
{
typval_T *expr = (typval_T *)context;
typval_T save_val;
***************
*** 1367,1375 ****
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);
--- 1383,1389 ----
typval_T argv[2];
int retval = 0;
int error = FALSE;
! char_u *name = (char_u*)item;
prepare_vimvar(VV_VAL, &save_val);
set_vim_var_string(VV_VAL, name, -1);
***************
*** 1408,1415 ****
path = tv_get_string(&argvars[0]);
expr = &argvars[1];
! ret = readdir_core(&ga, path, (void *)expr, readdir_checkitem);
! if (ret == OK && rettv->vval.v_list != NULL && ga.ga_len > 0)
{
for (i = 0; i < ga.ga_len; i++)
{
--- 1422,1430 ----
path = tv_get_string(&argvars[0]);
expr = &argvars[1];
! ret = readdir_core(&ga, path, FALSE, (void *)expr,
! (expr->v_type == VAR_UNKNOWN) ? NULL : readdir_checkitem);
! if (ret == OK)
{
for (i = 0; i < ga.ga_len; i++)
{
***************
*** 1421,1426 ****
--- 1436,1505 ----
}
/*
+ * Evaluate "expr" (= "context") for readdirex().
+ */
+ static int
+ readdirex_checkitem(void *context, void *item)
+ {
+ typval_T *expr = (typval_T *)context;
+ typval_T save_val;
+ typval_T rettv;
+ typval_T argv[2];
+ int retval = 0;
+ int error = FALSE;
+ dict_T *dict = (dict_T*)item;
+
+ prepare_vimvar(VV_VAL, &save_val);
+ set_vim_var_dict(VV_VAL, dict);
+ argv[0].v_type = VAR_DICT;
+ argv[0].vval.v_dict = dict;
+
+ 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_dict(VV_VAL, NULL);
+ restore_vimvar(VV_VAL, &save_val);
+ return retval;
+ }
+
+ /*
+ * "readdirex()" function
+ */
+ void
+ f_readdirex(typval_T *argvars, typval_T *rettv)
+ {
+ typval_T *expr;
+ int ret;
+ char_u *path;
+ garray_T ga;
+ int i;
+
+ if (rettv_list_alloc(rettv) == FAIL)
+ return;
+ path = tv_get_string(&argvars[0]);
+ expr = &argvars[1];
+
+ ret = readdir_core(&ga, path, TRUE, (void *)expr,
+ (expr->v_type == VAR_UNKNOWN) ? NULL : readdirex_checkitem);
+ if (ret == OK)
+ {
+ for (i = 0; i < ga.ga_len; i++)
+ {
+ dict_T *dict = ((dict_T**)ga.ga_data)[i];
+ list_append_dict(rettv->vval.v_list, dict);
+ dict_unref(dict);
+ }
+ }
+ ga_clear(&ga);
+ }
+
+ /*
* "readfile()" function
*/
void
*** ../vim-8.2.0874/src/proto/
fileio.pro 2019-12-12 12:55:21.000000000 +0100
--- src/proto/
fileio.pro 2020-06-01 15:56:35.576460287 +0200
***************
*** 31,37 ****
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);
--- 31,37 ----
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, int withattr, void *context, int (*checkitem)(void *context, void *item));
int delete_recursive(char_u *name);
void vim_deltempdir(void);
char_u *vim_tempname(int extra_char, int keep);
*** ../vim-8.2.0874/src/proto/
filepath.pro 2019-12-12 12:55:21.000000000 +0100
--- src/proto/
filepath.pro 2020-06-01 15:56:35.576460287 +0200
***************
*** 10,18 ****
--- 10,20 ----
void f_findfile(typval_T *argvars, typval_T *rettv);
void f_fnamemodify(typval_T *argvars, typval_T *rettv);
void f_getcwd(typval_T *argvars, typval_T *rettv);
+ char_u *getfpermst(stat_T *st, char_u *perm);
void f_getfperm(typval_T *argvars, typval_T *rettv);
void f_getfsize(typval_T *argvars, typval_T *rettv);
void f_getftime(typval_T *argvars, typval_T *rettv);
+ char_u *getftypest(stat_T *st);
void f_getftype(typval_T *argvars, typval_T *rettv);
void f_glob(typval_T *argvars, typval_T *rettv);
void f_glob2regpat(typval_T *argvars, typval_T *rettv);
***************
*** 21,26 ****
--- 23,29 ----
void f_mkdir(typval_T *argvars, typval_T *rettv);
void f_pathshorten(typval_T *argvars, typval_T *rettv);
void f_readdir(typval_T *argvars, typval_T *rettv);
+ void f_readdirex(typval_T *argvars, typval_T *rettv);
void f_readfile(typval_T *argvars, typval_T *rettv);
void f_resolve(typval_T *argvars, typval_T *rettv);
void f_tempname(typval_T *argvars, typval_T *rettv);
*** ../vim-8.2.0874/src/testdir/test_functions.vim 2020-05-31 22:19:52.898638027 +0200
--- src/testdir/test_functions.vim 2020-06-01 15:56:35.576460287 +0200
***************
*** 1834,1840 ****
call assert_equal(['bar.txt', 'dir', 'foo.txt'], sort(files))
" Only results containing "f"
! let files = 'Xdir'->readdir({ x -> stridx(x, 'f') !=- 1 })
call assert_equal(['foo.txt'], sort(files))
" Only .txt files
--- 1834,1840 ----
call assert_equal(['bar.txt', 'dir', 'foo.txt'], sort(files))
" Only results containing "f"
! let files = 'Xdir'->readdir({ x -> stridx(x, 'f') != -1 })
call assert_equal(['foo.txt'], sort(files))
" Only .txt files
***************
*** 1855,1860 ****
--- 1855,1899 ----
call sort(files)->assert_equal(['bar.txt', 'dir', 'foo.txt'])
eval 'Xdir'->delete('rf')
+ endfunc
+
+ func Test_readdirex()
+ call mkdir('Xdir')
+ call writefile([], 'Xdir/foo.txt')
+ call writefile([], 'Xdir/bar.txt')
+ call mkdir('Xdir/dir')
+
+ " All results
+ let files = readdirex('Xdir')->map({-> v:
val.name})
+ call assert_equal(['bar.txt', 'dir', 'foo.txt'], sort(files))
+
+ " Only results containing "f"
+ let files = 'Xdir'->readdirex({ e -> stridx(
e.name, 'f') != -1 })
+ \ ->map({-> v:
val.name})
+ call assert_equal(['foo.txt'], sort(files))
+
+ " Only .txt files
+ let files = readdirex('Xdir', { e ->
e.name =~ '.txt$' })
+ \ ->map({-> v:
val.name})
+ call assert_equal(['bar.txt', 'foo.txt'], sort(files))
+
+ " Only .txt files with string
+ let files = readdirex('Xdir', 'v:
val.name =~ ".txt$"')
+ \ ->map({-> v:
val.name})
+ call assert_equal(['bar.txt', 'foo.txt'], sort(files))
+
+ " Limit to 1 result.
+ let l = []
+ let files = readdirex('Xdir', {e -> len(add(l,
e.name)) == 2 ? -1 : 1})
+ \ ->map({-> v:
val.name})
+ call assert_equal(1, len(files))
+
+ " Nested readdirex() must not crash
+ let files = readdirex('Xdir', 'readdirex("Xdir", "1") != []')
+ \ ->map({-> v:
val.name})
+ call sort(files)->assert_equal(['bar.txt', 'dir', 'foo.txt'])
+
+ eval 'Xdir'->delete('rf')
endfunc
func Test_delete_rf()
*** ../vim-8.2.0874/src/version.c 2020-06-01 15:05:16.300297224 +0200
--- src/version.c 2020-06-01 15:57:31.716243691 +0200
***************
*** 748,749 ****
--- 748,751 ----
{ /* Add new patch number below this line */
+ /**/
+ 875,
/**/
--
Eagles may soar, but weasels don't get sucked into jet engines.
/// Bram Moolenaar -- Br...@Moolenaar.net --
http://www.Moolenaar.net \\\
/// sponsor Vim, vote for features --
http://www.Vim.org/sponsor/ \\\
\\\ an exciting new programming language --
http://www.Zimbu.org ///
\\\ help me help AIDS victims --
http://ICCF-Holland.org ///