]> git.lizzy.rs Git - rust.git/blob - src/libstd/io/stdio.rs
7374668a69d834c1cbfa961e1a20ab2ace2c990e
[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 failure::local_stderr;
33 use fmt;
34 use io::{Reader, Writer, IoResult, IoError, OtherIoError,
35          standard_error, EndOfFile, LineBufferedWriter, BufferedReader};
36 use iter::Iterator;
37 use kinds::Send;
38 use libc;
39 use option::{Option, Some, None};
40 use boxed::Box;
41 use sys::{fs, tty};
42 use result::{Ok, Err};
43 use rustrt;
44 use rustrt::local::Local;
45 use rustrt::task::Task;
46 use slice::SlicePrelude;
47 use str::StrPrelude;
48 use uint;
49
50 // And so begins the tale of acquiring a uv handle to a stdio stream on all
51 // platforms in all situations. Our story begins by splitting the world into two
52 // categories, windows and unix. Then one day the creators of unix said let
53 // there be redirection! And henceforth there was redirection away from the
54 // console for standard I/O streams.
55 //
56 // After this day, the world split into four factions:
57 //
58 // 1. Unix with stdout on a terminal.
59 // 2. Unix with stdout redirected.
60 // 3. Windows with stdout on a terminal.
61 // 4. Windows with stdout redirected.
62 //
63 // Many years passed, and then one day the nation of libuv decided to unify this
64 // world. After months of toiling, uv created three ideas: TTY, Pipe, File.
65 // These three ideas propagated throughout the lands and the four great factions
66 // decided to settle among them.
67 //
68 // The groups of 1, 2, and 3 all worked very hard towards the idea of TTY. Upon
69 // doing so, they even enhanced themselves further then their Pipe/File
70 // brethren, becoming the dominant powers.
71 //
72 // The group of 4, however, decided to work independently. They abandoned the
73 // common TTY belief throughout, and even abandoned the fledgling Pipe belief.
74 // The members of the 4th faction decided to only align themselves with File.
75 //
76 // tl;dr; TTY works on everything but when windows stdout is redirected, in that
77 //        case pipe also doesn't work, but magically file does!
78 enum StdSource {
79     TTY(tty::TTY),
80     File(fs::FileDesc),
81 }
82
83 fn src<T>(fd: libc::c_int, _readable: bool, f: |StdSource| -> T) -> T {
84     match tty::TTY::new(fd) {
85         Ok(tty) => f(TTY(tty)),
86         Err(_) => f(File(fs::FileDesc::new(fd, false))),
87     }
88 }
89
90 local_data_key!(local_stdout: Box<Writer + Send>)
91
92 /// Creates a new non-blocking handle to the stdin of the current process.
93 ///
94 /// The returned handled is buffered by default with a `BufferedReader`. If
95 /// buffered access is not desired, the `stdin_raw` function is provided to
96 /// provided unbuffered access to stdin.
97 ///
98 /// Care should be taken when creating multiple handles to the stdin of a
99 /// process. Because this is a buffered reader by default, it's possible for
100 /// pending input to be unconsumed in one reader and unavailable to other
101 /// readers. It is recommended that only one handle at a time is created for the
102 /// stdin of a process.
103 ///
104 /// See `stdout()` for more notes about this function.
105 pub fn stdin() -> BufferedReader<StdReader> {
106     // The default buffer capacity is 64k, but apparently windows doesn't like
107     // 64k reads on stdin. See #13304 for details, but the idea is that on
108     // windows we use a slightly smaller buffer that's been seen to be
109     // acceptable.
110     if cfg!(windows) {
111         BufferedReader::with_capacity(8 * 1024, stdin_raw())
112     } else {
113         BufferedReader::new(stdin_raw())
114     }
115 }
116
117 /// Creates a new non-blocking handle to the stdin of the current process.
118 ///
119 /// Unlike `stdin()`, the returned reader is *not* a buffered reader.
120 ///
121 /// See `stdout()` for more notes about this function.
122 pub fn stdin_raw() -> StdReader {
123     src(libc::STDIN_FILENO, true, |src| StdReader { inner: src })
124 }
125
126 /// Creates a line-buffered handle to the stdout of the current process.
127 ///
128 /// Note that this is a fairly expensive operation in that at least one memory
129 /// allocation is performed. Additionally, this must be called from a runtime
130 /// task context because the stream returned will be a non-blocking object using
131 /// the local scheduler to perform the I/O.
132 ///
133 /// Care should be taken when creating multiple handles to an output stream for
134 /// a single process. While usage is still safe, the output may be surprising if
135 /// no synchronization is performed to ensure a sane output.
136 pub fn stdout() -> LineBufferedWriter<StdWriter> {
137     LineBufferedWriter::new(stdout_raw())
138 }
139
140 /// Creates an unbuffered handle to the stdout of the current process
141 ///
142 /// See notes in `stdout()` for more information.
143 pub fn stdout_raw() -> StdWriter {
144     src(libc::STDOUT_FILENO, false, |src| StdWriter { inner: src })
145 }
146
147 /// Creates a line-buffered handle to the stderr of the current process.
148 ///
149 /// See `stdout()` for notes about this function.
150 pub fn stderr() -> LineBufferedWriter<StdWriter> {
151     LineBufferedWriter::new(stderr_raw())
152 }
153
154 /// Creates an unbuffered handle to the stderr of the current process
155 ///
156 /// See notes in `stdout()` for more information.
157 pub fn stderr_raw() -> StdWriter {
158     src(libc::STDERR_FILENO, false, |src| StdWriter { inner: src })
159 }
160
161 /// Resets the task-local stdout handle to the specified writer
162 ///
163 /// This will replace the current task's stdout handle, returning the old
164 /// handle. All future calls to `print` and friends will emit their output to
165 /// this specified handle.
166 ///
167 /// Note that this does not need to be called for all new tasks; the default
168 /// output handle is to the process's stdout stream.
169 pub fn set_stdout(stdout: Box<Writer + Send>) -> Option<Box<Writer + Send>> {
170     local_stdout.replace(Some(stdout)).and_then(|mut s| {
171         let _ = s.flush();
172         Some(s)
173     })
174 }
175
176 /// Resets the task-local stderr handle to the specified writer
177 ///
178 /// This will replace the current task's stderr handle, returning the old
179 /// handle. Currently, the stderr handle is used for printing panic messages
180 /// during task panic.
181 ///
182 /// Note that this does not need to be called for all new tasks; the default
183 /// output handle is to the process's stderr stream.
184 pub fn set_stderr(stderr: Box<Writer + Send>) -> Option<Box<Writer + Send>> {
185     local_stderr.replace(Some(stderr)).and_then(|mut s| {
186         let _ = s.flush();
187         Some(s)
188     })
189 }
190
191 // Helper to access the local task's stdout handle
192 //
193 // Note that this is not a safe function to expose because you can create an
194 // aliased pointer very easily:
195 //
196 //  with_task_stdout(|io1| {
197 //      with_task_stdout(|io2| {
198 //          // io1 aliases io2
199 //      })
200 //  })
201 fn with_task_stdout(f: |&mut Writer| -> IoResult<()>) {
202     let result = if Local::exists(None::<Task>) {
203         let mut my_stdout = local_stdout.replace(None).unwrap_or_else(|| {
204             box stdout() as Box<Writer + Send>
205         });
206         let result = f(&mut *my_stdout);
207         local_stdout.replace(Some(my_stdout));
208         result
209     } else {
210         let mut io = rustrt::Stdout;
211         f(&mut io as &mut Writer)
212     };
213     match result {
214         Ok(()) => {}
215         Err(e) => panic!("failed printing to stdout: {}", e),
216     }
217 }
218
219 /// Flushes the local task's stdout handle.
220 ///
221 /// By default, this stream is a line-buffering stream, so flushing may be
222 /// necessary to ensure that all output is printed to the screen (if there are
223 /// no newlines printed).
224 ///
225 /// Note that logging macros do not use this stream. Using the logging macros
226 /// will emit output to stderr, and while they are line buffered the log
227 /// messages are always terminated in a newline (no need to flush).
228 pub fn flush() {
229     with_task_stdout(|io| io.flush())
230 }
231
232 /// Prints a string to the stdout of the current process. No newline is emitted
233 /// after the string is printed.
234 pub fn print(s: &str) {
235     with_task_stdout(|io| io.write(s.as_bytes()))
236 }
237
238 /// Prints a string to the stdout of the current process. A literal
239 /// `\n` character is printed to the console after the string.
240 pub fn println(s: &str) {
241     with_task_stdout(|io| {
242         io.write(s.as_bytes()).and_then(|()| io.write(&[b'\n']))
243     })
244 }
245
246 /// Similar to `print`, but takes a `fmt::Arguments` structure to be compatible
247 /// with the `format_args!` macro.
248 pub fn print_args(fmt: &fmt::Arguments) {
249     with_task_stdout(|io| write!(io, "{}", fmt))
250 }
251
252 /// Similar to `println`, but takes a `fmt::Arguments` structure to be
253 /// compatible with the `format_args!` macro.
254 pub fn println_args(fmt: &fmt::Arguments) {
255     with_task_stdout(|io| writeln!(io, "{}", fmt))
256 }
257
258 /// Representation of a reader of a standard input stream
259 pub struct StdReader {
260     inner: StdSource
261 }
262
263 impl StdReader {
264     /// Returns whether this stream is attached to a TTY instance or not.
265     pub fn isatty(&self) -> bool {
266         match self.inner {
267             TTY(..) => true,
268             File(..) => false,
269         }
270     }
271 }
272
273 impl Reader for StdReader {
274     fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
275         let ret = match self.inner {
276             TTY(ref mut tty) => {
277                 // Flush the task-local stdout so that weird issues like a
278                 // print!'d prompt not being shown until after the user hits
279                 // enter.
280                 flush();
281                 tty.read(buf).map(|i| i as uint)
282             },
283             File(ref mut file) => file.read(buf).map(|i| i as uint),
284         };
285         match ret {
286             // When reading a piped stdin, libuv will return 0-length reads when
287             // stdin reaches EOF. For pretty much all other streams it will
288             // return an actual EOF error, but apparently for stdin it's a
289             // little different. Hence, here we convert a 0 length read to an
290             // end-of-file indicator so the caller knows to stop reading.
291             Ok(0) => { Err(standard_error(EndOfFile)) }
292             ret @ Ok(..) | ret @ Err(..) => ret,
293         }
294     }
295 }
296
297 /// Representation of a writer to a standard output stream
298 pub struct StdWriter {
299     inner: StdSource
300 }
301
302 impl StdWriter {
303     /// Gets the size of this output window, if possible. This is typically used
304     /// when the writer is attached to something like a terminal, this is used
305     /// to fetch the dimensions of the terminal.
306     ///
307     /// If successful, returns `Ok((width, height))`.
308     ///
309     /// # Error
310     ///
311     /// This function will return an error if the output stream is not actually
312     /// connected to a TTY instance, or if querying the TTY instance fails.
313     pub fn winsize(&mut self) -> IoResult<(int, int)> {
314         match self.inner {
315             TTY(ref mut tty) => {
316                 tty.get_winsize()
317             }
318             File(..) => {
319                 Err(IoError {
320                     kind: OtherIoError,
321                     desc: "stream is not a tty",
322                     detail: None,
323                 })
324             }
325         }
326     }
327
328     /// Controls whether this output stream is a "raw stream" or simply a normal
329     /// stream.
330     ///
331     /// # Error
332     ///
333     /// This function will return an error if the output stream is not actually
334     /// connected to a TTY instance, or if querying the TTY instance fails.
335     pub fn set_raw(&mut self, raw: bool) -> IoResult<()> {
336         match self.inner {
337             TTY(ref mut tty) => {
338                 tty.set_raw(raw)
339             }
340             File(..) => {
341                 Err(IoError {
342                     kind: OtherIoError,
343                     desc: "stream is not a tty",
344                     detail: None,
345                 })
346             }
347         }
348     }
349
350     /// Returns whether this stream is attached to a TTY instance or not.
351     pub fn isatty(&self) -> bool {
352         match self.inner {
353             TTY(..) => true,
354             File(..) => false,
355         }
356     }
357 }
358
359 impl Writer for StdWriter {
360     fn write(&mut self, buf: &[u8]) -> IoResult<()> {
361         // As with stdin on windows, stdout often can't handle writes of large
362         // sizes. For an example, see #14940. For this reason, chunk the output
363         // buffer on windows, but on unix we can just write the whole buffer all
364         // at once.
365         //
366         // For some other references, it appears that this problem has been
367         // encountered by others [1] [2]. We choose the number 8KB just because
368         // libuv does the same.
369         //
370         // [1]: https://tahoe-lafs.org/trac/tahoe-lafs/ticket/1232
371         // [2]: http://www.mail-archive.com/log4net-dev@logging.apache.org/msg00661.html
372         let max_size = if cfg!(windows) {8192} else {uint::MAX};
373         for chunk in buf.chunks(max_size) {
374             try!(match self.inner {
375                 TTY(ref mut tty) => tty.write(chunk),
376                 File(ref mut file) => file.write(chunk),
377             })
378         }
379         Ok(())
380     }
381 }
382
383 #[cfg(test)]
384 mod tests {
385     use super::*;
386     use prelude::*;
387
388     #[test]
389     fn smoke() {
390         // Just make sure we can acquire handles
391         stdin();
392         stdout();
393         stderr();
394     }
395
396     #[test]
397     fn capture_stdout() {
398         use io::{ChanReader, ChanWriter};
399
400         let (tx, rx) = channel();
401         let (mut r, w) = (ChanReader::new(rx), ChanWriter::new(tx));
402         spawn(proc() {
403             set_stdout(box w);
404             println!("hello!");
405         });
406         assert_eq!(r.read_to_string().unwrap(), "hello!\n".to_string());
407     }
408
409     #[test]
410     fn capture_stderr() {
411         use realstd::comm::channel;
412         use realstd::io::{ChanReader, ChanWriter, Reader};
413
414         let (tx, rx) = channel();
415         let (mut r, w) = (ChanReader::new(rx), ChanWriter::new(tx));
416         spawn(proc() {
417             ::realstd::io::stdio::set_stderr(box w);
418             panic!("my special message");
419         });
420         let s = r.read_to_string().unwrap();
421         assert!(s.as_slice().contains("my special message"));
422     }
423 }