patch 9.1.0923: too many strlen() calls in filepath.c
Problem: too many strlen() calls in filepath.c
Solution: refactor filepath.c and remove calls to STRLEN(),
unify dos_expandpath() and unix_expandpath() into
a single function
closes: #16160
Signed-off-by: John Marriott <basilisk@internode.on.net>
Signed-off-by: Christian Brabandt <cb@256bit.org>
diff --git a/src/filepath.c b/src/filepath.c
index 3dd71bc..7298842 100644
--- a/src/filepath.c
+++ b/src/filepath.c
@@ -29,12 +29,11 @@
static int
get_short_pathname(char_u **fnamep, char_u **bufp, int *fnamelen)
{
- int l, len;
+ int l;
WCHAR *newbuf;
WCHAR *wfname;
- len = MAXPATHL;
- newbuf = malloc(len * sizeof(*newbuf));
+ newbuf = alloc(MAXPATHL * sizeof(*newbuf));
if (newbuf == NULL)
return FAIL;
@@ -45,8 +44,8 @@
return FAIL;
}
- l = GetShortPathNameW(wfname, newbuf, len);
- if (l > len - 1)
+ l = GetShortPathNameW(wfname, newbuf, MAXPATHL);
+ if (l > MAXPATHL - 1)
{
// If that doesn't work (not enough space), then save the string
// and try again with a new buffer big enough.
@@ -105,7 +104,7 @@
char_u **bufp,
int *fnamelen)
{
- char_u *short_fname, *save_fname, *pbuf_unused;
+ char_u *short_fname = NULL, *save_fname = NULL, *pbuf_unused = NULL;
char_u *endp, *save_endp;
char_u ch;
int old_len, len;
@@ -115,6 +114,11 @@
// Make a copy
old_len = *fnamelen;
save_fname = vim_strnsave(*fname, old_len);
+ if (save_fname == NULL)
+ {
+ retval = FAIL;
+ goto theend;
+ }
pbuf_unused = NULL;
short_fname = NULL;
@@ -139,9 +143,9 @@
* resulting path.
*/
ch = *endp;
- *endp = 0;
+ *endp = NUL;
short_fname = save_fname;
- len = (int)STRLEN(short_fname) + 1;
+ len = (int)(endp - save_fname) + 1;
if (get_short_pathname(&short_fname, &pbuf_unused, &len) == FAIL)
{
retval = FAIL;
@@ -225,7 +229,7 @@
if (vim_ispathsep(*p))
++sepcount;
- // Need full path first (use expand_env() to remove a "~/")
+ // Need full path first (use expand_env_save() to remove a "~/")
hasTilde = (**fnamep == '~');
if (hasTilde)
pbuf = tfname = expand_env_save(*fnamep);
@@ -273,7 +277,7 @@
// Copy in the string - p indexes into tfname - allocated at pbuf
vim_free(*bufp);
- *fnamelen = (int)STRLEN(p);
+ *fnamelen = (int)((tfname + len) - p);
*bufp = pbuf;
*fnamep = p;
@@ -414,7 +418,7 @@
continue;
}
pbuf = NULL;
- // Need full path first (use expand_env() to remove a "~/")
+ // Need full path first (use expand_env_save() to remove a "~/")
if (!has_fullname && !has_homerelative)
{
if (**fnamep == '~')
@@ -505,7 +509,7 @@
if (*fnamelen == 0)
{
// Result is empty. Turn it into "." to make ":cd %:h" work.
- p = vim_strsave((char_u *)".");
+ p = vim_strnsave((char_u *)".", 1);
if (p == NULL)
return -1;
vim_free(*bufp);
@@ -1544,8 +1548,10 @@
tv[0].vval.v_string = created;
tv[1].v_type = VAR_STRING;
tv[1].v_lock = 0;
- tv[1].vval.v_string = vim_strsave(
- (char_u *)(defer_recurse ? "rf" : "d"));
+ if (defer_recurse)
+ tv[1].vval.v_string = vim_strnsave((char_u *)"rf", 2);
+ else
+ tv[1].vval.v_string = vim_strnsave((char_u *)"d", 1);
if (tv[0].vval.v_string == NULL || tv[1].vval.v_string == NULL
|| add_defer((char_u *)"delete", 2, tv) == FAIL)
{
@@ -2058,6 +2064,8 @@
char_u *p;
#ifdef HAVE_READLINK
char_u *buf = NULL;
+ char_u *remain = NULL;
+ int p_was_allocated = FALSE;
#endif
if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
@@ -2077,110 +2085,166 @@
#else
# ifdef HAVE_READLINK
{
+ size_t plen;
+ size_t buflen;
char_u *cpy;
- int len;
- char_u *remain = NULL;
+ size_t cpysize;
+ char_u *r = NULL; // points to current position in "remain"
+ size_t rlen = 0; // length of r (excluding the NUL)
char_u *q;
int is_relative_to_current = FALSE;
int has_trailing_pathsep = FALSE;
int limit = 100;
+ size_t len;
- p = vim_strsave(p);
+ rettv->vval.v_string = NULL;
+
+ plen = STRLEN(p);
+ p = vim_strnsave(p, plen);
if (p == NULL)
goto fail;
+
+ p_was_allocated = TRUE;
if (p[0] == '.' && (vim_ispathsep(p[1])
|| (p[1] == '.' && (vim_ispathsep(p[2])))))
is_relative_to_current = TRUE;
- len = STRLEN(p);
- if (len > 1 && after_pathsep(p, p + len))
+ if (plen > 1 && after_pathsep(p, p + plen))
{
has_trailing_pathsep = TRUE;
- p[len - 1] = NUL; // the trailing slash breaks readlink()
+ p[--plen] = NUL; // the trailing slash breaks readlink()
}
q = getnextcomp(p);
if (*q != NUL)
{
+ char_u *q_prev = q - 1;
+
+ // getnextcomp() finds the first path separator.
+ // if there is a run of >1 path separators, set all
+ // but the last in the run to NUL.
+ while (*q != NUL && vim_ispathsep(*q))
+ {
+ *q_prev = NUL;
+ q_prev = q;
+ MB_PTR_ADV(q);
+ }
+ q = q_prev;
+
// Separate the first path component in "p", and keep the
// remainder (beginning with the path separator).
- remain = vim_strsave(q - 1);
- q[-1] = NUL;
+ rlen = (size_t)(plen - (q - p));
+ r = remain = vim_strnsave(q, rlen);
+ if (remain == NULL)
+ rlen = 0;
+ *q = NUL;
+ plen -= rlen;
}
buf = alloc(MAXPATHL + 1);
if (buf == NULL)
- {
- vim_free(p);
- vim_free(remain);
goto fail;
- }
for (;;)
{
for (;;)
{
- len = readlink((char *)p, (char *)buf, MAXPATHL);
- if (len <= 0)
+ ssize_t rv = readlink((char *)p, (char *)buf, MAXPATHL);
+ if (rv <= 0)
break;
- buf[len] = NUL;
if (limit-- == 0)
{
- vim_free(p);
- vim_free(remain);
emsg(_(e_too_many_symbolic_links_cycle));
- rettv->vval.v_string = NULL;
goto fail;
}
+ buflen = (size_t)rv;
+ buf[buflen] = NUL;
+
// Ensure that the result will have a trailing path separator
// if the argument has one.
- if (remain == NULL && has_trailing_pathsep)
- add_pathsep(buf);
+ if (remain == NULL && has_trailing_pathsep && !after_pathsep(buf, buf + buflen))
+ {
+ STRCPY(buf + buflen, PATHSEPSTR);
+ ++buflen;
+ }
// Separate the first path component in the link value and
// concatenate the remainders.
q = getnextcomp(vim_ispathsep(*buf) ? buf + 1 : buf);
if (*q != NUL)
{
+ char_u *q_prev = q - 1;
+
+ // getnextcomp() finds the first path separator.
+ // if there is a run of >1 path separators, set all
+ // but the last in the run to NUL.
+ while (*q != NUL && vim_ispathsep(*q))
+ {
+ *q_prev = NUL;
+ q_prev = q;
+ MB_PTR_ADV(q);
+ }
+ q = q_prev;
+
if (remain == NULL)
- remain = vim_strsave(q - 1);
+ {
+ rlen = (size_t)(buflen - (q - buf));
+ r = remain = vim_strnsave(q, rlen);
+ if (remain == NULL)
+ rlen = 0;
+ }
else
{
- cpy = concat_str(q - 1, remain);
- if (cpy != NULL)
- {
- vim_free(remain);
- remain = cpy;
- }
+ len = (size_t)(buflen - (q - buf));
+ cpysize = (size_t)(len + rlen + 1); // +1 for NUL
+ cpy = alloc(plen + buflen + 1);
+ if (cpy == NULL)
+ goto fail;
+
+ rlen = (size_t)vim_snprintf((char *)cpy, cpysize, "%.*s%s", (int)len, q, r);
+ vim_free(remain);
+ r = remain = cpy;
}
- q[-1] = NUL;
+ *q = NUL;
+ buflen = (size_t)(q - buf);
}
q = gettail(p);
if (q > p && *q == NUL)
{
// Ignore trailing path separator.
- p[q - p - 1] = NUL;
+ plen = (size_t)(q - p - 1);
+ p[plen] = NUL;
q = gettail(p);
}
if (q > p && !mch_isFullName(buf))
{
+ char_u *tail;
+
// symlink is relative to directory of argument
- cpy = alloc(STRLEN(p) + STRLEN(buf) + 1);
- if (cpy != NULL)
- {
- STRCPY(cpy, p);
- STRCPY(gettail(cpy), buf);
- vim_free(p);
- p = cpy;
- }
+ cpy = alloc(plen + buflen + 1);
+ if (cpy == NULL)
+ goto fail;
+
+ STRCPY(cpy, p);
+ tail = gettail(cpy);
+ if (*tail != NUL)
+ plen -= (size_t)(plen - (tail - cpy)); // remove portion that will be replaced
+ STRCPY(tail, buf);
+ vim_free(p);
+ p = cpy;
+ plen += buflen;
}
else
{
vim_free(p);
- p = vim_strsave(buf);
+ p = vim_strnsave(buf, buflen);
+ if (p == NULL)
+ goto fail;
+
+ plen = buflen;
}
}
@@ -2188,20 +2252,29 @@
break;
// Append the first path component of "remain" to "p".
- q = getnextcomp(remain + 1);
- len = q - remain - (*q != NUL);
- cpy = vim_strnsave(p, STRLEN(p) + len);
- if (cpy != NULL)
- {
- STRNCAT(cpy, remain, len);
- vim_free(p);
- p = cpy;
- }
+ q = getnextcomp(r + 1);
+ len = (size_t)(q - r);
+ cpysize = (size_t)(plen + len + 1); // +1 for NUL
+ cpy = alloc(cpysize);
+ if (cpy == NULL)
+ goto fail;
+
+ plen = (size_t)vim_snprintf((char *)cpy, cpysize, "%s%.*s", p, (int)len, r);
+ vim_free(p);
+ p = cpy;
+
// Shorten "remain".
if (*q != NUL)
- STRMOVE(remain, q - 1);
+ {
+ r += len;
+ rlen -= len;
+ }
else
+ {
VIM_CLEAR(remain);
+ r = NULL;
+ rlen = 0;
+ }
}
// If the result is a relative path name, make it explicitly relative to
@@ -2218,12 +2291,14 @@
|| vim_ispathsep(p[2]))))))
{
// Prepend "./".
- cpy = concat_str((char_u *)"./", p);
- if (cpy != NULL)
- {
- vim_free(p);
- p = cpy;
- }
+ cpysize = plen + 3; // +2 for "./" and +1 for NUL
+ cpy = alloc(cpysize);
+ if (cpy == NULL)
+ goto fail;
+
+ plen = (size_t)vim_snprintf((char *)cpy, cpysize, "./%s", p);
+ vim_free(p);
+ p = cpy;
}
else if (!is_relative_to_current)
{
@@ -2232,18 +2307,17 @@
while (q[0] == '.' && vim_ispathsep(q[1]))
q += 2;
if (q > p)
- STRMOVE(p, p + 2);
+ {
+ mch_memmove(p, p + 2, (plen - 2) + 1);
+ plen -= 2;
+ }
}
}
// Ensure that the result will have no trailing path separator
// if the argument had none. But keep "/" or "//".
- if (!has_trailing_pathsep)
- {
- q = p + STRLEN(p);
- if (after_pathsep(p, q))
- *gettail_sep(p) = NUL;
- }
+ if (!has_trailing_pathsep && after_pathsep(p, p + plen))
+ *gettail_sep(p) = NUL;
rettv->vval.v_string = p;
}
@@ -2256,7 +2330,10 @@
#ifdef HAVE_READLINK
fail:
+ if (rettv->vval.v_string == NULL && p_was_allocated)
+ vim_free(p);
vim_free(buf);
+ vim_free(remain);
#endif
rettv->v_type = VAR_STRING;
}
@@ -2964,6 +3041,7 @@
{
while (*fname && !vim_ispathsep(*fname))
MB_PTR_ADV(fname);
+
if (*fname)
++fname;
return fname;
@@ -3116,16 +3194,18 @@
char_u *
concat_fnames(char_u *fname1, char_u *fname2, int sep)
{
+ size_t fname1len = STRLEN(fname1);
+ size_t destsize = fname1len + STRLEN(fname2) + 3;
char_u *dest;
- dest = alloc(STRLEN(fname1) + STRLEN(fname2) + 3);
+ dest = alloc(destsize);
if (dest == NULL)
return NULL;
- STRCPY(dest, fname1);
- if (sep)
- add_pathsep(dest);
- STRCAT(dest, fname2);
+ vim_snprintf((char *)dest, destsize, "%s%s%s",
+ fname1,
+ (sep && !after_pathsep(fname1, fname1 + fname1len)) ? PATHSEPSTR : "",
+ fname2);
return dest;
}
@@ -3136,8 +3216,14 @@
void
add_pathsep(char_u *p)
{
- if (*p != NUL && !after_pathsep(p, p + STRLEN(p)))
- STRCAT(p, PATHSEPSTR);
+ size_t plen;
+
+ if (p == NULL || *p == NUL)
+ return;
+
+ plen = STRLEN(p);
+ if (!after_pathsep(p, p + plen))
+ STRCPY(p + plen, PATHSEPSTR);
}
/*
@@ -3435,14 +3521,14 @@
}
#endif // VIM_BACKTICK
-#if defined(MSWIN)
+#if defined(MSWIN) || (defined(UNIX) && !defined(VMS)) || defined(USE_UNIXFILENAME) || defined(PROTO)
/*
- * File name expansion code for MS-DOS, Win16 and Win32. It's here because
+ * File name expansion code for Unix, Mac, MS-DOS, Win16 and Win32. It's here because
* it's shared between these systems.
*/
/*
- * comparison function for qsort in dos_expandpath()
+ * comparison function for qsort in unix_expandpath()
*/
static int
pstrcmp(const void *a, const void *b)
@@ -3457,33 +3543,35 @@
* "path" has backslashes before chars that are not to be expanded, starting
* at "path[wildoff]".
* Return the number of matches found.
- * NOTE: much of this is identical to unix_expandpath(), keep in sync!
*/
- static int
-dos_expandpath(
+ int
+unix_expandpath(
garray_T *gap,
- char_u *path,
- int wildoff,
- int flags, // EW_* flags
- int didstar) // expanded "**" once already
+ char_u *path,
+ int wildoff,
+ int flags, // EW_* flags
+ int didstar) // expanded "**" once already
{
- char_u *buf;
- char_u *path_end;
- char_u *p, *s, *e;
- int start_len = gap->ga_len;
- char_u *pat;
+ char_u *buf;
+ char_u *path_end;
+ size_t basepathlen; // length of non-variable portion of the path
+ size_t wildcardlen; // length of wildcard segment
+ char_u *p, *s, *e;
+ int start_len = gap->ga_len;
+ char_u *pat;
regmatch_T regmatch;
- int starts_with_dot;
- int matches;
- int len;
- int starstar = FALSE;
+ int starts_with_dot;
+ int matches; // number of matches found
+ int starstar = FALSE;
static int stardepth = 0; // depth for "**" expansion
- HANDLE hFind = INVALID_HANDLE_VALUE;
- WIN32_FIND_DATAW wfb;
- WCHAR *wn = NULL; // UCS-2 name, NULL when not used.
- char_u *matchname;
- int ok;
- char_u *p_alt;
+#ifdef MSWIN
+ HANDLE hFind = INVALID_HANDLE_VALUE;
+ WIN32_FIND_DATAW wfb;
+ WCHAR *wn = NULL; // UCS-2 name, NULL when not used.
+#else
+ DIR *dirp;
+#endif
+ int ok;
// Expanding "**" may take a long time, check for CTRL-C.
if (stardepth > 0)
@@ -3493,262 +3581,16 @@
return 0;
}
- // Make room for file name. When doing encoding conversion the actual
- // length may be quite a bit longer, thus use the maximum possible length.
+ // Make room for file name. When doing encoding conversion the actual
+ // length may be quite a bit longer.
buf = alloc(MAXPATHL);
if (buf == NULL)
return 0;
/*
* Find the first part in the path name that contains a wildcard or a ~1.
- * Copy it into buf, including the preceding characters.
- */
- p = buf;
- s = buf;
- e = NULL;
- path_end = path;
- while (*path_end != NUL)
- {
- // May ignore a wildcard that has a backslash before it; it will
- // be removed by rem_backslash() or file_pat_to_reg_pat() below.
- if (path_end >= path + wildoff && rem_backslash(path_end))
- *p++ = *path_end++;
- else if (*path_end == '\\' || *path_end == ':' || *path_end == '/')
- {
- if (e != NULL)
- break;
- s = p + 1;
- }
- else if (path_end >= path + wildoff
- && vim_strchr((char_u *)"*?[~", *path_end) != NULL)
- e = p;
- if (has_mbyte)
- {
- len = (*mb_ptr2len)(path_end);
- STRNCPY(p, path_end, len);
- p += len;
- path_end += len;
- }
- else
- *p++ = *path_end++;
- }
- e = p;
- *e = NUL;
-
- // now we have one wildcard component between s and e
- // Remove backslashes between "wildoff" and the start of the wildcard
- // component.
- for (p = buf + wildoff; p < s; ++p)
- if (rem_backslash(p))
- {
- STRMOVE(p, p + 1);
- --e;
- --s;
- }
-
- // Check for "**" between "s" and "e".
- for (p = s; p < e; ++p)
- if (p[0] == '*' && p[1] == '*')
- starstar = TRUE;
-
- starts_with_dot = *s == '.';
- pat = file_pat_to_reg_pat(s, e, NULL, FALSE);
- if (pat == NULL)
- {
- vim_free(buf);
- return 0;
- }
-
- // compile the regexp into a program
- if (flags & (EW_NOERROR | EW_NOTWILD))
- ++emsg_silent;
- regmatch.rm_ic = TRUE; // Always ignore case
- regmatch.regprog = vim_regcomp(pat, RE_MAGIC);
- if (flags & (EW_NOERROR | EW_NOTWILD))
- --emsg_silent;
- vim_free(pat);
-
- if (regmatch.regprog == NULL && (flags & EW_NOTWILD) == 0)
- {
- vim_free(buf);
- return 0;
- }
-
- // remember the pattern or file name being looked for
- matchname = vim_strsave(s);
-
- // If "**" is by itself, this is the first time we encounter it and more
- // is following then find matches without any directory.
- if (!didstar && stardepth < 100 && starstar && e - s == 2
- && *path_end == '/')
- {
- STRCPY(s, path_end + 1);
- ++stardepth;
- (void)dos_expandpath(gap, buf, (int)(s - buf), flags, TRUE);
- --stardepth;
- }
-
- // Scan all files in the directory with "dir/ *.*"
- STRCPY(s, "*.*");
- wn = enc_to_utf16(buf, NULL);
- if (wn != NULL)
- hFind = FindFirstFileW(wn, &wfb);
- ok = (hFind != INVALID_HANDLE_VALUE);
-
- while (ok)
- {
- p = utf16_to_enc(wfb.cFileName, NULL); // p is allocated here
-
- if (p == NULL)
- break; // out of memory
-
- // Do not use the alternate filename when the file name ends in '~',
- // because it picks up backup files: short name for "foo.vim~" is
- // "foo~1.vim", which matches "*.vim".
- if (*wfb.cAlternateFileName == NUL || p[STRLEN(p) - 1] == '~')
- p_alt = NULL;
- else
- p_alt = utf16_to_enc(wfb.cAlternateFileName, NULL);
-
- // Ignore entries starting with a dot, unless when asked for. Accept
- // all entries found with "matchname".
- if ((p[0] != '.' || starts_with_dot
- || ((flags & EW_DODOT)
- && p[1] != NUL && (p[1] != '.' || p[2] != NUL)))
- && (matchname == NULL
- || (regmatch.regprog != NULL
- && (vim_regexec(®match, p, (colnr_T)0)
- || (p_alt != NULL
- && vim_regexec(®match, p_alt, (colnr_T)0))))
- || ((flags & EW_NOTWILD)
- && fnamencmp(path + (s - buf), p, e - s) == 0)))
- {
- STRCPY(s, p);
- len = (int)STRLEN(buf);
-
- if (starstar && stardepth < 100
- && (wfb.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
- {
- // For "**" in the pattern first go deeper in the tree to
- // find matches.
- STRCPY(buf + len, "/**");
- STRCPY(buf + len + 3, path_end);
- ++stardepth;
- (void)dos_expandpath(gap, buf, len + 1, flags, TRUE);
- --stardepth;
- }
-
- STRCPY(buf + len, path_end);
- if (mch_has_exp_wildcard(path_end))
- {
- // need to expand another component of the path
- // remove backslashes for the remaining components only
- (void)dos_expandpath(gap, buf, len + 1, flags, FALSE);
- }
- else
- {
- stat_T sb;
-
- // no more wildcards, check if there is a match
- // remove backslashes for the remaining components only
- if (*path_end != 0)
- backslash_halve(buf + len + 1);
- // add existing file
- if ((flags & EW_ALLLINKS) ? mch_lstat((char *)buf, &sb) >= 0
- : mch_getperm(buf) >= 0)
- addfile(gap, buf, flags);
- }
- }
-
- vim_free(p_alt);
- vim_free(p);
- ok = FindNextFileW(hFind, &wfb);
- }
-
- FindClose(hFind);
- vim_free(wn);
- vim_free(buf);
- vim_regfree(regmatch.regprog);
- vim_free(matchname);
-
- matches = gap->ga_len - start_len;
- if (matches > 0)
- qsort(((char_u **)gap->ga_data) + start_len, (size_t)matches,
- sizeof(char_u *), pstrcmp);
- return matches;
-}
-
- int
-mch_expandpath(
- garray_T *gap,
- char_u *path,
- int flags) // EW_* flags
-{
- return dos_expandpath(gap, path, 0, flags, FALSE);
-}
-#endif // MSWIN
-
-#if (defined(UNIX) && !defined(VMS)) || defined(USE_UNIXFILENAME) \
- || defined(PROTO)
-/*
- * Unix style wildcard expansion code.
- * It's here because it's used both for Unix and Mac.
- */
- static int
-pstrcmp(const void *a, const void *b)
-{
- return (pathcmp(*(char **)a, *(char **)b, -1));
-}
-
-/*
- * Recursively expand one path component into all matching files and/or
- * directories. Adds matches to "gap". Handles "*", "?", "[a-z]", "**", etc.
- * "path" has backslashes before chars that are not to be expanded, starting
- * at "path + wildoff".
- * Return the number of matches found.
- * NOTE: much of this is identical to dos_expandpath(), keep in sync!
- */
- int
-unix_expandpath(
- garray_T *gap,
- char_u *path,
- int wildoff,
- int flags, // EW_* flags
- int didstar) // expanded "**" once already
-{
- char_u *buf;
- char_u *path_end;
- char_u *p, *s, *e;
- int start_len = gap->ga_len;
- char_u *pat;
- regmatch_T regmatch;
- int starts_with_dot;
- int matches;
- int len;
- int starstar = FALSE;
- static int stardepth = 0; // depth for "**" expansion
-
- DIR *dirp;
- struct dirent *dp;
-
- // Expanding "**" may take a long time, check for CTRL-C.
- if (stardepth > 0)
- {
- ui_breakcheck();
- if (got_int)
- return 0;
- }
-
- // make room for file name (a bit too much to stay on the safe side)
- size_t buflen = STRLEN(path) + MAXPATHL;
- buf = alloc(buflen);
- if (buf == NULL)
- return 0;
-
- /*
- * Find the first part in the path name that contains a wildcard.
- * When EW_ICASE is set every letter is considered to be a wildcard.
* Copy it into "buf", including the preceding characters.
+ * Note: for unix, when EW_ICASE is set every letter is considered to be a wildcard.
*/
p = buf;
s = buf;
@@ -3760,20 +3602,25 @@
// be removed by rem_backslash() or file_pat_to_reg_pat() below.
if (path_end >= path + wildoff && rem_backslash(path_end))
*p++ = *path_end++;
- else if (*path_end == '/')
+ else if (vim_ispathsep(*path_end))
{
if (e != NULL)
break;
s = p + 1;
}
else if (path_end >= path + wildoff
- && (vim_strchr((char_u *)"*?[{~$", *path_end) != NULL
- || (!p_fic && (flags & EW_ICASE)
- && vim_isalpha(PTR2CHAR(path_end)))))
+#ifdef MSWIN
+ && vim_strchr((char_u *)"*?[~", *path_end) != NULL
+#else
+ && (vim_strchr((char_u *)"*?[{~$", *path_end) != NULL
+ || (!p_fic && (flags & EW_ICASE)
+ && vim_isalpha(PTR2CHAR(path_end))))
+#endif
+ )
e = p;
if (has_mbyte)
{
- len = (*mb_ptr2len)(path_end);
+ int len = (*mb_ptr2len)(path_end);
STRNCPY(p, path_end, len);
p += len;
path_end += len;
@@ -3787,18 +3634,35 @@
// Now we have one wildcard component between "s" and "e".
// Remove backslashes between "wildoff" and the start of the wildcard
// component.
- for (p = buf + wildoff; p < s; ++p)
- if (rem_backslash(p))
+ p = buf + wildoff;
+ if (p < s)
+ {
+ size_t psize = STRLEN(p) + 1;
+
+ do
{
- STRMOVE(p, p + 1);
- --e;
- --s;
- }
+ if (!rem_backslash(p))
+ ++p;
+ else
+ {
+ mch_memmove(p, p + 1, psize);
+ --e;
+ --s;
+ }
+ --psize;
+ } while (p < s);
+ }
+
+ basepathlen = (size_t)(s - buf);
+ wildcardlen = (size_t)(e - s);
// Check for "**" between "s" and "e".
for (p = s; p < e; ++p)
if (p[0] == '*' && p[1] == '*')
+ {
starstar = TRUE;
+ break;
+ }
// convert the file pattern to a regexp pattern
starts_with_dot = *s == '.';
@@ -3810,10 +3674,14 @@
}
// compile the regexp into a program
+#ifdef MSWIN
+ regmatch.rm_ic = TRUE; // Always ignore case
+#else
if (flags & EW_ICASE)
- regmatch.rm_ic = TRUE; // 'wildignorecase' set
+ regmatch.rm_ic = TRUE; // 'wildignorecase' set
else
- regmatch.rm_ic = p_fic; // ignore case when 'fileignorecase' is set
+ regmatch.rm_ic = p_fic; // ignore case when 'fileignorecase' is set
+#endif
if (flags & (EW_NOERROR | EW_NOTWILD))
++emsg_silent;
regmatch.regprog = vim_regcomp(pat, RE_MAGIC);
@@ -3829,51 +3697,100 @@
// If "**" is by itself, this is the first time we encounter it and more
// is following then find matches without any directory.
- if (!didstar && stardepth < 100 && starstar && e - s == 2
- && *path_end == '/')
+ if (!didstar && stardepth < 100 && starstar && wildcardlen == 2
+ && *path_end == '/')
{
STRCPY(s, path_end + 1);
++stardepth;
- (void)unix_expandpath(gap, buf, (int)(s - buf), flags, TRUE);
+ (void)unix_expandpath(gap, buf, (int)basepathlen, flags, TRUE);
--stardepth;
}
+#ifdef MSWIN
+ // open the directory for scanning
+ STRCPY(s, "*.*");
+ wn = enc_to_utf16(buf, NULL);
+ if (wn != NULL)
+ hFind = FindFirstFileW(wn, &wfb);
+ ok = (hFind != INVALID_HANDLE_VALUE);
+#else
// open the directory for scanning
*s = NUL;
dirp = opendir(*buf == NUL ? "." : (char *)buf);
+ ok = (dirp != NULL);
+#endif
// Find all matching entries
- if (dirp != NULL)
+ if (ok)
{
+ char_u *d_name;
+#ifdef MSWIN
+ char_u *d_name_alt;
+ // remember the pattern or file name being looked for
+ char_u *matchname = vim_strnsave(s, basepathlen);
+#else
+ struct dirent *dp;
+#endif
+
while (!got_int)
{
+#ifdef MSWIN
+ d_name = utf16_to_enc(wfb.cFileName, NULL); // p is allocated here
+ if (d_name == NULL)
+ break; // out of memory
+
+ // Do not use the alternate filename when the file name ends in '~',
+ // because it picks up backup files: short name for "foo.vim~" is
+ // "foo~1.vim", which matches "*.vim".
+ if (*wfb.cAlternateFileName == NUL || d_name[STRLEN(d_name) - 1] == '~')
+ d_name_alt = NULL;
+ else
+ d_name_alt = utf16_to_enc(wfb.cAlternateFileName, NULL);
+#else
dp = readdir(dirp);
if (dp == NULL)
break;
- if ((dp->d_name[0] != '.' || starts_with_dot
- || ((flags & EW_DODOT)
- && dp->d_name[1] != NUL
- && (dp->d_name[1] != '.' || dp->d_name[2] != NUL)))
- && ((regmatch.regprog != NULL && vim_regexec(®match,
- (char_u *)dp->d_name, (colnr_T)0))
- || ((flags & EW_NOTWILD)
- && fnamencmp(path + (s - buf), dp->d_name, e - s) == 0)))
- {
- vim_strncpy(s, (char_u *)dp->d_name, buflen - (s - buf) - 1);
- len = STRLEN(buf);
+ d_name = (char_u *)dp->d_name;
+#endif
- if (starstar && stardepth < 100)
+ // Ignore entries starting with a dot, unless when asked for. For MSWIN accept
+ // all entries found with "matchname".
+ if (
+ (d_name[0] != '.' || starts_with_dot || (
+ (flags & EW_DODOT) && d_name[1] != NUL &&
+ (d_name[1] != '.' || d_name[2] != NUL)))
+ && (
+#ifdef MSWIN
+ matchname == NULL ||
+#endif
+ (regmatch.regprog != NULL
+ && vim_regexec(®match, (char_u *)d_name, (colnr_T)0))
+#ifdef MSWIN
+ || (d_name_alt != NULL
+ && vim_regexec(®match, d_name_alt, (colnr_T)0))
+#endif
+ || ((flags & EW_NOTWILD)
+ && fnamencmp(path + basepathlen, d_name, wildcardlen) == 0))
+ )
+ {
+ int len = (int)basepathlen + vim_snprintf((char *)s, (size_t)(MAXPATHL - (basepathlen + 1)), "%s", d_name);
+
+ if (starstar && stardepth < 100
+#ifdef MSWIN
+ && (wfb.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+#endif
+ )
{
// For "**" in the pattern first go deeper in the tree to
// find matches.
- vim_snprintf((char *)buf + len, buflen - len,
- "/**%s", path_end);
+ vim_snprintf((char *)buf + len, (size_t)(MAXPATHL - len),
+ "/**%s", path_end);
++stardepth;
(void)unix_expandpath(gap, buf, len + 1, flags, TRUE);
--stardepth;
}
- vim_snprintf((char *)buf + len, buflen - len, "%s", path_end);
+ vim_snprintf((char *)buf + len, (size_t)(MAXPATHL - len), "%s", path_end);
if (mch_has_exp_wildcard(path_end)) // handle more wildcards
{
// need to expand another component of the path
@@ -3890,7 +3807,7 @@
backslash_halve(buf + len + 1);
// add existing file or symbolic link
if ((flags & EW_ALLLINKS) ? mch_lstat((char *)buf, &sb) >= 0
- : mch_getperm(buf) >= 0)
+ : mch_getperm(buf) >= 0)
{
#ifdef MACOS_CONVERT
size_t precomp_len = STRLEN(buf)+1;
@@ -3907,11 +3824,26 @@
}
}
}
+
+#ifdef MSWIN
+ vim_free(d_name);
+ if (!FindNextFileW(hFind, &wfb))
+ break;
+#endif
}
+#ifdef MSWIN
+ FindClose(hFind);
+ vim_free(matchname);
+ vim_free(d_name_alt);
+#else
closedir(dirp);
+#endif
}
+#ifdef MSWIN
+ vim_free(wn);
+#endif
vim_free(buf);
vim_regfree(regmatch.regprog);
@@ -3920,9 +3852,24 @@
matches = gap->ga_len - start_len;
if (matches > 0 && !got_int)
qsort(((char_u **)gap->ga_data) + start_len, matches,
- sizeof(char_u *), pstrcmp);
+ sizeof(char_u *), pstrcmp);
return matches;
}
+
+/*
+ * Expand a path into all matching files and/or directories. Handles "*",
+ * "?", "[a-z]", "**", etc where appropriate for the platform.
+ * "path" has backslashes before chars that are not to be expanded.
+ * Returns the number of matches found.
+ */
+ int
+mch_expandpath(
+ garray_T *gap,
+ char_u *path,
+ int flags) // EW_* flags
+{
+ return unix_expandpath(gap, path, 0, flags, FALSE);
+}
#endif
/*
diff --git a/src/os_unix.c b/src/os_unix.c
index dc518fc..ac78733 100644
--- a/src/os_unix.c
+++ b/src/os_unix.c
@@ -6797,21 +6797,6 @@
}
/*
- * Expand a path into all matching files and/or directories. Handles "*",
- * "?", "[a-z]", "**", etc.
- * "path" has backslashes before chars that are not to be expanded.
- * Returns the number of matches found.
- */
- int
-mch_expandpath(
- garray_T *gap,
- char_u *path,
- int flags) // EW_* flags
-{
- return unix_expandpath(gap, path, 0, flags, FALSE);
-}
-
-/*
* mch_expand_wildcards() - this code does wild-card pattern matching using
* the shell
*
diff --git a/src/proto/filepath.pro b/src/proto/filepath.pro
index 46f51cb..1665089 100644
--- a/src/proto/filepath.pro
+++ b/src/proto/filepath.pro
@@ -56,6 +56,7 @@
int expand_wildcards(int num_pat, char_u **pat, int *num_files, char_u ***files, int flags);
int match_suffix(char_u *fname);
int unix_expandpath(garray_T *gap, char_u *path, int wildoff, int flags, int didstar);
+int mch_expandpath(garray_T *gap, char_u *path, int flags);
int gen_expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file, int flags);
void addfile(garray_T *gap, char_u *f, int flags);
void FreeWild(int count, char_u **files);
diff --git a/src/version.c b/src/version.c
index 47ff826..4feba92 100644
--- a/src/version.c
+++ b/src/version.c
@@ -705,6 +705,8 @@
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 923,
+/**/
922,
/**/
921,