2014-01-30 18:15:10 -06:00
|
|
|
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
|
2013-05-30 01:13:35 -05:00
|
|
|
// file at the top-level directory of this distribution and at
|
|
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
|
|
|
|
2014-03-16 17:15:55 -05:00
|
|
|
//! Terminfo database interface.
|
2013-10-12 21:02:46 -05:00
|
|
|
|
2014-05-29 21:03:06 -05:00
|
|
|
use std::collections::HashMap;
|
2014-04-08 10:18:10 -05:00
|
|
|
use std::io::IoResult;
|
|
|
|
use std::os;
|
|
|
|
|
|
|
|
use attr;
|
|
|
|
use color;
|
|
|
|
use Terminal;
|
2014-10-01 01:01:08 -05:00
|
|
|
use UnwrappableTerminal;
|
2014-04-08 10:18:10 -05:00
|
|
|
use self::searcher::open;
|
|
|
|
use self::parser::compiled::{parse, msys_terminfo};
|
|
|
|
use self::parm::{expand, Number, Variables};
|
|
|
|
|
2013-05-30 01:13:35 -05:00
|
|
|
|
2014-03-16 17:15:55 -05:00
|
|
|
/// A parsed terminfo database entry.
|
2014-05-07 08:45:53 -05:00
|
|
|
#[deriving(Show)]
|
2013-05-30 01:13:35 -05:00
|
|
|
pub struct TermInfo {
|
2013-05-30 18:14:40 -05:00
|
|
|
/// Names for the terminal
|
2014-05-22 18:57:53 -05:00
|
|
|
pub names: Vec<String> ,
|
2013-05-30 18:14:40 -05:00
|
|
|
/// Map of capability name to boolean value
|
2014-05-22 18:57:53 -05:00
|
|
|
pub bools: HashMap<String, bool>,
|
2013-05-30 18:14:40 -05:00
|
|
|
/// Map of capability name to numeric value
|
2014-05-22 18:57:53 -05:00
|
|
|
pub numbers: HashMap<String, u16>,
|
2013-05-30 18:14:40 -05:00
|
|
|
/// Map of capability name to raw (unexpanded) string
|
2014-05-22 18:57:53 -05:00
|
|
|
pub strings: HashMap<String, Vec<u8> >
|
2013-05-30 01:13:35 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
pub mod searcher;
|
2014-03-16 17:15:55 -05:00
|
|
|
|
|
|
|
/// TermInfo format parsing.
|
2013-05-30 01:13:35 -05:00
|
|
|
pub mod parser {
|
2014-03-16 17:15:55 -05:00
|
|
|
//! ncurses-compatible compiled terminfo format parsing (term(5))
|
2013-05-30 01:13:35 -05:00
|
|
|
pub mod compiled;
|
|
|
|
}
|
|
|
|
pub mod parm;
|
2014-04-08 10:18:10 -05:00
|
|
|
|
|
|
|
|
|
|
|
fn cap_for_attr(attr: attr::Attr) -> &'static str {
|
|
|
|
match attr {
|
|
|
|
attr::Bold => "bold",
|
|
|
|
attr::Dim => "dim",
|
|
|
|
attr::Italic(true) => "sitm",
|
|
|
|
attr::Italic(false) => "ritm",
|
|
|
|
attr::Underline(true) => "smul",
|
|
|
|
attr::Underline(false) => "rmul",
|
|
|
|
attr::Blink => "blink",
|
|
|
|
attr::Standout(true) => "smso",
|
|
|
|
attr::Standout(false) => "rmso",
|
|
|
|
attr::Reverse => "rev",
|
|
|
|
attr::Secure => "invis",
|
|
|
|
attr::ForegroundColor(_) => "setaf",
|
|
|
|
attr::BackgroundColor(_) => "setab"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A Terminal that knows how many colors it supports, with a reference to its
|
|
|
|
/// parsed Terminfo database record.
|
|
|
|
pub struct TerminfoTerminal<T> {
|
|
|
|
num_colors: u16,
|
|
|
|
out: T,
|
2014-05-07 10:12:04 -05:00
|
|
|
ti: Box<TermInfo>
|
2014-04-08 10:18:10 -05:00
|
|
|
}
|
|
|
|
|
2014-10-01 01:01:08 -05:00
|
|
|
impl<T: Writer+Send> Terminal<T> for TerminfoTerminal<T> {
|
2014-04-08 10:18:10 -05:00
|
|
|
fn fg(&mut self, color: color::Color) -> IoResult<bool> {
|
|
|
|
let color = self.dim_if_necessary(color);
|
|
|
|
if self.num_colors > color {
|
|
|
|
let s = expand(self.ti
|
|
|
|
.strings
|
2014-11-12 17:51:51 -06:00
|
|
|
.get("setaf")
|
2014-04-08 10:18:10 -05:00
|
|
|
.unwrap()
|
|
|
|
.as_slice(),
|
2014-11-17 02:39:01 -06:00
|
|
|
&[Number(color as int)], &mut Variables::new());
|
2014-04-08 10:18:10 -05:00
|
|
|
if s.is_ok() {
|
|
|
|
try!(self.out.write(s.unwrap().as_slice()));
|
|
|
|
return Ok(true)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(false)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn bg(&mut self, color: color::Color) -> IoResult<bool> {
|
|
|
|
let color = self.dim_if_necessary(color);
|
|
|
|
if self.num_colors > color {
|
|
|
|
let s = expand(self.ti
|
|
|
|
.strings
|
2014-11-12 17:51:51 -06:00
|
|
|
.get("setab")
|
2014-04-08 10:18:10 -05:00
|
|
|
.unwrap()
|
|
|
|
.as_slice(),
|
2014-11-17 02:39:01 -06:00
|
|
|
&[Number(color as int)], &mut Variables::new());
|
2014-04-08 10:18:10 -05:00
|
|
|
if s.is_ok() {
|
|
|
|
try!(self.out.write(s.unwrap().as_slice()));
|
|
|
|
return Ok(true)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(false)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn attr(&mut self, attr: attr::Attr) -> IoResult<bool> {
|
|
|
|
match attr {
|
|
|
|
attr::ForegroundColor(c) => self.fg(c),
|
|
|
|
attr::BackgroundColor(c) => self.bg(c),
|
|
|
|
_ => {
|
|
|
|
let cap = cap_for_attr(attr);
|
2014-11-12 17:51:51 -06:00
|
|
|
let parm = self.ti.strings.get(cap);
|
2014-04-08 10:18:10 -05:00
|
|
|
if parm.is_some() {
|
|
|
|
let s = expand(parm.unwrap().as_slice(),
|
2014-11-17 02:39:01 -06:00
|
|
|
&[],
|
2014-04-08 10:18:10 -05:00
|
|
|
&mut Variables::new());
|
|
|
|
if s.is_ok() {
|
|
|
|
try!(self.out.write(s.unwrap().as_slice()));
|
|
|
|
return Ok(true)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(false)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn supports_attr(&self, attr: attr::Attr) -> bool {
|
|
|
|
match attr {
|
|
|
|
attr::ForegroundColor(_) | attr::BackgroundColor(_) => {
|
|
|
|
self.num_colors > 0
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
let cap = cap_for_attr(attr);
|
2014-11-12 17:51:51 -06:00
|
|
|
self.ti.strings.get(cap).is_some()
|
2014-04-08 10:18:10 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn reset(&mut self) -> IoResult<()> {
|
2014-11-12 17:51:51 -06:00
|
|
|
let mut cap = self.ti.strings.get("sgr0");
|
2014-04-08 10:18:10 -05:00
|
|
|
if cap.is_none() {
|
|
|
|
// are there any terminals that have color/attrs and not sgr0?
|
|
|
|
// Try falling back to sgr, then op
|
2014-11-12 17:51:51 -06:00
|
|
|
cap = self.ti.strings.get("sgr");
|
2014-04-08 10:18:10 -05:00
|
|
|
if cap.is_none() {
|
2014-11-12 17:51:51 -06:00
|
|
|
cap = self.ti.strings.get("op");
|
2014-04-08 10:18:10 -05:00
|
|
|
}
|
|
|
|
}
|
2014-05-25 05:17:19 -05:00
|
|
|
let s = cap.map_or(Err("can't find terminfo capability `sgr0`".to_string()), |op| {
|
2014-11-17 02:39:01 -06:00
|
|
|
expand(op.as_slice(), &[], &mut Variables::new())
|
2014-04-08 10:18:10 -05:00
|
|
|
});
|
|
|
|
if s.is_ok() {
|
|
|
|
return self.out.write(s.unwrap().as_slice())
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_ref<'a>(&'a self) -> &'a T { &self.out }
|
|
|
|
|
|
|
|
fn get_mut<'a>(&'a mut self) -> &'a mut T { &mut self.out }
|
|
|
|
}
|
|
|
|
|
2014-10-01 01:01:08 -05:00
|
|
|
impl<T: Writer+Send> UnwrappableTerminal<T> for TerminfoTerminal<T> {
|
|
|
|
fn unwrap(self) -> T { self.out }
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T: Writer+Send> TerminfoTerminal<T> {
|
|
|
|
/// Returns `None` whenever the terminal cannot be created for some
|
|
|
|
/// reason.
|
|
|
|
pub fn new(out: T) -> Option<Box<Terminal<T>+Send+'static>> {
|
|
|
|
let term = match os::getenv("TERM") {
|
|
|
|
Some(t) => t,
|
|
|
|
None => {
|
|
|
|
debug!("TERM environment variable not defined");
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let entry = open(term.as_slice());
|
|
|
|
if entry.is_err() {
|
|
|
|
if os::getenv("MSYSCON").map_or(false, |s| {
|
2014-11-27 14:03:37 -06:00
|
|
|
"mintty.exe" == s
|
2014-10-01 01:01:08 -05:00
|
|
|
}) {
|
|
|
|
// msys terminal
|
|
|
|
return Some(box TerminfoTerminal {out: out,
|
|
|
|
ti: msys_terminfo(),
|
|
|
|
num_colors: 8} as Box<Terminal<T>+Send>);
|
|
|
|
}
|
|
|
|
debug!("error finding terminfo entry: {}", entry.err().unwrap());
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut file = entry.unwrap();
|
|
|
|
let ti = parse(&mut file, false);
|
|
|
|
if ti.is_err() {
|
|
|
|
debug!("error parsing terminfo entry: {}", ti.unwrap_err());
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
let inf = ti.unwrap();
|
2014-11-12 17:51:51 -06:00
|
|
|
let nc = if inf.strings.get("setaf").is_some()
|
|
|
|
&& inf.strings.get("setab").is_some() {
|
|
|
|
inf.numbers.get("colors").map_or(0, |&n| n)
|
2014-10-01 01:01:08 -05:00
|
|
|
} else { 0 };
|
|
|
|
|
|
|
|
return Some(box TerminfoTerminal {out: out,
|
|
|
|
ti: inf,
|
|
|
|
num_colors: nc} as Box<Terminal<T>+Send>);
|
|
|
|
}
|
|
|
|
|
2014-04-08 10:18:10 -05:00
|
|
|
fn dim_if_necessary(&self, color: color::Color) -> color::Color {
|
|
|
|
if color >= self.num_colors && color >= 8 && color < 16 {
|
|
|
|
color-8
|
|
|
|
} else { color }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl<T: Writer> Writer for TerminfoTerminal<T> {
|
|
|
|
fn write(&mut self, buf: &[u8]) -> IoResult<()> {
|
|
|
|
self.out.write(buf)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn flush(&mut self) -> IoResult<()> {
|
|
|
|
self.out.flush()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|