Skip to content

Commit

Permalink
IntoRef
Browse files Browse the repository at this point in the history
  • Loading branch information
arcnmx committed May 3, 2016
1 parent 76c80e6 commit 14c0ad2
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 38 deletions.
83 changes: 70 additions & 13 deletions src/null_terminated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ use std::mem::transmute;
use std::iter;
use libc::c_char;

pub trait IntoRef<'a, T: ?Sized> {
type Target: 'a + AsRef<T>;

fn into_ref(self) -> Self::Target;
}

pub struct NullTerminatedSlice<T> {
inner: [Option<T>],
}
Expand Down Expand Up @@ -55,29 +61,80 @@ impl<T> DerefMut for NullTerminatedSlice<T> {
}
}

pub trait BorrowNullTerminatedSlice<T> {
fn borrow_null_terminated_slice<R, F: FnOnce(&NullTerminatedSlice<&T>) -> R>(self, f: F) -> R;
impl<T> AsRef<NullTerminatedSlice<T>> for NullTerminatedSlice<T> {
fn as_ref(&self) -> &Self {
self
}
}

pub struct NullTerminatedVec<T> {
inner: Vec<Option<T>>,
}

impl<T> NullTerminatedVec<T> {
pub fn from_vec(vec: Vec<Option<T>>) -> Option<Self> {
if vec.last().map(Option::is_none).unwrap_or(false) {
Some(unsafe { Self::from_vec_unchecked(vec) })
} else {
None
}
}

pub unsafe fn from_vec_unchecked(vec: Vec<Option<T>>) -> Self {
NullTerminatedVec {
inner: vec,
}
}
}

impl<T> Deref for NullTerminatedVec<T> {
type Target = NullTerminatedSlice<T>;

fn deref(&self) -> &Self::Target {
unsafe {
NullTerminatedSlice::from_slice_unchecked(&self.inner)
}
}
}

impl<T> DerefMut for NullTerminatedVec<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe {
NullTerminatedSlice::from_slice_mut_unchecked(&mut self.inner)
}
}
}

impl<T> AsRef<NullTerminatedSlice<T>> for NullTerminatedVec<T> {
fn as_ref(&self) -> &NullTerminatedSlice<T> {
self
}
}

impl<T: AsRef<CStr>, I: IntoIterator<Item=T>> BorrowNullTerminatedSlice<c_char> for I {
fn borrow_null_terminated_slice<R, F: FnOnce(&NullTerminatedSlice<&c_char>) -> R>(self, f: F) -> R {
impl<'a, T: 'a> IntoRef<'a, NullTerminatedSlice<&'a T>> for &'a NullTerminatedSlice<&'a T> {
type Target = &'a NullTerminatedSlice<&'a T>;

fn into_ref(self) -> Self::Target {
self
}
}

impl<'a, T: AsRef<CStr> + 'a, I: IntoIterator<Item=T>> IntoRef<'a, NullTerminatedSlice<&'a c_char>> for I {
type Target = NullTerminatedVec<&'a c_char>;

fn into_ref(self) -> Self::Target {
fn cstr_char<'a, S: AsRef<CStr> + 'a>(s: S) -> &'a c_char {
unsafe {
&*s.as_ref().as_ptr()
}
}

let values: Vec<_> = self.into_iter()
let terminated = self.into_iter()
.map(cstr_char)
.map(Some).chain(iter::once(None)).collect();
let terminated = unsafe { NullTerminatedSlice::from_slice_unchecked(&values[..]) };

f(terminated)
}
}

impl<'a, T: 'a> BorrowNullTerminatedSlice<T> for &'a NullTerminatedSlice<&'a T> {
fn borrow_null_terminated_slice<R, F: FnOnce(&NullTerminatedSlice<&T>) -> R>(self, f: F) -> R {
f(self)
unsafe {
NullTerminatedVec::from_vec_unchecked(terminated)
}
}
}
49 changes: 24 additions & 25 deletions src/unistd.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Standard symbolic constants and types
//!
use {Errno, Error, Result, NixPath};
use null_terminated::BorrowNullTerminatedSlice;
use null_terminated::{IntoRef, NullTerminatedSlice};
use fcntl::{fcntl, OFlag, O_NONBLOCK, O_CLOEXEC, FD_CLOEXEC};
use fcntl::FcntlArg::{F_SETFD, F_SETFL};
use libc::{self, c_char, c_void, c_int, c_uint, size_t, pid_t, off_t, uid_t, gid_t};
Expand Down Expand Up @@ -123,38 +123,37 @@ pub fn chown<P: ?Sized + NixPath>(path: &P, owner: Option<uid_t>, group: Option<
}

#[inline]
pub fn execv<A: BorrowNullTerminatedSlice<c_char>>(path: &CStr, argv: A) -> Result<Void> {
argv.borrow_null_terminated_slice(|args_p| {
unsafe {
libc::execv(path.as_ptr(), args_p.as_ptr())
};
pub fn execv<'a, A: IntoRef<'a, NullTerminatedSlice<&'a c_char>>>(path: &CStr, argv: A) -> Result<Void> {
let argv = argv.into_ref();

Err(Error::Sys(Errno::last()))
})
unsafe {
libc::execv(path.as_ptr(), argv.as_ref().as_ptr())
};

Err(Error::Sys(Errno::last()))
}

#[inline]
pub fn execve<A: BorrowNullTerminatedSlice<c_char>, E: BorrowNullTerminatedSlice<c_char>>(path: &CStr, args: A, env: E) -> Result<Void> {
args.borrow_null_terminated_slice(|args_p| {
env.borrow_null_terminated_slice(|env_p| {
unsafe {
libc::execve(path.as_ptr(), args_p.as_ptr(), env_p.as_ptr())
};

Err(Error::Sys(Errno::last()))
})
})
pub fn execve<'a, 'e, A: IntoRef<'a, NullTerminatedSlice<&'a c_char>>, E: IntoRef<'e, NullTerminatedSlice<&'e c_char>>>(path: &CStr, args: A, env: E) -> Result<Void> {
let args = args.into_ref();
let env = env.into_ref();

unsafe {
libc::execve(path.as_ptr(), args.as_ref().as_ptr(), env.as_ref().as_ptr())
};

Err(Error::Sys(Errno::last()))
}

#[inline]
pub fn execvp<A: BorrowNullTerminatedSlice<c_char>>(filename: &CStr, args: A) -> Result<Void> {
args.borrow_null_terminated_slice(|args_p| {
unsafe {
libc::execvp(filename.as_ptr(), args_p.as_ptr())
};
pub fn execvp<'a, A: IntoRef<'a, NullTerminatedSlice<&'a c_char>>>(filename: &CStr, args: A) -> Result<Void> {
let args = args.into_ref();

Err(Error::Sys(Errno::last()))
})
unsafe {
libc::execvp(filename.as_ptr(), args.as_ref().as_ptr())
};

Err(Error::Sys(Errno::last()))
}

pub fn daemon(nochdir: bool, noclose: bool) -> Result<()> {
Expand Down

0 comments on commit 14c0ad2

Please sign in to comment.