about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorAaron Turon <aturon@mozilla.com>2014-05-30 10:31:20 -0700
committerAaron Turon <aturon@mozilla.com>2014-05-30 21:10:48 -0700
commitb1fbbf3e48fdcfaa5e875bf457bc098faec30dbb (patch)
treec5f73c492993039e425f26a82a9caec9c39b5293 /src
parentcc4513202d6f9c6896054ebaa1d99230b06e9f10 (diff)
downloadrust-b1fbbf3e48fdcfaa5e875bf457bc098faec30dbb.tar.gz
rust-b1fbbf3e48fdcfaa5e875bf457bc098faec30dbb.zip
Add os::split_paths
Adds a platform-specific function, `split_paths` to the `os` module. This
function can be used to parse PATH-like environment variables according to
local platform conventions.

Closes #14352.
Diffstat (limited to 'src')
-rw-r--r--src/libstd/os.rs96
1 files changed, 94 insertions, 2 deletions
diff --git a/src/libstd/os.rs b/src/libstd/os.rs
index 7e6d3c0606f..fa0116b2482 100644
--- a/src/libstd/os.rs
+++ b/src/libstd/os.rs
@@ -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};
@@ -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
@@ -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;
@@ -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
 }