From 70ed3a48dfa301c5bb56de3e0a7c25214539b83c Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 27 Jan 2015 12:20:58 -0800 Subject: [PATCH] std: Add a new `env` module This is an implementation of [RFC 578][rfc] which adds a new `std::env` module to replace most of the functionality in the current `std::os` module. More details can be found in the RFC itself, but as a summary the following methods have all been deprecated: [rfc]: https://github.com/rust-lang/rfcs/pull/578 * `os::args_as_bytes` => `env::args` * `os::args` => `env::args` * `os::consts` => `env::consts` * `os::dll_filename` => no replacement, use `env::consts` directly * `os::page_size` => `env::page_size` * `os::make_absolute` => use `env::current_dir` + `join` instead * `os::getcwd` => `env::current_dir` * `os::change_dir` => `env::set_current_dir` * `os::homedir` => `env::home_dir` * `os::tmpdir` => `env::temp_dir` * `os::join_paths` => `env::join_paths` * `os::split_paths` => `env::split_paths` * `os::self_exe_name` => `env::current_exe` * `os::self_exe_path` => use `env::current_exe` + `pop` * `os::set_exit_status` => `env::set_exit_status` * `os::get_exit_status` => `env::get_exit_status` * `os::env` => `env::vars` * `os::env_as_bytes` => `env::vars` * `os::getenv` => `env::var` or `env::var_string` * `os::getenv_as_bytes` => `env::var` * `os::setenv` => `env::set_var` * `os::unsetenv` => `env::remove_var` Many function signatures have also been tweaked for various purposes, but the main changes were: * `Vec`-returning APIs now all return iterators instead * All APIs are now centered around `OsString` instead of `Vec` or `String`. There is currently on convenience API, `env::var_string`, which can be used to get the value of an environment variable as a unicode `String`. All old APIs are `#[deprecated]` in-place and will remain for some time to allow for migrations. The semantics of the APIs have been tweaked slightly with regard to dealing with invalid unicode (panic instead of replacement). The new `std::env` module is all contained within the `env` feature, so crates must add the following to access the new APIs: #![feature(env)] [breaking-change] --- src/compiletest/compiletest.rs | 11 +- src/compiletest/runtest.rs | 6 +- src/etc/make-win-dist.py | 1 + src/liblog/lib.rs | 10 +- src/librustc/lib.rs | 2 +- src/librustc/metadata/filesearch.rs | 10 +- .../middle/infer/region_inference/graphviz.rs | 6 +- src/librustc/plugin/load.rs | 4 +- src/librustc/session/mod.rs | 6 +- src/librustc_back/archive.rs | 6 +- src/librustc_back/fs.rs | 4 +- src/librustc_back/lib.rs | 1 + src/librustc_back/rpath.rs | 11 +- src/librustc_back/target/mod.rs | 9 +- src/librustc_driver/driver.rs | 23 +- src/librustc_driver/lib.rs | 13 +- src/librustc_trans/lib.rs | 2 +- src/librustc_trans/save/mod.rs | 8 +- src/librustdoc/lib.rs | 7 +- src/librustdoc/test.rs | 6 +- src/libstd/dynamic_lib.rs | 20 +- src/libstd/env.rs | 833 ++++++++++++++++++ src/libstd/ffi/mod.rs | 7 +- src/libstd/ffi/os_str.rs | 86 +- src/libstd/lib.rs | 1 + src/libstd/old_io/process.rs | 1 + src/libstd/old_io/tempfile.rs | 12 +- src/libstd/old_io/test.rs | 6 +- src/libstd/os.rs | 247 +++--- src/libstd/rt/backtrace.rs | 4 +- src/libstd/rt/mod.rs | 4 +- src/libstd/rt/util.rs | 9 +- src/libstd/sys/common/mutex.rs | 7 - src/libstd/sys/common/rwlock.rs | 7 - src/libstd/sys/unix/backtrace.rs | 4 +- src/libstd/sys/unix/c.rs | 53 ++ src/libstd/sys/unix/mod.rs | 4 +- src/libstd/sys/unix/os.rs | 440 ++++++--- src/libstd/sys/unix/process.rs | 14 +- src/libstd/sys/windows/backtrace.rs | 34 +- src/libstd/sys/windows/c.rs | 190 +++- src/libstd/sys/windows/fs.rs | 20 +- src/libstd/sys/windows/handle.rs | 31 + src/libstd/sys/windows/mod.rs | 111 ++- src/libstd/sys/windows/mutex.rs | 2 - src/libstd/sys/windows/os.rs | 453 +++++----- src/libstd/sys/windows/pipe.rs | 18 +- src/libstd/sys/windows/process.rs | 24 +- src/libstd/sys/windows/rwlock.rs | 3 - src/libstd/sys/windows/stack_overflow.rs | 10 +- src/libstd/sys/windows/sync.rs | 2 +- src/libstd/sys/windows/tcp.rs | 9 +- src/libstd/sys/windows/thread.rs | 2 - src/libstd/sys/windows/thread_local.rs | 1 + src/libstd/sys/windows/timer.rs | 2 - src/libstd/sys/windows/tty.rs | 5 +- src/libsyntax/ext/env.rs | 14 +- src/libsyntax/lib.rs | 2 +- src/libterm/lib.rs | 3 +- src/libterm/terminfo/mod.rs | 10 +- src/libterm/terminfo/searcher.rs | 17 +- src/libtest/lib.rs | 12 +- src/test/bench/shootout-mandelbrot.rs | 7 +- src/test/compile-fail/liveness-unused.rs | 3 +- src/test/run-pass/env-home-dir.rs | 45 + src/test/run-pass/env-vars.rs | 22 + 66 files changed, 2175 insertions(+), 782 deletions(-) create mode 100644 src/libstd/env.rs create mode 100644 src/libstd/sys/windows/handle.rs create mode 100644 src/test/run-pass/env-home-dir.rs create mode 100644 src/test/run-pass/env-vars.rs diff --git a/src/compiletest/compiletest.rs b/src/compiletest/compiletest.rs index b73623223fd4e..7c0ee7be8a444 100644 --- a/src/compiletest/compiletest.rs +++ b/src/compiletest/compiletest.rs @@ -22,6 +22,7 @@ #![feature(std_misc)] #![feature(test)] #![feature(unicode)] +#![feature(env)] #![deny(warnings)] @@ -31,7 +32,7 @@ extern crate getopts; #[macro_use] extern crate log; -use std::os; +use std::env; use std::old_io; use std::old_io::fs; use std::thunk::Thunk; @@ -48,7 +49,7 @@ pub mod common; pub mod errors; pub fn main() { - let args = os::args(); + let args = env::args().map(|s| s.into_string().unwrap()).collect();; let config = parse_config(args); if config.valgrind_path.is_none() && config.force_valgrind { @@ -224,7 +225,7 @@ pub fn run_tests(config: &Config) { //arm-linux-androideabi debug-info test uses remote debugger //so, we test 1 task at once. // also trying to isolate problems with adb_run_wrapper.sh ilooping - os::setenv("RUST_TEST_TASKS","1"); + env::set_var("RUST_TEST_TASKS","1"); } match config.mode { @@ -232,7 +233,7 @@ pub fn run_tests(config: &Config) { // Some older versions of LLDB seem to have problems with multiple // instances running in parallel, so only run one test task at a // time. - os::setenv("RUST_TEST_TASKS", "1"); + env::set_var("RUST_TEST_TASKS", "1"); } _ => { /* proceed */ } } @@ -245,7 +246,7 @@ pub fn run_tests(config: &Config) { old_io::test::raise_fd_limit(); // Prevent issue #21352 UAC blocking .exe containing 'patch' etc. on Windows // If #11207 is resolved (adding manifest to .exe) this becomes unnecessary - os::setenv("__COMPAT_LAYER", "RunAsInvoker"); + env::set_var("__COMPAT_LAYER", "RunAsInvoker"); let res = test::run_tests_console(&opts, tests.into_iter().collect()); match res { Ok(true) => {} diff --git a/src/compiletest/runtest.rs b/src/compiletest/runtest.rs index 18cb3d1d5b006..5b0ea3b4854ed 100644 --- a/src/compiletest/runtest.rs +++ b/src/compiletest/runtest.rs @@ -31,7 +31,7 @@ use std::old_io::process::ProcessExit; use std::old_io::process; use std::old_io::timer; use std::old_io; -use std::os; +use std::env; use std::iter::repeat; use std::str; use std::string::String; @@ -1298,9 +1298,9 @@ fn make_lib_name(config: &Config, auxfile: &Path, testfile: &Path) -> Path { fn make_exe_name(config: &Config, testfile: &Path) -> Path { let mut f = output_base_name(config, testfile); - if !os::consts::EXE_SUFFIX.is_empty() { + if !env::consts::EXE_SUFFIX.is_empty() { let mut fname = f.filename().unwrap().to_vec(); - fname.extend(os::consts::EXE_SUFFIX.bytes()); + fname.extend(env::consts::EXE_SUFFIX.bytes()); f.set_filename(fname); } f diff --git a/src/etc/make-win-dist.py b/src/etc/make-win-dist.py index 13d0513502488..defda27acc9ec 100644 --- a/src/etc/make-win-dist.py +++ b/src/etc/make-win-dist.py @@ -87,6 +87,7 @@ def make_win_dist(rust_root, gcc_root, target_triple): "libsetupapi.a", "libshell32.a", "libuser32.a", + "libuserenv.a", "libuuid.a", "libwinhttp.a", "libwinmm.a", diff --git a/src/liblog/lib.rs b/src/liblog/lib.rs index 81d8c60f8939b..34216518c21d2 100644 --- a/src/liblog/lib.rs +++ b/src/liblog/lib.rs @@ -173,15 +173,15 @@ #![feature(int_uint)] #![feature(core)] #![feature(io)] -#![feature(os)] #![feature(std_misc)] +#![feature(env)] use std::cell::RefCell; use std::fmt; use std::old_io::LineBufferedWriter; use std::old_io; use std::mem; -use std::os; +use std::env; use std::ptr; use std::rt; use std::slice; @@ -397,9 +397,9 @@ fn enabled(level: u32, /// This is not threadsafe at all, so initialization is performed through a /// `Once` primitive (and this function is called from that primitive). fn init() { - let (mut directives, filter) = match os::getenv("RUST_LOG") { - Some(spec) => directive::parse_logging_spec(&spec[]), - None => (Vec::new(), None), + let (mut directives, filter) = match env::var_string("RUST_LOG") { + Ok(spec) => directive::parse_logging_spec(&spec[]), + Err(..) => (Vec::new(), None), }; // Sort the provided directives by length of their name, this allows a diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 6ae861fcb0424..2f31a2c083f7f 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -32,7 +32,7 @@ #![feature(int_uint)] #![feature(io)] #![feature(libc)] -#![feature(os)] +#![feature(env)] #![feature(path)] #![feature(quote)] #![feature(rustc_diagnostic_macros)] diff --git a/src/librustc/metadata/filesearch.rs b/src/librustc/metadata/filesearch.rs index 26046cfb43d8a..45dd4b4dbacbd 100644 --- a/src/librustc/metadata/filesearch.rs +++ b/src/librustc/metadata/filesearch.rs @@ -13,9 +13,9 @@ pub use self::FileMatch::*; use std::collections::HashSet; +use std::env; use std::old_io::fs::PathExtensions; use std::old_io::fs; -use std::os; use util::fs as myfs; use session::search_paths::{SearchPaths, PathKind}; @@ -194,7 +194,7 @@ pub fn get_or_default_sysroot() -> Path { }) } - match canonicalize(os::self_exe_name()) { + match canonicalize(env::current_exe().ok()) { Some(mut p) => { p.pop(); p.pop(); p } None => panic!("can't determine value for sysroot") } @@ -207,7 +207,7 @@ static PATH_ENTRY_SEPARATOR: &'static str = ":"; /// Returns RUST_PATH as a string, without default paths added pub fn get_rust_path() -> Option { - os::getenv("RUST_PATH").map(|x| x.to_string()) + env::var_string("RUST_PATH").ok() } /// Returns the value of RUST_PATH, as a list @@ -224,7 +224,7 @@ pub fn rust_path() -> Vec { } None => Vec::new() }; - let mut cwd = os::getcwd().unwrap(); + let mut cwd = env::current_dir().unwrap(); // now add in default entries let cwd_dot_rust = cwd.join(".rust"); if !env_rust_path.contains(&cwd_dot_rust) { @@ -243,7 +243,7 @@ pub fn rust_path() -> Vec { } cwd.pop(); } - let h = os::homedir(); + let h = env::home_dir(); for h in h.iter() { let p = h.join(".rust"); if !env_rust_path.contains(&p) && p.exists() { diff --git a/src/librustc/middle/infer/region_inference/graphviz.rs b/src/librustc/middle/infer/region_inference/graphviz.rs index 460687629e7b5..87ea5436dab6b 100644 --- a/src/librustc/middle/infer/region_inference/graphviz.rs +++ b/src/librustc/middle/infer/region_inference/graphviz.rs @@ -27,7 +27,7 @@ use util::ppaux::Repr; use std::collections::hash_map::Entry::Vacant; use std::old_io::{self, File}; -use std::os; +use std::env; use std::sync::atomic::{AtomicBool, Ordering, ATOMIC_BOOL_INIT}; use syntax::ast; @@ -59,13 +59,13 @@ pub fn maybe_print_constraints_for<'a, 'tcx>(region_vars: &RegionVarBindings<'a, } let requested_node : Option = - os::getenv("RUST_REGION_GRAPH_NODE").and_then(|s| s.parse().ok()); + env::var_string("RUST_REGION_GRAPH_NODE").ok().and_then(|s| s.parse().ok()); if requested_node.is_some() && requested_node != Some(subject_node) { return; } - let requested_output = os::getenv("RUST_REGION_GRAPH"); + let requested_output = env::var_string("RUST_REGION_GRAPH").ok(); debug!("requested_output: {:?} requested_node: {:?}", requested_output, requested_node); diff --git a/src/librustc/plugin/load.rs b/src/librustc/plugin/load.rs index c420d1f15b43b..c04bf755b1f6b 100644 --- a/src/librustc/plugin/load.rs +++ b/src/librustc/plugin/load.rs @@ -15,7 +15,7 @@ use metadata::creader::{CrateOrString, CrateReader}; use plugin::registry::Registry; use std::mem; -use std::os; +use std::env; use std::dynamic_lib::DynamicLibrary; use std::collections::HashSet; use syntax::ast; @@ -233,7 +233,7 @@ impl<'a> PluginLoader<'a> { path: Path, symbol: String) -> PluginRegistrarFun { // Make sure the path contains a / or the linker will search for it. - let path = os::make_absolute(&path).unwrap(); + let path = env::current_dir().unwrap().join(&path); let lib = match DynamicLibrary::open(Some(&path)) { Ok(lib) => lib, diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index e62f3145e5a2e..a983f995d3f64 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -27,7 +27,7 @@ use syntax::{ast, codemap}; use rustc_back::target::Target; -use std::os; +use std::env; use std::cell::{Cell, RefCell}; pub mod config; @@ -347,7 +347,7 @@ pub fn build_session_(sopts: config::Options, if path.is_absolute() { path.clone() } else { - os::getcwd().unwrap().join(&path) + env::current_dir().unwrap().join(&path) } ); @@ -370,7 +370,7 @@ pub fn build_session_(sopts: config::Options, plugin_registrar_fn: Cell::new(None), default_sysroot: default_sysroot, local_crate_source_file: local_crate_source_file, - working_dir: os::getcwd().unwrap(), + working_dir: env::current_dir().unwrap(), lint_store: RefCell::new(lint::LintStore::new()), lints: RefCell::new(NodeMap()), crate_types: RefCell::new(Vec::new()), diff --git a/src/librustc_back/archive.rs b/src/librustc_back/archive.rs index ee8bc71668b35..f5483e666cfd4 100644 --- a/src/librustc_back/archive.rs +++ b/src/librustc_back/archive.rs @@ -14,7 +14,7 @@ use std::old_io::fs::PathExtensions; use std::old_io::process::{Command, ProcessOutput}; use std::old_io::{fs, TempDir}; use std::old_io; -use std::os; +use std::env; use std::str; use syntax::diagnostic::Handler as ErrorHandler; @@ -224,7 +224,7 @@ impl<'a> ArchiveBuilder<'a> { pub fn build(self) -> Archive<'a> { // Get an absolute path to the destination, so `ar` will work even // though we run it from `self.work_dir`. - let abs_dst = os::getcwd().unwrap().join(&self.archive.dst); + let abs_dst = env::current_dir().unwrap().join(&self.archive.dst); assert!(!abs_dst.is_relative()); let mut args = vec![&abs_dst]; let mut total_len = abs_dst.as_vec().len(); @@ -283,7 +283,7 @@ impl<'a> ArchiveBuilder<'a> { // First, extract the contents of the archive to a temporary directory. // We don't unpack directly into `self.work_dir` due to the possibility // of filename collisions. - let archive = os::make_absolute(archive).unwrap(); + let archive = env::current_dir().unwrap().join(archive); run_ar(self.archive.handler, &self.archive.maybe_ar_prog, "x", Some(loc.path()), &[&archive]); diff --git a/src/librustc_back/fs.rs b/src/librustc_back/fs.rs index 24f81b024789c..acf49d1ca46e3 100644 --- a/src/librustc_back/fs.rs +++ b/src/librustc_back/fs.rs @@ -10,13 +10,13 @@ use std::old_io; use std::old_io::fs; -use std::os; +use std::env; /// Returns an absolute path in the filesystem that `path` points to. The /// returned path does not contain any symlinks in its hierarchy. pub fn realpath(original: &Path) -> old_io::IoResult { static MAX_LINKS_FOLLOWED: uint = 256; - let original = os::make_absolute(original).unwrap(); + let original = try!(env::current_dir()).join(original); // Right now lstat on windows doesn't work quite well if cfg!(windows) { diff --git a/src/librustc_back/lib.rs b/src/librustc_back/lib.rs index 2c6b5797f572b..54b3e8f208125 100644 --- a/src/librustc_back/lib.rs +++ b/src/librustc_back/lib.rs @@ -40,6 +40,7 @@ #![feature(path)] #![feature(rustc_private)] #![feature(staged_api)] +#![feature(env)] extern crate syntax; extern crate serialize; diff --git a/src/librustc_back/rpath.rs b/src/librustc_back/rpath.rs index 47b909df5e8da..b6a34b4beba71 100644 --- a/src/librustc_back/rpath.rs +++ b/src/librustc_back/rpath.rs @@ -10,7 +10,7 @@ use std::collections::HashSet; -use std::os; +use std::env; use std::old_io::IoError; use syntax::ast; @@ -105,8 +105,6 @@ fn get_rpath_relative_to_output(config: &mut RPathConfig, lib: &Path F: FnOnce() -> Path, G: FnMut(&Path) -> Result, { - use std::os; - // Mac doesn't appear to support $ORIGIN let prefix = if config.is_like_osx { "@loader_path" @@ -114,9 +112,10 @@ fn get_rpath_relative_to_output(config: &mut RPathConfig, lib: &Path "$ORIGIN" }; - let mut lib = (config.realpath)(&os::make_absolute(lib).unwrap()).unwrap(); + let cwd = env::current_dir().unwrap(); + let mut lib = (config.realpath)(&cwd.join(lib)).unwrap(); lib.pop(); - let mut output = (config.realpath)(&os::make_absolute(&config.out_filename).unwrap()).unwrap(); + let mut output = (config.realpath)(&cwd.join(&config.out_filename)).unwrap(); output.pop(); let relative = lib.path_relative_from(&output); let relative = relative.expect("could not create rpath relative to output"); @@ -131,7 +130,7 @@ fn get_install_prefix_rpath(config: RPathConfig) -> String where G: FnMut(&Path) -> Result, { let path = (config.get_install_prefix_lib_path)(); - let path = os::make_absolute(&path).unwrap(); + let path = env::current_dir().unwrap().join(&path); // FIXME (#9639): This needs to handle non-utf8 paths path.as_str().expect("non-utf8 component in rpath").to_string() } diff --git a/src/librustc_back/target/mod.rs b/src/librustc_back/target/mod.rs index 16adccfba575e..eb7c23f95e2e1 100644 --- a/src/librustc_back/target/mod.rs +++ b/src/librustc_back/target/mod.rs @@ -301,7 +301,8 @@ impl Target { /// The error string could come from any of the APIs called, including filesystem access and /// JSON decoding. pub fn search(target: &str) -> Result { - use std::os; + use std::env; + use std::ffi::OsString; use std::old_io::File; use std::path::Path; use serialize::json; @@ -379,12 +380,12 @@ impl Target { Path::new(target) }; - let target_path = os::getenv("RUST_TARGET_PATH").unwrap_or(String::new()); + let target_path = env::var("RUST_TARGET_PATH") + .unwrap_or(OsString::from_str("")); - let paths = os::split_paths(&target_path[]); // FIXME 16351: add a sane default search path? - for dir in paths.iter() { + for dir in env::split_paths(&target_path) { let p = dir.join(path.clone()); if p.is_file() { return load_file(&p); diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 9e00844b7eea2..4aa4193d51134 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -30,9 +30,10 @@ use rustc_privacy; use serialize::json; -use std::old_io; +use std::env; +use std::ffi::OsString; use std::old_io::fs; -use std::os; +use std::old_io; use syntax::ast; use syntax::ast_map; use syntax::attr; @@ -460,12 +461,12 @@ pub fn phase_2_configure_and_expand(sess: &Session, // dependent dlls. Note that this uses cfg!(windows) as opposed to // targ_cfg because syntax extensions are always loaded for the host // compiler, not for the target. - let mut _old_path = String::new(); + let mut _old_path = OsString::from_str(""); if cfg!(windows) { - _old_path = os::getenv("PATH").unwrap_or(_old_path); + _old_path = env::var("PATH").unwrap_or(_old_path); let mut new_path = sess.host_filesearch(PathKind::All).get_dylib_search_paths(); - new_path.extend(os::split_paths(&_old_path[]).into_iter()); - os::setenv("PATH", os::join_paths(&new_path[]).unwrap()); + new_path.extend(env::split_paths(&_old_path)); + env::set_var("PATH", &env::join_paths(new_path.iter()).unwrap()); } let cfg = syntax::ext::expand::ExpansionConfig { crate_name: crate_name.to_string(), @@ -478,7 +479,7 @@ pub fn phase_2_configure_and_expand(sess: &Session, syntax_exts, krate); if cfg!(windows) { - os::setenv("PATH", _old_path); + env::set_var("PATH", &_old_path); } ret } @@ -734,10 +735,10 @@ pub fn phase_5_run_llvm_passes(sess: &Session, pub fn phase_6_link_output(sess: &Session, trans: &trans::CrateTranslation, outputs: &OutputFilenames) { - let old_path = os::getenv("PATH").unwrap_or_else(||String::new()); + let old_path = env::var("PATH").unwrap_or(OsString::from_str("")); let mut new_path = sess.host_filesearch(PathKind::All).get_tools_search_paths(); - new_path.extend(os::split_paths(&old_path[]).into_iter()); - os::setenv("PATH", os::join_paths(&new_path[]).unwrap()); + new_path.extend(env::split_paths(&old_path)); + env::set_var("PATH", &env::join_paths(new_path.iter()).unwrap()); time(sess.time_passes(), "linking", (), |_| link::link_binary(sess, @@ -745,7 +746,7 @@ pub fn phase_6_link_output(sess: &Session, outputs, &trans.link.crate_name[])); - os::setenv("PATH", old_path); + env::set_var("PATH", &old_path); } fn escape_dep_filename(filename: &str) -> String { diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index 2eada1ff174f1..6230f0b14a23b 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -26,6 +26,7 @@ #![feature(box_syntax)] #![feature(collections)] #![feature(core)] +#![feature(env)] #![feature(int_uint)] #![feature(io)] #![feature(libc)] @@ -74,7 +75,7 @@ use rustc::util::common::time; use std::cmp::Ordering::Equal; use std::old_io; use std::iter::repeat; -use std::os; +use std::env; use std::sync::mpsc::channel; use std::thread; @@ -252,7 +253,7 @@ pub fn get_unstable_features_setting() -> UnstableFeatures { // subverting the unstable features lints let bootstrap_secret_key = option_env!("CFG_BOOTSTRAP_KEY"); // The matching key to the above, only known by the build system - let bootstrap_provided_key = os::getenv("RUSTC_BOOTSTRAP_KEY"); + let bootstrap_provided_key = env::var_string("RUSTC_BOOTSTRAP_KEY").ok(); match (disable_unstable_features, bootstrap_secret_key, bootstrap_provided_key) { (_, Some(ref s), Some(ref p)) if s == p => UnstableFeatures::Cheat, (true, _, _) => UnstableFeatures::Disallow, @@ -618,7 +619,7 @@ pub fn monitor(f: F) { // FIXME: Hacks on hacks. If the env is trying to override the stack size // then *don't* set it explicitly. - if os::getenv("RUST_MIN_STACK").is_none() { + if env::var("RUST_MIN_STACK").is_none() { cfg = cfg.stack_size(STACK_SIZE); } @@ -682,8 +683,8 @@ pub fn diagnostics_registry() -> diagnostics::registry::Registry { } pub fn main() { - let args = std::os::args(); - let result = run(args); - std::os::set_exit_status(result); + let args = env::args().map(|s| s.into_string().unwrap()); + let result = run(args.collect()); + std::env::set_exit_status(result as i32); } diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index c46c2b7e6ddaf..11ffade6da6c6 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -32,8 +32,8 @@ #![feature(hash)] #![feature(int_uint)] #![feature(io)] +#![feature(env)] #![feature(libc)] -#![feature(os)] #![feature(path)] #![feature(quote)] #![feature(rustc_diagnostic_macros)] diff --git a/src/librustc_trans/save/mod.rs b/src/librustc_trans/save/mod.rs index fbeaae1d1dfa3..f429ab7599fef 100644 --- a/src/librustc_trans/save/mod.rs +++ b/src/librustc_trans/save/mod.rs @@ -34,7 +34,7 @@ use middle::ty::{self, Ty}; use std::cell::Cell; use std::old_io::{self, File, fs}; -use std::os; +use std::env; use syntax::ast_util::{self, PostExpansionMethod}; use syntax::ast::{self, NodeId, DefId}; @@ -1551,9 +1551,9 @@ pub fn process_crate(sess: &Session, info!("Dumping crate {}", cratename); // find a path to dump our data to - let mut root_path = match os::getenv("DXR_RUST_TEMP_FOLDER") { - Some(val) => Path::new(val), - None => match odir { + let mut root_path = match env::var_string("DXR_RUST_TEMP_FOLDER") { + Ok(val) => Path::new(val), + Err(..) => match odir { Some(val) => val.join("dxr"), None => Path::new("dxr-temp"), }, diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 29e52d627cdfe..c9b50bdd3c1d2 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -21,6 +21,7 @@ #![feature(box_syntax)] #![feature(collections)] #![feature(core)] +#![feature(env)] #![feature(hash)] #![feature(int_uint)] #![feature(io)] @@ -50,6 +51,7 @@ extern crate "serialize" as rustc_serialize; // used by deriving use std::cell::RefCell; use std::collections::HashMap; +use std::env; use std::old_io::File; use std::old_io; use std::rc::Rc; @@ -121,9 +123,10 @@ struct Output { pub fn main() { static STACK_SIZE: uint = 32000000; // 32MB let res = std::thread::Builder::new().stack_size(STACK_SIZE).scoped(move || { - main_args(std::os::args().as_slice()) + let s = env::args().map(|s| s.into_string().unwrap()); + main_args(&s.collect::>()) }).join(); - std::os::set_exit_status(res.ok().unwrap()); + env::set_exit_status(res.ok().unwrap() as i32); } pub fn opts() -> Vec { diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index 2f692fe99cda7..29abea009e547 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -13,7 +13,7 @@ use std::sync::mpsc::channel; use std::dynamic_lib::DynamicLibrary; use std::old_io::{Command, TempDir}; use std::old_io; -use std::os; +use std::env; use std::str; use std::thread::Thread; use std::thunk::Thunk; @@ -49,7 +49,7 @@ pub fn run(input: &str, let input = config::Input::File(input_path.clone()); let sessopts = config::Options { - maybe_sysroot: Some(os::self_exe_path().unwrap().dir_path()), + maybe_sysroot: Some(env::current_exe().unwrap().dir_path().dir_path()), search_paths: libs.clone(), crate_types: vec!(config::CrateTypeDylib), externs: externs.clone(), @@ -119,7 +119,7 @@ fn runtest(test: &str, cratename: &str, libs: SearchPaths, let input = config::Input::Str(test.to_string()); let sessopts = config::Options { - maybe_sysroot: Some(os::self_exe_path().unwrap().dir_path()), + maybe_sysroot: Some(env::current_exe().unwrap().dir_path().dir_path()), search_paths: libs, crate_types: vec!(config::CrateTypeExecutable), output_types: vec!(config::OutputTypeExe), diff --git a/src/libstd/dynamic_lib.rs b/src/libstd/dynamic_lib.rs index f35f63143ef82..46567aedbec4e 100644 --- a/src/libstd/dynamic_lib.rs +++ b/src/libstd/dynamic_lib.rs @@ -19,7 +19,7 @@ use prelude::v1::*; use ffi::CString; use mem; -use os; +use env; use str; #[allow(missing_copy_implementations)] @@ -68,8 +68,8 @@ impl DynamicLibrary { let mut search_path = DynamicLibrary::search_path(); search_path.insert(0, path.clone()); let newval = DynamicLibrary::create_path(search_path.as_slice()); - os::setenv(DynamicLibrary::envvar(), - str::from_utf8(newval.as_slice()).unwrap()); + env::set_var(DynamicLibrary::envvar(), + str::from_utf8(newval.as_slice()).unwrap()); } /// From a slice of paths, create a new vector which is suitable to be an @@ -102,18 +102,10 @@ impl DynamicLibrary { /// Returns the current search path for dynamic libraries being used by this /// process pub fn search_path() -> Vec { - let mut ret = Vec::new(); - match os::getenv_as_bytes(DynamicLibrary::envvar()) { - Some(env) => { - for portion in - env.as_slice() - .split(|a| *a == DynamicLibrary::separator()) { - ret.push(Path::new(portion)); - } - } - None => {} + match env::var(DynamicLibrary::envvar()) { + Some(var) => env::split_paths(&var).collect(), + None => Vec::new(), } - return ret; } /// Access the value at the symbol of the dynamic library diff --git a/src/libstd/env.rs b/src/libstd/env.rs new file mode 100644 index 0000000000000..5070f8c547ab0 --- /dev/null +++ b/src/libstd/env.rs @@ -0,0 +1,833 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Inspection and manipulation of the process's environment. +//! +//! This module contains methods to inspect various aspects such as +//! environment varibles, process arguments, the current directory, and various +//! other important directories. + +#![unstable(feature = "env", reason = "recently added via RFC 578")] + +use prelude::v1::*; + +use error::Error; +use ffi::{OsString, AsOsStr}; +use fmt; +use old_io::IoResult; +use sync::atomic::{AtomicIsize, ATOMIC_ISIZE_INIT, Ordering}; +use sync::{StaticMutex, MUTEX_INIT}; +use sys::os as os_imp; + +/// Returns the current working directory as a `Path`. +/// +/// # Errors +/// +/// Returns an `Err` if the current working directory value is invalid. +/// Possible cases: +/// +/// * Current directory does not exist. +/// * There are insufficient permissions to access the current directory. +/// * The internal buffer is not large enough to hold the path. +/// +/// # Example +/// +/// ```rust +/// use std::env; +/// +/// // We assume that we are in a valid directory. +/// let p = env::current_dir().unwrap(); +/// println!("The current directory is {}", p.display()); +/// ``` +pub fn current_dir() -> IoResult { + os_imp::getcwd() +} + +/// Changes the current working directory to the specified path, returning +/// whether the change was completed successfully or not. +/// +/// # Example +/// +/// ```rust +/// use std::env; +/// use std::path::Path; +/// +/// let root = Path::new("/"); +/// assert!(env::set_current_dir(&root).is_ok()); +/// println!("Successfully changed working directory to {}!", root.display()); +/// ``` +pub fn set_current_dir(p: &Path) -> IoResult<()> { + os_imp::chdir(p) +} + +static ENV_LOCK: StaticMutex = MUTEX_INIT; + +/// An iterator over a snapshot of the environment variables of this process. +/// +/// This iterator is created through `std::env::vars()` and yields `(OsString, +/// OsString)` pairs. +pub struct Vars { inner: os_imp::Env } + +/// Returns an iterator of (variable, value) pairs, for all the environment +/// variables of the current process. +/// +/// The returned iterator contains a snapshot of the process's environment +/// variables at the time of this invocation, modifications to environment +/// variables afterwards will not be reflected in the returned iterator. +/// +/// # Example +/// +/// ```rust +/// use std::env; +/// +/// // We will iterate through the references to the element returned by +/// // env::vars(); +/// for (key, value) in env::vars() { +/// println!("{:?}: {:?}", key, value); +/// } +/// ``` +pub fn vars() -> Vars { + let _g = ENV_LOCK.lock(); + Vars { inner: os_imp::env() } +} + +impl Iterator for Vars { + type Item = (OsString, OsString); + fn next(&mut self) -> Option<(OsString, OsString)> { self.inner.next() } + fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } +} + +/// Fetches the environment variable `key` from the current process, returning +/// None if the variable isn't set. +/// +/// # Example +/// +/// ```rust +/// use std::env; +/// +/// let key = "HOME"; +/// match env::var(key) { +/// Some(val) => println!("{}: {:?}", key, val), +/// None => println!("{} is not defined in the environment.", key) +/// } +/// ``` +pub fn var(key: &K) -> Option where K: AsOsStr { + let _g = ENV_LOCK.lock(); + os_imp::getenv(key.as_os_str()) +} + +/// Fetches the environment variable `key` from the current process. +/// +/// The returned result is `Ok(s)` if the environment variable is present and is +/// valid unicode. If the environment variable is not present, or it is not +/// valid unicode, then `Err` will be returned. +/// +/// # Example +/// +/// ```rust +/// use std::env; +/// +/// let key = "HOME"; +/// match env::var_string(key) { +/// Ok(val) => println!("{}: {:?}", key, val), +/// Err(e) => println!("couldn't interpret {}: {}", key, e), +/// } +/// ``` +pub fn var_string(key: &K) -> Result where K: AsOsStr { + match var(key) { + Some(s) => s.into_string().map_err(VarError::NotUnicode), + None => Err(VarError::NotPresent) + } +} + +/// Possible errors from the `env::var` method. +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum VarError { + /// The specified environment variable was not present in the current + /// process's environment. + NotPresent, + + /// The specified environment variable was found, but it did not contain + /// valid unicode data. The found data is returned as a payload of this + /// variant. + NotUnicode(OsString), +} + +impl fmt::Display for VarError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + VarError::NotPresent => write!(f, "environment variable not found"), + VarError::NotUnicode(ref s) => { + write!(f, "environment variable was not valid unicode: {:?}", s) + } + } + } +} + +impl Error for VarError { + fn description(&self) -> &str { + match *self { + VarError::NotPresent => "environment variable not found", + VarError::NotUnicode(..) => "environment variable was not valid unicode", + } + } +} + +/// Sets the environment variable `k` to the value `v` for the currently running +/// process. +/// +/// # Example +/// +/// ```rust +/// use std::env; +/// +/// let key = "KEY"; +/// env::set_var(key, "VALUE"); +/// assert_eq!(env::var_string(key), Ok("VALUE".to_string())); +/// ``` +pub fn set_var(k: &K, v: &V) + where K: AsOsStr, V: AsOsStr +{ + let _g = ENV_LOCK.lock(); + os_imp::setenv(k.as_os_str(), v.as_os_str()) +} + +/// Remove a variable from the environment entirely. +pub fn remove_var(k: &K) where K: AsOsStr { + let _g = ENV_LOCK.lock(); + os_imp::unsetenv(k.as_os_str()) +} + +/// An iterator over `Path` instances for parsing an environment variable +/// according to platform-specific conventions. +/// +/// This structure is returned from `std::env::split_paths`. +pub struct SplitPaths<'a> { inner: os_imp::SplitPaths<'a> } + +/// Parses input according to platform conventions for the `PATH` +/// environment variable. +/// +/// Returns an iterator over the paths contained in `unparsed`. +/// +/// # Example +/// +/// ```rust +/// use std::env; +/// +/// let key = "PATH"; +/// match env::var(key) { +/// Some(paths) => { +/// for path in env::split_paths(&paths) { +/// println!("'{}'", path.display()); +/// } +/// } +/// None => println!("{} is not defined in the environment.", key) +/// } +/// ``` +pub fn split_paths(unparsed: &T) -> SplitPaths { + SplitPaths { inner: os_imp::split_paths(unparsed.as_os_str()) } +} + +impl<'a> Iterator for SplitPaths<'a> { + type Item = Path; + fn next(&mut self) -> Option { self.inner.next() } + fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } +} + +/// Error type returned from `std::env::join_paths` when paths fail to be +/// joined. +#[derive(Debug)] +pub struct JoinPathsError { + inner: os_imp::JoinPathsError +} + +/// Joins a collection of `Path`s appropriately for the `PATH` +/// environment variable. +/// +/// Returns an `OsString` on success. +/// +/// 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::env; +/// +/// if let Some(path) = env::var("PATH") { +/// let mut paths = env::split_paths(&path).collect::>(); +/// paths.push(Path::new("/home/xyz/bin")); +/// let new_path = env::join_paths(paths.iter()).unwrap(); +/// env::set_var("PATH", &new_path); +/// } +/// ``` +pub fn join_paths(paths: I) -> Result + where I: Iterator, T: AsOsStr +{ + os_imp::join_paths(paths).map_err(|e| { + JoinPathsError { inner: e } + }) +} + +impl fmt::Display for JoinPathsError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.inner.fmt(f) + } +} + +impl Error for JoinPathsError { + fn description(&self) -> &str { self.inner.description() } +} + +/// Optionally returns the path to the current user's home directory if known. +/// +/// # Unix +/// +/// Returns the value of the 'HOME' environment variable if it is set +/// and not equal to the empty string. +/// +/// # Windows +/// +/// Returns the value of the 'HOME' environment variable if it is +/// set and not equal to the empty string. Otherwise, returns the value of the +/// 'USERPROFILE' environment variable if it is set and not equal to the empty +/// string. +/// +/// # Example +/// +/// ```rust +/// use std::env; +/// +/// match env::home_dir() { +/// Some(ref p) => println!("{}", p.display()), +/// None => println!("Impossible to get your home dir!") +/// } +/// ``` +pub fn home_dir() -> Option { + os_imp::home_dir() +} + +/// Returns the path to a temporary directory. +/// +/// On Unix, returns the value of the 'TMPDIR' environment variable if it is +/// set, otherwise for non-Android it returns '/tmp'. If Android, since there +/// is no global temporary folder (it is usually allocated per-app), we return +/// '/data/local/tmp'. +/// +/// On Windows, returns the value of, in order, the 'TMP', 'TEMP', +/// 'USERPROFILE' environment variable if any are set and not the empty +/// string. Otherwise, tmpdir returns the path to the Windows directory. +pub fn temp_dir() -> Path { + os_imp::temp_dir() +} + +/// Optionally returns the filesystem path to the current executable which is +/// running but with the executable name. +/// +/// The path returned is not necessarily a "real path" to the executable as +/// there may be intermediate symlinks. +/// +/// # Errors +/// +/// Acquring the path to the current executable is a platform-specific operation +/// that can fail for a good number of reasons. Some errors can include, but not +/// be limited to filesystem operations failing or general syscall failures. +/// +/// # Examples +/// +/// ```rust +/// use std::env; +/// +/// match env::current_exe() { +/// Ok(exe_path) => println!("Path of this executable is: {}", +/// exe_path.display()), +/// Err(e) => println!("failed to get current exe path: {}", e), +/// }; +/// ``` +pub fn current_exe() -> IoResult { + os_imp::current_exe() +} + +static EXIT_STATUS: AtomicIsize = ATOMIC_ISIZE_INIT; + +/// Sets the process exit code +/// +/// Sets the exit code returned by the process if all supervised tasks +/// terminate successfully (without panicking). If the current root task panics +/// and is supervised by the scheduler then any user-specified exit status is +/// ignored and the process exits with the default panic status. +/// +/// Note that this is not synchronized against modifications of other threads. +pub fn set_exit_status(code: i32) { + EXIT_STATUS.store(code as isize, Ordering::SeqCst) +} + +/// Fetches the process's current exit code. This defaults to 0 and can change +/// by calling `set_exit_status`. +pub fn get_exit_status() -> i32 { + EXIT_STATUS.load(Ordering::SeqCst) as i32 +} + +/// An iterator over the arguments of a process, yielding an `OsString` value +/// for each argument. +/// +/// This structure is created through the `std::env::args` method. +pub struct Args { inner: os_imp::Args } + +/// Returns the arguments which this program was started with (normally passed +/// via the command line). +/// +/// The first element is traditionally the path to the executable, but it can be +/// set to arbitrary text, and it may not even exist, so this property should +/// not be relied upon for security purposes. +/// +/// # Example +/// +/// ```rust +/// use std::env; +/// +/// // Prints each argument on a separate line +/// for argument in env::args() { +/// println!("{:?}", argument); +/// } +/// ``` +pub fn args() -> Args { + Args { inner: os_imp::args() } +} + +impl Iterator for Args { + type Item = OsString; + fn next(&mut self) -> Option { self.inner.next() } + fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } +} + +/// Returns the page size of the current architecture in bytes. +pub fn page_size() -> usize { + os_imp::page_size() +} + +/// Constants associated with the current target +#[cfg(target_os = "linux")] +pub mod consts { + pub use super::arch_consts::ARCH; + + pub const FAMILY: &'static str = "unix"; + + /// A string describing the specific operating system in use: in this + /// case, `linux`. + pub const OS: &'static str = "linux"; + + /// Specifies the filename prefix used for shared libraries on this + /// platform: in this case, `lib`. + pub const DLL_PREFIX: &'static str = "lib"; + + /// Specifies the filename suffix used for shared libraries on this + /// platform: in this case, `.so`. + pub const 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 const DLL_EXTENSION: &'static str = "so"; + + /// Specifies the filename suffix used for executable binaries on this + /// platform: in this case, the empty string. + pub const EXE_SUFFIX: &'static str = ""; + + /// Specifies the file extension, if any, used for executable binaries + /// on this platform: in this case, the empty string. + pub const EXE_EXTENSION: &'static str = ""; +} + +/// Constants associated with the current target +#[cfg(target_os = "macos")] +pub mod consts { + pub use super::arch_consts::ARCH; + + pub const FAMILY: &'static str = "unix"; + + /// A string describing the specific operating system in use: in this + /// case, `macos`. + pub const OS: &'static str = "macos"; + + /// Specifies the filename prefix used for shared libraries on this + /// platform: in this case, `lib`. + pub const DLL_PREFIX: &'static str = "lib"; + + /// Specifies the filename suffix used for shared libraries on this + /// platform: in this case, `.dylib`. + pub const DLL_SUFFIX: &'static str = ".dylib"; + + /// Specifies the file extension used for shared libraries on this + /// platform that goes after the dot: in this case, `dylib`. + pub const DLL_EXTENSION: &'static str = "dylib"; + + /// Specifies the filename suffix used for executable binaries on this + /// platform: in this case, the empty string. + pub const EXE_SUFFIX: &'static str = ""; + + /// Specifies the file extension, if any, used for executable binaries + /// on this platform: in this case, the empty string. + pub const EXE_EXTENSION: &'static str = ""; +} + +/// Constants associated with the current target +#[cfg(target_os = "ios")] +pub mod consts { + pub use super::arch_consts::ARCH; + + pub const FAMILY: &'static str = "unix"; + + /// A string describing the specific operating system in use: in this + /// case, `ios`. + pub const OS: &'static str = "ios"; + + /// Specifies the filename suffix used for executable binaries on this + /// platform: in this case, the empty string. + pub const EXE_SUFFIX: &'static str = ""; + + /// Specifies the file extension, if any, used for executable binaries + /// on this platform: in this case, the empty string. + pub const EXE_EXTENSION: &'static str = ""; +} + +/// Constants associated with the current target +#[cfg(target_os = "freebsd")] +pub mod consts { + pub use super::arch_consts::ARCH; + + pub const FAMILY: &'static str = "unix"; + + /// A string describing the specific operating system in use: in this + /// case, `freebsd`. + pub const OS: &'static str = "freebsd"; + + /// Specifies the filename prefix used for shared libraries on this + /// platform: in this case, `lib`. + pub const DLL_PREFIX: &'static str = "lib"; + + /// Specifies the filename suffix used for shared libraries on this + /// platform: in this case, `.so`. + pub const 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 const DLL_EXTENSION: &'static str = "so"; + + /// Specifies the filename suffix used for executable binaries on this + /// platform: in this case, the empty string. + pub const EXE_SUFFIX: &'static str = ""; + + /// Specifies the file extension, if any, used for executable binaries + /// on this platform: in this case, the empty string. + pub const EXE_EXTENSION: &'static str = ""; +} + +/// Constants associated with the current target +#[cfg(target_os = "dragonfly")] +pub mod consts { + pub use super::arch_consts::ARCH; + + pub const FAMILY: &'static str = "unix"; + + /// A string describing the specific operating system in use: in this + /// case, `dragonfly`. + pub const OS: &'static str = "dragonfly"; + + /// Specifies the filename prefix used for shared libraries on this + /// platform: in this case, `lib`. + pub const DLL_PREFIX: &'static str = "lib"; + + /// Specifies the filename suffix used for shared libraries on this + /// platform: in this case, `.so`. + pub const 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 const DLL_EXTENSION: &'static str = "so"; + + /// Specifies the filename suffix used for executable binaries on this + /// platform: in this case, the empty string. + pub const EXE_SUFFIX: &'static str = ""; + + /// Specifies the file extension, if any, used for executable binaries + /// on this platform: in this case, the empty string. + pub const EXE_EXTENSION: &'static str = ""; +} + +/// Constants associated with the current target +#[cfg(target_os = "android")] +pub mod consts { + pub use super::arch_consts::ARCH; + + pub const FAMILY: &'static str = "unix"; + + /// A string describing the specific operating system in use: in this + /// case, `android`. + pub const OS: &'static str = "android"; + + /// Specifies the filename prefix used for shared libraries on this + /// platform: in this case, `lib`. + pub const DLL_PREFIX: &'static str = "lib"; + + /// Specifies the filename suffix used for shared libraries on this + /// platform: in this case, `.so`. + pub const 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 const DLL_EXTENSION: &'static str = "so"; + + /// Specifies the filename suffix used for executable binaries on this + /// platform: in this case, the empty string. + pub const EXE_SUFFIX: &'static str = ""; + + /// Specifies the file extension, if any, used for executable binaries + /// on this platform: in this case, the empty string. + pub const EXE_EXTENSION: &'static str = ""; +} + +/// Constants associated with the current target +#[cfg(target_os = "windows")] +pub mod consts { + pub use super::arch_consts::ARCH; + + pub const FAMILY: &'static str = "windows"; + + /// A string describing the specific operating system in use: in this + /// case, `windows`. + pub const OS: &'static str = "windows"; + + /// Specifies the filename prefix used for shared libraries on this + /// platform: in this case, the empty string. + pub const DLL_PREFIX: &'static str = ""; + + /// Specifies the filename suffix used for shared libraries on this + /// platform: in this case, `.dll`. + pub const DLL_SUFFIX: &'static str = ".dll"; + + /// Specifies the file extension used for shared libraries on this + /// platform that goes after the dot: in this case, `dll`. + pub const DLL_EXTENSION: &'static str = "dll"; + + /// Specifies the filename suffix used for executable binaries on this + /// platform: in this case, `.exe`. + pub const EXE_SUFFIX: &'static str = ".exe"; + + /// Specifies the file extension, if any, used for executable binaries + /// on this platform: in this case, `exe`. + pub const EXE_EXTENSION: &'static str = "exe"; +} + +#[cfg(target_arch = "x86")] +mod arch_consts { + pub const ARCH: &'static str = "x86"; +} + +#[cfg(target_arch = "x86_64")] +mod arch_consts { + pub const ARCH: &'static str = "x86_64"; +} + +#[cfg(target_arch = "arm")] +mod arch_consts { + pub const ARCH: &'static str = "arm"; +} + +#[cfg(target_arch = "aarch64")] +mod arch_consts { + pub const ARCH: &'static str = "aarch64"; +} + +#[cfg(target_arch = "mips")] +mod arch_consts { + pub const ARCH: &'static str = "mips"; +} + +#[cfg(target_arch = "mipsel")] +mod arch_consts { + pub const ARCH: &'static str = "mipsel"; +} + +#[cfg(target_arch = "powerpc")] +mod arch_consts { + pub const ARCH: &'static str = "powerpc"; +} + +#[cfg(test)] +mod tests { + use prelude::v1::*; + use super::*; + use iter::repeat; + use rand::{self, Rng}; + use ffi::{OsString, OsStr}; + + fn make_rand_name() -> OsString { + let mut rng = rand::thread_rng(); + let n = format!("TEST{}", rng.gen_ascii_chars().take(10) + .collect::()); + let n = OsString::from_string(n); + assert!(var(&n).is_none()); + n + } + + fn eq(a: Option, b: Option<&str>) { + assert_eq!(a.as_ref().map(|s| &**s), b.map(OsStr::from_str).map(|s| &*s)); + } + + #[test] + fn test_set_var() { + let n = make_rand_name(); + set_var(&n, "VALUE"); + eq(var(&n), Some("VALUE")); + } + + #[test] + fn test_remove_var() { + let n = make_rand_name(); + set_var(&n, "VALUE"); + remove_var(&n); + eq(var(&n), None); + } + + #[test] + fn test_set_var_overwrite() { + let n = make_rand_name(); + set_var(&n, "1"); + set_var(&n, "2"); + eq(var(&n), Some("2")); + set_var(&n, ""); + eq(var(&n), Some("")); + } + + #[test] + fn test_var_big() { + let mut s = "".to_string(); + let mut i = 0; + while i < 100 { + s.push_str("aaaaaaaaaa"); + i += 1; + } + let n = make_rand_name(); + set_var(&n, s.as_slice()); + eq(var(&n), Some(s.as_slice())); + } + + #[test] + fn test_self_exe_path() { + let path = current_exe(); + assert!(path.is_ok()); + let path = path.unwrap(); + + // Hard to test this function + assert!(path.is_absolute()); + } + + #[test] + fn test_env_set_get_huge() { + let n = make_rand_name(); + let s = repeat("x").take(10000).collect::(); + set_var(&n, &s); + eq(var(&n), Some(s.as_slice())); + remove_var(&n); + eq(var(&n), None); + } + + #[test] + fn test_env_set_var() { + let n = make_rand_name(); + + let mut e = vars(); + set_var(&n, "VALUE"); + assert!(!e.any(|(k, v)| { + &*k == &*n && &*v == "VALUE" + })); + + assert!(vars().any(|(k, v)| { + &*k == &*n && &*v == "VALUE" + })); + } + + #[test] + fn test() { + assert!((!Path::new("test-path").is_absolute())); + + current_dir().unwrap(); + } + + #[test] + #[cfg(windows)] + fn split_paths_windows() { + fn check_parse(unparsed: &str, parsed: &[&str]) -> bool { + split_paths(unparsed).collect::>() == + parsed.iter().map(|s| Path::new(*s)).collect::>() + } + + assert!(check_parse("", &mut [""])); + assert!(check_parse(r#""""#, &mut [""])); + assert!(check_parse(";;", &mut ["", "", ""])); + assert!(check_parse(r"c:\", &mut [r"c:\"])); + assert!(check_parse(r"c:\;", &mut [r"c:\", ""])); + assert!(check_parse(r"c:\;c:\Program Files\", + &mut [r"c:\", r"c:\Program Files\"])); + assert!(check_parse(r#"c:\;c:\"foo"\"#, &mut [r"c:\", r"c:\foo\"])); + assert!(check_parse(r#"c:\;c:\"foo;bar"\;c:\baz"#, + &mut [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).collect::>() == + parsed.iter().map(|s| Path::new(*s)).collect::>() + } + + assert!(check_parse("", &mut [""])); + assert!(check_parse("::", &mut ["", "", ""])); + assert!(check_parse("/", &mut ["/"])); + assert!(check_parse("/:", &mut ["/", ""])); + assert!(check_parse("/:/usr/local", &mut ["/", "/usr/local"])); + } + + #[test] + #[cfg(unix)] + fn join_paths_unix() { + fn test_eq(input: &[&str], output: &str) -> bool { + &*join_paths(input.iter().map(|s| *s)).unwrap() == + OsStr::from_str(output) + } + + 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"].iter().map(|s| *s)).is_err()); + } + + #[test] + #[cfg(windows)] + fn join_paths_windows() { + fn test_eq(input: &[&str], output: &str) -> bool { + &*join_paths(input.iter().map(|s| *s)).unwrap() == + OsStr::from_str(output) + } + + 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"#].iter().map(|s| *s)).is_err()); + } + } diff --git a/src/libstd/ffi/mod.rs b/src/libstd/ffi/mod.rs index 69ada28a4b4a4..76f925a23f174 100644 --- a/src/libstd/ffi/mod.rs +++ b/src/libstd/ffi/mod.rs @@ -20,7 +20,12 @@ pub use self::c_str::c_str_to_bytes_with_nul; pub use self::os_str::OsString; pub use self::os_str::OsStr; -pub use self::os_str::AsOsStr; mod c_str; mod os_str; + +/// Freely convertible to an `&OsStr` slice. +pub trait AsOsStr { + /// Convert to an `&OsStr` slice. + fn as_os_str(&self) -> &OsStr; +} diff --git a/src/libstd/ffi/os_str.rs b/src/libstd/ffi/os_str.rs index 18e2aa8c098a7..57489e8623006 100644 --- a/src/libstd/ffi/os_str.rs +++ b/src/libstd/ffi/os_str.rs @@ -41,9 +41,11 @@ use string::{String, CowString}; use ops; use cmp; use hash::{Hash, Hasher, Writer}; +use path::{Path, GenericPath}; use sys::os_str::{Buf, Slice}; use sys_common::{AsInner, IntoInner, FromInner}; +use super::AsOsStr; /// Owned, mutable OS strings. #[derive(Clone)] @@ -69,6 +71,11 @@ impl OsString { OsString { inner: Buf::from_str(s) } } + /// Constructs a new empty `OsString`. + pub fn new() -> OsString { + OsString { inner: Buf::from_string(String::new()) } + } + /// Convert the `OsString` into a `String` if it contains valid Unicode data. /// /// On failure, ownership of the original `OsString` is returned. @@ -117,6 +124,62 @@ impl Debug for OsString { } } +impl PartialEq for OsString { + fn eq(&self, other: &OsString) -> bool { + &**self == &**other + } +} + +impl PartialEq for OsString { + fn eq(&self, other: &str) -> bool { + &**self == other + } +} + +impl PartialEq for str { + fn eq(&self, other: &OsString) -> bool { + &**other == self + } +} + +impl Eq for OsString {} + +impl PartialOrd for OsString { + #[inline] + fn partial_cmp(&self, other: &OsString) -> Option { + (&**self).partial_cmp(&**other) + } + #[inline] + fn lt(&self, other: &OsString) -> bool { &**self < &**other } + #[inline] + fn le(&self, other: &OsString) -> bool { &**self <= &**other } + #[inline] + fn gt(&self, other: &OsString) -> bool { &**self > &**other } + #[inline] + fn ge(&self, other: &OsString) -> bool { &**self >= &**other } +} + +impl PartialOrd for OsString { + #[inline] + fn partial_cmp(&self, other: &str) -> Option { + (&**self).partial_cmp(other) + } +} + +impl Ord for OsString { + #[inline] + fn cmp(&self, other: &OsString) -> cmp::Ordering { + (&**self).cmp(&**other) + } +} + +impl<'a, S: Hasher + Writer> Hash for OsString { + #[inline] + fn hash(&self, state: &mut S) { + (&**self).hash(state) + } +} + impl OsStr { /// Coerce directly from a `&str` slice to a `&OsStr` slice. pub fn from_str(s: &str) -> &OsStr { @@ -222,10 +285,10 @@ impl ToOwned for OsStr { fn to_owned(&self) -> OsString { self.to_os_string() } } -/// Freely convertible to an `&OsStr` slice. -pub trait AsOsStr { - /// Convert to an `&OsStr` slice. - fn as_os_str(&self) -> &OsStr; +impl<'a, T: AsOsStr + ?Sized> AsOsStr for &'a T { + fn as_os_str(&self) -> &OsStr { + (*self).as_os_str() + } } impl AsOsStr for OsStr { @@ -252,6 +315,21 @@ impl AsOsStr for String { } } +#[cfg(unix)] +impl AsOsStr for Path { + fn as_os_str(&self) -> &OsStr { + unsafe { mem::transmute(self.as_vec()) } + } +} + +#[cfg(windows)] +impl AsOsStr for Path { + fn as_os_str(&self) -> &OsStr { + // currently .as_str() is actually infallible on windows + OsStr::from_str(self.as_str().unwrap()) + } +} + impl FromInner for OsString { fn from_inner(buf: Buf) -> OsString { OsString { inner: buf } diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 96aebb735ef12..487efb4332682 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -250,6 +250,7 @@ pub mod ffi; pub mod fmt; pub mod old_io; pub mod os; +pub mod env; pub mod path; pub mod rand; pub mod time; diff --git a/src/libstd/old_io/process.rs b/src/libstd/old_io/process.rs index f253f9799e9bd..81903d0a97e99 100644 --- a/src/libstd/old_io/process.rs +++ b/src/libstd/old_io/process.rs @@ -231,6 +231,7 @@ impl Command { self } // Get a mutable borrow of the environment variable map for this `Command`. + #[allow(deprecated)] fn get_env_map<'a>(&'a mut self) -> &'a mut EnvMap { match self.env { Some(ref mut map) => map, diff --git a/src/libstd/old_io/tempfile.rs b/src/libstd/old_io/tempfile.rs index 20cbde5db715a..83a42549424d6 100644 --- a/src/libstd/old_io/tempfile.rs +++ b/src/libstd/old_io/tempfile.rs @@ -10,13 +10,13 @@ //! Temporary files and directories +use env; +use iter::{IteratorExt}; use old_io::{fs, IoError, IoErrorKind, IoResult}; use old_io; -use iter::IteratorExt; use ops::Drop; -use option::Option; use option::Option::{None, Some}; -use os; +use option::Option; use path::{Path, GenericPath}; use rand::{Rng, thread_rng}; use result::Result::{Ok, Err}; @@ -97,8 +97,8 @@ impl TempDir { /// If no directory can be created, `Err` is returned. pub fn new_in(tmpdir: &Path, prefix: &str) -> IoResult { if !tmpdir.is_absolute() { - let abs_tmpdir = try!(os::make_absolute(tmpdir)); - return TempDir::new_in(&abs_tmpdir, prefix); + let cur_dir = try!(env::current_dir()); + return TempDir::new_in(&cur_dir.join(tmpdir), prefix); } let mut rng = thread_rng(); @@ -132,7 +132,7 @@ impl TempDir { /// /// If no directory can be created, `Err` is returned. pub fn new(prefix: &str) -> IoResult { - TempDir::new_in(&os::tmpdir(), prefix) + TempDir::new_in(&env::temp_dir(), prefix) } /// Unwrap the wrapped `std::path::Path` from the `TempDir` wrapper. diff --git a/src/libstd/old_io/test.rs b/src/libstd/old_io/test.rs index f49e2397d4282..3f4e070e30d95 100644 --- a/src/libstd/old_io/test.rs +++ b/src/libstd/old_io/test.rs @@ -12,8 +12,8 @@ use prelude::v1::*; +use env; use libc; -use os; use std::old_io::net::ip::*; use sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering}; @@ -41,7 +41,7 @@ fn next_test_unix_socket() -> String { pub fn next_test_unix() -> Path { let string = next_test_unix_socket(); if cfg!(unix) { - os::tmpdir().join(string) + env::temp_dir().join(string) } else { Path::new(format!("{}{}", r"\\.\pipe\", string)) } @@ -87,7 +87,7 @@ fn base_port() -> u16 { ]; // FIXME (#9639): This needs to handle non-utf8 paths - let path = os::getcwd().unwrap(); + let path = env::current_dir().unwrap(); let path_s = path.as_str().unwrap(); let mut final_base = base; diff --git a/src/libstd/os.rs b/src/libstd/os.rs index 600ca60349ae2..27e843d2383bf 100644 --- a/src/libstd/os.rs +++ b/src/libstd/os.rs @@ -34,29 +34,32 @@ use self::MemoryMapKind::*; use self::MapOption::*; use self::MapError::*; +use boxed::Box; use clone::Clone; +use env; use error::{FromError, Error}; +use ffi::{OsString, OsStr}; use fmt; -use old_io::{IoResult, IoError}; use iter::{Iterator, IteratorExt}; -use marker::{Copy, Send}; use libc::{c_void, c_int, c_char}; use libc; -use boxed::Box; +use marker::{Copy, Send}; +use old_io::{IoResult, IoError}; use ops::{Drop, FnOnce}; -use option::Option; use option::Option::{Some, None}; +use option::Option; use path::{Path, GenericPath, BytesContainer}; -use sys; -use sys::os as os_imp; use ptr::PtrExt; use ptr; -use result::Result; use result::Result::{Err, Ok}; +use result::Result; use slice::{AsSlice, SliceExt}; use str::{Str, StrExt}; +use str; use string::{String, ToString}; use sync::atomic::{AtomicIsize, ATOMIC_ISIZE_INIT, Ordering}; +use sys::os as os_imp; +use sys; use vec::Vec; #[cfg(unix)] use ffi::{self, CString}; @@ -97,23 +100,10 @@ pub const TMPBUF_SZ : uint = 1000u; /// let current_working_directory = os::getcwd().unwrap(); /// println!("The current directory is {:?}", current_working_directory.display()); /// ``` +#[deprecated(since = "1.0.0", reason = "renamed to std::env::current_dir")] +#[unstable(feature = "os")] pub fn getcwd() -> IoResult { - sys::os::getcwd() -} - -/* -Accessing environment variables is not generally threadsafe. -Serialize access through a global lock. -*/ -fn with_env_lock(f: F) -> T where - F: FnOnce() -> T, -{ - use sync::{StaticMutex, MUTEX_INIT}; - - static LOCK: StaticMutex = MUTEX_INIT; - - let _guard = LOCK.lock(); - f() + env::current_dir() } /// Returns a vector of (variable, value) pairs, for all the environment @@ -132,37 +122,22 @@ fn with_env_lock(f: F) -> T where /// println!("'{}': '{}'", key, value ); /// } /// ``` +#[deprecated(since = "1.0.0", reason = "use env::vars instead")] +#[unstable(feature = "os")] pub fn env() -> Vec<(String,String)> { - env_as_bytes().into_iter().map(|(k,v)| { - let k = String::from_utf8_lossy(k.as_slice()).into_owned(); - let v = String::from_utf8_lossy(v.as_slice()).into_owned(); - (k,v) + env::vars().map(|(k, v)| { + (k.to_string_lossy().into_owned(), v.to_string_lossy().into_owned()) }).collect() } /// Returns a vector of (variable, value) byte-vector pairs for all the /// environment variables of the current process. -pub fn env_as_bytes() -> Vec<(Vec,Vec)> { - unsafe { - fn env_convert(input: Vec>) -> Vec<(Vec, Vec)> { - let mut pairs = Vec::new(); - for p in input.iter() { - let mut it = p.splitn(1, |b| *b == b'='); - let key = it.next().unwrap().to_vec(); - let default: &[u8] = &[]; - let val = it.next().unwrap_or(default).to_vec(); - pairs.push((key, val)); - } - pairs - } - with_env_lock(|| { - let unparsed_environ = sys::os::get_env_pairs(); - env_convert(unparsed_environ) - }) - } +#[deprecated(since = "1.0.0", reason = "use env::vars instead")] +#[unstable(feature = "os")] +pub fn env_as_bytes() -> Vec<(Vec, Vec)> { + env::vars().map(|(k, v)| (byteify(k), byteify(v))).collect() } -#[cfg(unix)] /// Fetches the environment variable `n` from the current process, returning /// None if the variable isn't set. /// @@ -184,52 +159,32 @@ pub fn env_as_bytes() -> Vec<(Vec,Vec)> { /// None => println!("{} is not defined in the environment.", key) /// } /// ``` +#[deprecated(since = "1.0.0", reason = "use env::var or env::var_string instead")] +#[unstable(feature = "os")] pub fn getenv(n: &str) -> Option { - getenv_as_bytes(n).map(|v| String::from_utf8_lossy(v.as_slice()).into_owned()) + env::var_string(n).ok() } -#[cfg(unix)] /// Fetches the environment variable `n` byte vector from the current process, /// returning None if the variable isn't set. /// /// # Panics /// /// Panics if `n` has any interior NULs. +#[deprecated(since = "1.0.0", reason = "use env::var instead")] +#[unstable(feature = "os")] pub fn getenv_as_bytes(n: &str) -> Option> { - unsafe { - with_env_lock(|| { - let s = CString::from_slice(n.as_bytes()); - let s = libc::getenv(s.as_ptr()) as *const _; - if s.is_null() { - None - } else { - Some(ffi::c_str_to_bytes(&s).to_vec()) - } - }) - } + env::var(n).map(byteify) } -#[cfg(windows)] -/// Fetches the environment variable `n` from the current process, returning -/// None if the variable isn't set. -pub fn getenv(n: &str) -> Option { - unsafe { - with_env_lock(|| { - use sys::os::fill_utf16_buf_and_decode; - let mut n: Vec = n.utf16_units().collect(); - n.push(0); - fill_utf16_buf_and_decode(|buf, sz| { - libc::GetEnvironmentVariableW(n.as_ptr(), buf, sz) - }) - }) - } +#[cfg(unix)] +fn byteify(s: OsString) -> Vec { + use os::unix::*; + s.into_vec() } - #[cfg(windows)] -/// Fetches the environment variable `n` byte vector from the current process, -/// returning None if the variable isn't set. -pub fn getenv_as_bytes(n: &str) -> Option> { - getenv(n).map(|s| s.into_bytes()) +fn byteify(s: OsString) -> Vec { + s.to_string_lossy().as_bytes().to_vec() } /// Sets the environment variable `n` to the value `v` for the currently running @@ -247,68 +202,30 @@ pub fn getenv_as_bytes(n: &str) -> Option> { /// None => println!("{} is not defined in the environment.", key) /// } /// ``` +#[deprecated(since = "1.0.0", reason = "renamed to env::set_var")] +#[unstable(feature = "os")] pub fn setenv(n: &str, v: T) { #[cfg(unix)] fn _setenv(n: &str, v: &[u8]) { - unsafe { - with_env_lock(|| { - let k = CString::from_slice(n.as_bytes()); - let v = CString::from_slice(v); - if libc::funcs::posix01::unistd::setenv(k.as_ptr(), - v.as_ptr(), 1) != 0 { - panic!(IoError::last_error()); - } - }) - } + use os::unix::*; + let v: OsString = OsStringExt::from_vec(v.to_vec()); + env::set_var(n, &v) } #[cfg(windows)] fn _setenv(n: &str, v: &[u8]) { - let mut n: Vec = n.utf16_units().collect(); - n.push(0); - let mut v: Vec = ::str::from_utf8(v).unwrap().utf16_units().collect(); - v.push(0); - - unsafe { - with_env_lock(|| { - if libc::SetEnvironmentVariableW(n.as_ptr(), v.as_ptr()) == 0 { - panic!(IoError::last_error()); - } - }) - } + let v = str::from_utf8(v).unwrap(); + env::set_var(n, v) } _setenv(n, v.container_as_bytes()) } /// Remove a variable from the environment entirely. +#[deprecated(since = "1.0.0", reason = "renamed to env::remove_var")] +#[unstable(feature = "os")] pub fn unsetenv(n: &str) { - #[cfg(unix)] - fn _unsetenv(n: &str) { - unsafe { - with_env_lock(|| { - let nbuf = CString::from_slice(n.as_bytes()); - if libc::funcs::posix01::unistd::unsetenv(nbuf.as_ptr()) != 0 { - panic!(IoError::last_error()); - } - }) - } - } - - #[cfg(windows)] - fn _unsetenv(n: &str) { - let mut n: Vec = n.utf16_units().collect(); - n.push(0); - unsafe { - with_env_lock(|| { - if libc::SetEnvironmentVariableW(n.as_ptr(), ptr::null()) == 0 { - panic!(IoError::last_error()); - } - }) - } - } - - _unsetenv(n) + env::remove_var(n) } /// Parses input according to platform conventions for the `PATH` @@ -328,8 +245,12 @@ pub fn unsetenv(n: &str) { /// None => println!("{} is not defined in the environment.", key) /// } /// ``` +#[deprecated(since = "1.0.0", reason = "renamed to env::split_paths")] +#[unstable(feature = "os")] pub fn split_paths(unparsed: T) -> Vec { - sys::os::split_paths(unparsed.container_as_bytes()) + let b = unparsed.container_as_bytes(); + let s = str::from_utf8(b).unwrap(); + env::split_paths(s).collect() } /// Joins a collection of `Path`s appropriately for the `PATH` @@ -353,8 +274,14 @@ pub fn split_paths(unparsed: T) -> Vec { /// paths.push(Path::new("/home/xyz/bin")); /// os::setenv(key, os::join_paths(paths.as_slice()).unwrap()); /// ``` +#[deprecated(since = "1.0.0", reason = "renamed to env::join_paths")] +#[unstable(feature = "os")] pub fn join_paths(paths: &[T]) -> Result, &'static str> { - sys::os::join_paths(paths) + env::join_paths(paths.iter().map(|s| { + str::from_utf8(s.container_as_bytes()).unwrap() + })).map(|s| { + s.to_string_lossy().into_owned().into_bytes() + }).map_err(|_| "failed to join paths") } /// A low-level OS in-memory pipe. @@ -388,6 +315,8 @@ pub unsafe fn pipe() -> IoResult { /// Returns the proper dll filename for the given basename of a file /// as a String. #[cfg(not(target_os="ios"))] +#[deprecated(since = "1.0.0", reason = "this function will be removed, use the constants directly")] +#[unstable(feature = "os")] pub fn dll_filename(base: &str) -> String { format!("{}{}{}", consts::DLL_PREFIX, base, consts::DLL_SUFFIX) } @@ -405,8 +334,10 @@ pub fn dll_filename(base: &str) -> String { /// None => println!("Unable to get the path of this executable!") /// }; /// ``` +#[deprecated(since = "1.0.0", reason = "renamed to env::current_exe")] +#[unstable(feature = "os")] pub fn self_exe_name() -> Option { - sys::os::load_self().and_then(Path::new_opt) + env::current_exe().ok() } /// Optionally returns the filesystem path to the current executable which is @@ -424,8 +355,10 @@ pub fn self_exe_name() -> Option { /// None => println!("Impossible to fetch the path of this executable.") /// }; /// ``` +#[deprecated(since = "1.0.0", reason = "use env::current_exe + dir_path/pop")] +#[unstable(feature = "os")] pub fn self_exe_path() -> Option { - self_exe_name().map(|mut p| { p.pop(); p }) + env::current_exe().ok().map(|mut p| { p.pop(); p }) } /// Optionally returns the path to the current user's home directory if known. @@ -452,6 +385,9 @@ pub fn self_exe_path() -> Option { /// None => println!("Impossible to get your home dir!") /// } /// ``` +#[deprecated(since = "1.0.0", reason = "renamed to env::home_dir")] +#[allow(deprecated)] +#[unstable(feature = "os")] pub fn homedir() -> Option { #[inline] #[cfg(unix)] @@ -487,6 +423,9 @@ pub fn homedir() -> Option { /// On Windows, returns the value of, in order, the 'TMP', 'TEMP', /// 'USERPROFILE' environment variable if any are set and not the empty /// string. Otherwise, tmpdir returns the path to the Windows directory. +#[deprecated(since = "1.0.0", reason = "renamed to env::temp_dir")] +#[allow(deprecated)] +#[unstable(feature = "os")] pub fn tmpdir() -> Path { return lookup(); @@ -542,11 +481,13 @@ pub fn tmpdir() -> Path { // 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. +#[deprecated(since = "1.0.0", reason = "use env::current_dir + .join directly")] +#[unstable(feature = "os")] pub fn make_absolute(p: &Path) -> IoResult { if p.is_absolute() { Ok(p.clone()) } else { - getcwd().map(|mut cwd| { + env::current_dir().map(|mut cwd| { cwd.push(p); cwd }) @@ -565,6 +506,8 @@ pub fn make_absolute(p: &Path) -> IoResult { /// assert!(os::change_dir(&root).is_ok()); /// println!("Successfully changed working directory to {}!", root.display()); /// ``` +#[deprecated(since = "1.0.0", reason = "renamed to env::set_current_dir")] +#[unstable(feature = "os")] pub fn change_dir(p: &Path) -> IoResult<()> { return sys::os::chdir(p); } @@ -592,8 +535,6 @@ pub fn last_os_error() -> String { error_string(errno() as uint) } -static EXIT_STATUS: AtomicIsize = ATOMIC_ISIZE_INIT; - /// Sets the process exit code /// /// Sets the exit code returned by the process if all supervised tasks @@ -602,14 +543,18 @@ static EXIT_STATUS: AtomicIsize = ATOMIC_ISIZE_INIT; /// ignored and the process exits with the default panic status. /// /// Note that this is not synchronized against modifications of other threads. +#[deprecated(since = "1.0.0", reason = "renamed to env::set_exit_status")] +#[unstable(feature = "os")] pub fn set_exit_status(code: int) { - EXIT_STATUS.store(code, Ordering::SeqCst) + env::set_exit_status(code as i32) } /// Fetches the process's current exit code. This defaults to 0 and can change /// by calling `set_exit_status`. +#[deprecated(since = "1.0.0", reason = "renamed to env::get_exit_status")] +#[unstable(feature = "os")] pub fn get_exit_status() -> int { - EXIT_STATUS.load(Ordering::SeqCst) + env::get_exit_status() as isize } #[cfg(target_os = "macos")] @@ -726,7 +671,7 @@ fn real_args() -> Vec { // Push it onto the list. let ptr = ptr as *const u16; let buf = slice::from_raw_buf(&ptr, len); - let opt_s = String::from_utf16(sys::os::truncate_utf16_at_nul(buf)); + let opt_s = String::from_utf16(sys::truncate_utf16_at_nul(buf)); opt_s.ok().expect("CommandLineToArgvW returned invalid UTF-16") }).collect(); @@ -777,12 +722,16 @@ extern "system" { /// println!("{}", argument); /// } /// ``` +#[deprecated(since = "1.0.0", reason = "use env::args instead")] +#[unstable(feature = "os")] pub fn args() -> Vec { real_args() } /// Returns the arguments which this program was started with (normally passed /// via the command line) as byte vectors. +#[deprecated(since = "1.0.0", reason = "use env::args_raw instead")] +#[unstable(feature = "os")] pub fn args_as_bytes() -> Vec> { real_args_as_bytes() } @@ -790,11 +739,13 @@ pub fn args_as_bytes() -> Vec> { #[cfg(target_os = "macos")] extern { // These functions are in crt_externs.h. - pub fn _NSGetArgc() -> *mut c_int; - pub fn _NSGetArgv() -> *mut *mut *mut c_char; + fn _NSGetArgc() -> *mut c_int; + fn _NSGetArgv() -> *mut *mut *mut c_char; } /// Returns the page size of the current architecture in bytes. +#[deprecated(since = "1.0.0", reason = "renamed to env::page_size")] +#[unstable(feature = "os")] pub fn page_size() -> uint { sys::os::page_size() } @@ -970,7 +921,7 @@ impl MemoryMap { let mut fd = -1; let mut offset = 0; let mut custom_flags = false; - let len = round_up(min_len, page_size()); + let len = round_up(min_len, env::page_size()); for &o in options.iter() { match o { @@ -1020,7 +971,7 @@ impl MemoryMap { /// Granularity that the offset or address must be for `MapOffset` and /// `MapAddr` respectively. pub fn granularity() -> uint { - page_size() + env::page_size() } } @@ -1049,7 +1000,7 @@ impl MemoryMap { let mut executable = false; let mut handle: HANDLE = libc::INVALID_HANDLE_VALUE; let mut offset: uint = 0; - let len = round_up(min_len, page_size()); + let len = round_up(min_len, env::page_size()); for &o in options.iter() { match o { @@ -1184,6 +1135,8 @@ impl MemoryMap { } #[cfg(target_os = "linux")] +#[deprecated(since = "1.0.0", reason = "renamed to env::consts")] +#[unstable(feature = "os")] pub mod consts { pub use os::arch_consts::ARCH; @@ -1215,6 +1168,8 @@ pub mod consts { } #[cfg(target_os = "macos")] +#[deprecated(since = "1.0.0", reason = "renamed to env::consts")] +#[unstable(feature = "os")] pub mod consts { pub use os::arch_consts::ARCH; @@ -1246,6 +1201,8 @@ pub mod consts { } #[cfg(target_os = "ios")] +#[deprecated(since = "1.0.0", reason = "renamed to env::consts")] +#[unstable(feature = "os")] pub mod consts { pub use os::arch_consts::ARCH; @@ -1265,6 +1222,8 @@ pub mod consts { } #[cfg(target_os = "freebsd")] +#[deprecated(since = "1.0.0", reason = "renamed to env::consts")] +#[unstable(feature = "os")] pub mod consts { pub use os::arch_consts::ARCH; @@ -1296,6 +1255,8 @@ pub mod consts { } #[cfg(target_os = "dragonfly")] +#[deprecated(since = "1.0.0", reason = "renamed to env::consts")] +#[unstable(feature = "os")] pub mod consts { pub use os::arch_consts::ARCH; @@ -1327,6 +1288,8 @@ pub mod consts { } #[cfg(target_os = "android")] +#[deprecated(since = "1.0.0", reason = "renamed to env::consts")] +#[unstable(feature = "os")] pub mod consts { pub use os::arch_consts::ARCH; @@ -1358,6 +1321,8 @@ pub mod consts { } #[cfg(target_os = "windows")] +#[deprecated(since = "1.0.0", reason = "renamed to env::consts")] +#[unstable(feature = "os")] pub mod consts { pub use os::arch_consts::ARCH; diff --git a/src/libstd/rt/backtrace.rs b/src/libstd/rt/backtrace.rs index f2d66e1a4d765..905cc06c4f007 100644 --- a/src/libstd/rt/backtrace.rs +++ b/src/libstd/rt/backtrace.rs @@ -14,7 +14,7 @@ use prelude::v1::*; -use os; +use env; use sync::atomic::{self, Ordering}; pub use sys::backtrace::write; @@ -29,7 +29,7 @@ pub fn log_enabled() -> bool { _ => {} } - let val = match os::getenv("RUST_BACKTRACE") { + let val = match env::var("RUST_BACKTRACE") { Some(..) => 2, None => 1, }; diff --git a/src/libstd/rt/mod.rs b/src/libstd/rt/mod.rs index 7e19f1cac2c5b..00088d6d99a0a 100644 --- a/src/libstd/rt/mod.rs +++ b/src/libstd/rt/mod.rs @@ -65,7 +65,7 @@ fn lang_start(main: *const u8, argc: int, argv: *const *const u8) -> int { use prelude::v1::*; use mem; - use os; + use env; use rt; use sys_common::thread_info::{self, NewThread}; use sys_common; @@ -131,7 +131,7 @@ fn lang_start(main: *const u8, argc: int, argv: *const *const u8) -> int { if failed { rt::DEFAULT_ERROR_CODE } else { - os::get_exit_status() + env::get_exit_status() as isize } } diff --git a/src/libstd/rt/util.rs b/src/libstd/rt/util.rs index f5727a38b6997..86d21cf72782e 100644 --- a/src/libstd/rt/util.rs +++ b/src/libstd/rt/util.rs @@ -13,6 +13,7 @@ use prelude::v1::*; use cmp; +use env; use fmt; use intrinsics; use libc::{self, uintptr_t}; @@ -51,7 +52,7 @@ pub fn min_stack() -> uint { 0 => {} n => return n - 1, } - let amt = os::getenv("RUST_MIN_STACK").and_then(|s| s.parse().ok()); + let amt = env::var_string("RUST_MIN_STACK").ok().and_then(|s| s.parse().ok()); let amt = amt.unwrap_or(2 * 1024 * 1024); // 0 is our sentinel value, so ensure that we'll never see 0 after // initialization has run @@ -62,15 +63,15 @@ pub fn min_stack() -> uint { /// Get's the number of scheduler threads requested by the environment /// either `RUST_THREADS` or `num_cpus`. pub fn default_sched_threads() -> uint { - match os::getenv("RUST_THREADS") { - Some(nstr) => { + match env::var_string("RUST_THREADS") { + Ok(nstr) => { let opt_n: Option = nstr.parse().ok(); match opt_n { Some(n) if n > 0 => n, _ => panic!("`RUST_THREADS` is `{}`, should be a positive integer", nstr) } } - None => { + Err(..) => { if limit_thread_creation_due_to_osx_and_valgrind() { 1 } else { diff --git a/src/libstd/sys/common/mutex.rs b/src/libstd/sys/common/mutex.rs index 9aea0fb3b3172..0ca228267003d 100644 --- a/src/libstd/sys/common/mutex.rs +++ b/src/libstd/sys/common/mutex.rs @@ -24,13 +24,6 @@ unsafe impl Sync for Mutex {} pub const MUTEX_INIT: Mutex = Mutex(imp::MUTEX_INIT); impl Mutex { - /// Creates a newly initialized mutex. - /// - /// Behavior is undefined if the mutex is moved after the first method is - /// called on the mutex. - #[inline] - pub unsafe fn new() -> Mutex { Mutex(imp::Mutex::new()) } - /// Lock the mutex blocking the current thread until it is available. /// /// Behavior is undefined if the mutex has been moved between this and any diff --git a/src/libstd/sys/common/rwlock.rs b/src/libstd/sys/common/rwlock.rs index df016b9e293b7..fe374e1fd78a3 100644 --- a/src/libstd/sys/common/rwlock.rs +++ b/src/libstd/sys/common/rwlock.rs @@ -21,13 +21,6 @@ pub struct RWLock(imp::RWLock); pub const RWLOCK_INIT: RWLock = RWLock(imp::RWLOCK_INIT); impl RWLock { - /// Creates a new instance of an RWLock. - /// - /// Usage of an RWLock is undefined if it is moved after its first use (any - /// function calls below). - #[inline] - pub unsafe fn new() -> RWLock { RWLock(imp::RWLock::new()) } - /// Acquire shared access to the underlying lock, blocking the current /// thread to do so. /// diff --git a/src/libstd/sys/unix/backtrace.rs b/src/libstd/sys/unix/backtrace.rs index e310b8f6d90b3..51186feeaf4bc 100644 --- a/src/libstd/sys/unix/backtrace.rs +++ b/src/libstd/sys/unix/backtrace.rs @@ -240,7 +240,7 @@ fn print(w: &mut Writer, idx: int, addr: *mut libc::c_void) -> IoResult<()> { #[cfg(not(any(target_os = "macos", target_os = "ios")))] fn print(w: &mut Writer, idx: int, addr: *mut libc::c_void) -> IoResult<()> { - use os; + use env; use ptr; //////////////////////////////////////////////////////////////////////// @@ -319,7 +319,7 @@ fn print(w: &mut Writer, idx: int, addr: *mut libc::c_void) -> IoResult<()> { if !STATE.is_null() { return STATE } let selfname = if cfg!(target_os = "freebsd") || cfg!(target_os = "dragonfly") { - os::self_exe_name() + env::current_exe().ok() } else { None }; diff --git a/src/libstd/sys/unix/c.rs b/src/libstd/sys/unix/c.rs index 9016d1a2c99ca..7a17e8b6f1e15 100644 --- a/src/libstd/sys/unix/c.rs +++ b/src/libstd/sys/unix/c.rs @@ -66,6 +66,53 @@ pub const MSG_DONTWAIT: libc::c_int = 0x40; pub const WNOHANG: libc::c_int = 1; +#[cfg(target_os = "linux")] +pub const _SC_GETPW_R_SIZE_MAX: libc::c_int = 70; +#[cfg(any(target_os = "macos", + target_os = "freebsd"))] +pub const _SC_GETPW_R_SIZE_MAX: libc::c_int = 71; +#[cfg(target_os = "android")] +pub const _SC_GETPW_R_SIZE_MAX: libc::c_int = 0x0048; + +#[repr(C)] +#[cfg(target_os = "linux")] +pub struct passwd { + pub pw_name: *mut libc::c_char, + pub pw_passwd: *mut libc::c_char, + pub pw_uid: libc::uid_t, + pub pw_gid: libc::gid_t, + pub pw_gecos: *mut libc::c_char, + pub pw_dir: *mut libc::c_char, + pub pw_shell: *mut libc::c_char, +} + +#[repr(C)] +#[cfg(any(target_os = "macos", + target_os = "freebsd"))] +pub struct passwd { + pub pw_name: *mut libc::c_char, + pub pw_passwd: *mut libc::c_char, + pub pw_uid: libc::uid_t, + pub pw_gid: libc::gid_t, + pub pw_change: libc::time_t, + pub pw_class: *mut libc::c_char, + pub pw_gecos: *mut libc::c_char, + pub pw_dir: *mut libc::c_char, + pub pw_shell: *mut libc::c_char, + pub pw_expire: libc::time_t, +} + +#[repr(C)] +#[cfg(target_os = "android")] +pub struct passwd { + pub pw_name: *mut libc::c_char, + pub pw_passwd: *mut libc::c_char, + pub pw_uid: libc::uid_t, + pub pw_gid: libc::gid_t, + pub pw_dir: *mut libc::c_char, + pub pw_shell: *mut libc::c_char, +} + extern { pub fn gettimeofday(timeval: *mut libc::timeval, tzp: *mut libc::c_void) -> libc::c_int; @@ -92,6 +139,12 @@ extern { pub fn sigaddset(set: *mut sigset_t, signum: libc::c_int) -> libc::c_int; pub fn sigdelset(set: *mut sigset_t, signum: libc::c_int) -> libc::c_int; pub fn sigemptyset(set: *mut sigset_t) -> libc::c_int; + + pub fn getpwuid_r(uid: libc::uid_t, + pwd: *mut passwd, + buf: *mut libc::c_char, + buflen: libc::size_t, + result: *mut *mut passwd) -> libc::c_int; } #[cfg(any(target_os = "macos", target_os = "ios"))] diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs index f2f2e7436bfb7..b03b9046966a0 100644 --- a/src/libstd/sys/unix/mod.rs +++ b/src/libstd/sys/unix/mod.rs @@ -141,7 +141,7 @@ pub fn retry (mut f: F) -> T where let one: T = Int::one(); loop { let n = f(); - if n == -one && os::errno() == libc::EINTR as int { } + if n == -one && os::errno() == libc::EINTR as i32 { } else { return n } } } @@ -155,7 +155,7 @@ pub fn ms_to_timeval(ms: u64) -> libc::timeval { pub fn wouldblock() -> bool { let err = os::errno(); - err == libc::EWOULDBLOCK as int || err == libc::EAGAIN as int + err == libc::EWOULDBLOCK as i32 || err == libc::EAGAIN as i32 } pub fn set_nonblocking(fd: sock_t, nb: bool) -> IoResult<()> { diff --git a/src/libstd/sys/unix/os.rs b/src/libstd/sys/unix/os.rs index dd343baa7c9c3..68818b07b7fef 100644 --- a/src/libstd/sys/unix/os.rs +++ b/src/libstd/sys/unix/os.rs @@ -1,4 +1,4 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -11,93 +11,64 @@ //! Implementation of `std::os` functionality for unix systems use prelude::v1::*; +use os::unix::*; -use error::{FromError, Error}; -use ffi::{self, CString}; +use error::Error as StdError; +use ffi::{self, CString, OsString, OsStr, AsOsStr}; use fmt; -use old_io::{IoError, IoResult}; +use iter; use libc::{self, c_int, c_char, c_void}; -use os::TMPBUF_SZ; -use os; -use path::{BytesContainer}; +use mem; +use old_io::{IoResult, IoError, fs}; use ptr; +use slice; use str; +use sys::c; use sys::fs::FileDesc; +use vec; -const BUF_BYTES : uint = 2048u; +const BUF_BYTES: usize = 2048; +const TMPBUF_SZ: usize = 128; /// Returns the platform-specific value of errno -pub fn errno() -> int { +pub fn errno() -> i32 { #[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd"))] - fn errno_location() -> *const c_int { - extern { - fn __error() -> *const c_int; - } - unsafe { - __error() - } + unsafe fn errno_location() -> *const c_int { + extern { fn __error() -> *const c_int; } + __error() } #[cfg(target_os = "dragonfly")] - fn errno_location() -> *const c_int { - extern { - fn __dfly_error() -> *const c_int; - } - unsafe { - __dfly_error() - } + unsafe fn errno_location() -> *const c_int { + extern { fn __dfly_error() -> *const c_int; } + __dfly_error() } #[cfg(any(target_os = "linux", target_os = "android"))] - fn errno_location() -> *const c_int { - extern { - fn __errno_location() -> *const c_int; - } - unsafe { - __errno_location() - } + unsafe fn errno_location() -> *const c_int { + extern { fn __errno_location() -> *const c_int; } + __errno_location() } unsafe { - (*errno_location()) as int + (*errno_location()) as i32 } } /// Get a detailed string description for the given error number pub fn error_string(errno: i32) -> String { - #[cfg(any(target_os = "macos", - target_os = "ios", - target_os = "android", - target_os = "freebsd", - target_os = "dragonfly"))] - fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: libc::size_t) - -> c_int { - extern { - fn strerror_r(errnum: c_int, buf: *mut c_char, - buflen: libc::size_t) -> c_int; - } - unsafe { - strerror_r(errnum, buf, buflen) - } - } - - // GNU libc provides a non-compliant version of strerror_r by default - // and requires macros to instead use the POSIX compliant variant. - // So we just use __xpg_strerror_r which is always POSIX compliant #[cfg(target_os = "linux")] - fn strerror_r(errnum: c_int, buf: *mut c_char, - buflen: libc::size_t) -> c_int { - extern { - fn __xpg_strerror_r(errnum: c_int, - buf: *mut c_char, - buflen: libc::size_t) - -> c_int; - } - unsafe { - __xpg_strerror_r(errnum, buf, buflen) - } + extern { + #[link_name = "__xpg_strerror_r"] + fn strerror_r(errnum: c_int, buf: *mut c_char, + buflen: libc::size_t) -> c_int; + } + #[cfg(not(target_os = "linux"))] + extern { + fn strerror_r(errnum: c_int, buf: *mut c_char, + buflen: libc::size_t) -> c_int; } let mut buf = [0 as c_char; TMPBUF_SZ]; @@ -113,15 +84,6 @@ pub fn error_string(errno: i32) -> String { } } -pub unsafe fn pipe() -> IoResult<(FileDesc, FileDesc)> { - let mut fds = [0; 2]; - if libc::pipe(fds.as_mut_ptr()) == 0 { - Ok((FileDesc::new(fds[0], true), FileDesc::new(fds[1], true))) - } else { - Err(super::last_error()) - } -} - pub fn getcwd() -> IoResult { let mut buf = [0 as c_char; BUF_BYTES]; unsafe { @@ -133,43 +95,69 @@ pub fn getcwd() -> IoResult { } } -pub unsafe fn get_env_pairs() -> Vec> { - extern { - fn rust_env_pairs() -> *const *const c_char; - } - let mut environ = rust_env_pairs(); - if environ as uint == 0 { - panic!("os::env() failure getting env string from OS: {}", - os::last_os_error()); +pub fn chdir(p: &Path) -> IoResult<()> { + let p = CString::from_slice(p.as_vec()); + unsafe { + match libc::chdir(p.as_ptr()) == (0 as c_int) { + true => Ok(()), + false => Err(IoError::last_error()), + } } - let mut result = Vec::new(); - while *environ != ptr::null() { - let env_pair = ffi::c_str_to_bytes(&*environ).to_vec(); - result.push(env_pair); - environ = environ.offset(1); +} + +pub struct SplitPaths<'a> { + iter: iter::Map<&'a [u8], Path, + slice::Split<'a, u8, fn(&u8) -> bool>, + fn(&'a [u8]) -> Path>, +} + +pub fn split_paths<'a>(unparsed: &'a OsStr) -> SplitPaths<'a> { + fn is_colon(b: &u8) -> bool { *b == b':' } + let unparsed = unparsed.as_byte_slice(); + SplitPaths { + iter: unparsed.split(is_colon as fn(&u8) -> bool) + .map(Path::new as fn(&'a [u8]) -> Path) } - result } -pub fn split_paths(unparsed: &[u8]) -> Vec { - unparsed.split(|b| *b == b':').map(Path::new).collect() +impl<'a> Iterator for SplitPaths<'a> { + type Item = Path; + fn next(&mut self) -> Option { self.iter.next() } + fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } } -pub fn join_paths(paths: &[T]) -> Result, &'static str> { +#[derive(Debug)] +pub struct JoinPathsError; + +pub fn join_paths(paths: I) -> Result + where I: Iterator, T: AsOsStr +{ let mut joined = Vec::new(); let sep = b':'; - for (i, path) in paths.iter().map(|p| p.container_as_bytes()).enumerate() { + for (i, path) in paths.enumerate() { + let path = path.as_os_str().as_byte_slice(); if i > 0 { joined.push(sep) } - if path.contains(&sep) { return Err("path segment contains separator `:`") } + if path.contains(&sep) { + return Err(JoinPathsError) + } joined.push_all(path); } + Ok(OsStringExt::from_vec(joined)) +} - Ok(joined) +impl fmt::Display for JoinPathsError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + "path segment contains separator `:`".fmt(f) + } +} + +impl StdError for JoinPathsError { + fn description(&self) -> &str { "failed to join paths" } } #[cfg(target_os = "freebsd")] -pub fn load_self() -> Option> { +pub fn current_exe() -> IoResult { unsafe { use libc::funcs::bsd44::*; use libc::consts::os::extra::*; @@ -181,66 +169,276 @@ pub fn load_self() -> Option> { let err = sysctl(mib.as_mut_ptr(), mib.len() as ::libc::c_uint, ptr::null_mut(), &mut sz, ptr::null_mut(), 0u as libc::size_t); - if err != 0 { return None; } - if sz == 0 { return None; } + if err != 0 { return Err(IoError::last_error()); } + if sz == 0 { return Err(IoError::last_error()); } let mut v: Vec = Vec::with_capacity(sz as uint); let err = sysctl(mib.as_mut_ptr(), mib.len() as ::libc::c_uint, v.as_mut_ptr() as *mut libc::c_void, &mut sz, ptr::null_mut(), 0u as libc::size_t); - if err != 0 { return None; } - if sz == 0 { return None; } + if err != 0 { return Err(IoError::last_error()); } + if sz == 0 { return Err(IoError::last_error()); } v.set_len(sz as uint - 1); // chop off trailing NUL - Some(v) + Ok(Path::new(v)) } } #[cfg(target_os = "dragonfly")] -pub fn load_self() -> Option> { - use old_io; - - match old_io::fs::readlink(&Path::new("/proc/curproc/file")) { - Ok(path) => Some(path.into_vec()), - Err(..) => None - } +pub fn current_exe() -> IoResult { + fs::readlink(&Path::new("/proc/curproc/file")) } #[cfg(any(target_os = "linux", target_os = "android"))] -pub fn load_self() -> Option> { - use old_io; - - match old_io::fs::readlink(&Path::new("/proc/self/exe")) { - Ok(path) => Some(path.into_vec()), - Err(..) => None - } +pub fn current_exe() -> IoResult { + fs::readlink(&Path::new("/proc/self/exe")) } #[cfg(any(target_os = "macos", target_os = "ios"))] -pub fn load_self() -> Option> { +pub fn current_exe() -> IoResult { unsafe { use libc::funcs::extra::_NSGetExecutablePath; let mut sz: u32 = 0; _NSGetExecutablePath(ptr::null_mut(), &mut sz); - if sz == 0 { return None; } + if sz == 0 { return Err(IoError::last_error()); } let mut v: Vec = Vec::with_capacity(sz as uint); let err = _NSGetExecutablePath(v.as_mut_ptr() as *mut i8, &mut sz); - if err != 0 { return None; } + if err != 0 { return Err(IoError::last_error()); } v.set_len(sz as uint - 1); // chop off trailing NUL - Some(v) + Ok(Path::new(v)) } } -pub fn chdir(p: &Path) -> IoResult<()> { - let p = CString::from_slice(p.as_vec()); +pub struct Args { + iter: vec::IntoIter, + _dont_send_or_sync_me: *mut (), +} + +impl Iterator for Args { + type Item = OsString; + fn next(&mut self) -> Option { self.iter.next() } + fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } +} + +/// Returns the command line arguments +/// +/// Returns a list of the command line arguments. +#[cfg(target_os = "macos")] +pub fn args() -> Args { + extern { + // These functions are in crt_externs.h. + fn _NSGetArgc() -> *mut c_int; + fn _NSGetArgv() -> *mut *mut *mut c_char; + } + + let vec = unsafe { + let (argc, argv) = (*_NSGetArgc() as isize, + *_NSGetArgv() as *const *const c_char); + range(0, argc as isize).map(|i| { + let bytes = ffi::c_str_to_bytes(&*argv.offset(i)).to_vec(); + OsStringExt::from_vec(bytes) + }).collect::>() + }; + Args { + iter: vec.into_iter(), + _dont_send_or_sync_me: 0 as *mut (), + } +} + +// As _NSGetArgc and _NSGetArgv aren't mentioned in iOS docs +// and use underscores in their names - they're most probably +// are considered private and therefore should be avoided +// Here is another way to get arguments using Objective C +// runtime +// +// In general it looks like: +// res = Vec::new() +// let args = [[NSProcessInfo processInfo] arguments] +// for i in range(0, [args count]) +// res.push([args objectAtIndex:i]) +// res +#[cfg(target_os = "ios")] +pub fn args() -> Args { + use iter::range; + use mem; + + #[link(name = "objc")] + extern { + fn sel_registerName(name: *const libc::c_uchar) -> Sel; + fn objc_msgSend(obj: NsId, sel: Sel, ...) -> NsId; + fn objc_getClass(class_name: *const libc::c_uchar) -> NsId; + } + + #[link(name = "Foundation", kind = "framework")] + extern {} + + type Sel = *const libc::c_void; + type NsId = *const libc::c_void; + + let mut res = Vec::new(); + unsafe { - match libc::chdir(p.as_ptr()) == (0 as c_int) { - true => Ok(()), - false => Err(IoError::last_error()), + let processInfoSel = sel_registerName("processInfo\0".as_ptr()); + let argumentsSel = sel_registerName("arguments\0".as_ptr()); + let utf8Sel = sel_registerName("UTF8String\0".as_ptr()); + let countSel = sel_registerName("count\0".as_ptr()); + let objectAtSel = sel_registerName("objectAtIndex:\0".as_ptr()); + + let klass = objc_getClass("NSProcessInfo\0".as_ptr()); + let info = objc_msgSend(klass, processInfoSel); + let args = objc_msgSend(info, argumentsSel); + + let cnt: int = mem::transmute(objc_msgSend(args, countSel)); + for i in range(0, cnt) { + let tmp = objc_msgSend(args, objectAtSel, i); + let utf_c_str: *const libc::c_char = + mem::transmute(objc_msgSend(tmp, utf8Sel)); + let bytes = ffi::c_str_to_bytes(&utf_c_str).to_vec(); + res.push(OsString::from_vec(bytes)) + } + } + + Args { iter: res.into_iter(), _dont_send_or_sync_me: 0 as *mut _ } +} + +#[cfg(any(target_os = "linux", + target_os = "android", + target_os = "freebsd", + target_os = "dragonfly"))] +pub fn args() -> Args { + use rt; + let bytes = rt::args::clone().unwrap_or(Vec::new()); + let v: Vec = bytes.into_iter().map(|v| { + OsStringExt::from_vec(v) + }).collect(); + Args { iter: v.into_iter(), _dont_send_or_sync_me: 0 as *mut _ } +} + +pub struct Env { + iter: vec::IntoIter<(OsString, OsString)>, + _dont_send_or_sync_me: *mut (), +} + +impl Iterator for Env { + type Item = (OsString, OsString); + fn next(&mut self) -> Option<(OsString, OsString)> { self.iter.next() } + fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } +} + +#[cfg(target_os = "macos")] +pub unsafe fn environ() -> *mut *const *const c_char { + extern { fn _NSGetEnviron() -> *mut *const *const c_char; } + _NSGetEnviron() +} + +#[cfg(not(target_os = "macos"))] +pub unsafe fn environ() -> *mut *const *const c_char { + extern { static mut environ: *const *const c_char; } + &mut environ +} + +/// Returns a vector of (variable, value) byte-vector pairs for all the +/// environment variables of the current process. +pub fn env() -> Env { + return unsafe { + let mut environ = *environ(); + if environ as usize == 0 { + panic!("os::env() failure getting env string from OS: {}", + IoError::last_error()); + } + let mut result = Vec::new(); + while *environ != ptr::null() { + result.push(parse(ffi::c_str_to_bytes(&*environ))); + environ = environ.offset(1); + } + Env { iter: result.into_iter(), _dont_send_or_sync_me: 0 as *mut _ } + }; + + fn parse(input: &[u8]) -> (OsString, OsString) { + let mut it = input.splitn(1, |b| *b == b'='); + let key = it.next().unwrap().to_vec(); + let default: &[u8] = &[]; + let val = it.next().unwrap_or(default).to_vec(); + (OsStringExt::from_vec(key), OsStringExt::from_vec(val)) + } +} + +pub fn getenv(k: &OsStr) -> Option { + unsafe { + let s = CString::from_slice(k.as_byte_slice()); + let s = libc::getenv(s.as_ptr()) as *const _; + if s.is_null() { + None + } else { + Some(OsStringExt::from_vec(ffi::c_str_to_bytes(&s).to_vec())) + } + } +} + +pub fn setenv(k: &OsStr, v: &OsStr) { + unsafe { + let k = CString::from_slice(k.as_byte_slice()); + let v = CString::from_slice(v.as_byte_slice()); + if libc::funcs::posix01::unistd::setenv(k.as_ptr(), v.as_ptr(), 1) != 0 { + panic!("failed setenv: {}", IoError::last_error()); + } + } +} + +pub fn unsetenv(n: &OsStr) { + unsafe { + let nbuf = CString::from_slice(n.as_byte_slice()); + if libc::funcs::posix01::unistd::unsetenv(nbuf.as_ptr()) != 0 { + panic!("failed unsetenv: {}", IoError::last_error()); } } } -pub fn page_size() -> uint { +pub unsafe fn pipe() -> IoResult<(FileDesc, FileDesc)> { + let mut fds = [0; 2]; + if libc::pipe(fds.as_mut_ptr()) == 0 { + Ok((FileDesc::new(fds[0], true), FileDesc::new(fds[1], true))) + } else { + Err(IoError::last_error()) + } +} + +pub fn page_size() -> usize { unsafe { - libc::sysconf(libc::_SC_PAGESIZE) as uint + libc::sysconf(libc::_SC_PAGESIZE) as usize } } + +pub fn temp_dir() -> Path { + getenv("TMPDIR".as_os_str()).map(|p| Path::new(p.into_vec())).unwrap_or_else(|| { + if cfg!(target_os = "android") { + Path::new("/data/local/tmp") + } else { + Path::new("/tmp") + } + }) +} + +pub fn home_dir() -> Option { + getenv("HOME".as_os_str()).or_else(|| unsafe { + let mut amt = match libc::sysconf(c::_SC_GETPW_R_SIZE_MAX) { + n if n < 0 => 512 as usize, + n => n as usize, + }; + let me = libc::getuid(); + loop { + let mut buf = Vec::with_capacity(amt); + let mut passwd: c::passwd = mem::zeroed(); + let mut result = 0 as *mut _; + match c::getpwuid_r(me, &mut passwd, buf.as_mut_ptr(), + buf.capacity() as libc::size_t, + &mut result) { + 0 if !result.is_null() => {} + _ => return None + } + let ptr = passwd.pw_dir as *const _; + let bytes = ffi::c_str_to_bytes(&ptr).to_vec(); + return Some(OsStringExt::from_vec(bytes)) + } + }).map(|os| { + Path::new(os.into_vec()) + }) +} diff --git a/src/libstd/sys/unix/process.rs b/src/libstd/sys/unix/process.rs index b004a47f8a349..ae648d13959f9 100644 --- a/src/libstd/sys/unix/process.rs +++ b/src/libstd/sys/unix/process.rs @@ -72,18 +72,6 @@ impl Process { } } - #[cfg(target_os = "macos")] - unsafe fn set_environ(envp: *const c_void) { - extern { fn _NSGetEnviron() -> *mut *const c_void; } - - *_NSGetEnviron() = envp; - } - #[cfg(not(target_os = "macos"))] - unsafe fn set_environ(envp: *const c_void) { - extern { static mut environ: *const c_void; } - environ = envp; - } - unsafe fn set_cloexec(fd: c_int) { let ret = c::ioctl(fd, c::FIOCLEX); assert_eq!(ret, 0); @@ -269,7 +257,7 @@ impl Process { fail(&mut output); } if !envp.is_null() { - set_environ(envp); + *sys::os::environ() = envp as *const _; } let _ = execvp(*argv, argv as *mut _); fail(&mut output); diff --git a/src/libstd/sys/windows/backtrace.rs b/src/libstd/sys/windows/backtrace.rs index 1be1a412ffa9f..66712b9e3a1e6 100644 --- a/src/libstd/sys/windows/backtrace.rs +++ b/src/libstd/sys/windows/backtrace.rs @@ -7,29 +7,31 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. -/// As always, windows has something very different than unix, we mainly want -/// to avoid having to depend too much on libunwind for windows. -/// -/// If you google around, you'll find a fair bit of references to built-in -/// functions to get backtraces on windows. It turns out that most of these are -/// in an external library called dbghelp. I was unable to find this library -/// via `-ldbghelp`, but it is apparently normal to do the `dlopen` equivalent -/// of it. -/// -/// You'll also find that there's a function called CaptureStackBackTrace -/// mentioned frequently (which is also easy to use), but sadly I didn't have a -/// copy of that function in my mingw install (maybe it was broken?). Instead, -/// this takes the route of using StackWalk64 in order to walk the stack. + +//! As always, windows has something very different than unix, we mainly want +//! to avoid having to depend too much on libunwind for windows. +//! +//! If you google around, you'll find a fair bit of references to built-in +//! functions to get backtraces on windows. It turns out that most of these are +//! in an external library called dbghelp. I was unable to find this library +//! via `-ldbghelp`, but it is apparently normal to do the `dlopen` equivalent +//! of it. +//! +//! You'll also find that there's a function called CaptureStackBackTrace +//! mentioned frequently (which is also easy to use), but sadly I didn't have a +//! copy of that function in my mingw install (maybe it was broken?). Instead, +//! this takes the route of using StackWalk64 in order to walk the stack. + +#![allow(dead_code)] use dynamic_lib::DynamicLibrary; use ffi; -use core::ops::Index; use intrinsics; use old_io::{IoResult, Writer}; use libc; use mem; use ops::Drop; -use option::Option::{Some, None}; +use option::Option::{Some}; use path::Path; use ptr; use result::Result::{Ok, Err}; @@ -296,7 +298,7 @@ pub fn write(w: &mut Writer) -> IoResult<()> { // According to windows documentation, all dbghelp functions are // single-threaded. static LOCK: StaticMutex = MUTEX_INIT; - let _g = unsafe { LOCK.lock() }; + let _g = LOCK.lock(); // Open up dbghelp.dll, we don't link to it explicitly because it can't // always be found. Additionally, it's nice having fewer dependencies. diff --git a/src/libstd/sys/windows/c.rs b/src/libstd/sys/windows/c.rs index da3b7ee2f2fd5..0355565cf00f2 100644 --- a/src/libstd/sys/windows/c.rs +++ b/src/libstd/sys/windows/c.rs @@ -10,17 +10,21 @@ //! C definitions used by libnative that don't belong in liblibc -#![allow(overflowing_literals)] -#![allow(dead_code)] -#![allow(non_camel_case_types)] +#![allow(bad_style, dead_code, overflowing_literals)] use libc; -use prelude::v1::*; -pub const WSADESCRIPTION_LEN: uint = 256; -pub const WSASYS_STATUS_LEN: uint = 128; +pub use self::GET_FILEEX_INFO_LEVELS::*; +pub use self::FILE_INFO_BY_HANDLE_CLASS::*; +pub use libc::consts::os::extra::{ + FILE_ATTRIBUTE_READONLY, + FILE_ATTRIBUTE_DIRECTORY, +}; + +pub const WSADESCRIPTION_LEN: usize = 256; +pub const WSASYS_STATUS_LEN: usize = 128; pub const FIONBIO: libc::c_long = 0x8004667e; -pub const FD_SETSIZE: uint = 64; +pub const FD_SETSIZE: usize = 64; pub const MSG_DONTWAIT: libc::c_int = 0; pub const ERROR_ILLEGAL_CHARACTER: libc::c_int = 582; pub const ENABLE_ECHO_INPUT: libc::DWORD = 0x4; @@ -32,12 +36,15 @@ pub const ENABLE_QUICK_EDIT_MODE: libc::DWORD = 0x40; pub const WSA_INVALID_EVENT: WSAEVENT = 0 as WSAEVENT; pub const FD_ACCEPT: libc::c_long = 0x08; -pub const FD_MAX_EVENTS: uint = 10; +pub const FD_MAX_EVENTS: usize = 10; pub const WSA_INFINITE: libc::DWORD = libc::INFINITE; pub const WSA_WAIT_TIMEOUT: libc::DWORD = libc::consts::os::extra::WAIT_TIMEOUT; pub const WSA_WAIT_EVENT_0: libc::DWORD = libc::consts::os::extra::WAIT_OBJECT_0; pub const WSA_WAIT_FAILED: libc::DWORD = libc::consts::os::extra::WAIT_FAILED; +pub const ERROR_NO_MORE_FILES: libc::DWORD = 18; +pub const TOKEN_READ: libc::DWORD = 0x20008; + #[repr(C)] #[cfg(target_arch = "x86")] pub struct WSADATA { @@ -80,7 +87,7 @@ pub struct fd_set { } pub fn fd_set(set: &mut fd_set, s: libc::SOCKET) { - set.fd_array[set.fd_count as uint] = s; + set.fd_array[set.fd_count as usize] = s; set.fd_count += 1; } @@ -110,6 +117,69 @@ pub struct CONSOLE_SCREEN_BUFFER_INFO { } pub type PCONSOLE_SCREEN_BUFFER_INFO = *mut CONSOLE_SCREEN_BUFFER_INFO; +#[repr(C)] +pub struct WIN32_FILE_ATTRIBUTE_DATA { + pub dwFileAttributes: libc::DWORD, + pub ftCreationTime: libc::FILETIME, + pub ftLastAccessTime: libc::FILETIME, + pub ftLastWriteTime: libc::FILETIME, + pub nFileSizeHigh: libc::DWORD, + pub nFileSizeLow: libc::DWORD, +} + +#[repr(C)] +pub struct BY_HANDLE_FILE_INFORMATION { + pub dwFileAttributes: libc::DWORD, + pub ftCreationTime: libc::FILETIME, + pub ftLastAccessTime: libc::FILETIME, + pub ftLastWriteTime: libc::FILETIME, + pub dwVolumeSerialNumber: libc::DWORD, + pub nFileSizeHigh: libc::DWORD, + pub nFileSizeLow: libc::DWORD, + pub nNumberOfLinks: libc::DWORD, + pub nFileIndexHigh: libc::DWORD, + pub nFileIndexLow: libc::DWORD, +} + +pub type LPBY_HANDLE_FILE_INFORMATION = *mut BY_HANDLE_FILE_INFORMATION; + +#[repr(C)] +pub enum GET_FILEEX_INFO_LEVELS { + GetFileExInfoStandard, + GetFileExMaxInfoLevel +} + +#[repr(C)] +pub enum FILE_INFO_BY_HANDLE_CLASS { + FileBasicInfo = 0, + FileStandardInfo = 1, + FileNameInfo = 2, + FileRenameInfo = 3, + FileDispositionInfo = 4, + FileAllocationInfo = 5, + FileEndOfFileInfo = 6, + FileStreamInfo = 7, + FileCompressionInfo = 8, + FileAttributeTagInfo = 9, + FileIdBothDirectoryInfo = 10, // 0xA + FileIdBothDirectoryRestartInfo = 11, // 0xB + FileIoPriorityHintInfo = 12, // 0xC + FileRemoteProtocolInfo = 13, // 0xD + FileFullDirectoryInfo = 14, // 0xE + FileFullDirectoryRestartInfo = 15, // 0xF + FileStorageInfo = 16, // 0x10 + FileAlignmentInfo = 17, // 0x11 + FileIdInfo = 18, // 0x12 + FileIdExtdDirectoryInfo = 19, // 0x13 + FileIdExtdDirectoryRestartInfo = 20, // 0x14 + MaximumFileInfoByHandlesClass +} + +#[repr(C)] +pub struct FILE_END_OF_FILE_INFO { + pub EndOfFile: libc::LARGE_INTEGER, +} + #[link(name = "ws2_32")] extern "system" { pub fn WSAStartup(wVersionRequested: libc::WORD, @@ -156,31 +226,29 @@ extern "system" { } pub mod compat { - use intrinsics::{atomic_store_relaxed, transmute}; - use libc::types::os::arch::extra::{LPCWSTR, HMODULE, LPCSTR, LPVOID}; use prelude::v1::*; + use ffi::CString; + use libc::types::os::arch::extra::{LPCWSTR, HMODULE, LPCSTR, LPVOID}; + use sync::atomic::{AtomicUsize, Ordering}; extern "system" { fn GetModuleHandleW(lpModuleName: LPCWSTR) -> HMODULE; fn GetProcAddress(hModule: HMODULE, lpProcName: LPCSTR) -> LPVOID; } - // store_func() is idempotent, so using relaxed ordering for the atomics - // should be enough. This way, calling a function in this compatibility - // layer (after it's loaded) shouldn't be any slower than a regular DLL - // call. - unsafe fn store_func(ptr: *mut uint, module: &str, symbol: &str, fallback: uint) { + fn store_func(ptr: &AtomicUsize, module: &str, symbol: &str, + fallback: usize) -> usize { let mut module: Vec = module.utf16_units().collect(); module.push(0); let symbol = CString::from_slice(symbol.as_bytes()); - let handle = GetModuleHandleW(module.as_ptr()); - let func: uint = transmute(GetProcAddress(handle, symbol.as_ptr())); - atomic_store_relaxed(ptr, if func == 0 { - fallback - } else { - func - }) + let func = unsafe { + let handle = GetModuleHandleW(module.as_ptr()); + GetProcAddress(handle, symbol.as_ptr()) as usize + }; + let value = if func == 0 {fallback} else {func}; + ptr.store(value, Ordering::SeqCst); + value } /// Macro for creating a compatibility fallback for a Windows function @@ -192,29 +260,36 @@ pub mod compat { /// }) /// ``` /// - /// Note that arguments unused by the fallback implementation should not be called `_` as - /// they are used to be passed to the real function if available. + /// Note that arguments unused by the fallback implementation should not be + /// called `_` as they are used to be passed to the real function if + /// available. macro_rules! compat_fn { ($module:ident::$symbol:ident($($argname:ident: $argtype:ty),*) -> $rettype:ty { $fallback:expr }) => ( #[inline(always)] pub unsafe fn $symbol($($argname: $argtype),*) -> $rettype { - static mut ptr: extern "system" fn($($argname: $argtype),*) -> $rettype = thunk; - - extern "system" fn thunk($($argname: $argtype),*) -> $rettype { - unsafe { - ::sys::c::compat::store_func(&mut ptr as *mut _ as *mut uint, - stringify!($module), - stringify!($symbol), - fallback as uint); - ::intrinsics::atomic_load_relaxed(&ptr)($($argname),*) - } + use sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering}; + use mem; + + static PTR: AtomicUsize = ATOMIC_USIZE_INIT; + + fn load() -> usize { + ::sys::c::compat::store_func(&PTR, + stringify!($module), + stringify!($symbol), + fallback as usize) } extern "system" fn fallback($($argname: $argtype),*) -> $rettype { $fallback } - ::intrinsics::atomic_load_relaxed(&ptr)($($argname),*) + let addr = match PTR.load(Ordering::SeqCst) { + 0 => load(), + n => n, + }; + let f: extern "system" fn($($argtype),*) -> $rettype = + mem::transmute(addr); + f($($argname),*) } ) } @@ -229,10 +304,7 @@ pub mod compat { use libc::c_uint; use libc::types::os::arch::extra::{DWORD, LPCWSTR, BOOLEAN, HANDLE}; use libc::consts::os::extra::ERROR_CALL_NOT_IMPLEMENTED; - - extern "system" { - fn SetLastError(dwErrCode: DWORD); - } + use sys::c::SetLastError; compat_fn! { kernel32::CreateSymbolicLinkW(_lpSymlinkFileName: LPCWSTR, @@ -282,4 +354,42 @@ extern "system" { hConsoleOutput: libc::HANDLE, lpConsoleScreenBufferInfo: PCONSOLE_SCREEN_BUFFER_INFO, ) -> libc::BOOL; + + pub fn GetFileAttributesExW(lpFileName: libc::LPCWSTR, + fInfoLevelId: GET_FILEEX_INFO_LEVELS, + lpFileInformation: libc::LPVOID) -> libc::BOOL; + pub fn RemoveDirectoryW(lpPathName: libc::LPCWSTR) -> libc::BOOL; + pub fn SetFileAttributesW(lpFileName: libc::LPCWSTR, + dwFileAttributes: libc::DWORD) -> libc::BOOL; + pub fn GetFileAttributesW(lpFileName: libc::LPCWSTR) -> libc::DWORD; + pub fn GetFileInformationByHandle(hFile: libc::HANDLE, + lpFileInformation: LPBY_HANDLE_FILE_INFORMATION) + -> libc::BOOL; + + pub fn SetLastError(dwErrCode: libc::DWORD); + pub fn GetCommandLineW() -> *mut libc::LPCWSTR; + pub fn LocalFree(ptr: *mut libc::c_void); + pub fn CommandLineToArgvW(lpCmdLine: *mut libc::LPCWSTR, + pNumArgs: *mut libc::c_int) -> *mut *mut u16; + pub fn SetFileTime(hFile: libc::HANDLE, + lpCreationTime: *const libc::FILETIME, + lpLastAccessTime: *const libc::FILETIME, + lpLastWriteTime: *const libc::FILETIME) -> libc::BOOL; + pub fn SetFileInformationByHandle(hFile: libc::HANDLE, + FileInformationClass: FILE_INFO_BY_HANDLE_CLASS, + lpFileInformation: libc::LPVOID, + dwBufferSize: libc::DWORD) -> libc::BOOL; + pub fn GetTempPathW(nBufferLength: libc::DWORD, + lpBuffer: libc::LPCWSTR) -> libc::DWORD; + pub fn OpenProcessToken(ProcessHandle: libc::HANDLE, + DesiredAccess: libc::DWORD, + TokenHandle: *mut libc::HANDLE) -> libc::BOOL; + pub fn GetCurrentProcess() -> libc::HANDLE; +} + +#[link(name = "userenv")] +extern "system" { + pub fn GetUserProfileDirectoryW(hToken: libc::HANDLE, + lpProfileDir: libc::LPCWSTR, + lpcchSize: *mut libc::DWORD) -> libc::BOOL; } diff --git a/src/libstd/sys/windows/fs.rs b/src/libstd/sys/windows/fs.rs index 80cdf9782f20f..304d7e0153279 100644 --- a/src/libstd/sys/windows/fs.rs +++ b/src/libstd/sys/windows/fs.rs @@ -10,20 +10,15 @@ //! Blocking Windows-based file I/O -use alloc::arc::Arc; use libc::{self, c_int}; use mem; -use sys::os::fill_utf16_buf_and_decode; -use path; use ptr; -use str; use old_io; use prelude::v1::*; use sys; -use sys::os; -use sys_common::{keep_going, eof, mkerr_libc}; +use sys_common::{mkerr_libc}; use old_io::{FilePermission, Write, UnstableFileStat, Open, FileAccess, FileMode}; use old_io::{IoResult, IoError, FileStat, SeekStyle}; @@ -262,7 +257,7 @@ pub fn readdir(p: &Path) -> IoResult> { let mut more_files = 1 as libc::BOOL; while more_files != 0 { { - let filename = os::truncate_utf16_at_nul(&wfd.cFileName); + let filename = super::truncate_utf16_at_nul(&wfd.cFileName); match String::from_utf16(filename) { Ok(filename) => paths.push(Path::new(filename)), Err(..) => { @@ -368,19 +363,12 @@ pub fn readlink(p: &Path) -> IoResult { } // Specify (sz - 1) because the documentation states that it's the size // without the null pointer - let ret = fill_utf16_buf_and_decode(|buf, sz| unsafe { + let ret = super::fill_utf16_buf(|buf, sz| unsafe { GetFinalPathNameByHandleW(handle, buf as *const u16, sz - 1, libc::VOLUME_NAME_DOS) - }); - let ret = match ret { - Some(ref s) if s.starts_with(r"\\?\") => { // " - Ok(Path::new(&s[4..])) - } - Some(s) => Ok(Path::new(s)), - None => Err(super::last_error()), - }; + }, super::os2path); assert!(unsafe { libc::CloseHandle(handle) } != 0); return ret; } diff --git a/src/libstd/sys/windows/handle.rs b/src/libstd/sys/windows/handle.rs new file mode 100644 index 0000000000000..6737eeef12532 --- /dev/null +++ b/src/libstd/sys/windows/handle.rs @@ -0,0 +1,31 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use prelude::v1::*; + +use libc::{self, HANDLE}; + +pub struct Handle(HANDLE); + +unsafe impl Send for Handle {} +unsafe impl Sync for Handle {} + +impl Handle { + pub fn new(handle: HANDLE) -> Handle { + Handle(handle) + } +} + +impl Drop for Handle { + fn drop(&mut self) { + unsafe { let _ = libc::CloseHandle(self.0); } + } +} + diff --git a/src/libstd/sys/windows/mod.rs b/src/libstd/sys/windows/mod.rs index e8b65c9b64ea3..8dd467eba9e2a 100644 --- a/src/libstd/sys/windows/mod.rs +++ b/src/libstd/sys/windows/mod.rs @@ -11,18 +11,14 @@ #![allow(missing_docs)] #![allow(non_camel_case_types)] #![allow(non_snake_case)] -#![allow(unused_imports)] -#![allow(dead_code)] -#![allow(unused_unsafe)] -#![allow(unused_mut)] - -extern crate libc; use prelude::v1::*; -use num; +use ffi::OsStr; +use libc; use mem; use old_io::{self, IoResult, IoError}; +use os::windows::OsStrExt; use sync::{Once, ONCE_INIT}; macro_rules! helper_init { (static $name:ident: Helper<$m:ty>) => ( @@ -38,9 +34,10 @@ macro_rules! helper_init { (static $name:ident: Helper<$m:ty>) => ( pub mod backtrace; pub mod c; -pub mod ext; pub mod condvar; +pub mod ext; pub mod fs; +pub mod handle; pub mod helper_signal; pub mod mutex; pub mod os; @@ -48,12 +45,12 @@ pub mod os_str; pub mod pipe; pub mod process; pub mod rwlock; -pub mod sync; pub mod stack_overflow; +pub mod sync; pub mod tcp; -pub mod time; pub mod thread; pub mod thread_local; +pub mod time; pub mod timer; pub mod tty; pub mod udp; @@ -158,7 +155,7 @@ pub fn ms_to_timeval(ms: u64) -> libc::timeval { pub fn wouldblock() -> bool { let err = os::errno(); - err == libc::WSAEWOULDBLOCK as uint + err == libc::WSAEWOULDBLOCK as i32 } pub fn set_nonblocking(fd: sock_t, nb: bool) -> IoResult<()> { @@ -191,17 +188,93 @@ pub fn unimpl() -> IoError { } } -pub fn to_utf16(s: Option<&str>) -> IoResult> { +fn to_utf16(s: Option<&str>) -> IoResult> { match s { - Some(s) => Ok({ - let mut s = s.utf16_units().collect::>(); - s.push(0); - s - }), + Some(s) => Ok(to_utf16_os(OsStr::from_str(s))), None => Err(IoError { kind: old_io::InvalidInput, desc: "valid unicode input required", - detail: None - }) + detail: None, + }), + } +} + +fn to_utf16_os(s: &OsStr) -> Vec { + let mut v: Vec<_> = s.encode_wide().collect(); + v.push(0); + v +} + +// Many Windows APIs follow a pattern of where we hand the a buffer and then +// they will report back to us how large the buffer should be or how many bytes +// currently reside in the buffer. This function is an abstraction over these +// functions by making them easier to call. +// +// The first callback, `f1`, is yielded a (pointer, len) pair which can be +// passed to a syscall. The `ptr` is valid for `len` items (u16 in this case). +// The closure is expected to return what the syscall returns which will be +// interpreted by this function to determine if the syscall needs to be invoked +// again (with more buffer space). +// +// Once the syscall has completed (errors bail out early) the second closure is +// yielded the data which has been read from the syscall. The return value +// from this closure is then the return value of the function. +fn fill_utf16_buf(mut f1: F1, f2: F2) -> IoResult + where F1: FnMut(*mut u16, libc::DWORD) -> libc::DWORD, + F2: FnOnce(&[u16]) -> T +{ + // Start off with a stack buf but then spill over to the heap if we end up + // needing more space. + let mut stack_buf = [0u16; 512]; + let mut heap_buf = Vec::new(); + unsafe { + let mut n = stack_buf.len(); + loop { + let buf = if n <= stack_buf.len() { + &mut stack_buf[] + } else { + let extra = n - heap_buf.len(); + heap_buf.reserve(extra); + heap_buf.set_len(n); + &mut heap_buf[] + }; + + // This function is typically called on windows API functions which + // will return the correct length of the string, but these functions + // also return the `0` on error. In some cases, however, the + // returned "correct length" may actually be 0! + // + // To handle this case we call `SetLastError` to reset it to 0 and + // then check it again if we get the "0 error value". If the "last + // error" is still 0 then we interpret it as a 0 length buffer and + // not an actual error. + c::SetLastError(0); + let k = match f1(buf.as_mut_ptr(), n as libc::DWORD) { + 0 if libc::GetLastError() == 0 => 0, + 0 => return Err(IoError::last_error()), + n => n, + } as usize; + if k == n && libc::GetLastError() == + libc::ERROR_INSUFFICIENT_BUFFER as libc::DWORD { + n *= 2; + } else if k >= n { + n = k; + } else { + return Ok(f2(&buf[..k])) + } + } + } +} + +fn os2path(s: &[u16]) -> Path { + // FIXME: this should not be a panicking conversion (aka path reform) + Path::new(String::from_utf16(s).unwrap()) +} + +pub fn truncate_utf16_at_nul<'a>(v: &'a [u16]) -> &'a [u16] { + match v.iter().position(|c| *c == 0) { + // don't include the 0 + Some(i) => &v[..i], + None => v } } diff --git a/src/libstd/sys/windows/mutex.rs b/src/libstd/sys/windows/mutex.rs index 828ad795ed3b4..75495efc7cbb6 100644 --- a/src/libstd/sys/windows/mutex.rs +++ b/src/libstd/sys/windows/mutex.rs @@ -37,8 +37,6 @@ pub unsafe fn raw(m: &Mutex) -> ffi::PSRWLOCK { // no guarantees of fairness. impl Mutex { - #[inline] - pub unsafe fn new() -> Mutex { MUTEX_INIT } #[inline] pub unsafe fn lock(&self) { ffi::AcquireSRWLockExclusive(self.inner.get()) diff --git a/src/libstd/sys/windows/os.rs b/src/libstd/sys/windows/os.rs index a82259ad5ec57..c71e2d057c351 100644 --- a/src/libstd/sys/windows/os.rs +++ b/src/libstd/sys/windows/os.rs @@ -10,48 +10,32 @@ //! Implementation of `std::os` functionality for Windows -// FIXME: move various extern bindings from here into liblibc or -// something similar +#![allow(bad_style)] use prelude::v1::*; +use os::windows::*; +use error::Error as StdError; +use ffi::{OsString, OsStr, AsOsStr}; use fmt; -use old_io::{IoResult, IoError}; -use iter::repeat; -use libc::{c_int, c_void}; -use libc; -use os; -use path::BytesContainer; +use iter::Range; +use libc::types::os::arch::extra::LPWCH; +use libc::{self, c_int, c_void}; +use mem; +use old_io::{IoError, IoResult}; use ptr; use slice; +use sys::c; use sys::fs::FileDesc; +use sys::handle::Handle as RawHandle; -use os::TMPBUF_SZ; -use libc::types::os::arch::extra::DWORD; +use libc::funcs::extra::kernel32::{ + GetEnvironmentStringsW, + FreeEnvironmentStringsW +}; -const BUF_BYTES : uint = 2048u; - -/// Return a slice of `v` ending at (and not including) the first NUL -/// (0). -pub fn truncate_utf16_at_nul<'a>(v: &'a [u16]) -> &'a [u16] { - match v.iter().position(|c| *c == 0) { - // don't include the 0 - Some(i) => &v[..i], - None => v - } -} - -pub fn errno() -> uint { - use libc::types::os::arch::extra::DWORD; - - #[link_name = "kernel32"] - extern "system" { - fn GetLastError() -> DWORD; - } - - unsafe { - GetLastError() as uint - } +pub fn errno() -> i32 { + unsafe { libc::GetLastError() as i32 } } /// Get a detailed string description for the given error number @@ -80,7 +64,7 @@ pub fn error_string(errnum: i32) -> String { // MAKELANGID(LANG_SYSTEM_DEFAULT, SUBLANG_SYS_DEFAULT) let langId = 0x0800 as DWORD; - let mut buf = [0 as WCHAR; TMPBUF_SZ]; + let mut buf = [0 as WCHAR; 2048]; unsafe { let res = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | @@ -94,200 +78,170 @@ pub fn error_string(errnum: i32) -> String { if res == 0 { // Sometimes FormatMessageW can fail e.g. system doesn't like langId, let fm_err = errno(); - return format!("OS Error {} (FormatMessageW() returned error {})", errnum, fm_err); + return format!("OS Error {} (FormatMessageW() returned error {})", + errnum, fm_err); } - let msg = String::from_utf16(truncate_utf16_at_nul(&buf)); + let b = buf.iter().position(|&b| b == 0).unwrap_or(buf.len()); + let msg = String::from_utf16(&buf[..b]); match msg { - Ok(msg) => format!("OS Error {}: {}", errnum, msg), + Ok(msg) => msg, Err(..) => format!("OS Error {} (FormatMessageW() returned \ invalid UTF-16)", errnum), } } } -pub unsafe fn pipe() -> IoResult<(FileDesc, FileDesc)> { - // Windows pipes work subtly differently than unix pipes, and their - // inheritance has to be handled in a different way that I do not - // fully understand. Here we explicitly make the pipe non-inheritable, - // which means to pass it to a subprocess they need to be duplicated - // first, as in std::run. - let mut fds = [0; 2]; - match libc::pipe(fds.as_mut_ptr(), 1024 as ::libc::c_uint, - (libc::O_BINARY | libc::O_NOINHERIT) as c_int) { - 0 => { - assert!(fds[0] != -1 && fds[0] != 0); - assert!(fds[1] != -1 && fds[1] != 0); - Ok((FileDesc::new(fds[0], true), FileDesc::new(fds[1], true))) - } - _ => Err(IoError::last_error()), - } +pub struct Env { + base: LPWCH, + cur: LPWCH, } -pub fn fill_utf16_buf_and_decode(mut f: F) -> Option where - F: FnMut(*mut u16, DWORD) -> DWORD, -{ - unsafe { - let mut n = TMPBUF_SZ as DWORD; - let mut res = None; - let mut done = false; - while !done { - let mut buf: Vec = repeat(0u16).take(n as uint).collect(); - let k = f(buf.as_mut_ptr(), n); - if k == (0 as DWORD) { - done = true; - } else if k == n && - libc::GetLastError() == - libc::ERROR_INSUFFICIENT_BUFFER as DWORD { - n *= 2 as DWORD; - } else if k >= n { - n = k; - } else { - done = true; - } - if k != 0 && done { - let sub = &buf[.. (k as uint)]; - // We want to explicitly catch the case when the - // closure returned invalid UTF-16, rather than - // set `res` to None and continue. - let s = String::from_utf16(sub).ok() - .expect("fill_utf16_buf_and_decode: closure created invalid UTF-16"); - res = Some(s) +impl Iterator for Env { + type Item = (OsString, OsString); + + fn next(&mut self) -> Option<(OsString, OsString)> { + unsafe { + if *self.cur == 0 { return None } + let p = &*self.cur; + let mut len = 0; + while *(p as *const _).offset(len) != 0 { + len += 1; } + let p = p as *const u16; + let s = slice::from_raw_buf(&p, len as usize); + self.cur = self.cur.offset(len + 1); + + let (k, v) = match s.iter().position(|&b| b == '=' as u16) { + Some(n) => (&s[..n], &s[n+1..]), + None => (s, &[][]), + }; + Some((OsStringExt::from_wide(k), OsStringExt::from_wide(v))) } - return res; } } -pub fn getcwd() -> IoResult { - use libc::DWORD; - use libc::GetCurrentDirectoryW; - use old_io::OtherIoError; +impl Drop for Env { + fn drop(&mut self) { + unsafe { FreeEnvironmentStringsW(self.base); } + } +} - let mut buf = [0 as u16; BUF_BYTES]; +pub fn env() -> Env { unsafe { - if libc::GetCurrentDirectoryW(buf.len() as DWORD, buf.as_mut_ptr()) == 0 as DWORD { - return Err(IoError::last_error()); + let ch = GetEnvironmentStringsW(); + if ch as usize == 0 { + panic!("failure getting env string from OS: {}", + IoError::last_error()); } + Env { base: ch, cur: ch } } +} - match String::from_utf16(truncate_utf16_at_nul(&buf)) { - Ok(ref cwd) => Ok(Path::new(cwd)), - Err(..) => Err(IoError { - kind: OtherIoError, - desc: "GetCurrentDirectoryW returned invalid UTF-16", - detail: None, - }), - } +pub struct SplitPaths<'a> { + data: EncodeWide<'a>, + must_yield: bool, } -pub unsafe fn get_env_pairs() -> Vec> { - use libc::funcs::extra::kernel32::{ - GetEnvironmentStringsW, - FreeEnvironmentStringsW - }; - let ch = GetEnvironmentStringsW(); - if ch as uint == 0 { - panic!("os::env() failure getting env string from OS: {}", - os::last_os_error()); +pub fn split_paths(unparsed: &OsStr) -> SplitPaths { + SplitPaths { + data: unparsed.encode_wide(), + must_yield: true, } - // Here, we lossily decode the string as UTF16. - // - // The docs suggest that the result should be in Unicode, but - // Windows doesn't guarantee it's actually UTF16 -- it doesn't - // validate the environment string passed to CreateProcess nor - // SetEnvironmentVariable. Yet, it's unlikely that returning a - // raw u16 buffer would be of practical use since the result would - // be inherently platform-dependent and introduce additional - // complexity to this code. - // - // Using the non-Unicode version of GetEnvironmentStrings is even - // worse since the result is in an OEM code page. Characters that - // can't be encoded in the code page would be turned into question - // marks. - let mut result = Vec::new(); - let mut i = 0; - while *ch.offset(i) != 0 { - let p = &*ch.offset(i); - let mut len = 0; - while *(p as *const _).offset(len) != 0 { - len += 1; - } - let p = p as *const u16; - let s = slice::from_raw_buf(&p, len as uint); - result.push(String::from_utf16_lossy(s).into_bytes()); - i += len as int + 1; - } - FreeEnvironmentStringsW(ch); - result } -pub fn split_paths(unparsed: &[u8]) -> Vec { - // 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.iter() { - match *b { - b';' if !in_quote => { - parsed.push(Path::new(in_progress.as_slice())); - in_progress.truncate(0) - } - b'"' => { +impl<'a> Iterator for SplitPaths<'a> { + type Item = Path; + fn next(&mut self) -> Option { + // 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 must_yield = self.must_yield; + self.must_yield = false; + + let mut in_progress = Vec::new(); + let mut in_quote = false; + for b in self.data.by_ref() { + if b == '"' as u16 { in_quote = !in_quote; + } else if b == ';' as u16 && !in_quote { + self.must_yield = true; + break + } else { + in_progress.push(b) } - _ => { - in_progress.push(*b); - } + } + + if !must_yield && in_progress.is_empty() { + None + } else { + Some(super::os2path(&in_progress[])) } } - parsed.push(Path::new(in_progress)); - parsed } -pub fn join_paths(paths: &[T]) -> Result, &'static str> { +#[derive(Show)] +pub struct JoinPathsError; + +pub fn join_paths(paths: I) -> Result + where I: Iterator, T: AsOsStr +{ let mut joined = Vec::new(); - let sep = b';'; + let sep = b';' as u16; - for (i, path) in paths.iter().map(|p| p.container_as_bytes()).enumerate() { + for (i, path) in paths.enumerate() { + let path = path.as_os_str(); 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'"'); + let v = path.encode_wide().collect::>(); + if v.contains(&(b'"' as u16)) { + return Err(JoinPathsError) + } else if v.contains(&sep) { + joined.push(b'"' as u16); + joined.push_all(&v[]); + joined.push(b'"' as u16); } else { - joined.push_all(path); + joined.push_all(&v[]); } } - Ok(joined) + Ok(OsStringExt::from_wide(&joined[])) } -pub fn load_self() -> Option> { - unsafe { - fill_utf16_buf_and_decode(|buf, sz| { - libc::GetModuleFileNameW(ptr::null_mut(), buf, sz) - }).map(|s| s.to_string().into_bytes()) +impl fmt::Display for JoinPathsError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + "path segment contains `\"`".fmt(f) } } +impl StdError for JoinPathsError { + fn description(&self) -> &str { "failed to join paths" } +} + +pub fn current_exe() -> IoResult { + super::fill_utf16_buf(|buf, sz| unsafe { + libc::GetModuleFileNameW(ptr::null_mut(), buf, sz) + }, super::os2path) +} + +pub fn getcwd() -> IoResult { + super::fill_utf16_buf(|buf, sz| unsafe { + libc::GetCurrentDirectoryW(sz, buf) + }, super::os2path) +} + pub fn chdir(p: &Path) -> IoResult<()> { - let mut p = p.as_str().unwrap().utf16_units().collect::>(); + let mut p = p.as_os_str().encode_wide().collect::>(); p.push(0); unsafe { @@ -298,39 +252,124 @@ pub fn chdir(p: &Path) -> IoResult<()> { } } -pub fn page_size() -> uint { - use mem; +pub fn getenv(k: &OsStr) -> Option { + let k = super::to_utf16_os(k); + super::fill_utf16_buf(|buf, sz| unsafe { + libc::GetEnvironmentVariableW(k.as_ptr(), buf, sz) + }, |buf| { + OsStringExt::from_wide(buf) + }).ok() +} + +pub fn setenv(k: &OsStr, v: &OsStr) { + let k = super::to_utf16_os(k); + let v = super::to_utf16_os(v); + unsafe { - let mut info = mem::zeroed(); - libc::GetSystemInfo(&mut info); + if libc::SetEnvironmentVariableW(k.as_ptr(), v.as_ptr()) == 0 { + panic!("failed to set env: {}", IoError::last_error()); + } + } +} - return info.dwPageSize as uint; +pub fn unsetenv(n: &OsStr) { + let v = super::to_utf16_os(n); + unsafe { + if libc::SetEnvironmentVariableW(v.as_ptr(), ptr::null()) == 0 { + panic!("failed to unset env: {}", IoError::last_error()); + } } } -#[cfg(test)] -mod tests { - use super::truncate_utf16_at_nul; +pub struct Args { + range: Range, + cur: *mut *mut u16, +} - #[test] - fn test_truncate_utf16_at_nul() { - let v = []; - let b: &[u16] = &[]; - assert_eq!(truncate_utf16_at_nul(&v), b); +impl Iterator for Args { + type Item = OsString; + fn next(&mut self) -> Option { + self.range.next().map(|i| unsafe { + let ptr = *self.cur.offset(i); + let mut len = 0; + while *ptr.offset(len) != 0 { len += 1; } + + // Push it onto the list. + let ptr = ptr as *const u16; + let buf = slice::from_raw_buf(&ptr, len as usize); + OsStringExt::from_wide(buf) + }) + } + fn size_hint(&self) -> (usize, Option) { self.range.size_hint() } +} - let v = [0, 2, 3]; - assert_eq!(truncate_utf16_at_nul(&v), b); +impl Drop for Args { + fn drop(&mut self) { + unsafe { c::LocalFree(self.cur as *mut c_void); } + } +} - let v = [1, 0, 3]; - let b: &[u16] = &[1]; - assert_eq!(truncate_utf16_at_nul(&v), b); +pub fn args() -> Args { + unsafe { + let mut nArgs: c_int = 0; + let lpCmdLine = c::GetCommandLineW(); + let szArgList = c::CommandLineToArgvW(lpCmdLine, &mut nArgs); - let v = [1, 2, 0]; - let b: &[u16] = &[1, 2]; - assert_eq!(truncate_utf16_at_nul(&v), b); + Args { cur: szArgList, range: range(0, nArgs as isize) } + } +} - let v = [1, 2, 3]; - let b: &[u16] = &[1, 2, 3]; - assert_eq!(truncate_utf16_at_nul(&v), b); +pub fn page_size() -> usize { + unsafe { + let mut info = mem::zeroed(); + libc::GetSystemInfo(&mut info); + return info.dwPageSize as usize; + } +} + +pub unsafe fn pipe() -> IoResult<(FileDesc, FileDesc)> { + // Windows pipes work subtly differently than unix pipes, and their + // inheritance has to be handled in a different way that I do not + // fully understand. Here we explicitly make the pipe non-inheritable, + // which means to pass it to a subprocess they need to be duplicated + // first, as in std::run. + let mut fds = [0; 2]; + match libc::pipe(fds.as_mut_ptr(), 1024 as ::libc::c_uint, + (libc::O_BINARY | libc::O_NOINHERIT) as c_int) { + 0 => { + assert!(fds[0] != -1 && fds[0] != 0); + assert!(fds[1] != -1 && fds[1] != 0); + Ok((FileDesc::new(fds[0], true), FileDesc::new(fds[1], true))) + } + _ => Err(IoError::last_error()), } } + +pub fn temp_dir() -> Path { + super::fill_utf16_buf(|buf, sz| unsafe { + c::GetTempPathW(sz, buf) + }, super::os2path).unwrap() +} + +pub fn home_dir() -> Option { + getenv("HOME".as_os_str()).or_else(|| { + getenv("USERPROFILE".as_os_str()) + }).map(|os| { + // FIXME: OsString => Path + Path::new(os.to_str().unwrap()) + }).or_else(|| unsafe { + let me = c::GetCurrentProcess(); + let mut token = ptr::null_mut(); + if c::OpenProcessToken(me, c::TOKEN_READ, &mut token) == 0 { + return None + } + let _handle = RawHandle::new(token); + super::fill_utf16_buf(|buf, mut sz| { + match c::GetUserProfileDirectoryW(token, buf, &mut sz) { + 0 if libc::GetLastError() != 0 => 0, + 0 => sz, + n => n as libc::DWORD, + } + }, super::os2path).ok() + }) +} diff --git a/src/libstd/sys/windows/pipe.rs b/src/libstd/sys/windows/pipe.rs index 0bc2a827272e6..1f228b7d32e35 100644 --- a/src/libstd/sys/windows/pipe.rs +++ b/src/libstd/sys/windows/pipe.rs @@ -352,7 +352,7 @@ impl UnixStream { fn cancel_io(&self) -> IoResult<()> { match unsafe { c::CancelIoEx(self.handle(), ptr::null_mut()) } { - 0 if os::errno() == libc::ERROR_NOT_FOUND as uint => { + 0 if os::errno() == libc::ERROR_NOT_FOUND as i32 => { Ok(()) } 0 => Err(super::last_error()), @@ -374,7 +374,7 @@ impl UnixStream { // acquire the lock. // // See comments in close_read() about why this lock is necessary. - let guard = unsafe { self.inner.lock.lock() }; + let guard = self.inner.lock.lock(); if self.read_closed() { return Err(eof()) } @@ -392,7 +392,7 @@ impl UnixStream { // If our errno doesn't say that the I/O is pending, then we hit some // legitimate error and return immediately. - if os::errno() != libc::ERROR_IO_PENDING as uint { + if os::errno() != libc::ERROR_IO_PENDING as i32 { return Err(super::last_error()) } @@ -417,7 +417,7 @@ impl UnixStream { // If we succeeded, or we failed for some reason other than // CancelIoEx, return immediately if ret != 0 { return Ok(bytes_read as uint) } - if os::errno() != libc::ERROR_OPERATION_ABORTED as uint { + if os::errno() != libc::ERROR_OPERATION_ABORTED as i32 { return Err(super::last_error()) } @@ -450,7 +450,7 @@ impl UnixStream { // going after we woke up. // // See comments in close_read() about why this lock is necessary. - let guard = unsafe { self.inner.lock.lock() }; + let guard = self.inner.lock.lock(); if self.write_closed() { return Err(epipe()) } @@ -465,7 +465,7 @@ impl UnixStream { drop(guard); if ret == 0 { - if err != libc::ERROR_IO_PENDING as uint { + if err != libc::ERROR_IO_PENDING as i32 { return Err(decode_error_detailed(err as i32)) } // Process a timeout if one is pending @@ -481,7 +481,7 @@ impl UnixStream { // aborted, then check to see if the write half was actually // closed or whether we woke up from the read half closing. if ret == 0 { - if os::errno() != libc::ERROR_OPERATION_ABORTED as uint { + if os::errno() != libc::ERROR_OPERATION_ABORTED as i32 { return Err(super::last_error()) } if !wait_succeeded.is_ok() { @@ -525,14 +525,14 @@ impl UnixStream { // close_read() between steps 1 and 2. By atomically executing steps 1 // and 2 with a lock with respect to close_read(), we're guaranteed that // no thread will erroneously sit in a read forever. - let _guard = unsafe { self.inner.lock.lock() }; + let _guard = self.inner.lock.lock(); self.inner.read_closed.store(true, Ordering::SeqCst); self.cancel_io() } pub fn close_write(&mut self) -> IoResult<()> { // see comments in close_read() for why this lock is necessary - let _guard = unsafe { self.inner.lock.lock() }; + let _guard = self.inner.lock.lock(); self.inner.write_closed.store(true, Ordering::SeqCst); self.cancel_io() } diff --git a/src/libstd/sys/windows/process.rs b/src/libstd/sys/windows/process.rs index 3d66718d00ba0..043fdb38805dc 100644 --- a/src/libstd/sys/windows/process.rs +++ b/src/libstd/sys/windows/process.rs @@ -10,28 +10,27 @@ use prelude::v1::*; +use collections::hash_map::Hasher; use collections; +use env; use ffi::CString; use hash::Hash; -use collections::hash_map::Hasher; +use libc::{pid_t, c_void}; +use libc; +use mem; use old_io::fs::PathExtensions; -use old_io::process::{ProcessExit, ExitStatus, ExitSignal}; +use old_io::process::{ProcessExit, ExitStatus}; use old_io::{IoResult, IoError}; use old_io; -use libc::{pid_t, c_void, c_int}; -use libc; -use mem; use os; use path::BytesContainer; use ptr; use str; -use sys::fs::FileDesc; use sync::{StaticMutex, MUTEX_INIT}; +use sys::fs::FileDesc; -use sys::fs; -use sys::{self, retry, c, wouldblock, set_nonblocking, ms_to_timeval, timer}; -use sys_common::helper_thread::Helper; -use sys_common::{AsInner, mkerr_libc, timeout}; +use sys::timer; +use sys_common::{AsInner, timeout}; pub use sys_common::ProcessConfig; @@ -106,6 +105,7 @@ impl Process { return ret; } + #[allow(deprecated)] pub fn spawn(cfg: &C, in_fd: Option

, out_fd: Option

, err_fd: Option

) -> IoResult @@ -128,7 +128,7 @@ impl Process { use libc::funcs::extra::msvcrt::get_osfhandle; use mem; - use iter::{Iterator, IteratorExt}; + use iter::IteratorExt; use str::StrExt; if cfg.gid().is_some() || cfg.uid().is_some() { @@ -149,7 +149,7 @@ impl Process { // program exists. for path in os::split_paths(v.container_as_bytes()).into_iter() { let path = path.join(cfg.program().as_bytes()) - .with_extension(os::consts::EXE_EXTENSION); + .with_extension(env::consts::EXE_EXTENSION); if path.exists() { return Some(CString::from_slice(path.as_vec())) } diff --git a/src/libstd/sys/windows/rwlock.rs b/src/libstd/sys/windows/rwlock.rs index 88ce85c39f625..76fe352ed7717 100644 --- a/src/libstd/sys/windows/rwlock.rs +++ b/src/libstd/sys/windows/rwlock.rs @@ -18,9 +18,6 @@ pub const RWLOCK_INIT: RWLock = RWLock { }; impl RWLock { - #[inline] - pub unsafe fn new() -> RWLock { RWLOCK_INIT } - #[inline] pub unsafe fn read(&self) { ffi::AcquireSRWLockShared(self.inner.get()) diff --git a/src/libstd/sys/windows/stack_overflow.rs b/src/libstd/sys/windows/stack_overflow.rs index 0cb4c573ae3ef..b0410701ee101 100644 --- a/src/libstd/sys/windows/stack_overflow.rs +++ b/src/libstd/sys/windows/stack_overflow.rs @@ -14,7 +14,7 @@ use ptr; use mem; use libc; use libc::types::os::arch::extra::{LPVOID, DWORD, LONG, BOOL}; -use sys_common::{stack, thread_info}; +use sys_common::stack; pub struct Handler { _data: *mut libc::c_void @@ -30,14 +30,6 @@ impl Drop for Handler { fn drop(&mut self) {} } -// get_task_info is called from an exception / signal handler. -// It returns the guard page of the current task or 0 if that -// guard page doesn't exist. None is returned if there's currently -// no local task. -unsafe fn get_task_guard_page() -> uint { - thread_info::stack_guard() -} - // This is initialized in init() and only read from after static mut PAGE_SIZE: uint = 0; diff --git a/src/libstd/sys/windows/sync.rs b/src/libstd/sys/windows/sync.rs index d60646b7db915..7614104c98bf3 100644 --- a/src/libstd/sys/windows/sync.rs +++ b/src/libstd/sys/windows/sync.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use libc::{BOOL, DWORD, c_void, LPVOID, c_ulong}; +use libc::{BOOL, DWORD, LPVOID, c_ulong}; use libc::types::os::arch::extra::BOOLEAN; pub type PCONDITION_VARIABLE = *mut CONDITION_VARIABLE; diff --git a/src/libstd/sys/windows/tcp.rs b/src/libstd/sys/windows/tcp.rs index 64e440331c127..4804ca510cb0b 100644 --- a/src/libstd/sys/windows/tcp.rs +++ b/src/libstd/sys/windows/tcp.rs @@ -14,12 +14,11 @@ use libc; use mem; use ptr; use prelude::v1::*; -use super::{last_error, last_net_error, retry, sock_t}; +use super::{last_error, last_net_error, sock_t}; use sync::Arc; use sync::atomic::{AtomicBool, Ordering}; -use sys::fs::FileDesc; use sys::{self, c, set_nonblocking, wouldblock, timer}; -use sys_common::{self, timeout, eof, net}; +use sys_common::{timeout, eof, net}; pub use sys_common::net::TcpStream; @@ -202,10 +201,6 @@ impl TcpAcceptor { Err(eof()) } - pub fn socket_name(&mut self) -> IoResult { - net::sockname(self.socket(), libc::getsockname) - } - pub fn set_timeout(&mut self, timeout: Option) { self.deadline = timeout.map(|a| timer::now() + a).unwrap_or(0); } diff --git a/src/libstd/sys/windows/thread.rs b/src/libstd/sys/windows/thread.rs index a94adcb3bc7b9..a38dc9b2d3407 100644 --- a/src/libstd/sys/windows/thread.rs +++ b/src/libstd/sys/windows/thread.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use core::prelude::*; - use boxed::Box; use cmp; use mem; diff --git a/src/libstd/sys/windows/thread_local.rs b/src/libstd/sys/windows/thread_local.rs index 0f8ceed39a6cc..cc731738afe3e 100644 --- a/src/libstd/sys/windows/thread_local.rs +++ b/src/libstd/sys/windows/thread_local.rs @@ -233,6 +233,7 @@ unsafe extern "system" fn on_tls_callback(h: LPVOID, } } +#[allow(dead_code)] // actually called above unsafe fn run_dtors() { let mut any_run = true; for _ in 0..5 { diff --git a/src/libstd/sys/windows/timer.rs b/src/libstd/sys/windows/timer.rs index 34f3c418c558d..d156dd801f974 100644 --- a/src/libstd/sys/windows/timer.rs +++ b/src/libstd/sys/windows/timer.rs @@ -28,8 +28,6 @@ use ptr; use old_io::IoResult; use sync::mpsc::{channel, Sender, Receiver, TryRecvError}; -use sys::c; -use sys::fs::FileDesc; use sys_common::helper_thread::Helper; helper_init! { static HELPER: Helper } diff --git a/src/libstd/sys/windows/tty.rs b/src/libstd/sys/windows/tty.rs index 6ecabfa985304..f02c8e49f4188 100644 --- a/src/libstd/sys/windows/tty.rs +++ b/src/libstd/sys/windows/tty.rs @@ -38,7 +38,7 @@ use str::from_utf8; use super::c::{ENABLE_ECHO_INPUT, ENABLE_EXTENDED_FLAGS}; use super::c::{ENABLE_INSERT_MODE, ENABLE_LINE_INPUT}; use super::c::{ENABLE_PROCESSED_INPUT, ENABLE_QUICK_EDIT_MODE}; -use super::c::{ERROR_ILLEGAL_CHARACTER, CONSOLE_SCREEN_BUFFER_INFO}; +use super::c::{CONSOLE_SCREEN_BUFFER_INFO}; use super::c::{ReadConsoleW, WriteConsoleW, GetConsoleMode, SetConsoleMode}; use super::c::{GetConsoleScreenBufferInfo}; @@ -155,9 +155,6 @@ impl TTY { (info.srWindow.Bottom + 1 - info.srWindow.Top) as int)), } } - - // Let us magically declare this as a TTY - pub fn isatty(&self) -> bool { true } } impl Drop for TTY { diff --git a/src/libsyntax/ext/env.rs b/src/libsyntax/ext/env.rs index 9f6bf352b0404..9aa454ae8d5c9 100644 --- a/src/libsyntax/ext/env.rs +++ b/src/libsyntax/ext/env.rs @@ -21,7 +21,7 @@ use ext::base; use ext::build::AstBuilder; use parse::token; -use std::os; +use std::env; pub fn expand_option_env<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) -> Box { @@ -30,8 +30,8 @@ pub fn expand_option_env<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenT Some(v) => v }; - let e = match os::getenv(&var[]) { - None => { + let e = match env::var_string(&var[]) { + Err(..) => { cx.expr_path(cx.path_all(sp, true, vec!(cx.ident_of("std"), @@ -48,7 +48,7 @@ pub fn expand_option_env<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenT ast::MutImmutable)), Vec::new())) } - Some(s) => { + Ok(s) => { cx.expr_call_global(sp, vec!(cx.ident_of("std"), cx.ident_of("option"), @@ -101,12 +101,12 @@ pub fn expand_env<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) } } - let e = match os::getenv(var.get()) { - None => { + let e = match env::var_string(var.get()) { + Err(..) => { cx.span_err(sp, msg.get()); cx.expr_usize(sp, 0) } - Some(s) => cx.expr_str(sp, token::intern_and_get_ident(&s[])) + Ok(s) => cx.expr_str(sp, token::intern_and_get_ident(&s[])) }; MacExpr::new(e) } diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs index 73424136cfbcb..54b37483f8cfd 100644 --- a/src/libsyntax/lib.rs +++ b/src/libsyntax/lib.rs @@ -28,11 +28,11 @@ #![feature(box_syntax)] #![feature(collections)] #![feature(core)] +#![feature(env)] #![feature(hash)] #![feature(int_uint)] #![feature(io)] #![feature(libc)] -#![feature(os)] #![feature(path)] #![feature(quote, unsafe_destructor)] #![feature(rustc_private)] diff --git a/src/libterm/lib.rs b/src/libterm/lib.rs index 27a46fb5a682d..219c9baf4c892 100644 --- a/src/libterm/lib.rs +++ b/src/libterm/lib.rs @@ -55,13 +55,14 @@ #![feature(core)] #![feature(int_uint)] #![feature(io)] -#![feature(os)] #![feature(path)] #![feature(rustc_private)] #![feature(slicing_syntax)] #![feature(staged_api)] #![feature(std_misc)] #![feature(unicode)] +#![feature(env)] +#![cfg_attr(windows, feature(libc))] #[macro_use] extern crate log; diff --git a/src/libterm/terminfo/mod.rs b/src/libterm/terminfo/mod.rs index d126717ce6817..17c1e8cdb8c6d 100644 --- a/src/libterm/terminfo/mod.rs +++ b/src/libterm/terminfo/mod.rs @@ -12,7 +12,7 @@ use std::collections::HashMap; use std::old_io::IoResult; -use std::os; +use std::env; use attr; use color; @@ -172,9 +172,9 @@ impl TerminfoTerminal { /// Returns `None` whenever the terminal cannot be created for some /// reason. pub fn new(out: T) -> Option+Send+'static>> { - let term = match os::getenv("TERM") { - Some(t) => t, - None => { + let term = match env::var_string("TERM") { + Ok(t) => t, + Err(..) => { debug!("TERM environment variable not defined"); return None; } @@ -182,7 +182,7 @@ impl TerminfoTerminal { let entry = open(&term[]); if entry.is_err() { - if os::getenv("MSYSCON").map_or(false, |s| { + if env::var_string("MSYSCON").ok().map_or(false, |s| { "mintty.exe" == s }) { // msys terminal diff --git a/src/libterm/terminfo/searcher.rs b/src/libterm/terminfo/searcher.rs index 1e84c0462d61a..74c986cd9e262 100644 --- a/src/libterm/terminfo/searcher.rs +++ b/src/libterm/terminfo/searcher.rs @@ -14,8 +14,7 @@ use std::old_io::File; use std::old_io::fs::PathExtensions; -use std::os::getenv; -use std::os; +use std::env; /// Return path to database entry for `term` pub fn get_dbpath_for_term(term: &str) -> Option> { @@ -23,21 +22,21 @@ pub fn get_dbpath_for_term(term: &str) -> Option> { return None; } - let homedir = os::homedir(); + let homedir = env::home_dir(); let mut dirs_to_search = Vec::new(); let first_char = term.char_at(0); // Find search directory - match getenv("TERMINFO") { - Some(dir) => dirs_to_search.push(Path::new(dir)), - None => { + match env::var_string("TERMINFO") { + Ok(dir) => dirs_to_search.push(Path::new(dir)), + Err(..) => { if homedir.is_some() { // ncurses compatibility; dirs_to_search.push(homedir.unwrap().join(".terminfo")) } - match getenv("TERMINFO_DIRS") { - Some(dirs) => for i in dirs.split(':') { + match env::var_string("TERMINFO_DIRS") { + Ok(dirs) => for i in dirs.split(':') { if i == "" { dirs_to_search.push(Path::new("/usr/share/terminfo")); } else { @@ -48,7 +47,7 @@ pub fn get_dbpath_for_term(term: &str) -> Option> { // According to /etc/terminfo/README, after looking at // ~/.terminfo, ncurses will search /etc/terminfo, then // /lib/terminfo, and eventually /usr/share/terminfo. - None => { + Err(..) => { dirs_to_search.push(Path::new("/etc/terminfo")); dirs_to_search.push(Path::new("/lib/terminfo")); dirs_to_search.push(Path::new("/usr/share/terminfo")); diff --git a/src/libtest/lib.rs b/src/libtest/lib.rs index f22c58c54a685..2295f122822db 100644 --- a/src/libtest/lib.rs +++ b/src/libtest/lib.rs @@ -38,10 +38,10 @@ #![feature(box_syntax)] #![feature(collections)] #![feature(core)] +#![feature(env)] #![feature(hash)] #![feature(int_uint)] #![feature(io)] -#![feature(os)] #![feature(path)] #![feature(rustc_private)] #![feature(staged_api)] @@ -75,7 +75,7 @@ use std::old_io::{File, ChanReader, ChanWriter}; use std::old_io; use std::iter::repeat; use std::num::{Float, Int}; -use std::os; +use std::env; use std::sync::mpsc::{channel, Sender}; use std::thread::{self, Thread}; use std::thunk::{Thunk, Invoke}; @@ -388,7 +388,7 @@ pub fn parse_opts(args: &[String]) -> Option { let mut nocapture = matches.opt_present("nocapture"); if !nocapture { - nocapture = os::getenv("RUST_TEST_NOCAPTURE").is_some(); + nocapture = env::var("RUST_TEST_NOCAPTURE").is_some(); } let color = match matches.opt_str("color").as_ref().map(|s| s.as_slice()) { @@ -817,15 +817,15 @@ fn run_tests(opts: &TestOpts, fn get_concurrency() -> uint { use std::rt; - match os::getenv("RUST_TEST_TASKS") { - Some(s) => { + match env::var_string("RUST_TEST_TASKS") { + Ok(s) => { let opt_n: Option = s.parse().ok(); match opt_n { Some(n) if n > 0 => n, _ => panic!("RUST_TEST_TASKS is `{}`, should be a positive integer.", s) } } - None => { + Err(..) => { rt::default_sched_threads() } } diff --git a/src/test/bench/shootout-mandelbrot.rs b/src/test/bench/shootout-mandelbrot.rs index 4a9c5a91dcf0b..ac32b5848015d 100644 --- a/src/test/bench/shootout-mandelbrot.rs +++ b/src/test/bench/shootout-mandelbrot.rs @@ -48,12 +48,12 @@ use std::simd::f64x2; use std::sync::Arc; use std::thread::Thread; -const ITER: int = 50; +const ITER: usize = 50; const LIMIT: f64 = 2.0; -const WORKERS: uint = 16; +const WORKERS: usize = 16; #[inline(always)] -fn mandelbrot(w: uint, mut out: W) -> old_io::IoResult<()> { +fn mandelbrot(w: usize, mut out: W) -> old_io::IoResult<()> { assert!(WORKERS % 2 == 0); // Ensure w and h are multiples of 8. @@ -198,7 +198,6 @@ fn write_line(init_i: f64, vec_init_r: &[f64], res: &mut Vec) { fn main() { let args = os::args(); - let args = args.as_slice(); let res = if args.len() < 2 { println!("Test mode: do not dump the image because it's not utf8, \ which interferes with the test runner."); diff --git a/src/test/compile-fail/liveness-unused.rs b/src/test/compile-fail/liveness-unused.rs index ebcf46f7277d6..960d9ef0f2fb4 100644 --- a/src/test/compile-fail/liveness-unused.rs +++ b/src/test/compile-fail/liveness-unused.rs @@ -12,7 +12,6 @@ #![deny(unused_assignments)] #![allow(dead_code, non_camel_case_types)] #![feature(core)] -#![feature(os)] fn f1(x: isize) { //~^ ERROR unused variable: `x` @@ -98,7 +97,7 @@ fn f5c() { for (_, x) in [1is, 2, 3].iter().enumerate() { //~^ ERROR unused variable: `x` continue; - std::os::set_exit_status(*x); //~ WARNING unreachable statement + drop(*x as i32); //~ WARNING unreachable statement } } diff --git a/src/test/run-pass/env-home-dir.rs b/src/test/run-pass/env-home-dir.rs new file mode 100644 index 0000000000000..5623e98c92885 --- /dev/null +++ b/src/test/run-pass/env-home-dir.rs @@ -0,0 +1,45 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::env::*; + +#[cfg(unix)] +fn main() { + let oldhome = var("HOME"); + + set_var("HOME", "/home/MountainView"); + assert!(home_dir() == Some(Path::new("/home/MountainView"))); + + remove_var("HOME"); + assert!(home_dir().is_some()); +} + +#[cfg(windows)] +fn main() { + let oldhome = var("HOME"); + let olduserprofile = var("USERPROFILE"); + + remove_var("HOME"); + remove_var("USERPROFILE"); + + assert!(home_dir().is_some()); + + set_var("HOME", "/home/MountainView"); + assert!(home_dir() == Some(Path::new("/home/MountainView"))); + + remove_var("HOME"); + + set_var("USERPROFILE", "/home/MountainView"); + assert!(home_dir() == Some(Path::new("/home/MountainView"))); + + set_var("HOME", "/home/MountainView"); + set_var("USERPROFILE", "/home/PaloAlto"); + assert!(home_dir() == Some(Path::new("/home/MountainView"))); +} diff --git a/src/test/run-pass/env-vars.rs b/src/test/run-pass/env-vars.rs new file mode 100644 index 0000000000000..892041b964889 --- /dev/null +++ b/src/test/run-pass/env-vars.rs @@ -0,0 +1,22 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::env::*; + +fn main() { + for (k, v) in vars() { + let v2 = var(&k); + // MingW seems to set some funky environment variables like + // "=C:=C:\MinGW\msys\1.0\bin" and "!::=::\" that are returned + // from vars() but not visible from var(). + assert!(v2.is_none() || v2.as_ref().map(|s| &**s) == Some(&*v), + "bad vars->var transition: {:?} {:?} {:?}", k, v, v2); + } +}