fix(path): path_shorten_fname() treats "c:x" as a root #42657

Problem:
On Windows, path_shorten_fname() treats "c:x" as a root ("c:\"), so it
shortens "c:xyz\foo" to "yz\foo".

Solution:
Only "c:\" (drive and separator) is a root, not "c:x". Also simplify
path_to_absolute().
This commit is contained in:
Justin M. Keyes authored and GitHub committed 2026-11-02 11:06:06 -05:00
1 parent 54eac81f8f
commit 9213ffc7da
3 files changed
+28 -36

No files matched your search

+18 -18
View File
@@ -2035,7 +2035,7 @@ char *path_shorten_fname(char *full_path, char *dir_name)
}
// If dir_name is a path head, full_path can always be made relative.
if (len == (size_t)path_head_length() && is_path_head(dir_name)) {
if (len == (size_t)path_head_length() && get_past_head(dir_name) == dir_name + len) {
return full_path + len;
}
@@ -2288,28 +2288,28 @@ int append_path(char *path, const char *to_append, size_t max_len)
static int path_to_absolute(const char *fname, char *buf, size_t len, int force)
FUNC_ATTR_NONNULL_ALL
{
const char *p = fname;
*buf = NUL;
// expand it if forced or not an absolute path
if (force || !path_is_absolute(p)
#ifdef MSWIN // enforce drive letter on Windows paths
|| vim_ispathsep_nocolon(p[0])
if (!force && path_is_absolute(fname)
#ifdef MSWIN // Enforce drive letter on Windows paths.
&& !vim_ispathsep_nocolon(fname[0])
#endif
) {
p = path_tail(p);
if (strcmp(p, "..") == 0) {
p += 2;
}
char *relative_directory = xmemdupz(fname, (size_t)(p - fname));
int ret = path_full_dir_name(relative_directory, buf, len);
xfree(relative_directory);
if (ret == FAIL) {
return FAIL;
}
return append_path(buf, fname, len);
}
return append_path(buf, p, len);
const char *tail = path_tail(fname);
// "dir/.." => realpath("dir/.."), because append_path() doesn't resolve "..".
if (strcmp(tail, "..") == 0) {
tail += 2;
}
char *dir = xmemdupz(fname, (size_t)(tail - fname));
int ret = path_full_dir_name(dir, buf, len);
xfree(dir);
if (ret == FAIL) {
return FAIL;
}
return append_path(buf, tail, len);
}
/// Check if file `fname` is a full (absolute) path.
+1
View File
@@ -390,6 +390,7 @@ describe('vim._watch', function()
end)
it('watch() reports an error once when the parent directory is deleted #41851', function()
skip(is_os('mac'), 'flaky test on mac')
local root_dir = t.tmpname(false)
t.finally(function()
n.rmdir(root_dir)
+9 -18
View File
@@ -513,7 +513,7 @@ describe('path.c', function()
eq(OK, result)
end)
itp('produces absolute path for .. without a slash', function()
itp('produces absolute path for .. without a slash, and dir/..', function()
local old_dir = uv.cwd()
uv.chdir('..')
local expected = uv.cwd()
@@ -524,15 +524,10 @@ describe('path.c', function()
local buf, result = vim_FullName(filename, buflen, do_expand)
eq(expected, ffi.string(buf))
eq(OK, result)
end)
itp('produces absolute path for "dir/.."', function()
local expected = uv.cwd()
local filename = 'unit-test-directory/..'
local buflen = get_buf_len(expected, filename)
local do_expand = 1
local buf, result = vim_FullName(filename, buflen, do_expand)
eq(expected, ffi.string(buf))
-- Also "dir/..".
filename = 'unit-test-directory/..'
buf, result = vim_FullName(filename, get_buf_len(old_dir, filename), do_expand)
eq(old_dir, ffi.string(buf))
eq(OK, result)
end)
@@ -609,14 +604,10 @@ describe('path.c', function()
local buf, result = vim_FullName(filename, buflen, do_expand)
eq(expected, ffi.string(buf))
eq(OK, result)
end)
itp('removes trailing slash from an existing relative directory', function()
local expected = uv.cwd() .. '/unit-test-directory'
local filename = 'unit-test-directory/'
local buflen = get_buf_len(expected, filename)
local do_expand = 1
local buf, result = vim_FullName(filename, buflen, do_expand)
-- But removes it from an existing directory.
expected = uv.cwd() .. '/unit-test-directory'
filename = 'unit-test-directory/'
buf, result = vim_FullName(filename, get_buf_len(expected, filename), do_expand)
eq(expected, ffi.string(buf))
eq(OK, result)
end)