std: Redesign c_str and c_vec

This commit is an implementation of [RFC 494][rfc] which removes the entire
`std::c_vec` module and redesigns the `std::c_str` module as `std::ffi`.

[rfc]: https://github.com/rust-lang/rfcs/blob/master/text/0494-c_str-and-c_vec-stability.md

The interface of the new `CString` is outlined in the linked RFC, the primary
changes being:

* The `ToCStr` trait is gone, meaning the `with_c_str` and `to_c_str` methods
  are now gone. These two methods are replaced with a `CString::from_slice`
  method.
* The `CString` type is now just a wrapper around `Vec<u8>` with a static
  guarantee that there is a trailing nul byte with no internal nul bytes. This
  means that `CString` now implements `Deref<Target = [c_char]>`, which is where
  it gains most of its methods from. A few helper methods are added to acquire a
  slice of `u8` instead of `c_char`, as well as including a slice with the
  trailing nul byte if necessary.
* All usage of non-owned `CString` values is now done via two functions inside
  of `std::ffi`, called `c_str_to_bytes` and `c_str_to_bytes_with_nul`. These
  functions are now the one method used to convert a `*const c_char` to a Rust
  slice of `u8`.

Many more details, including newly deprecated methods, can be found linked in
the RFC. This is a:

[breaking-change]
Closes #20444
This commit is contained in:
Alex Crichton 2014-11-25 13:28:35 -08:00
parent 1f732ef53d
commit ec7a50d20d
59 changed files with 1018 additions and 1998 deletions

View file

@ -14,7 +14,7 @@
use io::{self, IoError, IoResult};
use prelude::v1::*;
use sys::{last_error, retry};
use c_str::CString;
use ffi::CString;
use num::Int;
use path::BytesContainer;
use collections;

View file

@ -12,15 +12,16 @@ use prelude::v1::*;
use self::SocketStatus::*;
use self::InAddr::*;
use c_str::ToCStr;
use ffi::CString;
use ffi;
use io::net::addrinfo;
use io::net::ip::{SocketAddr, IpAddr, Ipv4Addr, Ipv6Addr};
use io::{IoResult, IoError};
use libc::{self, c_char, c_int};
use c_str::CString;
use mem;
use num::Int;
use ptr::{self, null, null_mut};
use str;
use sys::{self, retry, c, sock_t, last_error, last_net_error, last_gai_error, close_sock,
wrlen, msglen_t, os, wouldblock, set_nonblocking, timer, ms_to_timeval,
decode_error_detailed};
@ -234,9 +235,9 @@ pub fn get_host_addresses(host: Option<&str>, servname: Option<&str>,
assert!(host.is_some() || servname.is_some());
let c_host = host.map(|x| x.to_c_str());
let c_host = host.map(|x| CString::from_slice(x.as_bytes()));
let c_host = c_host.as_ref().map(|x| x.as_ptr()).unwrap_or(null());
let c_serv = servname.map(|x| x.to_c_str());
let c_serv = servname.map(|x| CString::from_slice(x.as_bytes()));
let c_serv = c_serv.as_ref().map(|x| x.as_ptr()).unwrap_or(null());
let hint = hint.map(|hint| {
@ -324,7 +325,8 @@ pub fn get_address_name(addr: IpAddr) -> Result<String, IoError> {
}
unsafe {
Ok(CString::new(hostbuf.as_ptr(), false).as_str().unwrap().to_string())
Ok(str::from_utf8(ffi::c_str_to_bytes(&hostbuf.as_ptr()))
.unwrap().to_string())
}
}

View file

@ -83,12 +83,13 @@
/// to symbols. This is a bit of a hokey implementation as-is, but it works for
/// all unix platforms we support right now, so it at least gets the job done.
use c_str::CString;
use io::{IoResult, Writer};
use prelude::v1::*;
use ffi;
use io::IoResult;
use libc;
use mem;
use option::Option::{self, Some, None};
use result::Result::{Ok, Err};
use str;
use sync::{StaticMutex, MUTEX_INIT};
use sys_common::backtrace::*;
@ -105,9 +106,7 @@ use sys_common::backtrace::*;
#[cfg(all(target_os = "ios", target_arch = "arm"))]
#[inline(never)]
pub fn write(w: &mut Writer) -> IoResult<()> {
use iter::{IteratorExt, range};
use result;
use slice::SliceExt;
extern {
fn backtrace(buf: *mut *mut libc::c_void,
@ -234,19 +233,15 @@ fn print(w: &mut Writer, idx: int, addr: *mut libc::c_void) -> IoResult<()> {
output(w, idx,addr, None)
} else {
output(w, idx, addr, Some(unsafe {
CString::new(info.dli_sname, false)
ffi::c_str_to_bytes(&info.dli_sname)
}))
}
}
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
fn print(w: &mut Writer, idx: int, addr: *mut libc::c_void) -> IoResult<()> {
use iter::{Iterator, IteratorExt};
use os;
use path::GenericPath;
use ptr::PtrExt;
use ptr;
use slice::SliceExt;
////////////////////////////////////////////////////////////////////////
// libbacktrace.h API
@ -368,15 +363,15 @@ fn print(w: &mut Writer, idx: int, addr: *mut libc::c_void) -> IoResult<()> {
if ret == 0 || data.is_null() {
output(w, idx, addr, None)
} else {
output(w, idx, addr, Some(unsafe { CString::new(data, false) }))
output(w, idx, addr, Some(unsafe { ffi::c_str_to_bytes(&data) }))
}
}
// Finally, after all that work above, we can emit a symbol.
fn output(w: &mut Writer, idx: int, addr: *mut libc::c_void,
s: Option<CString>) -> IoResult<()> {
s: Option<&[u8]>) -> IoResult<()> {
try!(write!(w, " {:2}: {:2$} - ", idx, addr, HEX_WIDTH));
match s.as_ref().and_then(|c| c.as_str()) {
match s.and_then(|s| str::from_utf8(s).ok()) {
Some(string) => try!(demangle(w, string)),
None => try!(write!(w, "<unknown>")),
}

View file

@ -12,7 +12,7 @@
use prelude::v1::*;
use c_str::{CString, ToCStr};
use ffi::{self, CString};
use io::{FilePermission, Write, UnstableFileStat, Open, FileAccess, FileMode};
use io::{IoResult, FileStat, SeekStyle};
use io::{Read, Truncate, SeekCur, SeekSet, ReadWrite, SeekEnd, Append};
@ -150,6 +150,10 @@ impl Drop for FileDesc {
}
}
fn cstr(path: &Path) -> CString {
CString::from_slice(path.as_vec())
}
pub fn open(path: &Path, fm: FileMode, fa: FileAccess) -> IoResult<FileDesc> {
let flags = match fm {
Open => 0,
@ -165,7 +169,7 @@ pub fn open(path: &Path, fm: FileMode, fa: FileAccess) -> IoResult<FileDesc> {
libc::S_IRUSR | libc::S_IWUSR),
};
let path = path.to_c_str();
let path = cstr(path);
match retry(|| unsafe { libc::open(path.as_ptr(), flags, mode) }) {
-1 => Err(super::last_error()),
fd => Ok(FileDesc::new(fd, true)),
@ -173,7 +177,7 @@ pub fn open(path: &Path, fm: FileMode, fa: FileAccess) -> IoResult<FileDesc> {
}
pub fn mkdir(p: &Path, mode: uint) -> IoResult<()> {
let p = p.to_c_str();
let p = cstr(p);
mkerr_libc(unsafe { libc::mkdir(p.as_ptr(), mode as libc::mode_t) })
}
@ -182,7 +186,6 @@ pub fn readdir(p: &Path) -> IoResult<Vec<Path>> {
use libc::{opendir, readdir_r, closedir};
fn prune(root: &CString, dirs: Vec<Path>) -> Vec<Path> {
let root = unsafe { CString::new(root.as_ptr(), false) };
let root = Path::new(root);
dirs.into_iter().filter(|path| {
@ -199,7 +202,7 @@ pub fn readdir(p: &Path) -> IoResult<Vec<Path>> {
let mut buf = Vec::<u8>::with_capacity(size as uint);
let ptr = buf.as_mut_ptr() as *mut dirent_t;
let p = p.to_c_str();
let p = CString::from_slice(p.as_vec());
let dir_ptr = unsafe {opendir(p.as_ptr())};
if dir_ptr as uint != 0 {
@ -207,10 +210,9 @@ pub fn readdir(p: &Path) -> IoResult<Vec<Path>> {
let mut entry_ptr = 0 as *mut dirent_t;
while unsafe { readdir_r(dir_ptr, ptr, &mut entry_ptr) == 0 } {
if entry_ptr.is_null() { break }
let cstr = unsafe {
CString::new(rust_list_dir_val(entry_ptr), false)
};
paths.push(Path::new(cstr));
paths.push(unsafe {
Path::new(ffi::c_str_to_bytes(&rust_list_dir_val(entry_ptr)))
});
}
assert_eq!(unsafe { closedir(dir_ptr) }, 0);
Ok(prune(&p, paths))
@ -220,39 +222,39 @@ pub fn readdir(p: &Path) -> IoResult<Vec<Path>> {
}
pub fn unlink(p: &Path) -> IoResult<()> {
let p = p.to_c_str();
let p = cstr(p);
mkerr_libc(unsafe { libc::unlink(p.as_ptr()) })
}
pub fn rename(old: &Path, new: &Path) -> IoResult<()> {
let old = old.to_c_str();
let new = new.to_c_str();
let old = cstr(old);
let new = cstr(new);
mkerr_libc(unsafe {
libc::rename(old.as_ptr(), new.as_ptr())
})
}
pub fn chmod(p: &Path, mode: uint) -> IoResult<()> {
let p = p.to_c_str();
let p = cstr(p);
mkerr_libc(retry(|| unsafe {
libc::chmod(p.as_ptr(), mode as libc::mode_t)
}))
}
pub fn rmdir(p: &Path) -> IoResult<()> {
let p = p.to_c_str();
let p = cstr(p);
mkerr_libc(unsafe { libc::rmdir(p.as_ptr()) })
}
pub fn chown(p: &Path, uid: int, gid: int) -> IoResult<()> {
let p = p.to_c_str();
let p = cstr(p);
mkerr_libc(retry(|| unsafe {
libc::chown(p.as_ptr(), uid as libc::uid_t, gid as libc::gid_t)
}))
}
pub fn readlink(p: &Path) -> IoResult<Path> {
let c_path = p.to_c_str();
let c_path = cstr(p);
let p = c_path.as_ptr();
let mut len = unsafe { libc::pathconf(p as *mut _, libc::_PC_NAME_MAX) };
if len == -1 {
@ -273,14 +275,14 @@ pub fn readlink(p: &Path) -> IoResult<Path> {
}
pub fn symlink(src: &Path, dst: &Path) -> IoResult<()> {
let src = src.to_c_str();
let dst = dst.to_c_str();
let src = cstr(src);
let dst = cstr(dst);
mkerr_libc(unsafe { libc::symlink(src.as_ptr(), dst.as_ptr()) })
}
pub fn link(src: &Path, dst: &Path) -> IoResult<()> {
let src = src.to_c_str();
let dst = dst.to_c_str();
let src = cstr(src);
let dst = cstr(dst);
mkerr_libc(unsafe { libc::link(src.as_ptr(), dst.as_ptr()) })
}
@ -328,7 +330,7 @@ fn mkstat(stat: &libc::stat) -> FileStat {
}
pub fn stat(p: &Path) -> IoResult<FileStat> {
let p = p.to_c_str();
let p = cstr(p);
let mut stat: libc::stat = unsafe { mem::zeroed() };
match unsafe { libc::stat(p.as_ptr(), &mut stat) } {
0 => Ok(mkstat(&stat)),
@ -337,7 +339,7 @@ pub fn stat(p: &Path) -> IoResult<FileStat> {
}
pub fn lstat(p: &Path) -> IoResult<FileStat> {
let p = p.to_c_str();
let p = cstr(p);
let mut stat: libc::stat = unsafe { mem::zeroed() };
match unsafe { libc::lstat(p.as_ptr(), &mut stat) } {
0 => Ok(mkstat(&stat)),
@ -346,7 +348,7 @@ pub fn lstat(p: &Path) -> IoResult<FileStat> {
}
pub fn utime(p: &Path, atime: u64, mtime: u64) -> IoResult<()> {
let p = p.to_c_str();
let p = cstr(p);
let buf = libc::utimbuf {
actime: (atime / 1000) as libc::time_t,
modtime: (mtime / 1000) as libc::time_t,

View file

@ -15,12 +15,14 @@
#![allow(unused_unsafe)]
#![allow(unused_mut)]
extern crate libc;
use num;
use num::{Int, SignedInt};
use prelude::v1::*;
use ffi;
use io::{self, IoResult, IoError};
use libc;
use num::{Int, SignedInt};
use num;
use str;
use sys_common::mkerr_libc;
macro_rules! helper_init { (static $name:ident: Helper<$m:ty>) => (
@ -78,11 +80,10 @@ extern "system" {
}
pub fn last_gai_error(s: libc::c_int) -> IoError {
use c_str::CString;
let mut err = decode_error(s);
err.detail = Some(unsafe {
CString::new(gai_strerror(s), false).as_str().unwrap().to_string()
str::from_utf8(ffi::c_str_to_bytes(&gai_strerror(s))).unwrap().to_string()
});
err
}

View file

@ -12,18 +12,18 @@
use prelude::v1::*;
use c_str::ToCStr;
use error::{FromError, Error};
use ffi::{self, CString};
use fmt;
use io::{IoError, IoResult};
use libc::{self, c_int, c_char, c_void};
use os::TMPBUF_SZ;
use os;
use path::{BytesContainer};
use ptr;
use str;
use sys::fs::FileDesc;
use os::TMPBUF_SZ;
const BUF_BYTES : uint = 2048u;
/// Returns the platform-specific value of errno
@ -108,7 +108,8 @@ pub fn error_string(errno: i32) -> String {
panic!("strerror_r failure");
}
String::from_raw_buf(p as *const u8)
let p = p as *const _;
str::from_utf8(ffi::c_str_to_bytes(&p)).unwrap().to_string()
}
}
@ -122,21 +123,17 @@ pub unsafe fn pipe() -> IoResult<(FileDesc, FileDesc)> {
}
pub fn getcwd() -> IoResult<Path> {
use c_str::CString;
let mut buf = [0 as c_char; BUF_BYTES];
unsafe {
if libc::getcwd(buf.as_mut_ptr(), buf.len() as libc::size_t).is_null() {
Err(IoError::last_error())
} else {
Ok(Path::new(CString::new(buf.as_ptr(), false)))
Ok(Path::new(ffi::c_str_to_bytes(&buf.as_ptr())))
}
}
}
pub unsafe fn get_env_pairs() -> Vec<Vec<u8>> {
use c_str::CString;
extern {
fn rust_env_pairs() -> *const *const c_char;
}
@ -147,8 +144,7 @@ pub unsafe fn get_env_pairs() -> Vec<Vec<u8>> {
}
let mut result = Vec::new();
while *environ != 0 as *const _ {
let env_pair =
CString::new(*environ, false).as_bytes_no_nul().to_vec();
let env_pair = ffi::c_str_to_bytes(&*environ).to_vec();
result.push(env_pair);
environ = environ.offset(1);
}
@ -234,14 +230,13 @@ pub fn load_self() -> Option<Vec<u8>> {
}
pub fn chdir(p: &Path) -> IoResult<()> {
p.with_c_str(|buf| {
unsafe {
match libc::chdir(buf) == (0 as c_int) {
true => Ok(()),
false => Err(IoError::last_error()),
}
let p = CString::from_slice(p.as_vec());
unsafe {
match libc::chdir(p.as_ptr()) == (0 as c_int) {
true => Ok(()),
false => Err(IoError::last_error()),
}
})
}
}
pub fn page_size() -> uint {

View file

@ -10,8 +10,8 @@
use prelude::v1::*;
use ffi::CString;
use libc;
use c_str::CString;
use mem;
use sync::{Arc, Mutex};
use sync::atomic::{AtomicBool, Ordering};
@ -48,7 +48,7 @@ fn addr_to_sockaddr_un(addr: &CString,
}
s.sun_family = libc::AF_UNIX as libc::sa_family_t;
for (slot, value) in s.sun_path.iter_mut().zip(addr.iter()) {
*slot = value;
*slot = *value;
}
// count the null terminator

View file

@ -11,8 +11,8 @@
use prelude::v1::*;
use self::Req::*;
use c_str::{CString, ToCStr};
use collections;
use ffi::CString;
use hash::Hash;
use io::process::{ProcessExit, ExitStatus, ExitSignal};
use io::{self, IoResult, IoError, EndOfFile};
@ -101,7 +101,7 @@ impl Process {
// We may use this in the child, so perform allocations before the
// fork
let devnull = "/dev/null".to_c_str();
let devnull = b"/dev/null\0";
set_cloexec(output.fd());
@ -204,7 +204,7 @@ impl Process {
} else {
libc::O_RDWR
};
libc::open(devnull.as_ptr(), flags, 0)
libc::open(devnull.as_ptr() as *const _, flags, 0)
}
Some(obj) => {
let fd = obj.as_inner().fd();

View file

@ -54,7 +54,7 @@ use libc;
use mem;
use os;
use ptr;
use sync::atomic::{mod, Ordering};
use sync::atomic::{self, Ordering};
use sync::mpsc::{channel, Sender, Receiver, TryRecvError};
use sys::c;
use sys::fs::FileDesc;

View file

@ -21,7 +21,8 @@
/// copy of that function in my mingw install (maybe it was broken?). Instead,
/// this takes the route of using StackWalk64 in order to walk the stack.
use c_str::CString;
use dynamic_lib::DynamicLibrary;
use ffi;
use intrinsics;
use io::{IoResult, Writer};
use libc;
@ -30,10 +31,9 @@ use ops::Drop;
use option::Option::{Some, None};
use path::Path;
use result::Result::{Ok, Err};
use sync::{StaticMutex, MUTEX_INIT};
use slice::SliceExt;
use str::StrExt;
use dynamic_lib::DynamicLibrary;
use str::{self, StrExt};
use sync::{StaticMutex, MUTEX_INIT};
use sys_common::backtrace::*;
@ -357,11 +357,11 @@ pub fn write(w: &mut Writer) -> IoResult<()> {
if ret == libc::TRUE {
try!(write!(w, " - "));
let cstr = unsafe { CString::new(info.Name.as_ptr(), false) };
let bytes = cstr.as_bytes();
match cstr.as_str() {
Some(s) => try!(demangle(w, s)),
None => try!(w.write(bytes[..bytes.len()-1])),
let ptr = info.Name.as_ptr() as *const libc::c_char;
let bytes = unsafe { ffi::c_str_to_bytes(&ptr) };
match str::from_utf8(bytes) {
Ok(s) => try!(demangle(w, s)),
Err(..) => try!(w.write(bytes[..bytes.len()-1])),
}
}
try!(w.write(&['\n' as u8]));

View file

@ -133,7 +133,7 @@ pub mod compat {
use intrinsics::{atomic_store_relaxed, transmute};
use libc::types::os::arch::extra::{LPCWSTR, HMODULE, LPCSTR, LPVOID};
use prelude::v1::*;
use c_str::ToCStr;
use ffi::CString;
extern "system" {
fn GetModuleHandleW(lpModuleName: LPCWSTR) -> HMODULE;
@ -147,14 +147,13 @@ pub mod compat {
unsafe fn store_func(ptr: *mut uint, module: &str, symbol: &str, fallback: uint) {
let mut module: Vec<u16> = module.utf16_units().collect();
module.push(0);
symbol.with_c_str(|symbol| {
let handle = GetModuleHandleW(module.as_ptr());
let func: uint = transmute(GetProcAddress(handle, symbol));
atomic_store_relaxed(ptr, if func == 0 {
fallback
} else {
func
})
let symbol = CString::from_slice(symbol.as_bytes());
let handle = GetModuleHandleW(module.as_ptr());
let func: uint = transmute(GetProcAddress(handle, symbol.as_ptr()));
atomic_store_relaxed(ptr, if func == 0 {
fallback
} else {
func
})
}

View file

@ -13,7 +13,6 @@
use alloc::arc::Arc;
use libc::{self, c_int};
use c_str::CString;
use mem;
use sys::os::fill_utf16_buf_and_decode;
use path;

View file

@ -87,16 +87,21 @@
use prelude::v1::*;
use libc;
use c_str::CString;
use ffi::CString;
use io::{self, IoError, IoResult};
use mem;
use ptr;
use sync::{Arc, Mutex};
use str;
use sync::atomic::{AtomicBool, Ordering};
use io::{self, IoError, IoResult};
use sync::{Arc, Mutex};
use sys_common::{self, eof};
use super::{c, os, timer, to_utf16, decode_error_detailed};
use super::{c, os, timer, decode_error_detailed};
fn to_utf16(c: &CString) -> IoResult<Vec<u16>> {
super::to_utf16(str::from_utf8(c.as_bytes()).ok())
}
struct Event(libc::HANDLE);
@ -270,7 +275,7 @@ impl UnixStream {
}
pub fn connect(addr: &CString, timeout: Option<u64>) -> IoResult<UnixStream> {
let addr = try!(to_utf16(addr.as_str()));
let addr = try!(to_utf16(addr));
let start = timer::now();
loop {
match UnixStream::try_connect(addr.as_ptr()) {
@ -571,7 +576,7 @@ impl UnixListener {
// Although we technically don't need the pipe until much later, we
// create the initial handle up front to test the validity of the name
// and such.
let addr_v = try!(to_utf16(addr.as_str()));
let addr_v = try!(to_utf16(addr));
let ret = unsafe { pipe(addr_v.as_ptr(), true) };
if ret == libc::INVALID_HANDLE_VALUE {
Err(super::last_error())
@ -661,7 +666,7 @@ impl UnixAcceptor {
// proceed in accepting new clients in the future
if self.inner.closed.load(Ordering::SeqCst) { return Err(eof()) }
let name = try!(to_utf16(self.listener.name.as_str()));
let name = try!(to_utf16(&self.listener.name));
// Once we've got a "server handle", we need to wait for a client to
// connect. The ConnectNamedPipe function will block this thread until
@ -753,7 +758,7 @@ impl UnixAcceptor {
impl Clone for UnixAcceptor {
fn clone(&self) -> UnixAcceptor {
let name = to_utf16(self.listener.name.as_str()).ok().unwrap();
let name = to_utf16(&self.listener.name).ok().unwrap();
UnixAcceptor {
inner: self.inner.clone(),
event: Event::new(true, false).ok().unwrap(),

View file

@ -10,27 +10,26 @@
use prelude::v1::*;
use collections;
use ffi::CString;
use hash::Hash;
use io::fs::PathExtensions;
use io::process::{ProcessExit, ExitStatus, ExitSignal};
use io::{IoResult, IoError};
use io;
use libc::{pid_t, c_void, c_int};
use libc;
use c_str::{CString, ToCStr};
use io;
use mem;
use os;
use ptr;
use io::process::{ProcessExit, ExitStatus, ExitSignal};
use collections;
use path::BytesContainer;
use hash::Hash;
use io::{IoResult, IoError};
use ptr;
use str;
use sys::fs::FileDesc;
use sys::fs;
use sys::{self, retry, c, wouldblock, set_nonblocking, ms_to_timeval, timer};
use sys::fs::FileDesc;
use sys_common::helper_thread::Helper;
use sys_common::{AsInner, mkerr_libc, timeout};
use io::fs::PathExtensions;
pub use sys_common::ProcessConfig;
/// A value representing a child process.
@ -142,10 +141,10 @@ impl Process {
// Split the value and test each path to see if the
// program exists.
for path in os::split_paths(v.container_as_bytes()).into_iter() {
let path = path.join(cfg.program().as_bytes_no_nul())
let path = path.join(cfg.program().as_bytes())
.with_extension(os::consts::EXE_EXTENSION);
if path.exists() {
return Some(path.to_c_str())
return Some(CString::from_slice(path.as_vec()))
}
}
break
@ -363,11 +362,11 @@ fn zeroed_process_information() -> libc::types::os::arch::extra::PROCESS_INFORMA
fn make_command_line(prog: &CString, args: &[CString]) -> String {
let mut cmd = String::new();
append_arg(&mut cmd, prog.as_str()
append_arg(&mut cmd, str::from_utf8(prog.as_bytes()).ok()
.expect("expected program name to be utf-8 encoded"));
for arg in args.iter() {
cmd.push(' ');
append_arg(&mut cmd, arg.as_str()
append_arg(&mut cmd, str::from_utf8(arg.as_bytes()).ok()
.expect("expected argument to be utf-8 encoded"));
}
return cmd;
@ -449,7 +448,7 @@ fn with_dirp<T, F>(d: Option<&CString>, cb: F) -> T where
{
match d {
Some(dir) => {
let dir_str = dir.as_str()
let dir_str = str::from_utf8(dir.as_bytes()).ok()
.expect("expected workingdirectory to be utf-8 encoded");
let mut dir_str: Vec<u16> = dir_str.utf16_units().collect();
dir_str.push(0);