]> git.lizzy.rs Git - rust.git/blob - src/libstd/io/stdio.rs
Replace all ~"" with "".to_owned()
[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 container::Container;
31 use fmt;
32 use io::{Reader, Writer, IoResult, IoError, OtherIoError,
33          standard_error, EndOfFile, LineBufferedWriter, BufferedReader};
34 use libc;
35 use kinds::Send;
36 use mem::replace;
37 use option::{Option, Some, None};
38 use prelude::drop;
39 use result::{Ok, Err};
40 use rt::local::Local;
41 use rt::rtio::{DontClose, IoFactory, LocalIo, RtioFileStream, RtioTTY};
42 use rt::task::Task;
43 use str::StrSlice;
44 use slice::ImmutableVector;
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(~RtioTTY:Send),
76     File(~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: ~Writer:Send,
158                 f: |&mut Task, ~Writer:Send| -> Option<~Writer:Send>)
159     -> Option<~Writer:Send>
160 {
161     let mut t = Local::borrow(None::<Task>);
162     // Be sure to flush any pending output from the writer
163     match f(&mut *t, w) {
164         Some(mut w) => {
165             drop(t);
166             // FIXME: is failing right here?
167             w.flush().unwrap();
168             Some(w)
169         }
170         None => None
171     }
172 }
173
174 /// Resets the task-local stdout handle to the specified writer
175 ///
176 /// This will replace the current task's stdout handle, returning the old
177 /// handle. All future calls to `print` and friends will emit their output to
178 /// this specified handle.
179 ///
180 /// Note that this does not need to be called for all new tasks; the default
181 /// output handle is to the process's stdout stream.
182 pub fn set_stdout(stdout: ~Writer:Send) -> Option<~Writer:Send> {
183     reset_helper(stdout, |t, w| replace(&mut t.stdout, Some(w)))
184 }
185
186 /// Resets the task-local stderr handle to the specified writer
187 ///
188 /// This will replace the current task's stderr handle, returning the old
189 /// handle. Currently, the stderr handle is used for printing failure messages
190 /// during task failure.
191 ///
192 /// Note that this does not need to be called for all new tasks; the default
193 /// output handle is to the process's stderr stream.
194 pub fn set_stderr(stderr: ~Writer:Send) -> Option<~Writer:Send> {
195     reset_helper(stderr, |t, w| replace(&mut t.stderr, Some(w)))
196 }
197
198 // Helper to access the local task's stdout handle
199 //
200 // Note that this is not a safe function to expose because you can create an
201 // aliased pointer very easily:
202 //
203 //  with_task_stdout(|io1| {
204 //      with_task_stdout(|io2| {
205 //          // io1 aliases io2
206 //      })
207 //  })
208 fn with_task_stdout(f: |&mut Writer| -> IoResult<()> ) {
209     let task: Option<~Task> = Local::try_take();
210     let result = match task {
211         Some(mut task) => {
212             // Printing may run arbitrary code, so ensure that the task is in
213             // TLS to allow all std services. Note that this means a print while
214             // printing won't use the task's normal stdout handle, but this is
215             // necessary to ensure safety (no aliasing).
216             let mut my_stdout = task.stdout.take();
217             Local::put(task);
218
219             if my_stdout.is_none() {
220                 my_stdout = Some(~stdout() as ~Writer:Send);
221             }
222             let ret = f(*my_stdout.get_mut_ref());
223
224             // Note that we need to be careful when putting the stdout handle
225             // back into the task. If the handle was set to `Some` while
226             // printing, then we can run aribitrary code when destroying the
227             // previous handle. This means that the local task needs to be in
228             // TLS while we do this.
229             //
230             // To protect against this, we do a little dance in which we
231             // temporarily take the task, swap the handles, put the task in TLS,
232             // and only then drop the previous handle.
233             let prev = replace(&mut Local::borrow(None::<Task>).stdout, my_stdout);
234             drop(prev);
235             ret
236         }
237
238         None => {
239             struct Stdout;
240             impl Writer for Stdout {
241                 fn write(&mut self, data: &[u8]) -> IoResult<()> {
242                     unsafe {
243                         libc::write(libc::STDOUT_FILENO,
244                                     data.as_ptr() as *libc::c_void,
245                                     data.len() as libc::size_t);
246                     }
247                     Ok(()) // just ignore the results
248                 }
249             }
250             let mut io = Stdout;
251             f(&mut io as &mut Writer)
252         }
253     };
254
255     match result {
256         Ok(()) => {}
257         Err(e) => fail!("failed printing to stdout: {}", e),
258     }
259 }
260
261 /// Flushes the local task's stdout handle.
262 ///
263 /// By default, this stream is a line-buffering stream, so flushing may be
264 /// necessary to ensure that all output is printed to the screen (if there are
265 /// no newlines printed).
266 ///
267 /// Note that logging macros do not use this stream. Using the logging macros
268 /// will emit output to stderr, and while they are line buffered the log
269 /// messages are always terminated in a newline (no need to flush).
270 pub fn flush() {
271     with_task_stdout(|io| io.flush())
272 }
273
274 /// Prints a string to the stdout of the current process. No newline is emitted
275 /// after the string is printed.
276 pub fn print(s: &str) {
277     with_task_stdout(|io| io.write(s.as_bytes()))
278 }
279
280 /// Prints a string as a line. to the stdout of the current process. A literal
281 /// `\n` character is printed to the console after the string.
282 pub fn println(s: &str) {
283     with_task_stdout(|io| {
284         io.write(s.as_bytes()).and_then(|()| io.write(['\n' as u8]))
285     })
286 }
287
288 /// Similar to `print`, but takes a `fmt::Arguments` structure to be compatible
289 /// with the `format_args!` macro.
290 pub fn print_args(fmt: &fmt::Arguments) {
291     with_task_stdout(|io| fmt::write(io, fmt))
292 }
293
294 /// Similar to `println`, but takes a `fmt::Arguments` structure to be
295 /// compatible with the `format_args!` macro.
296 pub fn println_args(fmt: &fmt::Arguments) {
297     with_task_stdout(|io| fmt::writeln(io, fmt))
298 }
299
300 /// Representation of a reader of a standard input stream
301 pub struct StdReader {
302     inner: StdSource
303 }
304
305 impl Reader for StdReader {
306     fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
307         let ret = match self.inner {
308             TTY(ref mut tty) => {
309                 // Flush the task-local stdout so that weird issues like a
310                 // print!'d prompt not being shown until after the user hits
311                 // enter.
312                 flush();
313                 tty.read(buf)
314             },
315             File(ref mut file) => file.read(buf).map(|i| i as uint),
316         };
317         match ret {
318             // When reading a piped stdin, libuv will return 0-length reads when
319             // stdin reaches EOF. For pretty much all other streams it will
320             // return an actual EOF error, but apparently for stdin it's a
321             // little different. Hence, here we convert a 0 length read to an
322             // end-of-file indicator so the caller knows to stop reading.
323             Ok(0) => { Err(standard_error(EndOfFile)) }
324             ret @ Ok(..) | ret @ Err(..) => ret,
325         }
326     }
327 }
328
329 /// Representation of a writer to a standard output stream
330 pub struct StdWriter {
331     inner: StdSource
332 }
333
334 impl StdWriter {
335     /// Gets the size of this output window, if possible. This is typically used
336     /// when the writer is attached to something like a terminal, this is used
337     /// to fetch the dimensions of the terminal.
338     ///
339     /// If successful, returns `Ok((width, height))`.
340     ///
341     /// # Error
342     ///
343     /// This function will return an error if the output stream is not actually
344     /// connected to a TTY instance, or if querying the TTY instance fails.
345     pub fn winsize(&mut self) -> IoResult<(int, int)> {
346         match self.inner {
347             TTY(ref mut tty) => tty.get_winsize(),
348             File(..) => {
349                 Err(IoError {
350                     kind: OtherIoError,
351                     desc: "stream is not a tty",
352                     detail: None,
353                 })
354             }
355         }
356     }
357
358     /// Controls whether this output stream is a "raw stream" or simply a normal
359     /// stream.
360     ///
361     /// # Error
362     ///
363     /// This function will return an error if the output stream is not actually
364     /// connected to a TTY instance, or if querying the TTY instance fails.
365     pub fn set_raw(&mut self, raw: bool) -> IoResult<()> {
366         match self.inner {
367             TTY(ref mut tty) => tty.set_raw(raw),
368             File(..) => {
369                 Err(IoError {
370                     kind: OtherIoError,
371                     desc: "stream is not a tty",
372                     detail: None,
373                 })
374             }
375         }
376     }
377
378     /// Returns whether this stream is attached to a TTY instance or not.
379     pub fn isatty(&self) -> bool {
380         match self.inner {
381             TTY(..) => true,
382             File(..) => false,
383         }
384     }
385 }
386
387 impl Writer for StdWriter {
388     fn write(&mut self, buf: &[u8]) -> IoResult<()> {
389         match self.inner {
390             TTY(ref mut tty) => tty.write(buf),
391             File(ref mut file) => file.write(buf),
392         }
393     }
394 }
395
396 #[cfg(test)]
397 mod tests {
398     iotest!(fn smoke() {
399         // Just make sure we can acquire handles
400         stdin();
401         stdout();
402         stderr();
403     })
404
405     iotest!(fn capture_stdout() {
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_stdout(~w);
412             println!("hello!");
413         });
414         assert_eq!(r.read_to_str().unwrap(), "hello!\n".to_owned());
415     })
416
417     iotest!(fn capture_stderr() {
418         use io::{ChanReader, ChanWriter};
419
420         let (tx, rx) = channel();
421         let (mut r, w) = (ChanReader::new(rx), ChanWriter::new(tx));
422         spawn(proc() {
423             set_stderr(~w);
424             fail!("my special message");
425         });
426         let s = r.read_to_str().unwrap();
427         assert!(s.contains("my special message"));
428     })
429 }