1
Fork 0

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.
This commit is contained in:
Kevin Ballard 2013-08-14 19:19:29 -07:00
parent 1e4f13f95f
commit 48265b779f
2 changed files with 108 additions and 13 deletions

View file

@ -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,12 +140,34 @@ 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| {
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
}
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!");
@ -136,7 +179,6 @@ impl<'self> ToCStr for &'self [u8] {
CString::new(buf as *libc::c_char, true)
}
}
}
}
/// External iterator for a CString's bytes.
@ -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);
}
}
}
}

View file

@ -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 {