mirror of
https://github.com/BurntSushi/ripgrep.git
synced 2025-07-11 14:30:24 +02:00
printer: use std::path::absolute on Windows
This commit is contained in:
@ -702,16 +702,20 @@ impl HyperlinkPath {
|
|||||||
/// Returns a hyperlink path from an OS path.
|
/// Returns a hyperlink path from an OS path.
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
pub(crate) fn from_path(original_path: &Path) -> Option<HyperlinkPath> {
|
pub(crate) fn from_path(original_path: &Path) -> Option<HyperlinkPath> {
|
||||||
// On Windows, Path::canonicalize returns the result of
|
// On Windows, we use `std::path::absolute` instead of `Path::canonicalize`
|
||||||
// GetFinalPathNameByHandleW with VOLUME_NAME_DOS,
|
// as it can be much faster since it does not touch the file system.
|
||||||
// which produces paths such as the following:
|
// It wraps the [`GetFullPathNameW`][1] API, except for verbatim paths
|
||||||
|
// (those which start with `\\?\`, see [the documentation][2] for details).
|
||||||
|
//
|
||||||
|
// Here, we strip any verbatim path prefixes since we cannot use them
|
||||||
|
// in hyperlinks anyway. This can only happen if the user explicitly
|
||||||
|
// supplies a verbatim path as input, which already needs to be absolute:
|
||||||
//
|
//
|
||||||
// \\?\C:\dir\file.txt (local path)
|
// \\?\C:\dir\file.txt (local path)
|
||||||
// \\?\UNC\server\dir\file.txt (network share)
|
// \\?\UNC\server\dir\file.txt (network share)
|
||||||
//
|
//
|
||||||
// The \\?\ prefix comes from VOLUME_NAME_DOS and is constant.
|
// The `\\?\` prefix is constant for verbatim paths, and can be followed
|
||||||
// It is followed either by the drive letter, or by UNC\
|
// by `UNC\` (universal naming convention), which denotes a network share.
|
||||||
// (universal naming convention), which denotes a network share.
|
|
||||||
//
|
//
|
||||||
// Given that the default URL format on Windows is file://{path}
|
// Given that the default URL format on Windows is file://{path}
|
||||||
// we need to return the following from this function:
|
// we need to return the following from this function:
|
||||||
@ -750,18 +754,19 @@ impl HyperlinkPath {
|
|||||||
//
|
//
|
||||||
// It doesn't parse any other number of slashes in "file//server" as a
|
// It doesn't parse any other number of slashes in "file//server" as a
|
||||||
// network path.
|
// network path.
|
||||||
|
//
|
||||||
|
// [1]: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfullpathnamew
|
||||||
|
// [2]: https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file
|
||||||
|
|
||||||
const WIN32_NAMESPACE_PREFIX: &str = r"\\?\";
|
const WIN32_NAMESPACE_PREFIX: &str = r"\\?\";
|
||||||
const UNC_PREFIX: &str = r"UNC\";
|
const UNC_PREFIX: &str = r"UNC\";
|
||||||
|
|
||||||
// As for Unix, we canonicalize the path to make sure we have an
|
let path = match std::path::absolute(original_path) {
|
||||||
// absolute path.
|
|
||||||
let path = match original_path.canonicalize() {
|
|
||||||
Ok(path) => path,
|
Ok(path) => path,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
log::debug!(
|
log::debug!(
|
||||||
"hyperlink creation for {:?} failed, error occurred \
|
"hyperlink creation for {:?} failed, error occurred \
|
||||||
during path canonicalization: {}",
|
during conversion to absolute path: {}",
|
||||||
original_path,
|
original_path,
|
||||||
err,
|
err,
|
||||||
);
|
);
|
||||||
@ -784,24 +789,20 @@ impl HyperlinkPath {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// As the comment above says, we expect all canonicalized paths to
|
|
||||||
// begin with a \\?\. If it doesn't, then something weird is happening
|
|
||||||
// and we should just give up.
|
|
||||||
if !string.starts_with(WIN32_NAMESPACE_PREFIX) {
|
|
||||||
log::debug!(
|
|
||||||
"hyperlink creation for {:?} failed, canonicalization \
|
|
||||||
returned {:?}, which does not start with \\\\?\\",
|
|
||||||
original_path,
|
|
||||||
path,
|
|
||||||
);
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
string = &string[WIN32_NAMESPACE_PREFIX.len()..];
|
|
||||||
|
|
||||||
// And as above, drop the UNC prefix too, but keep the leading slash.
|
// Strip verbatim path prefixes (see the comment above for details).
|
||||||
if string.starts_with(UNC_PREFIX) {
|
if string.starts_with(WIN32_NAMESPACE_PREFIX) {
|
||||||
string = &string[(UNC_PREFIX.len() - 1)..];
|
string = &string[WIN32_NAMESPACE_PREFIX.len()..];
|
||||||
|
|
||||||
|
// Drop the UNC prefix if there is one, but keep the leading slash.
|
||||||
|
if string.starts_with(UNC_PREFIX) {
|
||||||
|
string = &string[(UNC_PREFIX.len() - 1)..];
|
||||||
|
}
|
||||||
|
} else if string.starts_with(r"\\") || string.starts_with(r"//") {
|
||||||
|
// Drop one of the two leading slashes of network paths, it will be added back.
|
||||||
|
string = &string[1..];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finally, add a leading slash. In the local file case, this turns
|
// Finally, add a leading slash. In the local file case, this turns
|
||||||
// C:\foo\bar into /C:\foo\bar (and then percent encoding turns it into
|
// C:\foo\bar into /C:\foo\bar (and then percent encoding turns it into
|
||||||
// /C:/foo/bar). In the network share case, this turns \share\foo\bar
|
// /C:/foo/bar). In the network share case, this turns \share\foo\bar
|
||||||
@ -1006,4 +1007,33 @@ mod tests {
|
|||||||
err(InvalidVariable("bar{{".to_string())),
|
err(InvalidVariable("bar{{".to_string())),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn convert_to_hyperlink_path() {
|
||||||
|
let convert = |path| {
|
||||||
|
String::from_utf8(
|
||||||
|
HyperlinkPath::from_path(Path::new(path)).unwrap().0,
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(convert(r"C:\dir\file.txt"), "/C:/dir/file.txt");
|
||||||
|
assert_eq!(
|
||||||
|
convert(r"C:\foo\bar\..\other\baz.txt"),
|
||||||
|
"/C:/foo/other/baz.txt"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(convert(r"\\server\dir\file.txt"), "//server/dir/file.txt");
|
||||||
|
assert_eq!(
|
||||||
|
convert(r"\\server\dir\foo\..\other\file.txt"),
|
||||||
|
"//server/dir/other/file.txt"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(convert(r"\\?\C:\dir\file.txt"), "/C:/dir/file.txt");
|
||||||
|
assert_eq!(
|
||||||
|
convert(r"\\?\UNC\server\dir\file.txt"),
|
||||||
|
"//server/dir/file.txt"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user