]> git.proxmox.com Git - rustc.git/blame - src/libterm/terminfo/mod.rs
New upstream version 1.42.0+dfsg1
[rustc.git] / src / libterm / terminfo / mod.rs
CommitLineData
1a4d82fc
JJ
1//! Terminfo database interface.
2
3use std::collections::HashMap;
85aaf69f 4use std::env;
92a42be0
SL
5use std::error;
6use std::fmt;
7use std::fs::File;
9fa01778 8use std::io::{self, prelude::*, BufReader};
92a42be0 9use std::path::Path;
1a4d82fc 10
9fa01778 11use crate::color;
dfeec247 12use crate::Attr;
9fa01778 13use crate::Terminal;
1a4d82fc 14
dfeec247
XL
15use parm::{expand, Param, Variables};
16use parser::compiled::{msys_terminfo, parse};
9fa01778 17use searcher::get_dbpath_for_term;
1a4d82fc
JJ
18
19/// A parsed terminfo database entry.
85aaf69f 20#[derive(Debug)]
1a4d82fc
JJ
21pub struct TermInfo {
22 /// Names for the terminal
92a42be0 23 pub names: Vec<String>,
1a4d82fc
JJ
24 /// Map of capability name to boolean value
25 pub bools: HashMap<String, bool>,
26 /// Map of capability name to numeric value
dfeec247 27 pub numbers: HashMap<String, u32>,
1a4d82fc 28 /// Map of capability name to raw (unexpanded) string
92a42be0
SL
29 pub strings: HashMap<String, Vec<u8>>,
30}
31
32/// A terminfo creation error.
33#[derive(Debug)]
34pub enum Error {
35 /// TermUnset Indicates that the environment doesn't include enough information to find
36 /// the terminfo entry.
37 TermUnset,
38 /// MalformedTerminfo indicates that parsing the terminfo entry failed.
39 MalformedTerminfo(String),
40 /// io::Error forwards any io::Errors encountered when finding or reading the terminfo entry.
41 IoError(io::Error),
42}
43
44impl error::Error for Error {
dfeec247 45 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
9fa01778 46 use Error::*;
dfeec247
XL
47 match self {
48 IoError(e) => Some(e),
92a42be0
SL
49 _ => None,
50 }
51 }
52}
53
54impl fmt::Display for Error {
9fa01778
XL
55 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56 use Error::*;
b7449926
XL
57 match *self {
58 TermUnset => Ok(()),
59 MalformedTerminfo(ref e) => e.fmt(f),
60 IoError(ref e) => e.fmt(f),
92a42be0
SL
61 }
62 }
63}
64
65impl TermInfo {
9fa01778 66 /// Creates a TermInfo based on current environment.
92a42be0
SL
67 pub fn from_env() -> Result<TermInfo, Error> {
68 let term = match env::var("TERM") {
69 Ok(name) => TermInfo::from_name(&name),
70 Err(..) => return Err(Error::TermUnset),
71 };
72
60c5eb7d 73 if term.is_err() && env::var("MSYSCON").map_or(false, |s| "mintty.exe" == s) {
92a42be0
SL
74 // msys terminal
75 Ok(msys_terminfo())
76 } else {
77 term
78 }
79 }
80
9fa01778 81 /// Creates a TermInfo for the named terminal.
92a42be0
SL
82 pub fn from_name(name: &str) -> Result<TermInfo, Error> {
83 get_dbpath_for_term(name)
84 .ok_or_else(|| {
85 Error::IoError(io::Error::new(io::ErrorKind::NotFound, "terminfo file not found"))
86 })
87 .and_then(|p| TermInfo::from_path(&(*p)))
88 }
89
90 /// Parse the given TermInfo.
91 pub fn from_path<P: AsRef<Path>>(path: P) -> Result<TermInfo, Error> {
92 Self::_from_path(path.as_ref())
93 }
94 // Keep the metadata small
95 fn _from_path(path: &Path) -> Result<TermInfo, Error> {
b7449926 96 let file = File::open(path).map_err(Error::IoError)?;
92a42be0 97 let mut reader = BufReader::new(file);
b7449926 98 parse(&mut reader, false).map_err(Error::MalformedTerminfo)
92a42be0 99 }
1a4d82fc
JJ
100}
101
102pub mod searcher;
103
104/// TermInfo format parsing.
105pub mod parser {
106 //! ncurses-compatible compiled terminfo format parsing (term(5))
107 pub mod compiled;
108}
109pub mod parm;
110
92a42be0 111fn cap_for_attr(attr: Attr) -> &'static str {
1a4d82fc 112 match attr {
92a42be0
SL
113 Attr::Bold => "bold",
114 Attr::Dim => "dim",
115 Attr::Italic(true) => "sitm",
116 Attr::Italic(false) => "ritm",
117 Attr::Underline(true) => "smul",
118 Attr::Underline(false) => "rmul",
119 Attr::Blink => "blink",
120 Attr::Standout(true) => "smso",
121 Attr::Standout(false) => "rmso",
122 Attr::Reverse => "rev",
123 Attr::Secure => "invis",
124 Attr::ForegroundColor(_) => "setaf",
125 Attr::BackgroundColor(_) => "setab",
1a4d82fc
JJ
126 }
127}
128
129/// A Terminal that knows how many colors it supports, with a reference to its
130/// parsed Terminfo database record.
131pub struct TerminfoTerminal<T> {
dfeec247 132 num_colors: u32,
1a4d82fc 133 out: T,
92a42be0 134 ti: TermInfo,
1a4d82fc
JJ
135}
136
9cc50fc6 137impl<T: Write + Send> Terminal for TerminfoTerminal<T> {
92a42be0 138 type Output = T;
c34b1796 139 fn fg(&mut self, color: color::Color) -> io::Result<bool> {
1a4d82fc
JJ
140 let color = self.dim_if_necessary(color);
141 if self.num_colors > color {
92a42be0 142 return self.apply_cap("setaf", &[Param::Number(color as i32)]);
1a4d82fc
JJ
143 }
144 Ok(false)
145 }
146
c34b1796 147 fn bg(&mut self, color: color::Color) -> io::Result<bool> {
1a4d82fc
JJ
148 let color = self.dim_if_necessary(color);
149 if self.num_colors > color {
92a42be0 150 return self.apply_cap("setab", &[Param::Number(color as i32)]);
1a4d82fc
JJ
151 }
152 Ok(false)
153 }
154
92a42be0 155 fn attr(&mut self, attr: Attr) -> io::Result<bool> {
1a4d82fc 156 match attr {
92a42be0
SL
157 Attr::ForegroundColor(c) => self.fg(c),
158 Attr::BackgroundColor(c) => self.bg(c),
159 _ => self.apply_cap(cap_for_attr(attr), &[]),
1a4d82fc
JJ
160 }
161 }
162
92a42be0 163 fn supports_attr(&self, attr: Attr) -> bool {
1a4d82fc 164 match attr {
92a42be0 165 Attr::ForegroundColor(_) | Attr::BackgroundColor(_) => self.num_colors > 0,
1a4d82fc
JJ
166 _ => {
167 let cap = cap_for_attr(attr);
168 self.ti.strings.get(cap).is_some()
169 }
170 }
171 }
172
92a42be0
SL
173 fn reset(&mut self) -> io::Result<bool> {
174 // are there any terminals that have color/attrs and not sgr0?
175 // Try falling back to sgr, then op
dfeec247
XL
176 let cmd =
177 match ["sgr0", "sgr", "op"].iter().filter_map(|cap| self.ti.strings.get(*cap)).next() {
178 Some(op) => match expand(&op, &[], &mut Variables::new()) {
92a42be0
SL
179 Ok(cmd) => cmd,
180 Err(e) => return Err(io::Error::new(io::ErrorKind::InvalidData, e)),
dfeec247
XL
181 },
182 None => return Ok(false),
183 };
92a42be0 184 self.out.write_all(&cmd).and(Ok(true))
1a4d82fc
JJ
185 }
186
7453a54e 187 fn get_ref(&self) -> &T {
92a42be0
SL
188 &self.out
189 }
1a4d82fc 190
7453a54e 191 fn get_mut(&mut self) -> &mut T {
92a42be0
SL
192 &mut self.out
193 }
1a4d82fc 194
92a42be0 195 fn into_inner(self) -> T
dfeec247
XL
196 where
197 Self: Sized,
92a42be0
SL
198 {
199 self.out
200 }
1a4d82fc
JJ
201}
202
9cc50fc6 203impl<T: Write + Send> TerminfoTerminal<T> {
9fa01778 204 /// Creates a new TerminfoTerminal with the given TermInfo and Write.
92a42be0 205 pub fn new_with_terminfo(out: T, terminfo: TermInfo) -> TerminfoTerminal<T> {
dfeec247
XL
206 let nc = if terminfo.strings.contains_key("setaf") && terminfo.strings.contains_key("setab")
207 {
92a42be0
SL
208 terminfo.numbers.get("colors").map_or(0, |&n| n)
209 } else {
210 0
1a4d82fc
JJ
211 };
212
dfeec247 213 TerminfoTerminal { out, ti: terminfo, num_colors: nc }
92a42be0 214 }
1a4d82fc 215
9fa01778 216 /// Creates a new TerminfoTerminal for the current environment with the given Write.
92a42be0
SL
217 ///
218 /// Returns `None` when the terminfo cannot be found or parsed.
219 pub fn new(out: T) -> Option<TerminfoTerminal<T>> {
220 TermInfo::from_env().map(move |ti| TerminfoTerminal::new_with_terminfo(out, ti)).ok()
1a4d82fc
JJ
221 }
222
223 fn dim_if_necessary(&self, color: color::Color) -> color::Color {
dfeec247 224 if color >= self.num_colors && color >= 8 && color < 16 { color - 8 } else { color }
92a42be0
SL
225 }
226
227 fn apply_cap(&mut self, cmd: &str, params: &[Param]) -> io::Result<bool> {
228 match self.ti.strings.get(cmd) {
dfeec247
XL
229 Some(cmd) => match expand(&cmd, params, &mut Variables::new()) {
230 Ok(s) => self.out.write_all(&s).and(Ok(true)),
231 Err(e) => Err(io::Error::new(io::ErrorKind::InvalidData, e)),
232 },
92a42be0
SL
233 None => Ok(false),
234 }
1a4d82fc
JJ
235 }
236}
237
c34b1796
AL
238impl<T: Write> Write for TerminfoTerminal<T> {
239 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
240 self.out.write(buf)
1a4d82fc
JJ
241 }
242
c34b1796 243 fn flush(&mut self) -> io::Result<()> {
1a4d82fc
JJ
244 self.out.flush()
245 }
246}