summaryrefslogtreecommitdiffstatshomepage
path: root/src
diff options
context:
space:
mode:
authortao <2471314@gmail.com>2026-04-25 01:20:25 +0800
committerGitHub <noreply@github.com>2026-04-24 13:20:25 -0400
commitf130922744657b731c24a842a85fd8a9f8058073 (patch)
tree9540b465b8a5d937638e2f9b8a7a0d2e920f593b /src
parent27191e0f4f4f9086180a8fbe3e52c1c280a70b09 (diff)
fix(path): normalize path slashes on Windows #37729
Problem: On Windows, path separators may become inconsistent for various reasons, which makes normalization quite painful. Solution: Normalize paths to `/` at the entry boundaries and always use it internally, converting back only in rare cases where `\` is really needed (e.g. cmd.exe/bat scripts?). This is the first commit in a series of incremental steps. Note: * some funcs won't respect shellslash. e.g. `expand/fnamemodify` * some funcs still respect shellslash, but will be updated in a follow PR. e.g. `ex_pwd/f_chdir/f_getcwd` * uv's built-in funcs always return `\`. e.g. `uv.cwd/uv.exepath` Co-authored-by: Justin M. Keyes <justinkz@gmail.com>
Diffstat (limited to 'src')
-rw-r--r--src/nvim/arglist.c3
-rw-r--r--src/nvim/ascii_defs.h9
-rw-r--r--src/nvim/cmdexpand.c10
-rw-r--r--src/nvim/cmdexpand.h2
-rw-r--r--src/nvim/eval/fs.c8
-rw-r--r--src/nvim/event/libuv_proc.c3
-rw-r--r--src/nvim/ex_docmd.c6
-rw-r--r--src/nvim/file_search.c8
-rw-r--r--src/nvim/insexpand.c35
-rw-r--r--src/nvim/main.c3
-rw-r--r--src/nvim/memline.c2
-rw-r--r--src/nvim/option.c34
-rw-r--r--src/nvim/os/env.c17
-rw-r--r--src/nvim/os/fs.c8
-rw-r--r--src/nvim/os/pty_proc_win.c4
-rw-r--r--src/nvim/os/stdpaths.c36
-rw-r--r--src/nvim/path.c43
-rw-r--r--src/nvim/path.h10
-rw-r--r--src/nvim/quickfix.c7
-rw-r--r--src/nvim/runtime.c5
-rw-r--r--src/nvim/shada.c2
-rw-r--r--src/nvim/syntax.c2
-rw-r--r--src/nvim/tag.c16
23 files changed, 145 insertions, 128 deletions
diff --git a/src/nvim/arglist.c b/src/nvim/arglist.c
index abe81202da..bf9883ce24 100644
--- a/src/nvim/arglist.c
+++ b/src/nvim/arglist.c
@@ -216,9 +216,6 @@ void alist_add(alist_T *al, char *fname, int set_fnum)
arglist_locked = true;
wp->w_locked++;
-#ifdef BACKSLASH_IN_FILENAME
- slash_adjust(fname);
-#endif
AARGLIST(al)[al->al_ga.ga_len].ae_fname = fname;
if (set_fnum > 0) {
AARGLIST(al)[al->al_ga.ga_len].ae_fnum =
diff --git a/src/nvim/ascii_defs.h b/src/nvim/ascii_defs.h
index 7bd0b21889..fb8546833f 100644
--- a/src/nvim/ascii_defs.h
+++ b/src/nvim/ascii_defs.h
@@ -75,13 +75,8 @@
#define Ctrl__ 31
// Character that separates dir names in a path.
-#ifdef BACKSLASH_IN_FILENAME
-# define PATHSEP psepc
-# define PATHSEPSTR pseps
-#else
-# define PATHSEP '/'
-# define PATHSEPSTR "/"
-#endif
+#define PATHSEP '/'
+#define PATHSEPSTR "/"
/// Checks if `c` is a space or tab character.
///
diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c
index cfcc9c033e..9c30f516cf 100644
--- a/src/nvim/cmdexpand.c
+++ b/src/nvim/cmdexpand.c
@@ -316,6 +316,7 @@ int nextwild(expand_T *xp, int type, int options, bool escape)
}
// Translate string into pattern and expand it.
const int use_options = (options
+ | WILD_USE_COMPLETESLASH
| WILD_HOME_REPLACE
| WILD_ADD_SLASH
| WILD_SILENT
@@ -2725,16 +2726,13 @@ static int expand_files_and_dirs(expand_T *xp, char *pat, char ***matches, int *
xfree(pat);
}
#ifdef BACKSLASH_IN_FILENAME
- if (p_csl[0] != NUL && (options & WILD_IGNORE_COMPLETESLASH) == 0) {
+ if ((options & WILD_USE_COMPLETESLASH) && ((p_csl[0] == NUL && !p_ssl) || p_csl[0] == 'b')) {
for (int j = 0; j < *numMatches; j++) {
char *ptr = (*matches)[j];
- while (*ptr != NUL) {
- if (p_csl[0] == 's' && *ptr == '\\') {
- *ptr = '/';
- } else if (p_csl[0] == 'b' && *ptr == '/') {
+ for (; *ptr; ptr++) {
+ if (*ptr == '/') {
*ptr = '\\';
}
- ptr += utfc_ptr2len(ptr);
}
}
}
diff --git a/src/nvim/cmdexpand.h b/src/nvim/cmdexpand.h
index 51b90b311e..198b178f71 100644
--- a/src/nvim/cmdexpand.h
+++ b/src/nvim/cmdexpand.h
@@ -36,7 +36,7 @@ enum {
WILD_ESCAPE = 0x80,
WILD_ICASE = 0x100,
WILD_ALLLINKS = 0x200,
- WILD_IGNORE_COMPLETESLASH = 0x400,
+ WILD_USE_COMPLETESLASH = 0x400,
WILD_NOERROR = 0x800, ///< sets EW_NOERROR
WILD_BUFLASTUSED = 0x1000,
BUF_DIFF_FILTER = 0x2000,
diff --git a/src/nvim/eval/fs.c b/src/nvim/eval/fs.c
index 7e4dc1801b..abec37c9c8 100644
--- a/src/nvim/eval/fs.c
+++ b/src/nvim/eval/fs.c
@@ -462,12 +462,6 @@ void f_exepath(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
os_can_exe(tv_get_string(&argvars[0]), &path, true);
-#ifdef BACKSLASH_IN_FILENAME
- if (path != NULL) {
- slash_adjust(path);
- }
-#endif
-
rettv->v_type = VAR_STRING;
rettv->vval.v_string = path;
}
@@ -878,7 +872,7 @@ void f_glob(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
/// "globpath()" function
void f_globpath(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- int flags = WILD_IGNORE_COMPLETESLASH; // Flags for globpath.
+ int flags = 0; // Flags for globpath.
bool error = false;
// Return a string, or a list if the optional third argument is non-zero.
diff --git a/src/nvim/event/libuv_proc.c b/src/nvim/event/libuv_proc.c
index 01076670c3..c12e7b0509 100644
--- a/src/nvim/event/libuv_proc.c
+++ b/src/nvim/event/libuv_proc.c
@@ -11,6 +11,7 @@
#include "nvim/log.h"
#include "nvim/os/os.h"
#include "nvim/os/os_defs.h"
+#include "nvim/path.h"
#include "nvim/types_defs.h"
#include "nvim/ui_client.h"
@@ -29,6 +30,8 @@ int libuv_proc_spawn(LibuvProc *uvproc)
// expects a different syntax (must be prepared by the caller before now).
if (os_shell_is_cmdexe(proc->argv[0])) {
uvproc->uvopts.flags |= UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS;
+ // cmd.exe compatibility: backslashes required for path.
+ TO_BACKSLASH(proc->argv[0]);
}
if (proc->detach) {
uvproc->uvopts.flags |= UV_PROCESS_DETACHED;
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 3a761136c0..373e111eec 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -4145,6 +4145,8 @@ int expand_filename(exarg_T *eap, char **cmdlinep, const char **errormsgp)
}
repl_cmdline(eap, eap->arg, strlen(eap->arg), p, cmdlinep);
xfree(p);
+ } else {
+ TO_SLASH(eap->arg);
}
}
return OK;
@@ -5673,7 +5675,7 @@ int expand_findfunc(char *pat, char ***files, int *numMatches)
int idx = 0;
TV_LIST_ITER_CONST(l, li, {
if (TV_LIST_ITEM_TV(li)->v_type == VAR_STRING) {
- (*files)[idx] = xstrdup(TV_LIST_ITEM_TV(li)->vval.v_string);
+ (*files)[idx] = TO_SLASH_SAVE(TV_LIST_ITEM_TV(li)->vval.v_string);
idx++;
}
});
@@ -5704,7 +5706,7 @@ static char *findfunc_find_file(char *findarg, size_t findarg_len, int count)
} else {
listitem_T *li = tv_list_find(fname_list, count - 1);
if (li != NULL && TV_LIST_ITEM_TV(li)->v_type == VAR_STRING) {
- ret_fname = xstrdup(TV_LIST_ITEM_TV(li)->vval.v_string);
+ ret_fname = TO_SLASH_SAVE(TV_LIST_ITEM_TV(li)->vval.v_string);
}
}
}
diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c
index a043da3f17..66254a15f1 100644
--- a/src/nvim/file_search.c
+++ b/src/nvim/file_search.c
@@ -1593,6 +1593,7 @@ theend:
char *grab_file_name(int count, linenr_T *file_lnum)
{
int options = FNAME_MESS | FNAME_EXP | FNAME_REL | FNAME_UNESC;
+ char *fname;
if (VIsual_active) {
size_t len;
char *ptr;
@@ -1605,9 +1606,12 @@ char *grab_file_name(int count, linenr_T *file_lnum)
*file_lnum = getdigits_int32(&p, false, 0);
}
- return find_file_name_in_path(ptr, len, options, count, curbuf->b_ffname);
+ fname = find_file_name_in_path(ptr, len, options, count, curbuf->b_ffname);
+ } else {
+ fname = file_name_at_cursor(options | FNAME_HYP, count, file_lnum);
}
- return file_name_at_cursor(options | FNAME_HYP, count, file_lnum);
+ TO_SLASH(fname);
+ return fname;
}
/// Return the file name under or after the cursor.
diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c
index 97b84a0e8b..ce676a8639 100644
--- a/src/nvim/insexpand.c
+++ b/src/nvim/insexpand.c
@@ -4038,30 +4038,9 @@ static void get_next_filename_completion(void)
int max_score = 0;
Direction dir = compl_direction;
-#ifdef BACKSLASH_IN_FILENAME
- char pathsep = (curbuf->b_p_csl[0] == 's')
- ? '/' : (curbuf->b_p_csl[0] == 'b') ? '\\' : PATHSEP;
-#else
- char pathsep = PATHSEP;
-#endif
-
if (in_fuzzy_collect) {
-#ifdef BACKSLASH_IN_FILENAME
- if (curbuf->b_p_csl[0] == 's') {
- for (size_t i = 0; i < leader_len; i++) {
- if (leader[i] == '\\') {
- leader[i] = '/';
- }
- }
- } else if (curbuf->b_p_csl[0] == 'b') {
- for (size_t i = 0; i < leader_len; i++) {
- if (leader[i] == '/') {
- leader[i] = '\\';
- }
- }
- }
-#endif
- char *last_sep = strrchr(leader, pathsep);
+ TO_SLASH(leader);
+ char *last_sep = strrchr(leader, PATHSEP);
if (last_sep == NULL) {
// No path separator or separator is the last character,
// fuzzy match the whole leader
@@ -4093,16 +4072,12 @@ static void get_next_filename_completion(void)
// May change home directory back to "~".
tilde_replace(compl_pattern.data, num_matches, matches);
#ifdef BACKSLASH_IN_FILENAME
- if (curbuf->b_p_csl[0] != NUL) {
+ if ((curbuf->b_p_csl[0] == NUL && !p_ssl) || curbuf->b_p_csl[0] == 'b') {
for (int i = 0; i < num_matches; i++) {
- char *ptr = matches[i];
- while (*ptr != NUL) {
- if (curbuf->b_p_csl[0] == 's' && *ptr == '\\') {
- *ptr = '/';
- } else if (curbuf->b_p_csl[0] == 'b' && *ptr == '/') {
+ for (char *ptr = matches[i]; *ptr; ptr++) {
+ if (*ptr == '/') {
*ptr = '\\';
}
- ptr += utfc_ptr2len(ptr);
}
}
}
diff --git a/src/nvim/main.c b/src/nvim/main.c
index 08e741b3a7..a1320648a4 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -256,6 +256,7 @@ int main(int argc, char **argv)
#endif
{
argv0 = argv[0];
+ TO_SLASH(argv0);
if (!appname_is_valid()) {
fprintf(stderr, "$NVIM_APPNAME must be a name or relative path.\n");
@@ -1461,6 +1462,7 @@ scripterror:
break;
case 'u': // "-u {vimrc}" vim inits file
parmp->use_vimrc = argv[0];
+ TO_SLASH(argv[0]);
break;
case 'U': // "-U {gvimrc}" gvim inits file
break;
@@ -1505,6 +1507,7 @@ scripterror:
xfree(p);
p = tilde_expanded;
}
+ TO_SLASH(p);
#endif
if (parmp->diff_mode && os_isdir(p) && GARGCOUNT > 0
diff --git a/src/nvim/memline.c b/src/nvim/memline.c
index b5ac0cdb28..334ab935d2 100644
--- a/src/nvim/memline.c
+++ b/src/nvim/memline.c
@@ -913,6 +913,7 @@ void ml_recover(bool checkext)
// If .swp file name given directly, use name from swapfile for buffer.
if (directly) {
+ TO_SLASH(b0p->b0_fname);
expand_env(b0p->b0_fname, NameBuff, MAXPATHL);
if (setfname(curbuf, NameBuff, NULL, true) == FAIL) {
goto theend;
@@ -3510,6 +3511,7 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
fd = os_open(fname, O_RDONLY, 0);
if (fd >= 0) {
if (read_eintr(fd, &b0, sizeof(b0)) == sizeof(b0)) {
+ TO_SLASH(b0.b0_fname);
proc_running = swapfile_proc_running(&b0, fname);
// If the swapfile has the same directory as the
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 4644fba49a..a392412d53 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -365,7 +365,7 @@ void set_init_1(bool clean_arg)
memmove(backupdir + 2, backupdir, backupdir_len + 1);
memmove(backupdir, ".,", 2);
set_string_default(kOptBackupdir, backupdir, true);
- set_string_default(kOptViewdir, stdpaths_user_state_subpath("view", 2, true),
+ set_string_default(kOptViewdir, stdpaths_user_state_subpath("view", 2, false),
true);
set_string_default(kOptDirectory, stdpaths_user_state_subpath("swap", 2, true),
true);
@@ -2650,6 +2650,8 @@ static const char *did_set_scrollbind(optset_T *args)
#ifdef BACKSLASH_IN_FILENAME
/// Process the updated 'shellslash' option value.
+/// TODO(ntdiary): Remove this once we're confident that the `shellslash`
+/// option is no longer needed.
static const char *did_set_shellslash(optset_T *args FUNC_ATTR_UNUSED)
{
if (p_ssl) {
@@ -2662,10 +2664,11 @@ static const char *did_set_shellslash(optset_T *args FUNC_ATTR_UNUSED)
pseps[0] = '\\';
}
+ // TODO(ntdiary): Remove these in the follow PR.
// need to adjust the file name arguments and buffer names.
- buflist_slash_adjust();
- alist_slash_adjust();
- scriptnames_slash_adjust();
+ // buflist_slash_adjust();
+ // alist_slash_adjust();
+ // scriptnames_slash_adjust();
return NULL;
}
#endif
@@ -3903,6 +3906,29 @@ static const char *set_option(const OptIndex opt_idx, OptVal value, int opt_flag
}
}
+#ifdef BACKSLASH_IN_FILENAME
+ // Ensure "/" slashes in various options.
+ uint32_t flags = options[opt_idx].flags;
+ if ((flags & kOptFlagExpand)
+ && opt_idx != kOptEqualprg
+ && opt_idx != kOptFormatprg
+ && opt_idx != kOptGrepprg
+ && opt_idx != kOptKeywordprg
+ && opt_idx != kOptMakeprg
+ && opt_idx != kOptShell) {
+ bool allow_comma = flags & kOptFlagComma;
+ bool allow_space = (opt_idx == kOptCdpath || opt_idx == kOptPath || opt_idx == kOptTags);
+ for (char *p = value.data.string.data; *p; p++) {
+ if (*p != '\\'
+ || (p[1] == ',' && allow_comma)
+ || (p[1] == ' ' && allow_space)) {
+ continue;
+ }
+ *p = '/';
+ }
+ }
+#endif
+
vimoption_T *opt = &options[opt_idx];
const bool scope_local = opt_flags & OPT_LOCAL;
const bool scope_global = opt_flags & OPT_GLOBAL;
diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c
index 329e6ebadb..8e21a8c7db 100644
--- a/src/nvim/os/env.c
+++ b/src/nvim/os/env.c
@@ -618,6 +618,7 @@ size_t expand_env_esc(const char *restrict srcp, char *restrict dst, int dstlen,
#endif
*var = NUL;
var = vim_getenv(dst);
+ TO_SLASH(var);
mustfree = true;
#ifdef UNIX
}
@@ -661,21 +662,6 @@ size_t expand_env_esc(const char *restrict srcp, char *restrict dst, int dstlen,
#endif // UNIX
}
-#ifdef BACKSLASH_IN_FILENAME
- // If 'shellslash' is set change backslashes to forward slashes.
- // Can't use slash_adjust(), p_ssl may be set temporarily.
- if (p_ssl && var != NULL && vim_strchr(var, '\\') != NULL) {
- char *p = xstrdup(var);
-
- if (mustfree) {
- xfree(var);
- }
- var = p;
- mustfree = true;
- forward_slash(var);
- }
-#endif
-
// If "var" contains white space, escape it with a backslash.
// Required for ":e ~/tt" when $HOME includes a space.
if (esc && var != NULL && strpbrk(var, " \t") != NULL) {
@@ -1034,6 +1020,7 @@ size_t home_replace(const buf_T *const buf, const char *src, char *const dst, si
homedir_env = os_getenv("USERPROFILE");
}
#endif
+ TO_SLASH(homedir_env);
char *homedir_env_mod = homedir_env;
bool must_free = false;
diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c
index 032e4e32f0..1d1f26349d 100644
--- a/src/nvim/os/fs.c
+++ b/src/nvim/os/fs.c
@@ -113,6 +113,7 @@ int os_dirname(char *buf, size_t len)
xstrlcpy(buf, uv_strerror(error_number), len);
return FAIL;
}
+ TO_SLASH(buf);
return OK;
}
@@ -224,7 +225,9 @@ int os_nodetype(const char *name)
int os_exepath(char *buffer, size_t *size)
FUNC_ATTR_NONNULL_ALL
{
- return uv_exepath(buffer, size);
+ int r = uv_exepath(buffer, size);
+ TO_SLASH(buffer);
+ return r;
}
/// Checks if the file `name` is executable.
@@ -1112,6 +1115,7 @@ int os_mkdtemp(const char *templ, char *path)
int result = uv_fs_mkdtemp(NULL, &request, templ, NULL);
if (result == kLibuvSuccess) {
xstrlcpy(path, request.path, TEMP_FILE_PATH_MAXLEN);
+ TO_SLASH(path);
}
uv_fs_req_cleanup(&request);
return result;
@@ -1345,6 +1349,7 @@ char *os_realpath(const char *name, char *buf, size_t len)
buf = xmalloc(len);
}
xstrlcpy(buf, request.ptr, len);
+ TO_SLASH(buf);
}
uv_fs_req_cleanup(&request);
return result == kLibuvSuccess ? buf : NULL;
@@ -1430,6 +1435,7 @@ shortcut_end:
}
CoUninitialize();
+ TO_SLASH(rfname);
return rfname;
}
diff --git a/src/nvim/os/pty_proc_win.c b/src/nvim/os/pty_proc_win.c
index 14aacc9800..8cc66bf319 100644
--- a/src/nvim/os/pty_proc_win.c
+++ b/src/nvim/os/pty_proc_win.c
@@ -11,6 +11,7 @@
#include "nvim/os/os.h"
#include "nvim/os/pty_conpty_win.h"
#include "nvim/os/pty_proc_win.h"
+#include "nvim/path.h"
#include "os/pty_proc_win.c.generated.h"
@@ -261,6 +262,9 @@ static int build_cmd_line(char **argv, wchar_t **cmd_line, bool is_cmdexe)
arg_node->arg = xmalloc(buf_len);
if (is_cmdexe) {
xstrlcpy(arg_node->arg, *argv, buf_len);
+ if (argc == 0) {
+ TO_BACKSLASH(arg_node->arg);
+ }
} else {
quote_cmd_arg(arg_node->arg, buf_len, *argv);
}
diff --git a/src/nvim/os/stdpaths.c b/src/nvim/os/stdpaths.c
index 19f541d252..907f7259c8 100644
--- a/src/nvim/os/stdpaths.c
+++ b/src/nvim/os/stdpaths.c
@@ -43,10 +43,10 @@ static const char *const xdg_defaults_env_vars[] = {
/// Used in case environment variables contain nothing. Need to be expanded.
static const char *const xdg_defaults[] = {
#ifdef MSWIN
- [kXDGConfigHome] = "~\\AppData\\Local",
- [kXDGDataHome] = "~\\AppData\\Local",
- [kXDGCacheHome] = "~\\AppData\\Local\\Temp",
- [kXDGStateHome] = "~\\AppData\\Local",
+ [kXDGConfigHome] = "~/AppData/Local",
+ [kXDGDataHome] = "~/AppData/Local",
+ [kXDGCacheHome] = "~/AppData/Local/Temp",
+ [kXDGStateHome] = "~/AppData/Local",
[kXDGRuntimeDir] = NULL, // Decided by vim_mktempdir().
[kXDGConfigDirs] = NULL,
[kXDGDataDirs] = NULL,
@@ -166,6 +166,7 @@ char *stdpaths_get_xdg_var(const XDGVarType idx)
env_val = xstrdup("");
}
#endif
+ TO_SLASH(env_val);
char *ret = NULL;
if (env_val != NULL) {
@@ -213,10 +214,6 @@ char *get_xdg_home(const XDGVarType idx)
}
#endif
dir = concat_fnames_realloc(dir, IObuff, true);
-
-#ifdef BACKSLASH_IN_FILENAME
- slash_adjust(dir);
-#endif
}
return dir;
}
@@ -266,21 +263,22 @@ char *stdpaths_user_state_subpath(const char *fname, const size_t trailing_paths
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET
{
char *ret = concat_fnames_realloc(get_xdg_home(kXDGStateHome), fname, true);
- const size_t len = strlen(ret);
+ size_t len = strlen(ret);
const size_t numcommas = (escape_commas ? memcnt(ret, ',', len) : 0);
if (numcommas || trailing_pathseps) {
- ret = xrealloc(ret, len + trailing_pathseps + numcommas + 1);
- for (size_t i = 0; i < len + numcommas; i++) {
- if (ret[i] == ',') {
- memmove(ret + i + 1, ret + i, len - i + numcommas);
- ret[i] = '\\';
- i++;
+ size_t newlen = len + numcommas + trailing_pathseps;
+ ret = xrealloc(ret, newlen + 1);
+ ret[newlen] = NUL;
+
+ memset(ret + len + numcommas, PATHSEP, trailing_pathseps);
+ newlen -= trailing_pathseps;
+
+ while (numcommas && len > 0) {
+ ret[--newlen] = ret[--len];
+ if (ret[newlen] == ',') {
+ ret[--newlen] = '\\';
}
}
- if (trailing_pathseps) {
- memset(ret + len + numcommas, PATHSEP, trailing_pathseps);
- }
- ret[len + trailing_pathseps + numcommas] = NUL;
}
return ret;
}
diff --git a/src/nvim/path.c b/src/nvim/path.c
index 999194895b..295993f971 100644
--- a/src/nvim/path.c
+++ b/src/nvim/path.c
@@ -537,7 +537,7 @@ char *FullName_save(const char *fname, bool force)
char *buf = xmalloc(MAXPATHL);
if (vim_FullName(fname, buf, MAXPATHL, force) == FAIL) {
xfree(buf);
- return xstrdup(fname);
+ return TO_SLASH_SAVE(fname);
}
return buf;
}
@@ -551,7 +551,7 @@ char *save_abs_path(const char *name)
if (!path_is_absolute(name)) {
return FullName_save(name, true);
}
- return xstrdup(name);
+ return TO_SLASH_SAVE(name);
}
/// Checks if a path has a wildcard character including '~', unless at the end.
@@ -1516,6 +1516,30 @@ void slash_adjust(char *p)
}
#endif
+/// Convert all slashes to backslashes in-place.
+char *path_to_backslash(char *p)
+{
+ if (p != NULL) {
+ strchrsub(p, PATHSEP, '\\');
+ }
+ return p;
+}
+
+/// Convert all backslashes to forward slashes in-place.
+char *path_to_slash(char *p)
+{
+ if (p != NULL) {
+ strchrsub(p, '\\', PATHSEP);
+ }
+ return p;
+}
+
+/// Get an allocated copy of path to convert backslashes.
+char *path_to_slash_save(const char *p)
+{
+ return p == NULL ? NULL : path_to_slash(xstrdup(p));
+}
+
/// Add a file to a file list. Accepted flags:
/// EW_DIR add directories
/// EW_FILE add files
@@ -1561,9 +1585,7 @@ void addfile(garray_T *gap, char *f, int flags)
char *p = xmalloc(strlen(f) + 1 + isdir);
STRCPY(p, f);
-#ifdef BACKSLASH_IN_FILENAME
- slash_adjust(p);
-#endif
+ TO_SLASH(p);
// Append a slash or backslash after directory names if none is present.
if (isdir && (flags & EW_ADDSLASH)) {
add_pathsep(p);
@@ -1849,9 +1871,7 @@ int vim_FullName(const char *fname, char *buf, size_t len, bool force)
if (strlen(fname) > (len - 1)) {
xstrlcpy(buf, fname, len); // truncate
-#ifdef MSWIN
- slash_adjust(buf);
-#endif
+ TO_SLASH(buf);
return FAIL;
}
@@ -1864,9 +1884,7 @@ int vim_FullName(const char *fname, char *buf, size_t len, bool force)
if (rv == FAIL) {
xstrlcpy(buf, fname, len); // something failed; use the filename
}
-#ifdef MSWIN
- slash_adjust(buf);
-#endif
+ TO_SLASH(buf);
return rv;
}
@@ -1899,7 +1917,7 @@ char *fix_fname(const char *fname)
return FullName_save(fname, false);
}
- fname = xstrdup(fname);
+ fname = TO_SLASH_SAVE(fname);
# ifdef CASE_INSENSITIVE_FILENAME
path_fix_case((char *)fname); // set correct case for file name
@@ -2476,6 +2494,7 @@ void path_guess_exepath(const char *argv0, char *buf, size_t bufsize)
xstrlcat(NameBuff, argv0, sizeof(NameBuff));
if (os_can_exe(NameBuff, NULL, false)) {
xstrlcpy(buf, NameBuff, bufsize);
+ TO_SLASH(buf);
return;
}
} while (iter != NULL);
diff --git a/src/nvim/path.h b/src/nvim/path.h
index ff898d3550..48f5250e53 100644
--- a/src/nvim/path.h
+++ b/src/nvim/path.h
@@ -41,4 +41,14 @@ typedef enum file_comparison {
kEqualFileNames = 7, ///< Both don't exist and file names are same.
} FileComparison;
+#ifdef BACKSLASH_IN_FILENAME
+# define TO_SLASH(p) path_to_slash(p)
+# define TO_SLASH_SAVE(p) path_to_slash_save(p)
+# define TO_BACKSLASH(p) path_to_backslash(p)
+#else
+# define TO_SLASH(...)
+# define TO_SLASH_SAVE(p) xstrdup(p)
+# define TO_BACKSLASH(...)
+#endif
+
#include "path.h.generated.h"
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index 9e90aabc5d..4a2f158232 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -2314,12 +2314,6 @@ static int qf_get_fnum(qf_list_T *qfl, char *directory, char *fname)
return 0;
}
-#ifdef BACKSLASH_IN_FILENAME
- if (directory != NULL) {
- slash_adjust(directory);
- }
- slash_adjust(fname);
-#endif
String fname_str = cstr_as_string(fname);
if (directory != NULL && !vim_isAbsName(fname)) {
ptr = concat_fnames(cstr_as_string(directory), fname_str, true);
@@ -2341,6 +2335,7 @@ static int qf_get_fnum(qf_list_T *qfl, char *directory, char *fname)
} else {
bufname = fname_str;
}
+ TO_SLASH(bufname.data);
if (qf_last_bufname != NULL && strcmp(bufname.data, qf_last_bufname) == 0
&& bufref_valid(&qf_last_bufref)) {
diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c
index e8fbda49b9..ec3c8ac9d8 100644
--- a/src/nvim/runtime.c
+++ b/src/nvim/runtime.c
@@ -710,6 +710,7 @@ static ArrayOf(String) runtime_get_named_common(bool lua, Array pat, bool all,
item->path, pat_item.data.string.data);
if (size < buf_len) {
if (os_file_is_readable(buf)) {
+ TO_SLASH(buf);
ADD_C(rv, CSTR_TO_ARENA_OBJ(arena, buf));
if (!all) {
goto done;
@@ -1843,10 +1844,11 @@ char *runtimepath_default(bool clean_arg)
char *const config_home = clean_arg
? NULL
: stdpaths_get_xdg_var(kXDGConfigHome);
- char *const vimruntime = vim_getenv("VIMRUNTIME");
char *const libdir = get_lib_dir();
char *const data_dirs = stdpaths_get_xdg_var(kXDGDataDirs);
char *const config_dirs = stdpaths_get_xdg_var(kXDGConfigDirs);
+ char *vimruntime = vim_getenv("VIMRUNTIME");
+ TO_SLASH(vimruntime);
#define SITE_SIZE (sizeof("site") - 1)
#define AFTER_SIZE (sizeof("after") - 1)
size_t data_len = 0;
@@ -2537,6 +2539,7 @@ void ex_scriptnames(exarg_T *eap)
} else {
expand_env(eap->arg, NameBuff, MAXPATHL);
eap->arg = NameBuff;
+ TO_SLASH(eap->arg);
}
do_exedit(eap, NULL);
}
diff --git a/src/nvim/shada.c b/src/nvim/shada.c
index afeecd9542..7034c84908 100644
--- a/src/nvim/shada.c
+++ b/src/nvim/shada.c
@@ -3272,6 +3272,7 @@ shada_read_next_item_start:
}
if (HAS_KEY(&it, _shada_mark, f)) {
entry->data.filemark.fname = xmemdupz(it.f.data, it.f.size);
+ TO_SLASH(entry->data.filemark.fname);
}
if (entry->data.filemark.fname == NULL) {
@@ -3458,6 +3459,7 @@ shada_read_next_item_start:
}
if (HAS_KEY(&it, _shada_buflist_item, f)) {
e->fname = xmemdupz(it.f.data, it.f.size);
+ TO_SLASH(e->fname);
}
if (e->pos.lnum <= 0) {
diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c
index 9dd3231b48..29f2d1de9f 100644
--- a/src/nvim/syntax.c
+++ b/src/nvim/syntax.c
@@ -3985,6 +3985,8 @@ static void syn_cmd_include(exarg_T *eap, int syncing)
}
return;
}
+ } else {
+ TO_SLASH(eap->arg);
}
// Save and restore the existing top-level grouplist id and ":syn
diff --git a/src/nvim/tag.c b/src/nvim/tag.c
index 78f2a53f7b..dc4c59e767 100644
--- a/src/nvim/tag.c
+++ b/src/nvim/tag.c
@@ -2017,11 +2017,6 @@ static void findtags_add_match(findtags_state_T *st, tagptrs_T *tagpp, findtags_
char *p = mfp;
p[0] = (char)(mtt + 1);
STRCPY(p + 1, st->tag_fname);
-#ifdef BACKSLASH_IN_FILENAME
- // Ignore differences in slashes, avoid adding
- // both path/file and path\file.
- slash_adjust(p + 1);
-#endif
p[tag_fname_len + 1] = TAG_SEP;
char *s = p + 1 + tag_fname_len + 1;
STRCPY(s, st->lbuf);
@@ -2438,9 +2433,6 @@ static bool found_tagfile_cb(int num_fnames, char **fnames, bool all, void *cook
for (int i = 0; i < num_fnames; i++) {
char *const tag_fname = xstrdup(fnames[i]);
-#ifdef BACKSLASH_IN_FILENAME
- slash_adjust(tag_fname);
-#endif
simplify_filename(tag_fname);
GA_APPEND(char *, &tag_fnames, tag_fname);
@@ -2502,9 +2494,6 @@ int get_tagfname(tagname_T *tnp, int first, char *buf)
tnp->tn_hf_idx++;
xstrlcpy(buf, p_hf, MAXPATHL - STRLEN_LITERAL("tags"));
STRCPY(path_tail(buf), "tags");
-#ifdef BACKSLASH_IN_FILENAME
- slash_adjust(buf);
-#endif
simplify_filename(buf);
for (int i = 0; i < tag_fnames.ga_len; i++) {
@@ -3075,6 +3064,8 @@ static char *expand_tag_fname(char *fname, char *const tag_fname, const bool exp
char *expanded_fname = NULL;
expand_T xpc;
+ fname = TO_SLASH_SAVE(fname);
+
// Expand file name (for environment variables) when needed.
// Disallow backticks, they could execute arbitrary shell
// commands. This is not needed for tag filenames.
@@ -3084,6 +3075,7 @@ static char *expand_tag_fname(char *fname, char *const tag_fname, const bool exp
expanded_fname = ExpandOne(&xpc, fname, NULL,
WILD_LIST_NOTFOUND|WILD_SILENT, WILD_EXPAND_FREE);
if (expanded_fname != NULL) {
+ xfree(fname);
fname = expanded_fname;
}
}
@@ -3101,7 +3093,7 @@ static char *expand_tag_fname(char *fname, char *const tag_fname, const bool exp
retval = xstrdup(fname);
}
- xfree(expanded_fname);
+ xfree(fname);
return retval;
}