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.
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.
11 //! Terminfo database interface.
13 use std::collections::HashMap;
14 use std::io::IoResult;
20 use UnwrappableTerminal;
21 use self::searcher::open;
22 use self::parser::compiled::{parse, msys_terminfo};
23 use self::parm::{expand, Number, Variables};
26 /// A parsed terminfo database entry.
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> >
41 /// TermInfo format parsing.
43 //! ncurses-compatible compiled terminfo format parsing (term(5))
49 fn cap_for_attr(attr: attr::Attr) -> &'static str {
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"
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> {
75 impl<T: Writer+Send> 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
84 &[Number(color as int)], &mut Variables::new());
86 try!(self.out.write(s.unwrap().as_slice()));
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
101 &[Number(color as int)], &mut Variables::new());
103 try!(self.out.write(s.unwrap().as_slice()));
110 fn attr(&mut self, attr: attr::Attr) -> IoResult<bool> {
112 attr::ForegroundColor(c) => self.fg(c),
113 attr::BackgroundColor(c) => self.bg(c),
115 let cap = cap_for_attr(attr);
116 let parm = self.ti.strings.get(cap);
118 let s = expand(parm.unwrap().as_slice(),
120 &mut Variables::new());
122 try!(self.out.write(s.unwrap().as_slice()));
131 fn supports_attr(&self, attr: attr::Attr) -> bool {
133 attr::ForegroundColor(_) | attr::BackgroundColor(_) => {
137 let cap = cap_for_attr(attr);
138 self.ti.strings.get(cap).is_some()
143 fn reset(&mut self) -> IoResult<()> {
144 let mut cap = self.ti.strings.get("sgr0");
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");
150 cap = self.ti.strings.get("op");
153 let s = cap.map_or(Err("can't find terminfo capability `sgr0`".to_string()), |op| {
154 expand(op.as_slice(), &[], &mut Variables::new())
157 return self.out.write(s.unwrap().as_slice())
162 fn get_ref<'a>(&'a self) -> &'a T { &self.out }
164 fn get_mut<'a>(&'a mut self) -> &'a mut T { &mut self.out }
167 impl<T: Writer+Send> UnwrappableTerminal<T> for TerminfoTerminal<T> {
168 fn unwrap(self) -> T { self.out }
171 impl<T: Writer+Send> TerminfoTerminal<T> {
172 /// Returns `None` whenever the terminal cannot be created for some
174 pub fn new(out: T) -> Option<Box<Terminal<T>+Send+'static>> {
175 let term = match os::getenv("TERM") {
178 debug!("TERM environment variable not defined");
183 let entry = open(term[]);
185 if os::getenv("MSYSCON").map_or(false, |s| {
189 return Some(box TerminfoTerminal {out: out,
191 num_colors: 8} as Box<Terminal<T>+Send>);
193 debug!("error finding terminfo entry: {}", entry.err().unwrap());
197 let mut file = entry.unwrap();
198 let ti = parse(&mut file, false);
200 debug!("error parsing terminfo entry: {}", ti.unwrap_err());
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)
210 return Some(box TerminfoTerminal {out: out,
212 num_colors: nc} as Box<Terminal<T>+Send>);
215 fn dim_if_necessary(&self, color: color::Color) -> color::Color {
216 if color >= self.num_colors && color >= 8 && color < 16 {
223 impl<T: Writer> Writer for TerminfoTerminal<T> {
224 fn write(&mut self, buf: &[u8]) -> IoResult<()> {
228 fn flush(&mut self) -> IoResult<()> {