1 // Copyright 2013 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.
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.
11 /*! Non-blocking access to stdin, stdout, and stderr.
13 This module provides bindings to the local event loop's TTY interface, using it
14 to offer synchronous but non-blocking versions of stdio. These handles can be
15 inspected for information about terminal dimensions or for related information
16 about the stream or terminal to which it is attached.
21 # #![allow(unused_must_use)]
24 let mut out = io::stdout();
25 out.write(b"Hello, world!");
30 use failure::local_stderr;
32 use io::{Reader, Writer, IoResult, IoError, OtherIoError,
33 standard_error, EndOfFile, LineBufferedWriter, BufferedReader};
37 use option::{Option, Some, None};
39 use result::{Ok, Err};
43 use rt::rtio::{DontClose, IoFactory, LocalIo, RtioFileStream, RtioTTY};
44 use slice::ImmutableVector;
48 // And so begins the tale of acquiring a uv handle to a stdio stream on all
49 // platforms in all situations. Our story begins by splitting the world into two
50 // categories, windows and unix. Then one day the creators of unix said let
51 // there be redirection! And henceforth there was redirection away from the
52 // console for standard I/O streams.
54 // After this day, the world split into four factions:
56 // 1. Unix with stdout on a terminal.
57 // 2. Unix with stdout redirected.
58 // 3. Windows with stdout on a terminal.
59 // 4. Windows with stdout redirected.
61 // Many years passed, and then one day the nation of libuv decided to unify this
62 // world. After months of toiling, uv created three ideas: TTY, Pipe, File.
63 // These three ideas propagated throughout the lands and the four great factions
64 // decided to settle among them.
66 // The groups of 1, 2, and 3 all worked very hard towards the idea of TTY. Upon
67 // doing so, they even enhanced themselves further then their Pipe/File
68 // brethren, becoming the dominant powers.
70 // The group of 4, however, decided to work independently. They abandoned the
71 // common TTY belief throughout, and even abandoned the fledgling Pipe belief.
72 // The members of the 4th faction decided to only align themselves with File.
74 // tl;dr; TTY works on everything but when windows stdout is redirected, in that
75 // case pipe also doesn't work, but magically file does!
77 TTY(Box<RtioTTY + Send>),
78 File(Box<RtioFileStream + Send>),
81 fn src<T>(fd: libc::c_int, readable: bool, f: |StdSource| -> T) -> T {
82 LocalIo::maybe_raise(|io| {
83 Ok(match io.tty_open(fd, readable) {
84 Ok(tty) => f(TTY(tty)),
85 Err(_) => f(File(io.fs_from_raw_fd(fd, DontClose))),
87 }).map_err(IoError::from_rtio_error).unwrap()
90 local_data_key!(local_stdout: Box<Writer + Send>)
92 /// Creates a new non-blocking handle to the stdin of the current process.
94 /// The returned handled is buffered by default with a `BufferedReader`. If
95 /// buffered access is not desired, the `stdin_raw` function is provided to
96 /// provided unbuffered access to stdin.
98 /// Care should be taken when creating multiple handles to the stdin of a
99 /// process. Because this is a buffered reader by default, it's possible for
100 /// pending input to be unconsumed in one reader and unavailable to other
101 /// readers. It is recommended that only one handle at a time is created for the
102 /// stdin of a process.
104 /// See `stdout()` for more notes about this function.
105 pub fn stdin() -> BufferedReader<StdReader> {
106 // The default buffer capacity is 64k, but apparently windows doesn't like
107 // 64k reads on stdin. See #13304 for details, but the idea is that on
108 // windows we use a slightly smaller buffer that's been seen to be
111 BufferedReader::with_capacity(8 * 1024, stdin_raw())
113 BufferedReader::new(stdin_raw())
117 /// Creates a new non-blocking handle to the stdin of the current process.
119 /// Unlike `stdin()`, the returned reader is *not* a buffered reader.
121 /// See `stdout()` for more notes about this function.
122 pub fn stdin_raw() -> StdReader {
123 src(libc::STDIN_FILENO, true, |src| StdReader { inner: src })
126 /// Creates a line-buffered handle to the stdout of the current process.
128 /// Note that this is a fairly expensive operation in that at least one memory
129 /// allocation is performed. Additionally, this must be called from a runtime
130 /// task context because the stream returned will be a non-blocking object using
131 /// the local scheduler to perform the I/O.
133 /// Care should be taken when creating multiple handles to an output stream for
134 /// a single process. While usage is still safe, the output may be surprising if
135 /// no synchronization is performed to ensure a sane output.
136 pub fn stdout() -> LineBufferedWriter<StdWriter> {
137 LineBufferedWriter::new(stdout_raw())
140 /// Creates an unbuffered handle to the stdout of the current process
142 /// See notes in `stdout()` for more information.
143 pub fn stdout_raw() -> StdWriter {
144 src(libc::STDOUT_FILENO, false, |src| StdWriter { inner: src })
147 /// Creates a line-buffered handle to the stderr of the current process.
149 /// See `stdout()` for notes about this function.
150 pub fn stderr() -> LineBufferedWriter<StdWriter> {
151 LineBufferedWriter::new(stderr_raw())
154 /// Creates an unbuffered handle to the stderr of the current process
156 /// See notes in `stdout()` for more information.
157 pub fn stderr_raw() -> StdWriter {
158 src(libc::STDERR_FILENO, false, |src| StdWriter { inner: src })
161 /// Resets the task-local stdout handle to the specified writer
163 /// This will replace the current task's stdout handle, returning the old
164 /// handle. All future calls to `print` and friends will emit their output to
165 /// this specified handle.
167 /// Note that this does not need to be called for all new tasks; the default
168 /// output handle is to the process's stdout stream.
169 pub fn set_stdout(stdout: Box<Writer + Send>) -> Option<Box<Writer + Send>> {
170 local_stdout.replace(Some(stdout)).and_then(|mut s| {
176 /// Resets the task-local stderr handle to the specified writer
178 /// This will replace the current task's stderr handle, returning the old
179 /// handle. Currently, the stderr handle is used for printing failure messages
180 /// during task failure.
182 /// Note that this does not need to be called for all new tasks; the default
183 /// output handle is to the process's stderr stream.
184 pub fn set_stderr(stderr: Box<Writer + Send>) -> Option<Box<Writer + Send>> {
185 local_stderr.replace(Some(stderr)).and_then(|mut s| {
191 // Helper to access the local task's stdout handle
193 // Note that this is not a safe function to expose because you can create an
194 // aliased pointer very easily:
196 // with_task_stdout(|io1| {
197 // with_task_stdout(|io2| {
198 // // io1 aliases io2
201 fn with_task_stdout(f: |&mut Writer| -> IoResult<()>) {
202 let result = if Local::exists(None::<Task>) {
203 let mut my_stdout = local_stdout.replace(None).unwrap_or_else(|| {
204 box stdout() as Box<Writer + Send>
206 let result = f(my_stdout);
207 local_stdout.replace(Some(my_stdout));
210 let mut io = rt::Stdout;
211 f(&mut io as &mut Writer)
215 Err(e) => fail!("failed printing to stdout: {}", e),
219 /// Flushes the local task's stdout handle.
221 /// By default, this stream is a line-buffering stream, so flushing may be
222 /// necessary to ensure that all output is printed to the screen (if there are
223 /// no newlines printed).
225 /// Note that logging macros do not use this stream. Using the logging macros
226 /// will emit output to stderr, and while they are line buffered the log
227 /// messages are always terminated in a newline (no need to flush).
229 with_task_stdout(|io| io.flush())
232 /// Prints a string to the stdout of the current process. No newline is emitted
233 /// after the string is printed.
234 pub fn print(s: &str) {
235 with_task_stdout(|io| io.write(s.as_bytes()))
238 /// Prints a string to the stdout of the current process. A literal
239 /// `\n` character is printed to the console after the string.
240 pub fn println(s: &str) {
241 with_task_stdout(|io| {
242 io.write(s.as_bytes()).and_then(|()| io.write([b'\n']))
246 /// Similar to `print`, but takes a `fmt::Arguments` structure to be compatible
247 /// with the `format_args!` macro.
248 pub fn print_args(fmt: &fmt::Arguments) {
249 with_task_stdout(|io| write!(io, "{}", fmt))
252 /// Similar to `println`, but takes a `fmt::Arguments` structure to be
253 /// compatible with the `format_args!` macro.
254 pub fn println_args(fmt: &fmt::Arguments) {
255 with_task_stdout(|io| writeln!(io, "{}", fmt))
258 /// Representation of a reader of a standard input stream
259 pub struct StdReader {
264 /// Returns whether this stream is attached to a TTY instance or not.
265 pub fn isatty(&self) -> bool {
273 impl Reader for StdReader {
274 fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
275 let ret = match self.inner {
276 TTY(ref mut tty) => {
277 // Flush the task-local stdout so that weird issues like a
278 // print!'d prompt not being shown until after the user hits
283 File(ref mut file) => file.read(buf).map(|i| i as uint),
284 }.map_err(IoError::from_rtio_error);
286 // When reading a piped stdin, libuv will return 0-length reads when
287 // stdin reaches EOF. For pretty much all other streams it will
288 // return an actual EOF error, but apparently for stdin it's a
289 // little different. Hence, here we convert a 0 length read to an
290 // end-of-file indicator so the caller knows to stop reading.
291 Ok(0) => { Err(standard_error(EndOfFile)) }
292 ret @ Ok(..) | ret @ Err(..) => ret,
297 /// Representation of a writer to a standard output stream
298 pub struct StdWriter {
303 /// Gets the size of this output window, if possible. This is typically used
304 /// when the writer is attached to something like a terminal, this is used
305 /// to fetch the dimensions of the terminal.
307 /// If successful, returns `Ok((width, height))`.
311 /// This function will return an error if the output stream is not actually
312 /// connected to a TTY instance, or if querying the TTY instance fails.
313 pub fn winsize(&mut self) -> IoResult<(int, int)> {
315 TTY(ref mut tty) => {
316 tty.get_winsize().map_err(IoError::from_rtio_error)
321 desc: "stream is not a tty",
328 /// Controls whether this output stream is a "raw stream" or simply a normal
333 /// This function will return an error if the output stream is not actually
334 /// connected to a TTY instance, or if querying the TTY instance fails.
335 pub fn set_raw(&mut self, raw: bool) -> IoResult<()> {
337 TTY(ref mut tty) => {
338 tty.set_raw(raw).map_err(IoError::from_rtio_error)
343 desc: "stream is not a tty",
350 /// Returns whether this stream is attached to a TTY instance or not.
351 pub fn isatty(&self) -> bool {
359 impl Writer for StdWriter {
360 fn write(&mut self, buf: &[u8]) -> IoResult<()> {
361 // As with stdin on windows, stdout often can't handle writes of large
362 // sizes. For an example, see #14940. For this reason, chunk the output
363 // buffer on windows, but on unix we can just write the whole buffer all
365 let max_size = if cfg!(windows) {64 * 1024} else {uint::MAX};
366 for chunk in buf.chunks(max_size) {
367 try!(match self.inner {
368 TTY(ref mut tty) => tty.write(chunk),
369 File(ref mut file) => file.write(chunk),
370 }.map_err(IoError::from_rtio_error))
379 // Just make sure we can acquire handles
385 iotest!(fn capture_stdout() {
386 use io::{ChanReader, ChanWriter};
388 let (tx, rx) = channel();
389 let (mut r, w) = (ChanReader::new(rx), ChanWriter::new(tx));
394 assert_eq!(r.read_to_string().unwrap(), "hello!\n".to_string());
397 iotest!(fn capture_stderr() {
398 use realstd::comm::channel;
399 use realstd::io::{Writer, ChanReader, ChanWriter, Reader};
401 let (tx, rx) = channel();
402 let (mut r, w) = (ChanReader::new(rx), ChanWriter::new(tx));
404 ::realstd::io::stdio::set_stderr(box w);
405 fail!("my special message");
407 let s = r.read_to_string().unwrap();
408 assert!(s.as_slice().contains("my special message"));