]> git.lizzy.rs Git - rust.git/blob - src/libstd/io/stdio.rs
core: Add unwrap()/unwrap_err() methods to Result
[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 fmt;
31 use io::{Reader, Writer, IoResult, IoError, OtherIoError,
32          standard_error, EndOfFile, LineBufferedWriter, BufferedReader};
33 use libc;
34 use kinds::Send;
35 use mem::replace;
36 use option::{Option, Some, None};
37 use owned::Box;
38 use prelude::drop;
39 use result::{Ok, Err, ResultUnwrap};
40 use rt;
41 use rt::local::Local;
42 use rt::rtio::{DontClose, IoFactory, LocalIo, RtioFileStream, RtioTTY};
43 use rt::task::Task;
44 use str::StrSlice;
45
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.
51 //
52 // After this day, the world split into four factions:
53 //
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.
58 //
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.
63 //
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.
67 //
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.
71 //
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!
74 enum StdSource {
75     TTY(Box<RtioTTY:Send>),
76     File(Box<RtioFileStream:Send>),
77 }
78
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))),
84         })
85     }).unwrap()
86 }
87
88 /// Creates a new non-blocking handle to the stdin of the current process.
89 ///
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.
93 ///
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.
99 ///
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
105     // acceptable.
106     if cfg!(windows) {
107         BufferedReader::with_capacity(8 * 1024, stdin_raw())
108     } else {
109         BufferedReader::new(stdin_raw())
110     }
111 }
112
113 /// Creates a new non-blocking handle to the stdin of the current process.
114 ///
115 /// Unlike `stdin()`, the returned reader is *not* a buffered reader.
116 ///
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 })
120 }
121
122 /// Creates a line-buffered handle to the stdout of the current process.
123 ///
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.
128 ///
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())
134 }
135
136 /// Creates an unbuffered handle to the stdout of the current process
137 ///
138 /// See notes in `stdout()` for more information.
139 pub fn stdout_raw() -> StdWriter {
140     src(libc::STDOUT_FILENO, false, |src| StdWriter { inner: src })
141 }
142
143 /// Creates a line-buffered handle to the stderr of the current process.
144 ///
145 /// See `stdout()` for notes about this function.
146 pub fn stderr() -> LineBufferedWriter<StdWriter> {
147     LineBufferedWriter::new(stderr_raw())
148 }
149
150 /// Creates an unbuffered handle to the stderr of the current process
151 ///
152 /// See notes in `stdout()` for more information.
153 pub fn stderr_raw() -> StdWriter {
154     src(libc::STDERR_FILENO, false, |src| StdWriter { inner: src })
155 }
156
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) {
163         Some(mut w) => {
164             drop(t);
165             // FIXME: is failing right here?
166             w.flush().unwrap();
167             Some(w)
168         }
169         None => None
170     }
171 }
172
173 /// Resets the task-local stdout handle to the specified writer
174 ///
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.
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 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)))
183 }
184
185 /// Resets the task-local stderr handle to the specified writer
186 ///
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.
190 ///
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)))
195 }
196
197 // Helper to access the local task's stdout handle
198 //
199 // Note that this is not a safe function to expose because you can create an
200 // aliased pointer very easily:
201 //
202 //  with_task_stdout(|io1| {
203 //      with_task_stdout(|io2| {
204 //          // io1 aliases io2
205 //      })
206 //  })
207 fn with_task_stdout(f: |&mut Writer| -> IoResult<()> ) {
208     let task: Option<Box<Task>> = Local::try_take();
209     let result = match task {
210         Some(mut 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();
216             Local::put(task);
217
218             if my_stdout.is_none() {
219                 my_stdout = Some(box stdout() as Box<Writer:Send>);
220             }
221             let ret = f(*my_stdout.get_mut_ref());
222
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.
228             //
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);
233             drop(prev);
234             ret
235         }
236
237         None => {
238             let mut io = rt::Stdout;
239             f(&mut io as &mut Writer)
240         }
241     };
242
243     match result {
244         Ok(()) => {}
245         Err(e) => fail!("failed printing to stdout: {}", e),
246     }
247 }
248
249 /// Flushes the local task's stdout handle.
250 ///
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).
254 ///
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).
258 pub fn flush() {
259     with_task_stdout(|io| io.flush())
260 }
261
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()))
266 }
267
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]))
273     })
274 }
275
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))
280 }
281
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))
286 }
287
288 /// Representation of a reader of a standard input stream
289 pub struct StdReader {
290     inner: StdSource
291 }
292
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
299                 // enter.
300                 flush();
301                 tty.read(buf)
302             },
303             File(ref mut file) => file.read(buf).map(|i| i as uint),
304         };
305         match ret {
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,
313         }
314     }
315 }
316
317 /// Representation of a writer to a standard output stream
318 pub struct StdWriter {
319     inner: StdSource
320 }
321
322 impl 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.
326     ///
327     /// If successful, returns `Ok((width, height))`.
328     ///
329     /// # Error
330     ///
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)> {
334         match self.inner {
335             TTY(ref mut tty) => tty.get_winsize(),
336             File(..) => {
337                 Err(IoError {
338                     kind: OtherIoError,
339                     desc: "stream is not a tty",
340                     detail: None,
341                 })
342             }
343         }
344     }
345
346     /// Controls whether this output stream is a "raw stream" or simply a normal
347     /// stream.
348     ///
349     /// # Error
350     ///
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<()> {
354         match self.inner {
355             TTY(ref mut tty) => tty.set_raw(raw),
356             File(..) => {
357                 Err(IoError {
358                     kind: OtherIoError,
359                     desc: "stream is not a tty",
360                     detail: None,
361                 })
362             }
363         }
364     }
365
366     /// Returns whether this stream is attached to a TTY instance or not.
367     pub fn isatty(&self) -> bool {
368         match self.inner {
369             TTY(..) => true,
370             File(..) => false,
371         }
372     }
373 }
374
375 impl Writer for StdWriter {
376     fn write(&mut self, buf: &[u8]) -> IoResult<()> {
377         match self.inner {
378             TTY(ref mut tty) => tty.write(buf),
379             File(ref mut file) => file.write(buf),
380         }
381     }
382 }
383
384 #[cfg(test)]
385 mod tests {
386     iotest!(fn smoke() {
387         // Just make sure we can acquire handles
388         stdin();
389         stdout();
390         stderr();
391     })
392
393     iotest!(fn capture_stdout() {
394         use io::{ChanReader, ChanWriter};
395
396         let (tx, rx) = channel();
397         let (mut r, w) = (ChanReader::new(rx), ChanWriter::new(tx));
398         spawn(proc() {
399             set_stdout(box w);
400             println!("hello!");
401         });
402         assert_eq!(r.read_to_str().unwrap(), "hello!\n".to_owned());
403     })
404
405     iotest!(fn capture_stderr() {
406         use io::{ChanReader, ChanWriter};
407
408         let (tx, rx) = channel();
409         let (mut r, w) = (ChanReader::new(rx), ChanWriter::new(tx));
410         spawn(proc() {
411             set_stderr(box w);
412             fail!("my special message");
413         });
414         let s = r.read_to_str().unwrap();
415         assert!(s.contains("my special message"));
416     })
417 }