From af650572e0fceb94b387db50ee31f19ee000fe4b Mon Sep 17 00:00:00 2001 From: Jeff Olson Date: Mon, 26 Aug 2013 07:24:10 -0700 Subject: [PATCH 01/21] std/rt: in-progress file io work std: remove unneeded field from RequestData struct std: rt::uv::file - map us_fs_stat & start refactoring calls into FsRequest std: stubbing out stat calls from the top-down into uvio std: us_fs_* operations are now by-val self methods on FsRequest std: post-rebase cleanup std: add uv_fs_mkdir|rmdir + tests & minor test cleanup in rt::uv::file WORKING: fleshing out FileStat and FileInfo + tests std: reverting test files.. refactoring back and cleanup... --- src/libstd/rt/io/file.rs | 162 ++++++++++++- src/libstd/rt/io/mod.rs | 24 ++ src/libstd/rt/rtio.rs | 9 +- src/libstd/rt/uv/file.rs | 510 +++++++++++++++++++++++++-------------- src/libstd/rt/uv/uvio.rs | 72 +++++- src/libstd/rt/uv/uvll.rs | 87 +++++++ src/rt/rust_uv.cpp | 42 ++++ src/rt/rustrt.def.in | 5 + 8 files changed, 708 insertions(+), 203 deletions(-) diff --git a/src/libstd/rt/io/file.rs b/src/libstd/rt/io/file.rs index c24f4eb257e1f..ee102c3a97ff8 100644 --- a/src/libstd/rt/io/file.rs +++ b/src/libstd/rt/io/file.rs @@ -11,12 +11,15 @@ use prelude::*; use super::support::PathLike; use super::{Reader, Writer, Seek}; -use super::{SeekSet, SeekCur, SeekEnd, SeekStyle}; +use super::{SeekStyle,SeekSet, SeekCur, SeekEnd, + Open, Read, Create, ReadWrite}; use rt::rtio::{RtioFileStream, IoFactory, IoFactoryObject}; use rt::io::{io_error, read_error, EndOfFile, - FileMode, FileAccess, Open, Read, Create, ReadWrite}; + FileMode, FileAccess, FileStat}; use rt::local::Local; -use rt::test::*; +use option::{Some, None}; +use path::Path; +use super::super::test::*; /// Open a file for reading/writing, as indicated by `path`. pub fn open(path: &P, @@ -145,6 +148,123 @@ impl Seek for FileStream { } } +pub struct FileInfo(Path); + +/// FIXME: DOCS +impl<'self> FileInfo { + pub fn new(path: &P) -> FileInfo { + do path.path_as_str |p| { + FileInfo(Path(p)) + } + } + // FIXME #8873 can't put this in FileSystemInfo + pub fn get_path(&'self self) -> &'self Path { + &(**self) + } + pub fn stat(&self) -> Option { + do io_error::cond.trap(|_| { + // FIXME: can we do something more useful here? + }).inside { + stat(self.get_path()) + } + } + pub fn exists(&self) -> bool { + match self.stat() { + Some(s) => { + match s.is_file { + true => { + true + }, + false => { + // FIXME: raise condition? + false + } + } + }, + None => false + } + } + pub fn is_file(&self) -> bool { + match self.stat() { + Some(s) => s.is_file, + None => { + // FIXME: raise condition + false + } + } + } + pub fn open(&self, mode: FileMode, access: FileAccess) -> Option { + match self.is_file() { + true => { + open(self.get_path(), mode, access) + }, + false => { + // FIXME: raise condition + None + } + } + } + //fn open_read(&self) -> FileStream; + //fn open_write(&self) -> FileStream; + //fn create(&self) -> FileStream; + //fn truncate(&self) -> FileStream; + //fn open_or_create(&self) -> FileStream; + //fn create_or_truncate(&self) -> FileStream; + //fn unlink(&self); +} + +/* +/// FIXME: DOCS +impl DirectoryInfo<'self> { + fn new(path: &P) -> FileInfo { + FileInfo(Path(path.path_as_str())) + } + // FIXME #8873 can't put this in FileSystemInfo + fn get_path(&'self self) -> &'self Path { + &*self + } + fn stat(&self) -> Option { + file::stat(self.get_path()) + } + fn exists(&self) -> bool { + do io_error::cond.trap(|_| { + }).inside { + match self.stat() { + Some(_) => true, + None => false + } + } + } + fn is_dir(&self) -> bool { + + } + fn create(&self); + fn get_subdirs(&self, filter: &str) -> ~[Path]; + fn get_files(&self, filter: &str) -> ~[Path]; +} +*/ + +/// Given a `rt::io::support::PathLike`, query the file system to get +/// information about a file, directory, etc. +/// +/// Returns a `Some(PathInfo)` on success, and raises a `rt::io::IoError` condition +/// on failure and returns `None`. +pub fn stat(path: &P) -> Option { + let open_result = unsafe { + let io: *mut IoFactoryObject = Local::unsafe_borrow(); + (*io).fs_stat(path) + }; + match open_result { + Ok(p) => { + Some(p) + }, + Err(ioerr) => { + read_error::cond.raise(ioerr); + None + } + } +} + fn file_test_smoke_test_impl() { do run_in_mt_newsched_task { let message = "it's alright. have a good time"; @@ -273,7 +393,6 @@ fn file_test_io_seek_and_tell_smoke_test() { } fn file_test_io_seek_and_write_impl() { - use io; do run_in_mt_newsched_task { use str; let initial_msg = "food-is-yummy"; @@ -293,8 +412,7 @@ fn file_test_io_seek_and_write_impl() { read_stream.read(read_mem); } unlink(filename); - let read_str = str::from_utf8(read_mem); - io::println(fmt!("read_str: '%?' final_msg: '%?'", read_str, final_msg)); + let read_str = str::from_bytes(read_mem); assert!(read_str == final_msg.to_owned()); } } @@ -343,3 +461,35 @@ fn file_test_io_seek_shakedown_impl() { fn file_test_io_seek_shakedown() { file_test_io_seek_shakedown_impl(); } + +#[test] +fn file_test_stat_is_correct_on_is_file() { + do run_in_newsched_task { + let filename = &Path("./tmp/file_stat_correct_on_is_file.txt"); + { + let mut fs = open(filename, Create, ReadWrite).unwrap(); + let msg = "hw"; + fs.write(msg.as_bytes()); + } + let stat_res = match stat(filename) { + Some(s) => s, + None => fail!("shouldn't happen") + }; + assert!(stat_res.is_file); + } +} + +#[test] +fn file_test_stat_is_correct_on_is_dir() { + //assert!(false); +} + +#[test] +fn file_test_fileinfo_false_when_checking_is_file_on_a_directory() { + //assert!(false); +} + +#[test] +fn file_test_fileinfo_check_exists_before_and_after_file_creation() { + //assert!(false); +} diff --git a/src/libstd/rt/io/mod.rs b/src/libstd/rt/io/mod.rs index 59ca5d5775980..55c7cfd8285bc 100644 --- a/src/libstd/rt/io/mod.rs +++ b/src/libstd/rt/io/mod.rs @@ -245,6 +245,7 @@ Out of scope use prelude::*; use to_str::ToStr; use str::{StrSlice, OwnedStr}; +use path::Path; // Reexports pub use self::stdio::stdin; @@ -596,3 +597,26 @@ pub enum FileAccess { Write, ReadWrite } + +pub struct FileStat { + /// A `Path` object containing information about the `PathInfo`'s location + path: Path, + /// `true` if the file pointed at by the `PathInfo` is a regular file + is_file: bool, + /// `true` if the file pointed at by the `PathInfo` is a directory + is_dir: bool + // `true` if the file pointed at by the `PathInfo` is a link (what this means + // is platform dependant) + /* + /// The file pointed at by the `PathInfo`'s size in bytes + size: u64, + /// The file pointed at by the `PathInfo`'s time date in platform-dependent msecs + created: u64, + /// The file pointed at by the `PathInfo`'s last-modification time in + /// platform-dependent msecs + modified: u64, + /// The file pointed at by the `PathInfo`'s last-accessd time (e.g. read) in + /// platform-dependent msecs + accessed: u64, + */ +} diff --git a/src/libstd/rt/rtio.rs b/src/libstd/rt/rtio.rs index c9c402baaf0fa..f08949e916586 100644 --- a/src/libstd/rt/rtio.rs +++ b/src/libstd/rt/rtio.rs @@ -18,7 +18,7 @@ use rt::uv::uvio; use path::Path; use super::io::support::PathLike; use super::io::{SeekStyle}; -use super::io::{FileMode, FileAccess}; +use super::io::{FileMode, FileAccess, FileStat}; // XXX: ~object doesn't work currently so these are some placeholder // types to use instead @@ -74,6 +74,13 @@ pub trait IoFactory { -> Result<~RtioFileStream, IoError>; fn fs_unlink(&mut self, path: &P) -> Result<(), IoError>; fn get_host_addresses(&mut self, host: &str) -> Result<~[IpAddr], IoError>; + fn fs_stat(&mut self, path: &P) -> Result; + //fn fs_fstat(&mut self, fd: c_int) -> Result; +} + +pub trait RtioStream { + fn read(&mut self, buf: &mut [u8]) -> Result; + fn write(&mut self, buf: &[u8]) -> Result<(), IoError>; } pub trait RtioTcpListener : RtioSocket { diff --git a/src/libstd/rt/uv/file.rs b/src/libstd/rt/uv/file.rs index e87e2d4b1e420..32e248254f4db 100644 --- a/src/libstd/rt/uv/file.rs +++ b/src/libstd/rt/uv/file.rs @@ -24,12 +24,11 @@ pub struct FsRequest(*uvll::uv_fs_t); impl Request for FsRequest; pub struct RequestData { - complete_cb: Option, - raw_fd: Option + complete_cb: Option } impl FsRequest { - pub fn new(cb: Option) -> FsRequest { + pub fn new_REFACTOR_ME(cb: Option) -> FsRequest { let fs_req = unsafe { malloc_req(UV_FS) }; assert!(fs_req.is_not_null()); let fs_req: FsRequest = NativeHandle::from_native_handle(fs_req); @@ -37,64 +36,174 @@ impl FsRequest { fs_req } - fn open_common(loop_: &Loop, path: &P, flags: int, mode: int, - cb: Option) -> int { - let complete_cb_ptr = match cb { - Some(_) => compl_cb as *u8, - None => 0 as *u8 - }; - let is_sync = cb.is_none(); - let req = FsRequest::new(cb); - let result = path.path_as_str(|p| { + pub fn new() -> FsRequest { + let fs_req = unsafe { malloc_req(UV_FS) }; + assert!(fs_req.is_not_null()); + let fs_req: FsRequest = NativeHandle::from_native_handle(fs_req); + fs_req + } + + pub fn open(self, loop_: &Loop, path: &P, flags: int, mode: int, + cb: FsCallback) { + let complete_cb_ptr = self.req_boilerplate(Some(cb)); + path.path_as_str(|p| { p.to_c_str().with_ref(|p| unsafe { uvll::fs_open(loop_.native_handle(), - req.native_handle(), p, flags, mode, complete_cb_ptr) as int + self.native_handle(), p, flags, mode, complete_cb_ptr) }) }); - if is_sync { req.cleanup_and_delete(); } - result } - pub fn open(loop_: &Loop, path: &P, flags: int, mode: int, - cb: FsCallback) { - FsRequest::open_common(loop_, path, flags, mode, Some(cb)); + + pub fn open_sync(self, loop_: &Loop, path: &P, + flags: int, mode: int) -> Result { + let complete_cb_ptr = self.req_boilerplate(None); + let result = path.path_as_str(|p| { + p.to_c_str().with_ref(|p| unsafe { + uvll::fs_open(loop_.native_handle(), + self.native_handle(), p, flags, mode, complete_cb_ptr) + }) + }); + self.sync_cleanup(result) } - pub fn open_sync(loop_: &Loop, path: &P, flags: int, mode: int) - -> Result { - let result = FsRequest::open_common(loop_, path, flags, mode, None); - sync_cleanup(result) + pub fn unlink(self, loop_: &Loop, path: &P, cb: FsCallback) { + let complete_cb_ptr = self.req_boilerplate(Some(cb)); + path.path_as_str(|p| { + p.to_c_str().with_ref(|p| unsafe { + uvll::fs_unlink(loop_.native_handle(), + self.native_handle(), p, complete_cb_ptr) + }) + }); } - fn unlink_common(loop_: &Loop, path: &P, cb: Option) -> int { - let complete_cb_ptr = match cb { - Some(_) => compl_cb as *u8, - None => 0 as *u8 - }; - let is_sync = cb.is_none(); - let req = FsRequest::new(cb); + pub fn unlink_sync(self, loop_: &Loop, path: &P) + -> Result { + let complete_cb_ptr = self.req_boilerplate(None); let result = path.path_as_str(|p| { p.to_c_str().with_ref(|p| unsafe { uvll::fs_unlink(loop_.native_handle(), - req.native_handle(), p, complete_cb_ptr) as int + self.native_handle(), p, complete_cb_ptr) }) }); - if is_sync { req.cleanup_and_delete(); } - result + self.sync_cleanup(result) + } + + pub fn stat(self, loop_: &Loop, path: &P, cb: FsCallback) { + let complete_cb_ptr = self.req_boilerplate(Some(cb)); + path.path_as_str(|p| { + p.to_c_str().with_ref(|p| unsafe { + uvll::fs_stat(loop_.native_handle(), + self.native_handle(), p, complete_cb_ptr) + }) + }); + } + + pub fn write(self, loop_: &Loop, fd: c_int, buf: Buf, offset: i64, cb: FsCallback) { + let complete_cb_ptr = self.req_boilerplate(Some(cb)); + let base_ptr = buf.base as *c_void; + let len = buf.len as uint; + unsafe { + uvll::fs_write(loop_.native_handle(), self.native_handle(), + fd, base_ptr, + len, offset, complete_cb_ptr) + }; + } + pub fn write_sync(self, loop_: &Loop, fd: c_int, buf: Buf, offset: i64) + -> Result { + let complete_cb_ptr = self.req_boilerplate(None); + let base_ptr = buf.base as *c_void; + let len = buf.len as uint; + let result = unsafe { + uvll::fs_write(loop_.native_handle(), self.native_handle(), + fd, base_ptr, + len, offset, complete_cb_ptr) + }; + self.sync_cleanup(result) + } + + pub fn read(self, loop_: &Loop, fd: c_int, buf: Buf, offset: i64, cb: FsCallback) { + let complete_cb_ptr = self.req_boilerplate(Some(cb)); + let buf_ptr = buf.base as *c_void; + let len = buf.len as uint; + unsafe { + uvll::fs_read(loop_.native_handle(), self.native_handle(), + fd, buf_ptr, + len, offset, complete_cb_ptr) + }; + } + pub fn read_sync(self, loop_: &Loop, fd: c_int, buf: Buf, offset: i64) + -> Result { + let complete_cb_ptr = self.req_boilerplate(None); + let buf_ptr = buf.base as *c_void; + let len = buf.len as uint; + let result = unsafe { + uvll::fs_read(loop_.native_handle(), self.native_handle(), + fd, buf_ptr, + len, offset, complete_cb_ptr) + }; + self.sync_cleanup(result) + } + + pub fn close(self, loop_: &Loop, fd: c_int, cb: FsCallback) { + let complete_cb_ptr = self.req_boilerplate(Some(cb)); + unsafe { + uvll::fs_close(loop_.native_handle(), self.native_handle(), + fd, complete_cb_ptr) + }; + } + pub fn close_sync(self, loop_: &Loop, fd: c_int) -> Result { + let complete_cb_ptr = self.req_boilerplate(None); + let result = unsafe { + uvll::fs_close(loop_.native_handle(), self.native_handle(), + fd, complete_cb_ptr) + }; + self.sync_cleanup(result) } - pub fn unlink(loop_: &Loop, path: &P, cb: FsCallback) { - let result = FsRequest::unlink_common(loop_, path, Some(cb)); - sync_cleanup(result); + + pub fn mkdir(self, loop_: &Loop, path: &P, mode: int, cb: FsCallback) { + let complete_cb_ptr = self.req_boilerplate(Some(cb)); + path.path_as_str(|p| { + p.to_c_str().with_ref(|p| unsafe { + uvll::fs_mkdir(loop_.native_handle(), + self.native_handle(), p, mode, complete_cb_ptr) + }) + }); } - pub fn unlink_sync(loop_: &Loop, path: &P) -> Result { - let result = FsRequest::unlink_common(loop_, path, None); - sync_cleanup(result) + + pub fn rmdir(self, loop_: &Loop, path: &P, cb: FsCallback) { + let complete_cb_ptr = self.req_boilerplate(Some(cb)); + path.path_as_str(|p| { + p.to_c_str().with_ref(|p| unsafe { + uvll::fs_rmdir(loop_.native_handle(), + self.native_handle(), p, complete_cb_ptr) + }) + }); } + // accessors/utility funcs + fn sync_cleanup(self, result: c_int) + -> Result { + self.cleanup_and_delete(); + match status_to_maybe_uv_error(result as i32) { + Some(err) => Err(err), + None => Ok(result) + } + } + fn req_boilerplate(&self, cb: Option) -> *u8 { + // XXX: this is unsafe/mutable + let result = match cb { + Some(_) => { + compl_cb as *u8 + }, + None => 0 as *u8 + }; + self.install_req_data(cb); + result + } pub fn install_req_data(&self, cb: Option) { let fs_req = (self.native_handle()) as *uvll::uv_write_t; let data = ~RequestData { - complete_cb: cb, - raw_fd: None + complete_cb: cb }; unsafe { let data = transmute::<~RequestData, *c_void>(data); @@ -106,7 +215,7 @@ impl FsRequest { unsafe { let data = uvll::get_data_for_req((self.native_handle())); let data = transmute::<&*c_void, &mut ~RequestData>(&data); - return &mut **data; + &mut **data } } @@ -120,6 +229,12 @@ impl FsRequest { unsafe { Loop{handle:uvll::get_loop_from_fs_req(self.native_handle())} } } + pub fn get_stat(&self) -> uv_stat_t { + let stat = uv_stat_t::new(); + unsafe { uvll::populate_stat(self.native_handle(), &stat); } + stat + } + fn cleanup_and_delete(self) { unsafe { let data = uvll::get_data_for_req(self.native_handle()); @@ -145,100 +260,7 @@ fn sync_cleanup(result: int) match status_to_maybe_uv_error(result as i32) { Some(err) => Err(err), None => Ok(result) - } -} - -pub struct FileDescriptor(c_int); - -impl FileDescriptor { - fn new(fd: c_int) -> FileDescriptor { - FileDescriptor(fd) - } - - - pub fn from_open_req(req: &mut FsRequest) -> FileDescriptor { - FileDescriptor::new(req.get_result()) - } - - // as per bnoordhuis in #libuv: offset >= 0 uses prwrite instead of write - fn write_common(&mut self, loop_: &Loop, buf: Buf, offset: i64, cb: Option) - -> int { - let complete_cb_ptr = match cb { - Some(_) => compl_cb as *u8, - None => 0 as *u8 - }; - let is_sync = cb.is_none(); - let mut req = FsRequest::new(cb); - let base_ptr = buf.base as *c_void; - let len = buf.len as uint; - req.get_req_data().raw_fd = Some(self.native_handle()); - let result = unsafe { - uvll::fs_write(loop_.native_handle(), req.native_handle(), - self.native_handle(), base_ptr, - len, offset, complete_cb_ptr) as int - }; - if is_sync { req.cleanup_and_delete(); } - result - } - pub fn write(&mut self, loop_: &Loop, buf: Buf, offset: i64, cb: FsCallback) { - self.write_common(loop_, buf, offset, Some(cb)); - } - pub fn write_sync(&mut self, loop_: &Loop, buf: Buf, offset: i64) - -> Result { - let result = self.write_common(loop_, buf, offset, None); - sync_cleanup(result) - } - - fn read_common(&mut self, loop_: &Loop, buf: Buf, - offset: i64, cb: Option) - -> int { - let complete_cb_ptr = match cb { - Some(_) => compl_cb as *u8, - None => 0 as *u8 - }; - let is_sync = cb.is_none(); - let mut req = FsRequest::new(cb); - req.get_req_data().raw_fd = Some(self.native_handle()); - let buf_ptr = buf.base as *c_void; - let result = unsafe { - uvll::fs_read(loop_.native_handle(), req.native_handle(), - self.native_handle(), buf_ptr, - buf.len as uint, offset, complete_cb_ptr) as int - }; - if is_sync { req.cleanup_and_delete(); } - result - } - pub fn read(&mut self, loop_: &Loop, buf: Buf, offset: i64, cb: FsCallback) { - self.read_common(loop_, buf, offset, Some(cb)); - } - pub fn read_sync(&mut self, loop_: &Loop, buf: Buf, offset: i64) - -> Result { - let result = self.read_common(loop_, buf, offset, None); - sync_cleanup(result) - } - fn close_common(self, loop_: &Loop, cb: Option) -> int { - let complete_cb_ptr = match cb { - Some(_) => compl_cb as *u8, - None => 0 as *u8 - }; - let is_sync = cb.is_none(); - let req = FsRequest::new(cb); - let result = unsafe { - uvll::fs_close(loop_.native_handle(), req.native_handle(), - self.native_handle(), complete_cb_ptr) as int - }; - if is_sync { req.cleanup_and_delete(); } - result - } - pub fn close(self, loop_: &Loop, cb: FsCallback) { - self.close_common(loop_, Some(cb)); - } - pub fn close_sync(self, loop_: &Loop) -> Result { - let result = self.close_common(loop_, None); - sync_cleanup(result) - } -} extern fn compl_cb(req: *uv_fs_t) { let mut req: FsRequest = NativeHandle::from_native_handle(req); // pull the user cb out of the req data @@ -261,15 +283,7 @@ extern fn compl_cb(req: *uv_fs_t) { req.cleanup_and_delete(); } -impl NativeHandle for FileDescriptor { - fn from_native_handle(handle: c_int) -> FileDescriptor { - FileDescriptor(handle) - } - fn native_handle(&self) -> c_int { - match self { &FileDescriptor(ptr) => ptr } - } -} - +#[cfg(test)] mod test { use super::*; //use rt::test::*; @@ -279,11 +293,12 @@ mod test { use unstable::run_in_bare_thread; use path::Path; use rt::uv::{Loop, Buf, slice_to_uv_buf}; - use libc::{O_CREAT, O_RDWR, O_RDONLY, - S_IWUSR, S_IRUSR}; //NOTE: need defs for S_**GRP|S_**OTH in libc:: ... - //S_IRGRP, S_IROTH}; + use libc::{c_int, O_CREAT, O_RDWR, O_RDONLY, + S_IWUSR, S_IRUSR}; - fn file_test_full_simple_impl() { + #[test] + #[ignore(cfg(windows))] // FIXME #8814 + fn file_test_full_simple() { do run_in_bare_thread { let mut loop_ = Loop::new(); let create_flags = O_RDWR | O_CREAT; @@ -302,25 +317,27 @@ mod test { let read_buf = slice_to_uv_buf(read_mem); let read_buf_ptr: *Buf = &read_buf; let p = Path(path_str); - do FsRequest::open(&loop_, &p, create_flags as int, mode as int) + let open_req = FsRequest::new(); + do open_req.open(&loop_, &p, create_flags as int, mode as int) |req, uverr| { assert!(uverr.is_none()); - let mut fd = FileDescriptor::from_open_req(req); - let raw_fd = fd.native_handle(); + let fd = req.get_result(); let buf = unsafe { *write_buf_ptr }; - do fd.write(&req.get_loop(), buf, -1) |req, uverr| { - let fd = FileDescriptor(raw_fd); - do fd.close(&req.get_loop()) |req, _| { - let loop_ = req.get_loop(); + let write_req = FsRequest::new(); + do write_req.write(&req.get_loop(), fd, buf, -1) |req, uverr| { + let close_req = FsRequest::new(); + do close_req.close(&req.get_loop(), fd) |req, _| { assert!(uverr.is_none()); - do FsRequest::open(&loop_, &Path(path_str), read_flags as int,0) + let loop_ = req.get_loop(); + let open_req = FsRequest::new(); + do open_req.open(&loop_, &Path(path_str), read_flags as int,0) |req, uverr| { assert!(uverr.is_none()); let loop_ = req.get_loop(); - let mut fd = FileDescriptor::from_open_req(req); - let raw_fd = fd.native_handle(); + let fd = req.get_result(); let read_buf = unsafe { *read_buf_ptr }; - do fd.read(&loop_, read_buf, 0) |req, uverr| { + let read_req = FsRequest::new(); + do read_req.read(&loop_, fd, read_buf, 0) |req, uverr| { assert!(uverr.is_none()); let loop_ = req.get_loop(); // we know nread >=0 because uverr is none.. @@ -334,15 +351,17 @@ mod test { read_buf.base, nread)) }; assert!(read_str == ~"hello"); - do FileDescriptor(raw_fd).close(&loop_) |req,uverr| { + let close_req = FsRequest::new(); + do close_req.close(&loop_, fd) |req,uverr| { assert!(uverr.is_none()); let loop_ = &req.get_loop(); - do FsRequest::unlink(loop_, &Path(path_str)) + let unlink_req = FsRequest::new(); + do unlink_req.unlink(loop_, &Path(path_str)) |_,uverr| { assert!(uverr.is_none()); }; }; - } + }; }; }; }; @@ -352,7 +371,10 @@ mod test { loop_.close(); } } - fn file_test_full_simple_impl_sync() { + + #[test] + #[ignore(cfg(windows))] // FIXME #8814 + fn file_test_full_simple_sync() { do run_in_bare_thread { // setup let mut loop_ = Loop::new(); @@ -368,26 +390,31 @@ mod test { let write_val = "hello".as_bytes().to_owned(); let write_buf = slice_to_uv_buf(write_val); // open/create - let result = FsRequest::open_sync(&loop_, &Path(path_str), + let open_req = FsRequest::new(); + let result = open_req.open_sync(&loop_, &Path(path_str), create_flags as int, mode as int); assert!(result.is_ok()); - let mut fd = FileDescriptor(result.unwrap() as i32); + let fd = result.unwrap(); // write - let result = fd.write_sync(&loop_, write_buf, -1); + let write_req = FsRequest::new(); + let result = write_req.write_sync(&loop_, fd, write_buf, -1); assert!(result.is_ok()); // close - let result = fd.close_sync(&loop_); + let close_req = FsRequest::new(); + let result = close_req.close_sync(&loop_, fd); assert!(result.is_ok()); // re-open - let result = FsRequest::open_sync(&loop_, &Path(path_str), + let open_req = FsRequest::new(); + let result = open_req.open_sync(&loop_, &Path(path_str), read_flags as int,0); assert!(result.is_ok()); let len = 1028; - let mut fd = FileDescriptor(result.unwrap() as i32); + let fd = result.unwrap(); // read let read_mem: ~[u8] = vec::from_elem(len, 0u8); let buf = slice_to_uv_buf(read_mem); - let result = fd.read_sync(&loop_, buf, 0); + let read_req = FsRequest::new(); + let result = read_req.read_sync(&loop_, fd, buf, 0); assert!(result.is_ok()); let nread = result.unwrap(); // nread == 0 would be EOF.. we know it's >= zero because otherwise @@ -397,31 +424,23 @@ mod test { read_mem.slice(0, nread as uint)); assert!(read_str == ~"hello"); // close - let result = fd.close_sync(&loop_); + let close_req = FsRequest::new(); + let result = close_req.close_sync(&loop_, fd); assert!(result.is_ok()); // unlink - let result = FsRequest::unlink_sync(&loop_, &Path(path_str)); + let unlink_req = FsRequest::new(); + let result = unlink_req.unlink_sync(&loop_, &Path(path_str)); assert!(result.is_ok()); } else { fail!("nread was 0.. wudn't expectin' that."); } loop_.close(); } } - #[test] - fn file_test_full_simple() { - file_test_full_simple_impl(); - } - - #[test] - fn file_test_full_simple_sync() { - file_test_full_simple_impl_sync(); - } - fn naive_print(loop_: &Loop, input: &str) { - let mut stdout = FileDescriptor(STDOUT_FILENO); let write_val = input.as_bytes(); let write_buf = slice_to_uv_buf(write_val); - stdout.write_sync(loop_, write_buf, -1); + let write_req = FsRequest::new(); + write_req.write_sync(loop_, stdout, write_buf, -1); } #[test] @@ -433,4 +452,129 @@ mod test { loop_.close(); }; } + #[test] + fn file_test_stat_simple() { + do run_in_bare_thread { + let mut loop_ = Loop::new(); + let path = "./tmp/file_test_stat_simple.txt"; + let create_flags = O_RDWR | + O_CREAT; + let mode = S_IWUSR | + S_IRUSR; + let write_val = "hello".as_bytes().to_owned(); + let write_buf = slice_to_uv_buf(write_val); + let write_buf_ptr: *Buf = &write_buf; + let open_req = FsRequest::new(); + do open_req.open(&loop_, &path, create_flags as int, mode as int) + |req, uverr| { + assert!(uverr.is_none()); + let fd = req.get_result(); + let buf = unsafe { *write_buf_ptr }; + let write_req = FsRequest::new(); + do write_req.write(&req.get_loop(), fd, buf, 0) |req, uverr| { + assert!(uverr.is_none()); + let loop_ = req.get_loop(); + let stat_req = FsRequest::new(); + do stat_req.stat(&loop_, &path) |req, uverr| { + assert!(uverr.is_none()); + let loop_ = req.get_loop(); + let stat = req.get_stat(); + let sz: uint = stat.st_size as uint; + assert!(sz > 0); + let close_req = FsRequest::new(); + do close_req.close(&loop_, fd) |req, uverr| { + assert!(uverr.is_none()); + let loop_ = req.get_loop(); + let unlink_req = FsRequest::new(); + do unlink_req.unlink(&loop_, &path) |req,uverr| { + assert!(uverr.is_none()); + let loop_ = req.get_loop(); + let stat_req = FsRequest::new(); + do stat_req.stat(&loop_, &path) |_, uverr| { + // should cause an error because the + // file doesn't exist anymore + assert!(uverr.is_some()); + }; + }; + }; + }; + }; + }; + loop_.run(); + loop_.close(); + } + } + + #[test] + fn file_test_mk_rm_dir() { + do run_in_bare_thread { + let mut loop_ = Loop::new(); + let path = "./tmp/mk_rm_dir"; + let mode = S_IWUSR | + S_IRUSR; + let mkdir_req = FsRequest::new(); + do mkdir_req.mkdir(&loop_, &path, mode as int) |req,uverr| { + assert!(uverr.is_none()); + let loop_ = req.get_loop(); + let stat_req = FsRequest::new(); + do stat_req.stat(&loop_, &path) |req, uverr| { + assert!(uverr.is_none()); + let loop_ = req.get_loop(); + let stat = req.get_stat(); + assert!(stat.is_dir()); + let rmdir_req = FsRequest::new(); + do rmdir_req.rmdir(&loop_, &path) |req,uverr| { + assert!(uverr.is_none()); + let loop_ = req.get_loop(); + let stat_req = FsRequest::new(); + do stat_req.stat(&loop_, &path) |req, uverr| { + assert!(uverr.is_some()); + } + } + } + } + loop_.run(); + loop_.close(); + } + } + #[test] + fn file_test_mkdir_chokes_on_double_create() { + do run_in_bare_thread { + let mut loop_ = Loop::new(); + let path = "./tmp/double_create_dir"; + let mode = S_IWUSR | + S_IRUSR; + let mkdir_req = FsRequest::new(); + do mkdir_req.mkdir(&loop_, &path, mode as int) |req,uverr| { + assert!(uverr.is_none()); + let loop_ = req.get_loop(); + let mkdir_req = FsRequest::new(); + do mkdir_req.mkdir(&loop_, &path, mode as int) |req,uverr| { + assert!(uverr.is_some()); + let loop_ = req.get_loop(); + let stat = req.get_stat(); + let rmdir_req = FsRequest::new(); + do rmdir_req.rmdir(&loop_, &path) |req,uverr| { + assert!(uverr.is_none()); + let loop_ = req.get_loop(); + } + } + } + loop_.run(); + loop_.close(); + } + } + #[test] + fn file_test_rmdir_chokes_on_nonexistant_path() { + do run_in_bare_thread { + let mut loop_ = Loop::new(); + let path = "./tmp/never_existed_dir"; + let rmdir_req = FsRequest::new(); + do rmdir_req.rmdir(&loop_, &path) |req,uverr| { + assert!(uverr.is_some()); + } + loop_.run(); + loop_.close(); + } + } } diff --git a/src/libstd/rt/uv/uvio.rs b/src/libstd/rt/uv/uvio.rs index b930ea2437ea8..4307c57529b5f 100644 --- a/src/libstd/rt/uv/uvio.rs +++ b/src/libstd/rt/uv/uvio.rs @@ -32,11 +32,13 @@ use rt::uv::idle::IdleWatcher; use rt::uv::net::{UvIpv4SocketAddr, UvIpv6SocketAddr, accum_sockaddrs}; use rt::uv::addrinfo::GetAddrInfoRequest; use unstable::sync::Exclusive; +use path::Path; use super::super::io::support::PathLike; use libc::{lseek, off_t, O_CREAT, O_APPEND, O_TRUNC, O_RDWR, O_RDONLY, O_WRONLY, S_IRUSR, S_IWUSR}; use rt::io::{FileMode, FileAccess, OpenOrCreate, Open, Create, - CreateOrTruncate, Append, Truncate, Read, Write, ReadWrite}; + CreateOrTruncate, Append, Truncate, Read, Write, ReadWrite, + FileStat}; use task; #[cfg(test)] use container::Container; @@ -516,7 +518,6 @@ impl IoFactory for UvIoFactory { fn fs_from_raw_fd(&mut self, fd: c_int, close_on_drop: bool) -> ~RtioFileStream { let loop_ = Loop {handle: self.uv_loop().native_handle()}; - let fd = file::FileDescriptor(fd); let home = get_handle_to_current_scheduler!(); ~UvFileStream::new(loop_, fd, close_on_drop, home) as ~RtioFileStream } @@ -547,15 +548,16 @@ impl IoFactory for UvIoFactory { let path_cell = Cell::new(path); do task::unkillable { // FIXME(#8674) let scheduler: ~Scheduler = Local::take(); + let open_req = file::FsRequest::new(); do scheduler.deschedule_running_task_and_then |_, task| { let task_cell = Cell::new(task); let path = path_cell.take(); - do file::FsRequest::open(self.uv_loop(), path, flags as int, create_mode as int) + do open_req.open(self.uv_loop(), path, flags as int, create_mode as int) |req,err| { if err.is_none() { let loop_ = Loop {handle: req.get_loop().native_handle()}; let home = get_handle_to_current_scheduler!(); - let fd = file::FileDescriptor(req.get_result()); + let fd = req.get_result() as c_int; let fs = ~UvFileStream::new( loop_, fd, true, home) as ~RtioFileStream; let res = Ok(fs); @@ -570,7 +572,7 @@ impl IoFactory for UvIoFactory { } }; }; - } + }; assert!(!result_cell.is_empty()); return result_cell.take(); } @@ -581,10 +583,11 @@ impl IoFactory for UvIoFactory { let path_cell = Cell::new(path); do task::unkillable { // FIXME(#8674) let scheduler: ~Scheduler = Local::take(); + let unlink_req = FsRequest::new(); do scheduler.deschedule_running_task_and_then |_, task| { let task_cell = Cell::new(task); let path = path_cell.take(); - do file::FsRequest::unlink(self.uv_loop(), path) |_, err| { + do unlink_req.unlink(self.uv_loop(), path) |_, err| { let res = match err { None => Ok(()), Some(err) => Err(uv_error_to_io_error(err)) @@ -593,11 +596,48 @@ impl IoFactory for UvIoFactory { let scheduler: ~Scheduler = Local::take(); scheduler.resume_blocked_task_immediately(task_cell.take()); }; - }; + } } assert!(!result_cell.is_empty()); return result_cell.take(); } + fn fs_stat(&mut self, path: &P) -> Result { + use str::StrSlice; + let result_cell = Cell::new_empty(); + let result_cell_ptr: *Cell> = &result_cell; + let path_cell = Cell::new(path); + do task::unkillable { // FIXME(#8674) + let scheduler: ~Scheduler = Local::take(); + let stat_req = file::FsRequest::new(); + do scheduler.deschedule_running_task_and_then |_, task| { + let task_cell = Cell::new(task); + let path = path_cell.take(); + let path_str = path.path_as_str(|p| p.to_owned()); + do stat_req.stat(self.uv_loop(), path) + |req,err| { + if err.is_none() { + let stat = req.get_stat(); + let res = Ok(FileStat { + path: Path(path_str), + is_file: stat.is_file(), + is_dir: stat.is_dir() + }); + unsafe { (*result_cell_ptr).put_back(res); } + let scheduler: ~Scheduler = Local::take(); + scheduler.resume_blocked_task_immediately(task_cell.take()); + } else { + let res = Err(uv_error_to_io_error(err.unwrap())); + unsafe { (*result_cell_ptr).put_back(res); } + let scheduler: ~Scheduler = Local::take(); + scheduler.resume_blocked_task_immediately(task_cell.take()); + } + }; + }; + }; + assert!(!result_cell.is_empty()); + return result_cell.take(); + } fn get_host_addresses(&mut self, host: &str) -> Result<~[IpAddr], IoError> { let result_cell = Cell::new_empty(); @@ -629,6 +669,9 @@ impl IoFactory for UvIoFactory { assert!(!result_cell.is_empty()); return result_cell.take(); } + //fn fs_fstat(&mut self, _fd: c_int) -> Result { + // Ok(FileStat) + //} } pub struct UvTcpListener { @@ -1173,7 +1216,7 @@ impl RtioTimer for UvTimer { pub struct UvFileStream { loop_: Loop, - fd: file::FileDescriptor, + fd: c_int, close_on_drop: bool, home: SchedHandle } @@ -1183,7 +1226,7 @@ impl HomingIO for UvFileStream { } impl UvFileStream { - fn new(loop_: Loop, fd: file::FileDescriptor, close_on_drop: bool, + fn new(loop_: Loop, fd: c_int, close_on_drop: bool, home: SchedHandle) -> UvFileStream { UvFileStream { loop_: loop_, @@ -1200,7 +1243,8 @@ impl UvFileStream { do scheduler.deschedule_running_task_and_then |_, task| { let buf = unsafe { slice_to_uv_buf(*buf_ptr) }; let task_cell = Cell::new(task); - do self_.fd.read(&self_.loop_, buf, offset) |req, uverr| { + let read_req = file::FsRequest::new(); + do read_req.read(&self_.loop_, self_.fd, buf, offset) |req, uverr| { let res = match uverr { None => Ok(req.get_result() as int), Some(err) => Err(uv_error_to_io_error(err)) @@ -1221,7 +1265,8 @@ impl UvFileStream { do scheduler.deschedule_running_task_and_then |_, task| { let buf = unsafe { slice_to_uv_buf(*buf_ptr) }; let task_cell = Cell::new(task); - do self_.fd.write(&self_.loop_, buf, offset) |_, uverr| { + let write_req = file::FsRequest::new(); + do write_req.write(&self_.loop_, self_.fd, buf, offset) |_, uverr| { let res = match uverr { None => Ok(()), Some(err) => Err(uv_error_to_io_error(err)) @@ -1238,7 +1283,7 @@ impl UvFileStream { Result{ #[fixed_stack_segment]; #[inline(never)]; unsafe { - match lseek((*self.fd), pos as off_t, whence) { + match lseek(self.fd, pos as off_t, whence) { -1 => { Err(IoError { kind: OtherIoError, @@ -1259,7 +1304,8 @@ impl Drop for UvFileStream { do self_.home_for_io_with_sched |self_, scheduler| { do scheduler.deschedule_running_task_and_then |_, task| { let task_cell = Cell::new(task); - do self_.fd.close(&self.loop_) |_,_| { + let close_req = file::FsRequest::new(); + do close_req.close(&self.loop_, self_.fd) |_,_| { let scheduler: ~Scheduler = Local::take(); scheduler.resume_blocked_task_immediately(task_cell.take()); }; diff --git a/src/libstd/rt/uv/uvll.rs b/src/libstd/rt/uv/uvll.rs index 8f3cef4d23883..89ee54be349ad 100644 --- a/src/libstd/rt/uv/uvll.rs +++ b/src/libstd/rt/uv/uvll.rs @@ -96,6 +96,59 @@ pub type uv_fs_t = c_void; pub type uv_udp_send_t = c_void; pub type uv_getaddrinfo_t = c_void; +pub struct uv_timespec_t { + tv_sec: libc::c_long, + tv_nsec: libc::c_long +} + +pub struct uv_stat_t { + st_dev: libc::uint64_t, + st_mode: libc::uint64_t, + st_nlink: libc::uint64_t, + st_uid: libc::uint64_t, + st_gid: libc::uint64_t, + st_rdev: libc::uint64_t, + st_ino: libc::uint64_t, + st_size: libc::uint64_t, + st_blksize: libc::uint64_t, + st_blocks: libc::uint64_t, + st_flags: libc::uint64_t, + st_gen: libc::uint64_t, + st_atim: uv_timespec_t, + st_mtim: uv_timespec_t, + st_ctim: uv_timespec_t, + st_birthtim: uv_timespec_t +} + +impl uv_stat_t { + pub fn new() -> uv_stat_t { + uv_stat_t { + st_dev: 0, + st_mode: 0, + st_nlink: 0, + st_uid: 0, + st_gid: 0, + st_rdev: 0, + st_ino: 0, + st_size: 0, + st_blksize: 0, + st_blocks: 0, + st_flags: 0, + st_gen: 0, + st_atim: uv_timespec_t { tv_sec: 0, tv_nsec: 0 }, + st_mtim: uv_timespec_t { tv_sec: 0, tv_nsec: 0 }, + st_ctim: uv_timespec_t { tv_sec: 0, tv_nsec: 0 }, + st_birthtim: uv_timespec_t { tv_sec: 0, tv_nsec: 0 } + } + } + pub fn is_file(&self) -> bool { + ((self.st_mode as c_int) & libc::S_IFMT) == libc::S_IFREG + } + pub fn is_dir(&self) -> bool { + ((self.st_mode as c_int) & libc::S_IFMT) == libc::S_IFDIR + } +} + #[cfg(stage0)] pub type uv_idle_cb = *u8; #[cfg(stage0)] @@ -736,6 +789,33 @@ pub unsafe fn fs_close(loop_ptr: *uv_loop_t, req: *uv_fs_t, fd: c_int, rust_uv_fs_close(loop_ptr, req, fd, cb) } +pub unsafe fn fs_stat(loop_ptr: *uv_loop_t, req: *uv_fs_t, path: *c_char, cb: *u8) -> c_int { + #[fixed_stack_segment]; #[inline(never)]; + + rust_uv_fs_stat(loop_ptr, req, path, cb) +} +pub unsafe fn fs_fstat(loop_ptr: *uv_loop_t, req: *uv_fs_t, fd: c_int, cb: *u8) -> c_int { + #[fixed_stack_segment]; #[inline(never)]; + + rust_uv_fs_fstat(loop_ptr, req, fd, cb) +} +pub unsafe fn fs_mkdir(loop_ptr: *uv_loop_t, req: *uv_fs_t, path: *c_char, mode: int, + cb: *u8) -> c_int { + #[fixed_stack_segment]; #[inline(never)]; + + rust_uv_fs_mkdir(loop_ptr, req, path, mode as c_int, cb) +} +pub unsafe fn fs_rmdir(loop_ptr: *uv_loop_t, req: *uv_fs_t, path: *c_char, + cb: *u8) -> c_int { + #[fixed_stack_segment]; #[inline(never)]; + + rust_uv_fs_rmdir(loop_ptr, req, path, cb) +} +pub unsafe fn populate_stat(req_in: *uv_fs_t, stat_out: *uv_stat_t) { + #[fixed_stack_segment]; #[inline(never)]; + + rust_uv_populate_uv_stat(req_in, stat_out) +} pub unsafe fn fs_req_cleanup(req: *uv_fs_t) { #[fixed_stack_segment]; #[inline(never)]; @@ -928,7 +1008,14 @@ extern { buf: *c_void, len: c_uint, offset: i64, cb: *u8) -> c_int; fn rust_uv_fs_close(loop_ptr: *c_void, req: *uv_fs_t, fd: c_int, cb: *u8) -> c_int; + fn rust_uv_fs_stat(loop_ptr: *c_void, req: *uv_fs_t, path: *c_char, cb: *u8) -> c_int; + fn rust_uv_fs_fstat(loop_ptr: *c_void, req: *uv_fs_t, fd: c_int, cb: *u8) -> c_int; + fn rust_uv_fs_mkdir(loop_ptr: *c_void, req: *uv_fs_t, path: *c_char, + mode: c_int, cb: *u8) -> c_int; + fn rust_uv_fs_rmdir(loop_ptr: *c_void, req: *uv_fs_t, path: *c_char, + cb: *u8) -> c_int; fn rust_uv_fs_req_cleanup(req: *uv_fs_t); + fn rust_uv_populate_uv_stat(req_in: *uv_fs_t, stat_out: *uv_stat_t); fn rust_uv_get_result_from_fs_req(req: *uv_fs_t) -> c_int; fn rust_uv_get_loop_from_fs_req(req: *uv_fs_t) -> *uv_loop_t; fn rust_uv_get_loop_from_getaddrinfo_req(req: *uv_fs_t) -> *uv_loop_t; diff --git a/src/rt/rust_uv.cpp b/src/rt/rust_uv.cpp index bfdf0e67a9b81..ebc76c84ec9ff 100644 --- a/src/rt/rust_uv.cpp +++ b/src/rt/rust_uv.cpp @@ -551,3 +551,45 @@ extern "C" uv_loop_t* rust_uv_get_loop_from_getaddrinfo_req(uv_getaddrinfo_t* req) { return req->loop; } + +extern "C" int +rust_uv_fs_stat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { + return uv_fs_stat(loop, req, path, cb); +} +extern "C" int +rust_uv_fs_fstat(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) { + return uv_fs_fstat(loop, req, file, cb); +} + +extern "C" void +rust_uv_populate_uv_stat(uv_fs_t* req_in, uv_stat_t* stat_out) { + stat_out->st_dev = req_in->statbuf.st_dev; + stat_out->st_mode = req_in->statbuf.st_mode; + stat_out->st_nlink = req_in->statbuf.st_nlink; + stat_out->st_uid = req_in->statbuf.st_uid; + stat_out->st_gid = req_in->statbuf.st_gid; + stat_out->st_rdev = req_in->statbuf.st_rdev; + stat_out->st_ino = req_in->statbuf.st_ino; + stat_out->st_size = req_in->statbuf.st_size; + stat_out->st_blksize = req_in->statbuf.st_blksize; + stat_out->st_blocks = req_in->statbuf.st_blocks; + stat_out->st_flags = req_in->statbuf.st_flags; + stat_out->st_gen = req_in->statbuf.st_gen; + stat_out->st_atim.tv_sec = req_in->statbuf.st_atim.tv_sec; + stat_out->st_atim.tv_nsec = req_in->statbuf.st_atim.tv_nsec; + stat_out->st_mtim.tv_sec = req_in->statbuf.st_mtim.tv_sec; + stat_out->st_mtim.tv_nsec = req_in->statbuf.st_mtim.tv_nsec; + stat_out->st_ctim.tv_sec = req_in->statbuf.st_ctim.tv_sec; + stat_out->st_ctim.tv_nsec = req_in->statbuf.st_ctim.tv_nsec; + stat_out->st_birthtim.tv_sec = req_in->statbuf.st_birthtim.tv_sec; + stat_out->st_birthtim.tv_nsec = req_in->statbuf.st_birthtim.tv_nsec; +} + +extern "C" int +rust_uv_fs_mkdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, uv_fs_cb cb) { + return uv_fs_mkdir(loop, req, path, mode, cb); +} +extern "C" int +rust_uv_fs_rmdir(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { + return uv_fs_rmdir(loop, req, path, cb); +} diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in index 4cbee0dcbd068..d6b0d7c7a9dee 100644 --- a/src/rt/rustrt.def.in +++ b/src/rt/rustrt.def.in @@ -114,7 +114,12 @@ rust_uv_fs_read rust_uv_fs_close rust_uv_get_result_from_fs_req rust_uv_get_loop_from_fs_req +rust_uv_fs_stat +rust_uv_fs_fstat rust_uv_fs_req_cleanup +rust_uv_populate_uv_stat +rust_uv_fs_mkdir +rust_uv_fs_rmdir rust_dbg_lock_create rust_dbg_lock_destroy rust_dbg_lock_lock From 055488df1a6a4500de565ac2531d7bc42dd02f83 Mon Sep 17 00:00:00 2001 From: Jeff Olson Date: Mon, 2 Sep 2013 20:23:12 -0700 Subject: [PATCH 02/21] merge cleanup --- src/libstd/rt/uv/file.rs | 3 ++- src/rt/rust_uv.cpp | 42 ++++++++++++++++++++-------------------- 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/src/libstd/rt/uv/file.rs b/src/libstd/rt/uv/file.rs index 32e248254f4db..850b28718d0d2 100644 --- a/src/libstd/rt/uv/file.rs +++ b/src/libstd/rt/uv/file.rs @@ -183,8 +183,9 @@ impl FsRequest { // accessors/utility funcs fn sync_cleanup(self, result: c_int) -> Result { + let loop_ = self.get_loop().native_handle(); self.cleanup_and_delete(); - match status_to_maybe_uv_error(result as i32) { + match status_to_maybe_uv_error_with_loop(loop_,result as i32) { Some(err) => Err(err), None => Ok(result) } diff --git a/src/rt/rust_uv.cpp b/src/rt/rust_uv.cpp index ebc76c84ec9ff..5756ffb0de0a6 100644 --- a/src/rt/rust_uv.cpp +++ b/src/rt/rust_uv.cpp @@ -562,27 +562,27 @@ rust_uv_fs_fstat(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) { } extern "C" void -rust_uv_populate_uv_stat(uv_fs_t* req_in, uv_stat_t* stat_out) { - stat_out->st_dev = req_in->statbuf.st_dev; - stat_out->st_mode = req_in->statbuf.st_mode; - stat_out->st_nlink = req_in->statbuf.st_nlink; - stat_out->st_uid = req_in->statbuf.st_uid; - stat_out->st_gid = req_in->statbuf.st_gid; - stat_out->st_rdev = req_in->statbuf.st_rdev; - stat_out->st_ino = req_in->statbuf.st_ino; - stat_out->st_size = req_in->statbuf.st_size; - stat_out->st_blksize = req_in->statbuf.st_blksize; - stat_out->st_blocks = req_in->statbuf.st_blocks; - stat_out->st_flags = req_in->statbuf.st_flags; - stat_out->st_gen = req_in->statbuf.st_gen; - stat_out->st_atim.tv_sec = req_in->statbuf.st_atim.tv_sec; - stat_out->st_atim.tv_nsec = req_in->statbuf.st_atim.tv_nsec; - stat_out->st_mtim.tv_sec = req_in->statbuf.st_mtim.tv_sec; - stat_out->st_mtim.tv_nsec = req_in->statbuf.st_mtim.tv_nsec; - stat_out->st_ctim.tv_sec = req_in->statbuf.st_ctim.tv_sec; - stat_out->st_ctim.tv_nsec = req_in->statbuf.st_ctim.tv_nsec; - stat_out->st_birthtim.tv_sec = req_in->statbuf.st_birthtim.tv_sec; - stat_out->st_birthtim.tv_nsec = req_in->statbuf.st_birthtim.tv_nsec; +rust_uv_populate_uv_stat(uv_fs_t* req_in, uv_statbuf_t* stat_out) { + stat_out->st_dev = ((uv_statbuf_t*)req_in->ptr)->st_dev; + stat_out->st_mode = ((uv_statbuf_t*)req_in->ptr)->st_mode; + stat_out->st_nlink = ((uv_statbuf_t*)req_in->ptr)->st_nlink; + stat_out->st_uid = ((uv_statbuf_t*)req_in->ptr)->st_uid; + stat_out->st_gid = ((uv_statbuf_t*)req_in->ptr)->st_gid; + stat_out->st_rdev = ((uv_statbuf_t*)req_in->ptr)->st_rdev; + stat_out->st_ino = ((uv_statbuf_t*)req_in->ptr)->st_ino; + stat_out->st_size = ((uv_statbuf_t*)req_in->ptr)->st_size; + stat_out->st_blksize = ((uv_statbuf_t*)req_in->ptr)->st_blksize; + stat_out->st_blocks = ((uv_statbuf_t*)req_in->ptr)->st_blocks; + //stat_out->st_flags = ((uv_statbuf_t*)req_in->ptr)->st_flags; + //stat_out->st_gen = ((uv_statbuf_t*)req_in->ptr)->st_gen; + stat_out->st_atim.tv_sec = ((uv_statbuf_t*)req_in->ptr)->st_atim.tv_sec; + stat_out->st_atim.tv_nsec = ((uv_statbuf_t*)req_in->ptr)->st_atim.tv_nsec; + stat_out->st_mtim.tv_sec = ((uv_statbuf_t*)req_in->ptr)->st_mtim.tv_sec; + stat_out->st_mtim.tv_nsec = ((uv_statbuf_t*)req_in->ptr)->st_mtim.tv_nsec; + stat_out->st_ctim.tv_sec = ((uv_statbuf_t*)req_in->ptr)->st_ctim.tv_sec; + stat_out->st_ctim.tv_nsec = ((uv_statbuf_t*)req_in->ptr)->st_ctim.tv_nsec; + //stat_out->st_birthtim.tv_sec = ((uv_statbuf_t*)req_in->ptr)->st_birthtim.tv_sec; + //stat_out->st_birthtim.tv_nsec = ((uv_statbuf_t*)req_in->ptr)->st_birthtim.tv_nsec; } extern "C" int From b49fc4cf4eb7299a08d83ed8880d1002ecef9257 Mon Sep 17 00:00:00 2001 From: Jeff Olson Date: Sat, 14 Sep 2013 09:33:53 -0700 Subject: [PATCH 03/21] std: adding file::{stat,mkdir,rmdir}, FileInfo and FileReader/FileWriter add ignores for win32 tests on previous file io stuff... --- src/libstd/rt/io/file.rs | 284 +++++++++++++++++++++++++++------------ src/libstd/rt/rtio.rs | 2 + src/libstd/rt/uv/file.rs | 6 +- src/libstd/rt/uv/uvio.rs | 104 ++++++++------ src/libstd/rt/uv/uvll.rs | 4 +- src/rt/rust_uv.cpp | 42 +++--- 6 files changed, 290 insertions(+), 152 deletions(-) diff --git a/src/libstd/rt/io/file.rs b/src/libstd/rt/io/file.rs index ee102c3a97ff8..4968f3276026f 100644 --- a/src/libstd/rt/io/file.rs +++ b/src/libstd/rt/io/file.rs @@ -12,7 +12,7 @@ use prelude::*; use super::support::PathLike; use super::{Reader, Writer, Seek}; use super::{SeekStyle,SeekSet, SeekCur, SeekEnd, - Open, Read, Create, ReadWrite}; + Open, Read, Write, Create, ReadWrite}; use rt::rtio::{RtioFileStream, IoFactory, IoFactoryObject}; use rt::io::{io_error, read_error, EndOfFile, FileMode, FileAccess, FileStat}; @@ -57,26 +57,108 @@ pub fn unlink(path: &P) { } } -/// Abstraction representing *positional* access to a file. In this case, -/// *positional* refers to it keeping an encounter *cursor* of where in the -/// file a subsequent `read` or `write` will begin from. Users of a `FileStream` -/// can `seek` to move the cursor to a given location *within the bounds of the -/// file* and can ask to have the `FileStream` `tell` them the location, in -/// bytes, of the cursor. -/// -/// This abstraction is roughly modeled on the access workflow as represented -/// by `open(2)`, `read(2)`, `write(2)` and friends. +/// Create a new directory with default permissions (process user +/// has read/write privs) +pub fn mkdir(path: &P) { + let mkdir_result = unsafe { + let io: *mut IoFactoryObject = Local::unsafe_borrow(); + (*io).fs_mkdir(path) + }; + match mkdir_result { + Ok(_) => (), + Err(ioerr) => { + io_error::cond.raise(ioerr); + } + } +} +/// Removes a directory +pub fn rmdir(path: &P) { + let rmdir_result = unsafe { + let io: *mut IoFactoryObject = Local::unsafe_borrow(); + (*io).fs_rmdir(path) + }; + match rmdir_result { + Ok(_) => (), + Err(ioerr) => { + io_error::cond.raise(ioerr); + } + } +} + +/// Given a `rt::io::support::PathLike`, query the file system to get +/// information about a file, directory, etc. /// -/// The `open` and `unlink` static methods are provided to manage creation/removal -/// of files. All other methods operatin on an instance of `FileStream`. +/// Returns a `Some(PathInfo)` on success, and raises a `rt::io::IoError` condition +/// on failure and returns `None`. +pub fn stat(path: &P) -> Option { + let open_result = unsafe { + let io: *mut IoFactoryObject = Local::unsafe_borrow(); + (*io).fs_stat(path) + }; + match open_result { + Ok(p) => { + Some(p) + }, + Err(ioerr) => { + read_error::cond.raise(ioerr); + None + } + } +} + +/// Read-only view of file +pub struct FileReader { priv stream: FileStream } + +impl Reader for FileReader { + fn read(&mut self, buf: &mut [u8]) -> Option { + self.stream.read(buf) + } + + fn eof(&mut self) -> bool { + self.stream.eof() + } +} + +impl Seek for FileReader { + fn tell(&self) -> u64 { + self.stream.tell() + } + + fn seek(&mut self, pos: i64, style: SeekStyle) { + self.stream.seek(pos, style); + } +} + +/// Write-only view of a file +pub struct FileWriter { priv stream: FileStream } + +impl Writer for FileWriter { + fn write(&mut self, buf: &[u8]) { + self.stream.write(buf); + } + + fn flush(&mut self) { + self.stream.flush(); + } +} + +impl Seek for FileWriter { + fn tell(&self) -> u64 { + self.stream.tell() + } + + fn seek(&mut self, pos: i64, style: SeekStyle) { + self.stream.seek(pos, style); + } +} + +/// Internal representation of a FileStream, used to consolidate functionality +/// exposed in the public API pub struct FileStream { fd: ~RtioFileStream, last_nread: int, } -impl FileStream { -} - impl Reader for FileStream { fn read(&mut self, buf: &mut [u8]) -> Option { match self.fd.read(buf) { @@ -148,69 +230,85 @@ impl Seek for FileStream { } } -pub struct FileInfo(Path); - -/// FIXME: DOCS -impl<'self> FileInfo { - pub fn new(path: &P) -> FileInfo { - do path.path_as_str |p| { - FileInfo(Path(p)) - } - } - // FIXME #8873 can't put this in FileSystemInfo - pub fn get_path(&'self self) -> &'self Path { - &(**self) - } - pub fn stat(&self) -> Option { - do io_error::cond.trap(|_| { +/// Represents passive information about a file (primarily exposed +/// via the `stat()` method. Also provides methods for opening +/// a file in various modes/permissions. +pub trait FileInfo<'self> { + /// Get the filesystem path that this `FileInfo` points at, + /// whether it is valid or not. This way, it can be used to + /// to specify a file path of a non-existent file which it + /// later create + fn get_file_path(&'self self) -> &'self Path; + + /// Ask the operating system for information about the file + fn stat(&self) -> Option { + use mod_stat = super::file::stat; + do read_error::cond.trap(|_| { // FIXME: can we do something more useful here? }).inside { - stat(self.get_path()) + mod_stat(self.get_file_path()) } } - pub fn exists(&self) -> bool { + + /// returns `true` if the location pointed at by the enclosing + /// exists on the filesystem + fn file_exists(&self) -> bool { match self.stat() { - Some(s) => { - match s.is_file { - true => { - true - }, - false => { - // FIXME: raise condition? - false - } - } - }, + Some(_) => true, None => false } } - pub fn is_file(&self) -> bool { + + /// Whether the underlying implemention (be it a file path + /// or active file descriptor) is a "regular file". Will return + /// false for paths to non-existent locations or directories or + /// other non-regular files (named pipes, etc). + fn is_file(&self) -> bool { match self.stat() { Some(s) => s.is_file, - None => { - // FIXME: raise condition - false - } + None => false } } - pub fn open(&self, mode: FileMode, access: FileAccess) -> Option { - match self.is_file() { - true => { - open(self.get_path(), mode, access) + + /// Attempts to open a regular file for reading/writing based + /// on provided inputs + fn open_stream(&self, mode: FileMode, access: FileAccess) -> Option { + match self.stat() { + Some(s) => match s.is_file { + true => open(self.get_file_path(), mode, access), + false => None // FIXME: raise condition, not a regular file.. }, - false => { - // FIXME: raise condition - None - } + None => open(self.get_file_path(), mode, access) + } + } + /// Attempts to open a regular file for reading-only based + /// on provided inputs + fn open_reader(&self, mode: FileMode) -> Option { + match self.open_stream(mode, Read) { + Some(s) => Some(FileReader { stream: s}), + None => None + } + } + + /// Attempts to open a regular file for writing-only based + /// on provided inputs + fn open_writer(&self, mode: FileMode) -> Option { + match self.open_stream(mode, Write) { + Some(s) => Some(FileWriter { stream: s}), + None => None } } - //fn open_read(&self) -> FileStream; - //fn open_write(&self) -> FileStream; - //fn create(&self) -> FileStream; - //fn truncate(&self) -> FileStream; - //fn open_or_create(&self) -> FileStream; - //fn create_or_truncate(&self) -> FileStream; - //fn unlink(&self); + + /// Attempt to remove a file from the filesystem, pending the closing + /// of any open file descriptors pointing to the file + fn unlink(&self) { + unlink(self.get_file_path()); + } +} + +/// `FileInfo` implementation for `Path`s +impl<'self> FileInfo<'self> for Path { + fn get_file_path(&'self self) -> &'self Path { self } } /* @@ -244,27 +342,6 @@ impl DirectoryInfo<'self> { } */ -/// Given a `rt::io::support::PathLike`, query the file system to get -/// information about a file, directory, etc. -/// -/// Returns a `Some(PathInfo)` on success, and raises a `rt::io::IoError` condition -/// on failure and returns `None`. -pub fn stat(path: &P) -> Option { - let open_result = unsafe { - let io: *mut IoFactoryObject = Local::unsafe_borrow(); - (*io).fs_stat(path) - }; - match open_result { - Ok(p) => { - Some(p) - }, - Err(ioerr) => { - read_error::cond.raise(ioerr); - None - } - } -} - fn file_test_smoke_test_impl() { do run_in_mt_newsched_task { let message = "it's alright. have a good time"; @@ -412,7 +489,7 @@ fn file_test_io_seek_and_write_impl() { read_stream.read(read_mem); } unlink(filename); - let read_str = str::from_bytes(read_mem); + let read_str = str::from_utf8(read_mem); assert!(read_str == final_msg.to_owned()); } } @@ -463,8 +540,9 @@ fn file_test_io_seek_shakedown() { } #[test] +#[ignore(cfg(windows))] // FIXME #8810 fn file_test_stat_is_correct_on_is_file() { - do run_in_newsched_task { + do run_in_mt_newsched_task { let filename = &Path("./tmp/file_stat_correct_on_is_file.txt"); { let mut fs = open(filename, Create, ReadWrite).unwrap(); @@ -476,20 +554,48 @@ fn file_test_stat_is_correct_on_is_file() { None => fail!("shouldn't happen") }; assert!(stat_res.is_file); + unlink(filename); } } #[test] +#[ignore(cfg(windows))] // FIXME #8810 fn file_test_stat_is_correct_on_is_dir() { - //assert!(false); + do run_in_mt_newsched_task { + let filename = &Path("./tmp/file_stat_correct_on_is_dir"); + mkdir(filename); + let stat_res = match stat(filename) { + Some(s) => s, + None => fail!("shouldn't happen") + }; + assert!(stat_res.is_dir); + rmdir(filename); + } } #[test] +#[ignore(cfg(windows))] // FIXME #8810 fn file_test_fileinfo_false_when_checking_is_file_on_a_directory() { - //assert!(false); + do run_in_mt_newsched_task { + let dir = &Path("./tmp/fileinfo_false_on_dir"); + mkdir(dir); + assert!(dir.is_file() == false); + rmdir(dir); + } } #[test] +#[ignore(cfg(windows))] // FIXME #8810 fn file_test_fileinfo_check_exists_before_and_after_file_creation() { - //assert!(false); + do run_in_mt_newsched_task { + let file = &Path("./tmp/fileinfo_check_exists_b_and_a.txt"); + { + let msg = "foo".as_bytes(); + let mut w = file.open_writer(Create); + w.write(msg); + } + assert!(file.file_exists()); + file.unlink(); + assert!(!file.file_exists()); + } } diff --git a/src/libstd/rt/rtio.rs b/src/libstd/rt/rtio.rs index f08949e916586..0abf81f62de28 100644 --- a/src/libstd/rt/rtio.rs +++ b/src/libstd/rt/rtio.rs @@ -76,6 +76,8 @@ pub trait IoFactory { fn get_host_addresses(&mut self, host: &str) -> Result<~[IpAddr], IoError>; fn fs_stat(&mut self, path: &P) -> Result; //fn fs_fstat(&mut self, fd: c_int) -> Result; + fn fs_mkdir(&mut self, path: &P) -> Result<(), IoError>; + fn fs_rmdir(&mut self, path: &P) -> Result<(), IoError>; } pub trait RtioStream { diff --git a/src/libstd/rt/uv/file.rs b/src/libstd/rt/uv/file.rs index 850b28718d0d2..34f87e3601e74 100644 --- a/src/libstd/rt/uv/file.rs +++ b/src/libstd/rt/uv/file.rs @@ -183,9 +183,8 @@ impl FsRequest { // accessors/utility funcs fn sync_cleanup(self, result: c_int) -> Result { - let loop_ = self.get_loop().native_handle(); self.cleanup_and_delete(); - match status_to_maybe_uv_error_with_loop(loop_,result as i32) { + match status_to_maybe_uv_error(result as i32) { Some(err) => Err(err), None => Ok(result) } @@ -261,6 +260,8 @@ fn sync_cleanup(result: int) match status_to_maybe_uv_error(result as i32) { Some(err) => Err(err), None => Ok(result) + } +} extern fn compl_cb(req: *uv_fs_t) { let mut req: FsRequest = NativeHandle::from_native_handle(req); @@ -522,6 +523,7 @@ mod test { assert!(uverr.is_none()); let loop_ = req.get_loop(); let stat = req.get_stat(); + naive_print(&loop_, fmt!("%?", stat)); assert!(stat.is_dir()); let rmdir_req = FsRequest::new(); do rmdir_req.rmdir(&loop_, &path) |req,uverr| { diff --git a/src/libstd/rt/uv/uvio.rs b/src/libstd/rt/uv/uvio.rs index 4307c57529b5f..88d168c85d21d 100644 --- a/src/libstd/rt/uv/uvio.rs +++ b/src/libstd/rt/uv/uvio.rs @@ -35,7 +35,7 @@ use unstable::sync::Exclusive; use path::Path; use super::super::io::support::PathLike; use libc::{lseek, off_t, O_CREAT, O_APPEND, O_TRUNC, O_RDWR, O_RDONLY, O_WRONLY, - S_IRUSR, S_IWUSR}; + S_IRUSR, S_IWUSR, S_IRWXU}; use rt::io::{FileMode, FileAccess, OpenOrCreate, Open, Create, CreateOrTruncate, Append, Truncate, Read, Write, ReadWrite, FileStat}; @@ -413,6 +413,36 @@ impl UvIoFactory { } } +/// Helper for a variety of simple uv_fs_* functions that +/// have no ret val +fn uv_fs_helper(loop_: &mut Loop, path: &P, + cb: ~fn(&mut FsRequest, &mut Loop, &P, + ~fn(&FsRequest, Option))) + -> Result<(), IoError> { + let result_cell = Cell::new_empty(); + let result_cell_ptr: *Cell> = &result_cell; + let path_cell = Cell::new(path); + do task::unkillable { // FIXME(#8674) + let scheduler: ~Scheduler = Local::take(); + let mut new_req = FsRequest::new(); + do scheduler.deschedule_running_task_and_then |_, task| { + let task_cell = Cell::new(task); + let path = path_cell.take(); + do cb(&mut new_req, loop_, path) |_, err| { + let res = match err { + None => Ok(()), + Some(err) => Err(uv_error_to_io_error(err)) + }; + unsafe { (*result_cell_ptr).put_back(res); } + let scheduler: ~Scheduler = Local::take(); + scheduler.resume_blocked_task_immediately(task_cell.take()); + }; + } + } + assert!(!result_cell.is_empty()); + return result_cell.take(); +} + impl IoFactory for UvIoFactory { // Connect to an address and return a new stream // NB: This blocks the task waiting on the connection. @@ -578,28 +608,11 @@ impl IoFactory for UvIoFactory { } fn fs_unlink(&mut self, path: &P) -> Result<(), IoError> { - let result_cell = Cell::new_empty(); - let result_cell_ptr: *Cell> = &result_cell; - let path_cell = Cell::new(path); - do task::unkillable { // FIXME(#8674) - let scheduler: ~Scheduler = Local::take(); - let unlink_req = FsRequest::new(); - do scheduler.deschedule_running_task_and_then |_, task| { - let task_cell = Cell::new(task); - let path = path_cell.take(); - do unlink_req.unlink(self.uv_loop(), path) |_, err| { - let res = match err { - None => Ok(()), - Some(err) => Err(uv_error_to_io_error(err)) - }; - unsafe { (*result_cell_ptr).put_back(res); } - let scheduler: ~Scheduler = Local::take(); - scheduler.resume_blocked_task_immediately(task_cell.take()); - }; - } + do uv_fs_helper(self.uv_loop(), path) |unlink_req, l, p, cb| { + do unlink_req.unlink(l, p) |req, err| { + cb(req, err) + }; } - assert!(!result_cell.is_empty()); - return result_cell.take(); } fn fs_stat(&mut self, path: &P) -> Result { use str::StrSlice; @@ -616,22 +629,22 @@ impl IoFactory for UvIoFactory { let path_str = path.path_as_str(|p| p.to_owned()); do stat_req.stat(self.uv_loop(), path) |req,err| { - if err.is_none() { - let stat = req.get_stat(); - let res = Ok(FileStat { - path: Path(path_str), - is_file: stat.is_file(), - is_dir: stat.is_dir() - }); - unsafe { (*result_cell_ptr).put_back(res); } - let scheduler: ~Scheduler = Local::take(); - scheduler.resume_blocked_task_immediately(task_cell.take()); - } else { - let res = Err(uv_error_to_io_error(err.unwrap())); - unsafe { (*result_cell_ptr).put_back(res); } - let scheduler: ~Scheduler = Local::take(); - scheduler.resume_blocked_task_immediately(task_cell.take()); - } + let res = match err { + None => { + let stat = req.get_stat(); + Ok(FileStat { + path: Path(path_str), + is_file: stat.is_file(), + is_dir: stat.is_dir() + }) + }, + Some(e) => { + Err(uv_error_to_io_error(e)) + } + }; + unsafe { (*result_cell_ptr).put_back(res); } + let scheduler: ~Scheduler = Local::take(); + scheduler.resume_blocked_task_immediately(task_cell.take()); }; }; }; @@ -672,6 +685,21 @@ impl IoFactory for UvIoFactory { //fn fs_fstat(&mut self, _fd: c_int) -> Result { // Ok(FileStat) //} + fn fs_mkdir(&mut self, path: &P) -> Result<(), IoError> { + let mode = S_IRWXU as int; + do uv_fs_helper(self.uv_loop(), path) |mkdir_req, l, p, cb| { + do mkdir_req.mkdir(l, p, mode as int) |req, err| { + cb(req, err) + }; + } + } + fn fs_rmdir(&mut self, path: &P) -> Result<(), IoError> { + do uv_fs_helper(self.uv_loop(), path) |rmdir_req, l, p, cb| { + do rmdir_req.rmdir(l, p) |req, err| { + cb(req, err) + }; + } + } } pub struct UvTcpListener { diff --git a/src/libstd/rt/uv/uvll.rs b/src/libstd/rt/uv/uvll.rs index 89ee54be349ad..a2d1c48c3e1a0 100644 --- a/src/libstd/rt/uv/uvll.rs +++ b/src/libstd/rt/uv/uvll.rs @@ -142,10 +142,10 @@ impl uv_stat_t { } } pub fn is_file(&self) -> bool { - ((self.st_mode as c_int) & libc::S_IFMT) == libc::S_IFREG + ((self.st_mode) & libc::S_IFMT as libc::uint64_t) == libc::S_IFREG as libc::uint64_t } pub fn is_dir(&self) -> bool { - ((self.st_mode as c_int) & libc::S_IFMT) == libc::S_IFDIR + ((self.st_mode) & libc::S_IFMT as libc::uint64_t) == libc::S_IFDIR as libc::uint64_t } } diff --git a/src/rt/rust_uv.cpp b/src/rt/rust_uv.cpp index 5756ffb0de0a6..ebc76c84ec9ff 100644 --- a/src/rt/rust_uv.cpp +++ b/src/rt/rust_uv.cpp @@ -562,27 +562,27 @@ rust_uv_fs_fstat(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) { } extern "C" void -rust_uv_populate_uv_stat(uv_fs_t* req_in, uv_statbuf_t* stat_out) { - stat_out->st_dev = ((uv_statbuf_t*)req_in->ptr)->st_dev; - stat_out->st_mode = ((uv_statbuf_t*)req_in->ptr)->st_mode; - stat_out->st_nlink = ((uv_statbuf_t*)req_in->ptr)->st_nlink; - stat_out->st_uid = ((uv_statbuf_t*)req_in->ptr)->st_uid; - stat_out->st_gid = ((uv_statbuf_t*)req_in->ptr)->st_gid; - stat_out->st_rdev = ((uv_statbuf_t*)req_in->ptr)->st_rdev; - stat_out->st_ino = ((uv_statbuf_t*)req_in->ptr)->st_ino; - stat_out->st_size = ((uv_statbuf_t*)req_in->ptr)->st_size; - stat_out->st_blksize = ((uv_statbuf_t*)req_in->ptr)->st_blksize; - stat_out->st_blocks = ((uv_statbuf_t*)req_in->ptr)->st_blocks; - //stat_out->st_flags = ((uv_statbuf_t*)req_in->ptr)->st_flags; - //stat_out->st_gen = ((uv_statbuf_t*)req_in->ptr)->st_gen; - stat_out->st_atim.tv_sec = ((uv_statbuf_t*)req_in->ptr)->st_atim.tv_sec; - stat_out->st_atim.tv_nsec = ((uv_statbuf_t*)req_in->ptr)->st_atim.tv_nsec; - stat_out->st_mtim.tv_sec = ((uv_statbuf_t*)req_in->ptr)->st_mtim.tv_sec; - stat_out->st_mtim.tv_nsec = ((uv_statbuf_t*)req_in->ptr)->st_mtim.tv_nsec; - stat_out->st_ctim.tv_sec = ((uv_statbuf_t*)req_in->ptr)->st_ctim.tv_sec; - stat_out->st_ctim.tv_nsec = ((uv_statbuf_t*)req_in->ptr)->st_ctim.tv_nsec; - //stat_out->st_birthtim.tv_sec = ((uv_statbuf_t*)req_in->ptr)->st_birthtim.tv_sec; - //stat_out->st_birthtim.tv_nsec = ((uv_statbuf_t*)req_in->ptr)->st_birthtim.tv_nsec; +rust_uv_populate_uv_stat(uv_fs_t* req_in, uv_stat_t* stat_out) { + stat_out->st_dev = req_in->statbuf.st_dev; + stat_out->st_mode = req_in->statbuf.st_mode; + stat_out->st_nlink = req_in->statbuf.st_nlink; + stat_out->st_uid = req_in->statbuf.st_uid; + stat_out->st_gid = req_in->statbuf.st_gid; + stat_out->st_rdev = req_in->statbuf.st_rdev; + stat_out->st_ino = req_in->statbuf.st_ino; + stat_out->st_size = req_in->statbuf.st_size; + stat_out->st_blksize = req_in->statbuf.st_blksize; + stat_out->st_blocks = req_in->statbuf.st_blocks; + stat_out->st_flags = req_in->statbuf.st_flags; + stat_out->st_gen = req_in->statbuf.st_gen; + stat_out->st_atim.tv_sec = req_in->statbuf.st_atim.tv_sec; + stat_out->st_atim.tv_nsec = req_in->statbuf.st_atim.tv_nsec; + stat_out->st_mtim.tv_sec = req_in->statbuf.st_mtim.tv_sec; + stat_out->st_mtim.tv_nsec = req_in->statbuf.st_mtim.tv_nsec; + stat_out->st_ctim.tv_sec = req_in->statbuf.st_ctim.tv_sec; + stat_out->st_ctim.tv_nsec = req_in->statbuf.st_ctim.tv_nsec; + stat_out->st_birthtim.tv_sec = req_in->statbuf.st_birthtim.tv_sec; + stat_out->st_birthtim.tv_nsec = req_in->statbuf.st_birthtim.tv_nsec; } extern "C" int From daf497462844a55678e12545114bcc75d5ce8ebc Mon Sep 17 00:00:00 2001 From: Jeff Olson Date: Sat, 14 Sep 2013 09:35:11 -0700 Subject: [PATCH 04/21] std: win32 os::env() str parsing to str::raw::from_c_multistring + test --- src/libstd/os.rs | 11 +---------- src/libstd/str.rs | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/src/libstd/os.rs b/src/libstd/os.rs index c45f2af8f7ec7..af657274c670d 100644 --- a/src/libstd/os.rs +++ b/src/libstd/os.rs @@ -196,16 +196,7 @@ pub fn env() -> ~[(~str,~str)] { if (ch as uint == 0) { fail!("os::env() failure getting env string from OS: %s", os::last_os_error()); } - let mut curr_ptr: uint = ch as uint; - let mut result = ~[]; - while(*(curr_ptr as *libc::c_char) != 0 as libc::c_char) { - let env_pair = str::raw::from_c_str( - curr_ptr as *libc::c_char); - result.push(env_pair); - curr_ptr += - libc::strlen(curr_ptr as *libc::c_char) as uint - + 1; - } + result = unsafe { str::raw::from_c_multistring(ch as *libc::c_char) }; FreeEnvironmentStringsA(ch); result } diff --git a/src/libstd/str.rs b/src/libstd/str.rs index bd484a5074c5e..84df2d2005da3 100644 --- a/src/libstd/str.rs +++ b/src/libstd/str.rs @@ -1091,6 +1091,25 @@ pub mod raw { vec::raw::set_len(as_owned_vec(s), new_len) } + /// Parses a C "multistring", eg windows env values or + /// the req->ptr result in a uv_fs_readdir() call + #[inline] + pub unsafe fn from_c_multistring(c: *libc::c_char) -> ~[~str] { + #[fixed_stack_segment]; #[inline(never)]; + + let mut curr_ptr: uint = c as uint; + let mut result = ~[]; + while(*(curr_ptr as *libc::c_char) != 0 as libc::c_char) { + let env_pair = from_c_str( + curr_ptr as *libc::c_char); + result.push(env_pair); + curr_ptr += + libc::strlen(curr_ptr as *libc::c_char) as uint + + 1; + } + result + } + /// Sets the length of a string /// /// This will explicitly set the size of the string, without actually @@ -1106,6 +1125,24 @@ pub mod raw { } } + #[test] + fn test_str_multistring_parsing() { + unsafe { + let input = bytes!("zero", "\x00", "one", "\x00", "\x00"); + let ptr = vec::raw::to_ptr(input); + let mut result = from_c_multistring(ptr as *libc::c_char); + assert!(result.len() == 2); + let mut ctr = 0; + for x in result.iter() { + match ctr { + 0 => assert_eq!(x, &~"zero"), + 1 => assert_eq!(x, &~"one"), + _ => fail!("shouldn't happen!") + } + ctr += 1; + } + } + } } /* From 71c7798d66953e9f53192d54985a6a46736f5ca5 Mon Sep 17 00:00:00 2001 From: Jeff Olson Date: Sun, 15 Sep 2013 12:23:53 -0700 Subject: [PATCH 05/21] std: clean up Dir/FileInfo inheritence and flesh out Dir Info --- src/libstd/rt/io/file.rs | 178 ++++++++++++++++++++++++++------------- src/libstd/rt/io/mod.rs | 10 ++- 2 files changed, 129 insertions(+), 59 deletions(-) diff --git a/src/libstd/rt/io/file.rs b/src/libstd/rt/io/file.rs index 4968f3276026f..af202a729e3d4 100644 --- a/src/libstd/rt/io/file.rs +++ b/src/libstd/rt/io/file.rs @@ -15,7 +15,9 @@ use super::{SeekStyle,SeekSet, SeekCur, SeekEnd, Open, Read, Write, Create, ReadWrite}; use rt::rtio::{RtioFileStream, IoFactory, IoFactoryObject}; use rt::io::{io_error, read_error, EndOfFile, - FileMode, FileAccess, FileStat}; + FileMode, FileAccess, FileStat, IoError, + PathAlreadyExists, PathDoesntExist, + MismatchedFileTypeForOperation}; use rt::local::Local; use option::{Some, None}; use path::Path; @@ -100,7 +102,7 @@ pub fn stat(path: &P) -> Option { Some(p) }, Err(ioerr) => { - read_error::cond.raise(ioerr); + io_error::cond.raise(ioerr); None } } @@ -230,41 +232,53 @@ impl Seek for FileStream { } } -/// Represents passive information about a file (primarily exposed -/// via the `stat()` method. Also provides methods for opening -/// a file in various modes/permissions. -pub trait FileInfo<'self> { - /// Get the filesystem path that this `FileInfo` points at, - /// whether it is valid or not. This way, it can be used to - /// to specify a file path of a non-existent file which it - /// later create - fn get_file_path(&'self self) -> &'self Path; - - /// Ask the operating system for information about the file +// helper for grabbing a stat and ignoring any +// error.. used in Info wrappers +fn suppressed_stat(cb: &fn() -> Option) -> Option { + do io_error::cond.trap(|_| { + // just swallow the error.. downstream users + // who can make a decision based on a None result + // won't care + }).inside { + cb() + } +} + +/// Shared functionality between `FileInfo` and `DirectoryInfo` +pub trait FileSystemInfo { + /// Get the filesystem path that this instance points at, + /// whether it is valid or not. In this way, it can be used to + /// to specify a path of a non-existent file which it + /// later creates + fn get_path<'a>(&'a self) -> &'a Path; + + /// Ask the operating system for information about the path, + /// will raise a condition if an error occurs during the process fn stat(&self) -> Option { - use mod_stat = super::file::stat; - do read_error::cond.trap(|_| { - // FIXME: can we do something more useful here? - }).inside { - mod_stat(self.get_file_path()) - } + stat(self.get_path()) } /// returns `true` if the location pointed at by the enclosing /// exists on the filesystem - fn file_exists(&self) -> bool { - match self.stat() { + fn exists(&self) -> bool { + match suppressed_stat(|| self.stat()) { Some(_) => true, None => false } } - /// Whether the underlying implemention (be it a file path - /// or active file descriptor) is a "regular file". Will return +} + +/// Represents passive information about a file (primarily exposed +/// via the `stat()` method. Also provides methods for opening +/// a file in various modes/permissions. +pub trait FileInfo : FileSystemInfo { + /// Whether the underlying implemention (be it a file path, + /// or something else) points at a "regular file" on the FS. Will return /// false for paths to non-existent locations or directories or /// other non-regular files (named pipes, etc). fn is_file(&self) -> bool { - match self.stat() { + match suppressed_stat(|| self.stat()) { Some(s) => s.is_file, None => false } @@ -273,12 +287,12 @@ pub trait FileInfo<'self> { /// Attempts to open a regular file for reading/writing based /// on provided inputs fn open_stream(&self, mode: FileMode, access: FileAccess) -> Option { - match self.stat() { + match suppressed_stat(|| self.stat()) { Some(s) => match s.is_file { - true => open(self.get_file_path(), mode, access), + true => open(self.get_path(), mode, access), false => None // FIXME: raise condition, not a regular file.. }, - None => open(self.get_file_path(), mode, access) + None => open(self.get_path(), mode, access) } } /// Attempts to open a regular file for reading-only based @@ -302,45 +316,82 @@ pub trait FileInfo<'self> { /// Attempt to remove a file from the filesystem, pending the closing /// of any open file descriptors pointing to the file fn unlink(&self) { - unlink(self.get_file_path()); + unlink(self.get_path()); } } -/// `FileInfo` implementation for `Path`s -impl<'self> FileInfo<'self> for Path { - fn get_file_path(&'self self) -> &'self Path { self } +/// `FileSystemInfo` implementation for `Path`s +impl FileSystemInfo for Path { + fn get_path<'a>(&'a self) -> &'a Path { self } } +/// `FileInfo` implementation for `Path`s +impl FileInfo for Path { } -/* -/// FIXME: DOCS -impl DirectoryInfo<'self> { - fn new(path: &P) -> FileInfo { - FileInfo(Path(path.path_as_str())) - } - // FIXME #8873 can't put this in FileSystemInfo - fn get_path(&'self self) -> &'self Path { - &*self +trait DirectoryInfo : FileSystemInfo { + /// Whether the underlying implemention (be it a file path, + /// or something else) points at a directory file" on the FS. Will return + /// false for paths to non-existent locations or if the item is + /// not a directory (eg files, named pipes, links, etc) + fn is_dir(&self) -> bool { + match suppressed_stat(|| self.stat()) { + Some(s) => s.is_dir, + None => false + } } - fn stat(&self) -> Option { - file::stat(self.get_path()) + /// Create a directory at the location pointed to by the + /// type underlying the given `DirectoryInfo`. Raises a + /// condition if a file, directory, etc already exists + /// at that location or if some other error occurs during + /// the mkdir operation + fn mkdir(&self) { + match suppressed_stat(|| self.stat()) { + Some(_) => { + io_error::cond.raise(IoError { + kind: PathAlreadyExists, + desc: "path already exists", + detail: + Some(fmt!("%s already exists; can't mkdir it", self.get_path().to_str())) + }) + }, + None => mkdir(self.get_path()) + } } - fn exists(&self) -> bool { - do io_error::cond.trap(|_| { - }).inside { - match self.stat() { - Some(_) => true, - None => false - } + /// Remove a directory at the given location pointed to by + /// the type underlying the given `DirectoryInfo`. Will fail + /// if there is no directory at the given location or if + fn rmdir(&self) { + match suppressed_stat(|| self.stat()) { + Some(s) => { + match s.is_dir { + true => rmdir(self.get_path()), + false => { + let ioerr = IoError { + kind: MismatchedFileTypeForOperation, + desc: "Cannot do rmdir() on a non-directory", + detail: + Some(fmt!("%s is a non-directory; can't rmdir it", self.get_path().to_str())) + }; + io_error::cond.raise(ioerr); + } + } + }, + None => + io_error::cond.raise(IoError { + kind: PathDoesntExist, + desc: "path doesn't exist", + detail: Some(fmt!("%s doesn't exist; can't rmdir it", self.get_path().to_str())) + }) } } - fn is_dir(&self) -> bool { - + fn readdir(&self) -> ~[~str] { + ~[] } - fn create(&self); - fn get_subdirs(&self, filter: &str) -> ~[Path]; - fn get_files(&self, filter: &str) -> ~[Path]; + //fn get_subdirs(&self, filter: &str) -> ~[Path]; + //fn get_files(&self, filter: &str) -> ~[Path]; } -*/ + +/// FIXME: DOCS +impl DirectoryInfo for Path { } fn file_test_smoke_test_impl() { do run_in_mt_newsched_task { @@ -594,8 +645,21 @@ fn file_test_fileinfo_check_exists_before_and_after_file_creation() { let mut w = file.open_writer(Create); w.write(msg); } - assert!(file.file_exists()); + assert!(file.exists()); file.unlink(); - assert!(!file.file_exists()); + assert!(!file.exists()); + } +} + +#[test] +fn file_test_directoryinfo_check_exists_before_and_after_mkdir() { + do run_in_mt_newsched_task { + let dir = &Path("./tmp/before_and_after_dir"); + assert!(!dir.exists()); + dir.mkdir(); + assert!(dir.exists()); + assert!(dir.is_dir()); + dir.rmdir(); + assert!(!dir.exists()); } } diff --git a/src/libstd/rt/io/mod.rs b/src/libstd/rt/io/mod.rs index 55c7cfd8285bc..16c8a9deea0db 100644 --- a/src/libstd/rt/io/mod.rs +++ b/src/libstd/rt/io/mod.rs @@ -358,7 +358,10 @@ pub enum IoErrorKind { Closed, ConnectionRefused, ConnectionReset, - BrokenPipe + BrokenPipe, + PathAlreadyExists, + PathDoesntExist, + MismatchedFileTypeForOperation } // FIXME: #8242 implementing manually because deriving doesn't work for some reason @@ -374,7 +377,10 @@ impl ToStr for IoErrorKind { Closed => ~"Closed", ConnectionRefused => ~"ConnectionRefused", ConnectionReset => ~"ConnectionReset", - BrokenPipe => ~"BrokenPipe" + BrokenPipe => ~"BrokenPipe", + PathAlreadyExists => ~"PathAlreadyExists", + PathDoesntExist => ~"PathDoesntExist", + MismatchedFileTypeForOperation => ~"MismatchedFileTypeForOperation" } } } From 63182885d857fdf5641449a397ad7e3f4ebff8a7 Mon Sep 17 00:00:00 2001 From: Jeff Olson Date: Mon, 16 Sep 2013 13:20:58 -0700 Subject: [PATCH 06/21] std: more work on from_c_multistring.. let it take an optional len param --- src/libstd/os.rs | 2 +- src/libstd/str.rs | 22 +++++++++++++++++----- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/libstd/os.rs b/src/libstd/os.rs index af657274c670d..d0a658a46a907 100644 --- a/src/libstd/os.rs +++ b/src/libstd/os.rs @@ -196,7 +196,7 @@ pub fn env() -> ~[(~str,~str)] { if (ch as uint == 0) { fail!("os::env() failure getting env string from OS: %s", os::last_os_error()); } - result = unsafe { str::raw::from_c_multistring(ch as *libc::c_char) }; + result = unsafe { str::raw::from_c_multistring(ch as *libc::c_char, None) }; FreeEnvironmentStringsA(ch); result } diff --git a/src/libstd/str.rs b/src/libstd/str.rs index 84df2d2005da3..7cbc88ed204fe 100644 --- a/src/libstd/str.rs +++ b/src/libstd/str.rs @@ -22,6 +22,7 @@ use char; use char::Char; use clone::{Clone, DeepClone}; use container::{Container, Mutable}; +use num::Times; use iter::{Iterator, FromIterator, Extendable, range}; use iter::{Filter, AdditiveIterator, Map}; use iter::{Invert, DoubleEndedIterator, ExactSize}; @@ -938,6 +939,7 @@ static TAG_CONT_U8: u8 = 128u8; /// Unsafe operations pub mod raw { + use option::{Option, Some}; use cast; use libc; use ptr; @@ -1092,20 +1094,29 @@ pub mod raw { } /// Parses a C "multistring", eg windows env values or - /// the req->ptr result in a uv_fs_readdir() call + /// the req->ptr result in a uv_fs_readdir() call. + /// Optionally, a `count` can be passed in, limiting the + /// parsing to only being done `count`-times. #[inline] - pub unsafe fn from_c_multistring(c: *libc::c_char) -> ~[~str] { + pub unsafe fn from_c_multistring(buf: *libc::c_char, count: Option) -> ~[~str] { #[fixed_stack_segment]; #[inline(never)]; - let mut curr_ptr: uint = c as uint; + let mut curr_ptr: uint = buf as uint; let mut result = ~[]; - while(*(curr_ptr as *libc::c_char) != 0 as libc::c_char) { + let mut ctr = 0; + let (limited_count, limit) = match count { + Some(limit) => (true, limit), + None => (false, 0) + }; + while(*(curr_ptr as *libc::c_char) != 0 as libc::c_char + && ((limited_count && ctr < limit) || !limited_count)) { let env_pair = from_c_str( curr_ptr as *libc::c_char); result.push(env_pair); curr_ptr += libc::strlen(curr_ptr as *libc::c_char) as uint + 1; + ctr += 1; } result } @@ -1127,10 +1138,11 @@ pub mod raw { #[test] fn test_str_multistring_parsing() { + use option::None; unsafe { let input = bytes!("zero", "\x00", "one", "\x00", "\x00"); let ptr = vec::raw::to_ptr(input); - let mut result = from_c_multistring(ptr as *libc::c_char); + let mut result = from_c_multistring(ptr as *libc::c_char, None); assert!(result.len() == 2); let mut ctr = 0; for x in result.iter() { From 25b4d8c1d7de70bab8eca8a57fdfb703e5e281c9 Mon Sep 17 00:00:00 2001 From: Jeff Olson Date: Mon, 16 Sep 2013 13:24:23 -0700 Subject: [PATCH 07/21] std: expose more stat info --- src/libstd/rt/io/mod.rs | 8 ++------ src/libstd/rt/uv/uvio.rs | 6 +++++- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/libstd/rt/io/mod.rs b/src/libstd/rt/io/mod.rs index 16c8a9deea0db..afef369fa4414 100644 --- a/src/libstd/rt/io/mod.rs +++ b/src/libstd/rt/io/mod.rs @@ -610,13 +610,10 @@ pub struct FileStat { /// `true` if the file pointed at by the `PathInfo` is a regular file is_file: bool, /// `true` if the file pointed at by the `PathInfo` is a directory - is_dir: bool - // `true` if the file pointed at by the `PathInfo` is a link (what this means - // is platform dependant) - /* + is_dir: bool, /// The file pointed at by the `PathInfo`'s size in bytes size: u64, - /// The file pointed at by the `PathInfo`'s time date in platform-dependent msecs + /// The file pointed at by the `PathInfo`'s creation time created: u64, /// The file pointed at by the `PathInfo`'s last-modification time in /// platform-dependent msecs @@ -624,5 +621,4 @@ pub struct FileStat { /// The file pointed at by the `PathInfo`'s last-accessd time (e.g. read) in /// platform-dependent msecs accessed: u64, - */ } diff --git a/src/libstd/rt/uv/uvio.rs b/src/libstd/rt/uv/uvio.rs index 88d168c85d21d..d701a87004c72 100644 --- a/src/libstd/rt/uv/uvio.rs +++ b/src/libstd/rt/uv/uvio.rs @@ -635,7 +635,11 @@ impl IoFactory for UvIoFactory { Ok(FileStat { path: Path(path_str), is_file: stat.is_file(), - is_dir: stat.is_dir() + is_dir: stat.is_dir(), + size: stat.st_size, + created: stat.st_ctim.tv_sec as u64, + modified: stat.st_mtim.tv_sec as u64, + accessed: stat.st_atim.tv_sec as u64 }) }, Some(e) => { From bf399d558e755b5964666892e9f95440ad6f8ef2 Mon Sep 17 00:00:00 2001 From: Jeff Olson Date: Mon, 16 Sep 2013 13:25:10 -0700 Subject: [PATCH 08/21] std: bind uv_fs_readdir(), flesh out DirectoryInfo and docs/cleanup --- src/libstd/rt/io/file.rs | 85 ++++++++++++++++++++++++++++++++++++---- src/libstd/rt/rtio.rs | 2 + src/libstd/rt/uv/file.rs | 50 +++++++++++++++++++---- src/libstd/rt/uv/uvio.rs | 38 ++++++++++++++++++ src/libstd/rt/uv/uvll.rs | 14 +++++++ src/rt/rust_uv.cpp | 9 +++++ src/rt/rustrt.def.in | 2 + 7 files changed, 184 insertions(+), 16 deletions(-) diff --git a/src/libstd/rt/io/file.rs b/src/libstd/rt/io/file.rs index af202a729e3d4..7aac79b964f1f 100644 --- a/src/libstd/rt/io/file.rs +++ b/src/libstd/rt/io/file.rs @@ -108,6 +108,22 @@ pub fn stat(path: &P) -> Option { } } +pub fn readdir(path: &P) -> Option<~[Path]> { + let readdir_result = unsafe { + let io: *mut IoFactoryObject = Local::unsafe_borrow(); + (*io).fs_readdir(path, 0) + }; + match readdir_result { + Ok(p) => { + Some(p) + }, + Err(ioerr) => { + io_error::cond.raise(ioerr); + None + } + } +} + /// Read-only view of file pub struct FileReader { priv stream: FileStream } @@ -272,6 +288,18 @@ pub trait FileSystemInfo { /// Represents passive information about a file (primarily exposed /// via the `stat()` method. Also provides methods for opening /// a file in various modes/permissions. +/// +/// # Example +/// +/// * Check if a file exists, reading from it if so +/// +/// let f = &Path("/some/file/path.txt"); +/// if f.exists() { +/// let reader = f.open_reader(Open); +/// let mut mem = [0u8, 8*64000]; +/// reader.read(mem); +/// // ... +/// } pub trait FileInfo : FileSystemInfo { /// Whether the underlying implemention (be it a file path, /// or something else) points at a "regular file" on the FS. Will return @@ -290,7 +318,7 @@ pub trait FileInfo : FileSystemInfo { match suppressed_stat(|| self.stat()) { Some(s) => match s.is_file { true => open(self.get_path(), mode, access), - false => None // FIXME: raise condition, not a regular file.. + false => None }, None => open(self.get_path(), mode, access) } @@ -320,13 +348,16 @@ pub trait FileInfo : FileSystemInfo { } } -/// `FileSystemInfo` implementation for `Path`s +/// `FileSystemInfo` implementation for `Path`s impl FileSystemInfo for Path { fn get_path<'a>(&'a self) -> &'a Path { self } } -/// `FileInfo` implementation for `Path`s +/// `FileInfo` implementation for `Path`s impl FileInfo for Path { } +/// Passive information about a directory on the filesystem. Includes +/// Convenience methods to iterate over a directory's contents (via `readdir`, as +/// as `mkdir` and `rmdir` operations. trait DirectoryInfo : FileSystemInfo { /// Whether the underlying implemention (be it a file path, /// or something else) points at a directory file" on the FS. Will return @@ -368,8 +399,9 @@ trait DirectoryInfo : FileSystemInfo { let ioerr = IoError { kind: MismatchedFileTypeForOperation, desc: "Cannot do rmdir() on a non-directory", - detail: - Some(fmt!("%s is a non-directory; can't rmdir it", self.get_path().to_str())) + detail: Some(fmt!( + "%s is a non-directory; can't rmdir it", + self.get_path().to_str())) }; io_error::cond.raise(ioerr); } @@ -383,14 +415,13 @@ trait DirectoryInfo : FileSystemInfo { }) } } - fn readdir(&self) -> ~[~str] { - ~[] + fn readdir(&self) -> Option<~[Path]> { + readdir(self.get_path()) } //fn get_subdirs(&self, filter: &str) -> ~[Path]; //fn get_files(&self, filter: &str) -> ~[Path]; } -/// FIXME: DOCS impl DirectoryInfo for Path { } fn file_test_smoke_test_impl() { @@ -663,3 +694,41 @@ fn file_test_directoryinfo_check_exists_before_and_after_mkdir() { assert!(!dir.exists()); } } + +#[test] +fn file_test_directoryinfo_readdir() { + use str; + do run_in_mt_newsched_task { + let dir = &Path("./tmp/di_readdir"); + dir.mkdir(); + let prefix = "foo"; + for n in range(0,3) { + let f = dir.push(fmt!("%d.txt", n)); + let mut w = f.open_writer(Create); + let msg_str = (prefix + n.to_str().to_owned()).to_owned(); + let msg = msg_str.as_bytes(); + w.write(msg); + } + match dir.readdir() { + Some(files) => { + let mut mem = [0u8, .. 4]; + for f in files.iter() { + { + let n = f.filestem(); + let mut r = f.open_reader(Open); + r.read(mem); + let read_str = str::from_utf8(mem); + let expected = match n { + Some(n) => prefix+n, + None => fail!("really shouldn't happen..") + }; + assert!(expected == read_str); + } + f.unlink(); + } + }, + None => fail!("shouldn't happen") + } + dir.rmdir(); + } +} \ No newline at end of file diff --git a/src/libstd/rt/rtio.rs b/src/libstd/rt/rtio.rs index 0abf81f62de28..568ea3317e53d 100644 --- a/src/libstd/rt/rtio.rs +++ b/src/libstd/rt/rtio.rs @@ -78,6 +78,8 @@ pub trait IoFactory { //fn fs_fstat(&mut self, fd: c_int) -> Result; fn fs_mkdir(&mut self, path: &P) -> Result<(), IoError>; fn fs_rmdir(&mut self, path: &P) -> Result<(), IoError>; + fn fs_readdir(&mut self, path: &P, flags: c_int) -> + Result<~[Path], IoError>; } pub trait RtioStream { diff --git a/src/libstd/rt/uv/file.rs b/src/libstd/rt/uv/file.rs index 34f87e3601e74..cd0a217cc45a9 100644 --- a/src/libstd/rt/uv/file.rs +++ b/src/libstd/rt/uv/file.rs @@ -17,6 +17,7 @@ use rt::uv::uvll; use rt::uv::uvll::*; use super::super::io::support::PathLike; use cast::transmute; +use libc; use libc::{c_int}; use option::{None, Some, Option}; @@ -28,14 +29,6 @@ pub struct RequestData { } impl FsRequest { - pub fn new_REFACTOR_ME(cb: Option) -> FsRequest { - let fs_req = unsafe { malloc_req(UV_FS) }; - assert!(fs_req.is_not_null()); - let fs_req: FsRequest = NativeHandle::from_native_handle(fs_req); - fs_req.install_req_data(cb); - fs_req - } - pub fn new() -> FsRequest { let fs_req = unsafe { malloc_req(UV_FS) }; assert!(fs_req.is_not_null()); @@ -180,6 +173,17 @@ impl FsRequest { }); } + pub fn readdir(self, loop_: &Loop, path: &P, + flags: c_int, cb: FsCallback) { + let complete_cb_ptr = self.req_boilerplate(Some(cb)); + path.path_as_str(|p| { + p.to_c_str().with_ref(|p| unsafe { + uvll::fs_readdir(loop_.native_handle(), + self.native_handle(), p, flags, complete_cb_ptr) + }) + }); + } + // accessors/utility funcs fn sync_cleanup(self, result: c_int) -> Result { @@ -235,6 +239,36 @@ impl FsRequest { stat } + pub fn get_ptr(&self) -> *libc::c_void { + unsafe { + uvll::get_ptr_from_fs_req(self.native_handle()) + } + } + + pub fn get_paths(&mut self) -> ~[~str] { + use str; + let ptr = self.get_ptr(); + match self.get_result() { + n if (n <= 0) => { + ~[] + }, + n => { + let n_len = n as uint; + // we pass in the len that uv tells us is there + // for the entries and we don't continue past that.. + // it appears that sometimes the multistring isn't + // correctly delimited and we stray into garbage memory? + // in any case, passing Some(n_len) fixes it and ensures + // good results + let raw_path_strs = unsafe { + str::raw::from_c_multistring(ptr as *libc::c_char, Some(n_len)) }; + let raw_len = raw_path_strs.len(); + assert_eq!(raw_len, n_len); + raw_path_strs + } + } + } + fn cleanup_and_delete(self) { unsafe { let data = uvll::get_data_for_req(self.native_handle()); diff --git a/src/libstd/rt/uv/uvio.rs b/src/libstd/rt/uv/uvio.rs index d701a87004c72..2d024f04e1d8b 100644 --- a/src/libstd/rt/uv/uvio.rs +++ b/src/libstd/rt/uv/uvio.rs @@ -704,6 +704,44 @@ impl IoFactory for UvIoFactory { }; } } + fn fs_readdir(&mut self, path: &P, flags: c_int) -> + Result<~[Path], IoError> { + use str::StrSlice; + let result_cell = Cell::new_empty(); + let result_cell_ptr: *Cell> = &result_cell; + let path_cell = Cell::new(path); + do task::unkillable { // FIXME(#8674) + let scheduler: ~Scheduler = Local::take(); + let stat_req = file::FsRequest::new(); + do scheduler.deschedule_running_task_and_then |_, task| { + let task_cell = Cell::new(task); + let path = path_cell.take(); + let path_str = path.path_as_str(|p| p.to_owned()); + do stat_req.readdir(self.uv_loop(), path, flags) + |req,err| { + let res = match err { + None => { + let rel_paths = req.get_paths(); + let mut paths = ~[]; + for r in rel_paths.iter() { + paths.push(Path(path_str+"/"+*r)); + } + Ok(paths) + }, + Some(e) => { + Err(uv_error_to_io_error(e)) + } + }; + unsafe { (*result_cell_ptr).put_back(res); } + let scheduler: ~Scheduler = Local::take(); + scheduler.resume_blocked_task_immediately(task_cell.take()); + }; + }; + }; + assert!(!result_cell.is_empty()); + return result_cell.take(); + } } pub struct UvTcpListener { diff --git a/src/libstd/rt/uv/uvll.rs b/src/libstd/rt/uv/uvll.rs index a2d1c48c3e1a0..42102a52e2e35 100644 --- a/src/libstd/rt/uv/uvll.rs +++ b/src/libstd/rt/uv/uvll.rs @@ -811,6 +811,12 @@ pub unsafe fn fs_rmdir(loop_ptr: *uv_loop_t, req: *uv_fs_t, path: *c_char, rust_uv_fs_rmdir(loop_ptr, req, path, cb) } +pub unsafe fn fs_readdir(loop_ptr: *uv_loop_t, req: *uv_fs_t, path: *c_char, + flags: c_int, cb: *u8) -> c_int { + #[fixed_stack_segment]; #[inline(never)]; + + rust_uv_fs_readdir(loop_ptr, req, path, flags, cb) +} pub unsafe fn populate_stat(req_in: *uv_fs_t, stat_out: *uv_stat_t) { #[fixed_stack_segment]; #[inline(never)]; @@ -828,6 +834,11 @@ pub unsafe fn get_result_from_fs_req(req: *uv_fs_t) -> c_int { rust_uv_get_result_from_fs_req(req) } +pub unsafe fn get_ptr_from_fs_req(req: *uv_fs_t) -> *libc::c_void { + #[fixed_stack_segment]; #[inline(never)]; + + rust_uv_get_ptr_from_fs_req(req) +} pub unsafe fn get_loop_from_fs_req(req: *uv_fs_t) -> *uv_loop_t { #[fixed_stack_segment]; #[inline(never)]; @@ -1014,9 +1025,12 @@ extern { mode: c_int, cb: *u8) -> c_int; fn rust_uv_fs_rmdir(loop_ptr: *c_void, req: *uv_fs_t, path: *c_char, cb: *u8) -> c_int; + fn rust_uv_fs_readdir(loop_ptr: *c_void, req: *uv_fs_t, path: *c_char, + flags: c_int, cb: *u8) -> c_int; fn rust_uv_fs_req_cleanup(req: *uv_fs_t); fn rust_uv_populate_uv_stat(req_in: *uv_fs_t, stat_out: *uv_stat_t); fn rust_uv_get_result_from_fs_req(req: *uv_fs_t) -> c_int; + fn rust_uv_get_ptr_from_fs_req(req: *uv_fs_t) -> *libc::c_void; fn rust_uv_get_loop_from_fs_req(req: *uv_fs_t) -> *uv_loop_t; fn rust_uv_get_loop_from_getaddrinfo_req(req: *uv_fs_t) -> *uv_loop_t; diff --git a/src/rt/rust_uv.cpp b/src/rt/rust_uv.cpp index ebc76c84ec9ff..9b460cffd747c 100644 --- a/src/rt/rust_uv.cpp +++ b/src/rt/rust_uv.cpp @@ -542,6 +542,10 @@ extern "C" int rust_uv_get_result_from_fs_req(uv_fs_t* req) { return req->result; } +extern "C" void* +rust_uv_get_ptr_from_fs_req(uv_fs_t* req) { + return req->ptr; +} extern "C" uv_loop_t* rust_uv_get_loop_from_fs_req(uv_fs_t* req) { return req->loop; @@ -593,3 +597,8 @@ extern "C" int rust_uv_fs_rmdir(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { return uv_fs_rmdir(loop, req, path, cb); } + +extern "C" int +rust_uv_fs_readdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, uv_fs_cb cb) { + return uv_fs_readdir(loop, req, path, flags, cb); +} diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in index d6b0d7c7a9dee..3be958837dc4b 100644 --- a/src/rt/rustrt.def.in +++ b/src/rt/rustrt.def.in @@ -113,6 +113,7 @@ rust_uv_fs_write rust_uv_fs_read rust_uv_fs_close rust_uv_get_result_from_fs_req +rust_uv_get_ptr_from_fs_req rust_uv_get_loop_from_fs_req rust_uv_fs_stat rust_uv_fs_fstat @@ -120,6 +121,7 @@ rust_uv_fs_req_cleanup rust_uv_populate_uv_stat rust_uv_fs_mkdir rust_uv_fs_rmdir +rust_uv_fs_readdir rust_dbg_lock_create rust_dbg_lock_destroy rust_dbg_lock_lock From e741449ea1f8b3526d7da6b0874be191b916ea2d Mon Sep 17 00:00:00 2001 From: Jeff Olson Date: Mon, 16 Sep 2013 14:02:35 -0700 Subject: [PATCH 09/21] std: unignore some file io tests that work on windows, now --- src/libstd/rt/io/file.rs | 4 ---- src/libstd/rt/uv/file.rs | 2 -- 2 files changed, 6 deletions(-) diff --git a/src/libstd/rt/io/file.rs b/src/libstd/rt/io/file.rs index 7aac79b964f1f..b53a2f177d6c4 100644 --- a/src/libstd/rt/io/file.rs +++ b/src/libstd/rt/io/file.rs @@ -622,7 +622,6 @@ fn file_test_io_seek_shakedown() { } #[test] -#[ignore(cfg(windows))] // FIXME #8810 fn file_test_stat_is_correct_on_is_file() { do run_in_mt_newsched_task { let filename = &Path("./tmp/file_stat_correct_on_is_file.txt"); @@ -641,7 +640,6 @@ fn file_test_stat_is_correct_on_is_file() { } #[test] -#[ignore(cfg(windows))] // FIXME #8810 fn file_test_stat_is_correct_on_is_dir() { do run_in_mt_newsched_task { let filename = &Path("./tmp/file_stat_correct_on_is_dir"); @@ -656,7 +654,6 @@ fn file_test_stat_is_correct_on_is_dir() { } #[test] -#[ignore(cfg(windows))] // FIXME #8810 fn file_test_fileinfo_false_when_checking_is_file_on_a_directory() { do run_in_mt_newsched_task { let dir = &Path("./tmp/fileinfo_false_on_dir"); @@ -667,7 +664,6 @@ fn file_test_fileinfo_false_when_checking_is_file_on_a_directory() { } #[test] -#[ignore(cfg(windows))] // FIXME #8810 fn file_test_fileinfo_check_exists_before_and_after_file_creation() { do run_in_mt_newsched_task { let file = &Path("./tmp/fileinfo_check_exists_b_and_a.txt"); diff --git a/src/libstd/rt/uv/file.rs b/src/libstd/rt/uv/file.rs index cd0a217cc45a9..74203165d8f4c 100644 --- a/src/libstd/rt/uv/file.rs +++ b/src/libstd/rt/uv/file.rs @@ -333,7 +333,6 @@ mod test { S_IWUSR, S_IRUSR}; #[test] - #[ignore(cfg(windows))] // FIXME #8814 fn file_test_full_simple() { do run_in_bare_thread { let mut loop_ = Loop::new(); @@ -409,7 +408,6 @@ mod test { } #[test] - #[ignore(cfg(windows))] // FIXME #8814 fn file_test_full_simple_sync() { do run_in_bare_thread { // setup From 52840a5bbc7fa3bc4bf93dacc95d0db523812639 Mon Sep 17 00:00:00 2001 From: Jeff Olson Date: Mon, 16 Sep 2013 15:33:19 -0700 Subject: [PATCH 10/21] std: correctly pass STDOUT in to naive_print test fn --- src/libstd/rt/uv/file.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstd/rt/uv/file.rs b/src/libstd/rt/uv/file.rs index 74203165d8f4c..229a641708438 100644 --- a/src/libstd/rt/uv/file.rs +++ b/src/libstd/rt/uv/file.rs @@ -474,7 +474,7 @@ mod test { let write_val = input.as_bytes(); let write_buf = slice_to_uv_buf(write_val); let write_req = FsRequest::new(); - write_req.write_sync(loop_, stdout, write_buf, -1); + write_req.write_sync(loop_, STDOUT_FILENO, write_buf, -1); } #[test] From e9acdd93920f126d733d525d98234cd2df5706f1 Mon Sep 17 00:00:00 2001 From: Jeff Olson Date: Mon, 16 Sep 2013 21:56:51 -0700 Subject: [PATCH 11/21] std: generlize & move io::file::suppressed_stat to io::ignore_io_error --- src/libstd/rt/io/file.rs | 26 +++++++------------------- src/libstd/rt/io/mod.rs | 12 ++++++++++++ 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/libstd/rt/io/file.rs b/src/libstd/rt/io/file.rs index b53a2f177d6c4..ee96583206acb 100644 --- a/src/libstd/rt/io/file.rs +++ b/src/libstd/rt/io/file.rs @@ -17,7 +17,7 @@ use rt::rtio::{RtioFileStream, IoFactory, IoFactoryObject}; use rt::io::{io_error, read_error, EndOfFile, FileMode, FileAccess, FileStat, IoError, PathAlreadyExists, PathDoesntExist, - MismatchedFileTypeForOperation}; + MismatchedFileTypeForOperation, ignore_io_error}; use rt::local::Local; use option::{Some, None}; use path::Path; @@ -248,18 +248,6 @@ impl Seek for FileStream { } } -// helper for grabbing a stat and ignoring any -// error.. used in Info wrappers -fn suppressed_stat(cb: &fn() -> Option) -> Option { - do io_error::cond.trap(|_| { - // just swallow the error.. downstream users - // who can make a decision based on a None result - // won't care - }).inside { - cb() - } -} - /// Shared functionality between `FileInfo` and `DirectoryInfo` pub trait FileSystemInfo { /// Get the filesystem path that this instance points at, @@ -277,7 +265,7 @@ pub trait FileSystemInfo { /// returns `true` if the location pointed at by the enclosing /// exists on the filesystem fn exists(&self) -> bool { - match suppressed_stat(|| self.stat()) { + match ignore_io_error(|| self.stat()) { Some(_) => true, None => false } @@ -306,7 +294,7 @@ pub trait FileInfo : FileSystemInfo { /// false for paths to non-existent locations or directories or /// other non-regular files (named pipes, etc). fn is_file(&self) -> bool { - match suppressed_stat(|| self.stat()) { + match ignore_io_error(|| self.stat()) { Some(s) => s.is_file, None => false } @@ -315,7 +303,7 @@ pub trait FileInfo : FileSystemInfo { /// Attempts to open a regular file for reading/writing based /// on provided inputs fn open_stream(&self, mode: FileMode, access: FileAccess) -> Option { - match suppressed_stat(|| self.stat()) { + match ignore_io_error(|| self.stat()) { Some(s) => match s.is_file { true => open(self.get_path(), mode, access), false => None @@ -364,7 +352,7 @@ trait DirectoryInfo : FileSystemInfo { /// false for paths to non-existent locations or if the item is /// not a directory (eg files, named pipes, links, etc) fn is_dir(&self) -> bool { - match suppressed_stat(|| self.stat()) { + match ignore_io_error(|| self.stat()) { Some(s) => s.is_dir, None => false } @@ -375,7 +363,7 @@ trait DirectoryInfo : FileSystemInfo { /// at that location or if some other error occurs during /// the mkdir operation fn mkdir(&self) { - match suppressed_stat(|| self.stat()) { + match ignore_io_error(|| self.stat()) { Some(_) => { io_error::cond.raise(IoError { kind: PathAlreadyExists, @@ -391,7 +379,7 @@ trait DirectoryInfo : FileSystemInfo { /// the type underlying the given `DirectoryInfo`. Will fail /// if there is no directory at the given location or if fn rmdir(&self) { - match suppressed_stat(|| self.stat()) { + match ignore_io_error(|| self.stat()) { Some(s) => { match s.is_dir { true => rmdir(self.get_path()), diff --git a/src/libstd/rt/io/mod.rs b/src/libstd/rt/io/mod.rs index afef369fa4414..871b41039d1ce 100644 --- a/src/libstd/rt/io/mod.rs +++ b/src/libstd/rt/io/mod.rs @@ -401,6 +401,18 @@ condition! { pub read_error: super::IoError -> (); } +/// Helper for wrapper calls where you want to +/// ignore any io_errors that might be raised +pub fn ignore_io_error(cb: &fn() -> T) -> T { + do io_error::cond.trap(|_| { + // just swallow the error.. downstream users + // who can make a decision based on a None result + // won't care + }).inside { + cb() + } +} + pub trait Reader { /// Read bytes, up to the length of `buf` and place them in `buf`. /// Returns the number of bytes read. The number of bytes read my From a87ff60f496da746733b91b28d1e7b10fc22a612 Mon Sep 17 00:00:00 2001 From: Jeff Olson Date: Mon, 16 Sep 2013 21:57:17 -0700 Subject: [PATCH 12/21] std: remove impl'd/commented-out fstat signatures --- src/libstd/rt/rtio.rs | 1 - src/libstd/rt/uv/uvio.rs | 3 --- 2 files changed, 4 deletions(-) diff --git a/src/libstd/rt/rtio.rs b/src/libstd/rt/rtio.rs index 568ea3317e53d..c3ab2bd1cfd4e 100644 --- a/src/libstd/rt/rtio.rs +++ b/src/libstd/rt/rtio.rs @@ -75,7 +75,6 @@ pub trait IoFactory { fn fs_unlink(&mut self, path: &P) -> Result<(), IoError>; fn get_host_addresses(&mut self, host: &str) -> Result<~[IpAddr], IoError>; fn fs_stat(&mut self, path: &P) -> Result; - //fn fs_fstat(&mut self, fd: c_int) -> Result; fn fs_mkdir(&mut self, path: &P) -> Result<(), IoError>; fn fs_rmdir(&mut self, path: &P) -> Result<(), IoError>; fn fs_readdir(&mut self, path: &P, flags: c_int) -> diff --git a/src/libstd/rt/uv/uvio.rs b/src/libstd/rt/uv/uvio.rs index 2d024f04e1d8b..154c90ec6fa49 100644 --- a/src/libstd/rt/uv/uvio.rs +++ b/src/libstd/rt/uv/uvio.rs @@ -686,9 +686,6 @@ impl IoFactory for UvIoFactory { assert!(!result_cell.is_empty()); return result_cell.take(); } - //fn fs_fstat(&mut self, _fd: c_int) -> Result { - // Ok(FileStat) - //} fn fs_mkdir(&mut self, path: &P) -> Result<(), IoError> { let mode = S_IRWXU as int; do uv_fs_helper(self.uv_loop(), path) |mkdir_req, l, p, cb| { From 60ba17098b92f4631e032040e547405ad046172d Mon Sep 17 00:00:00 2001 From: Jeff Olson Date: Mon, 16 Sep 2013 21:58:49 -0700 Subject: [PATCH 13/21] std: FsRequest.req_boilerplate() be &mut self --- src/libstd/rt/uv/file.rs | 75 +++++++++++++++++++++++++++++++--------- 1 file changed, 58 insertions(+), 17 deletions(-) diff --git a/src/libstd/rt/uv/file.rs b/src/libstd/rt/uv/file.rs index 229a641708438..ada558036cfea 100644 --- a/src/libstd/rt/uv/file.rs +++ b/src/libstd/rt/uv/file.rs @@ -38,7 +38,10 @@ impl FsRequest { pub fn open(self, loop_: &Loop, path: &P, flags: int, mode: int, cb: FsCallback) { - let complete_cb_ptr = self.req_boilerplate(Some(cb)); + let complete_cb_ptr = { + let mut me = self; + me.req_boilerplate(Some(cb)) + }; path.path_as_str(|p| { p.to_c_str().with_ref(|p| unsafe { uvll::fs_open(loop_.native_handle(), @@ -49,7 +52,10 @@ impl FsRequest { pub fn open_sync(self, loop_: &Loop, path: &P, flags: int, mode: int) -> Result { - let complete_cb_ptr = self.req_boilerplate(None); + let complete_cb_ptr = { + let mut me = self; + me.req_boilerplate(None) + }; let result = path.path_as_str(|p| { p.to_c_str().with_ref(|p| unsafe { uvll::fs_open(loop_.native_handle(), @@ -60,7 +66,10 @@ impl FsRequest { } pub fn unlink(self, loop_: &Loop, path: &P, cb: FsCallback) { - let complete_cb_ptr = self.req_boilerplate(Some(cb)); + let complete_cb_ptr = { + let mut me = self; + me.req_boilerplate(Some(cb)) + }; path.path_as_str(|p| { p.to_c_str().with_ref(|p| unsafe { uvll::fs_unlink(loop_.native_handle(), @@ -71,7 +80,10 @@ impl FsRequest { pub fn unlink_sync(self, loop_: &Loop, path: &P) -> Result { - let complete_cb_ptr = self.req_boilerplate(None); + let complete_cb_ptr = { + let mut me = self; + me.req_boilerplate(None) + }; let result = path.path_as_str(|p| { p.to_c_str().with_ref(|p| unsafe { uvll::fs_unlink(loop_.native_handle(), @@ -82,7 +94,10 @@ impl FsRequest { } pub fn stat(self, loop_: &Loop, path: &P, cb: FsCallback) { - let complete_cb_ptr = self.req_boilerplate(Some(cb)); + let complete_cb_ptr = { + let mut me = self; + me.req_boilerplate(Some(cb)) + }; path.path_as_str(|p| { p.to_c_str().with_ref(|p| unsafe { uvll::fs_stat(loop_.native_handle(), @@ -92,7 +107,10 @@ impl FsRequest { } pub fn write(self, loop_: &Loop, fd: c_int, buf: Buf, offset: i64, cb: FsCallback) { - let complete_cb_ptr = self.req_boilerplate(Some(cb)); + let complete_cb_ptr = { + let mut me = self; + me.req_boilerplate(Some(cb)) + }; let base_ptr = buf.base as *c_void; let len = buf.len as uint; unsafe { @@ -103,7 +121,10 @@ impl FsRequest { } pub fn write_sync(self, loop_: &Loop, fd: c_int, buf: Buf, offset: i64) -> Result { - let complete_cb_ptr = self.req_boilerplate(None); + let complete_cb_ptr = { + let mut me = self; + me.req_boilerplate(None) + }; let base_ptr = buf.base as *c_void; let len = buf.len as uint; let result = unsafe { @@ -115,7 +136,10 @@ impl FsRequest { } pub fn read(self, loop_: &Loop, fd: c_int, buf: Buf, offset: i64, cb: FsCallback) { - let complete_cb_ptr = self.req_boilerplate(Some(cb)); + let complete_cb_ptr = { + let mut me = self; + me.req_boilerplate(Some(cb)) + }; let buf_ptr = buf.base as *c_void; let len = buf.len as uint; unsafe { @@ -126,7 +150,10 @@ impl FsRequest { } pub fn read_sync(self, loop_: &Loop, fd: c_int, buf: Buf, offset: i64) -> Result { - let complete_cb_ptr = self.req_boilerplate(None); + let complete_cb_ptr = { + let mut me = self; + me.req_boilerplate(None) + }; let buf_ptr = buf.base as *c_void; let len = buf.len as uint; let result = unsafe { @@ -138,14 +165,20 @@ impl FsRequest { } pub fn close(self, loop_: &Loop, fd: c_int, cb: FsCallback) { - let complete_cb_ptr = self.req_boilerplate(Some(cb)); + let complete_cb_ptr = { + let mut me = self; + me.req_boilerplate(Some(cb)) + }; unsafe { uvll::fs_close(loop_.native_handle(), self.native_handle(), fd, complete_cb_ptr) }; } pub fn close_sync(self, loop_: &Loop, fd: c_int) -> Result { - let complete_cb_ptr = self.req_boilerplate(None); + let complete_cb_ptr = { + let mut me = self; + me.req_boilerplate(None) + }; let result = unsafe { uvll::fs_close(loop_.native_handle(), self.native_handle(), fd, complete_cb_ptr) @@ -154,7 +187,10 @@ impl FsRequest { } pub fn mkdir(self, loop_: &Loop, path: &P, mode: int, cb: FsCallback) { - let complete_cb_ptr = self.req_boilerplate(Some(cb)); + let complete_cb_ptr = { + let mut me = self; + me.req_boilerplate(Some(cb)) + }; path.path_as_str(|p| { p.to_c_str().with_ref(|p| unsafe { uvll::fs_mkdir(loop_.native_handle(), @@ -164,7 +200,10 @@ impl FsRequest { } pub fn rmdir(self, loop_: &Loop, path: &P, cb: FsCallback) { - let complete_cb_ptr = self.req_boilerplate(Some(cb)); + let complete_cb_ptr = { + let mut me = self; + me.req_boilerplate(Some(cb)) + }; path.path_as_str(|p| { p.to_c_str().with_ref(|p| unsafe { uvll::fs_rmdir(loop_.native_handle(), @@ -175,7 +214,10 @@ impl FsRequest { pub fn readdir(self, loop_: &Loop, path: &P, flags: c_int, cb: FsCallback) { - let complete_cb_ptr = self.req_boilerplate(Some(cb)); + let complete_cb_ptr = { + let mut me = self; + me.req_boilerplate(Some(cb)) + }; path.path_as_str(|p| { p.to_c_str().with_ref(|p| unsafe { uvll::fs_readdir(loop_.native_handle(), @@ -193,8 +235,7 @@ impl FsRequest { None => Ok(result) } } - fn req_boilerplate(&self, cb: Option) -> *u8 { - // XXX: this is unsafe/mutable + fn req_boilerplate(&mut self, cb: Option) -> *u8 { let result = match cb { Some(_) => { compl_cb as *u8 @@ -204,7 +245,7 @@ impl FsRequest { self.install_req_data(cb); result } - pub fn install_req_data(&self, cb: Option) { + pub fn install_req_data(&mut self, cb: Option) { let fs_req = (self.native_handle()) as *uvll::uv_write_t; let data = ~RequestData { complete_cb: cb From d3ed9a9e3bbcbdb6b098f8b7c8edc283f317cd30 Mon Sep 17 00:00:00 2001 From: Jeff Olson Date: Mon, 16 Sep 2013 23:10:03 -0700 Subject: [PATCH 14/21] std: lots of docs for std::rt::io::file i hope they don't bitrot --- src/libstd/rt/io/file.rs | 350 +++++++++++++++++++++++++++++++++------ 1 file changed, 298 insertions(+), 52 deletions(-) diff --git a/src/libstd/rt/io/file.rs b/src/libstd/rt/io/file.rs index ee96583206acb..8227f3a1cb560 100644 --- a/src/libstd/rt/io/file.rs +++ b/src/libstd/rt/io/file.rs @@ -23,7 +23,73 @@ use option::{Some, None}; use path::Path; use super::super::test::*; -/// Open a file for reading/writing, as indicated by `path`. +/*! Synchronous File I/O + +This module provides a set of functions and traits for working +with regular files & directories on a filesystem. + +At the top-level of the module are a set of freestanding functions, +associated with various filesystem operations. They all operate +on a `PathLike` object. + +All operations in this module, including those as part of `FileStream` et al +block the task during execution. Most will raise `std::rt::io::{io_error,read_error}` +conditions in the event of failure. + +Also included in this module are the `FileInfo` and `DirectoryInfo` traits. When +`use`'d alongside a value whose type implements them (A `std::path::Path` impl is +a part of this module), they expose a set of functions for operations against +a given file location, depending on whether the path already exists. Whenever +possible, the `{FileInfo, DirectoryInfo}` preserve the same semantics as their +free function counterparts. +*/ + +/*! Open a file for reading/writing, as indicated by `path`. + +# Example + + use std; + use std::path::Path; + use std::rt::io::support::PathLike; + use std::rt::io::file::open; + use std::rt::io::{FileMode, FileAccess}; + + let p = &Path("/some/file/path.txt"); + + do io_error::cond.trap(|_| { + // hoo-boy... + }).inside { + let stream = match open(p, Create, ReadWrite) { + Some(s) => s, + None => fail!("whoops! I'm sure this raised, anyways.."); + } + // do some stuff with that stream + + // the file stream will be closed at the end of this block + } + // .. + +`FileMode` and `FileAccess` provide information about the permissions +context in which a given stream is created. More information about them +can be found in `std::rt::io`'s docs. + +Note that, with this function, a `FileStream` is returned regardless of +the access-limitations indicated by `FileAccess` (e.g. calling `write` on a +`FileStream` opened as `ReadOnly` will raise an `io_error` condition at runtime). If you +desire a more-correctly-constrained interface to files, use the +`{open_stream, open_reader, open_writer}` methods that are a part of `FileInfo` + +# Errors + +This function will raise an `io_error` condition under a number of different circumstances, +to include but not limited to: + +* Opening a file that already exists with `FileMode` of `Create` or vice versa (e.g. + opening a non-existant file with `FileMode` or `Open`) +* Attempting to open a file with a `FileAccess` that the user lacks permissions + for +* Filesystem-level errors (full disk, etc) +*/ pub fn open(path: &P, mode: FileMode, access: FileAccess @@ -44,8 +110,29 @@ pub fn open(path: &P, } } -/// Unlink (remove) a file from the filesystem, as indicated -/// by `path`. +/*! Unlink a file from the underlying filesystem. + +# Example + + use std; + use std::path::Path; + use std::rt::io::support::PathLike; + use std::rt::io::file::unlink; + + let p = &Path("/some/file/path.txt"); + unlink(p); + // if we made it here without failing, then the + // unlink operation was successful + +Note that, just because an unlink call was successful, it is not +guaranteed that a file is immediately deleted (e.g. depending on +platform, other open file descriptors may prevent immediate removal) + +# Errors + +This function will raise an `io_error` condition if the user lacks permissions to +remove the file or if some other filesystem-level error occurs +*/ pub fn unlink(path: &P) { let unlink_result = unsafe { let io: *mut IoFactoryObject = Local::unsafe_borrow(); @@ -59,8 +146,24 @@ pub fn unlink(path: &P) { } } -/// Create a new directory with default permissions (process user -/// has read/write privs) +/*! Create a new, empty directory at the provided path + +# Example + + use std; + use std::path::Path; + use std::rt::io::support::PathLike; + use std::rt::io::file::mkdir; + + let p = &Path("/some/dir"); + mkdir(p); + // If we got here, our directory exists! Horray! + +# Errors + +This call will raise an `io_error` condition if the user lacks permissions to make a +new directory at the provided path, or if the directory already exists +*/ pub fn mkdir(path: &P) { let mkdir_result = unsafe { let io: *mut IoFactoryObject = Local::unsafe_borrow(); @@ -73,7 +176,25 @@ pub fn mkdir(path: &P) { } } } -/// Removes a directory + +/*! Remove an existing, empty directory + +# Example + + use std; + use std::path::Path; + use std::rt::io::support::PathLike; + use std::rt::io::file::rmdir; + + let p = &Path("/some/dir"); + rmdir(p); + // good riddance, you mean ol' directory + +# Errors + +This call will raise an `io_error` condition if the user lacks permissions to remove the +directory at the provided path, or if the directory isn't empty +*/ pub fn rmdir(path: &P) { let rmdir_result = unsafe { let io: *mut IoFactoryObject = Local::unsafe_borrow(); @@ -87,11 +208,43 @@ pub fn rmdir(path: &P) { } } -/// Given a `rt::io::support::PathLike`, query the file system to get -/// information about a file, directory, etc. -/// -/// Returns a `Some(PathInfo)` on success, and raises a `rt::io::IoError` condition -/// on failure and returns `None`. +/*! Get information on the file, directory, etc at the provided path + +Given a `rt::io::support::PathLike`, query the file system to get +information about a file, directory, etc. + +Returns a `Some(std::rt::io::PathInfo)` on success + +# Example + + use std; + use std::path::Path; + use std::rt::io::support::PathLike; + use std::rt::io::file::stat; + + let p = &Path("/some/file/path.txt"); + + do io_error::cond.trap(|_| { + // hoo-boy... + }).inside { + let info = match stat(p) { + Some(s) => s, + None => fail!("whoops! I'm sure this raised, anyways.."); + } + if stat.is_file { + // just imagine the possibilities ... + } + + // the file stream will be closed at the end of this block + } + // .. + +# Errors + +This call will raise an `io_error` condition if the user lacks the requisite +permissions to perform a `stat` call on the given path or if there is no +entry in the filesystem at the provided path. +*/ pub fn stat(path: &P) -> Option { let open_result = unsafe { let io: *mut IoFactoryObject = Local::unsafe_borrow(); @@ -108,6 +261,8 @@ pub fn stat(path: &P) -> Option { } } +/*! Retrieve a vector containing all entries within a provided directory +*/ pub fn readdir(path: &P) -> Option<~[Path]> { let readdir_result = unsafe { let io: *mut IoFactoryObject = Local::unsafe_borrow(); @@ -124,9 +279,13 @@ pub fn readdir(path: &P) -> Option<~[Path]> { } } -/// Read-only view of file +/*! Constrained version of `FileStream` that only exposes read-specific operations. + +Can be retreived via `FileInfo.open_reader()`. +*/ pub struct FileReader { priv stream: FileStream } +/// a `std::rt::io::Reader` trait impl for file I/O. impl Reader for FileReader { fn read(&mut self, buf: &mut [u8]) -> Option { self.stream.read(buf) @@ -137,6 +296,7 @@ impl Reader for FileReader { } } +/// a `std::rt::io::Seek` trait impl for file I/O. impl Seek for FileReader { fn tell(&self) -> u64 { self.stream.tell() @@ -147,9 +307,13 @@ impl Seek for FileReader { } } -/// Write-only view of a file +/*! Constrained version of `FileStream` that only exposes write-specific operations. + +Can be retreived via `FileInfo.open_writer()`. +*/ pub struct FileWriter { priv stream: FileStream } +/// a `std::rt::io::Writer` trait impl for file I/O. impl Writer for FileWriter { fn write(&mut self, buf: &[u8]) { self.stream.write(buf); @@ -160,6 +324,7 @@ impl Writer for FileWriter { } } +/// a `std::rt::io::Seek` trait impl for file I/O. impl Seek for FileWriter { fn tell(&self) -> u64 { self.stream.tell() @@ -170,13 +335,25 @@ impl Seek for FileWriter { } } -/// Internal representation of a FileStream, used to consolidate functionality -/// exposed in the public API +/*! Unconstrained file access type that exposes read and write operations + +Can be retreived via `file::open()` and `FileInfo.open_stream()`. + +# Errors + +This type will raise an io_error condition if operations are attempted against +it for which its underlying file descriptor was not configured at creation +time, via the `FileAccess` parameter to `file::open()`. + +For this reason, it is best to use the access-constrained wrappers that are +exposed via `FileInfo.open_reader()` and `FileInfo.open_writer()`. +*/ pub struct FileStream { fd: ~RtioFileStream, last_nread: int, } +/// a `std::rt::io::Reader` trait impl for file I/O. impl Reader for FileStream { fn read(&mut self, buf: &mut [u8]) -> Option { match self.fd.read(buf) { @@ -202,6 +379,7 @@ impl Reader for FileStream { } } +/// a `std::rt::io::Writer` trait impl for file I/O. impl Writer for FileStream { fn write(&mut self, buf: &[u8]) { match self.fd.write(buf) { @@ -222,6 +400,7 @@ impl Writer for FileStream { } } +/// a `std::rt::io:Seek` trait impl for file I/O. impl Seek for FileStream { fn tell(&self) -> u64 { let res = self.fd.tell(); @@ -256,14 +435,21 @@ pub trait FileSystemInfo { /// later creates fn get_path<'a>(&'a self) -> &'a Path; - /// Ask the operating system for information about the path, - /// will raise a condition if an error occurs during the process + /*! Get information on the file, directory, etc at the provided path + + Consult the `file::stat` documentation for more info. + + This call preserves identical runtime/error semantics + */ fn stat(&self) -> Option { stat(self.get_path()) } - /// returns `true` if the location pointed at by the enclosing - /// exists on the filesystem + /// Boolean value indicator whether the underlying file exists on the filesystem + /// + /// # Errors + /// + /// Will not raise a condition fn exists(&self) -> bool { match ignore_io_error(|| self.stat()) { Some(_) => true, @@ -273,21 +459,32 @@ pub trait FileSystemInfo { } -/// Represents passive information about a file (primarily exposed -/// via the `stat()` method. Also provides methods for opening -/// a file in various modes/permissions. -/// -/// # Example -/// -/// * Check if a file exists, reading from it if so -/// -/// let f = &Path("/some/file/path.txt"); -/// if f.exists() { -/// let reader = f.open_reader(Open); -/// let mut mem = [0u8, 8*64000]; -/// reader.read(mem); -/// // ... -/// } +/*! Represents a file, whose underlying path may or may not be valid + +# Example + +* Check if a file exists, reading from it if so + + use std; + use std::path::Path; + use std::rt::io::file::{FileInfo, FileReader}; + + let f = &Path("/some/file/path.txt"); + if f.exists() { + let reader = f.open_reader(Open); + let mut mem = [0u8, 8*64000]; + reader.read(mem); + // ... + } + +* Is the given path a file? + + let f = get_file_path_from_wherever(); + match f.is_file() { + true => doing_something_with_a_file(f), + _ => {} + } +*/ pub trait FileInfo : FileSystemInfo { /// Whether the underlying implemention (be it a file path, /// or something else) points at a "regular file" on the FS. Will return @@ -302,6 +499,8 @@ pub trait FileInfo : FileSystemInfo { /// Attempts to open a regular file for reading/writing based /// on provided inputs + /// + /// See `file::open` for more information on runtime semantics and error conditions fn open_stream(&self, mode: FileMode, access: FileAccess) -> Option { match ignore_io_error(|| self.stat()) { Some(s) => match s.is_file { @@ -311,8 +510,11 @@ pub trait FileInfo : FileSystemInfo { None => open(self.get_path(), mode, access) } } - /// Attempts to open a regular file for reading-only based + + /// Attempts to open a regular file in read-only mode, based /// on provided inputs + /// + /// See `file::open` for more information on runtime semantics and error conditions fn open_reader(&self, mode: FileMode) -> Option { match self.open_stream(mode, Read) { Some(s) => Some(FileReader { stream: s}), @@ -320,8 +522,10 @@ pub trait FileInfo : FileSystemInfo { } } - /// Attempts to open a regular file for writing-only based + /// Attempts to open a regular file in write-only mode, based /// on provided inputs + /// + /// See `file::open` for more information on runtime semantics and error conditions fn open_writer(&self, mode: FileMode) -> Option { match self.open_stream(mode, Write) { Some(s) => Some(FileWriter { stream: s}), @@ -329,8 +533,9 @@ pub trait FileInfo : FileSystemInfo { } } - /// Attempt to remove a file from the filesystem, pending the closing - /// of any open file descriptors pointing to the file + /// Attempt to remove a file from the filesystem + /// + /// See `file::unlink` for more information on runtime semantics and error conditions fn unlink(&self) { unlink(self.get_path()); } @@ -340,16 +545,42 @@ pub trait FileInfo : FileSystemInfo { impl FileSystemInfo for Path { fn get_path<'a>(&'a self) -> &'a Path { self } } + /// `FileInfo` implementation for `Path`s impl FileInfo for Path { } -/// Passive information about a directory on the filesystem. Includes -/// Convenience methods to iterate over a directory's contents (via `readdir`, as -/// as `mkdir` and `rmdir` operations. +/*! Represents a directory, whose underlying path may or may not be valid + +# Example + +* Check if a directory exists, `mkdir`'ing it if not + + use std; + use std::path::Path; + use std::rt::io::file::{DirectoryInfo}; + + let dir = &Path("/some/dir"); + if !dir.exists() { + dir.mkdir(); + } + +* Is the given path a directory? If so, iterate on its contents + + fn visit_dirs(dir: &Path, cb: &fn(&Path)) { + if dir.is_dir() { + let contents = dir.readdir(); + for entry in contents.iter() { + if entry.is_dir() { visit_dirs(entry, cb); } + else { cb(entry); } + } + } + else { fail!("nope"); } + } +*/ trait DirectoryInfo : FileSystemInfo { /// Whether the underlying implemention (be it a file path, - /// or something else) points at a directory file" on the FS. Will return - /// false for paths to non-existent locations or if the item is + /// or something else) is pointing at a directory in the underlying FS. + /// Will return false for paths to non-existent locations or if the item is /// not a directory (eg files, named pipes, links, etc) fn is_dir(&self) -> bool { match ignore_io_error(|| self.stat()) { @@ -357,11 +588,16 @@ trait DirectoryInfo : FileSystemInfo { None => false } } + /// Create a directory at the location pointed to by the - /// type underlying the given `DirectoryInfo`. Raises a - /// condition if a file, directory, etc already exists - /// at that location or if some other error occurs during - /// the mkdir operation + /// type underlying the given `DirectoryInfo`. + /// + /// # Errors + /// + /// This method will raise a `PathAlreadyExists` kind of `io_error` condition + /// if the provided path exists + /// + /// See `file::mkdir` for more information on runtime semantics and error conditions fn mkdir(&self) { match ignore_io_error(|| self.stat()) { Some(_) => { @@ -375,9 +611,17 @@ trait DirectoryInfo : FileSystemInfo { None => mkdir(self.get_path()) } } - /// Remove a directory at the given location pointed to by - /// the type underlying the given `DirectoryInfo`. Will fail - /// if there is no directory at the given location or if + + /// Remove a directory at the given location. + /// + /// # Errors + /// + /// This method will raise a `PathDoesntExist` kind of `io_error` condition + /// if the provided path exists. It will raise a `MismatchedFileTypeForOperation` + /// kind of `io_error` condition if the provided path points at any + /// non-directory file type + /// + /// See `file::rmdir` for more information on runtime semantics and error conditions fn rmdir(&self) { match ignore_io_error(|| self.stat()) { Some(s) => { @@ -403,13 +647,15 @@ trait DirectoryInfo : FileSystemInfo { }) } } + + // Get a collection of all entries at the given + // directory fn readdir(&self) -> Option<~[Path]> { readdir(self.get_path()) } - //fn get_subdirs(&self, filter: &str) -> ~[Path]; - //fn get_files(&self, filter: &str) -> ~[Path]; } +/// `DirectoryInfo` impl for `path::Path` impl DirectoryInfo for Path { } fn file_test_smoke_test_impl() { From 56c87ffb30d0c876210497d20c3493fd39a75163 Mon Sep 17 00:00:00 2001 From: Jeff Olson Date: Mon, 16 Sep 2013 23:13:16 -0700 Subject: [PATCH 15/21] std: minor cleanup in some io_error descs in io::file --- src/libstd/rt/io/file.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libstd/rt/io/file.rs b/src/libstd/rt/io/file.rs index 8227f3a1cb560..75798837aff41 100644 --- a/src/libstd/rt/io/file.rs +++ b/src/libstd/rt/io/file.rs @@ -603,7 +603,7 @@ trait DirectoryInfo : FileSystemInfo { Some(_) => { io_error::cond.raise(IoError { kind: PathAlreadyExists, - desc: "path already exists", + desc: "Path already exists", detail: Some(fmt!("%s already exists; can't mkdir it", self.get_path().to_str())) }) @@ -642,7 +642,7 @@ trait DirectoryInfo : FileSystemInfo { None => io_error::cond.raise(IoError { kind: PathDoesntExist, - desc: "path doesn't exist", + desc: "Path doesn't exist", detail: Some(fmt!("%s doesn't exist; can't rmdir it", self.get_path().to_str())) }) } From 95ef1f54153095d7287ac1ca4209cdef4635ab0e Mon Sep 17 00:00:00 2001 From: Jeff Olson Date: Mon, 16 Sep 2013 23:36:39 -0700 Subject: [PATCH 16/21] std: docstring fixes in io::file --- src/libstd/rt/io/file.rs | 498 ++++++++++++++++++++------------------- 1 file changed, 259 insertions(+), 239 deletions(-) diff --git a/src/libstd/rt/io/file.rs b/src/libstd/rt/io/file.rs index 75798837aff41..2a9b71db90bb0 100644 --- a/src/libstd/rt/io/file.rs +++ b/src/libstd/rt/io/file.rs @@ -8,21 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use prelude::*; -use super::support::PathLike; -use super::{Reader, Writer, Seek}; -use super::{SeekStyle,SeekSet, SeekCur, SeekEnd, - Open, Read, Write, Create, ReadWrite}; -use rt::rtio::{RtioFileStream, IoFactory, IoFactoryObject}; -use rt::io::{io_error, read_error, EndOfFile, - FileMode, FileAccess, FileStat, IoError, - PathAlreadyExists, PathDoesntExist, - MismatchedFileTypeForOperation, ignore_io_error}; -use rt::local::Local; -use option::{Some, None}; -use path::Path; -use super::super::test::*; - /*! Synchronous File I/O This module provides a set of functions and traits for working @@ -44,52 +29,66 @@ possible, the `{FileInfo, DirectoryInfo}` preserve the same semantics as their free function counterparts. */ -/*! Open a file for reading/writing, as indicated by `path`. - -# Example - - use std; - use std::path::Path; - use std::rt::io::support::PathLike; - use std::rt::io::file::open; - use std::rt::io::{FileMode, FileAccess}; - - let p = &Path("/some/file/path.txt"); - - do io_error::cond.trap(|_| { - // hoo-boy... - }).inside { - let stream = match open(p, Create, ReadWrite) { - Some(s) => s, - None => fail!("whoops! I'm sure this raised, anyways.."); - } - // do some stuff with that stream - - // the file stream will be closed at the end of this block - } - // .. - -`FileMode` and `FileAccess` provide information about the permissions -context in which a given stream is created. More information about them -can be found in `std::rt::io`'s docs. - -Note that, with this function, a `FileStream` is returned regardless of -the access-limitations indicated by `FileAccess` (e.g. calling `write` on a -`FileStream` opened as `ReadOnly` will raise an `io_error` condition at runtime). If you -desire a more-correctly-constrained interface to files, use the -`{open_stream, open_reader, open_writer}` methods that are a part of `FileInfo` - -# Errors - -This function will raise an `io_error` condition under a number of different circumstances, -to include but not limited to: +use prelude::*; +use super::support::PathLike; +use super::{Reader, Writer, Seek}; +use super::{SeekStyle,SeekSet, SeekCur, SeekEnd, + Open, Read, Write, Create, ReadWrite}; +use rt::rtio::{RtioFileStream, IoFactory, IoFactoryObject}; +use rt::io::{io_error, read_error, EndOfFile, + FileMode, FileAccess, FileStat, IoError, + PathAlreadyExists, PathDoesntExist, + MismatchedFileTypeForOperation, ignore_io_error}; +use rt::local::Local; +use option::{Some, None}; +use path::Path; +use super::super::test::*; -* Opening a file that already exists with `FileMode` of `Create` or vice versa (e.g. - opening a non-existant file with `FileMode` or `Open`) -* Attempting to open a file with a `FileAccess` that the user lacks permissions - for -* Filesystem-level errors (full disk, etc) -*/ +/// Open a file for reading/writing, as indicated by `path`. +/// +/// # Example +/// +/// use std; +/// use std::path::Path; +/// use std::rt::io::support::PathLike; +/// use std::rt::io::file::open; +/// use std::rt::io::{FileMode, FileAccess}; +/// +/// let p = &Path("/some/file/path.txt"); +/// +/// do io_error::cond.trap(|_| { +/// // hoo-boy... +/// }).inside { +/// let stream = match open(p, Create, ReadWrite) { +/// Some(s) => s, +/// None => fail!("whoops! I'm sure this raised, anyways.."); +/// } +/// // do some stuff with that stream +/// +/// // the file stream will be closed at the end of this block +/// } +/// // .. +/// +/// `FileMode` and `FileAccess` provide information about the permissions +/// context in which a given stream is created. More information about them +/// can be found in `std::rt::io`'s docs. +/// +/// Note that, with this function, a `FileStream` is returned regardless of +/// the access-limitations indicated by `FileAccess` (e.g. calling `write` on a +/// `FileStream` opened as `ReadOnly` will raise an `io_error` condition at runtime). If you +/// desire a more-correctly-constrained interface to files, use the +/// `{open_stream, open_reader, open_writer}` methods that are a part of `FileInfo` +/// +/// # Errors +/// +/// This function will raise an `io_error` condition under a number of different circumstances, +/// to include but not limited to: +/// +/// * Opening a file that already exists with `FileMode` of `Create` or vice versa (e.g. +/// opening a non-existant file with `FileMode` or `Open`) +/// * Attempting to open a file with a `FileAccess` that the user lacks permissions +/// for +/// * Filesystem-level errors (full disk, etc) pub fn open(path: &P, mode: FileMode, access: FileAccess @@ -110,29 +109,28 @@ pub fn open(path: &P, } } -/*! Unlink a file from the underlying filesystem. - -# Example - - use std; - use std::path::Path; - use std::rt::io::support::PathLike; - use std::rt::io::file::unlink; - - let p = &Path("/some/file/path.txt"); - unlink(p); - // if we made it here without failing, then the - // unlink operation was successful - -Note that, just because an unlink call was successful, it is not -guaranteed that a file is immediately deleted (e.g. depending on -platform, other open file descriptors may prevent immediate removal) - -# Errors - -This function will raise an `io_error` condition if the user lacks permissions to -remove the file or if some other filesystem-level error occurs -*/ +/// Unlink a file from the underlying filesystem. +/// +/// # Example +/// +/// use std; +/// use std::path::Path; +/// use std::rt::io::support::PathLike; +/// use std::rt::io::file::unlink; +/// +/// let p = &Path("/some/file/path.txt"); +/// unlink(p); +/// // if we made it here without failing, then the +/// // unlink operation was successful +/// +/// Note that, just because an unlink call was successful, it is not +/// guaranteed that a file is immediately deleted (e.g. depending on +/// platform, other open file descriptors may prevent immediate removal) +/// +/// # Errors +/// +/// This function will raise an `io_error` condition if the user lacks permissions to +/// remove the file or if some other filesystem-level error occurs pub fn unlink(path: &P) { let unlink_result = unsafe { let io: *mut IoFactoryObject = Local::unsafe_borrow(); @@ -146,24 +144,23 @@ pub fn unlink(path: &P) { } } -/*! Create a new, empty directory at the provided path - -# Example - - use std; - use std::path::Path; - use std::rt::io::support::PathLike; - use std::rt::io::file::mkdir; - - let p = &Path("/some/dir"); - mkdir(p); - // If we got here, our directory exists! Horray! - -# Errors - -This call will raise an `io_error` condition if the user lacks permissions to make a -new directory at the provided path, or if the directory already exists -*/ +/// Create a new, empty directory at the provided path +/// +/// # Example +/// +/// use std; +/// use std::path::Path; +/// use std::rt::io::support::PathLike; +/// use std::rt::io::file::mkdir; +/// +/// let p = &Path("/some/dir"); +/// mkdir(p); +/// // If we got here, our directory exists! Horray! +/// +/// # Errors +/// +/// This call will raise an `io_error` condition if the user lacks permissions to make a +/// new directory at the provided path, or if the directory already exists pub fn mkdir(path: &P) { let mkdir_result = unsafe { let io: *mut IoFactoryObject = Local::unsafe_borrow(); @@ -177,24 +174,23 @@ pub fn mkdir(path: &P) { } } -/*! Remove an existing, empty directory - -# Example - - use std; - use std::path::Path; - use std::rt::io::support::PathLike; - use std::rt::io::file::rmdir; - - let p = &Path("/some/dir"); - rmdir(p); - // good riddance, you mean ol' directory - -# Errors - -This call will raise an `io_error` condition if the user lacks permissions to remove the -directory at the provided path, or if the directory isn't empty -*/ +/// Remove an existing, empty directory +/// +/// # Example +/// +/// use std; +/// use std::path::Path; +/// use std::rt::io::support::PathLike; +/// use std::rt::io::file::rmdir; +/// +/// let p = &Path("/some/dir"); +/// rmdir(p); +/// // good riddance, you mean ol' directory +/// +/// # Errors +/// +/// This call will raise an `io_error` condition if the user lacks permissions to remove the +/// directory at the provided path, or if the directory isn't empty pub fn rmdir(path: &P) { let rmdir_result = unsafe { let io: *mut IoFactoryObject = Local::unsafe_borrow(); @@ -208,43 +204,42 @@ pub fn rmdir(path: &P) { } } -/*! Get information on the file, directory, etc at the provided path - -Given a `rt::io::support::PathLike`, query the file system to get -information about a file, directory, etc. - -Returns a `Some(std::rt::io::PathInfo)` on success - -# Example - - use std; - use std::path::Path; - use std::rt::io::support::PathLike; - use std::rt::io::file::stat; - - let p = &Path("/some/file/path.txt"); - - do io_error::cond.trap(|_| { - // hoo-boy... - }).inside { - let info = match stat(p) { - Some(s) => s, - None => fail!("whoops! I'm sure this raised, anyways.."); - } - if stat.is_file { - // just imagine the possibilities ... - } - - // the file stream will be closed at the end of this block - } - // .. - -# Errors - -This call will raise an `io_error` condition if the user lacks the requisite -permissions to perform a `stat` call on the given path or if there is no -entry in the filesystem at the provided path. -*/ +/// Get information on the file, directory, etc at the provided path +/// +/// Given a `rt::io::support::PathLike`, query the file system to get +/// information about a file, directory, etc. +/// +/// Returns a `Some(std::rt::io::PathInfo)` on success +/// +/// # Example +/// +/// use std; +/// use std::path::Path; +/// use std::rt::io::support::PathLike; +/// use std::rt::io::file::stat; +/// +/// let p = &Path("/some/file/path.txt"); +/// +/// do io_error::cond.trap(|_| { +/// // hoo-boy... +/// }).inside { +/// let info = match stat(p) { +/// Some(s) => s, +/// None => fail!("whoops! I'm sure this raised, anyways.."); +/// } +/// if stat.is_file { +/// // just imagine the possibilities ... +/// } +/// +/// // the file stream will be closed at the end of this block +/// } +/// // .. +/// +/// # Errors +/// +/// This call will raise an `io_error` condition if the user lacks the requisite +/// permissions to perform a `stat` call on the given path or if there is no +/// entry in the filesystem at the provided path. pub fn stat(path: &P) -> Option { let open_result = unsafe { let io: *mut IoFactoryObject = Local::unsafe_borrow(); @@ -261,8 +256,31 @@ pub fn stat(path: &P) -> Option { } } -/*! Retrieve a vector containing all entries within a provided directory -*/ +/// Retrieve a vector containing all entries within a provided directory +/// +/// # Example +/// +/// use std; +/// use std::path::Path; +/// use std::rt::io::support::PathLike; +/// use std::rt::io::file::readdir; +/// +/// fn visit_dirs(dir: &Path, cb: &fn(&Path)) { +/// if dir.is_dir() { +/// let contents = dir.readdir(); +/// for entry in contents.iter() { +/// if entry.is_dir() { visit_dirs(entry, cb); } +/// else { cb(entry); } +/// } +/// } +/// else { fail!("nope"); } +/// } +/// +/// # Errors +/// +/// Will raise an `io_error` condition if the provided `path` doesn't exist, +/// the process lacks permissions to view the contents or if the `path` points +/// at a non-directory file pub fn readdir(path: &P) -> Option<~[Path]> { let readdir_result = unsafe { let io: *mut IoFactoryObject = Local::unsafe_borrow(); @@ -279,10 +297,9 @@ pub fn readdir(path: &P) -> Option<~[Path]> { } } -/*! Constrained version of `FileStream` that only exposes read-specific operations. - -Can be retreived via `FileInfo.open_reader()`. -*/ +/// Constrained version of `FileStream` that only exposes read-specific operations. +/// +/// Can be retreived via `FileInfo.open_reader()`. pub struct FileReader { priv stream: FileStream } /// a `std::rt::io::Reader` trait impl for file I/O. @@ -307,10 +324,9 @@ impl Seek for FileReader { } } -/*! Constrained version of `FileStream` that only exposes write-specific operations. - -Can be retreived via `FileInfo.open_writer()`. -*/ +/// Constrained version of `FileStream` that only exposes write-specific operations. +/// +/// Can be retreived via `FileInfo.open_writer()`. pub struct FileWriter { priv stream: FileStream } /// a `std::rt::io::Writer` trait impl for file I/O. @@ -335,19 +351,18 @@ impl Seek for FileWriter { } } -/*! Unconstrained file access type that exposes read and write operations - -Can be retreived via `file::open()` and `FileInfo.open_stream()`. - -# Errors - -This type will raise an io_error condition if operations are attempted against -it for which its underlying file descriptor was not configured at creation -time, via the `FileAccess` parameter to `file::open()`. - -For this reason, it is best to use the access-constrained wrappers that are -exposed via `FileInfo.open_reader()` and `FileInfo.open_writer()`. -*/ +/// Unconstrained file access type that exposes read and write operations +/// +/// Can be retreived via `file::open()` and `FileInfo.open_stream()`. +/// +/// # Errors +/// +/// This type will raise an io_error condition if operations are attempted against +/// it for which its underlying file descriptor was not configured at creation +/// time, via the `FileAccess` parameter to `file::open()`. +/// +/// For this reason, it is best to use the access-constrained wrappers that are +/// exposed via `FileInfo.open_reader()` and `FileInfo.open_writer()`. pub struct FileStream { fd: ~RtioFileStream, last_nread: int, @@ -435,12 +450,11 @@ pub trait FileSystemInfo { /// later creates fn get_path<'a>(&'a self) -> &'a Path; - /*! Get information on the file, directory, etc at the provided path - - Consult the `file::stat` documentation for more info. - - This call preserves identical runtime/error semantics - */ + /// Get information on the file, directory, etc at the provided path + /// + /// Consult the `file::stat` documentation for more info. + /// + /// This call preserves identical runtime/error semantics with `file::stat` fn stat(&self) -> Option { stat(self.get_path()) } @@ -459,37 +473,40 @@ pub trait FileSystemInfo { } -/*! Represents a file, whose underlying path may or may not be valid - -# Example - -* Check if a file exists, reading from it if so - - use std; - use std::path::Path; - use std::rt::io::file::{FileInfo, FileReader}; - - let f = &Path("/some/file/path.txt"); - if f.exists() { - let reader = f.open_reader(Open); - let mut mem = [0u8, 8*64000]; - reader.read(mem); - // ... - } - -* Is the given path a file? - - let f = get_file_path_from_wherever(); - match f.is_file() { - true => doing_something_with_a_file(f), - _ => {} - } -*/ +/// Represents a file, whose underlying path may or may not be valid +/// +/// # Example +/// +/// * Check if a file exists, reading from it if so +/// +/// use std; +/// use std::path::Path; +/// use std::rt::io::file::{FileInfo, FileReader}; +/// +/// let f = &Path("/some/file/path.txt"); +/// if f.exists() { +/// let reader = f.open_reader(Open); +/// let mut mem = [0u8, 8*64000]; +/// reader.read(mem); +/// // ... +/// } +/// +/// * Is the given path a file? +/// +/// let f = get_file_path_from_wherever(); +/// match f.is_file() { +/// true => doing_something_with_a_file(f), +/// _ => {} +/// } pub trait FileInfo : FileSystemInfo { /// Whether the underlying implemention (be it a file path, /// or something else) points at a "regular file" on the FS. Will return /// false for paths to non-existent locations or directories or /// other non-regular files (named pipes, etc). + /// + /// # Errors + /// + /// Will not raise a condition fn is_file(&self) -> bool { match ignore_io_error(|| self.stat()) { Some(s) => s.is_file, @@ -549,39 +566,42 @@ impl FileSystemInfo for Path { /// `FileInfo` implementation for `Path`s impl FileInfo for Path { } -/*! Represents a directory, whose underlying path may or may not be valid - -# Example - -* Check if a directory exists, `mkdir`'ing it if not - - use std; - use std::path::Path; - use std::rt::io::file::{DirectoryInfo}; - - let dir = &Path("/some/dir"); - if !dir.exists() { - dir.mkdir(); - } - -* Is the given path a directory? If so, iterate on its contents - - fn visit_dirs(dir: &Path, cb: &fn(&Path)) { - if dir.is_dir() { - let contents = dir.readdir(); - for entry in contents.iter() { - if entry.is_dir() { visit_dirs(entry, cb); } - else { cb(entry); } - } - } - else { fail!("nope"); } - } -*/ +/// Represents a directory, whose underlying path may or may not be valid +/// +/// # Example +/// +/// * Check if a directory exists, `mkdir`'ing it if not +/// +/// use std; +/// use std::path::Path; +/// use std::rt::io::file::{DirectoryInfo}; +/// +/// let dir = &Path("/some/dir"); +/// if !dir.exists() { +/// dir.mkdir(); +/// } +/// +/// * Is the given path a directory? If so, iterate on its contents +/// +/// fn visit_dirs(dir: &Path, cb: &fn(&Path)) { +/// if dir.is_dir() { +/// let contents = dir.readdir(); +/// for entry in contents.iter() { +/// if entry.is_dir() { visit_dirs(entry, cb); } +/// else { cb(entry); } +/// } +/// } +/// else { fail!("nope"); } +/// } trait DirectoryInfo : FileSystemInfo { /// Whether the underlying implemention (be it a file path, /// or something else) is pointing at a directory in the underlying FS. /// Will return false for paths to non-existent locations or if the item is /// not a directory (eg files, named pipes, links, etc) + /// + /// # Errors + /// + /// Will not raise a condition fn is_dir(&self) -> bool { match ignore_io_error(|| self.stat()) { Some(s) => s.is_dir, From c0ec40f74bd33317fb0af76a68d29bfbaf211b58 Mon Sep 17 00:00:00 2001 From: Jeff Olson Date: Mon, 16 Sep 2013 23:39:33 -0700 Subject: [PATCH 17/21] std: merge conflict cleanup from std::str --- src/libstd/str.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libstd/str.rs b/src/libstd/str.rs index 7cbc88ed204fe..93cac8797bb75 100644 --- a/src/libstd/str.rs +++ b/src/libstd/str.rs @@ -22,7 +22,6 @@ use char; use char::Char; use clone::{Clone, DeepClone}; use container::{Container, Mutable}; -use num::Times; use iter::{Iterator, FromIterator, Extendable, range}; use iter::{Filter, AdditiveIterator, Map}; use iter::{Invert, DoubleEndedIterator, ExactSize}; From 3067ee6373fd956c8659cba51c69a474f8e71219 Mon Sep 17 00:00:00 2001 From: Jeff Olson Date: Tue, 17 Sep 2013 08:01:44 -0700 Subject: [PATCH 18/21] std: remove RtioStream --- src/libstd/rt/rtio.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/libstd/rt/rtio.rs b/src/libstd/rt/rtio.rs index c3ab2bd1cfd4e..d05a3a26169b1 100644 --- a/src/libstd/rt/rtio.rs +++ b/src/libstd/rt/rtio.rs @@ -81,11 +81,6 @@ pub trait IoFactory { Result<~[Path], IoError>; } -pub trait RtioStream { - fn read(&mut self, buf: &mut [u8]) -> Result; - fn write(&mut self, buf: &[u8]) -> Result<(), IoError>; -} - pub trait RtioTcpListener : RtioSocket { fn listen(self) -> Result<~RtioTcpAcceptorObject, IoError>; } From 5d9932fddba27572b0aeb1424b014cd21a5cf28e Mon Sep 17 00:00:00 2001 From: Jeff Olson Date: Tue, 17 Sep 2013 08:40:31 -0700 Subject: [PATCH 19/21] std: fix win32 build error in os::env() --- src/libstd/os.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstd/os.rs b/src/libstd/os.rs index d0a658a46a907..17b71e7b15521 100644 --- a/src/libstd/os.rs +++ b/src/libstd/os.rs @@ -196,7 +196,7 @@ pub fn env() -> ~[(~str,~str)] { if (ch as uint == 0) { fail!("os::env() failure getting env string from OS: %s", os::last_os_error()); } - result = unsafe { str::raw::from_c_multistring(ch as *libc::c_char, None) }; + let result = unsafe { str::raw::from_c_multistring(ch as *libc::c_char, None) }; FreeEnvironmentStringsA(ch); result } From a5275ffd5c3fe22228b23f602ae4e7169df55d82 Mon Sep 17 00:00:00 2001 From: Jeff Olson Date: Tue, 17 Sep 2013 10:14:15 -0700 Subject: [PATCH 20/21] std: whitespace clean up io::file docs --- src/libstd/rt/io/file.rs | 104 +++++++++++++++++++-------------------- 1 file changed, 52 insertions(+), 52 deletions(-) diff --git a/src/libstd/rt/io/file.rs b/src/libstd/rt/io/file.rs index 2a9b71db90bb0..a884961fd1e0d 100644 --- a/src/libstd/rt/io/file.rs +++ b/src/libstd/rt/io/file.rs @@ -45,17 +45,17 @@ use path::Path; use super::super::test::*; /// Open a file for reading/writing, as indicated by `path`. -/// +/// /// # Example -/// +/// /// use std; /// use std::path::Path; /// use std::rt::io::support::PathLike; /// use std::rt::io::file::open; /// use std::rt::io::{FileMode, FileAccess}; -/// +/// /// let p = &Path("/some/file/path.txt"); -/// +/// /// do io_error::cond.trap(|_| { /// // hoo-boy... /// }).inside { @@ -64,26 +64,26 @@ use super::super::test::*; /// None => fail!("whoops! I'm sure this raised, anyways.."); /// } /// // do some stuff with that stream -/// +/// /// // the file stream will be closed at the end of this block /// } /// // .. -/// +/// /// `FileMode` and `FileAccess` provide information about the permissions /// context in which a given stream is created. More information about them /// can be found in `std::rt::io`'s docs. -/// +/// /// Note that, with this function, a `FileStream` is returned regardless of /// the access-limitations indicated by `FileAccess` (e.g. calling `write` on a /// `FileStream` opened as `ReadOnly` will raise an `io_error` condition at runtime). If you /// desire a more-correctly-constrained interface to files, use the /// `{open_stream, open_reader, open_writer}` methods that are a part of `FileInfo` -/// +/// /// # Errors -/// +/// /// This function will raise an `io_error` condition under a number of different circumstances, /// to include but not limited to: -/// +/// /// * Opening a file that already exists with `FileMode` of `Create` or vice versa (e.g. /// opening a non-existant file with `FileMode` or `Open`) /// * Attempting to open a file with a `FileAccess` that the user lacks permissions @@ -110,25 +110,25 @@ pub fn open(path: &P, } /// Unlink a file from the underlying filesystem. -/// +/// /// # Example -/// +/// /// use std; /// use std::path::Path; /// use std::rt::io::support::PathLike; /// use std::rt::io::file::unlink; -/// +/// /// let p = &Path("/some/file/path.txt"); /// unlink(p); /// // if we made it here without failing, then the /// // unlink operation was successful -/// +/// /// Note that, just because an unlink call was successful, it is not /// guaranteed that a file is immediately deleted (e.g. depending on /// platform, other open file descriptors may prevent immediate removal) -/// +/// /// # Errors -/// +/// /// This function will raise an `io_error` condition if the user lacks permissions to /// remove the file or if some other filesystem-level error occurs pub fn unlink(path: &P) { @@ -145,20 +145,20 @@ pub fn unlink(path: &P) { } /// Create a new, empty directory at the provided path -/// +/// /// # Example -/// +/// /// use std; /// use std::path::Path; /// use std::rt::io::support::PathLike; /// use std::rt::io::file::mkdir; -/// +/// /// let p = &Path("/some/dir"); /// mkdir(p); /// // If we got here, our directory exists! Horray! -/// +/// /// # Errors -/// +/// /// This call will raise an `io_error` condition if the user lacks permissions to make a /// new directory at the provided path, or if the directory already exists pub fn mkdir(path: &P) { @@ -175,20 +175,20 @@ pub fn mkdir(path: &P) { } /// Remove an existing, empty directory -/// +/// /// # Example -/// +/// /// use std; /// use std::path::Path; /// use std::rt::io::support::PathLike; /// use std::rt::io::file::rmdir; -/// +/// /// let p = &Path("/some/dir"); /// rmdir(p); /// // good riddance, you mean ol' directory -/// +/// /// # Errors -/// +/// /// This call will raise an `io_error` condition if the user lacks permissions to remove the /// directory at the provided path, or if the directory isn't empty pub fn rmdir(path: &P) { @@ -205,21 +205,21 @@ pub fn rmdir(path: &P) { } /// Get information on the file, directory, etc at the provided path -/// +/// /// Given a `rt::io::support::PathLike`, query the file system to get /// information about a file, directory, etc. -/// +/// /// Returns a `Some(std::rt::io::PathInfo)` on success -/// +/// /// # Example -/// +/// /// use std; /// use std::path::Path; /// use std::rt::io::support::PathLike; /// use std::rt::io::file::stat; -/// +/// /// let p = &Path("/some/file/path.txt"); -/// +/// /// do io_error::cond.trap(|_| { /// // hoo-boy... /// }).inside { @@ -230,13 +230,13 @@ pub fn rmdir(path: &P) { /// if stat.is_file { /// // just imagine the possibilities ... /// } -/// +/// /// // the file stream will be closed at the end of this block /// } /// // .. -/// +/// /// # Errors -/// +/// /// This call will raise an `io_error` condition if the user lacks the requisite /// permissions to perform a `stat` call on the given path or if there is no /// entry in the filesystem at the provided path. @@ -325,7 +325,7 @@ impl Seek for FileReader { } /// Constrained version of `FileStream` that only exposes write-specific operations. -/// +/// /// Can be retreived via `FileInfo.open_writer()`. pub struct FileWriter { priv stream: FileStream } @@ -352,15 +352,15 @@ impl Seek for FileWriter { } /// Unconstrained file access type that exposes read and write operations -/// +/// /// Can be retreived via `file::open()` and `FileInfo.open_stream()`. -/// +/// /// # Errors -/// +/// /// This type will raise an io_error condition if operations are attempted against /// it for which its underlying file descriptor was not configured at creation /// time, via the `FileAccess` parameter to `file::open()`. -/// +/// /// For this reason, it is best to use the access-constrained wrappers that are /// exposed via `FileInfo.open_reader()` and `FileInfo.open_writer()`. pub struct FileStream { @@ -474,15 +474,15 @@ pub trait FileSystemInfo { } /// Represents a file, whose underlying path may or may not be valid -/// +/// /// # Example -/// +/// /// * Check if a file exists, reading from it if so -/// +/// /// use std; /// use std::path::Path; /// use std::rt::io::file::{FileInfo, FileReader}; -/// +/// /// let f = &Path("/some/file/path.txt"); /// if f.exists() { /// let reader = f.open_reader(Open); @@ -490,9 +490,9 @@ pub trait FileSystemInfo { /// reader.read(mem); /// // ... /// } -/// +/// /// * Is the given path a file? -/// +/// /// let f = get_file_path_from_wherever(); /// match f.is_file() { /// true => doing_something_with_a_file(f), @@ -567,22 +567,22 @@ impl FileSystemInfo for Path { impl FileInfo for Path { } /// Represents a directory, whose underlying path may or may not be valid -/// +/// /// # Example -/// +/// /// * Check if a directory exists, `mkdir`'ing it if not -/// +/// /// use std; /// use std::path::Path; /// use std::rt::io::file::{DirectoryInfo}; -/// +/// /// let dir = &Path("/some/dir"); /// if !dir.exists() { /// dir.mkdir(); /// } -/// +/// /// * Is the given path a directory? If so, iterate on its contents -/// +/// /// fn visit_dirs(dir: &Path, cb: &fn(&Path)) { /// if dir.is_dir() { /// let contents = dir.readdir(); From 70152ff55722878cde684ee6462c14c65f2c4729 Mon Sep 17 00:00:00 2001 From: Jeff Olson Date: Tue, 17 Sep 2013 11:15:33 -0700 Subject: [PATCH 21/21] std: fix win32 build issue re: multistring parsing --- src/libstd/os.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstd/os.rs b/src/libstd/os.rs index 17b71e7b15521..09ac2b368e76d 100644 --- a/src/libstd/os.rs +++ b/src/libstd/os.rs @@ -196,7 +196,7 @@ pub fn env() -> ~[(~str,~str)] { if (ch as uint == 0) { fail!("os::env() failure getting env string from OS: %s", os::last_os_error()); } - let result = unsafe { str::raw::from_c_multistring(ch as *libc::c_char, None) }; + let result = str::raw::from_c_multistring(ch as *libc::c_char, None); FreeEnvironmentStringsA(ch); result }