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:
parent
1e4f13f95f
commit
48265b779f
2 changed files with 108 additions and 13 deletions
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue