]> git.proxmox.com Git - rustc.git/blob - src/libstd/sys/windows/stdio.rs
Imported Upstream version 1.9.0+dfsg1
[rustc.git] / src / libstd / sys / windows / stdio.rs
1 // Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 #![unstable(issue = "0", feature = "windows_stdio")]
12
13 use prelude::v1::*;
14 use io::prelude::*;
15
16 use cmp;
17 use io::{self, Cursor};
18 use ptr;
19 use str;
20 use sync::Mutex;
21 use sys::c;
22 use sys::cvt;
23 use sys::handle::Handle;
24 use sys_common::io::read_to_end_uninitialized;
25
26 pub struct NoClose(Option<Handle>);
27
28 pub enum Output {
29 Console(NoClose),
30 Pipe(NoClose),
31 }
32
33 pub struct Stdin {
34 handle: Output,
35 utf8: Mutex<io::Cursor<Vec<u8>>>,
36 }
37 pub struct Stdout(Output);
38 pub struct Stderr(Output);
39
40 pub fn get(handle: c::DWORD) -> io::Result<Output> {
41 let handle = unsafe { c::GetStdHandle(handle) };
42 if handle == c::INVALID_HANDLE_VALUE {
43 Err(io::Error::last_os_error())
44 } else if handle.is_null() {
45 Err(io::Error::new(io::ErrorKind::Other,
46 "no stdio handle available for this process"))
47 } else {
48 let ret = NoClose::new(handle);
49 let mut out = 0;
50 match unsafe { c::GetConsoleMode(handle, &mut out) } {
51 0 => Ok(Output::Pipe(ret)),
52 _ => Ok(Output::Console(ret)),
53 }
54 }
55 }
56
57 fn write(out: &Output, data: &[u8]) -> io::Result<usize> {
58 let handle = match *out {
59 Output::Console(ref c) => c.get().raw(),
60 Output::Pipe(ref p) => return p.get().write(data),
61 };
62 // As with stdin on windows, stdout often can't handle writes of large
63 // sizes. For an example, see #14940. For this reason, don't try to
64 // write the entire output buffer on windows.
65 //
66 // For some other references, it appears that this problem has been
67 // encountered by others [1] [2]. We choose the number 8K just because
68 // libuv does the same.
69 //
70 // [1]: https://tahoe-lafs.org/trac/tahoe-lafs/ticket/1232
71 // [2]: http://www.mail-archive.com/log4net-dev@logging.apache.org/msg00661.html
72 const OUT_MAX: usize = 8192;
73 let len = cmp::min(data.len(), OUT_MAX);
74 let utf8 = match str::from_utf8(&data[..len]) {
75 Ok(s) => s,
76 Err(ref e) if e.valid_up_to() == 0 => return Err(invalid_encoding()),
77 Err(e) => str::from_utf8(&data[..e.valid_up_to()]).unwrap(),
78 };
79 let utf16 = utf8.encode_utf16().collect::<Vec<u16>>();
80 let mut written = 0;
81 cvt(unsafe {
82 c::WriteConsoleW(handle,
83 utf16.as_ptr() as c::LPCVOID,
84 utf16.len() as u32,
85 &mut written,
86 ptr::null_mut())
87 })?;
88
89 // FIXME if this only partially writes the utf16 buffer then we need to
90 // figure out how many bytes of `data` were actually written
91 assert_eq!(written as usize, utf16.len());
92 Ok(utf8.len())
93 }
94
95 impl Stdin {
96 pub fn new() -> io::Result<Stdin> {
97 get(c::STD_INPUT_HANDLE).map(|handle| {
98 Stdin {
99 handle: handle,
100 utf8: Mutex::new(Cursor::new(Vec::new())),
101 }
102 })
103 }
104
105 pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
106 let handle = match self.handle {
107 Output::Console(ref c) => c.get().raw(),
108 Output::Pipe(ref p) => return p.get().read(buf),
109 };
110 let mut utf8 = self.utf8.lock().unwrap();
111 // Read more if the buffer is empty
112 if utf8.position() as usize == utf8.get_ref().len() {
113 let mut utf16 = vec![0u16; 0x1000];
114 let mut num = 0;
115 cvt(unsafe {
116 c::ReadConsoleW(handle,
117 utf16.as_mut_ptr() as c::LPVOID,
118 utf16.len() as u32,
119 &mut num,
120 ptr::null_mut())
121 })?;
122 utf16.truncate(num as usize);
123 // FIXME: what to do about this data that has already been read?
124 let data = match String::from_utf16(&utf16) {
125 Ok(utf8) => utf8.into_bytes(),
126 Err(..) => return Err(invalid_encoding()),
127 };
128 *utf8 = Cursor::new(data);
129 }
130
131 // MemReader shouldn't error here since we just filled it
132 utf8.read(buf)
133 }
134
135 pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
136 let mut me = self;
137 (&mut me).read_to_end(buf)
138 }
139 }
140
141 #[unstable(reason = "not public", issue = "0", feature = "fd_read")]
142 impl<'a> Read for &'a Stdin {
143 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
144 (**self).read(buf)
145 }
146
147 fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
148 unsafe { read_to_end_uninitialized(self, buf) }
149 }
150 }
151
152 impl Stdout {
153 pub fn new() -> io::Result<Stdout> {
154 get(c::STD_OUTPUT_HANDLE).map(Stdout)
155 }
156
157 pub fn write(&self, data: &[u8]) -> io::Result<usize> {
158 write(&self.0, data)
159 }
160 }
161
162 impl Stderr {
163 pub fn new() -> io::Result<Stderr> {
164 get(c::STD_ERROR_HANDLE).map(Stderr)
165 }
166
167 pub fn write(&self, data: &[u8]) -> io::Result<usize> {
168 write(&self.0, data)
169 }
170 }
171
172 // FIXME: right now this raw stderr handle is used in a few places because
173 // std::io::stderr_raw isn't exposed, but once that's exposed this impl
174 // should go away
175 impl io::Write for Stderr {
176 fn write(&mut self, data: &[u8]) -> io::Result<usize> {
177 Stderr::write(self, data)
178 }
179 fn flush(&mut self) -> io::Result<()> { Ok(()) }
180 }
181
182 impl NoClose {
183 fn new(handle: c::HANDLE) -> NoClose {
184 NoClose(Some(Handle::new(handle)))
185 }
186
187 fn get(&self) -> &Handle { self.0.as_ref().unwrap() }
188 }
189
190 impl Drop for NoClose {
191 fn drop(&mut self) {
192 self.0.take().unwrap().into_raw();
193 }
194 }
195
196 impl Output {
197 pub fn handle(&self) -> &Handle {
198 let nc = match *self {
199 Output::Console(ref c) => c,
200 Output::Pipe(ref c) => c,
201 };
202 nc.0.as_ref().unwrap()
203 }
204 }
205
206 fn invalid_encoding() -> io::Error {
207 io::Error::new(io::ErrorKind::InvalidData, "text was not valid unicode")
208 }