Skip to content

Add os::split_paths #14544

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
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 94 additions & 2 deletions src/libstd/os.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ use libc;
use ops::Drop;
use option::{Some, None, Option};
use os;
use path::{Path, GenericPath};
use path::{Path, GenericPath, BytesContainer};
use ptr::RawPtr;
use ptr;
use result::{Err, Ok, Result};
Expand Down Expand Up @@ -395,6 +395,63 @@ pub fn unsetenv(n: &str) {
_unsetenv(n);
}

#[cfg(unix)]
/// Parse a string or vector according to the platform's conventions
/// for the `PATH` environment variable. Drops empty paths.
pub fn split_paths<T: BytesContainer>(unparsed: T) -> Vec<Path> {
unparsed.container_as_bytes()
.split(|b| *b == ':' as u8)
.filter(|s| s.len() > 0)
.map(Path::new)
.collect()
}

#[cfg(windows)]
/// Parse a string or vector according to the platform's conventions
/// for the `PATH` environment variable. Drops empty paths.
pub fn split_paths<T: BytesContainer>(unparsed: T) -> Vec<Path> {
// On Windows, the PATH environment variable is semicolon separated. Double
// quotes are used as a way of introducing literal semicolons (since
// c:\some;dir is a valid Windows path). Double quotes are not themselves
// permitted in path names, so there is no way to escape a double quote.
// Quoted regions can appear in arbitrary locations, so
//
// c:\foo;c:\som"e;di"r;c:\bar
//
// Should parse as [c:\foo, c:\some;dir, c:\bar].
//
// (The above is based on testing; there is no clear reference available
// for the grammar.)

let mut parsed = Vec::new();
let mut in_progress = Vec::new();
let mut in_quote = false;

for b in unparsed.container_as_bytes().iter() {
match *b as char {
';' if !in_quote => {
// ignore zero-length path strings
if in_progress.len() > 0 {
parsed.push(Path::new(in_progress.as_slice()));
}
in_progress.truncate(0)
}
'\"' => {
in_quote = !in_quote;
}
_ => {
in_progress.push(*b);
}
}
}

if in_progress.len() > 0 {
parsed.push(Path::new(in_progress));
}

parsed
}

/// A low-level OS in-memory pipe.
pub struct Pipe {
/// A file descriptor representing the reading end of the pipe. Data written
Expand Down Expand Up @@ -1502,7 +1559,7 @@ mod tests {
use c_str::ToCStr;
use option;
use os::{env, getcwd, getenv, make_absolute};
use os::{setenv, unsetenv};
use os::{split_paths, setenv, unsetenv};
use os;
use rand::Rng;
use rand;
Expand Down Expand Up @@ -1754,5 +1811,40 @@ mod tests {
fs::unlink(&path).unwrap();
}

#[test]
#[cfg(windows)]
fn split_paths_windows() {
fn check_parse(unparsed: &str, parsed: &[&str]) -> bool {
split_paths(unparsed) ==
parsed.iter().map(|s| Path::new(*s)).collect()
}

assert!(check_parse("", []));
assert!(check_parse(r#""""#, []));
assert!(check_parse(";;", []));
assert!(check_parse(r"c:\", [r"c:\"]));
assert!(check_parse(r"c:\;", [r"c:\"]));
assert!(check_parse(r"c:\;c:\Program Files\",
[r"c:\", r"c:\Program Files\"]));
assert!(check_parse(r#"c:\;c:\"foo"\"#, [r"c:\", r"c:\foo\"]));
assert!(check_parse(r#"c:\;c:\"foo;bar"\;c:\baz"#,
[r"c:\", r"c:\foo;bar\", r"c:\baz"]));
}

#[test]
#[cfg(unix)]
fn split_paths_unix() {
fn check_parse(unparsed: &str, parsed: &[&str]) -> bool {
split_paths(unparsed) ==
parsed.iter().map(|s| Path::new(*s)).collect()
}

assert!(check_parse("", []));
assert!(check_parse("::", []));
assert!(check_parse("/", ["/"]));
assert!(check_parse("/:", ["/"]));
assert!(check_parse("/:/usr/local", ["/", "/usr/local"]));
}

// More recursive_mkdir tests are in extra::tempfile
}