(index<- )        ./librustuv/tty.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  use libc;
  12  use std::io::IoError;
  13  use std::ptr;
  14  use std::rt::rtio::RtioTTY;
  15  
  16  use homing::{HomingIO, HomeHandle};
  17  use stream::StreamWatcher;
  18  use super::{UvError, UvHandle, uv_error_to_io_error};
  19  use uvio::UvIoFactory;
  20  use uvll;
  21  
  22  pub struct TtyWatcher{
  23      tty: *uvll::uv_tty_t,
  24      stream: StreamWatcher,
  25      home: HomeHandle,
  26      fd: libc::c_int,
  27  }
  28  
  29  impl TtyWatcher {
  30      pub fn new(io&mut UvIoFactory, fdlibc::c_int, readablebool)
  31          -> Result<TtyWatcher, UvError>
  32      {
  33          // libuv may succeed in giving us a handle (via uv_tty_init), but if the
  34          // handle isn't actually connected to a terminal there are frequently
  35          // many problems in using it with libuv. To get around this, always
  36          // return a failure if the specified file descriptor isn't actually a
  37          // TTY.
  38          //
  39          // Related:
  40          // - https://github.com/joyent/libuv/issues/982
  41          // - https://github.com/joyent/libuv/issues/988
  42          let guess = unsafe { uvll::guess_handle(fd) };
  43          if guess != uvll::UV_TTY as libc::c_int {
  44              return Err(UvError(uvll::EBADF));
  45          }
  46  
  47          // libuv was recently changed to not close the stdio file descriptors,
  48          // but it did not change the behavior for windows. Until this issue is
  49          // fixed, we need to dup the stdio file descriptors because otherwise
  50          // uv_close will close them
  51          let fd = if cfg!(windows) && fd <= libc::STDERR_FILENO {
  52              unsafe { libc::dup(fd) }
  53          } else { fd };
  54  
  55          // If this file descriptor is indeed guessed to be a tty, then go ahead
  56          // with attempting to open it as a tty.
  57          let handle = UvHandle::alloc(None::<TtyWatcher>, uvll::UV_TTY);
  58          let mut watcher = TtyWatcher {
  59              tty: handle,
  60              stream: StreamWatcher::new(handle),
  61              home: io.make_handle(),
  62              fd: fd,
  63          };
  64          match unsafe {
  65              uvll::uv_tty_init(io.uv_loop(), handle, fd as libc::c_int,
  66                                readable as libc::c_int)
  67          } {
  68              0 => Ok(watcher),
  69              n => {
  70                  // On windows, libuv returns errors before initializing the
  71                  // handle, so our only cleanup is to free the handle itself
  72                  if cfg!(windows) {
  73                      unsafe { uvll::free_handle(handle); }
  74                      watcher.tty = ptr::null();
  75                  }
  76                  Err(UvError(n))
  77              }
  78          }
  79      }
  80  }
  81  
  82  impl RtioTTY for TtyWatcher {
  83      fn read(&mut self, buf&mut [u8]) -> Result<uint, IoError> {
  84          let _m = self.fire_homing_missile();
  85          self.stream.read(buf).map_err(uv_error_to_io_error)
  86      }
  87  
  88      fn write(&mut self, buf&[u8]) -> Result<(), IoError> {
  89          let _m = self.fire_homing_missile();
  90          self.stream.write(buf, false).map_err(uv_error_to_io_error)
  91      }
  92  
  93      fn set_raw(&mut self, rawbool) -> Result<(), IoError> {
  94          let raw = raw as libc::c_int;
  95          let _m = self.fire_homing_missile();
  96          match unsafe { uvll::uv_tty_set_mode(self.tty, raw) } {
  97              0 => Ok(()),
  98              n => Err(uv_error_to_io_error(UvError(n)))
  99          }
 100      }
 101  
 102      #[allow(unused_mut)]
 103      fn get_winsize(&mut self) -> Result<(int, int), IoError> {
 104          let mut widthlibc::c_int = 0;
 105          let mut heightlibc::c_int = 0;
 106          let widthptr*libc::c_int = &width;
 107          let heightptr*libc::c_int = &width;
 108  
 109          let _m = self.fire_homing_missile();
 110          match unsafe { uvll::uv_tty_get_winsize(self.tty,
 111                                                  widthptr, heightptr) } {
 112              0 => Ok((width as int, height as int)),
 113              n => Err(uv_error_to_io_error(UvError(n)))
 114          }
 115      }
 116  
 117      fn isatty(&self) -> bool {
 118          unsafe { uvll::guess_handle(self.fd) == uvll::UV_TTY as libc::c_int }
 119      }
 120  }
 121  
 122  impl UvHandle<uvll::uv_tty_t> for TtyWatcher {
 123      fn uv_handle(&self) -> *uvll::uv_tty_t { self.tty }
 124  }
 125  
 126  impl HomingIO for TtyWatcher {
 127      fn home<'a>(&'a mut self) -> &'a mut HomeHandle { &mut self.home }
 128  }
 129  
 130  impl Drop for TtyWatcher {
 131      fn drop(&mut self) {
 132          if !self.tty.is_null() {
 133              let _m = self.fire_homing_missile();
 134              self.close_async_();
 135          }
 136      }
 137  }