2013-04-17 17:55:21 -07:00
|
|
|
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
|
|
|
|
// file at the top-level directory of this distribution and at
|
|
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
|
|
|
|
|
|
|
//! Blocking posix-based file I/O
|
|
|
|
|
2013-10-06 13:21:29 -07:00
|
|
|
#[allow(non_camel_case_types)];
|
|
|
|
|
|
|
|
use libc;
|
|
|
|
use os;
|
2013-04-17 17:55:21 -07:00
|
|
|
use prelude::*;
|
|
|
|
use super::super::*;
|
|
|
|
|
2013-10-17 21:08:48 -07:00
|
|
|
#[cfg(windows)]
|
|
|
|
fn get_err(errno: i32) -> (IoErrorKind, &'static str) {
|
|
|
|
match errno {
|
|
|
|
libc::EOF => (EndOfFile, "end of file"),
|
|
|
|
_ => (OtherIoError, "unknown error"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(not(windows))]
|
|
|
|
fn get_err(errno: i32) -> (IoErrorKind, &'static str) {
|
2013-10-06 13:21:29 -07:00
|
|
|
// XXX: this should probably be a bit more descriptive...
|
2013-10-17 21:08:48 -07:00
|
|
|
match errno {
|
2013-10-06 13:21:29 -07:00
|
|
|
libc::EOF => (EndOfFile, "end of file"),
|
2013-10-17 17:04:51 -07:00
|
|
|
|
|
|
|
// These two constants can have the same value on some systems, but
|
|
|
|
// different values on others, so we can't use a match clause
|
|
|
|
x if x == libc::EAGAIN || x == libc::EWOULDBLOCK =>
|
|
|
|
(ResourceUnavailable, "resource temporarily unavailable"),
|
|
|
|
|
2013-10-06 13:21:29 -07:00
|
|
|
_ => (OtherIoError, "unknown error"),
|
2013-10-17 21:08:48 -07:00
|
|
|
}
|
|
|
|
}
|
2013-10-06 13:21:29 -07:00
|
|
|
|
2013-10-17 21:08:48 -07:00
|
|
|
fn raise_error() {
|
|
|
|
let (kind, desc) = get_err(os::errno() as i32);
|
2013-10-06 13:21:29 -07:00
|
|
|
io_error::cond.raise(IoError {
|
|
|
|
kind: kind,
|
|
|
|
desc: desc,
|
|
|
|
detail: Some(os::last_os_error())
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
fn keep_going(data: &[u8], f: &fn(*u8, uint) -> i64) -> i64 {
|
|
|
|
#[cfg(windows)] static eintr: int = 0; // doesn't matter
|
|
|
|
#[cfg(not(windows))] static eintr: int = libc::EINTR as int;
|
|
|
|
|
|
|
|
let (data, origamt) = do data.as_imm_buf |data, amt| { (data, amt) };
|
|
|
|
let mut data = data;
|
|
|
|
let mut amt = origamt;
|
|
|
|
while amt > 0 {
|
|
|
|
let mut ret;
|
|
|
|
loop {
|
|
|
|
ret = f(data, amt);
|
|
|
|
if cfg!(not(windows)) { break } // windows has no eintr
|
|
|
|
// if we get an eintr, then try again
|
|
|
|
if ret != -1 || os::errno() as int != eintr { break }
|
|
|
|
}
|
|
|
|
if ret == 0 {
|
|
|
|
break
|
|
|
|
} else if ret != -1 {
|
|
|
|
amt -= ret as uint;
|
|
|
|
data = unsafe { data.offset(ret as int) };
|
|
|
|
} else {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return (origamt - amt) as i64;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub type fd_t = libc::c_int;
|
2013-04-17 17:55:21 -07:00
|
|
|
|
2013-10-02 18:52:03 -07:00
|
|
|
pub struct FileDesc {
|
2013-10-06 13:21:29 -07:00
|
|
|
priv fd: fd_t,
|
2013-10-02 18:52:03 -07:00
|
|
|
}
|
2013-04-17 17:55:21 -07:00
|
|
|
|
|
|
|
impl FileDesc {
|
|
|
|
/// Create a `FileDesc` from an open C file descriptor.
|
|
|
|
///
|
2013-10-06 13:21:29 -07:00
|
|
|
/// The `FileDesc` will take ownership of the specified file descriptor and
|
|
|
|
/// close it upon destruction.
|
|
|
|
///
|
|
|
|
/// Note that all I/O operations done on this object will be *blocking*, but
|
|
|
|
/// they do not require the runtime to be active.
|
|
|
|
pub fn new(fd: fd_t) -> FileDesc {
|
|
|
|
FileDesc { fd: fd }
|
|
|
|
}
|
2013-04-17 17:55:21 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Reader for FileDesc {
|
2013-10-06 13:21:29 -07:00
|
|
|
#[fixed_stack_segment] #[inline(never)]
|
|
|
|
fn read(&mut self, buf: &mut [u8]) -> Option<uint> {
|
|
|
|
#[cfg(windows)] type rlen = libc::c_uint;
|
|
|
|
#[cfg(not(windows))] type rlen = libc::size_t;
|
|
|
|
let ret = do keep_going(buf) |buf, len| {
|
|
|
|
unsafe {
|
|
|
|
libc::read(self.fd, buf as *mut libc::c_void, len as rlen) as i64
|
|
|
|
}
|
|
|
|
};
|
|
|
|
if ret == 0 {
|
|
|
|
None
|
|
|
|
} else if ret < 0 {
|
|
|
|
raise_error();
|
|
|
|
None
|
|
|
|
} else {
|
|
|
|
Some(ret as uint)
|
|
|
|
}
|
|
|
|
}
|
2013-04-17 17:55:21 -07:00
|
|
|
|
2013-10-06 13:21:29 -07:00
|
|
|
fn eof(&mut self) -> bool { false }
|
2013-04-17 17:55:21 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Writer for FileDesc {
|
2013-10-06 13:21:29 -07:00
|
|
|
#[fixed_stack_segment] #[inline(never)]
|
|
|
|
fn write(&mut self, buf: &[u8]) {
|
|
|
|
#[cfg(windows)] type wlen = libc::c_uint;
|
|
|
|
#[cfg(not(windows))] type wlen = libc::size_t;
|
|
|
|
let ret = do keep_going(buf) |buf, len| {
|
|
|
|
unsafe {
|
|
|
|
libc::write(self.fd, buf as *libc::c_void, len as wlen) as i64
|
|
|
|
}
|
|
|
|
};
|
|
|
|
if ret < 0 {
|
|
|
|
raise_error();
|
|
|
|
}
|
|
|
|
}
|
2013-04-17 17:55:21 -07:00
|
|
|
}
|
|
|
|
|
2013-10-06 13:21:29 -07:00
|
|
|
impl Drop for FileDesc {
|
|
|
|
#[fixed_stack_segment] #[inline(never)]
|
|
|
|
fn drop(&mut self) {
|
|
|
|
unsafe { libc::close(self.fd); }
|
|
|
|
}
|
2013-04-17 17:55:21 -07:00
|
|
|
}
|
|
|
|
|
2013-10-02 18:52:03 -07:00
|
|
|
pub struct CFile {
|
2013-10-06 13:21:29 -07:00
|
|
|
priv file: *libc::FILE
|
2013-10-02 18:52:03 -07:00
|
|
|
}
|
2013-04-17 17:55:21 -07:00
|
|
|
|
|
|
|
impl CFile {
|
|
|
|
/// Create a `CFile` from an open `FILE` pointer.
|
|
|
|
///
|
2013-10-06 13:21:29 -07:00
|
|
|
/// The `CFile` takes ownership of the `FILE` pointer and will close it upon
|
|
|
|
/// destruction.
|
|
|
|
pub fn new(file: *libc::FILE) -> CFile { CFile { file: file } }
|
2013-04-17 17:55:21 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Reader for CFile {
|
2013-10-06 13:21:29 -07:00
|
|
|
#[fixed_stack_segment] #[inline(never)]
|
|
|
|
fn read(&mut self, buf: &mut [u8]) -> Option<uint> {
|
|
|
|
let ret = do keep_going(buf) |buf, len| {
|
|
|
|
unsafe {
|
|
|
|
libc::fread(buf as *mut libc::c_void, 1, len as libc::size_t,
|
|
|
|
self.file) as i64
|
|
|
|
}
|
|
|
|
};
|
|
|
|
if ret == 0 {
|
|
|
|
None
|
|
|
|
} else if ret < 0 {
|
|
|
|
raise_error();
|
|
|
|
None
|
|
|
|
} else {
|
|
|
|
Some(ret as uint)
|
|
|
|
}
|
|
|
|
}
|
2013-04-17 17:55:21 -07:00
|
|
|
|
2013-10-06 13:21:29 -07:00
|
|
|
#[fixed_stack_segment] #[inline(never)]
|
|
|
|
fn eof(&mut self) -> bool {
|
|
|
|
unsafe { libc::feof(self.file) != 0 }
|
|
|
|
}
|
2013-04-17 17:55:21 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Writer for CFile {
|
2013-10-06 13:21:29 -07:00
|
|
|
#[fixed_stack_segment] #[inline(never)]
|
|
|
|
fn write(&mut self, buf: &[u8]) {
|
|
|
|
let ret = do keep_going(buf) |buf, len| {
|
|
|
|
unsafe {
|
|
|
|
libc::fwrite(buf as *libc::c_void, 1, len as libc::size_t,
|
|
|
|
self.file) as i64
|
|
|
|
}
|
|
|
|
};
|
|
|
|
if ret < 0 {
|
|
|
|
raise_error();
|
|
|
|
}
|
|
|
|
}
|
2013-04-17 17:55:21 -07:00
|
|
|
|
2013-10-06 13:21:29 -07:00
|
|
|
#[fixed_stack_segment] #[inline(never)]
|
|
|
|
fn flush(&mut self) {
|
|
|
|
if unsafe { libc::fflush(self.file) } < 0 {
|
|
|
|
raise_error();
|
|
|
|
}
|
|
|
|
}
|
2013-04-17 17:55:21 -07:00
|
|
|
}
|
|
|
|
|
2013-04-19 14:58:21 -07:00
|
|
|
impl Seek for CFile {
|
2013-10-06 13:21:29 -07:00
|
|
|
#[fixed_stack_segment] #[inline(never)]
|
|
|
|
fn tell(&self) -> u64 {
|
|
|
|
let ret = unsafe { libc::ftell(self.file) };
|
|
|
|
if ret < 0 {
|
|
|
|
raise_error();
|
|
|
|
}
|
|
|
|
return ret as u64;
|
|
|
|
}
|
|
|
|
|
|
|
|
#[fixed_stack_segment] #[inline(never)]
|
|
|
|
fn seek(&mut self, pos: i64, style: SeekStyle) {
|
|
|
|
let whence = match style {
|
|
|
|
SeekSet => libc::SEEK_SET,
|
|
|
|
SeekEnd => libc::SEEK_END,
|
|
|
|
SeekCur => libc::SEEK_CUR,
|
|
|
|
};
|
|
|
|
if unsafe { libc::fseek(self.file, pos as libc::c_long, whence) } < 0 {
|
|
|
|
raise_error();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Drop for CFile {
|
|
|
|
#[fixed_stack_segment] #[inline(never)]
|
|
|
|
fn drop(&mut self) {
|
|
|
|
unsafe { libc::fclose(self.file); }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use libc;
|
|
|
|
use os;
|
|
|
|
use prelude::*;
|
|
|
|
use rt::io::{io_error, SeekSet};
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test] #[fixed_stack_segment]
|
2013-10-07 13:25:06 -07:00
|
|
|
#[ignore(cfg(target_os = "freebsd"))] // hmm, maybe pipes have a tiny buffer
|
2013-10-06 13:21:29 -07:00
|
|
|
fn test_file_desc() {
|
|
|
|
// Run this test with some pipes so we don't have to mess around with
|
|
|
|
// opening or closing files.
|
|
|
|
unsafe {
|
|
|
|
let os::Pipe { input, out } = os::pipe();
|
|
|
|
let mut reader = FileDesc::new(input);
|
|
|
|
let mut writer = FileDesc::new(out);
|
|
|
|
|
|
|
|
writer.write(bytes!("test"));
|
|
|
|
let mut buf = [0u8, ..4];
|
|
|
|
match reader.read(buf) {
|
|
|
|
Some(4) => {
|
|
|
|
assert_eq!(buf[0], 't' as u8);
|
|
|
|
assert_eq!(buf[1], 'e' as u8);
|
|
|
|
assert_eq!(buf[2], 's' as u8);
|
|
|
|
assert_eq!(buf[3], 't' as u8);
|
|
|
|
}
|
2013-10-21 13:08:31 -07:00
|
|
|
r => fail!("invalid read: {:?}", r)
|
2013-10-06 13:21:29 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
let mut raised = false;
|
|
|
|
do io_error::cond.trap(|_| { raised = true; }).inside {
|
|
|
|
writer.read(buf);
|
|
|
|
}
|
|
|
|
assert!(raised);
|
|
|
|
|
|
|
|
raised = false;
|
|
|
|
do io_error::cond.trap(|_| { raised = true; }).inside {
|
|
|
|
reader.write(buf);
|
|
|
|
}
|
|
|
|
assert!(raised);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test] #[fixed_stack_segment]
|
2013-10-07 13:25:06 -07:00
|
|
|
#[ignore(cfg(windows))] // apparently windows doesn't like tmpfile
|
2013-10-06 13:21:29 -07:00
|
|
|
fn test_cfile() {
|
|
|
|
unsafe {
|
|
|
|
let f = libc::tmpfile();
|
|
|
|
assert!(!f.is_null());
|
|
|
|
let mut file = CFile::new(f);
|
|
|
|
|
|
|
|
file.write(bytes!("test"));
|
|
|
|
let mut buf = [0u8, ..4];
|
|
|
|
file.seek(0, SeekSet);
|
|
|
|
match file.read(buf) {
|
|
|
|
Some(4) => {
|
|
|
|
assert_eq!(buf[0], 't' as u8);
|
|
|
|
assert_eq!(buf[1], 'e' as u8);
|
|
|
|
assert_eq!(buf[2], 's' as u8);
|
|
|
|
assert_eq!(buf[3], 't' as u8);
|
|
|
|
}
|
2013-10-21 13:08:31 -07:00
|
|
|
r => fail!("invalid read: {:?}", r)
|
2013-10-06 13:21:29 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-04-17 17:55:21 -07:00
|
|
|
}
|