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
;
18 use std
::io
::prelude
::*;
20 use std
::io
::BufReader
;
26 use self::searcher
::get_dbpath_for_term
;
27 use self::parser
::compiled
::{parse, msys_terminfo}
;
28 use self::parm
::{expand, Variables, Param}
;
31 /// A parsed terminfo database entry.
34 /// Names for the terminal
35 pub names
: Vec
<String
>,
36 /// Map of capability name to boolean value
37 pub bools
: HashMap
<String
, bool
>,
38 /// Map of capability name to numeric value
39 pub numbers
: HashMap
<String
, u16>,
40 /// Map of capability name to raw (unexpanded) string
41 pub strings
: HashMap
<String
, Vec
<u8>>,
44 /// A terminfo creation error.
47 /// TermUnset Indicates that the environment doesn't include enough information to find
48 /// the terminfo entry.
50 /// MalformedTerminfo indicates that parsing the terminfo entry failed.
51 MalformedTerminfo(String
),
52 /// io::Error forwards any io::Errors encountered when finding or reading the terminfo entry.
56 impl error
::Error
for Error
{
57 fn description(&self) -> &str {
58 "failed to create TermInfo"
61 fn cause(&self) -> Option
<&error
::Error
> {
64 &IoError(ref e
) => Some(e
),
70 impl fmt
::Display
for Error
{
71 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
75 &MalformedTerminfo(ref e
) => e
.fmt(f
),
76 &IoError(ref e
) => e
.fmt(f
),
82 /// Create a TermInfo based on current environment.
83 pub fn from_env() -> Result
<TermInfo
, Error
> {
84 let term
= match env
::var("TERM") {
85 Ok(name
) => TermInfo
::from_name(&name
),
86 Err(..) => return Err(Error
::TermUnset
),
89 if term
.is_err() && env
::var("MSYSCON").ok().map_or(false, |s
| "mintty.exe" == s
) {
97 /// Create a TermInfo for the named terminal.
98 pub fn from_name(name
: &str) -> Result
<TermInfo
, Error
> {
99 get_dbpath_for_term(name
)
101 Error
::IoError(io
::Error
::new(io
::ErrorKind
::NotFound
, "terminfo file not found"))
103 .and_then(|p
| TermInfo
::from_path(&(*p
)))
106 /// Parse the given TermInfo.
107 pub fn from_path
<P
: AsRef
<Path
>>(path
: P
) -> Result
<TermInfo
, Error
> {
108 Self::_from_path(path
.as_ref())
110 // Keep the metadata small
111 fn _from_path(path
: &Path
) -> Result
<TermInfo
, Error
> {
112 let file
= File
::open(path
).map_err(|e
| Error
::IoError(e
))?
;
113 let mut reader
= BufReader
::new(file
);
114 parse(&mut reader
, false).map_err(|e
| Error
::MalformedTerminfo(e
))
120 /// TermInfo format parsing.
122 //! ncurses-compatible compiled terminfo format parsing (term(5))
128 fn cap_for_attr(attr
: Attr
) -> &'
static str {
130 Attr
::Bold
=> "bold",
132 Attr
::Italic(true) => "sitm",
133 Attr
::Italic(false) => "ritm",
134 Attr
::Underline(true) => "smul",
135 Attr
::Underline(false) => "rmul",
136 Attr
::Blink
=> "blink",
137 Attr
::Standout(true) => "smso",
138 Attr
::Standout(false) => "rmso",
139 Attr
::Reverse
=> "rev",
140 Attr
::Secure
=> "invis",
141 Attr
::ForegroundColor(_
) => "setaf",
142 Attr
::BackgroundColor(_
) => "setab",
146 /// A Terminal that knows how many colors it supports, with a reference to its
147 /// parsed Terminfo database record.
148 pub struct TerminfoTerminal
<T
> {
154 impl<T
: Write
+ Send
> Terminal
for TerminfoTerminal
<T
> {
156 fn fg(&mut self, color
: color
::Color
) -> io
::Result
<bool
> {
157 let color
= self.dim_if_necessary(color
);
158 if self.num_colors
> color
{
159 return self.apply_cap("setaf", &[Param
::Number(color
as i32)]);
164 fn bg(&mut self, color
: color
::Color
) -> io
::Result
<bool
> {
165 let color
= self.dim_if_necessary(color
);
166 if self.num_colors
> color
{
167 return self.apply_cap("setab", &[Param
::Number(color
as i32)]);
172 fn attr(&mut self, attr
: Attr
) -> io
::Result
<bool
> {
174 Attr
::ForegroundColor(c
) => self.fg(c
),
175 Attr
::BackgroundColor(c
) => self.bg(c
),
176 _
=> self.apply_cap(cap_for_attr(attr
), &[]),
180 fn supports_attr(&self, attr
: Attr
) -> bool
{
182 Attr
::ForegroundColor(_
) | Attr
::BackgroundColor(_
) => self.num_colors
> 0,
184 let cap
= cap_for_attr(attr
);
185 self.ti
.strings
.get(cap
).is_some()
190 fn reset(&mut self) -> io
::Result
<bool
> {
191 // are there any terminals that have color/attrs and not sgr0?
192 // Try falling back to sgr, then op
193 let cmd
= match ["sg0", "sgr", "op"]
195 .filter_map(|cap
| self.ti
.strings
.get(*cap
))
198 match expand(&op
, &[], &mut Variables
::new()) {
200 Err(e
) => return Err(io
::Error
::new(io
::ErrorKind
::InvalidData
, e
)),
203 None
=> return Ok(false),
205 self.out
.write_all(&cmd
).and(Ok(true))
208 fn get_ref(&self) -> &T
{
212 fn get_mut(&mut self) -> &mut T
{
216 fn into_inner(self) -> T
223 impl<T
: Write
+ Send
> TerminfoTerminal
<T
> {
224 /// Create a new TerminfoTerminal with the given TermInfo and Write.
225 pub fn new_with_terminfo(out
: T
, terminfo
: TermInfo
) -> TerminfoTerminal
<T
> {
226 let nc
= if terminfo
.strings
.contains_key("setaf") &&
227 terminfo
.strings
.contains_key("setab") {
228 terminfo
.numbers
.get("colors").map_or(0, |&n
| n
)
240 /// Create a new TerminfoTerminal for the current environment with the given Write.
242 /// Returns `None` when the terminfo cannot be found or parsed.
243 pub fn new(out
: T
) -> Option
<TerminfoTerminal
<T
>> {
244 TermInfo
::from_env().map(move |ti
| TerminfoTerminal
::new_with_terminfo(out
, ti
)).ok()
247 fn dim_if_necessary(&self, color
: color
::Color
) -> color
::Color
{
248 if color
>= self.num_colors
&& color
>= 8 && color
< 16 {
255 fn apply_cap(&mut self, cmd
: &str, params
: &[Param
]) -> io
::Result
<bool
> {
256 match self.ti
.strings
.get(cmd
) {
258 match expand(&cmd
, params
, &mut Variables
::new()) {
259 Ok(s
) => self.out
.write_all(&s
).and(Ok(true)),
260 Err(e
) => Err(io
::Error
::new(io
::ErrorKind
::InvalidData
, e
)),
269 impl<T
: Write
> Write
for TerminfoTerminal
<T
> {
270 fn write(&mut self, buf
: &[u8]) -> io
::Result
<usize> {
274 fn flush(&mut self) -> io
::Result
<()> {