// Copyright 2015 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 prelude::v1::*; use io::prelude::*; use io::{self, Cursor}; use iter::repeat; use libc; use ptr; use str; use sync::Mutex; use sys::c; use sys::cvt; use sys::handle::Handle; pub struct NoClose(Option); pub enum Output { Console(NoClose), Pipe(NoClose), } pub struct Stdin { handle: Output, utf8: Mutex>>, } pub struct Stdout(Output); pub struct Stderr(Output); pub fn get(handle: libc::DWORD) -> io::Result { let handle = unsafe { c::GetStdHandle(handle) }; if handle == libc::INVALID_HANDLE_VALUE { Err(io::Error::last_os_error()) } else if handle.is_null() { Err(io::Error::new(io::ErrorKind::Other, "no stdio handle available for this process")) } else { let ret = NoClose::new(handle); let mut out = 0; match unsafe { c::GetConsoleMode(handle, &mut out) } { 0 => Ok(Output::Pipe(ret)), _ => Ok(Output::Console(ret)), } } } fn write(out: &Output, data: &[u8]) -> io::Result { let handle = match *out { Output::Console(ref c) => c.get().raw(), Output::Pipe(ref p) => return p.get().write(data), }; let utf16 = match str::from_utf8(data).ok() { Some(utf8) => utf8.utf16_units().collect::>(), None => return Err(invalid_encoding()), }; let mut written = 0; try!(cvt(unsafe { c::WriteConsoleW(handle, utf16.as_ptr() as libc::LPCVOID, utf16.len() as u32, &mut written, ptr::null_mut()) })); // FIXME if this only partially writes the utf16 buffer then we need to // figure out how many bytes of `data` were actually written assert_eq!(written as usize, utf16.len()); Ok(data.len()) } impl Stdin { pub fn new() -> Stdin { Stdin { handle: get(c::STD_INPUT_HANDLE).unwrap(), utf8: Mutex::new(Cursor::new(Vec::new())), } } pub fn read(&self, buf: &mut [u8]) -> io::Result { let handle = match self.handle { Output::Console(ref c) => c.get().raw(), Output::Pipe(ref p) => return p.get().read(buf), }; let mut utf8 = self.utf8.lock().unwrap(); // Read more if the buffer is empty if utf8.position() as usize == utf8.get_ref().len() { let mut utf16: Vec = repeat(0u16).take(0x1000).collect(); let mut num = 0; try!(cvt(unsafe { c::ReadConsoleW(handle, utf16.as_mut_ptr() as libc::LPVOID, utf16.len() as u32, &mut num, ptr::null_mut()) })); utf16.truncate(num as usize); // FIXME: what to do about this data that has already been read? let data = match String::from_utf16(&utf16) { Ok(utf8) => utf8.into_bytes(), Err(..) => return Err(invalid_encoding()), }; *utf8 = Cursor::new(data); } // MemReader shouldn't error here since we just filled it utf8.read(buf) } } impl Stdout { pub fn new() -> Stdout { Stdout(get(c::STD_OUTPUT_HANDLE).unwrap()) } pub fn write(&self, data: &[u8]) -> io::Result { write(&self.0, data) } } impl Stderr { pub fn new() -> Stderr { Stderr(get(c::STD_ERROR_HANDLE).unwrap()) } pub fn write(&self, data: &[u8]) -> io::Result { write(&self.0, data) } } // FIXME: right now this raw stderr handle is used in a few places because // std::io::stderr_raw isn't exposed, but once that's exposed this impl // should go away impl io::Write for Stderr { fn write(&mut self, data: &[u8]) -> io::Result { Stderr::write(self, data) } fn flush(&mut self) -> io::Result<()> { Ok(()) } } impl NoClose { fn new(handle: libc::HANDLE) -> NoClose { NoClose(Some(Handle::new(handle))) } fn get(&self) -> &Handle { self.0.as_ref().unwrap() } } impl Drop for NoClose { fn drop(&mut self) { self.0.take().unwrap().into_raw(); } } impl Output { pub fn handle(&self) -> &Handle { let nc = match *self { Output::Console(ref c) => c, Output::Pipe(ref c) => c, }; nc.0.as_ref().unwrap() } } fn invalid_encoding() -> io::Error { io::Error::new(io::ErrorKind::InvalidInput, "text was not valid unicode") }