]> git.lizzy.rs Git - rust.git/blob - src/libstd/old_io/stdio.rs
70e8a4ceff0a0e3d8053befeace1961ef529bd74
[rust.git] / src / libstd / old_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 //! # Examples
19 //!
20 //! ```rust
21 //! # #![allow(unused_must_use)]
22 //! use std::old_io;
23 //!
24 //! let mut out = old_io::stdout();
25 //! out.write_all(b"Hello, world!");
26 //! ```
27
28 use self::StdSource::*;
29
30 use boxed;
31 use boxed::Box;
32 use cell::RefCell;
33 use clone::Clone;
34 use panicking::LOCAL_STDERR;
35 use fmt;
36 use old_io::{Reader, Writer, IoResult, IoError, OtherIoError, Buffer,
37          standard_error, EndOfFile, LineBufferedWriter, BufferedReader};
38 use marker::{Sync, Send};
39 use libc;
40 use mem;
41 use option::Option;
42 use option::Option::{Some, None};
43 use ops::{Deref, DerefMut, FnOnce};
44 use ptr;
45 use result::Result::{Ok, Err};
46 use rt;
47 use slice::SliceExt;
48 use str::StrExt;
49 use string::String;
50 use sys::{fs, tty};
51 use sync::{Arc, Mutex, MutexGuard, Once, ONCE_INIT};
52 use usize;
53 use vec::Vec;
54
55 // And so begins the tale of acquiring a uv handle to a stdio stream on all
56 // platforms in all situations. Our story begins by splitting the world into two
57 // categories, windows and unix. Then one day the creators of unix said let
58 // there be redirection! And henceforth there was redirection away from the
59 // console for standard I/O streams.
60 //
61 // After this day, the world split into four factions:
62 //
63 // 1. Unix with stdout on a terminal.
64 // 2. Unix with stdout redirected.
65 // 3. Windows with stdout on a terminal.
66 // 4. Windows with stdout redirected.
67 //
68 // Many years passed, and then one day the nation of libuv decided to unify this
69 // world. After months of toiling, uv created three ideas: TTY, Pipe, File.
70 // These three ideas propagated throughout the lands and the four great factions
71 // decided to settle among them.
72 //
73 // The groups of 1, 2, and 3 all worked very hard towards the idea of TTY. Upon
74 // doing so, they even enhanced themselves further then their Pipe/File
75 // brethren, becoming the dominant powers.
76 //
77 // The group of 4, however, decided to work independently. They abandoned the
78 // common TTY belief throughout, and even abandoned the fledgling Pipe belief.
79 // The members of the 4th faction decided to only align themselves with File.
80 //
81 // tl;dr; TTY works on everything but when windows stdout is redirected, in that
82 //        case pipe also doesn't work, but magically file does!
83 enum StdSource {
84     TTY(tty::TTY),
85     File(fs::FileDesc),
86 }
87
88 fn src<T, F>(fd: libc::c_int, _readable: bool, f: F) -> T where
89     F: FnOnce(StdSource) -> T,
90 {
91     match tty::TTY::new(fd) {
92         Ok(tty) => f(TTY(tty)),
93         Err(_) => f(File(fs::FileDesc::new(fd, false))),
94     }
95 }
96
97 thread_local! {
98     static LOCAL_STDOUT: RefCell<Option<Box<Writer + Send>>> = {
99         RefCell::new(None)
100     }
101 }
102
103 struct RaceBox(BufferedReader<StdReader>);
104
105 unsafe impl Send for RaceBox {}
106 unsafe impl Sync for RaceBox {}
107
108 /// A synchronized wrapper around a buffered reader from stdin
109 #[derive(Clone)]
110 pub struct StdinReader {
111     inner: Arc<Mutex<RaceBox>>,
112 }
113
114 unsafe impl Send for StdinReader {}
115 unsafe impl Sync for StdinReader {}
116
117 /// A guard for exclusive access to `StdinReader`'s internal `BufferedReader`.
118 pub struct StdinReaderGuard<'a> {
119     inner: MutexGuard<'a, RaceBox>,
120 }
121
122 impl<'a> Deref for StdinReaderGuard<'a> {
123     type Target = BufferedReader<StdReader>;
124
125     fn deref(&self) -> &BufferedReader<StdReader> {
126         &self.inner.0
127     }
128 }
129
130 impl<'a> DerefMut for StdinReaderGuard<'a> {
131     fn deref_mut(&mut self) -> &mut BufferedReader<StdReader> {
132         &mut self.inner.0
133     }
134 }
135
136 impl StdinReader {
137     /// Locks the `StdinReader`, granting the calling thread exclusive access
138     /// to the underlying `BufferedReader`.
139     ///
140     /// This provides access to methods like `chars` and `lines`.
141     ///
142     /// # Examples
143     ///
144     /// ```rust
145     /// use std::old_io;
146     ///
147     /// let mut stdin = old_io::stdin();
148     /// for line in stdin.lock().lines() {
149     ///     println!("{}", line.unwrap());
150     /// }
151     /// ```
152     pub fn lock<'a>(&'a mut self) -> StdinReaderGuard<'a> {
153         StdinReaderGuard {
154             inner: self.inner.lock().unwrap()
155         }
156     }
157
158     /// Like `Buffer::read_line`.
159     ///
160     /// The read is performed atomically - concurrent read calls in other
161     /// threads will not interleave with this one.
162     pub fn read_line(&mut self) -> IoResult<String> {
163         self.inner.lock().unwrap().0.read_line()
164     }
165
166     /// Like `Buffer::read_until`.
167     ///
168     /// The read is performed atomically - concurrent read calls in other
169     /// threads will not interleave with this one.
170     pub fn read_until(&mut self, byte: u8) -> IoResult<Vec<u8>> {
171         self.inner.lock().unwrap().0.read_until(byte)
172     }
173
174     /// Like `Buffer::read_char`.
175     ///
176     /// The read is performed atomically - concurrent read calls in other
177     /// threads will not interleave with this one.
178     pub fn read_char(&mut self) -> IoResult<char> {
179         self.inner.lock().unwrap().0.read_char()
180     }
181 }
182
183 impl Reader for StdinReader {
184     fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
185         self.inner.lock().unwrap().0.read(buf)
186     }
187
188     // We have to manually delegate all of these because the default impls call
189     // read more than once and we don't want those calls to interleave (or
190     // incur the costs of repeated locking).
191
192     fn read_at_least(&mut self, min: uint, buf: &mut [u8]) -> IoResult<uint> {
193         self.inner.lock().unwrap().0.read_at_least(min, buf)
194     }
195
196     fn push_at_least(&mut self, min: uint, len: uint, buf: &mut Vec<u8>) -> IoResult<uint> {
197         self.inner.lock().unwrap().0.push_at_least(min, len, buf)
198     }
199
200     fn read_to_end(&mut self) -> IoResult<Vec<u8>> {
201         self.inner.lock().unwrap().0.read_to_end()
202     }
203
204     fn read_le_uint_n(&mut self, nbytes: uint) -> IoResult<u64> {
205         self.inner.lock().unwrap().0.read_le_uint_n(nbytes)
206     }
207
208     fn read_be_uint_n(&mut self, nbytes: uint) -> IoResult<u64> {
209         self.inner.lock().unwrap().0.read_be_uint_n(nbytes)
210     }
211 }
212
213 /// Creates a new handle to the stdin of the current process.
214 ///
215 /// The returned handle is a wrapper around a global `BufferedReader` shared
216 /// by all threads. If buffered access is not desired, the `stdin_raw` function
217 /// is provided to provided unbuffered access to stdin.
218 ///
219 /// See `stdout()` for more notes about this function.
220 pub fn stdin() -> StdinReader {
221     // We're following the same strategy as kimundi's lazy_static library
222     static mut STDIN: *mut StdinReader = 0 as *mut StdinReader;
223     static ONCE: Once = ONCE_INIT;
224
225     unsafe {
226         ONCE.call_once(|| {
227             // The default buffer capacity is 64k, but apparently windows
228             // doesn't like 64k reads on stdin. See #13304 for details, but the
229             // idea is that on windows we use a slightly smaller buffer that's
230             // been seen to be acceptable.
231             let stdin = if cfg!(windows) {
232                 BufferedReader::with_capacity(8 * 1024, stdin_raw())
233             } else {
234                 BufferedReader::new(stdin_raw())
235             };
236             let stdin = StdinReader {
237                 inner: Arc::new(Mutex::new(RaceBox(stdin)))
238             };
239             STDIN = boxed::into_raw(box stdin);
240
241             // Make sure to free it at exit
242             rt::at_exit(|| {
243                 Box::from_raw(STDIN);
244                 STDIN = ptr::null_mut();
245             });
246         });
247
248         (*STDIN).clone()
249     }
250 }
251
252 /// Creates a new non-blocking handle to the stdin of the current process.
253 ///
254 /// Unlike `stdin()`, the returned reader is *not* a buffered reader.
255 ///
256 /// See `stdout()` for more notes about this function.
257 pub fn stdin_raw() -> StdReader {
258     src(libc::STDIN_FILENO, true, |src| StdReader { inner: src })
259 }
260
261 /// Creates a line-buffered handle to the stdout of the current process.
262 ///
263 /// Note that this is a fairly expensive operation in that at least one memory
264 /// allocation is performed. Additionally, this must be called from a runtime
265 /// task context because the stream returned will be a non-blocking object using
266 /// the local scheduler to perform the I/O.
267 ///
268 /// Care should be taken when creating multiple handles to an output stream for
269 /// a single process. While usage is still safe, the output may be surprising if
270 /// no synchronization is performed to ensure a sane output.
271 pub fn stdout() -> LineBufferedWriter<StdWriter> {
272     LineBufferedWriter::new(stdout_raw())
273 }
274
275 /// Creates an unbuffered handle to the stdout of the current process
276 ///
277 /// See notes in `stdout()` for more information.
278 pub fn stdout_raw() -> StdWriter {
279     src(libc::STDOUT_FILENO, false, |src| StdWriter { inner: src })
280 }
281
282 /// Creates a line-buffered handle to the stderr of the current process.
283 ///
284 /// See `stdout()` for notes about this function.
285 pub fn stderr() -> LineBufferedWriter<StdWriter> {
286     LineBufferedWriter::new(stderr_raw())
287 }
288
289 /// Creates an unbuffered handle to the stderr of the current process
290 ///
291 /// See notes in `stdout()` for more information.
292 pub fn stderr_raw() -> StdWriter {
293     src(libc::STDERR_FILENO, false, |src| StdWriter { inner: src })
294 }
295
296 /// Resets the task-local stdout handle to the specified writer
297 ///
298 /// This will replace the current task's stdout handle, returning the old
299 /// handle. All future calls to `print` and friends will emit their output to
300 /// this specified handle.
301 ///
302 /// Note that this does not need to be called for all new tasks; the default
303 /// output handle is to the process's stdout stream.
304 pub fn set_stdout(stdout: Box<Writer + Send>) -> Option<Box<Writer + Send>> {
305     let mut new = Some(stdout);
306     LOCAL_STDOUT.with(|slot| {
307         mem::replace(&mut *slot.borrow_mut(), new.take())
308     }).and_then(|mut s| {
309         let _ = s.flush();
310         Some(s)
311     })
312 }
313
314 /// Resets the task-local stderr handle to the specified writer
315 ///
316 /// This will replace the current task's stderr handle, returning the old
317 /// handle. Currently, the stderr handle is used for printing panic messages
318 /// during task panic.
319 ///
320 /// Note that this does not need to be called for all new tasks; the default
321 /// output handle is to the process's stderr stream.
322 pub fn set_stderr(stderr: Box<Writer + Send>) -> Option<Box<Writer + Send>> {
323     let mut new = Some(stderr);
324     LOCAL_STDERR.with(|slot| {
325         mem::replace(&mut *slot.borrow_mut(), new.take())
326     }).and_then(|mut s| {
327         let _ = s.flush();
328         Some(s)
329     })
330 }
331
332 // Helper to access the local task's stdout handle
333 //
334 // Note that this is not a safe function to expose because you can create an
335 // aliased pointer very easily:
336 //
337 //  with_task_stdout(|io1| {
338 //      with_task_stdout(|io2| {
339 //          // io1 aliases io2
340 //      })
341 //  })
342 fn with_task_stdout<F>(f: F) where F: FnOnce(&mut Writer) -> IoResult<()> {
343     let mut my_stdout = LOCAL_STDOUT.with(|slot| {
344         slot.borrow_mut().take()
345     }).unwrap_or_else(|| {
346         box stdout() as Box<Writer + Send>
347     });
348     let result = f(&mut *my_stdout);
349     let mut var = Some(my_stdout);
350     LOCAL_STDOUT.with(|slot| {
351         *slot.borrow_mut() = var.take();
352     });
353     match result {
354         Ok(()) => {}
355         Err(e) => panic!("failed printing to stdout: {:?}", e),
356     }
357 }
358
359 /// Flushes the local task's stdout handle.
360 ///
361 /// By default, this stream is a line-buffering stream, so flushing may be
362 /// necessary to ensure that all output is printed to the screen (if there are
363 /// no newlines printed).
364 ///
365 /// Note that logging macros do not use this stream. Using the logging macros
366 /// will emit output to stderr, and while they are line buffered the log
367 /// messages are always terminated in a newline (no need to flush).
368 pub fn flush() {
369     with_task_stdout(|io| io.flush())
370 }
371
372 /// Prints a string to the stdout of the current process. No newline is emitted
373 /// after the string is printed.
374 pub fn print(s: &str) {
375     with_task_stdout(|io| io.write_all(s.as_bytes()))
376 }
377
378 /// Prints a string to the stdout of the current process. A literal
379 /// `\n` character is printed to the console after the string.
380 pub fn println(s: &str) {
381     with_task_stdout(|io| {
382         io.write_all(s.as_bytes()).and_then(|()| io.write_all(&[b'\n']))
383     })
384 }
385
386 /// Similar to `print`, but takes a `fmt::Arguments` structure to be compatible
387 /// with the `format_args!` macro.
388 #[stable(feature = "rust1", since = "1.0.0")]
389 pub fn print_args(fmt: fmt::Arguments) {
390     with_task_stdout(|io| write!(io, "{}", fmt))
391 }
392
393 /// Similar to `println`, but takes a `fmt::Arguments` structure to be
394 /// compatible with the `format_args!` macro.
395 #[stable(feature = "rust1", since = "1.0.0")]
396 pub fn println_args(fmt: fmt::Arguments) {
397     with_task_stdout(|io| writeln!(io, "{}", fmt))
398 }
399
400 /// Representation of a reader of a standard input stream
401 pub struct StdReader {
402     inner: StdSource
403 }
404
405 impl StdReader {
406     /// Returns whether this stream is attached to a TTY instance or not.
407     pub fn isatty(&self) -> bool {
408         match self.inner {
409             TTY(..) => true,
410             File(..) => false,
411         }
412     }
413 }
414
415 impl Reader for StdReader {
416     fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
417         let ret = match self.inner {
418             TTY(ref mut tty) => {
419                 // Flush the task-local stdout so that weird issues like a
420                 // print!'d prompt not being shown until after the user hits
421                 // enter.
422                 flush();
423                 tty.read(buf).map(|i| i as uint)
424             },
425             File(ref mut file) => file.read(buf).map(|i| i as uint),
426         };
427         match ret {
428             // When reading a piped stdin, libuv will return 0-length reads when
429             // stdin reaches EOF. For pretty much all other streams it will
430             // return an actual EOF error, but apparently for stdin it's a
431             // little different. Hence, here we convert a 0 length read to an
432             // end-of-file indicator so the caller knows to stop reading.
433             Ok(0) => { Err(standard_error(EndOfFile)) }
434             ret @ Ok(..) | ret @ Err(..) => ret,
435         }
436     }
437 }
438
439 /// Representation of a writer to a standard output stream
440 pub struct StdWriter {
441     inner: StdSource
442 }
443
444 unsafe impl Send for StdWriter {}
445 unsafe impl Sync for StdWriter {}
446
447 impl StdWriter {
448     /// Gets the size of this output window, if possible. This is typically used
449     /// when the writer is attached to something like a terminal, this is used
450     /// to fetch the dimensions of the terminal.
451     ///
452     /// If successful, returns `Ok((width, height))`.
453     ///
454     /// # Error
455     ///
456     /// This function will return an error if the output stream is not actually
457     /// connected to a TTY instance, or if querying the TTY instance fails.
458     pub fn winsize(&mut self) -> IoResult<(int, int)> {
459         match self.inner {
460             TTY(ref mut tty) => {
461                 tty.get_winsize()
462             }
463             File(..) => {
464                 Err(IoError {
465                     kind: OtherIoError,
466                     desc: "stream is not a tty",
467                     detail: None,
468                 })
469             }
470         }
471     }
472
473     /// Controls whether this output stream is a "raw stream" or simply a normal
474     /// stream.
475     ///
476     /// # Error
477     ///
478     /// This function will return an error if the output stream is not actually
479     /// connected to a TTY instance, or if querying the TTY instance fails.
480     pub fn set_raw(&mut self, raw: bool) -> IoResult<()> {
481         match self.inner {
482             TTY(ref mut tty) => {
483                 tty.set_raw(raw)
484             }
485             File(..) => {
486                 Err(IoError {
487                     kind: OtherIoError,
488                     desc: "stream is not a tty",
489                     detail: None,
490                 })
491             }
492         }
493     }
494
495     /// Returns whether this stream is attached to a TTY instance or not.
496     pub fn isatty(&self) -> bool {
497         match self.inner {
498             TTY(..) => true,
499             File(..) => false,
500         }
501     }
502 }
503
504 impl Writer for StdWriter {
505     fn write_all(&mut self, buf: &[u8]) -> IoResult<()> {
506         // As with stdin on windows, stdout often can't handle writes of large
507         // sizes. For an example, see #14940. For this reason, chunk the output
508         // buffer on windows, but on unix we can just write the whole buffer all
509         // at once.
510         //
511         // For some other references, it appears that this problem has been
512         // encountered by others [1] [2]. We choose the number 8KB just because
513         // libuv does the same.
514         //
515         // [1]: https://tahoe-lafs.org/trac/tahoe-lafs/ticket/1232
516         // [2]: http://www.mail-archive.com/log4net-dev@logging.apache.org/msg00661.html
517         let max_size = if cfg!(windows) {8192} else {usize::MAX};
518         for chunk in buf.chunks(max_size) {
519             try!(match self.inner {
520                 TTY(ref mut tty) => tty.write(chunk),
521                 File(ref mut file) => file.write(chunk),
522             })
523         }
524         Ok(())
525     }
526 }
527
528 #[cfg(test)]
529 mod tests {
530     use prelude::v1::*;
531
532     use super::*;
533     use sync::mpsc::channel;
534     use thread;
535
536     #[test]
537     fn smoke() {
538         // Just make sure we can acquire handles
539         stdin();
540         stdout();
541         stderr();
542     }
543
544     #[test]
545     fn capture_stdout() {
546         use old_io::{ChanReader, ChanWriter};
547
548         let (tx, rx) = channel();
549         let (mut r, w) = (ChanReader::new(rx), ChanWriter::new(tx));
550         // FIXME (#22405): Replace `Box::new` with `box` here when/if possible.
551         let _t = thread::spawn(move|| {
552             set_stdout(Box::new(w));
553             println!("hello!");
554         });
555         assert_eq!(r.read_to_string().unwrap(), "hello!\n");
556     }
557
558     #[test]
559     fn capture_stderr() {
560         use old_io::{ChanReader, ChanWriter, Reader};
561
562         let (tx, rx) = channel();
563         let (mut r, w) = (ChanReader::new(rx), ChanWriter::new(tx));
564         // FIXME (#22405): Replace `Box::new` with `box` here when/if possible.
565         let _t = thread::spawn(move || -> () {
566             set_stderr(Box::new(w));
567             panic!("my special message");
568         });
569         let s = r.read_to_string().unwrap();
570         assert!(s.contains("my special message"));
571     }
572 }