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