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 self::StdSource::*;
34 use failure::LOCAL_STDERR;
36 use io::{Reader, Writer, IoResult, IoError, OtherIoError,
37 standard_error, EndOfFile, LineBufferedWriter, BufferedReader};
42 use option::{Option, Some, None};
43 use result::{Ok, Err};
45 use rustrt::local::Local;
46 use rustrt::task::Task;
47 use slice::SlicePrelude;
52 // And so begins the tale of acquiring a uv handle to a stdio stream on all
53 // platforms in all situations. Our story begins by splitting the world into two
54 // categories, windows and unix. Then one day the creators of unix said let
55 // there be redirection! And henceforth there was redirection away from the
56 // console for standard I/O streams.
58 // After this day, the world split into four factions:
60 // 1. Unix with stdout on a terminal.
61 // 2. Unix with stdout redirected.
62 // 3. Windows with stdout on a terminal.
63 // 4. Windows with stdout redirected.
65 // Many years passed, and then one day the nation of libuv decided to unify this
66 // world. After months of toiling, uv created three ideas: TTY, Pipe, File.
67 // These three ideas propagated throughout the lands and the four great factions
68 // decided to settle among them.
70 // The groups of 1, 2, and 3 all worked very hard towards the idea of TTY. Upon
71 // doing so, they even enhanced themselves further then their Pipe/File
72 // brethren, becoming the dominant powers.
74 // The group of 4, however, decided to work independently. They abandoned the
75 // common TTY belief throughout, and even abandoned the fledgling Pipe belief.
76 // The members of the 4th faction decided to only align themselves with File.
78 // tl;dr; TTY works on everything but when windows stdout is redirected, in that
79 // case pipe also doesn't work, but magically file does!
85 fn src<T>(fd: libc::c_int, _readable: bool, f: |StdSource| -> T) -> T {
86 match tty::TTY::new(fd) {
87 Ok(tty) => f(TTY(tty)),
88 Err(_) => f(File(fs::FileDesc::new(fd, false))),
92 thread_local!(static LOCAL_STDOUT: RefCell<Option<Box<Writer + Send>>> = {
96 /// Creates a new non-blocking handle to the stdin of the current process.
98 /// The returned handled is buffered by default with a `BufferedReader`. If
99 /// buffered access is not desired, the `stdin_raw` function is provided to
100 /// provided unbuffered access to stdin.
102 /// Care should be taken when creating multiple handles to the stdin of a
103 /// process. Because this is a buffered reader by default, it's possible for
104 /// pending input to be unconsumed in one reader and unavailable to other
105 /// readers. It is recommended that only one handle at a time is created for the
106 /// stdin of a process.
108 /// See `stdout()` for more notes about this function.
109 pub fn stdin() -> BufferedReader<StdReader> {
110 // The default buffer capacity is 64k, but apparently windows doesn't like
111 // 64k reads on stdin. See #13304 for details, but the idea is that on
112 // windows we use a slightly smaller buffer that's been seen to be
115 BufferedReader::with_capacity(8 * 1024, stdin_raw())
117 BufferedReader::new(stdin_raw())
121 /// Creates a new non-blocking handle to the stdin of the current process.
123 /// Unlike `stdin()`, the returned reader is *not* a buffered reader.
125 /// See `stdout()` for more notes about this function.
126 pub fn stdin_raw() -> StdReader {
127 src(libc::STDIN_FILENO, true, |src| StdReader { inner: src })
130 /// Creates a line-buffered handle to the stdout of the current process.
132 /// Note that this is a fairly expensive operation in that at least one memory
133 /// allocation is performed. Additionally, this must be called from a runtime
134 /// task context because the stream returned will be a non-blocking object using
135 /// the local scheduler to perform the I/O.
137 /// Care should be taken when creating multiple handles to an output stream for
138 /// a single process. While usage is still safe, the output may be surprising if
139 /// no synchronization is performed to ensure a sane output.
140 pub fn stdout() -> LineBufferedWriter<StdWriter> {
141 LineBufferedWriter::new(stdout_raw())
144 /// Creates an unbuffered handle to the stdout of the current process
146 /// See notes in `stdout()` for more information.
147 pub fn stdout_raw() -> StdWriter {
148 src(libc::STDOUT_FILENO, false, |src| StdWriter { inner: src })
151 /// Creates a line-buffered handle to the stderr of the current process.
153 /// See `stdout()` for notes about this function.
154 pub fn stderr() -> LineBufferedWriter<StdWriter> {
155 LineBufferedWriter::new(stderr_raw())
158 /// Creates an unbuffered handle to the stderr of the current process
160 /// See notes in `stdout()` for more information.
161 pub fn stderr_raw() -> StdWriter {
162 src(libc::STDERR_FILENO, false, |src| StdWriter { inner: src })
165 /// Resets the task-local stdout handle to the specified writer
167 /// This will replace the current task's stdout handle, returning the old
168 /// handle. All future calls to `print` and friends will emit their output to
169 /// this specified handle.
171 /// Note that this does not need to be called for all new tasks; the default
172 /// output handle is to the process's stdout stream.
173 pub fn set_stdout(stdout: Box<Writer + Send>) -> Option<Box<Writer + Send>> {
174 let mut new = Some(stdout);
175 LOCAL_STDOUT.with(|slot| {
176 mem::replace(&mut *slot.borrow_mut(), new.take())
177 }).and_then(|mut s| {
183 /// Resets the task-local stderr handle to the specified writer
185 /// This will replace the current task's stderr handle, returning the old
186 /// handle. Currently, the stderr handle is used for printing panic messages
187 /// during task panic.
189 /// Note that this does not need to be called for all new tasks; the default
190 /// output handle is to the process's stderr stream.
191 pub fn set_stderr(stderr: Box<Writer + Send>) -> Option<Box<Writer + Send>> {
192 let mut new = Some(stderr);
193 LOCAL_STDERR.with(|slot| {
194 mem::replace(&mut *slot.borrow_mut(), new.take())
195 }).and_then(|mut s| {
201 // Helper to access the local task's stdout handle
203 // Note that this is not a safe function to expose because you can create an
204 // aliased pointer very easily:
206 // with_task_stdout(|io1| {
207 // with_task_stdout(|io2| {
208 // // io1 aliases io2
211 fn with_task_stdout(f: |&mut Writer| -> IoResult<()>) {
212 let result = if Local::exists(None::<Task>) {
213 let mut my_stdout = LOCAL_STDOUT.with(|slot| {
214 slot.borrow_mut().take()
215 }).unwrap_or_else(|| {
216 box stdout() as Box<Writer + Send>
218 let result = f(&mut *my_stdout);
219 let mut var = Some(my_stdout);
220 LOCAL_STDOUT.with(|slot| {
221 *slot.borrow_mut() = var.take();
225 let mut io = rustrt::Stdout;
226 f(&mut io as &mut Writer)
230 Err(e) => panic!("failed printing to stdout: {}", e),
234 /// Flushes the local task's stdout handle.
236 /// By default, this stream is a line-buffering stream, so flushing may be
237 /// necessary to ensure that all output is printed to the screen (if there are
238 /// no newlines printed).
240 /// Note that logging macros do not use this stream. Using the logging macros
241 /// will emit output to stderr, and while they are line buffered the log
242 /// messages are always terminated in a newline (no need to flush).
244 with_task_stdout(|io| io.flush())
247 /// Prints a string to the stdout of the current process. No newline is emitted
248 /// after the string is printed.
249 pub fn print(s: &str) {
250 with_task_stdout(|io| io.write(s.as_bytes()))
253 /// Prints a string to the stdout of the current process. A literal
254 /// `\n` character is printed to the console after the string.
255 pub fn println(s: &str) {
256 with_task_stdout(|io| {
257 io.write(s.as_bytes()).and_then(|()| io.write(&[b'\n']))
261 /// Similar to `print`, but takes a `fmt::Arguments` structure to be compatible
262 /// with the `format_args!` macro.
263 pub fn print_args(fmt: &fmt::Arguments) {
264 with_task_stdout(|io| write!(io, "{}", fmt))
267 /// Similar to `println`, but takes a `fmt::Arguments` structure to be
268 /// compatible with the `format_args!` macro.
269 pub fn println_args(fmt: &fmt::Arguments) {
270 with_task_stdout(|io| writeln!(io, "{}", fmt))
273 /// Representation of a reader of a standard input stream
274 pub struct StdReader {
279 /// Returns whether this stream is attached to a TTY instance or not.
280 pub fn isatty(&self) -> bool {
288 impl Reader for StdReader {
289 fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
290 let ret = match self.inner {
291 TTY(ref mut tty) => {
292 // Flush the task-local stdout so that weird issues like a
293 // print!'d prompt not being shown until after the user hits
296 tty.read(buf).map(|i| i as uint)
298 File(ref mut file) => file.read(buf).map(|i| i as uint),
301 // When reading a piped stdin, libuv will return 0-length reads when
302 // stdin reaches EOF. For pretty much all other streams it will
303 // return an actual EOF error, but apparently for stdin it's a
304 // little different. Hence, here we convert a 0 length read to an
305 // end-of-file indicator so the caller knows to stop reading.
306 Ok(0) => { Err(standard_error(EndOfFile)) }
307 ret @ Ok(..) | ret @ Err(..) => ret,
312 /// Representation of a writer to a standard output stream
313 pub struct StdWriter {
318 /// Gets the size of this output window, if possible. This is typically used
319 /// when the writer is attached to something like a terminal, this is used
320 /// to fetch the dimensions of the terminal.
322 /// If successful, returns `Ok((width, height))`.
326 /// This function will return an error if the output stream is not actually
327 /// connected to a TTY instance, or if querying the TTY instance fails.
328 pub fn winsize(&mut self) -> IoResult<(int, int)> {
330 TTY(ref mut tty) => {
336 desc: "stream is not a tty",
343 /// Controls whether this output stream is a "raw stream" or simply a normal
348 /// This function will return an error if the output stream is not actually
349 /// connected to a TTY instance, or if querying the TTY instance fails.
350 pub fn set_raw(&mut self, raw: bool) -> IoResult<()> {
352 TTY(ref mut tty) => {
358 desc: "stream is not a tty",
365 /// Returns whether this stream is attached to a TTY instance or not.
366 pub fn isatty(&self) -> bool {
374 impl Writer for StdWriter {
375 fn write(&mut self, buf: &[u8]) -> IoResult<()> {
376 // As with stdin on windows, stdout often can't handle writes of large
377 // sizes. For an example, see #14940. For this reason, chunk the output
378 // buffer on windows, but on unix we can just write the whole buffer all
381 // For some other references, it appears that this problem has been
382 // encountered by others [1] [2]. We choose the number 8KB just because
383 // libuv does the same.
385 // [1]: https://tahoe-lafs.org/trac/tahoe-lafs/ticket/1232
386 // [2]: http://www.mail-archive.com/log4net-dev@logging.apache.org/msg00661.html
387 let max_size = if cfg!(windows) {8192} else {uint::MAX};
388 for chunk in buf.chunks(max_size) {
389 try!(match self.inner {
390 TTY(ref mut tty) => tty.write(chunk),
391 File(ref mut file) => file.write(chunk),
405 // Just make sure we can acquire handles
412 fn capture_stdout() {
413 use io::{ChanReader, ChanWriter};
415 let (tx, rx) = channel();
416 let (mut r, w) = (ChanReader::new(rx), ChanWriter::new(tx));
421 assert_eq!(r.read_to_string().unwrap(), "hello!\n".to_string());
425 fn capture_stderr() {
426 use realstd::comm::channel;
427 use realstd::io::{ChanReader, ChanWriter, Reader};
429 let (tx, rx) = channel();
430 let (mut r, w) = (ChanReader::new(rx), ChanWriter::new(tx));
432 ::realstd::io::stdio::set_stderr(box w);
433 panic!("my special message");
435 let s = r.read_to_string().unwrap();
436 assert!(s.as_slice().contains("my special message"));