diff options
| author | Kevin Ballard <kevin@sb.org> | 2013-08-14 19:19:29 -0700 |
|---|---|---|
| committer | Kevin Ballard <kevin@sb.org> | 2013-08-15 01:32:10 -0700 |
| commit | 48265b779fabf865a4b05f000ea1575c90e3cd73 (patch) | |
| tree | 70e4fc83ed145142c5f7b53440e2ff7f9181ad29 /src/libstd | |
| parent | 1e4f13f95fc629bcee2ad9a766d9af7c7b2e18f7 (diff) | |
| download | rust-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.rs | 111 | ||||
| -rw-r--r-- | src/libstd/path.rs | 8 |
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 { |
