(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, 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 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, 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 }