aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt3
-rw-r--r--config/config.h.in2
-rw-r--r--src/nvim/buffer.c2
-rw-r--r--src/nvim/charset.c5
-rw-r--r--src/nvim/eval.c2
-rw-r--r--src/nvim/ex_cmds.c2
-rw-r--r--src/nvim/ex_docmd.c4
-rw-r--r--src/nvim/main.c4
-rw-r--r--src/nvim/os/fs.c33
-rw-r--r--src/nvim/os/fs_defs.h5
-rw-r--r--src/nvim/os_unix.c93
-rw-r--r--src/nvim/os_unix_defs.h19
-rw-r--r--src/nvim/path.c221
-rw-r--r--src/nvim/tag.c2
-rw-r--r--test/unit/path_spec.lua24
15 files changed, 234 insertions, 187 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c56e883f24..a026af7a1a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -27,6 +27,9 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
if(CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_INCLUDE_SYSTEM_FLAG_CXX "-isystem ")
endif()
+
+ # Enable fixing case-insensitive filenames for Mac.
+ set(USE_FNAME_CASE TRUE)
endif()
# Set available build types for CMake GUIs.
diff --git a/config/config.h.in b/config/config.h.in
index 8f3d154553..382b5c653d 100644
--- a/config/config.h.in
+++ b/config/config.h.in
@@ -15,7 +15,6 @@
#cmakedefine HAVE__NSGETENVIRON
#cmakedefine HAVE_CRT_EXTERNS_H
-#cmakedefine HAVE_DIRENT_H
#cmakedefine HAVE_FCNTL_H
#cmakedefine HAVE_FD_CLOEXEC
#cmakedefine HAVE_FSEEKO
@@ -60,6 +59,7 @@
#define SIGRETURN return
#define TIME_WITH_SYS_TIME 1
#cmakedefine UNIX
+#cmakedefine USE_FNAME_CASE
#define USEMAN_S 1
#define FEAT_BROWSE
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index dd20d61f75..11cb7bdeac 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -2253,7 +2253,7 @@ setfname (
}
sfname = vim_strsave(sfname);
#ifdef USE_FNAME_CASE
- fname_case(sfname, 0); /* set correct case for short file name */
+ path_fix_case(sfname); /* set correct case for short file name */
#endif
free(buf->b_ffname);
free(buf->b_sfname);
diff --git a/src/nvim/charset.c b/src/nvim/charset.c
index 5e11e202a3..4633bacb78 100644
--- a/src/nvim/charset.c
+++ b/src/nvim/charset.c
@@ -24,6 +24,7 @@
#include "nvim/move.h"
#include "nvim/os_unix.h"
#include "nvim/strings.h"
+#include "nvim/path.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -871,7 +872,7 @@ int vim_isfilec(int c)
/// return TRUE if 'c' is a valid file-name character or a wildcard character
/// Assume characters above 0x100 are valid (multi-byte).
-/// Explicitly interpret ']' as a wildcard character as mch_has_wildcard("]")
+/// Explicitly interpret ']' as a wildcard character as path_has_wildcard("]")
/// returns false.
///
/// @param c
@@ -882,7 +883,7 @@ int vim_isfilec_or_wc(int c)
char_u buf[2];
buf[0] = (char_u)c;
buf[1] = NUL;
- return vim_isfilec(c) || c == ']' || mch_has_wildcard(buf);
+ return vim_isfilec(c) || c == ']' || path_has_wildcard(buf);
}
/// return TRUE if 'c' is a printable character
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index deeca10310..9d8421ef04 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -9895,9 +9895,7 @@ static void f_has(typval_T *argvars, typval_T *rettv)
#if defined(WIN64) || defined(_WIN64)
"win64",
#endif
-#ifndef CASE_INSENSITIVE_FILENAME
"fname_case",
-#endif
#ifdef HAVE_ACL
"acl",
#endif
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 81c7ecab5f..e687eab3c4 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -2563,7 +2563,7 @@ do_ecmd (
sfname = ffname;
#ifdef USE_FNAME_CASE
if (sfname != NULL)
- fname_case(sfname, 0); /* set correct case for sfname */
+ path_fix_case(sfname); // set correct case for sfname
#endif
if ((flags & ECMD_ADDBUF) && (ffname == NULL || *ffname == NUL))
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 776ed844e9..db4f3a54f1 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -3432,7 +3432,7 @@ int expand_filename(exarg_T *eap, char_u **cmdlinep, char_u **errormsgp)
* the file name contains a wildcard it should not cause expanding.
* (it will be expanded anyway if there is a wildcard before replacing).
*/
- has_wildcards = mch_has_wildcard(p);
+ has_wildcards = path_has_wildcard(p);
while (*p != NUL) {
/* Skip over `=expr`, wildcards in it are not expanded. */
if (p[0] == '`' && p[1] == '=') {
@@ -3543,7 +3543,7 @@ int expand_filename(exarg_T *eap, char_u **cmdlinep, char_u **errormsgp)
|| vim_strchr(eap->arg, '~') != NULL) {
expand_env_esc(eap->arg, NameBuff, MAXPATHL,
TRUE, TRUE, NULL);
- has_wildcards = mch_has_wildcard(NameBuff);
+ has_wildcards = path_has_wildcard(NameBuff);
p = NameBuff;
} else
p = NULL;
diff --git a/src/nvim/main.c b/src/nvim/main.c
index 55935564b8..a03fd2e8a9 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -1290,8 +1290,8 @@ scripterror:
}
#ifdef USE_FNAME_CASE
- /* Make the case of the file name match the actual file. */
- fname_case(p, 0);
+ // Make the case of the file name match the actual file.
+ path_fix_case(p);
#endif
alist_add(&global_alist, p,
diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c
index 6200685076..d583323b1f 100644
--- a/src/nvim/os/fs.c
+++ b/src/nvim/os/fs.c
@@ -348,6 +348,39 @@ int os_rmdir(const char *path)
return result;
}
+/// Opens a directory.
+/// @param[out] dir The Directory object.
+/// @param path Path to the directory.
+/// @returns true if dir contains one or more items, false if not or an error
+/// occurred.
+bool os_scandir(Directory *dir, const char *path)
+ FUNC_ATTR_NONNULL_ALL
+{
+ int r = uv_fs_scandir(uv_default_loop(), &dir->request, path, 0, NULL);
+ if (r <= 0) {
+ os_closedir(dir);
+ }
+ return r > 0;
+}
+
+/// Increments the directory pointer.
+/// @param dir The Directory object.
+/// @returns a pointer to the next path in `dir` or `NULL`.
+const char *os_scandir_next(Directory *dir)
+ FUNC_ATTR_NONNULL_ALL
+{
+ int err = uv_fs_scandir_next(&dir->request, &dir->ent);
+ return err != UV_EOF ? dir->ent.name : NULL;
+}
+
+/// Frees memory associated with `os_scandir()`.
+/// @param dir The directory.
+void os_closedir(Directory *dir)
+ FUNC_ATTR_NONNULL_ALL
+{
+ uv_fs_req_cleanup(&dir->request);
+}
+
/// Remove a file.
///
/// @return `0` for success, non-zero for failure.
diff --git a/src/nvim/os/fs_defs.h b/src/nvim/os/fs_defs.h
index e4d79aab66..ddd382a3cb 100644
--- a/src/nvim/os/fs_defs.h
+++ b/src/nvim/os/fs_defs.h
@@ -16,4 +16,9 @@ typedef struct {
#define FILE_ID_EMPTY (FileID) {.inode = 0, .device_id = 0}
+typedef struct {
+ uv_fs_t request; ///< @private The request to uv for the directory.
+ uv_dirent_t ent; ///< @private The entry information.
+} Directory;
+
#endif // NVIM_OS_FS_DEFS_H
diff --git a/src/nvim/os_unix.c b/src/nvim/os_unix.c
index 8e4569033a..70cafc62ca 100644
--- a/src/nvim/os_unix.c
+++ b/src/nvim/os_unix.c
@@ -69,62 +69,6 @@ static int selinux_enabled = -1;
# include "os_unix.c.generated.h"
#endif
-#if defined(USE_FNAME_CASE)
-/*
- * Set the case of the file name, if it already exists. This will cause the
- * file name to remain exactly the same.
- * Only required for file systems where case is ignored and preserved.
- */
-void fname_case(
-char_u *name,
-int len /* buffer size, only used when name gets longer */
-)
-{
- char_u *slash, *tail;
- DIR *dirp;
- struct dirent *dp;
-
- FileInfo file_info;
- if (os_fileinfo_link((char *)name, &file_info)) {
- /* Open the directory where the file is located. */
- slash = vim_strrchr(name, '/');
- if (slash == NULL) {
- dirp = opendir(".");
- tail = name;
- } else {
- *slash = NUL;
- dirp = opendir((char *)name);
- *slash = '/';
- tail = slash + 1;
- }
-
- if (dirp != NULL) {
- while ((dp = readdir(dirp)) != NULL) {
- /* Only accept names that differ in case and are the same byte
- * length. TODO: accept different length name. */
- if (STRICMP(tail, dp->d_name) == 0
- && STRLEN(tail) == STRLEN(dp->d_name)) {
- char_u newname[MAXPATHL + 1];
-
- /* Verify the inode is equal. */
- STRLCPY(newname, name, MAXPATHL + 1);
- STRLCPY(newname + (tail - name), dp->d_name,
- MAXPATHL - (tail - name) + 1);
- FileInfo file_info_new;
- if (os_fileinfo_link((char *)newname, &file_info_new)
- && os_fileinfo_id_equal(&file_info, &file_info_new)) {
- STRCPY(tail, dp->d_name);
- break;
- }
- }
- }
-
- closedir(dirp);
- }
- }
-}
-#endif
-
#if defined(HAVE_ACL)
# ifdef HAVE_SYS_ACL_H
# include <sys/acl.h>
@@ -719,47 +663,12 @@ static void save_patterns(int num_pat, char_u **pat, int *num_file,
*num_file = num_pat;
}
-/*
- * Return TRUE if the string "p" contains a wildcard that mch_expandpath() can
- * expand.
- */
-int mch_has_exp_wildcard(char_u *p)
-{
- for (; *p; mb_ptr_adv(p)) {
- if (*p == '\\' && p[1] != NUL)
- ++p;
- else if (vim_strchr((char_u *)
- "*?[{'"
- , *p) != NULL)
- return TRUE;
- }
- return FALSE;
-}
-
-/*
- * Return TRUE if the string "p" contains a wildcard.
- * Don't recognize '~' at the end as a wildcard.
- */
-int mch_has_wildcard(char_u *p)
-{
- for (; *p; mb_ptr_adv(p)) {
- if (*p == '\\' && p[1] != NUL)
- ++p;
- else if (vim_strchr((char_u *)
- "*?[{`'$"
- , *p) != NULL
- || (*p == '~' && p[1] != NUL))
- return TRUE;
- }
- return FALSE;
-}
-
static int have_wildcard(int num, char_u **file)
{
int i;
for (i = 0; i < num; i++)
- if (mch_has_wildcard(file[i]))
+ if (path_has_wildcard(file[i]))
return 1;
return 0;
}
diff --git a/src/nvim/os_unix_defs.h b/src/nvim/os_unix_defs.h
index 4ffd23aa25..24b069b090 100644
--- a/src/nvim/os_unix_defs.h
+++ b/src/nvim/os_unix_defs.h
@@ -44,25 +44,6 @@
# define SIGDUMMYARG
#endif
-#ifdef HAVE_DIRENT_H
-# include <dirent.h>
-# ifndef NAMLEN
-# define NAMLEN(dirent) strlen((dirent)->d_name)
-# endif
-#else
-# define dirent direct
-# define NAMLEN(dirent) (dirent)->d_namlen
-# if HAVE_SYS_NDIR_H
-# include <sys/ndir.h>
-# endif
-# if HAVE_SYS_DIR_H
-# include <sys/dir.h>
-# endif
-# if HAVE_NDIR_H
-# include <ndir.h>
-# endif
-#endif
-
#if !defined(HAVE_SYS_TIME_H) || defined(TIME_WITH_SYS_TIME)
# include <time.h> /* on some systems time.h should not be
included together with sys/time.h */
diff --git a/src/nvim/path.c b/src/nvim/path.c
index 346be9108d..e83b4e44ed 100644
--- a/src/nvim/path.c
+++ b/src/nvim/path.c
@@ -400,6 +400,32 @@ char_u *save_absolute_path(const char_u *name)
return vim_strsave((char_u *) name);
}
+/// Checks if a path has a wildcard character including '~', unless at the end.
+/// @param p The path to expand.
+/// @returns Unix: True if it contains one of "?[{`'$".
+/// @returns Windows: True if it contains one of "*?$[".
+bool path_has_wildcard(const char_u *p)
+ FUNC_ATTR_NONNULL_ALL
+{
+ for (; *p; mb_ptr_adv(p)) {
+#if defined(UNIX)
+ if (p[0] == '\\' && p[1] != NUL) {
+ p++;
+ continue;
+ }
+
+ const char *wildcards = "*?[{`'$";
+#else
+ // Windows:
+ const char *wildcards = "?*$[`";
+#endif
+ if (vim_strchr((char_u *)wildcards, *p) != NULL
+ || (p[0] == '~' && p[1] != NUL)) {
+ return true;
+ }
+ }
+ return false;
+}
#if defined(UNIX)
/*
@@ -410,39 +436,67 @@ static int pstrcmp(const void *a, const void *b)
{
return pathcmp(*(char **)a, *(char **)b, -1);
}
+#endif
-/*
- * 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 */
-)
+/// Checks if a path has a character path_expand can expand.
+/// @param p The path to expand.
+/// @returns Unix: True if it contains one of *?[{.
+/// @returns Windows: True if it contains one of *?[.
+bool path_has_exp_wildcard(const char_u *p)
+ FUNC_ATTR_NONNULL_ALL
+{
+ for (; *p != NUL; mb_ptr_adv(p)) {
+#if defined(UNIX)
+ if (p[0] == '\\' && p[1] != NUL) {
+ p++;
+ continue;
+ }
+
+ const char *wildcards = "*?[{";
+#else
+ const char *wildcards = "*?["; // Windows.
+#endif
+ if (vim_strchr((char_u *) wildcards, *p) != NULL) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/// Recursively expands one path component into all matching files and/or
+/// directories. Handles "*", "?", "[a-z]", "**", etc.
+/// @remark "**" in `path` requests recursive expansion.
+///
+/// @param[out] gap The matches found.
+/// @param path The path to search.
+/// @param flags Flags for regexp expansion.
+/// - EW_ICASE: Ignore case.
+/// - EW_NOERROR: Silence error messeges.
+/// - EW_NOTWILD: Add matches literally.
+/// @returns the number of matches found.
+static size_t path_expand(garray_T *gap, const char_u *path, int flags)
+ FUNC_ATTR_NONNULL_ALL
+{
+ return do_path_expand(gap, path, 0, flags, false);
+}
+
+/// Implementation of path_expand().
+///
+/// Chars before `path + wildoff` do not get expanded.
+static size_t do_path_expand(garray_T *gap, const char_u *path,
+ size_t wildoff, int flags, bool didstar)
+ FUNC_ATTR_NONNULL_ALL
{
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) {
os_breakcheck();
@@ -461,7 +515,7 @@ unix_expandpath (
p = buf;
s = buf;
e = NULL;
- path_end = path;
+ const char_u *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. */
@@ -510,11 +564,14 @@ unix_expandpath (
return 0;
}
- /* compile the regexp into a program */
- if (flags & EW_ICASE)
- regmatch.rm_ic = TRUE; /* 'wildignorecase' set */
- else
- regmatch.rm_ic = p_fic; /* ignore case when 'fileignorecase' is set */
+ // compile the regexp into a program
+ regmatch_T regmatch;
+#if defined(UNIX)
+ // Ignore case if given 'wildignorecase', else respect 'fileignorecase'.
+ regmatch.rm_ic = (flags & EW_ICASE) || p_fic;
+#else
+ regmatch.rm_ic = true; // Always ignore case on Windows.
+#endif
if (flags & (EW_NOERROR | EW_NOTWILD))
++emsg_silent;
regmatch.regprog = vim_regcomp(pat, RE_MAGIC);
@@ -533,26 +590,22 @@ unix_expandpath (
&& *path_end == '/') {
STRCPY(s, path_end + 1);
++stardepth;
- (void)unix_expandpath(gap, buf, (int)(s - buf), flags, TRUE);
+ (void)do_path_expand(gap, buf, (int)(s - buf), flags, TRUE);
--stardepth;
}
-
- /* open the directory for scanning */
*s = NUL;
- dirp = opendir(*buf == NUL ? "." : (char *)buf);
- /* Find all matching entries */
- if (dirp != NULL) {
- for (;; ) {
- dp = readdir(dirp);
- if (dp == NULL)
- break;
- if ((dp->d_name[0] != '.' || starts_with_dot)
- && ((regmatch.regprog != NULL && vim_regexec(&regmatch,
- (char_u *)dp->d_name, (colnr_T)0))
+ Directory dir;
+ // open the directory for scanning
+ if (os_scandir(&dir, *buf == NUL ? "." : (char *)buf)) {
+ // Find all matching entries.
+ char_u *name;
+ while((name = (char_u *) os_scandir_next(&dir))) {
+ if ((name[0] != '.' || starts_with_dot)
+ && ((regmatch.regprog != NULL && vim_regexec(&regmatch, name, 0))
|| ((flags & EW_NOTWILD)
- && fnamencmp(path + (s - buf), dp->d_name, e - s) == 0))) {
- STRCPY(s, dp->d_name);
+ && fnamencmp(path + (s - buf), name, e - s) == 0))) {
+ STRCPY(s, name);
len = STRLEN(buf);
if (starstar && stardepth < 100) {
@@ -561,15 +614,15 @@ unix_expandpath (
STRCPY(buf + len, "/**");
STRCPY(buf + len + 3, path_end);
++stardepth;
- (void)unix_expandpath(gap, buf, len + 1, flags, TRUE);
+ (void)do_path_expand(gap, buf, len + 1, flags, true);
--stardepth;
}
STRCPY(buf + len, path_end);
- if (mch_has_exp_wildcard(path_end)) { /* handle more wildcards */
+ if (path_has_exp_wildcard(path_end)) { /* handle more wildcards */
/* need to expand another component of the path */
/* remove backslashes for the remaining components only */
- (void)unix_expandpath(gap, buf, len + 1, flags, FALSE);
+ (void)do_path_expand(gap, buf, len + 1, flags, false);
} else {
/* no more wildcards, check if there is a match */
/* remove backslashes for the remaining components only */
@@ -581,8 +634,7 @@ unix_expandpath (
}
}
}
-
- closedir(dirp);
+ os_closedir(&dir);
}
free(buf);
@@ -594,7 +646,6 @@ unix_expandpath (
sizeof(char_u *), pstrcmp);
return matches;
}
-#endif
/*
* Moves "*psep" back to the previous path separator in "path".
@@ -1078,7 +1129,7 @@ gen_expand_wildcards (
* If there are no wildcards: Add the file name if it exists or
* when EW_NOTFOUND is given.
*/
- if (mch_has_exp_wildcard(p)) {
+ if (path_has_exp_wildcard(p)) {
if ((flags & EW_PATH)
&& !path_is_absolute_path(p)
&& !(p[0] == '.'
@@ -1092,7 +1143,7 @@ gen_expand_wildcards (
recursive = TRUE;
did_expand_in_path = TRUE;
} else
- add_pat = mch_expandpath(&ga, p, flags);
+ add_pat = path_expand(&ga, p, flags);
}
}
@@ -1566,13 +1617,68 @@ char_u *fix_fname(char_u *fname)
fname = vim_strsave(fname);
# ifdef USE_FNAME_CASE
- fname_case(fname, 0); // set correct case for file name
+ path_fix_case(fname); // set correct case for file name
# endif
return fname;
#endif
}
+/// Set the case of the file name, if it already exists. This will cause the
+/// file name to remain exactly the same.
+/// Only required for file systems where case is ignored and preserved.
+// TODO(SplinterOfChaos): Could also be used when mounting case-insensitive
+// file systems.
+void path_fix_case(char_u *name)
+ FUNC_ATTR_NONNULL_ALL
+{
+ FileInfo file_info;
+ if (!os_fileinfo_link((char *)name, &file_info)) {
+ return;
+ }
+
+ // Open the directory where the file is located.
+ char_u *slash = vim_strrchr(name, '/');
+ char_u *tail;
+ Directory dir;
+ bool ok;
+ if (slash == NULL) {
+ ok = os_scandir(&dir, ".");
+ tail = name;
+ } else {
+ *slash = NUL;
+ ok = os_scandir(&dir, (char *) name);
+ *slash = '/';
+ tail = slash + 1;
+ }
+
+ if (!ok) {
+ return;
+ }
+
+ char_u *entry;
+ while ((entry = (char_u *) os_scandir_next(&dir))) {
+ // Only accept names that differ in case and are the same byte
+ // length. TODO: accept different length name.
+ if (STRICMP(tail, entry) == 0 && STRLEN(tail) == STRLEN(entry)) {
+ char_u newname[MAXPATHL + 1];
+
+ // Verify the inode is equal.
+ STRLCPY(newname, name, MAXPATHL + 1);
+ STRLCPY(newname + (tail - name), entry,
+ MAXPATHL - (tail - name) + 1);
+ FileInfo file_info_new;
+ if (os_fileinfo_link((char *)newname, &file_info_new)
+ && os_fileinfo_id_equal(&file_info, &file_info_new)) {
+ STRCPY(tail, entry);
+ break;
+ }
+ }
+ }
+
+ os_closedir(&dir);
+}
+
/*
* Return TRUE if "p" points to just after a path separator.
* Takes care of multi-byte characters.
@@ -1670,19 +1776,6 @@ int pathcmp(const char *p, const char *q, int maxlen)
return 1;
}
-/*
- * 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.
- *
- * Uses EW_* flags
- */
-int mch_expandpath(garray_T *gap, char_u *path, int flags)
-{
- return unix_expandpath(gap, path, 0, flags, FALSE);
-}
-
/// Try to find a shortname by comparing the fullname with the current
/// directory.
///
diff --git a/src/nvim/tag.c b/src/nvim/tag.c
index 361afb7e07..1f6b42d9cf 100644
--- a/src/nvim/tag.c
+++ b/src/nvim/tag.c
@@ -2587,7 +2587,7 @@ static char_u *expand_tag_fname(char_u *fname, char_u *tag_fname, int expand)
/*
* Expand file name (for environment variables) when needed.
*/
- if (expand && mch_has_wildcard(fname)) {
+ if (expand && path_has_wildcard(fname)) {
ExpandInit(&xpc);
xpc.xp_context = EXPAND_FILES;
expanded_fname = ExpandOne(&xpc, fname, NULL,
diff --git a/test/unit/path_spec.lua b/test/unit/path_spec.lua
index 1824eaeccc..13546a6183 100644
--- a/test/unit/path_spec.lua
+++ b/test/unit/path_spec.lua
@@ -418,6 +418,30 @@ describe('more path function', function()
end)
end)
+ describe('path_fix_case', function()
+ function fix_case(file)
+ c_file = to_cstr(file)
+ path.path_fix_case(c_file)
+ return ffi.string(c_file)
+ end
+
+ if ffi.os == 'Windows' or ffi.os == 'OSX' then
+ it('Corrects the case of file names in Mac and Windows', function()
+ lfs.mkdir('CamelCase')
+ eq('CamelCase', fix_case('camelcase'))
+ eq('CamelCase', fix_case('cAMELcASE'))
+ lfs.rmdir('CamelCase')
+ end)
+ else
+ it('does nothing on Linux', function()
+ lfs.mkdir('CamelCase')
+ eq('camelcase', fix_case('camelcase'))
+ eq('cAMELcASE', fix_case('cAMELcASE'))
+ lfs.mkdir('CamelCase')
+ end)
+ end
+ end)
+
describe('append_path', function()
it('joins given paths with a slash', function()
local path1 = cstr(100, 'path1')