1
Fork 0

Keep the path after program_exists succeeds

This commit is contained in:
Chris Denton 2022-02-17 13:17:19 +00:00
parent d4686c6066
commit 93f627daa5
No known key found for this signature in database
GPG key ID: 713472F2F45627DE

View file

@ -269,11 +269,11 @@ impl Command {
None None
}; };
let program = resolve_exe(&self.program, || env::var_os("PATH"), child_paths)?; let program = resolve_exe(&self.program, || env::var_os("PATH"), child_paths)?;
let is_batch_file = program // Case insensitive "ends_with" of UTF-16 encoded ".bat" or ".cmd"
.extension() let is_batch_file = matches!(
.map(|ext| ext.eq_ignore_ascii_case("cmd") || ext.eq_ignore_ascii_case("bat")) program.len().checked_sub(5).and_then(|i| program.get(i..)),
.unwrap_or(false); Some([46, 98 | 66, 97 | 65, 116 | 84, 0] | [46, 99 | 67, 109 | 77, 100 | 68, 0])
let program = path::maybe_verbatim(&program)?; );
let mut cmd_str = let mut cmd_str =
make_command_line(&program, &self.args, self.force_quotes_enabled, is_batch_file)?; make_command_line(&program, &self.args, self.force_quotes_enabled, is_batch_file)?;
cmd_str.push(0); // add null terminator cmd_str.push(0); // add null terminator
@ -370,7 +370,7 @@ fn resolve_exe<'a>(
exe_path: &'a OsStr, exe_path: &'a OsStr,
parent_paths: impl FnOnce() -> Option<OsString>, parent_paths: impl FnOnce() -> Option<OsString>,
child_paths: Option<&OsStr>, child_paths: Option<&OsStr>,
) -> io::Result<PathBuf> { ) -> io::Result<Vec<u16>> {
// Early return if there is no filename. // Early return if there is no filename.
if exe_path.is_empty() || path::has_trailing_slash(exe_path) { if exe_path.is_empty() || path::has_trailing_slash(exe_path) {
return Err(io::const_io_error!( return Err(io::const_io_error!(
@ -392,19 +392,19 @@ fn resolve_exe<'a>(
if has_exe_suffix { if has_exe_suffix {
// The application name is a path to a `.exe` file. // The application name is a path to a `.exe` file.
// Let `CreateProcessW` figure out if it exists or not. // Let `CreateProcessW` figure out if it exists or not.
return Ok(exe_path.into()); return path::maybe_verbatim(Path::new(exe_path));
} }
let mut path = PathBuf::from(exe_path); let mut path = PathBuf::from(exe_path);
// Append `.exe` if not already there. // Append `.exe` if not already there.
path = path::append_suffix(path, EXE_SUFFIX.as_ref()); path = path::append_suffix(path, EXE_SUFFIX.as_ref());
if program_exists(&path) { if let Some(path) = program_exists(&path) {
return Ok(path); return Ok(path);
} else { } else {
// It's ok to use `set_extension` here because the intent is to // It's ok to use `set_extension` here because the intent is to
// remove the extension that was just added. // remove the extension that was just added.
path.set_extension(""); path.set_extension("");
return Ok(path); return path::maybe_verbatim(&path);
} }
} else { } else {
ensure_no_nuls(exe_path)?; ensure_no_nuls(exe_path)?;
@ -419,7 +419,7 @@ fn resolve_exe<'a>(
if !has_extension { if !has_extension {
path.set_extension(EXE_EXTENSION); path.set_extension(EXE_EXTENSION);
} }
if program_exists(&path) { Some(path) } else { None } program_exists(&path)
}); });
if let Some(path) = result { if let Some(path) = result {
return Ok(path); return Ok(path);
@ -435,10 +435,10 @@ fn search_paths<Paths, Exists>(
parent_paths: Paths, parent_paths: Paths,
child_paths: Option<&OsStr>, child_paths: Option<&OsStr>,
mut exists: Exists, mut exists: Exists,
) -> Option<PathBuf> ) -> Option<Vec<u16>>
where where
Paths: FnOnce() -> Option<OsString>, Paths: FnOnce() -> Option<OsString>,
Exists: FnMut(PathBuf) -> Option<PathBuf>, Exists: FnMut(PathBuf) -> Option<Vec<u16>>,
{ {
// 1. Child paths // 1. Child paths
// This is for consistency with Rust's historic behaviour. // This is for consistency with Rust's historic behaviour.
@ -490,17 +490,18 @@ where
} }
/// Check if a file exists without following symlinks. /// Check if a file exists without following symlinks.
fn program_exists(path: &Path) -> bool { fn program_exists(path: &Path) -> Option<Vec<u16>> {
unsafe { unsafe {
to_u16s(path) let path = path::maybe_verbatim(path).ok()?;
.map(|path| { // Getting attributes using `GetFileAttributesW` does not follow symlinks
// Getting attributes using `GetFileAttributesW` does not follow symlinks // and it will almost always be successful if the link exists.
// and it will almost always be successful if the link exists. // There are some exceptions for special system files (e.g. the pagefile)
// There are some exceptions for special system files (e.g. the pagefile) // but these are not executable.
// but these are not executable. if c::GetFileAttributesW(path.as_ptr()) == c::INVALID_FILE_ATTRIBUTES {
c::GetFileAttributesW(path.as_ptr()) != c::INVALID_FILE_ATTRIBUTES None
}) } else {
.unwrap_or(false) Some(path)
}
} }
} }