]> git.lizzy.rs Git - rust.git/blob - src/libstd/io/stdio.rs
std: Add a new top-level thread_local module
[rust.git] / src / libstd / io / stdio.rs
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.
4 //
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.
10
11 /*! Non-blocking access to stdin, stdout, and stderr.
12
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.
17
18 # Example
19
20 ```rust
21 # #![allow(unused_must_use)]
22 use std::io;
23
24 let mut out = io::stdout();
25 out.write(b"Hello, world!");
26 ```
27
28 */
29
30 use self::StdSource::*;
31
32 use boxed::Box;
33 use cell::RefCell;
34 use failure::LOCAL_STDERR;
35 use fmt;
36 use io::{Reader, Writer, IoResult, IoError, OtherIoError,
37          standard_error, EndOfFile, LineBufferedWriter, BufferedReader};
38 use iter::Iterator;
39 use kinds::Send;
40 use libc;
41 use mem;
42 use option::{Option, Some, None};
43 use result::{Ok, Err};
44 use rustrt;
45 use rustrt::local::Local;
46 use rustrt::task::Task;
47 use slice::SlicePrelude;
48 use str::StrPrelude;
49 use sys::{fs, tty};
50 use uint;
51
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.
57 //
58 // After this day, the world split into four factions:
59 //
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.
64 //
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.
69 //
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.
73 //
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.
77 //
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!
80 enum StdSource {
81     TTY(tty::TTY),
82     File(fs::FileDesc),
83 }
84
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))),
89     }
90 }
91
92 thread_local!(static LOCAL_STDOUT: RefCell<Option<Box<Writer + Send>>> = {
93     RefCell::new(None)
94 })
95
96 /// Creates a new non-blocking handle to the stdin of the current process.
97 ///
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.
101 ///
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.
107 ///
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
113     // acceptable.
114     if cfg!(windows) {
115         BufferedReader::with_capacity(8 * 1024, stdin_raw())
116     } else {
117         BufferedReader::new(stdin_raw())
118     }
119 }
120
121 /// Creates a new non-blocking handle to the stdin of the current process.
122 ///
123 /// Unlike `stdin()`, the returned reader is *not* a buffered reader.
124 ///
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 })
128 }
129
130 /// Creates a line-buffered handle to the stdout of the current process.
131 ///
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.
136 ///
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())
142 }
143
144 /// Creates an unbuffered handle to the stdout of the current process
145 ///
146 /// See notes in `stdout()` for more information.
147 pub fn stdout_raw() -> StdWriter {
148     src(libc::STDOUT_FILENO, false, |src| StdWriter { inner: src })
149 }
150
151 /// Creates a line-buffered handle to the stderr of the current process.
152 ///
153 /// See `stdout()` for notes about this function.
154 pub fn stderr() -> LineBufferedWriter<StdWriter> {
155     LineBufferedWriter::new(stderr_raw())
156 }
157
158 /// Creates an unbuffered handle to the stderr of the current process
159 ///
160 /// See notes in `stdout()` for more information.
161 pub fn stderr_raw() -> StdWriter {
162     src(libc::STDERR_FILENO, false, |src| StdWriter { inner: src })
163 }
164
165 /// Resets the task-local stdout handle to the specified writer
166 ///
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.
170 ///
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| {
178         let _ = s.flush();
179         Some(s)
180     })
181 }
182
183 /// Resets the task-local stderr handle to the specified writer
184 ///
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.
188 ///
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| {
196         let _ = s.flush();
197         Some(s)
198     })
199 }
200
201 // Helper to access the local task's stdout handle
202 //
203 // Note that this is not a safe function to expose because you can create an
204 // aliased pointer very easily:
205 //
206 //  with_task_stdout(|io1| {
207 //      with_task_stdout(|io2| {
208 //          // io1 aliases io2
209 //      })
210 //  })
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>
217         });
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();
222         });
223         result
224     } else {
225         let mut io = rustrt::Stdout;
226         f(&mut io as &mut Writer)
227     };
228     match result {
229         Ok(()) => {}
230         Err(e) => panic!("failed printing to stdout: {}", e),
231     }
232 }
233
234 /// Flushes the local task's stdout handle.
235 ///
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).
239 ///
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).
243 pub fn flush() {
244     with_task_stdout(|io| io.flush())
245 }
246
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()))
251 }
252
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']))
258     })
259 }
260
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))
265 }
266
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))
271 }
272
273 /// Representation of a reader of a standard input stream
274 pub struct StdReader {
275     inner: StdSource
276 }
277
278 impl StdReader {
279     /// Returns whether this stream is attached to a TTY instance or not.
280     pub fn isatty(&self) -> bool {
281         match self.inner {
282             TTY(..) => true,
283             File(..) => false,
284         }
285     }
286 }
287
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
294                 // enter.
295                 flush();
296                 tty.read(buf).map(|i| i as uint)
297             },
298             File(ref mut file) => file.read(buf).map(|i| i as uint),
299         };
300         match ret {
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,
308         }
309     }
310 }
311
312 /// Representation of a writer to a standard output stream
313 pub struct StdWriter {
314     inner: StdSource
315 }
316
317 impl 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.
321     ///
322     /// If successful, returns `Ok((width, height))`.
323     ///
324     /// # Error
325     ///
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)> {
329         match self.inner {
330             TTY(ref mut tty) => {
331                 tty.get_winsize()
332             }
333             File(..) => {
334                 Err(IoError {
335                     kind: OtherIoError,
336                     desc: "stream is not a tty",
337                     detail: None,
338                 })
339             }
340         }
341     }
342
343     /// Controls whether this output stream is a "raw stream" or simply a normal
344     /// stream.
345     ///
346     /// # Error
347     ///
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<()> {
351         match self.inner {
352             TTY(ref mut tty) => {
353                 tty.set_raw(raw)
354             }
355             File(..) => {
356                 Err(IoError {
357                     kind: OtherIoError,
358                     desc: "stream is not a tty",
359                     detail: None,
360                 })
361             }
362         }
363     }
364
365     /// Returns whether this stream is attached to a TTY instance or not.
366     pub fn isatty(&self) -> bool {
367         match self.inner {
368             TTY(..) => true,
369             File(..) => false,
370         }
371     }
372 }
373
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
379         // at once.
380         //
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.
384         //
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),
392             })
393         }
394         Ok(())
395     }
396 }
397
398 #[cfg(test)]
399 mod tests {
400     use super::*;
401     use prelude::*;
402
403     #[test]
404     fn smoke() {
405         // Just make sure we can acquire handles
406         stdin();
407         stdout();
408         stderr();
409     }
410
411     #[test]
412     fn capture_stdout() {
413         use io::{ChanReader, ChanWriter};
414
415         let (tx, rx) = channel();
416         let (mut r, w) = (ChanReader::new(rx), ChanWriter::new(tx));
417         spawn(proc() {
418             set_stdout(box w);
419             println!("hello!");
420         });
421         assert_eq!(r.read_to_string().unwrap(), "hello!\n".to_string());
422     }
423
424     #[test]
425     fn capture_stderr() {
426         use realstd::comm::channel;
427         use realstd::io::{ChanReader, ChanWriter, Reader};
428
429         let (tx, rx) = channel();
430         let (mut r, w) = (ChanReader::new(rx), ChanWriter::new(tx));
431         spawn(proc() {
432             ::realstd::io::stdio::set_stderr(box w);
433             panic!("my special message");
434         });
435         let s = r.read_to_string().unwrap();
436         assert!(s.as_slice().contains("my special message"));
437     }
438 }