]> git.lizzy.rs Git - rust.git/blob - src/libterm/terminfo/mod.rs
Auto merge of #22517 - brson:relnotes, r=Gankro
[rust.git] / src / libterm / terminfo / mod.rs
1 // Copyright 2012-2014 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 //! Terminfo database interface.
12
13 use std::collections::HashMap;
14 use std::old_io::IoResult;
15 use std::env;
16
17 use attr;
18 use color;
19 use Terminal;
20 use UnwrappableTerminal;
21 use self::searcher::open;
22 use self::parser::compiled::{parse, msys_terminfo};
23 use self::parm::{expand, Number, Variables};
24
25
26 /// A parsed terminfo database entry.
27 #[derive(Debug)]
28 pub struct TermInfo {
29     /// Names for the terminal
30     pub names: Vec<String> ,
31     /// Map of capability name to boolean value
32     pub bools: HashMap<String, bool>,
33     /// Map of capability name to numeric value
34     pub numbers: HashMap<String, u16>,
35     /// Map of capability name to raw (unexpanded) string
36     pub strings: HashMap<String, Vec<u8> >
37 }
38
39 pub mod searcher;
40
41 /// TermInfo format parsing.
42 pub mod parser {
43     //! ncurses-compatible compiled terminfo format parsing (term(5))
44     pub mod compiled;
45 }
46 pub mod parm;
47
48
49 fn cap_for_attr(attr: attr::Attr) -> &'static str {
50     match attr {
51         attr::Bold               => "bold",
52         attr::Dim                => "dim",
53         attr::Italic(true)       => "sitm",
54         attr::Italic(false)      => "ritm",
55         attr::Underline(true)    => "smul",
56         attr::Underline(false)   => "rmul",
57         attr::Blink              => "blink",
58         attr::Standout(true)     => "smso",
59         attr::Standout(false)    => "rmso",
60         attr::Reverse            => "rev",
61         attr::Secure             => "invis",
62         attr::ForegroundColor(_) => "setaf",
63         attr::BackgroundColor(_) => "setab"
64     }
65 }
66
67 /// A Terminal that knows how many colors it supports, with a reference to its
68 /// parsed Terminfo database record.
69 pub struct TerminfoTerminal<T> {
70     num_colors: u16,
71     out: T,
72     ti: Box<TermInfo>
73 }
74
75 impl<T: Writer+Send+'static> Terminal<T> for TerminfoTerminal<T> {
76     fn fg(&mut self, color: color::Color) -> IoResult<bool> {
77         let color = self.dim_if_necessary(color);
78         if self.num_colors > color {
79             let s = expand(self.ti
80                                .strings
81                                .get("setaf")
82                                .unwrap()
83                                ,
84                            &[Number(color as int)], &mut Variables::new());
85             if s.is_ok() {
86                 try!(self.out.write_all(&s.unwrap()));
87                 return Ok(true)
88             }
89         }
90         Ok(false)
91     }
92
93     fn bg(&mut self, color: color::Color) -> IoResult<bool> {
94         let color = self.dim_if_necessary(color);
95         if self.num_colors > color {
96             let s = expand(self.ti
97                                .strings
98                                .get("setab")
99                                .unwrap()
100                                ,
101                            &[Number(color as int)], &mut Variables::new());
102             if s.is_ok() {
103                 try!(self.out.write_all(&s.unwrap()));
104                 return Ok(true)
105             }
106         }
107         Ok(false)
108     }
109
110     fn attr(&mut self, attr: attr::Attr) -> IoResult<bool> {
111         match attr {
112             attr::ForegroundColor(c) => self.fg(c),
113             attr::BackgroundColor(c) => self.bg(c),
114             _ => {
115                 let cap = cap_for_attr(attr);
116                 let parm = self.ti.strings.get(cap);
117                 if parm.is_some() {
118                     let s = expand(parm.unwrap(),
119                                    &[],
120                                    &mut Variables::new());
121                     if s.is_ok() {
122                         try!(self.out.write_all(&s.unwrap()));
123                         return Ok(true)
124                     }
125                 }
126                 Ok(false)
127             }
128         }
129     }
130
131     fn supports_attr(&self, attr: attr::Attr) -> bool {
132         match attr {
133             attr::ForegroundColor(_) | attr::BackgroundColor(_) => {
134                 self.num_colors > 0
135             }
136             _ => {
137                 let cap = cap_for_attr(attr);
138                 self.ti.strings.get(cap).is_some()
139             }
140         }
141     }
142
143     fn reset(&mut self) -> IoResult<()> {
144         let mut cap = self.ti.strings.get("sgr0");
145         if cap.is_none() {
146             // are there any terminals that have color/attrs and not sgr0?
147             // Try falling back to sgr, then op
148             cap = self.ti.strings.get("sgr");
149             if cap.is_none() {
150                 cap = self.ti.strings.get("op");
151             }
152         }
153         let s = cap.map_or(Err("can't find terminfo capability `sgr0`".to_string()), |op| {
154             expand(op, &[], &mut Variables::new())
155         });
156         if s.is_ok() {
157             return self.out.write_all(&s.unwrap())
158         }
159         Ok(())
160     }
161
162     fn get_ref<'a>(&'a self) -> &'a T { &self.out }
163
164     fn get_mut<'a>(&'a mut self) -> &'a mut T { &mut self.out }
165 }
166
167 impl<T: Writer+Send+'static> UnwrappableTerminal<T> for TerminfoTerminal<T> {
168     fn unwrap(self) -> T { self.out }
169 }
170
171 impl<T: Writer+Send+'static> TerminfoTerminal<T> {
172     /// Returns `None` whenever the terminal cannot be created for some
173     /// reason.
174     pub fn new(out: T) -> Option<Box<Terminal<T>+Send+'static>> {
175         let term = match env::var("TERM") {
176             Ok(t) => t,
177             Err(..) => {
178                 debug!("TERM environment variable not defined");
179                 return None;
180             }
181         };
182
183         let entry = open(&term[]);
184         if entry.is_err() {
185             if env::var("MSYSCON").ok().map_or(false, |s| {
186                     "mintty.exe" == s
187                 }) {
188                 // msys terminal
189                 return Some(box TerminfoTerminal {out: out,
190                                                   ti: msys_terminfo(),
191                                                   num_colors: 8} as Box<Terminal<T>+Send>);
192             }
193             debug!("error finding terminfo entry: {:?}", entry.err().unwrap());
194             return None;
195         }
196
197         let mut file = entry.unwrap();
198         let ti = parse(&mut file, false);
199         if ti.is_err() {
200             debug!("error parsing terminfo entry: {:?}", ti.err().unwrap());
201             return None;
202         }
203
204         let inf = ti.unwrap();
205         let nc = if inf.strings.get("setaf").is_some()
206                  && inf.strings.get("setab").is_some() {
207                      inf.numbers.get("colors").map_or(0, |&n| n)
208                  } else { 0 };
209
210         return Some(box TerminfoTerminal {out: out,
211                                           ti: inf,
212                                           num_colors: nc} as Box<Terminal<T>+Send>);
213     }
214
215     fn dim_if_necessary(&self, color: color::Color) -> color::Color {
216         if color >= self.num_colors && color >= 8 && color < 16 {
217             color-8
218         } else { color }
219     }
220 }
221
222
223 impl<T: Writer> Writer for TerminfoTerminal<T> {
224     fn write_all(&mut self, buf: &[u8]) -> IoResult<()> {
225         self.out.write_all(buf)
226     }
227
228     fn flush(&mut self) -> IoResult<()> {
229         self.out.flush()
230     }
231 }