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 container::Container;
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};
41 use rt::rtio::{DontClose, IoFactory, LocalIo, RtioFileStream, RtioTTY};
44 use slice::ImmutableVector;
46 // And so begins the tale of acquiring a uv handle to a stdio stream on all
47 // platforms in all situations. Our story begins by splitting the world into two
48 // categories, windows and unix. Then one day the creators of unix said let
49 // there be redirection! And henceforth there was redirection away from the
50 // console for standard I/O streams.
52 // After this day, the world split into four factions:
54 // 1. Unix with stdout on a terminal.
55 // 2. Unix with stdout redirected.
56 // 3. Windows with stdout on a terminal.
57 // 4. Windows with stdout redirected.
59 // Many years passed, and then one day the nation of libuv decided to unify this
60 // world. After months of toiling, uv created three ideas: TTY, Pipe, File.
61 // These three ideas propagated throughout the lands and the four great factions
62 // decided to settle among them.
64 // The groups of 1, 2, and 3 all worked very hard towards the idea of TTY. Upon
65 // doing so, they even enhanced themselves further then their Pipe/File
66 // brethren, becoming the dominant powers.
68 // The group of 4, however, decided to work independently. They abandoned the
69 // common TTY belief throughout, and even abandoned the fledgling Pipe belief.
70 // The members of the 4th faction decided to only align themselves with File.
72 // tl;dr; TTY works on everything but when windows stdout is redirected, in that
73 // case pipe also doesn't work, but magically file does!
76 File(~RtioFileStream:Send),
79 fn src<T>(fd: libc::c_int, readable: bool, f: |StdSource| -> T) -> T {
80 LocalIo::maybe_raise(|io| {
81 Ok(match io.tty_open(fd, readable) {
82 Ok(tty) => f(TTY(tty)),
83 Err(_) => f(File(io.fs_from_raw_fd(fd, DontClose))),
88 /// Creates a new non-blocking handle to the stdin of the current process.
90 /// The returned handled is buffered by default with a `BufferedReader`. If
91 /// buffered access is not desired, the `stdin_raw` function is provided to
92 /// provided unbuffered access to stdin.
94 /// Care should be taken when creating multiple handles to the stdin of a
95 /// process. Beause this is a buffered reader by default, it's possible for
96 /// pending input to be unconsumed in one reader and unavailable to other
97 /// readers. It is recommended that only one handle at a time is created for the
98 /// stdin of a process.
100 /// See `stdout()` for more notes about this function.
101 pub fn stdin() -> BufferedReader<StdReader> {
102 // The default buffer capacity is 64k, but apparently windows doesn't like
103 // 64k reads on stdin. See #13304 for details, but the idea is that on
104 // windows we use a slighly smaller buffer that's been seen to be
107 BufferedReader::with_capacity(8 * 1024, stdin_raw())
109 BufferedReader::new(stdin_raw())
113 /// Creates a new non-blocking handle to the stdin of the current process.
115 /// Unlike `stdin()`, the returned reader is *not* a buffered reader.
117 /// See `stdout()` for more notes about this function.
118 pub fn stdin_raw() -> StdReader {
119 src(libc::STDIN_FILENO, true, |src| StdReader { inner: src })
122 /// Creates a line-buffered handle to the stdout of the current process.
124 /// Note that this is a fairly expensive operation in that at least one memory
125 /// allocation is performed. Additionally, this must be called from a runtime
126 /// task context because the stream returned will be a non-blocking object using
127 /// the local scheduler to perform the I/O.
129 /// Care should be taken when creating multiple handles to an output stream for
130 /// a single process. While usage is still safe, the output may be surprising if
131 /// no synchronization is performed to ensure a sane output.
132 pub fn stdout() -> LineBufferedWriter<StdWriter> {
133 LineBufferedWriter::new(stdout_raw())
136 /// Creates an unbuffered handle to the stdout of the current process
138 /// See notes in `stdout()` for more information.
139 pub fn stdout_raw() -> StdWriter {
140 src(libc::STDOUT_FILENO, false, |src| StdWriter { inner: src })
143 /// Creates a line-buffered handle to the stderr of the current process.
145 /// See `stdout()` for notes about this function.
146 pub fn stderr() -> LineBufferedWriter<StdWriter> {
147 LineBufferedWriter::new(stderr_raw())
150 /// Creates an unbuffered handle to the stderr of the current process
152 /// See notes in `stdout()` for more information.
153 pub fn stderr_raw() -> StdWriter {
154 src(libc::STDERR_FILENO, false, |src| StdWriter { inner: src })
157 fn reset_helper(w: ~Writer:Send,
158 f: |&mut Task, ~Writer:Send| -> Option<~Writer:Send>)
159 -> Option<~Writer:Send>
161 let mut t = Local::borrow(None::<Task>);
162 // Be sure to flush any pending output from the writer
163 match f(&mut *t, w) {
166 // FIXME: is failing right here?
174 /// Resets the task-local stdout handle to the specified writer
176 /// This will replace the current task's stdout handle, returning the old
177 /// handle. All future calls to `print` and friends will emit their output to
178 /// this specified handle.
180 /// Note that this does not need to be called for all new tasks; the default
181 /// output handle is to the process's stdout stream.
182 pub fn set_stdout(stdout: ~Writer:Send) -> Option<~Writer:Send> {
183 reset_helper(stdout, |t, w| replace(&mut t.stdout, Some(w)))
186 /// Resets the task-local stderr handle to the specified writer
188 /// This will replace the current task's stderr handle, returning the old
189 /// handle. Currently, the stderr handle is used for printing failure messages
190 /// during task failure.
192 /// Note that this does not need to be called for all new tasks; the default
193 /// output handle is to the process's stderr stream.
194 pub fn set_stderr(stderr: ~Writer:Send) -> Option<~Writer:Send> {
195 reset_helper(stderr, |t, w| replace(&mut t.stderr, Some(w)))
198 // Helper to access the local task's stdout handle
200 // Note that this is not a safe function to expose because you can create an
201 // aliased pointer very easily:
203 // with_task_stdout(|io1| {
204 // with_task_stdout(|io2| {
205 // // io1 aliases io2
208 fn with_task_stdout(f: |&mut Writer| -> IoResult<()> ) {
209 let task: Option<~Task> = Local::try_take();
210 let result = match task {
212 // Printing may run arbitrary code, so ensure that the task is in
213 // TLS to allow all std services. Note that this means a print while
214 // printing won't use the task's normal stdout handle, but this is
215 // necessary to ensure safety (no aliasing).
216 let mut my_stdout = task.stdout.take();
219 if my_stdout.is_none() {
220 my_stdout = Some(~stdout() as ~Writer:Send);
222 let ret = f(*my_stdout.get_mut_ref());
224 // Note that we need to be careful when putting the stdout handle
225 // back into the task. If the handle was set to `Some` while
226 // printing, then we can run aribitrary code when destroying the
227 // previous handle. This means that the local task needs to be in
228 // TLS while we do this.
230 // To protect against this, we do a little dance in which we
231 // temporarily take the task, swap the handles, put the task in TLS,
232 // and only then drop the previous handle.
233 let prev = replace(&mut Local::borrow(None::<Task>).stdout, my_stdout);
240 impl Writer for Stdout {
241 fn write(&mut self, data: &[u8]) -> IoResult<()> {
243 libc::write(libc::STDOUT_FILENO,
244 data.as_ptr() as *libc::c_void,
245 data.len() as libc::size_t);
247 Ok(()) // just ignore the results
251 f(&mut io as &mut Writer)
257 Err(e) => fail!("failed printing to stdout: {}", e),
261 /// Flushes the local task's stdout handle.
263 /// By default, this stream is a line-buffering stream, so flushing may be
264 /// necessary to ensure that all output is printed to the screen (if there are
265 /// no newlines printed).
267 /// Note that logging macros do not use this stream. Using the logging macros
268 /// will emit output to stderr, and while they are line buffered the log
269 /// messages are always terminated in a newline (no need to flush).
271 with_task_stdout(|io| io.flush())
274 /// Prints a string to the stdout of the current process. No newline is emitted
275 /// after the string is printed.
276 pub fn print(s: &str) {
277 with_task_stdout(|io| io.write(s.as_bytes()))
280 /// Prints a string as a line. to the stdout of the current process. A literal
281 /// `\n` character is printed to the console after the string.
282 pub fn println(s: &str) {
283 with_task_stdout(|io| {
284 io.write(s.as_bytes()).and_then(|()| io.write(['\n' as u8]))
288 /// Similar to `print`, but takes a `fmt::Arguments` structure to be compatible
289 /// with the `format_args!` macro.
290 pub fn print_args(fmt: &fmt::Arguments) {
291 with_task_stdout(|io| fmt::write(io, fmt))
294 /// Similar to `println`, but takes a `fmt::Arguments` structure to be
295 /// compatible with the `format_args!` macro.
296 pub fn println_args(fmt: &fmt::Arguments) {
297 with_task_stdout(|io| fmt::writeln(io, fmt))
300 /// Representation of a reader of a standard input stream
301 pub struct StdReader {
305 impl Reader for StdReader {
306 fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
307 let ret = match self.inner {
308 TTY(ref mut tty) => {
309 // Flush the task-local stdout so that weird issues like a
310 // print!'d prompt not being shown until after the user hits
315 File(ref mut file) => file.read(buf).map(|i| i as uint),
318 // When reading a piped stdin, libuv will return 0-length reads when
319 // stdin reaches EOF. For pretty much all other streams it will
320 // return an actual EOF error, but apparently for stdin it's a
321 // little different. Hence, here we convert a 0 length read to an
322 // end-of-file indicator so the caller knows to stop reading.
323 Ok(0) => { Err(standard_error(EndOfFile)) }
324 ret @ Ok(..) | ret @ Err(..) => ret,
329 /// Representation of a writer to a standard output stream
330 pub struct StdWriter {
335 /// Gets the size of this output window, if possible. This is typically used
336 /// when the writer is attached to something like a terminal, this is used
337 /// to fetch the dimensions of the terminal.
339 /// If successful, returns `Ok((width, height))`.
343 /// This function will return an error if the output stream is not actually
344 /// connected to a TTY instance, or if querying the TTY instance fails.
345 pub fn winsize(&mut self) -> IoResult<(int, int)> {
347 TTY(ref mut tty) => tty.get_winsize(),
351 desc: "stream is not a tty",
358 /// Controls whether this output stream is a "raw stream" or simply a normal
363 /// This function will return an error if the output stream is not actually
364 /// connected to a TTY instance, or if querying the TTY instance fails.
365 pub fn set_raw(&mut self, raw: bool) -> IoResult<()> {
367 TTY(ref mut tty) => tty.set_raw(raw),
371 desc: "stream is not a tty",
378 /// Returns whether this stream is attached to a TTY instance or not.
379 pub fn isatty(&self) -> bool {
387 impl Writer for StdWriter {
388 fn write(&mut self, buf: &[u8]) -> IoResult<()> {
390 TTY(ref mut tty) => tty.write(buf),
391 File(ref mut file) => file.write(buf),
399 // Just make sure we can acquire handles
405 iotest!(fn capture_stdout() {
406 use io::{ChanReader, ChanWriter};
408 let (tx, rx) = channel();
409 let (mut r, w) = (ChanReader::new(rx), ChanWriter::new(tx));
414 assert_eq!(r.read_to_str().unwrap(), "hello!\n".to_owned());
417 iotest!(fn capture_stderr() {
418 use io::{ChanReader, ChanWriter};
420 let (tx, rx) = channel();
421 let (mut r, w) = (ChanReader::new(rx), ChanWriter::new(tx));
424 fail!("my special message");
426 let s = r.read_to_str().unwrap();
427 assert!(s.contains("my special message"));