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.
|
// except according to those terms.
|
||||||
|
|
||||||
use cast;
|
use cast;
|
||||||
use iterator::Iterator;
|
use iterator::{Iterator,range};
|
||||||
use libc;
|
use libc;
|
||||||
use ops::Drop;
|
use ops::Drop;
|
||||||
use option::{Option, Some, None};
|
use option::{Option, Some, None};
|
||||||
use ptr::RawPtr;
|
use ptr::RawPtr;
|
||||||
use ptr;
|
use ptr;
|
||||||
use str::StrSlice;
|
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.
|
/// The representation of a C String.
|
||||||
///
|
///
|
||||||
|
@ -110,8 +124,15 @@ impl Drop for CString {
|
||||||
|
|
||||||
/// A generic trait for converting a value to a CString.
|
/// A generic trait for converting a value to a CString.
|
||||||
pub trait ToCStr {
|
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;
|
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 {
|
impl<'self> ToCStr for &'self str {
|
||||||
|
@ -119,23 +140,44 @@ impl<'self> ToCStr for &'self str {
|
||||||
fn to_c_str(&self) -> CString {
|
fn to_c_str(&self) -> CString {
|
||||||
self.as_bytes().to_c_str()
|
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] {
|
impl<'self> ToCStr for &'self [u8] {
|
||||||
fn to_c_str(&self) -> CString {
|
fn to_c_str(&self) -> CString {
|
||||||
do self.as_imm_buf |self_buf, self_len| {
|
let mut cs = unsafe { self.to_c_str_unchecked() };
|
||||||
unsafe {
|
do cs.with_mut_ref |buf| {
|
||||||
let buf = libc::malloc(self_len as libc::size_t + 1) as *mut u8;
|
for i in range(0, self.len()) {
|
||||||
if buf.is_null() {
|
unsafe {
|
||||||
fail!("failed to allocate memory!");
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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!");
|
||||||
|
}
|
||||||
|
|
||||||
|
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(), Some('o' as libc::c_char));
|
||||||
assert_eq!(iter.next(), None);
|
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 {
|
fn to_c_str(&self) -> c_str::CString {
|
||||||
self.to_str().to_c_str()
|
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
|
// 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 {
|
fn to_c_str(&self) -> c_str::CString {
|
||||||
self.to_str().to_c_str()
|
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 {
|
impl GenericPath for WindowsPath {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue