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!"));
31 use io::{Reader, Writer, IoResult, IoError, OtherIoError,
32 standard_error, EndOfFile, LineBufferedWriter, BufferedReader};
36 use option::{Option, Some, None};
39 use result::{Ok, Err, ResultUnwrap};
42 use rt::rtio::{DontClose, IoFactory, LocalIo, RtioFileStream, RtioTTY};
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!
75 TTY(Box<RtioTTY:Send>),
76 File(Box<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: Box<Writer:Send>,
158 f: |&mut Task, Box<Writer:Send>| -> Option<Box<Writer:Send>>)
159 -> Option<Box<Writer:Send>> {
160 let mut t = Local::borrow(None::<Task>);
161 // Be sure to flush any pending output from the writer
162 match f(&mut *t, w) {
165 // FIXME: is failing right here?
173 /// Resets the task-local stdout handle to the specified writer
175 /// This will replace the current task's stdout handle, returning the old
176 /// handle. All future calls to `print` and friends will emit their output to
177 /// this specified handle.
179 /// Note that this does not need to be called for all new tasks; the default
180 /// output handle is to the process's stdout stream.
181 pub fn set_stdout(stdout: Box<Writer:Send>) -> Option<Box<Writer:Send>> {
182 reset_helper(stdout, |t, w| replace(&mut t.stdout, Some(w)))
185 /// Resets the task-local stderr handle to the specified writer
187 /// This will replace the current task's stderr handle, returning the old
188 /// handle. Currently, the stderr handle is used for printing failure messages
189 /// during task failure.
191 /// Note that this does not need to be called for all new tasks; the default
192 /// output handle is to the process's stderr stream.
193 pub fn set_stderr(stderr: Box<Writer:Send>) -> Option<Box<Writer:Send>> {
194 reset_helper(stderr, |t, w| replace(&mut t.stderr, Some(w)))
197 // Helper to access the local task's stdout handle
199 // Note that this is not a safe function to expose because you can create an
200 // aliased pointer very easily:
202 // with_task_stdout(|io1| {
203 // with_task_stdout(|io2| {
204 // // io1 aliases io2
207 fn with_task_stdout(f: |&mut Writer| -> IoResult<()> ) {
208 let task: Option<Box<Task>> = Local::try_take();
209 let result = match task {
211 // Printing may run arbitrary code, so ensure that the task is in
212 // TLS to allow all std services. Note that this means a print while
213 // printing won't use the task's normal stdout handle, but this is
214 // necessary to ensure safety (no aliasing).
215 let mut my_stdout = task.stdout.take();
218 if my_stdout.is_none() {
219 my_stdout = Some(box stdout() as Box<Writer:Send>);
221 let ret = f(*my_stdout.get_mut_ref());
223 // Note that we need to be careful when putting the stdout handle
224 // back into the task. If the handle was set to `Some` while
225 // printing, then we can run aribitrary code when destroying the
226 // previous handle. This means that the local task needs to be in
227 // TLS while we do this.
229 // To protect against this, we do a little dance in which we
230 // temporarily take the task, swap the handles, put the task in TLS,
231 // and only then drop the previous handle.
232 let prev = replace(&mut Local::borrow(None::<Task>).stdout, my_stdout);
238 let mut io = rt::Stdout;
239 f(&mut io as &mut Writer)
245 Err(e) => fail!("failed printing to stdout: {}", e),
249 /// Flushes the local task's stdout handle.
251 /// By default, this stream is a line-buffering stream, so flushing may be
252 /// necessary to ensure that all output is printed to the screen (if there are
253 /// no newlines printed).
255 /// Note that logging macros do not use this stream. Using the logging macros
256 /// will emit output to stderr, and while they are line buffered the log
257 /// messages are always terminated in a newline (no need to flush).
259 with_task_stdout(|io| io.flush())
262 /// Prints a string to the stdout of the current process. No newline is emitted
263 /// after the string is printed.
264 pub fn print(s: &str) {
265 with_task_stdout(|io| io.write(s.as_bytes()))
268 /// Prints a string as a line. to the stdout of the current process. A literal
269 /// `\n` character is printed to the console after the string.
270 pub fn println(s: &str) {
271 with_task_stdout(|io| {
272 io.write(s.as_bytes()).and_then(|()| io.write(['\n' as u8]))
276 /// Similar to `print`, but takes a `fmt::Arguments` structure to be compatible
277 /// with the `format_args!` macro.
278 pub fn print_args(fmt: &fmt::Arguments) {
279 with_task_stdout(|io| fmt::write(io, fmt))
282 /// Similar to `println`, but takes a `fmt::Arguments` structure to be
283 /// compatible with the `format_args!` macro.
284 pub fn println_args(fmt: &fmt::Arguments) {
285 with_task_stdout(|io| fmt::writeln(io, fmt))
288 /// Representation of a reader of a standard input stream
289 pub struct StdReader {
293 impl Reader for StdReader {
294 fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
295 let ret = match self.inner {
296 TTY(ref mut tty) => {
297 // Flush the task-local stdout so that weird issues like a
298 // print!'d prompt not being shown until after the user hits
303 File(ref mut file) => file.read(buf).map(|i| i as uint),
306 // When reading a piped stdin, libuv will return 0-length reads when
307 // stdin reaches EOF. For pretty much all other streams it will
308 // return an actual EOF error, but apparently for stdin it's a
309 // little different. Hence, here we convert a 0 length read to an
310 // end-of-file indicator so the caller knows to stop reading.
311 Ok(0) => { Err(standard_error(EndOfFile)) }
312 ret @ Ok(..) | ret @ Err(..) => ret,
317 /// Representation of a writer to a standard output stream
318 pub struct StdWriter {
323 /// Gets the size of this output window, if possible. This is typically used
324 /// when the writer is attached to something like a terminal, this is used
325 /// to fetch the dimensions of the terminal.
327 /// If successful, returns `Ok((width, height))`.
331 /// This function will return an error if the output stream is not actually
332 /// connected to a TTY instance, or if querying the TTY instance fails.
333 pub fn winsize(&mut self) -> IoResult<(int, int)> {
335 TTY(ref mut tty) => tty.get_winsize(),
339 desc: "stream is not a tty",
346 /// Controls whether this output stream is a "raw stream" or simply a normal
351 /// This function will return an error if the output stream is not actually
352 /// connected to a TTY instance, or if querying the TTY instance fails.
353 pub fn set_raw(&mut self, raw: bool) -> IoResult<()> {
355 TTY(ref mut tty) => tty.set_raw(raw),
359 desc: "stream is not a tty",
366 /// Returns whether this stream is attached to a TTY instance or not.
367 pub fn isatty(&self) -> bool {
375 impl Writer for StdWriter {
376 fn write(&mut self, buf: &[u8]) -> IoResult<()> {
378 TTY(ref mut tty) => tty.write(buf),
379 File(ref mut file) => file.write(buf),
387 // Just make sure we can acquire handles
393 iotest!(fn capture_stdout() {
394 use io::{ChanReader, ChanWriter};
396 let (tx, rx) = channel();
397 let (mut r, w) = (ChanReader::new(rx), ChanWriter::new(tx));
402 assert_eq!(r.read_to_str().unwrap(), "hello!\n".to_owned());
405 iotest!(fn capture_stderr() {
406 use io::{ChanReader, ChanWriter};
408 let (tx, rx) = channel();
409 let (mut r, w) = (ChanReader::new(rx), ChanWriter::new(tx));
412 fail!("my special message");
414 let s = r.read_to_str().unwrap();
415 assert!(s.contains("my special message"));