aboutsummaryrefslogtreecommitdiff
path: root/ctr-std/src/sys/windows/stdio.rs
diff options
context:
space:
mode:
authorValentin <[email protected]>2018-06-15 18:57:24 +0200
committerFenrirWolf <[email protected]>2018-06-15 10:57:24 -0600
commitf2a90174bb36b9ad528e863ab34c02ebce002b02 (patch)
tree959e8d67883d3a89e179b3549b1f30d28e51a87c /ctr-std/src/sys/windows/stdio.rs
parentMerge pull request #68 from linouxis9/master (diff)
downloadctru-rs-f2a90174bb36b9ad528e863ab34c02ebce002b02.tar.xz
ctru-rs-f2a90174bb36b9ad528e863ab34c02ebce002b02.zip
Update for latest nightly 2018-06-09 (#70)
* Update for latest nightly 2018-06-09 * We now have a proper horizon os and sys modules in libstd
Diffstat (limited to 'ctr-std/src/sys/windows/stdio.rs')
-rw-r--r--ctr-std/src/sys/windows/stdio.rs233
1 files changed, 233 insertions, 0 deletions
diff --git a/ctr-std/src/sys/windows/stdio.rs b/ctr-std/src/sys/windows/stdio.rs
new file mode 100644
index 0000000..81b89da
--- /dev/null
+++ b/ctr-std/src/sys/windows/stdio.rs
@@ -0,0 +1,233 @@
+// 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 <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.
+
+#![unstable(issue = "0", feature = "windows_stdio")]
+
+use io::prelude::*;
+
+use cmp;
+use io::{self, Cursor};
+use ptr;
+use str;
+use sync::Mutex;
+use sys::c;
+use sys::cvt;
+use sys::handle::Handle;
+
+pub enum Output {
+ Console(c::HANDLE),
+ Pipe(c::HANDLE),
+}
+
+pub struct Stdin {
+ utf8: Mutex<io::Cursor<Vec<u8>>>,
+}
+pub struct Stdout;
+pub struct Stderr;
+
+pub fn get(handle: c::DWORD) -> io::Result<Output> {
+ let handle = unsafe { c::GetStdHandle(handle) };
+ if handle == c::INVALID_HANDLE_VALUE {
+ Err(io::Error::last_os_error())
+ } else if handle.is_null() {
+ Err(io::Error::from_raw_os_error(c::ERROR_INVALID_HANDLE as i32))
+ } else {
+ let mut out = 0;
+ match unsafe { c::GetConsoleMode(handle, &mut out) } {
+ 0 => Ok(Output::Pipe(handle)),
+ _ => Ok(Output::Console(handle)),
+ }
+ }
+}
+
+fn write(handle: c::DWORD, data: &[u8]) -> io::Result<usize> {
+ let handle = match try!(get(handle)) {
+ Output::Console(c) => c,
+ Output::Pipe(p) => {
+ let handle = Handle::new(p);
+ let ret = handle.write(data);
+ handle.into_raw();
+ return ret
+ }
+ };
+
+ // As with stdin on windows, stdout often can't handle writes of large
+ // sizes. For an example, see #14940. For this reason, don't try to
+ // write the entire output buffer on windows.
+ //
+ // For some other references, it appears that this problem has been
+ // encountered by others [1] [2]. We choose the number 8K just because
+ // libuv does the same.
+ //
+ // [1]: https://tahoe-lafs.org/trac/tahoe-lafs/ticket/1232
+ // [2]: http://www.mail-archive.com/[email protected]/msg00661.html
+ const OUT_MAX: usize = 8192;
+ let len = cmp::min(data.len(), OUT_MAX);
+ let utf8 = match str::from_utf8(&data[..len]) {
+ Ok(s) => s,
+ Err(ref e) if e.valid_up_to() == 0 => return Err(invalid_encoding()),
+ Err(e) => str::from_utf8(&data[..e.valid_up_to()]).unwrap(),
+ };
+ let utf16 = utf8.encode_utf16().collect::<Vec<u16>>();
+ let mut written = 0;
+ cvt(unsafe {
+ c::WriteConsoleW(handle,
+ utf16.as_ptr() as c::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(utf8.len())
+}
+
+impl Stdin {
+ pub fn new() -> io::Result<Stdin> {
+ Ok(Stdin {
+ utf8: Mutex::new(Cursor::new(Vec::new())),
+ })
+ }
+
+ pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
+ let handle = match try!(get(c::STD_INPUT_HANDLE)) {
+ Output::Console(c) => c,
+ Output::Pipe(p) => {
+ let handle = Handle::new(p);
+ let ret = handle.read(buf);
+ handle.into_raw();
+ return ret
+ }
+ };
+ 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![0u16; 0x1000];
+ let mut num = 0;
+ let mut input_control = readconsole_input_control(CTRL_Z_MASK);
+ cvt(unsafe {
+ c::ReadConsoleW(handle,
+ utf16.as_mut_ptr() as c::LPVOID,
+ utf16.len() as u32,
+ &mut num,
+ &mut input_control as c::PCONSOLE_READCONSOLE_CONTROL)
+ })?;
+ utf16.truncate(num as usize);
+ // FIXME: what to do about this data that has already been read?
+ let mut data = match String::from_utf16(&utf16) {
+ Ok(utf8) => utf8.into_bytes(),
+ Err(..) => return Err(invalid_encoding()),
+ };
+ if let Some(&last_byte) = data.last() {
+ if last_byte == CTRL_Z {
+ data.pop();
+ }
+ }
+ *utf8 = Cursor::new(data);
+ }
+
+ // MemReader shouldn't error here since we just filled it
+ utf8.read(buf)
+ }
+
+ pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
+ let mut me = self;
+ (&mut me).read_to_end(buf)
+ }
+}
+
+#[unstable(reason = "not public", issue = "0", feature = "fd_read")]
+impl<'a> Read for &'a Stdin {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ (**self).read(buf)
+ }
+}
+
+impl Stdout {
+ pub fn new() -> io::Result<Stdout> {
+ Ok(Stdout)
+ }
+
+ pub fn write(&self, data: &[u8]) -> io::Result<usize> {
+ write(c::STD_OUTPUT_HANDLE, data)
+ }
+
+ pub fn flush(&self) -> io::Result<()> {
+ Ok(())
+ }
+}
+
+impl Stderr {
+ pub fn new() -> io::Result<Stderr> {
+ Ok(Stderr)
+ }
+
+ pub fn write(&self, data: &[u8]) -> io::Result<usize> {
+ write(c::STD_ERROR_HANDLE, data)
+ }
+
+ pub fn flush(&self) -> io::Result<()> {
+ Ok(())
+ }
+}
+
+// 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<usize> {
+ Stderr::write(self, data)
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ Stderr::flush(self)
+ }
+}
+
+impl Output {
+ pub fn handle(&self) -> c::HANDLE {
+ match *self {
+ Output::Console(c) => c,
+ Output::Pipe(c) => c,
+ }
+ }
+}
+
+fn invalid_encoding() -> io::Error {
+ io::Error::new(io::ErrorKind::InvalidData, "text was not valid unicode")
+}
+
+fn readconsole_input_control(wakeup_mask: c::ULONG) -> c::CONSOLE_READCONSOLE_CONTROL {
+ c::CONSOLE_READCONSOLE_CONTROL {
+ nLength: ::mem::size_of::<c::CONSOLE_READCONSOLE_CONTROL>() as c::ULONG,
+ nInitialChars: 0,
+ dwCtrlWakeupMask: wakeup_mask,
+ dwControlKeyState: 0,
+ }
+}
+
+const CTRL_Z: u8 = 0x1A;
+const CTRL_Z_MASK: c::ULONG = 0x4000000; //1 << 0x1A
+
+pub fn is_ebadf(err: &io::Error) -> bool {
+ err.raw_os_error() == Some(c::ERROR_INVALID_HANDLE as i32)
+}
+
+// The default buffer capacity is 64k, but apparently windows
+// doesn't like 64k reads on stdin. See #13304 for details, but the
+// idea is that on windows we use a slightly smaller buffer that's
+// been seen to be acceptable.
+pub const STDIN_BUF_SIZE: usize = 8 * 1024;
+
+pub fn stderr_prints_nothing() -> bool {
+ false
+}