about summary refs log tree commit diff
path: root/src/libstd
diff options
context:
space:
mode:
authorKevin Ballard <kevin@sb.org>2013-08-14 19:19:29 -0700
committerKevin Ballard <kevin@sb.org>2013-08-15 01:32:10 -0700
commit48265b779fabf865a4b05f000ea1575c90e3cd73 (patch)
tree70e4fc83ed145142c5f7b53440e2ff7f9181ad29 /src/libstd
parent1e4f13f95fc629bcee2ad9a766d9af7c7b2e18f7 (diff)
downloadrust-48265b779fabf865a4b05f000ea1575c90e3cd73.tar.gz
rust-48265b779fabf865a4b05f000ea1575c90e3cd73.zip
Check for interior nulls in .to_c_str()
Previous dicussions about CString suggested that interior nulls should
throw an error. This was never implemented. Add this now, using a
condition (named null_byte) to allow for recovery.

Add method .to_c_str_unchecked() that skips this check.
Diffstat (limited to 'src/libstd')
-rw-r--r--src/libstd/c_str.rs111
-rw-r--r--src/libstd/path.rs8
2 files changed, 107 insertions, 12 deletions
diff --git a/src/libstd/c_str.rs b/src/libstd/c_str.rs
index bf2d55aa0b2..5c77aa4a65a 100644
--- a/src/libstd/c_str.rs
+++ b/src/libstd/c_str.rs
@@ -9,14 +9,28 @@
 // except according to those terms.
 
 use cast;
-use iterator::Iterator;
+use iterator::{Iterator,range};
 use libc;
 use ops::Drop;
 use option::{Option, Some, None};
 use ptr::RawPtr;
 use ptr;
 use str::StrSlice;
-use vec::ImmutableVector;
+use vec::{ImmutableVector,CopyableVector};
+use container::Container;
+
+/// Resolution options for the `null_byte` condition
+pub enum NullByteResolution {
+    /// Truncate at the null byte
+    Truncate,
+    /// Use a replacement byte
+    ReplaceWith(libc::c_char)
+}
+
+condition! {
+    // this should be &[u8] but there's a lifetime issue
+    null_byte: (~[u8]) -> super::NullByteResolution;
+}
 
 /// The representation of a C String.
 ///
@@ -110,8 +124,15 @@ impl Drop for CString {
 
 /// A generic trait for converting a value to a CString.
 pub trait ToCStr {
-    /// Create a C String.
+    /// Copy the receiver into a CString.
+    ///
+    /// # Failure
+    ///
+    /// Raises the `null_byte` condition if the receiver has an interior null.
     fn to_c_str(&self) -> CString;
+
+    /// Unsafe variant of `to_c_str()` that doesn't check for nulls.
+    unsafe fn to_c_str_unchecked(&self) -> CString;
 }
 
 impl<'self> ToCStr for &'self str {
@@ -119,22 +140,43 @@ impl<'self> ToCStr for &'self str {
     fn to_c_str(&self) -> CString {
         self.as_bytes().to_c_str()
     }
+
+    #[inline]
+    unsafe fn to_c_str_unchecked(&self) -> CString {
+        self.as_bytes().to_c_str_unchecked()
+    }
 }
 
 impl<'self> ToCStr for &'self [u8] {
     fn to_c_str(&self) -> CString {
-        do self.as_imm_buf |self_buf, self_len| {
-            unsafe {
-                let buf = libc::malloc(self_len as libc::size_t + 1) as *mut u8;
-                if buf.is_null() {
-                    fail!("failed to allocate memory!");
+        let mut cs = unsafe { self.to_c_str_unchecked() };
+        do cs.with_mut_ref |buf| {
+            for i in range(0, self.len()) {
+                unsafe {
+                    let p = buf.offset_inbounds(i as int);
+                    if *p == 0 {
+                        match null_byte::cond.raise(self.to_owned()) {
+                            Truncate => break,
+                            ReplaceWith(c) => *p = c
+                        }
+                    }
                 }
+            }
+        }
+        cs
+    }
 
-                ptr::copy_memory(buf, self_buf, self_len);
-                *ptr::mut_offset(buf, self_len as int) = 0;
-
-                CString::new(buf as *libc::c_char, true)
+    unsafe fn to_c_str_unchecked(&self) -> CString {
+        do self.as_imm_buf |self_buf, self_len| {
+            let buf = libc::malloc(self_len as libc::size_t + 1) as *mut u8;
+            if buf.is_null() {
+                fail!("failed to allocate memory!");
             }
+
+            ptr::copy_memory(buf, self_buf, self_len);
+            *ptr::mut_offset(buf, self_len as int) = 0;
+
+            CString::new(buf as *libc::c_char, true)
         }
     }
 }
@@ -231,4 +273,49 @@ mod tests {
         assert_eq!(iter.next(), Some('o' as libc::c_char));
         assert_eq!(iter.next(), None);
     }
+
+    #[test]
+    #[ignore(cfg(windows))]
+    fn test_to_c_str_fail() {
+        use c_str::null_byte::cond;
+
+        let mut error_happened = false;
+        do cond.trap(|err| {
+            assert_eq!(err, bytes!("he", 0, "llo").to_owned())
+            error_happened = true;
+            Truncate
+        }).inside {
+            "he\x00llo".to_c_str()
+        };
+        assert!(error_happened);
+
+        do cond.trap(|_| {
+            ReplaceWith('?' as libc::c_char)
+        }).inside(|| "he\x00llo".to_c_str()).with_ref |buf| {
+            unsafe {
+                assert_eq!(*buf.offset(0), 'h' as libc::c_char);
+                assert_eq!(*buf.offset(1), 'e' as libc::c_char);
+                assert_eq!(*buf.offset(2), '?' as libc::c_char);
+                assert_eq!(*buf.offset(3), 'l' as libc::c_char);
+                assert_eq!(*buf.offset(4), 'l' as libc::c_char);
+                assert_eq!(*buf.offset(5), 'o' as libc::c_char);
+                assert_eq!(*buf.offset(6), 0);
+            }
+        }
+    }
+
+    #[test]
+    fn test_to_c_str_unchecked() {
+        unsafe {
+            do "he\x00llo".to_c_str_unchecked().with_ref |buf| {
+                assert_eq!(*buf.offset(0), 'h' as libc::c_char);
+                assert_eq!(*buf.offset(1), 'e' as libc::c_char);
+                assert_eq!(*buf.offset(2), 0);
+                assert_eq!(*buf.offset(3), 'l' as libc::c_char);
+                assert_eq!(*buf.offset(4), 'l' as libc::c_char);
+                assert_eq!(*buf.offset(5), 'o' as libc::c_char);
+                assert_eq!(*buf.offset(6), 0);
+            }
+        }
+    }
 }
diff --git a/src/libstd/path.rs b/src/libstd/path.rs
index 177f0efb6da..7f53ddf6e79 100644
--- a/src/libstd/path.rs
+++ b/src/libstd/path.rs
@@ -569,6 +569,10 @@ impl ToCStr for PosixPath {
     fn to_c_str(&self) -> c_str::CString {
         self.to_str().to_c_str()
     }
+
+    unsafe fn to_c_str_unchecked(&self) -> c_str::CString {
+        self.to_str().to_c_str_unchecked()
+    }
 }
 
 // FIXME (#3227): when default methods in traits are working, de-duplicate
@@ -781,6 +785,10 @@ impl c_str::ToCStr for WindowsPath {
     fn to_c_str(&self) -> c_str::CString {
         self.to_str().to_c_str()
     }
+
+    unsafe fn to_c_str_unchecked(&self) -> c_str::CString {
+        self.to_str().to_c_str_unchecked()
+    }
 }
 
 impl GenericPath for WindowsPath {