about summary refs log tree commit diff
path: root/src/libstd/sys/unix/process/process_common.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/libstd/sys/unix/process/process_common.rs')
-rw-r--r--src/libstd/sys/unix/process/process_common.rs143
1 files changed, 60 insertions, 83 deletions
diff --git a/src/libstd/sys/unix/process/process_common.rs b/src/libstd/sys/unix/process/process_common.rs
index 383434b1cd8..c53bcdbf8e3 100644
--- a/src/libstd/sys/unix/process/process_common.rs
+++ b/src/libstd/sys/unix/process/process_common.rs
@@ -10,8 +10,6 @@
 
 use os::unix::prelude::*;
 
-use collections::hash_map::{HashMap, Entry};
-use env;
 use ffi::{OsString, OsStr, CString, CStr};
 use fmt;
 use io;
@@ -20,6 +18,8 @@ use ptr;
 use sys::fd::FileDesc;
 use sys::fs::{File, OpenOptions};
 use sys::pipe::{self, AnonPipe};
+use sys_common::process::{CommandEnv, DefaultEnvKey};
+use collections::BTreeMap;
 
 ////////////////////////////////////////////////////////////////////////////////
 // Command
@@ -45,9 +45,8 @@ pub struct Command {
     // other keys.
     program: CString,
     args: Vec<CString>,
-    env: Option<HashMap<OsString, (usize, CString)>>,
     argv: Vec<*const c_char>,
-    envp: Option<Vec<*const c_char>>,
+    env: CommandEnv<DefaultEnvKey>,
 
     cwd: Option<CString>,
     uid: Option<uid_t>,
@@ -96,8 +95,7 @@ impl Command {
             argv: vec![program.as_ptr(), ptr::null()],
             program,
             args: Vec::new(),
-            env: None,
-            envp: None,
+            env: Default::default(),
             cwd: None,
             uid: None,
             gid: None,
@@ -121,68 +119,6 @@ impl Command {
         self.args.push(arg);
     }
 
-    fn init_env_map(&mut self) -> (&mut HashMap<OsString, (usize, CString)>,
-                                   &mut Vec<*const c_char>) {
-        if self.env.is_none() {
-            let mut map = HashMap::new();
-            let mut envp = Vec::new();
-            for (k, v) in env::vars_os() {
-                let s = pair_to_key(&k, &v, &mut self.saw_nul);
-                envp.push(s.as_ptr());
-                map.insert(k, (envp.len() - 1, s));
-            }
-            envp.push(ptr::null());
-            self.env = Some(map);
-            self.envp = Some(envp);
-        }
-        (self.env.as_mut().unwrap(), self.envp.as_mut().unwrap())
-    }
-
-    pub fn env(&mut self, key: &OsStr, val: &OsStr) {
-        let new_key = pair_to_key(key, val, &mut self.saw_nul);
-        let (map, envp) = self.init_env_map();
-
-        // If `key` is already present then we just update `envp` in place
-        // (and store the owned value), but if it's not there we override the
-        // trailing NULL pointer, add a new NULL pointer, and store where we
-        // were located.
-        match map.entry(key.to_owned()) {
-            Entry::Occupied(mut e) => {
-                let (i, ref mut s) = *e.get_mut();
-                envp[i] = new_key.as_ptr();
-                *s = new_key;
-            }
-            Entry::Vacant(e) => {
-                let len = envp.len();
-                envp[len - 1] = new_key.as_ptr();
-                envp.push(ptr::null());
-                e.insert((len - 1, new_key));
-            }
-        }
-    }
-
-    pub fn env_remove(&mut self, key: &OsStr) {
-        let (map, envp) = self.init_env_map();
-
-        // If we actually ended up removing a key, then we need to update the
-        // position of all keys that come after us in `envp` because they're all
-        // one element sooner now.
-        if let Some((i, _)) = map.remove(key) {
-            envp.remove(i);
-
-            for (_, &mut (ref mut j, _)) in map.iter_mut() {
-                if *j >= i {
-                    *j -= 1;
-                }
-            }
-        }
-    }
-
-    pub fn env_clear(&mut self) {
-        self.env = Some(HashMap::new());
-        self.envp = Some(vec![ptr::null()]);
-    }
-
     pub fn cwd(&mut self, dir: &OsStr) {
         self.cwd = Some(os2c(dir, &mut self.saw_nul));
     }
@@ -196,9 +132,6 @@ impl Command {
     pub fn saw_nul(&self) -> bool {
         self.saw_nul
     }
-    pub fn get_envp(&self) -> &Option<Vec<*const c_char>> {
-        &self.envp
-    }
     pub fn get_argv(&self) -> &Vec<*const c_char> {
         &self.argv
     }
@@ -237,6 +170,15 @@ impl Command {
         self.stderr = Some(stderr);
     }
 
+    pub fn env_mut(&mut self) -> &mut CommandEnv<DefaultEnvKey> {
+        &mut self.env
+    }
+
+    pub fn capture_env(&mut self) -> Option<CStringArray> {
+        let maybe_env = self.env.capture_if_changed();
+        maybe_env.map(|env| construct_envp(env, &mut self.saw_nul))
+    }
+
     pub fn setup_io(&self, default: Stdio, needs_stdin: bool)
                 -> io::Result<(StdioPipes, ChildPipes)> {
         let null = Stdio::Null;
@@ -268,6 +210,53 @@ fn os2c(s: &OsStr, saw_nul: &mut bool) -> CString {
     })
 }
 
+// Helper type to manage ownership of the strings within a C-style array.
+pub struct CStringArray {
+    items: Vec<CString>,
+    ptrs: Vec<*const c_char>
+}
+
+impl CStringArray {
+    pub fn with_capacity(capacity: usize) -> Self {
+        let mut result = CStringArray {
+            items: Vec::with_capacity(capacity),
+            ptrs: Vec::with_capacity(capacity+1)
+        };
+        result.ptrs.push(ptr::null());
+        result
+    }
+    pub fn push(&mut self, item: CString) {
+        let l = self.ptrs.len();
+        self.ptrs[l-1] = item.as_ptr();
+        self.ptrs.push(ptr::null());
+        self.items.push(item);
+    }
+    pub fn as_ptr(&self) -> *const *const c_char {
+        self.ptrs.as_ptr()
+    }
+}
+
+fn construct_envp(env: BTreeMap<DefaultEnvKey, OsString>, saw_nul: &mut bool) -> CStringArray {
+    let mut result = CStringArray::with_capacity(env.len());
+    for (k, v) in env {
+        let mut k: OsString = k.into();
+
+        // Reserve additional space for '=' and null terminator
+        k.reserve_exact(v.len() + 2);
+        k.push("=");
+        k.push(&v);
+
+        // Add the new entry into the array
+        if let Ok(item) = CString::new(k.into_vec()) {
+            result.push(item);
+        } else {
+            *saw_nul = true;
+        }
+    }
+
+    result
+}
+
 impl Stdio {
     pub fn to_child_stdio(&self, readable: bool)
                       -> io::Result<(ChildStdio, Option<AnonPipe>)> {
@@ -337,18 +326,6 @@ impl ChildStdio {
     }
 }
 
-fn pair_to_key(key: &OsStr, value: &OsStr, saw_nul: &mut bool) -> CString {
-    let (key, value) = (key.as_bytes(), value.as_bytes());
-    let mut v = Vec::with_capacity(key.len() + value.len() + 1);
-    v.extend(key);
-    v.push(b'=');
-    v.extend(value);
-    CString::new(v).unwrap_or_else(|_e| {
-        *saw_nul = true;
-        CString::new("foo=bar").unwrap()
-    })
-}
-
 impl fmt::Debug for Command {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         write!(f, "{:?}", self.program)?;