mirror of
https://github.com/neovim/neovim.git
synced 2025-01-01 17:23:36 -07:00
Merge PR #1212 'os_scandir/scandir_next/closedir()'
This commit is contained in:
commit
ad7a317c42
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 */
|
||||
|
221
src/nvim/path.c
221
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(®match,
|
||||
(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(®match, 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.
|
||||
///
|
||||
|
@ -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,
|
||||
|
@ -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')
|
||||
|
Loading…
Reference in New Issue
Block a user