about summary refs log tree commit diff
path: root/src/libstd/io
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2014-11-25 13:28:35 -0800
committerAlex Crichton <alex@alexcrichton.com>2015-01-05 08:00:13 -0800
commitec7a50d20dff416d9fec837a6492dfe244f5f3ab (patch)
tree559c2e05abb4f51d6309a1af3f4e1953a6627e3d /src/libstd/io
parent1f732ef53d54ccfc3e7728390ffbcea8a696ecee (diff)
downloadrust-ec7a50d20dff416d9fec837a6492dfe244f5f3ab.tar.gz
rust-ec7a50d20dff416d9fec837a6492dfe244f5f3ab.zip
std: Redesign c_str and c_vec
This commit is an implementation of [RFC 494][rfc] which removes the entire
`std::c_vec` module and redesigns the `std::c_str` module as `std::ffi`.

[rfc]: https://github.com/rust-lang/rfcs/blob/master/text/0494-c_str-and-c_vec-stability.md

The interface of the new `CString` is outlined in the linked RFC, the primary
changes being:

* The `ToCStr` trait is gone, meaning the `with_c_str` and `to_c_str` methods
  are now gone. These two methods are replaced with a `CString::from_slice`
  method.
* The `CString` type is now just a wrapper around `Vec<u8>` with a static
  guarantee that there is a trailing nul byte with no internal nul bytes. This
  means that `CString` now implements `Deref<Target = [c_char]>`, which is where
  it gains most of its methods from. A few helper methods are added to acquire a
  slice of `u8` instead of `c_char`, as well as including a slice with the
  trailing nul byte if necessary.
* All usage of non-owned `CString` values is now done via two functions inside
  of `std::ffi`, called `c_str_to_bytes` and `c_str_to_bytes_with_nul`. These
  functions are now the one method used to convert a `*const c_char` to a Rust
  slice of `u8`.

Many more details, including newly deprecated methods, can be found linked in
the RFC. This is a:

[breaking-change]
Closes #20444
Diffstat (limited to 'src/libstd/io')
-rw-r--r--src/libstd/io/net/pipe.rs21
-rw-r--r--src/libstd/io/process.rs81
2 files changed, 59 insertions, 43 deletions
diff --git a/src/libstd/io/net/pipe.rs b/src/libstd/io/net/pipe.rs
index daefdd28b30..738c70412f7 100644
--- a/src/libstd/io/net/pipe.rs
+++ b/src/libstd/io/net/pipe.rs
@@ -22,7 +22,8 @@
 
 use prelude::v1::*;
 
-use c_str::ToCStr;
+use ffi::CString;
+use path::BytesContainer;
 use io::{Listener, Acceptor, IoResult, TimedOut, standard_error};
 use sys::pipe::UnixAcceptor as UnixAcceptorImp;
 use sys::pipe::UnixListener as UnixListenerImp;
@@ -53,8 +54,9 @@ impl UnixStream {
     /// let mut stream = UnixStream::connect(&server);
     /// stream.write(&[1, 2, 3]);
     /// ```
-    pub fn connect<P: ToCStr>(path: &P) -> IoResult<UnixStream> {
-        UnixStreamImp::connect(&path.to_c_str(), None)
+    pub fn connect<P: BytesContainer>(path: P) -> IoResult<UnixStream> {
+        let path = CString::from_slice(path.container_as_bytes());
+        UnixStreamImp::connect(&path, None)
             .map(|inner| UnixStream { inner: inner })
     }
 
@@ -67,13 +69,15 @@ impl UnixStream {
     /// If a `timeout` with zero or negative duration is specified then
     /// the function returns `Err`, with the error kind set to `TimedOut`.
     #[experimental = "the timeout argument is likely to change types"]
-    pub fn connect_timeout<P: ToCStr>(path: &P,
-                                      timeout: Duration) -> IoResult<UnixStream> {
+    pub fn connect_timeout<P>(path: P, timeout: Duration)
+                              -> IoResult<UnixStream>
+                              where P: BytesContainer {
         if timeout <= Duration::milliseconds(0) {
             return Err(standard_error(TimedOut));
         }
 
-        UnixStreamImp::connect(&path.to_c_str(), Some(timeout.num_milliseconds() as u64))
+        let path = CString::from_slice(path.container_as_bytes());
+        UnixStreamImp::connect(&path, Some(timeout.num_milliseconds() as u64))
             .map(|inner| UnixStream { inner: inner })
     }
 
@@ -177,8 +181,9 @@ impl UnixListener {
     /// }
     /// # }
     /// ```
-    pub fn bind<P: ToCStr>(path: &P) -> IoResult<UnixListener> {
-        UnixListenerImp::bind(&path.to_c_str())
+    pub fn bind<P: BytesContainer>(path: P) -> IoResult<UnixListener> {
+        let path = CString::from_slice(path.container_as_bytes());
+        UnixListenerImp::bind(&path)
             .map(|inner| UnixListener { inner: inner })
     }
 }
diff --git a/src/libstd/io/process.rs b/src/libstd/io/process.rs
index 5886c9cc3e2..ea232ad0c3f 100644
--- a/src/libstd/io/process.rs
+++ b/src/libstd/io/process.rs
@@ -18,8 +18,8 @@ pub use self::ProcessExit::*;
 
 use prelude::v1::*;
 
-use c_str::{CString, ToCStr};
 use collections::HashMap;
+use ffi::CString;
 use fmt;
 use hash::Hash;
 use io::pipe::{PipeStream, PipePair};
@@ -35,6 +35,7 @@ use sys;
 use thread::Thread;
 
 #[cfg(windows)] use std::hash::sip::SipState;
+#[cfg(windows)] use str;
 
 /// Signal a process to exit, without forcibly killing it. Corresponds to
 /// SIGTERM on unix platforms.
@@ -109,11 +110,11 @@ struct EnvKey(CString);
 impl Hash for EnvKey {
     fn hash(&self, state: &mut SipState) {
         let &EnvKey(ref x) = self;
-        match x.as_str() {
-            Some(s) => for ch in s.chars() {
+        match str::from_utf8(x.as_bytes()) {
+            Ok(s) => for ch in s.chars() {
                 (ch as u8 as char).to_lowercase().hash(state);
             },
-            None => x.hash(state)
+            Err(..) => x.hash(state)
         }
     }
 }
@@ -123,8 +124,8 @@ impl PartialEq for EnvKey {
     fn eq(&self, other: &EnvKey) -> bool {
         let &EnvKey(ref x) = self;
         let &EnvKey(ref y) = other;
-        match (x.as_str(), y.as_str()) {
-            (Some(xs), Some(ys)) => {
+        match (str::from_utf8(x.as_bytes()), str::from_utf8(y.as_bytes())) {
+            (Ok(xs), Ok(ys)) => {
                 if xs.len() != ys.len() {
                     return false
                 } else {
@@ -185,10 +186,10 @@ pub struct Command {
 }
 
 // FIXME (#12938): Until DST lands, we cannot decompose &str into & and str, so
-// we cannot usefully take ToCStr arguments by reference (without forcing an
+// we cannot usefully take BytesContainer arguments by reference (without forcing an
 // additional & around &str). So we are instead temporarily adding an instance
-// for &Path, so that we can take ToCStr as owned. When DST lands, the &Path
-// instance should be removed, and arguments bound by ToCStr should be passed by
+// for &Path, so that we can take BytesContainer as owned. When DST lands, the &Path
+// instance should be removed, and arguments bound by BytesContainer should be passed by
 // reference. (Here: {new, arg, args, env}.)
 
 impl Command {
@@ -203,9 +204,9 @@ impl Command {
     ///
     /// Builder methods are provided to change these defaults and
     /// otherwise configure the process.
-    pub fn new<T:ToCStr>(program: T) -> Command {
+    pub fn new<T: BytesContainer>(program: T) -> Command {
         Command {
-            program: program.to_c_str(),
+            program: CString::from_slice(program.container_as_bytes()),
             args: Vec::new(),
             env: None,
             cwd: None,
@@ -219,27 +220,29 @@ impl Command {
     }
 
     /// Add an argument to pass to the program.
-    pub fn arg<'a, T: ToCStr>(&'a mut self, arg: T) -> &'a mut Command {
-        self.args.push(arg.to_c_str());
+    pub fn arg<'a, T: BytesContainer>(&'a mut self, arg: T) -> &'a mut Command {
+        self.args.push(CString::from_slice(arg.container_as_bytes()));
         self
     }
 
     /// Add multiple arguments to pass to the program.
-    pub fn args<'a, T: ToCStr>(&'a mut self, args: &[T]) -> &'a mut Command {
-        self.args.extend(args.iter().map(|arg| arg.to_c_str()));;
+    pub fn args<'a, T: BytesContainer>(&'a mut self, args: &[T]) -> &'a mut Command {
+        self.args.extend(args.iter().map(|arg| {
+            CString::from_slice(arg.container_as_bytes())
+        }));
         self
     }
     // Get a mutable borrow of the environment variable map for this `Command`.
-    fn get_env_map<'a>(&'a mut self) -> &'a mut  EnvMap {
+    fn get_env_map<'a>(&'a mut self) -> &'a mut EnvMap {
         match self.env {
             Some(ref mut map) => map,
             None => {
                 // if the env is currently just inheriting from the parent's,
                 // materialize the parent's env into a hashtable.
-                self.env = Some(os::env_as_bytes().into_iter()
-                                   .map(|(k, v)| (EnvKey(k.to_c_str()),
-                                                  v.to_c_str()))
-                                   .collect());
+                self.env = Some(os::env_as_bytes().into_iter().map(|(k, v)| {
+                    (EnvKey(CString::from_slice(k.as_slice())),
+                     CString::from_slice(v.as_slice()))
+                }).collect());
                 self.env.as_mut().unwrap()
             }
         }
@@ -249,15 +252,20 @@ impl Command {
     ///
     /// Note that environment variable names are case-insensitive (but case-preserving) on Windows,
     /// and case-sensitive on all other platforms.
-    pub fn env<'a, T: ToCStr, U: ToCStr>(&'a mut self, key: T, val: U)
-                                         -> &'a mut Command {
-        self.get_env_map().insert(EnvKey(key.to_c_str()), val.to_c_str());
+    pub fn env<'a, T, U>(&'a mut self, key: T, val: U)
+                         -> &'a mut Command
+                         where T: BytesContainer, U: BytesContainer {
+        let key = EnvKey(CString::from_slice(key.container_as_bytes()));
+        let val = CString::from_slice(val.container_as_bytes());
+        self.get_env_map().insert(key, val);
         self
     }
 
     /// Removes an environment variable mapping.
-    pub fn env_remove<'a, T: ToCStr>(&'a mut self, key: T) -> &'a mut Command {
-        self.get_env_map().remove(&EnvKey(key.to_c_str()));
+    pub fn env_remove<'a, T>(&'a mut self, key: T) -> &'a mut Command
+                             where T: BytesContainer {
+        let key = EnvKey(CString::from_slice(key.container_as_bytes()));
+        self.get_env_map().remove(&key);
         self
     }
 
@@ -265,16 +273,19 @@ impl Command {
     ///
     /// If the given slice contains multiple instances of an environment
     /// variable, the *rightmost* instance will determine the value.
-    pub fn env_set_all<'a, T: ToCStr, U: ToCStr>(&'a mut self, env: &[(T,U)])
-                                                 -> &'a mut Command {
-        self.env = Some(env.iter().map(|&(ref k, ref v)| (EnvKey(k.to_c_str()), v.to_c_str()))
-                                  .collect());
+    pub fn env_set_all<'a, T, U>(&'a mut self, env: &[(T,U)])
+                                 -> &'a mut Command
+                                 where T: BytesContainer, U: BytesContainer {
+        self.env = Some(env.iter().map(|&(ref k, ref v)| {
+            (EnvKey(CString::from_slice(k.container_as_bytes())),
+             CString::from_slice(v.container_as_bytes()))
+        }).collect());
         self
     }
 
     /// Set the working directory for the child process.
     pub fn cwd<'a>(&'a mut self, dir: &Path) -> &'a mut Command {
-        self.cwd = Some(dir.to_c_str());
+        self.cwd = Some(CString::from_slice(dir.as_vec()));
         self
     }
 
@@ -389,9 +400,9 @@ impl fmt::Show for Command {
     /// non-utf8 data is lossily converted using the utf8 replacement
     /// character.
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        try!(write!(f, "{}", String::from_utf8_lossy(self.program.as_bytes_no_nul())));
+        try!(write!(f, "{}", String::from_utf8_lossy(self.program.as_bytes())));
         for arg in self.args.iter() {
-            try!(write!(f, " '{}'", String::from_utf8_lossy(arg.as_bytes_no_nul())));
+            try!(write!(f, " '{}'", String::from_utf8_lossy(arg.as_bytes())));
         }
         Ok(())
     }
@@ -1208,13 +1219,13 @@ mod tests {
     #[test]
     #[cfg(windows)]
     fn env_map_keys_ci() {
-        use c_str::ToCStr;
+        use ffi::CString;
         use super::EnvKey;
         let mut cmd = Command::new("");
         cmd.env("path", "foo");
         cmd.env("Path", "bar");
         let env = &cmd.env.unwrap();
-        let val = env.get(&EnvKey("PATH".to_c_str()));
-        assert!(val.unwrap() == &"bar".to_c_str());
+        let val = env.get(&EnvKey(CString::from_slice(b"PATH")));
+        assert!(val.unwrap() == &CString::from_slice(b"bar"));
     }
 }