summaryrefslogtreecommitdiffstats
path: root/library/test/src/term/terminfo/mod.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
commit698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch)
tree173a775858bd501c378080a10dca74132f05bc50 /library/test/src/term/terminfo/mod.rs
parentInitial commit. (diff)
downloadrustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz
rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip
Adding upstream version 1.64.0+dfsg1.upstream/1.64.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'library/test/src/term/terminfo/mod.rs')
-rw-r--r--library/test/src/term/terminfo/mod.rs185
1 files changed, 185 insertions, 0 deletions
diff --git a/library/test/src/term/terminfo/mod.rs b/library/test/src/term/terminfo/mod.rs
new file mode 100644
index 000000000..694473f52
--- /dev/null
+++ b/library/test/src/term/terminfo/mod.rs
@@ -0,0 +1,185 @@
+//! Terminfo database interface.
+
+use std::collections::HashMap;
+use std::env;
+use std::error;
+use std::fmt;
+use std::fs::File;
+use std::io::{self, prelude::*, BufReader};
+use std::path::Path;
+
+use super::color;
+use super::Terminal;
+
+use parm::{expand, Param, Variables};
+use parser::compiled::{msys_terminfo, parse};
+use searcher::get_dbpath_for_term;
+
+/// A parsed terminfo database entry.
+#[allow(unused)]
+#[derive(Debug)]
+pub(crate) struct TermInfo {
+ /// Names for the terminal
+ pub(crate) names: Vec<String>,
+ /// Map of capability name to boolean value
+ pub(crate) bools: HashMap<String, bool>,
+ /// Map of capability name to numeric value
+ pub(crate) numbers: HashMap<String, u32>,
+ /// Map of capability name to raw (unexpanded) string
+ pub(crate) strings: HashMap<String, Vec<u8>>,
+}
+
+/// A terminfo creation error.
+#[derive(Debug)]
+pub(crate) enum Error {
+ /// TermUnset Indicates that the environment doesn't include enough information to find
+ /// the terminfo entry.
+ TermUnset,
+ /// MalformedTerminfo indicates that parsing the terminfo entry failed.
+ MalformedTerminfo(String),
+ /// io::Error forwards any io::Errors encountered when finding or reading the terminfo entry.
+ IoError(io::Error),
+}
+
+impl error::Error for Error {
+ fn source(&self) -> Option<&(dyn error::Error + 'static)> {
+ use Error::*;
+ match self {
+ IoError(e) => Some(e),
+ _ => None,
+ }
+ }
+}
+
+impl fmt::Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ use Error::*;
+ match *self {
+ TermUnset => Ok(()),
+ MalformedTerminfo(ref e) => e.fmt(f),
+ IoError(ref e) => e.fmt(f),
+ }
+ }
+}
+
+impl TermInfo {
+ /// Creates a TermInfo based on current environment.
+ pub(crate) fn from_env() -> Result<TermInfo, Error> {
+ let term = match env::var("TERM") {
+ Ok(name) => TermInfo::from_name(&name),
+ Err(..) => return Err(Error::TermUnset),
+ };
+
+ if term.is_err() && env::var("MSYSCON").map_or(false, |s| "mintty.exe" == s) {
+ // msys terminal
+ Ok(msys_terminfo())
+ } else {
+ term
+ }
+ }
+
+ /// Creates a TermInfo for the named terminal.
+ pub(crate) fn from_name(name: &str) -> Result<TermInfo, Error> {
+ get_dbpath_for_term(name)
+ .ok_or_else(|| {
+ Error::IoError(io::Error::new(io::ErrorKind::NotFound, "terminfo file not found"))
+ })
+ .and_then(|p| TermInfo::from_path(&(*p)))
+ }
+
+ /// Parse the given TermInfo.
+ pub(crate) fn from_path<P: AsRef<Path>>(path: P) -> Result<TermInfo, Error> {
+ Self::_from_path(path.as_ref())
+ }
+ // Keep the metadata small
+ fn _from_path(path: &Path) -> Result<TermInfo, Error> {
+ let file = File::open(path).map_err(Error::IoError)?;
+ let mut reader = BufReader::new(file);
+ parse(&mut reader, false).map_err(Error::MalformedTerminfo)
+ }
+}
+
+pub(crate) mod searcher;
+
+/// TermInfo format parsing.
+pub(crate) mod parser {
+ //! ncurses-compatible compiled terminfo format parsing (term(5))
+ pub(crate) mod compiled;
+}
+pub(crate) mod parm;
+
+/// A Terminal that knows how many colors it supports, with a reference to its
+/// parsed Terminfo database record.
+pub(crate) struct TerminfoTerminal<T> {
+ num_colors: u32,
+ out: T,
+ ti: TermInfo,
+}
+
+impl<T: Write + Send> Terminal for TerminfoTerminal<T> {
+ fn fg(&mut self, color: color::Color) -> io::Result<bool> {
+ let color = self.dim_if_necessary(color);
+ if self.num_colors > color {
+ return self.apply_cap("setaf", &[Param::Number(color as i32)]);
+ }
+ Ok(false)
+ }
+
+ fn reset(&mut self) -> io::Result<bool> {
+ // are there any terminals that have color/attrs and not sgr0?
+ // Try falling back to sgr, then op
+ let cmd = match ["sgr0", "sgr", "op"].iter().find_map(|cap| self.ti.strings.get(*cap)) {
+ Some(op) => match expand(&op, &[], &mut Variables::new()) {
+ Ok(cmd) => cmd,
+ Err(e) => return Err(io::Error::new(io::ErrorKind::InvalidData, e)),
+ },
+ None => return Ok(false),
+ };
+ self.out.write_all(&cmd).and(Ok(true))
+ }
+}
+
+impl<T: Write + Send> TerminfoTerminal<T> {
+ /// Creates a new TerminfoTerminal with the given TermInfo and Write.
+ pub(crate) fn new_with_terminfo(out: T, terminfo: TermInfo) -> TerminfoTerminal<T> {
+ let nc = if terminfo.strings.contains_key("setaf") && terminfo.strings.contains_key("setab")
+ {
+ terminfo.numbers.get("colors").map_or(0, |&n| n)
+ } else {
+ 0
+ };
+
+ TerminfoTerminal { out, ti: terminfo, num_colors: nc }
+ }
+
+ /// Creates a new TerminfoTerminal for the current environment with the given Write.
+ ///
+ /// Returns `None` when the terminfo cannot be found or parsed.
+ pub(crate) fn new(out: T) -> Option<TerminfoTerminal<T>> {
+ TermInfo::from_env().map(move |ti| TerminfoTerminal::new_with_terminfo(out, ti)).ok()
+ }
+
+ fn dim_if_necessary(&self, color: color::Color) -> color::Color {
+ if color >= self.num_colors && color >= 8 && color < 16 { color - 8 } else { color }
+ }
+
+ fn apply_cap(&mut self, cmd: &str, params: &[Param]) -> io::Result<bool> {
+ match self.ti.strings.get(cmd) {
+ Some(cmd) => match expand(&cmd, params, &mut Variables::new()) {
+ Ok(s) => self.out.write_all(&s).and(Ok(true)),
+ Err(e) => Err(io::Error::new(io::ErrorKind::InvalidData, e)),
+ },
+ None => Ok(false),
+ }
+ }
+}
+
+impl<T: Write> Write for TerminfoTerminal<T> {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ self.out.write(buf)
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ self.out.flush()
+ }
+}