]> git.lizzy.rs Git - rust.git/blobdiff - src/libstd/os.rs
auto merge of #15999 : Kimundi/rust/fix_folder, r=nikomatsakis
[rust.git] / src / libstd / os.rs
index 6674dd532aeb0b5f76d924b3e8c5a5555a863a00..7fff510a60a4b19ae7a2c69c97c46d723b63320c 100644 (file)
@@ -32,7 +32,7 @@
 #![allow(non_snake_case_functions)]
 
 use clone::Clone;
-use collections::Collection;
+use collections::{Collection, MutableSeq};
 use fmt;
 use io::{IoResult, IoError};
 use iter::Iterator;
@@ -45,9 +45,8 @@
 use ptr::RawPtr;
 use ptr;
 use result::{Err, Ok, Result};
-use slice::{Vector, ImmutableVector, MutableVector};
+use slice::{Vector, ImmutableVector, MutableVector, ImmutableEqVector};
 use str::{Str, StrSlice, StrAllocating};
-use str;
 use string::String;
 use sync::atomics::{AtomicInt, INIT_ATOMIC_INT, SeqCst};
 use vec::Vec;
@@ -56,8 +55,6 @@
 use c_str::ToCStr;
 #[cfg(unix)]
 use libc::c_char;
-#[cfg(windows)]
-use str::OwnedStr;
 
 /// Get the number of cores available
 pub fn num_cpus() -> uint {
@@ -137,7 +134,7 @@ pub fn getcwd() -> Path {
             fail!();
         }
     }
-    Path::new(str::from_utf16(str::truncate_utf16_at_nul(buf))
+    Path::new(String::from_utf16(::str::truncate_utf16_at_nul(buf))
               .expect("GetCurrentDirectoryW returned invalid UTF-16"))
 }
 
@@ -151,7 +148,6 @@ pub mod win32 {
     use slice::{MutableVector, ImmutableVector};
     use string::String;
     use str::StrSlice;
-    use str;
     use vec::Vec;
 
     pub fn fill_utf16_buf_and_decode(f: |*mut u16, DWORD| -> DWORD)
@@ -180,7 +176,7 @@ pub fn fill_utf16_buf_and_decode(f: |*mut u16, DWORD| -> DWORD)
                     // We want to explicitly catch the case when the
                     // closure returned invalid UTF-16, rather than
                     // set `res` to None and continue.
-                    let s = str::from_utf16(sub)
+                    let s = String::from_utf16(sub)
                         .expect("fill_utf16_buf_and_decode: closure created invalid UTF-16");
                     res = option::Some(s)
                 }
@@ -208,7 +204,7 @@ fn with_env_lock<T>(f: || -> T) -> T {
 /// Returns a vector of (variable, value) pairs, for all the environment
 /// variables of the current process.
 ///
-/// Invalid UTF-8 bytes are replaced with \uFFFD. See `str::from_utf8_lossy()`
+/// Invalid UTF-8 bytes are replaced with \uFFFD. See `String::from_utf8_lossy()`
 /// for details.
 ///
 /// # Example
@@ -223,8 +219,8 @@ fn with_env_lock<T>(f: || -> T) -> T {
 /// ```
 pub fn env() -> Vec<(String,String)> {
     env_as_bytes().move_iter().map(|(k,v)| {
-        let k = str::from_utf8_lossy(k.as_slice()).to_string();
-        let v = str::from_utf8_lossy(v.as_slice()).to_string();
+        let k = String::from_utf8_lossy(k.as_slice()).into_string();
+        let v = String::from_utf8_lossy(v.as_slice()).into_string();
         (k,v)
     }).collect()
 }
@@ -266,7 +262,7 @@ unsafe fn get_env_pairs() -> Vec<Vec<u8>> {
                 let p = &*ch.offset(i);
                 let len = ptr::position(p, |c| *c == 0);
                 raw::buf_as_slice(p, len, |s| {
-                    result.push(str::from_utf16_lossy(s).into_bytes());
+                    result.push(String::from_utf16_lossy(s).into_bytes());
                 });
                 i += len as int + 1;
             }
@@ -316,7 +312,7 @@ fn env_convert(input: Vec<Vec<u8>>) -> Vec<(Vec<u8>, Vec<u8>)> {
 /// None if the variable isn't set.
 ///
 /// Any invalid UTF-8 bytes in the value are replaced by \uFFFD. See
-/// `str::from_utf8_lossy()` for details.
+/// `String::from_utf8_lossy()` for details.
 ///
 /// # Failure
 ///
@@ -334,7 +330,7 @@ fn env_convert(input: Vec<Vec<u8>>) -> Vec<(Vec<u8>, Vec<u8>)> {
 /// }
 /// ```
 pub fn getenv(n: &str) -> Option<String> {
-    getenv_as_bytes(n).map(|v| str::from_utf8_lossy(v.as_slice()).to_string())
+    getenv_as_bytes(n).map(|v| String::from_utf8_lossy(v.as_slice()).into_string())
 }
 
 #[cfg(unix)]
@@ -398,9 +394,9 @@ pub fn getenv_as_bytes(n: &str) -> Option<Vec<u8>> {
 ///     None => println!("{} is not defined in the environment.", key)
 /// }
 /// ```
-pub fn setenv(n: &str, v: &str) {
+pub fn setenv<T: BytesContainer>(n: &str, v: T) {
     #[cfg(unix)]
-    fn _setenv(n: &str, v: &str) {
+    fn _setenv(n: &str, v: &[u8]) {
         unsafe {
             with_env_lock(|| {
                 n.with_c_str(|nbuf| {
@@ -413,18 +409,20 @@ fn _setenv(n: &str, v: &str) {
     }
 
     #[cfg(windows)]
-    fn _setenv(n: &str, v: &str) {
+    fn _setenv(n: &str, v: &[u8]) {
         let n: Vec<u16> = n.utf16_units().collect();
         let n = n.append_one(0);
-        let v: Vec<u16> = v.utf16_units().collect();
+        let v: Vec<u16> = ::str::from_utf8(v).unwrap().utf16_units().collect();
         let v = v.append_one(0);
+
         unsafe {
             with_env_lock(|| {
                 libc::SetEnvironmentVariableW(n.as_ptr(), v.as_ptr());
             })
         }
     }
-    _setenv(n, v)
+
+    _setenv(n, v.container_as_bytes())
 }
 
 /// Remove a variable from the environment entirely.
@@ -453,77 +451,130 @@ fn _unsetenv(n: &str) {
     _unsetenv(n);
 }
 
-#[cfg(unix)]
-/// Parse a string or vector according to the platform's conventions
-/// for the `PATH` environment variable and return a Vec<Path>.
-/// Drops empty paths.
+/// Parses input according to platform conventions for the `PATH`
+/// environment variable.
 ///
 /// # Example
 /// ```rust
 /// use std::os;
 ///
 /// let key = "PATH";
-/// match os::getenv(key) {
+/// match os::getenv_as_bytes(key) {
 ///     Some(paths) => {
 ///         for path in os::split_paths(paths).iter() {
 ///             println!("'{}'", path.display());
 ///         }
 ///     }
-///     None => println!("{} is not defined in the environnement.", key)
+///     None => println!("{} is not defined in the environment.", key)
 /// }
 /// ```
 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(unix)]
+    fn _split_paths<T: BytesContainer>(unparsed: T) -> Vec<Path> {
+        unparsed.container_as_bytes()
+                .split(|b| *b == b':')
+                .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 {
+    #[cfg(windows)]
+    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 {
+                b';' if !in_quote => {
                     parsed.push(Path::new(in_progress.as_slice()));
+                    in_progress.truncate(0)
+                }
+                b'"' => {
+                    in_quote = !in_quote;
+                }
+                _  => {
+                    in_progress.push(*b);
                 }
-                in_progress.truncate(0)
-            }
-            '\"' => {
-                in_quote = !in_quote;
             }
-            _  => {
-                in_progress.push(*b);
+        }
+        parsed.push(Path::new(in_progress));
+        parsed
+    }
+
+    _split_paths(unparsed)
+}
+
+/// Joins a collection of `Path`s appropriately for the `PATH`
+/// environment variable.
+///
+/// Returns a `Vec<u8>` on success, since `Path`s are not utf-8
+/// encoded on all platforms.
+///
+/// Returns an `Err` (containing an error message) if one of the input
+/// `Path`s contains an invalid character for constructing the `PATH`
+/// variable (a double quote on Windows or a colon on Unix).
+///
+/// # Example
+///
+/// ```rust
+/// use std::os;
+/// use std::path::Path;
+///
+/// let key = "PATH";
+/// let mut paths = os::getenv_as_bytes(key).map_or(Vec::new(), os::split_paths);
+/// paths.push(Path::new("/home/xyz/bin"));
+/// os::setenv(key, os::join_paths(paths.as_slice()).unwrap());
+/// ```
+pub fn join_paths<T: BytesContainer>(paths: &[T]) -> Result<Vec<u8>, &'static str> {
+    #[cfg(windows)]
+    fn _join_paths<T: BytesContainer>(paths: &[T]) -> Result<Vec<u8>, &'static str> {
+        let mut joined = Vec::new();
+        let sep = b';';
+
+        for (i, path) in paths.iter().map(|p| p.container_as_bytes()).enumerate() {
+            if i > 0 { joined.push(sep) }
+            if path.contains(&b'"') {
+                return Err("path segment contains `\"`");
+            } else if path.contains(&sep) {
+                joined.push(b'"');
+                joined.push_all(path);
+                joined.push(b'"');
+            } else {
+                joined.push_all(path);
             }
         }
+
+        Ok(joined)
     }
 
-    if in_progress.len() > 0 {
-        parsed.push(Path::new(in_progress));
+    #[cfg(unix)]
+    fn _join_paths<T: BytesContainer>(paths: &[T]) -> Result<Vec<u8>, &'static str> {
+        let mut joined = Vec::new();
+        let sep = b':';
+
+        for (i, path) in paths.iter().map(|p| p.container_as_bytes()).enumerate() {
+            if i > 0 { joined.push(sep) }
+            if path.contains(&sep) { return Err("path segment contains separator `:`") }
+            joined.push_all(path);
+        }
+
+        Ok(joined)
     }
 
-    parsed
+    _join_paths(paths)
 }
 
 /// A low-level OS in-memory pipe.
@@ -600,6 +651,7 @@ pub fn dll_filename(base: &str) -> String {
 pub fn self_exe_name() -> Option<Path> {
 
     #[cfg(target_os = "freebsd")]
+    #[cfg(target_os = "dragonfly")]
     fn load_self() -> Option<Vec<u8>> {
         unsafe {
             use libc::funcs::bsd44::*;
@@ -654,8 +706,6 @@ fn load_self() -> Option<Vec<u8>> {
 
     #[cfg(windows)]
     fn load_self() -> Option<Vec<u8>> {
-        use str::OwnedStr;
-
         unsafe {
             use os::win32::fill_utf16_buf_and_decode;
             fill_utf16_buf_and_decode(|buf, sz| {
@@ -782,13 +832,24 @@ fn lookup() -> Path {
     }
 }
 
-/**
- * Convert a relative path to an absolute path
- *
- * If the given path is relative, return it prepended with the current working
- * directory. If the given path is already an absolute path, return it
- * as is.
- */
+///
+/// Convert a relative path to an absolute path
+///
+/// If the given path is relative, return it prepended with the current working
+/// directory. If the given path is already an absolute path, return it
+/// as is.
+///
+/// # Example
+/// ```rust
+/// use std::os;
+/// use std::path::Path;
+///
+/// // Assume we're in a path like /home/someuser
+/// let rel_path = Path::new("..");
+/// let abs_path = os::make_absolute(&rel_path);
+/// println!("The absolute path is {}", abs_path.display());
+/// // Prints "The absolute path is /home"
+/// ```
 // NB: this is here rather than in path because it is a form of environment
 // querying; what it does depends on the process working directory, not just
 // the input paths.
@@ -804,6 +865,16 @@ pub fn make_absolute(p: &Path) -> Path {
 
 /// Changes the current working directory to the specified path, returning
 /// whether the change was completed successfully or not.
+///
+/// # Example
+/// ```rust
+/// use std::os;
+/// use std::path::Path;
+///
+/// let root = Path::new("/");
+/// assert!(os::change_dir(&root));
+/// println!("Succesfully changed working directory to {}!", root.display());
+/// ```
 pub fn change_dir(p: &Path) -> bool {
     return chdir(p);
 
@@ -843,6 +914,16 @@ fn errno_location() -> *const c_int {
         }
     }
 
+    #[cfg(target_os = "dragonfly")]
+    fn errno_location() -> *const c_int {
+        extern {
+            fn __dfly_error() -> *const c_int;
+        }
+        unsafe {
+            __dfly_error()
+        }
+    }
+
     #[cfg(target_os = "linux")]
     #[cfg(target_os = "android")]
     fn errno_location() -> *const c_int {
@@ -875,6 +956,13 @@ pub fn errno() -> uint {
 }
 
 /// Return the string corresponding to an `errno()` value of `errnum`.
+/// # Example
+/// ```rust
+/// use std::os;
+///
+/// // Same as println!("{}", last_os_error());
+/// println!("{}", os::error_string(os::errno() as uint));
+/// ```
 pub fn error_string(errnum: uint) -> String {
     return strerror(errnum);
 
@@ -884,6 +972,7 @@ fn strerror(errnum: uint) -> String {
         #[cfg(target_os = "ios")]
         #[cfg(target_os = "android")]
         #[cfg(target_os = "freebsd")]
+        #[cfg(target_os = "dragonfly")]
         fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: libc::size_t)
                       -> c_int {
             extern {
@@ -920,7 +1009,7 @@ fn __xpg_strerror_r(errnum: c_int,
                 fail!("strerror_r failure");
             }
 
-            str::raw::from_c_str(p as *const c_char).into_string()
+            ::string::raw::from_buf(p as *const u8)
         }
     }
 
@@ -967,7 +1056,7 @@ fn FormatMessageW(flags: DWORD,
                 return format!("OS Error {} (FormatMessageW() returned error {})", errnum, fm_err);
             }
 
-            let msg = str::from_utf16(str::truncate_utf16_at_nul(buf));
+            let msg = String::from_utf16(::str::truncate_utf16_at_nul(buf));
             match msg {
                 Some(msg) => format!("OS Error {}: {}", errnum, msg),
                 None => format!("OS Error {} (FormatMessageW() returned invalid UTF-16)", errnum),
@@ -1090,6 +1179,7 @@ fn real_args_as_bytes() -> Vec<Vec<u8>> {
 #[cfg(target_os = "linux")]
 #[cfg(target_os = "android")]
 #[cfg(target_os = "freebsd")]
+#[cfg(target_os = "dragonfly")]
 fn real_args_as_bytes() -> Vec<Vec<u8>> {
     use rt;
 
@@ -1103,7 +1193,7 @@ fn real_args_as_bytes() -> Vec<Vec<u8>> {
 fn real_args() -> Vec<String> {
     real_args_as_bytes().move_iter()
                         .map(|v| {
-                            str::from_utf8_lossy(v.as_slice()).into_string()
+                            String::from_utf8_lossy(v.as_slice()).into_string()
                         }).collect()
 }
 
@@ -1124,7 +1214,7 @@ fn real_args() -> Vec<String> {
 
         // Push it onto the list.
         let opt_s = slice::raw::buf_as_slice(ptr as *const _, len, |buf| {
-            str::from_utf16(str::truncate_utf16_at_nul(buf))
+            String::from_utf16(::str::truncate_utf16_at_nul(buf))
         });
         opt_s.expect("CommandLineToArgvW returned invalid UTF-16")
     });
@@ -1161,7 +1251,17 @@ fn CommandLineToArgvW(lpCmdLine: LPCWSTR,
 /// via the command line).
 ///
 /// The arguments are interpreted as utf-8, with invalid bytes replaced with \uFFFD.
-/// See `str::from_utf8_lossy` for details.
+/// See `String::from_utf8_lossy` for details.
+/// # Example
+///
+/// ```rust
+/// use std::os;
+///
+/// // Prints each argument on a separate line
+/// for argument in os::args().iter() {
+///     println!("{}", argument);
+/// }
+/// ```
 pub fn args() -> Vec<String> {
     real_args()
 }
@@ -1222,12 +1322,9 @@ pub fn page_size() -> uint {
 /// The memory map is released (unmapped) when the destructor is run, so don't
 /// let it leave scope by accident if you want it to stick around.
 pub struct MemoryMap {
-    /// Pointer to the memory created or modified by this map.
-    pub data: *mut u8,
-    /// Number of bytes this map applies to
-    pub len: uint,
-    /// Type of mapping
-    pub kind: MemoryMapKind,
+    data: *mut u8,
+    len: uint,
+    kind: MemoryMapKind,
 }
 
 /// Type of memory map
@@ -1562,6 +1659,15 @@ fn drop(&mut self) {
     }
 }
 
+impl MemoryMap {
+    /// Returns the pointer to the memory created or modified by this map.
+    pub fn data(&self) -> *mut u8 { self.data }
+    /// Returns the number of bytes this map applies to.
+    pub fn len(&self) -> uint { self.len }
+    /// Returns the type of mapping this represents.
+    pub fn kind(&self) -> MemoryMapKind { self.kind }
+}
+
 #[cfg(target_os = "linux")]
 pub mod consts {
     pub use os::arch_consts::ARCH;
@@ -1674,6 +1780,37 @@ pub mod consts {
     pub static EXE_EXTENSION: &'static str = "";
 }
 
+#[cfg(target_os = "dragonfly")]
+pub mod consts {
+    pub use os::arch_consts::ARCH;
+
+    pub static FAMILY: &'static str = "unix";
+
+    /// A string describing the specific operating system in use: in this
+    /// case, `dragonfly`.
+    pub static SYSNAME: &'static str = "dragonfly";
+
+    /// Specifies the filename prefix used for shared libraries on this
+    /// platform: in this case, `lib`.
+    pub static DLL_PREFIX: &'static str = "lib";
+
+    /// Specifies the filename suffix used for shared libraries on this
+    /// platform: in this case, `.so`.
+    pub static DLL_SUFFIX: &'static str = ".so";
+
+    /// Specifies the file extension used for shared libraries on this
+    /// platform that goes after the dot: in this case, `so`.
+    pub static DLL_EXTENSION: &'static str = "so";
+
+    /// Specifies the filename suffix used for executable binaries on this
+    /// platform: in this case, the empty string.
+    pub static EXE_SUFFIX: &'static str = "";
+
+    /// Specifies the file extension, if any, used for executable binaries
+    /// on this platform: in this case, the empty string.
+    pub static EXE_EXTENSION: &'static str = "";
+}
+
 #[cfg(target_os = "android")]
 pub mod consts {
     pub use os::arch_consts::ARCH;
@@ -1767,7 +1904,7 @@ mod tests {
     use c_str::ToCStr;
     use option;
     use os::{env, getcwd, getenv, make_absolute};
-    use os::{split_paths, setenv, unsetenv};
+    use os::{split_paths, join_paths, setenv, unsetenv};
     use os;
     use rand::Rng;
     use rand;
@@ -2032,11 +2169,11 @@ fn check_parse(unparsed: &str, parsed: &[&str]) -> bool {
                 parsed.iter().map(|s| Path::new(*s)).collect()
         }
 
-        assert!(check_parse("", []));
-        assert!(check_parse(r#""""#, []));
-        assert!(check_parse(";;", []));
+        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:\;", [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\"]));
@@ -2052,12 +2189,44 @@ fn check_parse(unparsed: &str, parsed: &[&str]) -> bool {
                 parsed.iter().map(|s| Path::new(*s)).collect()
         }
 
-        assert!(check_parse("", []));
-        assert!(check_parse("::", []));
+        assert!(check_parse("", [""]));
+        assert!(check_parse("::", ["", "", ""]));
         assert!(check_parse("/", ["/"]));
-        assert!(check_parse("/:", ["/"]));
+        assert!(check_parse("/:", ["/", ""]));
         assert!(check_parse("/:/usr/local", ["/", "/usr/local"]));
     }
 
+    #[test]
+    #[cfg(unix)]
+    fn join_paths_unix() {
+        fn test_eq(input: &[&str], output: &str) -> bool {
+            join_paths(input).unwrap().as_slice() == output.as_bytes()
+        }
+
+        assert!(test_eq([], ""));
+        assert!(test_eq(["/bin", "/usr/bin", "/usr/local/bin"],
+                        "/bin:/usr/bin:/usr/local/bin"));
+        assert!(test_eq(["", "/bin", "", "", "/usr/bin", ""],
+                        ":/bin:::/usr/bin:"));
+        assert!(join_paths(["/te:st"]).is_err());
+    }
+
+    #[test]
+    #[cfg(windows)]
+    fn join_paths_windows() {
+        fn test_eq(input: &[&str], output: &str) -> bool {
+            join_paths(input).unwrap().as_slice() == output.as_bytes()
+        }
+
+        assert!(test_eq([], ""));
+        assert!(test_eq([r"c:\windows", r"c:\"],
+                        r"c:\windows;c:\"));
+        assert!(test_eq(["", r"c:\windows", "", "", r"c:\", ""],
+                        r";c:\windows;;;c:\;"));
+        assert!(test_eq([r"c:\te;st", r"c:\"],
+                        r#""c:\te;st";c:\"#));
+        assert!(join_paths([r#"c:\te"st"#]).is_err());
+    }
+
     // More recursive_mkdir tests are in extra::tempfile
 }