(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>(fdlibc::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(wBox<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(stdoutBox<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(stderrBox<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 taskOption<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, rawbool) -> 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:- 4
293: 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:- 2
106:     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:- 8
153: 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:- 5
270: 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:- 3
79: 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:- 3
139: 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:- 2
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: }