(index<- ) ./libstd/io/stdio.rs
git branch: * master 5200215 auto merge of #14035 : alexcrichton/rust/experimental, r=huonw
modified: Fri May 9 13:02:28 2014
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 }
libstd/io/stdio.rs:288:58-288:58 -struct- definition:
/// Representation of a reader of a standard input stream
pub struct StdReader {
inner: StdSource
references:- 4293: impl Reader for StdReader {
294: fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
libstd/io/stdio.rs:117:55-117:55 -fn- definition:
/// See `stdout()` for more notes about this function.
pub fn stdin_raw() -> StdReader {
src(libc::STDIN_FILENO, true, |src| StdReader { inner: src })
references:- 2106: if cfg!(windows) {
107: BufferedReader::with_capacity(8 * 1024, stdin_raw())
108: } else {
109: BufferedReader::new(stdin_raw())
110: }
libstd/io/stdio.rs:317:59-317:59 -struct- definition:
/// Representation of a writer to a standard output stream
pub struct StdWriter {
inner: StdSource
references:- 8153: pub fn stderr_raw() -> StdWriter {
154: src(libc::STDERR_FILENO, false, |src| StdWriter { inner: src })
155: }
--
375: impl Writer for StdWriter {
376: fn write(&mut self, buf: &[u8]) -> IoResult<()> {
libstd/io/stdio.rs:206:7-206:7 -fn- definition:
// })
fn with_task_stdout(f: |&mut Writer| -> IoResult<()> ) {
let task: Option<Box<Task>> = Local::try_take();
references:- 5270: pub fn println(s: &str) {
271: with_task_stdout(|io| {
272: io.write(s.as_bytes()).and_then(|()| io.write(['\n' as u8]))
--
284: pub fn println_args(fmt: &fmt::Arguments) {
285: with_task_stdout(|io| fmt::writeln(io, fmt))
286: }
libstd/io/stdio.rs:73:64-73:64 -enum- definition:
// case pipe also doesn't work, but magically file does!
enum StdSource {
TTY(Box<RtioTTY:Send>),
references:- 379: fn src<T>(fd: libc::c_int, readable: bool, f: |StdSource| -> T) -> T {
80: LocalIo::maybe_raise(|io| {
--
318: pub struct StdWriter {
319: inner: StdSource
320: }
libstd/io/stdio.rs:78:1-78:1 -fn- definition:
fn src<T>(fd: libc::c_int, readable: bool, f: |StdSource| -> T) -> T {
LocalIo::maybe_raise(|io| {
Ok(match io.tty_open(fd, readable) {
references:- 3139: pub fn stdout_raw() -> StdWriter {
140: src(libc::STDOUT_FILENO, false, |src| StdWriter { inner: src })
141: }
--
153: pub fn stderr_raw() -> StdWriter {
154: src(libc::STDERR_FILENO, false, |src| StdWriter { inner: src })
155: }
libstd/io/stdio.rs:156:1-156:1 -fn- definition:
fn reset_helper(w: Box<Writer:Send>,
f: |&mut Task, Box<Writer:Send>| -> Option<Box<Writer:Send>>)
-> Option<Box<Writer:Send>> {
references:- 2193: 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: }