]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.1.0923: too many strlen() calls in filepath.c v9.1.0923
authorJohn Marriott <basilisk@internode.on.net>
Fri, 13 Dec 2024 12:58:53 +0000 (13:58 +0100)
committerChristian Brabandt <cb@256bit.org>
Fri, 13 Dec 2024 12:58:53 +0000 (13:58 +0100)
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>
src/filepath.c
src/os_unix.c
src/proto/filepath.pro
src/version.c

index 3dd71bc407e92b253b9146286e71ad45156496af..729884252716a8d66c2b09e554cd3f736a3c1be5 100644 (file)
     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 @@ get_short_pathname(char_u **fnamep, char_u **bufp, int *fnamelen)
        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 @@ shortpath_for_invalid_fname(
     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 @@ shortpath_for_invalid_fname(
     // 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 @@ shortpath_for_invalid_fname(
         * 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 @@ shortpath_for_partial(
        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 @@ shortpath_for_partial(
 
     // 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 @@ repeat:
            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 @@ repeat:
        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 @@ f_mkdir(typval_T *argvars, typval_T *rettv)
        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 @@ f_resolve(typval_T *argvars, typval_T *rettv)
     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 @@ f_resolve(typval_T *argvars, typval_T *rettv)
 #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 @@ f_resolve(typval_T *argvars, typval_T *rettv)
                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 @@ f_resolve(typval_T *argvars, typval_T *rettv)
                                    || 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 @@ f_resolve(typval_T *argvars, typval_T *rettv)
                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 @@ f_resolve(typval_T *argvars, typval_T *rettv)
 
 #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 @@ getnextcomp(char_u *fname)
 {
     while (*fname && !vim_ispathsep(*fname))
        MB_PTR_ADV(fname);
+
     if (*fname)
        ++fname;
     return fname;
@@ -3116,16 +3194,18 @@ vim_fnamencmp(char_u *x, char_u *y, size_t len)
     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 @@ concat_fnames(char_u *fname1, char_u *fname2, int sep)
     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 @@ expand_backtick(
 }
 #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 @@ pstrcmp(const void *a, const void *b)
  * "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,15 +3581,16 @@ dos_expandpath(
            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.
+     * 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;
@@ -3513,18 +3602,25 @@ dos_expandpath(
        // 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 == '/')
+       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)
+#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;
@@ -3535,270 +3631,38 @@ dos_expandpath(
     e = p;
     *e = NUL;
 
-    // now we have one wildcard component between s and e
+    // 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)
+    p = buf + wildoff;
+    if (p < s)
     {
-       vim_free(buf);
-       return 0;
-    }
+       size_t  psize = STRLEN(p) + 1;
 
-    // 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(&regmatch, p, (colnr_T)0)
-                        || (p_alt != NULL
-                               && vim_regexec(&regmatch, p_alt, (colnr_T)0))))
-                 || ((flags & EW_NOTWILD)
-                    && fnamencmp(path + (s - buf), p, e - s) == 0)))
+       do
        {
-           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);
-           }
+           if (!rem_backslash(p))
+               ++p;
            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);
+               mch_memmove(p, p + 1, psize);
+               --e;
+               --s;
            }
-       }
-
-       vim_free(p_alt);
-       vim_free(p);
-       ok = FindNextFileW(hFind, &wfb);
+           --psize;
+       } while (p < s);
     }
 
-    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.
-     */
-    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 == '/')
-       {
-           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)))))
-           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;
-       }
+    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 @@ unix_expandpath(
     }
 
     // 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 @@ unix_expandpath(
 
     // 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(&regmatch,
-                                            (char_u *)dp->d_name, (colnr_T)0))
+           d_name = (char_u *)dp->d_name;
+#endif
+
+           // 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(&regmatch, (char_u *)d_name, (colnr_T)0))
+#ifdef MSWIN
+                || (d_name_alt != NULL
+                   && vim_regexec(&regmatch, d_name_alt, (colnr_T)0))
+#endif
                   || ((flags & EW_NOTWILD)
-                    && fnamencmp(path + (s - buf), dp->d_name, e - s) == 0)))
+                    && fnamencmp(path + basepathlen, d_name, wildcardlen) == 0))
+           )
            {
-               vim_strncpy(s, (char_u *)dp->d_name, buflen - (s - buf) - 1);
-               len = STRLEN(buf);
+               int len = (int)basepathlen + vim_snprintf((char *)s, (size_t)(MAXPATHL - (basepathlen + 1)), "%s", d_name);
 
-               if (starstar && stardepth < 100)
+               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 @@ unix_expandpath(
                        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 @@ unix_expandpath(
                    }
                }
            }
+
+#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 @@ unix_expandpath(
     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
 
 /*
index dc518fc6764d6345ec4109e40019c3074483ace0..ac78733d34558b5a46ed4f380a8f50379e5ea5a9 100644 (file)
@@ -6796,21 +6796,6 @@ select_eintr:
     return result;
 }
 
-/*
- * 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
index 46f51cb36fd7a41a5d88215e62975350b4758acd..1665089e6c5b5057e21e63b6ded7b12ee4676a6d 100644 (file)
@@ -56,6 +56,7 @@ int expand_wildcards_eval(char_u **pat, int *num_file, char_u ***file, int flags
 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);
index 47ff826398a34a853246942714dbff163197d89f..4feba92608bd4c6b8b325edec7dbc898c44847c1 100644 (file)
@@ -704,6 +704,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    923,
 /**/
     922,
 /**/