Skip to content

Find the program using PATHEXT #37381

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
31 changes: 25 additions & 6 deletions src/libstd/sys/windows/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,12 +125,9 @@ impl Command {
self.stderr = Some(stderr);
}

pub fn spawn(&mut self, default: Stdio, needs_stdin: bool)
-> io::Result<(Process, StdioPipes)> {
// To have the spawning semantics of unix/windows stay the same, we need
// to read the *child's* PATH if one is provided. See #15149 for more
// details.
let program = self.env.as_ref().and_then(|env| {
pub fn find_program(&mut self) -> Option<Path> {
self.env.as_ref().and_then(|env| {
let env_pathext = env.get("PATHEXT");
for (key, v) in env {
if OsStr::new("PATH") != &**key { continue }

Expand All @@ -141,12 +138,34 @@ impl Command {
.with_extension(env::consts::EXE_EXTENSION);
if fs::metadata(&path).is_ok() {
return Some(path.into_os_string())
} else {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We typically try to avoid rightward drift where possible, so because of the return above you can omit this else, de-indenting what's below. You can also change what's below to:

let exts = match env_pathext {
    Some(e) => e,
    None => continue,
};
// ...

to get rid of another layer of indentation.

// Windows relies on path extensions to resolve commands.
// Path extensions are found in the PATHEXT environment variable.
if let Some(exts) = env_pathext {
for ext in split_paths(&exts) {
let ext_str = ext.to_string_lossy();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we'll want to call to_str here to not misinterpret OS strings (e.g. lossy may change the meaning), but you can just skip anything that's None. You can probably do:

for ext in split_paths(&ext).filter_map(|e| e.to_str()) {
    // ...
}

let path = path.with_extension(
ext_str.trim_matches('.')
);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps this could fit on oneline?

if fs::metadata(&path).is_ok() {
return Some(path.into_os_string())
}
}
}
}
}
break
}
None
});
}

pub fn spawn(&mut self, default: Stdio, needs_stdin: bool)
-> io::Result<(Process, StdioPipes)> {
// To have the spawning semantics of unix/windows stay the same, we need
// to read the *child's* PATH if one is provided. See #15149 for more
// details.
let program = self.find_program();

let mut si = zeroed_startupinfo();
si.cb = mem::size_of::<c::STARTUPINFO>() as c::DWORD;
Expand Down