diff options
| author | tao <2471314@gmail.com> | 2026-04-25 01:20:25 +0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-04-24 13:20:25 -0400 |
| commit | f130922744657b731c24a842a85fd8a9f8058073 (patch) | |
| tree | 9540b465b8a5d937638e2f9b8a7a0d2e920f593b /src | |
| parent | 27191e0f4f4f9086180a8fbe3e52c1c280a70b09 (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.c | 3 | ||||
| -rw-r--r-- | src/nvim/ascii_defs.h | 9 | ||||
| -rw-r--r-- | src/nvim/cmdexpand.c | 10 | ||||
| -rw-r--r-- | src/nvim/cmdexpand.h | 2 | ||||
| -rw-r--r-- | src/nvim/eval/fs.c | 8 | ||||
| -rw-r--r-- | src/nvim/event/libuv_proc.c | 3 | ||||
| -rw-r--r-- | src/nvim/ex_docmd.c | 6 | ||||
| -rw-r--r-- | src/nvim/file_search.c | 8 | ||||
| -rw-r--r-- | src/nvim/insexpand.c | 35 | ||||
| -rw-r--r-- | src/nvim/main.c | 3 | ||||
| -rw-r--r-- | src/nvim/memline.c | 2 | ||||
| -rw-r--r-- | src/nvim/option.c | 34 | ||||
| -rw-r--r-- | src/nvim/os/env.c | 17 | ||||
| -rw-r--r-- | src/nvim/os/fs.c | 8 | ||||
| -rw-r--r-- | src/nvim/os/pty_proc_win.c | 4 | ||||
| -rw-r--r-- | src/nvim/os/stdpaths.c | 36 | ||||
| -rw-r--r-- | src/nvim/path.c | 43 | ||||
| -rw-r--r-- | src/nvim/path.h | 10 | ||||
| -rw-r--r-- | src/nvim/quickfix.c | 7 | ||||
| -rw-r--r-- | src/nvim/runtime.c | 5 | ||||
| -rw-r--r-- | src/nvim/shada.c | 2 | ||||
| -rw-r--r-- | src/nvim/syntax.c | 2 | ||||
| -rw-r--r-- | src/nvim/tag.c | 16 |
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; } |
