From b509f7905a578663d57121973b4a9a6b619341c2 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Sun, 6 Oct 2013 13:21:29 -0700 Subject: Implement io::native::file --- src/libstd/rt/io/native/file.rs | 255 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 230 insertions(+), 25 deletions(-) (limited to 'src/libstd/rt') diff --git a/src/libstd/rt/io/native/file.rs b/src/libstd/rt/io/native/file.rs index 47ae89ccf9f..f0dd63a3224 100644 --- a/src/libstd/rt/io/native/file.rs +++ b/src/libstd/rt/io/native/file.rs @@ -10,68 +10,273 @@ //! Blocking posix-based file I/O +#[allow(non_camel_case_types)]; + +use libc; +use os; use prelude::*; use super::super::*; -use libc::{c_int, FILE}; -#[allow(non_camel_case_types)] -pub type fd_t = c_int; +fn raise_error() { + // XXX: this should probably be a bit more descriptive... + let (kind, desc) = match os::errno() as i32 { + libc::EOF => (EndOfFile, "end of file"), + _ => (OtherIoError, "unknown error"), + }; + + 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; pub struct FileDesc { - priv fd: fd_t + priv fd: fd_t, } impl FileDesc { /// Create a `FileDesc` from an open C file descriptor. /// - /// The `FileDesc` takes ownership of the file descriptor - /// and will close it upon destruction. - pub fn new(_fd: fd_t) -> FileDesc { fail2!() } + /// 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 } + } } impl Reader for FileDesc { - fn read(&mut self, _buf: &mut [u8]) -> Option { fail2!() } + #[fixed_stack_segment] #[inline(never)] + fn read(&mut self, buf: &mut [u8]) -> Option { + #[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) + } + } - fn eof(&mut self) -> bool { fail2!() } + fn eof(&mut self) -> bool { false } } impl Writer for FileDesc { - fn write(&mut self, _buf: &[u8]) { fail2!() } + #[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(); + } + } - fn flush(&mut self) { fail2!() } + fn flush(&mut self) {} } -impl Seek for FileDesc { - fn tell(&self) -> u64 { fail2!() } - - fn seek(&mut self, _pos: i64, _style: SeekStyle) { fail2!() } +impl Drop for FileDesc { + #[fixed_stack_segment] #[inline(never)] + fn drop(&mut self) { + unsafe { libc::close(self.fd); } + } } pub struct CFile { - priv file: *FILE + priv file: *libc::FILE } impl CFile { /// Create a `CFile` from an open `FILE` pointer. /// - /// The `CFile` takes ownership of the file descriptor - /// and will close it upon destruction. - pub fn new(_file: *FILE) -> CFile { fail2!() } + /// The `CFile` takes ownership of the `FILE` pointer and will close it upon + /// destruction. + pub fn new(file: *libc::FILE) -> CFile { CFile { file: file } } } impl Reader for CFile { - fn read(&mut self, _buf: &mut [u8]) -> Option { fail2!() } + #[fixed_stack_segment] #[inline(never)] + fn read(&mut self, buf: &mut [u8]) -> Option { + 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) + } + } - fn eof(&mut self) -> bool { fail2!() } + #[fixed_stack_segment] #[inline(never)] + fn eof(&mut self) -> bool { + unsafe { libc::feof(self.file) != 0 } + } } impl Writer for CFile { - fn write(&mut self, _buf: &[u8]) { fail2!() } + #[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(); + } + } - fn flush(&mut self) { fail2!() } + #[fixed_stack_segment] #[inline(never)] + fn flush(&mut self) { + if unsafe { libc::fflush(self.file) } < 0 { + raise_error(); + } + } } impl Seek for CFile { - fn tell(&self) -> u64 { fail2!() } - fn seek(&mut self, _pos: i64, _style: SeekStyle) { fail2!() } + #[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] + 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); + } + r => fail2!("invalid read: {:?}", r) + } + + 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] + #[ignore(windows)] // apparently windows doesn't like tmpfile + 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); + } + r => fail2!("invalid read: {:?}", r) + } + } + } } -- cgit 1.4.1-3-g733a5 From edf4c16997af6af08ef734ad34eccc5aa279595a Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Sun, 6 Oct 2013 13:21:42 -0700 Subject: Implement io::native::stdio --- src/libstd/rt/io/native/stdio.rs | 67 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 src/libstd/rt/io/native/stdio.rs (limited to 'src/libstd/rt') diff --git a/src/libstd/rt/io/native/stdio.rs b/src/libstd/rt/io/native/stdio.rs new file mode 100644 index 00000000000..5661725d77b --- /dev/null +++ b/src/libstd/rt/io/native/stdio.rs @@ -0,0 +1,67 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use libc; +use option::Option; +use rt::io::{Reader, Writer}; +use super::file; + +/// Creates a new handle to the stdin of this process +pub fn stdin() -> StdIn { StdIn::new() } +/// Creates a new handle to the stdout of this process +pub fn stdout() -> StdOut { StdOut::new(libc::STDOUT_FILENO) } +/// Creates a new handle to the stderr of this process +pub fn stderr() -> StdOut { StdOut::new(libc::STDERR_FILENO) } + +pub fn print(s: &str) { + stdout().write(s.as_bytes()) +} + +pub fn println(s: &str) { + let mut out = stdout(); + out.write(s.as_bytes()); + out.write(['\n' as u8]); +} + +pub struct StdIn { + priv fd: file::FileDesc +} + +impl StdIn { + /// Duplicates the stdin file descriptor, returning an io::Reader + #[fixed_stack_segment] #[inline(never)] + pub fn new() -> StdIn { + let fd = unsafe { libc::dup(libc::STDIN_FILENO) }; + StdIn { fd: file::FileDesc::new(fd) } + } +} + +impl Reader for StdIn { + fn read(&mut self, buf: &mut [u8]) -> Option { self.fd.read(buf) } + fn eof(&mut self) -> bool { self.fd.eof() } +} + +pub struct StdOut { + priv fd: file::FileDesc +} + +impl StdOut { + /// Duplicates the specified file descriptor, returning an io::Writer + #[fixed_stack_segment] #[inline(never)] + pub fn new(fd: file::fd_t) -> StdOut { + let fd = unsafe { libc::dup(fd) }; + StdOut { fd: file::FileDesc::new(fd) } + } +} + +impl Writer for StdOut { + fn write(&mut self, buf: &[u8]) { self.fd.write(buf) } + fn flush(&mut self) { self.fd.flush() } +} -- cgit 1.4.1-3-g733a5 From a0d2f71e8e64d7994f20932caedab1b8dccc5539 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Sun, 6 Oct 2013 13:22:18 -0700 Subject: Implement io::native::process --- src/compiletest/procsrv.rs | 2 +- src/libstd/rt/io/native/process.rs | 745 +++++++++++++++++++++++++++++++++++++ src/libstd/rt/io/process.rs | 7 + 3 files changed, 753 insertions(+), 1 deletion(-) create mode 100644 src/libstd/rt/io/native/process.rs (limited to 'src/libstd/rt') diff --git a/src/compiletest/procsrv.rs b/src/compiletest/procsrv.rs index ef8049aa717..829916117d2 100644 --- a/src/compiletest/procsrv.rs +++ b/src/compiletest/procsrv.rs @@ -57,7 +57,7 @@ pub fn run(lib_path: &str, }); for input in input.iter() { - proc.input().write_str(*input); + proc.input().write(input.as_bytes()); } let output = proc.finish_with_output(); diff --git a/src/libstd/rt/io/native/process.rs b/src/libstd/rt/io/native/process.rs new file mode 100644 index 00000000000..d338192c664 --- /dev/null +++ b/src/libstd/rt/io/native/process.rs @@ -0,0 +1,745 @@ +// Copyright 2012-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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use cast; +use libc::{pid_t, c_void, c_int}; +use libc; +use os; +use prelude::*; +use ptr; +use rt::io; +use super::file; + +/** + * A value representing a child process. + * + * The lifetime of this value is linked to the lifetime of the actual + * process - the Process destructor calls self.finish() which waits + * for the process to terminate. + */ +pub struct Process { + /// The unique id of the process (this should never be negative). + priv pid: pid_t, + + /// A handle to the process - on unix this will always be NULL, but on + /// windows it will be a HANDLE to the process, which will prevent the + /// pid being re-used until the handle is closed. + priv handle: *(), + + /// Currently known stdin of the child, if any + priv input: Option, + /// Currently known stdout of the child, if any + priv output: Option, + /// Currently known stderr of the child, if any + priv error: Option, + + /// None until finish() is called. + priv exit_code: Option, +} + +impl Process { + /// Creates a new process using native process-spawning abilities provided + /// by the OS. Operations on this process will be blocking instead of using + /// the runtime for sleeping just this current task. + /// + /// # Arguments + /// + /// * prog - the program to run + /// * args - the arguments to pass to the program, not including the program + /// itself + /// * env - an optional envrionment to specify for the child process. If + /// this value is `None`, then the child will inherit the parent's + /// environment + /// * cwd - an optionally specified current working directory of the child, + /// defaulting to the parent's current working directory + /// * stdin, stdout, stderr - These optionally specified file descriptors + /// dictate where the stdin/out/err of the child process will go. If + /// these are `None`, then this module will bind the input/output to an + /// os pipe instead. This process takes ownership of these file + /// descriptors, closing them upon destruction of the process. + pub fn new(prog: &str, args: &[~str], env: Option<~[(~str, ~str)]>, + cwd: Option<&Path>, + stdin: Option, + stdout: Option, + stderr: Option) -> Process { + #[fixed_stack_segment]; #[inline(never)]; + + let (in_pipe, in_fd) = match stdin { + None => { + let pipe = os::pipe(); + (Some(pipe), pipe.input) + }, + Some(fd) => (None, fd) + }; + let (out_pipe, out_fd) = match stdout { + None => { + let pipe = os::pipe(); + (Some(pipe), pipe.out) + }, + Some(fd) => (None, fd) + }; + let (err_pipe, err_fd) = match stderr { + None => { + let pipe = os::pipe(); + (Some(pipe), pipe.out) + }, + Some(fd) => (None, fd) + }; + + let res = spawn_process_os(prog, args, env, cwd, + in_fd, out_fd, err_fd); + + unsafe { + for pipe in in_pipe.iter() { libc::close(pipe.input); } + for pipe in out_pipe.iter() { libc::close(pipe.out); } + for pipe in err_pipe.iter() { libc::close(pipe.out); } + } + + Process { + pid: res.pid, + handle: res.handle, + input: in_pipe.map(|pipe| file::FileDesc::new(pipe.out)), + output: out_pipe.map(|pipe| file::FileDesc::new(pipe.input)), + error: err_pipe.map(|pipe| file::FileDesc::new(pipe.input)), + exit_code: None, + } + } + + /// Returns the unique id of the process + pub fn id(&self) -> pid_t { self.pid } + + /** + * Returns an io::Writer that can be used to write to this Process's stdin. + * + * Fails if there is no stdinavailable (it's already been removed by + * take_input) + */ + pub fn input<'a>(&'a mut self) -> &'a mut io::Writer { + match self.input { + Some(ref mut fd) => fd as &mut io::Writer, + None => fail2!("This process has no stdin") + } + } + + /** + * Returns an io::Reader that can be used to read from this Process's + * stdout. + * + * Fails if there is no stdin available (it's already been removed by + * take_output) + */ + pub fn output<'a>(&'a mut self) -> &'a mut io::Reader { + match self.input { + Some(ref mut fd) => fd as &mut io::Reader, + None => fail2!("This process has no stdout") + } + } + + /** + * Returns an io::Reader that can be used to read from this Process's + * stderr. + * + * Fails if there is no stdin available (it's already been removed by + * take_error) + */ + pub fn error<'a>(&'a mut self) -> &'a mut io::Reader { + match self.error { + Some(ref mut fd) => fd as &mut io::Reader, + None => fail2!("This process has no stderr") + } + } + + /** + * Takes the stdin of this process, transferring ownership to the caller. + * Note that when the return value is destroyed, the handle will be closed + * for the child process. + */ + pub fn take_input(&mut self) -> Option<~io::Writer> { + self.input.take().map(|fd| ~fd as ~io::Writer) + } + + /** + * Takes the stdout of this process, transferring ownership to the caller. + * Note that when the return value is destroyed, the handle will be closed + * for the child process. + */ + pub fn take_output(&mut self) -> Option<~io::Reader> { + self.output.take().map(|fd| ~fd as ~io::Reader) + } + + /** + * Takes the stderr of this process, transferring ownership to the caller. + * Note that when the return value is destroyed, the handle will be closed + * for the child process. + */ + pub fn take_error(&mut self) -> Option<~io::Reader> { + self.error.take().map(|fd| ~fd as ~io::Reader) + } + + pub fn wait(&mut self) -> int { + for &code in self.exit_code.iter() { + return code; + } + let code = waitpid(self.pid); + self.exit_code = Some(code); + return code; + } + + pub fn signal(&mut self, signum: int) -> Result<(), io::IoError> { + // if the process has finished, and therefore had waitpid called, + // and we kill it, then on unix we might ending up killing a + // newer process that happens to have the same (re-used) id + match self.exit_code { + Some(*) => return Err(io::IoError { + kind: io::OtherIoError, + desc: "can't kill an exited process", + detail: None, + }), + None => {} + } + return unsafe { killpid(self.pid, signum) }; + + #[cfg(windows)] + unsafe fn killpid(pid: pid_t, signal: int) -> Result<(), io::IoError> { + #[fixed_stack_segment]; #[inline(never)]; + match signal { + io::process::PleaseExitSignal | + io::process::MustDieSignal => { + libc::funcs::extra::kernel32::TerminateProcess( + cast::transmute(pid), 1); + Ok(()) + } + _ => Err(io::IoError { + kind: io::OtherIoError, + desc: "unsupported signal on windows", + detail: None, + }) + } + } + + #[cfg(not(windows))] + unsafe fn killpid(pid: pid_t, signal: int) -> Result<(), io::IoError> { + #[fixed_stack_segment]; #[inline(never)]; + libc::funcs::posix88::signal::kill(pid, signal as c_int); + Ok(()) + } + } +} + +impl Drop for Process { + fn drop(&mut self) { + // close all these handles + self.take_input(); + self.take_output(); + self.take_error(); + self.wait(); + free_handle(self.handle); + } +} + +struct SpawnProcessResult { + pid: pid_t, + handle: *(), +} + +#[cfg(windows)] +fn spawn_process_os(prog: &str, args: &[~str], + env: Option<~[(~str, ~str)]>, + dir: Option<&Path>, + in_fd: c_int, out_fd: c_int, err_fd: c_int) -> SpawnProcessResult { + #[fixed_stack_segment]; #[inline(never)]; + + use libc::types::os::arch::extra::{DWORD, HANDLE, STARTUPINFO}; + use libc::consts::os::extra::{ + TRUE, FALSE, + STARTF_USESTDHANDLES, + INVALID_HANDLE_VALUE, + DUPLICATE_SAME_ACCESS + }; + use libc::funcs::extra::kernel32::{ + GetCurrentProcess, + DuplicateHandle, + CloseHandle, + CreateProcessA + }; + use libc::funcs::extra::msvcrt::get_osfhandle; + + use sys; + + unsafe { + + let mut si = zeroed_startupinfo(); + si.cb = sys::size_of::() as DWORD; + si.dwFlags = STARTF_USESTDHANDLES; + + let cur_proc = GetCurrentProcess(); + + let orig_std_in = get_osfhandle(in_fd) as HANDLE; + if orig_std_in == INVALID_HANDLE_VALUE as HANDLE { + fail2!("failure in get_osfhandle: {}", os::last_os_error()); + } + if DuplicateHandle(cur_proc, orig_std_in, cur_proc, &mut si.hStdInput, + 0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE { + fail2!("failure in DuplicateHandle: {}", os::last_os_error()); + } + + let orig_std_out = get_osfhandle(out_fd) as HANDLE; + if orig_std_out == INVALID_HANDLE_VALUE as HANDLE { + fail2!("failure in get_osfhandle: {}", os::last_os_error()); + } + if DuplicateHandle(cur_proc, orig_std_out, cur_proc, &mut si.hStdOutput, + 0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE { + fail2!("failure in DuplicateHandle: {}", os::last_os_error()); + } + + let orig_std_err = get_osfhandle(err_fd) as HANDLE; + if orig_std_err == INVALID_HANDLE_VALUE as HANDLE { + fail2!("failure in get_osfhandle: {}", os::last_os_error()); + } + if DuplicateHandle(cur_proc, orig_std_err, cur_proc, &mut si.hStdError, + 0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE { + fail2!("failure in DuplicateHandle: {}", os::last_os_error()); + } + + let cmd = make_command_line(prog, args); + let mut pi = zeroed_process_information(); + let mut create_err = None; + + do with_envp(env) |envp| { + do with_dirp(dir) |dirp| { + do cmd.with_c_str |cmdp| { + let created = CreateProcessA(ptr::null(), cast::transmute(cmdp), + ptr::mut_null(), ptr::mut_null(), TRUE, + 0, envp, dirp, &mut si, &mut pi); + if created == FALSE { + create_err = Some(os::last_os_error()); + } + } + } + } + + CloseHandle(si.hStdInput); + CloseHandle(si.hStdOutput); + CloseHandle(si.hStdError); + + for msg in create_err.iter() { + fail2!("failure in CreateProcess: {}", *msg); + } + + // We close the thread handle because we don't care about keeping the + // thread id valid, and we aren't keeping the thread handle around to be + // able to close it later. We don't close the process handle however + // because we want the process id to stay valid at least until the + // calling code closes the process handle. + CloseHandle(pi.hThread); + + SpawnProcessResult { + pid: pi.dwProcessId as pid_t, + handle: pi.hProcess as *() + } + } +} + +#[cfg(windows)] +fn zeroed_startupinfo() -> libc::types::os::arch::extra::STARTUPINFO { + libc::types::os::arch::extra::STARTUPINFO { + cb: 0, + lpReserved: ptr::mut_null(), + lpDesktop: ptr::mut_null(), + lpTitle: ptr::mut_null(), + dwX: 0, + dwY: 0, + dwXSize: 0, + dwYSize: 0, + dwXCountChars: 0, + dwYCountCharts: 0, + dwFillAttribute: 0, + dwFlags: 0, + wShowWindow: 0, + cbReserved2: 0, + lpReserved2: ptr::mut_null(), + hStdInput: ptr::mut_null(), + hStdOutput: ptr::mut_null(), + hStdError: ptr::mut_null() + } +} + +#[cfg(windows)] +fn zeroed_process_information() -> libc::types::os::arch::extra::PROCESS_INFORMATION { + libc::types::os::arch::extra::PROCESS_INFORMATION { + hProcess: ptr::mut_null(), + hThread: ptr::mut_null(), + dwProcessId: 0, + dwThreadId: 0 + } +} + +// FIXME: this is only pub so it can be tested (see issue #4536) +#[cfg(windows)] +pub fn make_command_line(prog: &str, args: &[~str]) -> ~str { + let mut cmd = ~""; + append_arg(&mut cmd, prog); + for arg in args.iter() { + cmd.push_char(' '); + append_arg(&mut cmd, *arg); + } + return cmd; + + fn append_arg(cmd: &mut ~str, arg: &str) { + let quote = arg.iter().any(|c| c == ' ' || c == '\t'); + if quote { + cmd.push_char('"'); + } + for i in range(0u, arg.len()) { + append_char_at(cmd, arg, i); + } + if quote { + cmd.push_char('"'); + } + } + + fn append_char_at(cmd: &mut ~str, arg: &str, i: uint) { + match arg[i] as char { + '"' => { + // Escape quotes. + cmd.push_str("\\\""); + } + '\\' => { + if backslash_run_ends_in_quote(arg, i) { + // Double all backslashes that are in runs before quotes. + cmd.push_str("\\\\"); + } else { + // Pass other backslashes through unescaped. + cmd.push_char('\\'); + } + } + c => { + cmd.push_char(c); + } + } + } + + fn backslash_run_ends_in_quote(s: &str, mut i: uint) -> bool { + while i < s.len() && s[i] as char == '\\' { + i += 1; + } + return i < s.len() && s[i] as char == '"'; + } +} + +#[cfg(unix)] +fn spawn_process_os(prog: &str, args: &[~str], + env: Option<~[(~str, ~str)]>, + dir: Option<&Path>, + in_fd: c_int, out_fd: c_int, err_fd: c_int) -> SpawnProcessResult { + #[fixed_stack_segment]; #[inline(never)]; + + use libc::funcs::posix88::unistd::{fork, dup2, close, chdir, execvp}; + use libc::funcs::bsd44::getdtablesize; + + mod rustrt { + #[abi = "cdecl"] + extern { + pub fn rust_unset_sigprocmask(); + } + } + + #[cfg(windows)] + unsafe fn set_environ(_envp: *c_void) {} + #[cfg(target_os = "macos")] + unsafe fn set_environ(envp: *c_void) { + externfn!(fn _NSGetEnviron() -> *mut *c_void); + + *_NSGetEnviron() = envp; + } + #[cfg(not(target_os = "macos"), not(windows))] + unsafe fn set_environ(envp: *c_void) { + extern { + static mut environ: *c_void; + } + environ = envp; + } + + unsafe { + + let pid = fork(); + if pid < 0 { + fail2!("failure in fork: {}", os::last_os_error()); + } else if pid > 0 { + return SpawnProcessResult {pid: pid, handle: ptr::null()}; + } + + rustrt::rust_unset_sigprocmask(); + + if dup2(in_fd, 0) == -1 { + fail2!("failure in dup2(in_fd, 0): {}", os::last_os_error()); + } + if dup2(out_fd, 1) == -1 { + fail2!("failure in dup2(out_fd, 1): {}", os::last_os_error()); + } + if dup2(err_fd, 2) == -1 { + fail2!("failure in dup3(err_fd, 2): {}", os::last_os_error()); + } + // close all other fds + for fd in range(3, getdtablesize()).invert() { + close(fd as c_int); + } + + do with_dirp(dir) |dirp| { + if !dirp.is_null() && chdir(dirp) == -1 { + fail2!("failure in chdir: {}", os::last_os_error()); + } + } + + do with_envp(env) |envp| { + if !envp.is_null() { + set_environ(envp); + } + do with_argv(prog, args) |argv| { + execvp(*argv, argv); + // execvp only returns if an error occurred + fail2!("failure in execvp: {}", os::last_os_error()); + } + } + } +} + +#[cfg(unix)] +fn with_argv(prog: &str, args: &[~str], cb: &fn(**libc::c_char) -> T) -> T { + use vec; + + // We can't directly convert `str`s into `*char`s, as someone needs to hold + // a reference to the intermediary byte buffers. So first build an array to + // hold all the ~[u8] byte strings. + let mut tmps = vec::with_capacity(args.len() + 1); + + tmps.push(prog.to_c_str()); + + for arg in args.iter() { + tmps.push(arg.to_c_str()); + } + + // Next, convert each of the byte strings into a pointer. This is + // technically unsafe as the caller could leak these pointers out of our + // scope. + let mut ptrs = do tmps.map |tmp| { + tmp.with_ref(|buf| buf) + }; + + // Finally, make sure we add a null pointer. + ptrs.push(ptr::null()); + + ptrs.as_imm_buf(|buf, _| cb(buf)) +} + +#[cfg(unix)] +fn with_envp(env: Option<~[(~str, ~str)]>, cb: &fn(*c_void) -> T) -> T { + use vec; + + // On posixy systems we can pass a char** for envp, which is a + // null-terminated array of "k=v\n" strings. Like `with_argv`, we have to + // have a temporary buffer to hold the intermediary `~[u8]` byte strings. + match env { + Some(env) => { + let mut tmps = vec::with_capacity(env.len()); + + for pair in env.iter() { + let kv = format!("{}={}", pair.first(), pair.second()); + tmps.push(kv.to_c_str()); + } + + // Once again, this is unsafe. + let mut ptrs = do tmps.map |tmp| { + tmp.with_ref(|buf| buf) + }; + ptrs.push(ptr::null()); + + do ptrs.as_imm_buf |buf, _| { + unsafe { cb(cast::transmute(buf)) } + } + } + _ => cb(ptr::null()) + } +} + +#[cfg(windows)] +fn with_envp(env: Option<~[(~str, ~str)]>, cb: &fn(*mut c_void) -> T) -> T { + // On win32 we pass an "environment block" which is not a char**, but + // rather a concatenation of null-terminated k=v\0 sequences, with a final + // \0 to terminate. + match env { + Some(env) => { + let mut blk = ~[]; + + for pair in env.iter() { + let kv = format!("{}={}", pair.first(), pair.second()); + blk.push_all(kv.as_bytes()); + blk.push(0); + } + + blk.push(0); + + do blk.as_imm_buf |p, _len| { + unsafe { cb(cast::transmute(p)) } + } + } + _ => cb(ptr::mut_null()) + } +} + +fn with_dirp(d: Option<&Path>, cb: &fn(*libc::c_char) -> T) -> T { + match d { + Some(dir) => dir.with_c_str(|buf| cb(buf)), + None => cb(ptr::null()) + } +} + +#[cfg(windows)] +fn free_handle(handle: *()) { + #[fixed_stack_segment]; #[inline(never)]; + unsafe { + libc::funcs::extra::kernel32::CloseHandle(cast::transmute(handle)); + } +} + +#[cfg(unix)] +fn free_handle(_handle: *()) { + // unix has no process handle object, just a pid +} + +/** + * Waits for a process to exit and returns the exit code, failing + * if there is no process with the specified id. + * + * Note that this is private to avoid race conditions on unix where if + * a user calls waitpid(some_process.get_id()) then some_process.finish() + * and some_process.destroy() and some_process.finalize() will then either + * operate on a none-existent process or, even worse, on a newer process + * with the same id. + */ +fn waitpid(pid: pid_t) -> int { + return waitpid_os(pid); + + #[cfg(windows)] + fn waitpid_os(pid: pid_t) -> int { + #[fixed_stack_segment]; #[inline(never)]; + + use libc::types::os::arch::extra::DWORD; + use libc::consts::os::extra::{ + SYNCHRONIZE, + PROCESS_QUERY_INFORMATION, + FALSE, + STILL_ACTIVE, + INFINITE, + WAIT_FAILED + }; + use libc::funcs::extra::kernel32::{ + OpenProcess, + GetExitCodeProcess, + CloseHandle, + WaitForSingleObject + }; + + unsafe { + + let proc = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION, FALSE, pid as DWORD); + if proc.is_null() { + fail2!("failure in OpenProcess: {}", os::last_os_error()); + } + + loop { + let mut status = 0; + if GetExitCodeProcess(proc, &mut status) == FALSE { + CloseHandle(proc); + fail2!("failure in GetExitCodeProcess: {}", os::last_os_error()); + } + if status != STILL_ACTIVE { + CloseHandle(proc); + return status as int; + } + if WaitForSingleObject(proc, INFINITE) == WAIT_FAILED { + CloseHandle(proc); + fail2!("failure in WaitForSingleObject: {}", os::last_os_error()); + } + } + } + } + + #[cfg(unix)] + fn waitpid_os(pid: pid_t) -> int { + #[fixed_stack_segment]; #[inline(never)]; + + use libc::funcs::posix01::wait::*; + + #[cfg(target_os = "linux")] + #[cfg(target_os = "android")] + fn WIFEXITED(status: i32) -> bool { + (status & 0xffi32) == 0i32 + } + + #[cfg(target_os = "macos")] + #[cfg(target_os = "freebsd")] + fn WIFEXITED(status: i32) -> bool { + (status & 0x7fi32) == 0i32 + } + + #[cfg(target_os = "linux")] + #[cfg(target_os = "android")] + fn WEXITSTATUS(status: i32) -> i32 { + (status >> 8i32) & 0xffi32 + } + + #[cfg(target_os = "macos")] + #[cfg(target_os = "freebsd")] + fn WEXITSTATUS(status: i32) -> i32 { + status >> 8i32 + } + + let mut status = 0 as c_int; + if unsafe { waitpid(pid, &mut status, 0) } == -1 { + fail2!("failure in waitpid: {}", os::last_os_error()); + } + + return if WIFEXITED(status) { + WEXITSTATUS(status) as int + } else { + 1 + }; + } +} + +#[cfg(test)] +mod tests { + + #[test] #[cfg(windows)] + fn test_make_command_line() { + use super::make_command_line; + assert_eq!( + make_command_line("prog", [~"aaa", ~"bbb", ~"ccc"]), + ~"prog aaa bbb ccc" + ); + assert_eq!( + make_command_line("C:\\Program Files\\blah\\blah.exe", [~"aaa"]), + ~"\"C:\\Program Files\\blah\\blah.exe\" aaa" + ); + assert_eq!( + make_command_line("C:\\Program Files\\test", [~"aa\"bb"]), + ~"\"C:\\Program Files\\test\" aa\\\"bb" + ); + assert_eq!( + make_command_line("echo", [~"a b c"]), + ~"echo \"a b c\"" + ); + } + + // Currently most of the tests of this functionality live inside std::run, + // but they may move here eventually as a non-blocking backend is added to + // std::run +} diff --git a/src/libstd/rt/io/process.rs b/src/libstd/rt/io/process.rs index c190547889d..5f2453852ee 100644 --- a/src/libstd/rt/io/process.rs +++ b/src/libstd/rt/io/process.rs @@ -18,6 +18,13 @@ use rt::io::io_error; use rt::local::Local; use rt::rtio::{RtioProcess, RtioProcessObject, IoFactoryObject, IoFactory}; +// windows values don't matter as long as they're at least one of unix's +// TERM/KILL/INT signals +#[cfg(windows)] pub static PleaseExitSignal: int = 15; +#[cfg(windows)] pub static MustDieSignal: int = 9; +#[cfg(not(windows))] pub static PleaseExitSignal: int = libc::SIGTERM as int; +#[cfg(not(windows))] pub static MustDieSignal: int = libc::SIGKILL as int; + pub struct Process { priv handle: ~RtioProcessObject, io: ~[Option], -- cgit 1.4.1-3-g733a5 From facefa7c8d1adf6c851dca88fcf4f5d26f72caa9 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 7 Oct 2013 13:25:06 -0700 Subject: Implement rt::io::stdio Additionally, this moves the prelude imports of print/println from std::io to std::rt::io. Closes #6846 --- src/libstd/prelude.rs | 2 +- src/libstd/rt/io/native/file.rs | 3 +- src/libstd/rt/io/stdio.rs | 105 +++++++++++++++++++++++++++++++--------- 3 files changed, 84 insertions(+), 26 deletions(-) (limited to 'src/libstd/rt') diff --git a/src/libstd/prelude.rs b/src/libstd/prelude.rs index 273a01c1811..3da337add94 100644 --- a/src/libstd/prelude.rs +++ b/src/libstd/prelude.rs @@ -39,7 +39,7 @@ pub use option::{Option, Some, None}; pub use result::{Result, Ok, Err}; // Reexported functions -pub use io::{print, println}; +pub use rt::io::stdio::{print, println}; pub use iter::range; pub use from_str::from_str; diff --git a/src/libstd/rt/io/native/file.rs b/src/libstd/rt/io/native/file.rs index f0dd63a3224..dc8d34d1b11 100644 --- a/src/libstd/rt/io/native/file.rs +++ b/src/libstd/rt/io/native/file.rs @@ -223,6 +223,7 @@ mod tests { use super::*; #[test] #[fixed_stack_segment] + #[ignore(cfg(target_os = "freebsd"))] // hmm, maybe pipes have a tiny buffer fn test_file_desc() { // Run this test with some pipes so we don't have to mess around with // opening or closing files. @@ -258,7 +259,7 @@ mod tests { } #[test] #[fixed_stack_segment] - #[ignore(windows)] // apparently windows doesn't like tmpfile + #[ignore(cfg(windows))] // apparently windows doesn't like tmpfile fn test_cfile() { unsafe { let f = libc::tmpfile(); diff --git a/src/libstd/rt/io/stdio.rs b/src/libstd/rt/io/stdio.rs index 734a40429a6..e3ca148862f 100644 --- a/src/libstd/rt/io/stdio.rs +++ b/src/libstd/rt/io/stdio.rs @@ -8,45 +8,102 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use prelude::*; -use super::{Reader, Writer}; +use libc; +use option::{Option, Some, None}; +use result::{Ok, Err}; +use rt::local::Local; +use rt::rtio::{RtioFileStream, IoFactoryObject, IoFactory}; +use super::{Reader, Writer, io_error}; -pub fn stdin() -> StdReader { fail2!() } - -pub fn stdout() -> StdWriter { fail2!() } - -pub fn stderr() -> StdReader { fail2!() } +/// Creates a new non-blocking handle to the stdin of the current process. +/// +/// See `stdout()` for notes about this function. +pub fn stdin() -> StdReader { + let stream = unsafe { + let io: *mut IoFactoryObject = Local::unsafe_borrow(); + (*io).fs_from_raw_fd(libc::STDIN_FILENO, false) + }; + StdReader { inner: stream } +} -pub fn print(_s: &str) { fail2!() } +/// Creates a new non-blocking handle to the stdout of the current process. +/// +/// Note that this is a fairly expensive operation in that at least one memory +/// allocation is performed. Additionally, this must be called from a runtime +/// task context because the stream returned will be a non-blocking object using +/// the local scheduler to perform the I/O. +pub fn stdout() -> StdWriter { + let stream = unsafe { + let io: *mut IoFactoryObject = Local::unsafe_borrow(); + (*io).fs_from_raw_fd(libc::STDOUT_FILENO, false) + }; + StdWriter { inner: stream } +} -pub fn println(_s: &str) { fail2!() } +/// Creates a new non-blocking handle to the stderr of the current process. +/// +/// See `stdout()` for notes about this function. +pub fn stderr() -> StdWriter { + let stream = unsafe { + let io: *mut IoFactoryObject = Local::unsafe_borrow(); + (*io).fs_from_raw_fd(libc::STDERR_FILENO, false) + }; + StdWriter { inner: stream } +} -pub enum StdStream { - StdIn, - StdOut, - StdErr +/// Prints a string to the stdout of the current process. No newline is emitted +/// after the string is printed. +pub fn print(s: &str) { + // XXX: need to see if not caching stdin() is the cause of performance + // issues, it should be possible to cache a stdout handle in each Task + // and then re-use that across calls to print/println + stdout().write(s.as_bytes()); } -pub struct StdReader; +/// Prints a string as a line. to the stdout of the current process. A literal +/// `\n` character is printed to the console after the string. +pub fn println(s: &str) { + let mut out = stdout(); + out.write(s.as_bytes()); + out.write(['\n' as u8]); +} -impl StdReader { - pub fn new(_stream: StdStream) -> StdReader { fail2!() } +/// Representation of a reader of a standard input stream +pub struct StdReader { + priv inner: ~RtioFileStream } impl Reader for StdReader { - fn read(&mut self, _buf: &mut [u8]) -> Option { fail2!() } + fn read(&mut self, buf: &mut [u8]) -> Option { + match self.inner.read(buf) { + Ok(amt) => Some(amt as uint), + Err(e) => { + io_error::cond.raise(e); + None + } + } + } - fn eof(&mut self) -> bool { fail2!() } + fn eof(&mut self) -> bool { false } } -pub struct StdWriter; - -impl StdWriter { - pub fn new(_stream: StdStream) -> StdWriter { fail2!() } +/// Representation of a writer to a standard output stream +pub struct StdWriter { + priv inner: ~RtioFileStream } impl Writer for StdWriter { - fn write(&mut self, _buf: &[u8]) { fail2!() } + fn write(&mut self, buf: &[u8]) { + match self.inner.write(buf) { + Ok(()) => {} + Err(e) => io_error::cond.raise(e) + } + } - fn flush(&mut self) { fail2!() } + fn flush(&mut self) { + match self.inner.flush() { + Ok(()) => {} + Err(e) => io_error::cond.raise(e) + } + } } -- cgit 1.4.1-3-g733a5 From ee1e6529bddc4ea262e6453589a8042e0128594e Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Sun, 6 Oct 2013 15:39:42 -0700 Subject: Implement BufferedReader.{read_until, read_line} These two functions will be useful when replacing various other counterparts used by std::io consumers. --- src/libstd/rt/io/buffered.rs | 72 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 65 insertions(+), 7 deletions(-) (limited to 'src/libstd/rt') diff --git a/src/libstd/rt/io/buffered.rs b/src/libstd/rt/io/buffered.rs index 2269469ee23..725477bec41 100644 --- a/src/libstd/rt/io/buffered.rs +++ b/src/libstd/rt/io/buffered.rs @@ -55,6 +55,7 @@ use prelude::*; use num; use vec; +use str; use super::{Reader, Writer, Stream, Decorator}; // libuv recommends 64k buffers to maximize throughput @@ -84,23 +85,69 @@ impl BufferedReader { pub fn new(inner: R) -> BufferedReader { BufferedReader::with_capacity(DEFAULT_CAPACITY, inner) } -} -impl Reader for BufferedReader { - fn read(&mut self, buf: &mut [u8]) -> Option { + /// Reads the next line of input, interpreted as a sequence of utf-8 + /// encoded unicode codepoints. If a newline is encountered, then the + /// newline is contained in the returned string. + pub fn read_line(&mut self) -> ~str { + str::from_utf8_owned(self.read_until('\n' as u8)) + } + + /// Reads a sequence of bytes leading up to a specified delimeter. Once the + /// specified byte is encountered, reading ceases and the bytes up to and + /// including the delimiter are returned. + pub fn read_until(&mut self, byte: u8) -> ~[u8] { + let mut res = ~[]; + let mut used; + loop { + { + let available = self.fill_buffer(); + match available.iter().position(|&b| b == byte) { + Some(i) => { + res.push_all(available.slice_to(i + 1)); + used = i + 1; + break + } + None => { + res.push_all(available); + used = available.len(); + } + } + } + if used == 0 { + break + } + self.pos += used; + } + self.pos += used; + return res; + } + + fn fill_buffer<'a>(&'a mut self) -> &'a [u8] { if self.pos == self.cap { match self.inner.read(self.buf) { Some(cap) => { self.pos = 0; self.cap = cap; } - None => return None + None => {} } } + return self.buf.slice(self.pos, self.cap); + } +} - let src = self.buf.slice(self.pos, self.cap); - let nread = num::min(src.len(), buf.len()); - vec::bytes::copy_memory(buf, src, nread); +impl Reader for BufferedReader { + fn read(&mut self, buf: &mut [u8]) -> Option { + let nread = { + let available = self.fill_buffer(); + if available.len() == 0 { + return None; + } + let nread = num::min(available.len(), buf.len()); + vec::bytes::copy_memory(buf, available, nread); + nread + }; self.pos += nread; Some(nread) } @@ -355,4 +402,15 @@ mod test { stream.write(buf); stream.flush(); } + + #[test] + fn test_read_until() { + let inner = MemReader::new(~[0, 1, 2, 1, 0]); + let mut reader = BufferedReader::with_capacity(2, inner); + assert_eq!(reader.read_until(0), Some(~[0])); + assert_eq!(reader.read_until(2), Some(~[1, 2])); + assert_eq!(reader.read_until(1), Some(~[1])); + assert_eq!(reader.read_until(8), Some(~[0])); + assert_eq!(reader.read_until(9), None); + } } -- cgit 1.4.1-3-g733a5 From b07ab1fe4baa584401fa17a7ba20bea8c97c5043 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Sun, 6 Oct 2013 13:24:50 -0700 Subject: Migrate users of io::fd_t to io::native::file::fd_t --- src/compiletest/errors.rs | 1 + src/libstd/cleanup.rs | 17 +++++--------- src/libstd/macros.rs | 2 +- src/libstd/rt/borrowck.rs | 56 +++++------------------------------------------ src/libstd/rt/io/mod.rs | 7 ++++-- src/libstd/rt/logging.rs | 49 ++++++++++++++--------------------------- src/libstd/rt/util.rs | 10 +++++---- 7 files changed, 41 insertions(+), 101 deletions(-) (limited to 'src/libstd/rt') diff --git a/src/compiletest/errors.rs b/src/compiletest/errors.rs index 02195e684e3..13f4cbf8e21 100644 --- a/src/compiletest/errors.rs +++ b/src/compiletest/errors.rs @@ -26,6 +26,7 @@ pub fn load_errors(testfile: &Path) -> ~[ExpectedError] { } fn parse_expected(line_num: uint, line: ~str) -> ~[ExpectedError] { + let line = line.trim(); let error_tag = ~"//~"; let mut idx; match line.find_str(error_tag) { diff --git a/src/libstd/cleanup.rs b/src/libstd/cleanup.rs index 6b982ec75da..1c9944664ee 100644 --- a/src/libstd/cleanup.rs +++ b/src/libstd/cleanup.rs @@ -68,9 +68,6 @@ fn debug_mem() -> bool { /// Destroys all managed memory (i.e. @ boxes) held by the current task. pub unsafe fn annihilate() { use rt::local_heap::local_free; - use io::WriterUtil; - use io; - use libc; use sys; use managed; @@ -126,14 +123,10 @@ pub unsafe fn annihilate() { if debug_mem() { // We do logging here w/o allocation. - let dbg = libc::STDERR_FILENO as io::fd_t; - dbg.write_str("annihilator stats:"); - dbg.write_str("\n total_boxes: "); - dbg.write_uint(stats.n_total_boxes); - dbg.write_str("\n unique_boxes: "); - dbg.write_uint(stats.n_unique_boxes); - dbg.write_str("\n bytes_freed: "); - dbg.write_uint(stats.n_bytes_freed); - dbg.write_str("\n"); + rterrln!("annihilator stats:\n \ + total boxes: {}\n \ + unique boxes: {}\n \ + bytes freed: {}", + stats.n_total_boxes, stats.n_unique_boxes, stats.n_bytes_freed); } } diff --git a/src/libstd/macros.rs b/src/libstd/macros.rs index 0b1475ff380..7fd27ea7ccc 100644 --- a/src/libstd/macros.rs +++ b/src/libstd/macros.rs @@ -13,7 +13,7 @@ macro_rules! rterrln ( ($($arg:tt)*) => ( { - ::rt::util::dumb_println(format!($($arg)*)); + format_args!(::rt::util::dumb_println, $($arg)*) } ) ) diff --git a/src/libstd/rt/borrowck.rs b/src/libstd/rt/borrowck.rs index d703272420c..6be23a983ab 100644 --- a/src/libstd/rt/borrowck.rs +++ b/src/libstd/rt/borrowck.rs @@ -9,11 +9,8 @@ // except according to those terms. use cell::Cell; -use c_str::ToCStr; -use cast::transmute; -use io::{Writer, WriterUtil}; -use io; -use libc::{c_char, size_t, STDERR_FILENO}; +use c_str::{ToCStr, CString}; +use libc::{c_char, size_t}; use option::{Option, None, Some}; use ptr::RawPtr; use rt::env; @@ -113,51 +110,10 @@ unsafe fn debug_borrow>(tag: &'static str, new_bits: uint, filename: *c_char, line: size_t) { - let dbg = STDERR_FILENO as io::fd_t; - dbg.write_str(tag); - dbg.write_hex(p.to_uint()); - dbg.write_str(" "); - dbg.write_hex(old_bits); - dbg.write_str(" "); - dbg.write_hex(new_bits); - dbg.write_str(" "); - dbg.write_cstr(filename); - dbg.write_str(":"); - dbg.write_hex(line as uint); - dbg.write_str("\n"); - } -} - -trait DebugPrints { - fn write_hex(&self, val: uint); - unsafe fn write_cstr(&self, str: *c_char); -} - -impl DebugPrints for io::fd_t { - fn write_hex(&self, mut i: uint) { - let letters = ['0', '1', '2', '3', '4', '5', '6', '7', '8', - '9', 'a', 'b', 'c', 'd', 'e', 'f']; - static UINT_NIBBLES: uint = ::uint::bytes << 1; - let mut buffer = [0_u8, ..UINT_NIBBLES+1]; - let mut c = UINT_NIBBLES; - while c > 0 { - c -= 1; - buffer[c] = letters[i & 0xF] as u8; - i >>= 4; - } - self.write(buffer.slice(0, UINT_NIBBLES)); - } - - unsafe fn write_cstr(&self, p: *c_char) { - #[fixed_stack_segment]; #[inline(never)]; - use libc::strlen; - use vec; - - let len = strlen(p); - let p: *u8 = transmute(p); - do vec::raw::buf_as_slice(p, len as uint) |s| { - self.write(s); - } + let filename = CString::new(filename, false); + rterrln!("{}{:#x} {:x} {:x} {}:{}", + tag, p.to_uint(), old_bits, new_bits, + filename.as_str().unwrap(), line); } } diff --git a/src/libstd/rt/io/mod.rs b/src/libstd/rt/io/mod.rs index a18f97930fa..d56ad9ce848 100644 --- a/src/libstd/rt/io/mod.rs +++ b/src/libstd/rt/io/mod.rs @@ -313,8 +313,11 @@ pub mod buffered; pub mod native { /// Posix file I/O pub mod file; - /// # XXX - implement this - pub mod stdio { } + /// Process spawning and child management + pub mod process; + /// Posix stdio + pub mod stdio; + /// Sockets /// # XXX - implement this pub mod net { diff --git a/src/libstd/rt/logging.rs b/src/libstd/rt/logging.rs index b08e76921d8..660d1cd4359 100644 --- a/src/libstd/rt/logging.rs +++ b/src/libstd/rt/logging.rs @@ -12,8 +12,6 @@ use fmt; use from_str::from_str; use libc::exit; use option::{Some, None, Option}; -use rt; -use rt::util::dumb_println; use rt::crate_map::{ModEntry, CrateMap, iter_crate_map, get_crate_map}; use str::StrSlice; use u32; @@ -88,16 +86,16 @@ fn parse_logging_spec(spec: ~str) -> ~[LogDirective]{ log_level = num; }, _ => { - dumb_println(format!("warning: invalid logging spec \ - '{}', ignoring it", parts[1])); - continue; + rterrln!("warning: invalid logging spec '{}', \ + ignoring it", parts[1]); + continue } } }, _ => { - dumb_println(format!("warning: invalid logging spec '{}',\ - ignoring it", s)); - continue; + rterrln!("warning: invalid logging spec '{}', \ + ignoring it", s); + continue } } let dir = LogDirective {name: name, level: log_level}; @@ -141,9 +139,9 @@ fn update_log_settings(crate_map: &CrateMap, settings: ~str) { let mut dirs = ~[]; if settings.len() > 0 { if settings == ~"::help" || settings == ~"?" { - dumb_println("\nCrate log map:\n"); + rterrln!("\nCrate log map:\n"); do iter_crate_map(crate_map) |entry| { - dumb_println(" "+entry.name); + rterrln!(" {}", entry.name); } unsafe { exit(1); } } @@ -157,12 +155,10 @@ fn update_log_settings(crate_map: &CrateMap, settings: ~str) { } if n_matches < (dirs.len() as u32) { - dumb_println(format!("warning: got {} RUST_LOG specs but only matched\n\ - {} of them. You may have mistyped a RUST_LOG \ - spec. \n\ - Use RUST_LOG=::help to see the list of crates \ - and modules.\n", - dirs.len(), n_matches)); + rterrln!("warning: got {} RUST_LOG specs but only matched\n\ + {} of them. You may have mistyped a RUST_LOG spec. \n\ + Use RUST_LOG=::help to see the list of crates and modules.\n", + dirs.len(), n_matches); } } @@ -174,24 +170,13 @@ pub struct StdErrLogger; impl Logger for StdErrLogger { fn log(&mut self, args: &fmt::Arguments) { - fmt::writeln(self as &mut rt::io::Writer, args); + // FIXME(#6846): this should not call the blocking version of println, + // or at least the default loggers for tasks shouldn't do + // that + ::rt::util::dumb_println(args); } } -impl rt::io::Writer for StdErrLogger { - fn write(&mut self, buf: &[u8]) { - // Nothing like swapping between I/O implementations! In theory this - // could use the libuv bindings for writing to file descriptors, but - // that may not necessarily be desirable because logging should work - // outside of the uv loop. (modify with caution) - use io::Writer; - let dbg = ::libc::STDERR_FILENO as ::io::fd_t; - dbg.write(buf); - } - - fn flush(&mut self) {} -} - /// Configure logging by traversing the crate map and setting the /// per-module global logging flags based on the logging spec pub fn init() { @@ -212,7 +197,7 @@ pub fn init() { _ => { match log_spec { Some(_) => { - dumb_println("warning: RUST_LOG set, but no crate map found."); + rterrln!("warning: RUST_LOG set, but no crate map found."); }, None => {} } diff --git a/src/libstd/rt/util.rs b/src/libstd/rt/util.rs index 68996a3a2a5..727bdb782d2 100644 --- a/src/libstd/rt/util.rs +++ b/src/libstd/rt/util.rs @@ -9,6 +9,7 @@ // except according to those terms. use container::Container; +use fmt; use from_str::FromStr; use libc; use option::{Some, None, Option}; @@ -74,10 +75,11 @@ pub fn default_sched_threads() -> uint { } } -pub fn dumb_println(s: &str) { - use io::WriterUtil; - let dbg = ::libc::STDERR_FILENO as ::io::fd_t; - dbg.write_str(s + "\n"); +pub fn dumb_println(args: &fmt::Arguments) { + use rt::io::native::stdio::stderr; + use rt::io::Writer; + let mut out = stderr(); + fmt::writeln(&mut out as &mut Writer, args); } pub fn abort(msg: &str) -> ! { -- cgit 1.4.1-3-g733a5 From 2290ce14f29450560cd34c219dbc8b6a10283967 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Sun, 6 Oct 2013 16:08:56 -0700 Subject: Remove some users of io::file_reader --- src/compiletest/errors.rs | 14 +++++++----- src/compiletest/header.rs | 18 ++++++++++------ src/libstd/rt/io/buffered.rs | 8 +++---- src/libstd/rt/io/mod.rs | 20 +++++++++++++++++ src/test/bench/core-std.rs | 7 ++++-- src/test/bench/shootout-k-nucleotide-pipes.rs | 31 ++++++++++++++++----------- 6 files changed, 68 insertions(+), 30 deletions(-) (limited to 'src/libstd/rt') diff --git a/src/compiletest/errors.rs b/src/compiletest/errors.rs index 13f4cbf8e21..e49a9701460 100644 --- a/src/compiletest/errors.rs +++ b/src/compiletest/errors.rs @@ -8,17 +8,21 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::io; - pub struct ExpectedError { line: uint, kind: ~str, msg: ~str } // Load any test directives embedded in the file pub fn load_errors(testfile: &Path) -> ~[ExpectedError] { + use std::rt::io::Open; + use std::rt::io::file::FileInfo; + use std::rt::io::buffered::BufferedReader; + let mut error_patterns = ~[]; - let rdr = io::file_reader(testfile).unwrap(); + let mut rdr = BufferedReader::new(testfile.open_reader(Open).unwrap()); let mut line_num = 1u; - while !rdr.eof() { - let ln = rdr.read_line(); + loop { + let ln = match rdr.read_line() { + Some(ln) => ln, None => break, + }; error_patterns.push_all_move(parse_expected(line_num, ln)); line_num += 1u; } diff --git a/src/compiletest/header.rs b/src/compiletest/header.rs index 36780abc7ee..730df66af23 100644 --- a/src/compiletest/header.rs +++ b/src/compiletest/header.rs @@ -12,8 +12,6 @@ use common::config; use common; use util; -use std::io; - pub struct TestProps { // Lines that should be expected, in order, on standard out error_patterns: ~[~str], @@ -104,17 +102,23 @@ pub fn is_test_ignored(config: &config, testfile: &Path) -> bool { !val } -fn iter_header(testfile: &Path, it: &fn(~str) -> bool) -> bool { - let rdr = io::file_reader(testfile).unwrap(); - while !rdr.eof() { - let ln = rdr.read_line(); +fn iter_header(testfile: &Path, it: &fn(&str) -> bool) -> bool { + use std::rt::io::Open; + use std::rt::io::file::FileInfo; + use std::rt::io::buffered::BufferedReader; + + let mut rdr = BufferedReader::new(testfile.open_reader(Open).unwrap()); + loop { + let ln = match rdr.read_line() { + Some(ln) => ln, None => break + }; // Assume that any directives will be found before the first // module or function. This doesn't seem to be an optimization // with a warm page cache. Maybe with a cold one. if ln.starts_with("fn") || ln.starts_with("mod") { return true; - } else { if !(it(ln)) { return false; } } + } else { if !(it(ln.trim())) { return false; } } } return true; } diff --git a/src/libstd/rt/io/buffered.rs b/src/libstd/rt/io/buffered.rs index 725477bec41..9dcb35c806f 100644 --- a/src/libstd/rt/io/buffered.rs +++ b/src/libstd/rt/io/buffered.rs @@ -89,14 +89,14 @@ impl BufferedReader { /// Reads the next line of input, interpreted as a sequence of utf-8 /// encoded unicode codepoints. If a newline is encountered, then the /// newline is contained in the returned string. - pub fn read_line(&mut self) -> ~str { - str::from_utf8_owned(self.read_until('\n' as u8)) + pub fn read_line(&mut self) -> Option<~str> { + self.read_until('\n' as u8).map(str::from_utf8_owned) } /// Reads a sequence of bytes leading up to a specified delimeter. Once the /// specified byte is encountered, reading ceases and the bytes up to and /// including the delimiter are returned. - pub fn read_until(&mut self, byte: u8) -> ~[u8] { + pub fn read_until(&mut self, byte: u8) -> Option<~[u8]> { let mut res = ~[]; let mut used; loop { @@ -120,7 +120,7 @@ impl BufferedReader { self.pos += used; } self.pos += used; - return res; + return if res.len() == 0 {None} else {Some(res)}; } fn fill_buffer<'a>(&'a mut self) -> &'a [u8] { diff --git a/src/libstd/rt/io/mod.rs b/src/libstd/rt/io/mod.rs index d56ad9ce848..fa53f3cda25 100644 --- a/src/libstd/rt/io/mod.rs +++ b/src/libstd/rt/io/mod.rs @@ -462,6 +462,16 @@ pub trait Reader { fn eof(&mut self) -> bool; } +impl Reader for ~Reader { + fn read(&mut self, buf: &mut [u8]) -> Option { self.read(buf) } + fn eof(&mut self) -> bool { self.eof() } +} + +impl<'self> Reader for &'self mut Reader { + fn read(&mut self, buf: &mut [u8]) -> Option { self.read(buf) } + fn eof(&mut self) -> bool { self.eof() } +} + pub trait Writer { /// Write the given buffer /// @@ -474,6 +484,16 @@ pub trait Writer { fn flush(&mut self); } +impl Writer for ~Writer { + fn write(&mut self, buf: &[u8]) { self.write(buf) } + fn flush(&mut self) { self.flush() } +} + +impl<'self> Writer for &'self mut Writer { + fn write(&mut self, buf: &[u8]) { self.write(buf) } + fn flush(&mut self) { self.flush() } +} + pub trait Stream: Reader + Writer { } impl Stream for T {} diff --git a/src/test/bench/core-std.rs b/src/test/bench/core-std.rs index 7323dcf4ecb..5222c4f59b7 100644 --- a/src/test/bench/core-std.rs +++ b/src/test/bench/core-std.rs @@ -15,7 +15,6 @@ extern mod extra; use extra::time::precise_time_s; -use std::io; use std::os; use std::rand::Rng; use std::rand; @@ -70,11 +69,15 @@ fn shift_push() { } fn read_line() { + use std::rt::io::{Reader, Open}; + use std::rt::io::file::FileInfo; + use std::rt::io::buffered::BufferedReader; + let path = Path(env!("CFG_SRC_DIR")) .push_rel(&Path("src/test/bench/shootout-k-nucleotide.data")); for _ in range(0, 3) { - let reader = io::file_reader(&path).unwrap(); + let mut reader = BufferedReader::new(path.open_reader(Open).unwrap()); while !reader.eof() { reader.read_line(); } diff --git a/src/test/bench/shootout-k-nucleotide-pipes.rs b/src/test/bench/shootout-k-nucleotide-pipes.rs index ff7e7192b9f..445b28b693c 100644 --- a/src/test/bench/shootout-k-nucleotide-pipes.rs +++ b/src/test/bench/shootout-k-nucleotide-pipes.rs @@ -156,17 +156,21 @@ fn make_sequence_processor(sz: uint, // given a FASTA file on stdin, process sequence THREE fn main() { - let rdr = if os::getenv("RUST_BENCH").is_some() { - // FIXME: Using this compile-time env variable is a crummy way to - // get to this massive data set, but include_bin! chokes on it (#2598) - let path = Path(env!("CFG_SRC_DIR")) - .push_rel(&Path("src/test/bench/shootout-k-nucleotide.data")); - io::file_reader(&path).unwrap() - } else { - io::stdin() - }; - + use std::rt::io::{Reader, Open}; + use std::rt::io::file::FileInfo; + use std::rt::io::native::stdio; + use std::rt::io::buffered::BufferedReader; + let rdr = if os::getenv("RUST_BENCH").is_some() { + // FIXME: Using this compile-time env variable is a crummy way to + // get to this massive data set, but include_bin! chokes on it (#2598) + let path = Path(env!("CFG_SRC_DIR")) + .push_rel(&Path("src/test/bench/shootout-k-nucleotide.data")); + ~path.open_reader(Open).unwrap() as ~Reader + } else { + ~stdio::stdin() as ~Reader + }; + let mut rdr = BufferedReader::new(rdr); // initialize each sequence sorter let sizes = ~[1u,2,3,4,6,12,18]; @@ -193,8 +197,11 @@ fn main() { // reading the sequence of interest let mut proc_mode = false; - while !rdr.eof() { - let line: ~str = rdr.read_line(); + loop { + let line = match rdr.read_line() { + Some(ln) => ln, None => break, + }; + let line = line.trim().to_owned(); if line.len() == 0u { continue; } -- cgit 1.4.1-3-g733a5 From 413747176c9ce52a87775175e096b3eca88e6b64 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 7 Oct 2013 13:44:57 -0700 Subject: Make the file::DirectoryInfo trait public This was just a mistake that it was hidden. --- src/libstd/rt/io/file.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/libstd/rt') diff --git a/src/libstd/rt/io/file.rs b/src/libstd/rt/io/file.rs index a18eec8773e..3258c350cd0 100644 --- a/src/libstd/rt/io/file.rs +++ b/src/libstd/rt/io/file.rs @@ -599,7 +599,7 @@ impl FileInfo for Path { } /// else { fail2!("nope"); } /// } /// ``` -trait DirectoryInfo : FileSystemInfo { +pub 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 -- cgit 1.4.1-3-g733a5