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