]> git.lizzy.rs Git - rust.git/blob - src/librustuv/tty.rs
Register new snapshots
[rust.git] / src / librustuv / tty.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 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, fd: libc::c_int, readable: bool)
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 readable && 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).map_err(uv_error_to_io_error)
91     }
92
93     fn set_raw(&mut self, raw: bool) -> 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 width: libc::c_int = 0;
105         let mut height: libc::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 }