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(bytes!("Hello, world!"));
30 use failure::local_stderr;
32 use io::{Reader, Writer, IoResult, IoError, OtherIoError,
33 standard_error, EndOfFile, LineBufferedWriter, BufferedReader};
36 use option::{Option, Some, None};
38 use result::{Ok, Err};
42 use rt::rtio::{DontClose, IoFactory, LocalIo, RtioFileStream, RtioTTY};
45 // And so begins the tale of acquiring a uv handle to a stdio stream on all
46 // platforms in all situations. Our story begins by splitting the world into two
47 // categories, windows and unix. Then one day the creators of unix said let
48 // there be redirection! And henceforth there was redirection away from the
49 // console for standard I/O streams.
51 // After this day, the world split into four factions:
53 // 1. Unix with stdout on a terminal.
54 // 2. Unix with stdout redirected.
55 // 3. Windows with stdout on a terminal.
56 // 4. Windows with stdout redirected.
58 // Many years passed, and then one day the nation of libuv decided to unify this
59 // world. After months of toiling, uv created three ideas: TTY, Pipe, File.
60 // These three ideas propagated throughout the lands and the four great factions
61 // decided to settle among them.
63 // The groups of 1, 2, and 3 all worked very hard towards the idea of TTY. Upon
64 // doing so, they even enhanced themselves further then their Pipe/File
65 // brethren, becoming the dominant powers.
67 // The group of 4, however, decided to work independently. They abandoned the
68 // common TTY belief throughout, and even abandoned the fledgling Pipe belief.
69 // The members of the 4th faction decided to only align themselves with File.
71 // tl;dr; TTY works on everything but when windows stdout is redirected, in that
72 // case pipe also doesn't work, but magically file does!
74 TTY(Box<RtioTTY:Send>),
75 File(Box<RtioFileStream:Send>),
78 fn src<T>(fd: libc::c_int, readable: bool, f: |StdSource| -> T) -> T {
79 LocalIo::maybe_raise(|io| {
80 Ok(match io.tty_open(fd, readable) {
81 Ok(tty) => f(TTY(tty)),
82 Err(_) => f(File(io.fs_from_raw_fd(fd, DontClose))),
84 }).map_err(IoError::from_rtio_error).unwrap()
87 local_data_key!(local_stdout: Box<Writer:Send>)
89 /// Creates a new non-blocking handle to the stdin of the current process.
91 /// The returned handled is buffered by default with a `BufferedReader`. If
92 /// buffered access is not desired, the `stdin_raw` function is provided to
93 /// provided unbuffered access to stdin.
95 /// Care should be taken when creating multiple handles to the stdin of a
96 /// process. Because this is a buffered reader by default, it's possible for
97 /// pending input to be unconsumed in one reader and unavailable to other
98 /// readers. It is recommended that only one handle at a time is created for the
99 /// stdin of a process.
101 /// See `stdout()` for more notes about this function.
102 pub fn stdin() -> BufferedReader<StdReader> {
103 // The default buffer capacity is 64k, but apparently windows doesn't like
104 // 64k reads on stdin. See #13304 for details, but the idea is that on
105 // windows we use a slightly smaller buffer that's been seen to be
108 BufferedReader::with_capacity(8 * 1024, stdin_raw())
110 BufferedReader::new(stdin_raw())
114 /// Creates a new non-blocking handle to the stdin of the current process.
116 /// Unlike `stdin()`, the returned reader is *not* a buffered reader.
118 /// See `stdout()` for more notes about this function.
119 pub fn stdin_raw() -> StdReader {
120 src(libc::STDIN_FILENO, true, |src| StdReader { inner: src })
123 /// Creates a line-buffered handle to the stdout of the current process.
125 /// Note that this is a fairly expensive operation in that at least one memory
126 /// allocation is performed. Additionally, this must be called from a runtime
127 /// task context because the stream returned will be a non-blocking object using
128 /// the local scheduler to perform the I/O.
130 /// Care should be taken when creating multiple handles to an output stream for
131 /// a single process. While usage is still safe, the output may be surprising if
132 /// no synchronization is performed to ensure a sane output.
133 pub fn stdout() -> LineBufferedWriter<StdWriter> {
134 LineBufferedWriter::new(stdout_raw())
137 /// Creates an unbuffered handle to the stdout of the current process
139 /// See notes in `stdout()` for more information.
140 pub fn stdout_raw() -> StdWriter {
141 src(libc::STDOUT_FILENO, false, |src| StdWriter { inner: src })
144 /// Creates a line-buffered handle to the stderr of the current process.
146 /// See `stdout()` for notes about this function.
147 pub fn stderr() -> LineBufferedWriter<StdWriter> {
148 LineBufferedWriter::new(stderr_raw())
151 /// Creates an unbuffered handle to the stderr of the current process
153 /// See notes in `stdout()` for more information.
154 pub fn stderr_raw() -> StdWriter {
155 src(libc::STDERR_FILENO, false, |src| StdWriter { inner: src })
158 /// Resets the task-local stdout handle to the specified writer
160 /// This will replace the current task's stdout handle, returning the old
161 /// handle. All future calls to `print` and friends will emit their output to
162 /// this specified handle.
164 /// Note that this does not need to be called for all new tasks; the default
165 /// output handle is to the process's stdout stream.
166 pub fn set_stdout(stdout: Box<Writer:Send>) -> Option<Box<Writer:Send>> {
167 local_stdout.replace(Some(stdout)).and_then(|mut s| {
173 /// Resets the task-local stderr handle to the specified writer
175 /// This will replace the current task's stderr handle, returning the old
176 /// handle. Currently, the stderr handle is used for printing failure messages
177 /// during task failure.
179 /// Note that this does not need to be called for all new tasks; the default
180 /// output handle is to the process's stderr stream.
181 pub fn set_stderr(stderr: Box<Writer:Send>) -> Option<Box<Writer:Send>> {
182 local_stderr.replace(Some(stderr)).and_then(|mut s| {
188 // Helper to access the local task's stdout handle
190 // Note that this is not a safe function to expose because you can create an
191 // aliased pointer very easily:
193 // with_task_stdout(|io1| {
194 // with_task_stdout(|io2| {
195 // // io1 aliases io2
198 fn with_task_stdout(f: |&mut Writer| -> IoResult<()>) {
199 let result = if Local::exists(None::<Task>) {
200 let mut my_stdout = local_stdout.replace(None).unwrap_or_else(|| {
201 box stdout() as Box<Writer:Send>
203 let result = f(my_stdout);
204 local_stdout.replace(Some(my_stdout));
207 let mut io = rt::Stdout;
208 f(&mut io as &mut Writer)
212 Err(e) => fail!("failed printing to stdout: {}", e),
216 /// Flushes the local task's stdout handle.
218 /// By default, this stream is a line-buffering stream, so flushing may be
219 /// necessary to ensure that all output is printed to the screen (if there are
220 /// no newlines printed).
222 /// Note that logging macros do not use this stream. Using the logging macros
223 /// will emit output to stderr, and while they are line buffered the log
224 /// messages are always terminated in a newline (no need to flush).
226 with_task_stdout(|io| io.flush())
229 /// Prints a string to the stdout of the current process. No newline is emitted
230 /// after the string is printed.
231 pub fn print(s: &str) {
232 with_task_stdout(|io| io.write(s.as_bytes()))
235 /// Prints a string as a line. to the stdout of the current process. A literal
236 /// `\n` character is printed to the console after the string.
237 pub fn println(s: &str) {
238 with_task_stdout(|io| {
239 io.write(s.as_bytes()).and_then(|()| io.write(['\n' as u8]))
243 /// Similar to `print`, but takes a `fmt::Arguments` structure to be compatible
244 /// with the `format_args!` macro.
245 pub fn print_args(fmt: &fmt::Arguments) {
246 with_task_stdout(|io| write!(io, "{}", fmt))
249 /// Similar to `println`, but takes a `fmt::Arguments` structure to be
250 /// compatible with the `format_args!` macro.
251 pub fn println_args(fmt: &fmt::Arguments) {
252 with_task_stdout(|io| writeln!(io, "{}", fmt))
255 /// Representation of a reader of a standard input stream
256 pub struct StdReader {
261 /// Returns whether this stream is attached to a TTY instance or not.
262 pub fn isatty(&self) -> bool {
270 impl Reader for StdReader {
271 fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
272 let ret = match self.inner {
273 TTY(ref mut tty) => {
274 // Flush the task-local stdout so that weird issues like a
275 // print!'d prompt not being shown until after the user hits
280 File(ref mut file) => file.read(buf).map(|i| i as uint),
281 }.map_err(IoError::from_rtio_error);
283 // When reading a piped stdin, libuv will return 0-length reads when
284 // stdin reaches EOF. For pretty much all other streams it will
285 // return an actual EOF error, but apparently for stdin it's a
286 // little different. Hence, here we convert a 0 length read to an
287 // end-of-file indicator so the caller knows to stop reading.
288 Ok(0) => { Err(standard_error(EndOfFile)) }
289 ret @ Ok(..) | ret @ Err(..) => ret,
294 /// Representation of a writer to a standard output stream
295 pub struct StdWriter {
300 /// Gets the size of this output window, if possible. This is typically used
301 /// when the writer is attached to something like a terminal, this is used
302 /// to fetch the dimensions of the terminal.
304 /// If successful, returns `Ok((width, height))`.
308 /// This function will return an error if the output stream is not actually
309 /// connected to a TTY instance, or if querying the TTY instance fails.
310 pub fn winsize(&mut self) -> IoResult<(int, int)> {
312 TTY(ref mut tty) => {
313 tty.get_winsize().map_err(IoError::from_rtio_error)
318 desc: "stream is not a tty",
325 /// Controls whether this output stream is a "raw stream" or simply a normal
330 /// This function will return an error if the output stream is not actually
331 /// connected to a TTY instance, or if querying the TTY instance fails.
332 pub fn set_raw(&mut self, raw: bool) -> IoResult<()> {
334 TTY(ref mut tty) => {
335 tty.set_raw(raw).map_err(IoError::from_rtio_error)
340 desc: "stream is not a tty",
347 /// Returns whether this stream is attached to a TTY instance or not.
348 pub fn isatty(&self) -> bool {
356 impl Writer for StdWriter {
357 fn write(&mut self, buf: &[u8]) -> IoResult<()> {
359 TTY(ref mut tty) => tty.write(buf),
360 File(ref mut file) => file.write(buf),
361 }.map_err(IoError::from_rtio_error)
368 // Just make sure we can acquire handles
374 iotest!(fn capture_stdout() {
375 use io::{ChanReader, ChanWriter};
377 let (tx, rx) = channel();
378 let (mut r, w) = (ChanReader::new(rx), ChanWriter::new(tx));
383 assert_eq!(r.read_to_str().unwrap(), "hello!\n".to_string());
386 iotest!(fn capture_stderr() {
387 use realstd::comm::channel;
388 use realstd::io::{Writer, ChanReader, ChanWriter, Reader};
390 let (tx, rx) = channel();
391 let (mut r, w) = (ChanReader::new(rx), ChanWriter::new(tx));
393 ::realstd::io::stdio::set_stderr(box w);
394 fail!("my special message");
396 let s = r.read_to_str().unwrap();
397 assert!(s.as_slice().contains("my special message"));