summaryrefslogtreecommitdiffstats
path: root/vendor/term/src
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 /vendor/term/src
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 'vendor/term/src')
-rw-r--r--vendor/term/src/lib.rs392
-rw-r--r--vendor/term/src/terminfo/mod.rs400
-rw-r--r--vendor/term/src/terminfo/parm.rs741
-rw-r--r--vendor/term/src/terminfo/parser/compiled.rs204
-rw-r--r--vendor/term/src/terminfo/parser/names.rs553
-rw-r--r--vendor/term/src/terminfo/searcher.rs93
-rw-r--r--vendor/term/src/win.rs434
7 files changed, 2817 insertions, 0 deletions
diff --git a/vendor/term/src/lib.rs b/vendor/term/src/lib.rs
new file mode 100644
index 000000000..d23d873ca
--- /dev/null
+++ b/vendor/term/src/lib.rs
@@ -0,0 +1,392 @@
+// Copyright 2013-2019 The Rust Project Developers. See the COPYRIGHT
+// 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.
+
+//! Terminal formatting library.
+//!
+//! This crate provides the `Terminal` trait, which abstracts over an [ANSI
+//! Terminal][ansi] to provide color printing, among other things. There are two
+//! implementations, the `TerminfoTerminal`, which uses control characters from
+//! a [terminfo][ti] database, and `WinConsole`, which uses the [Win32 Console
+//! API][win].
+//!
+//! # Usage
+//!
+//! This crate is [on crates.io](https://crates.io/crates/term) and can be
+//! used by adding `term` to the dependencies in your project's `Cargo.toml`.
+//!
+//! ```toml
+//! [dependencies]
+//!
+//! term = "*"
+//! ```
+//!
+//! and this to your crate root:
+//!
+//! ```rust
+//! extern crate term;
+//! ```
+//!
+//! # Examples
+//!
+//! ```no_run
+//! extern crate term;
+//! use std::io::prelude::*;
+//!
+//! fn main() {
+//! let mut t = term::stdout().unwrap();
+//!
+//! t.fg(term::color::GREEN).unwrap();
+//! write!(t, "hello, ").unwrap();
+//!
+//! t.fg(term::color::RED).unwrap();
+//! writeln!(t, "world!").unwrap();
+//!
+//! t.reset().unwrap();
+//! }
+//! ```
+//!
+//! [ansi]: https://en.wikipedia.org/wiki/ANSI_escape_code
+//! [win]: http://msdn.microsoft.com/en-us/library/windows/desktop/ms682010%28v=vs.85%29.aspx
+//! [ti]: https://en.wikipedia.org/wiki/Terminfo
+
+#![doc(
+ html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
+ html_favicon_url = "https://doc.rust-lang.org/favicon.ico",
+ html_root_url = "https://stebalien.github.io/doc/term/term/",
+ test(attr(deny(warnings)))
+)]
+#![deny(missing_docs)]
+#![cfg_attr(test, deny(warnings))]
+#![allow(clippy::redundant_field_names)]
+
+use std::io::prelude::*;
+
+pub use crate::terminfo::TerminfoTerminal;
+#[cfg(windows)]
+pub use win::{WinConsole, WinConsoleInfo};
+
+use std::io::{self, Stderr, Stdout};
+
+pub mod terminfo;
+
+#[cfg(windows)]
+mod win;
+
+/// Alias for stdout terminals.
+pub type StdoutTerminal = dyn Terminal<Output = Stdout> + Send;
+/// Alias for stderr terminals.
+pub type StderrTerminal = dyn Terminal<Output = Stderr> + Send;
+
+#[cfg(not(windows))]
+/// Return a Terminal wrapping stdout, or None if a terminal couldn't be
+/// opened.
+pub fn stdout() -> Option<Box<StdoutTerminal>> {
+ TerminfoTerminal::new(io::stdout()).map(|t| Box::new(t) as Box<StdoutTerminal>)
+}
+
+#[cfg(windows)]
+/// Return a Terminal wrapping stdout, or None if a terminal couldn't be
+/// opened.
+pub fn stdout() -> Option<Box<StdoutTerminal>> {
+ TerminfoTerminal::new(io::stdout())
+ .map(|t| Box::new(t) as Box<StdoutTerminal>)
+ .or_else(|| {
+ WinConsole::new(io::stdout())
+ .ok()
+ .map(|t| Box::new(t) as Box<StdoutTerminal>)
+ })
+}
+
+#[cfg(not(windows))]
+/// Return a Terminal wrapping stderr, or None if a terminal couldn't be
+/// opened.
+pub fn stderr() -> Option<Box<StderrTerminal>> {
+ TerminfoTerminal::new(io::stderr()).map(|t| Box::new(t) as Box<StderrTerminal>)
+}
+
+#[cfg(windows)]
+/// Return a Terminal wrapping stderr, or None if a terminal couldn't be
+/// opened.
+pub fn stderr() -> Option<Box<StderrTerminal>> {
+ TerminfoTerminal::new(io::stderr())
+ .map(|t| Box::new(t) as Box<StderrTerminal>)
+ .or_else(|| {
+ WinConsole::new(io::stderr())
+ .ok()
+ .map(|t| Box::new(t) as Box<StderrTerminal>)
+ })
+}
+
+/// Terminal color definitions
+#[allow(missing_docs)]
+pub mod color {
+ /// Number for a terminal color
+ pub type Color = u32;
+
+ pub const BLACK: Color = 0;
+ pub const RED: Color = 1;
+ pub const GREEN: Color = 2;
+ pub const YELLOW: Color = 3;
+ pub const BLUE: Color = 4;
+ pub const MAGENTA: Color = 5;
+ pub const CYAN: Color = 6;
+ pub const WHITE: Color = 7;
+
+ pub const BRIGHT_BLACK: Color = 8;
+ pub const BRIGHT_RED: Color = 9;
+ pub const BRIGHT_GREEN: Color = 10;
+ pub const BRIGHT_YELLOW: Color = 11;
+ pub const BRIGHT_BLUE: Color = 12;
+ pub const BRIGHT_MAGENTA: Color = 13;
+ pub const BRIGHT_CYAN: Color = 14;
+ pub const BRIGHT_WHITE: Color = 15;
+}
+
+/// Terminal attributes for use with term.attr().
+///
+/// Most attributes can only be turned on and must be turned off with term.reset().
+/// The ones that can be turned off explicitly take a boolean value.
+/// Color is also represented as an attribute for convenience.
+#[derive(Debug, PartialEq, Hash, Eq, Copy, Clone)]
+pub enum Attr {
+ /// Bold (or possibly bright) mode
+ Bold,
+ /// Dim mode, also called faint or half-bright. Often not supported
+ Dim,
+ /// Italics mode. Often not supported
+ Italic(bool),
+ /// Underline mode
+ Underline(bool),
+ /// Blink mode
+ Blink,
+ /// Standout mode. Often implemented as Reverse, sometimes coupled with Bold
+ Standout(bool),
+ /// Reverse mode, inverts the foreground and background colors
+ Reverse,
+ /// Secure mode, also called invis mode. Hides the printed text
+ Secure,
+ /// Convenience attribute to set the foreground color
+ ForegroundColor(color::Color),
+ /// Convenience attribute to set the background color
+ BackgroundColor(color::Color),
+}
+
+/// An error arising from interacting with the terminal.
+#[derive(Debug)]
+pub enum Error {
+ /// Indicates an error from any underlying IO
+ Io(io::Error),
+ /// Indicates an error during terminfo parsing
+ TerminfoParsing(terminfo::Error),
+ /// Indicates an error expanding a parameterized string from the terminfo database
+ ParameterizedExpansion(terminfo::parm::Error),
+ /// Indicates that the terminal does not support the requested operation.
+ NotSupported,
+ /// Indicates that the `TERM` environment variable was unset, and thus we were unable to detect
+ /// which terminal we should be using.
+ TermUnset,
+ /// Indicates that we were unable to find a terminfo entry for the requested terminal.
+ TerminfoEntryNotFound,
+ /// Indicates that the cursor could not be moved to the requested position.
+ CursorDestinationInvalid,
+ /// Indicates that the terminal does not support displaying the requested color.
+ ///
+ /// This is like `NotSupported`, but more specific.
+ ColorOutOfRange,
+ #[doc(hidden)]
+ /// Please don't match against this - if you do, we can't promise we won't break your crate
+ /// with a semver-compliant version bump.
+ __Nonexhaustive,
+}
+
+// manually implemented because std::io::Error does not implement Eq/PartialEq
+impl std::cmp::PartialEq for Error {
+ fn eq(&self, other: &Error) -> bool {
+ use crate::Error::*;
+ match *self {
+ Io(_) => false,
+ TerminfoParsing(ref inner1) => match *other {
+ TerminfoParsing(ref inner2) => inner1 == inner2,
+ _ => false,
+ },
+ ParameterizedExpansion(ref inner1) => match *other {
+ ParameterizedExpansion(ref inner2) => inner1 == inner2,
+ _ => false,
+ },
+ NotSupported => match *other {
+ NotSupported => true,
+ _ => false,
+ },
+ TermUnset => match *other {
+ TermUnset => true,
+ _ => false,
+ },
+ TerminfoEntryNotFound => match *other {
+ TerminfoEntryNotFound => true,
+ _ => false,
+ },
+ CursorDestinationInvalid => match *other {
+ CursorDestinationInvalid => true,
+ _ => false,
+ },
+ ColorOutOfRange => match *other {
+ ColorOutOfRange => true,
+ _ => false,
+ },
+ __Nonexhaustive => match *other {
+ __Nonexhaustive => true,
+ _ => false,
+ },
+ }
+ }
+}
+
+/// The canonical `Result` type using this crate's Error type.
+pub type Result<T> = std::result::Result<T, Error>;
+
+impl std::fmt::Display for Error {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ use crate::Error::*;
+ match *self {
+ Io(ref io) => io.fmt(f),
+ TerminfoParsing(ref e) => e.fmt(f),
+ ParameterizedExpansion(ref e) => e.fmt(f),
+ NotSupported => f.write_str("operation not supported by the terminal"),
+ TermUnset => {
+ f.write_str("TERM environment variable unset, unable to detect a terminal")
+ }
+ TerminfoEntryNotFound => {
+ f.write_str("could not find a terminfo entry for this terminal")
+ }
+ CursorDestinationInvalid => f.write_str("could not move cursor to requested position"),
+ ColorOutOfRange => f.write_str("color not supported by the terminal"),
+ __Nonexhaustive => f.write_str("placeholder variant that shouldn't be used"),
+ }
+ }
+}
+
+impl std::error::Error for Error {
+ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+ match *self {
+ Error::Io(ref io) => Some(io),
+ Error::TerminfoParsing(ref e) => Some(e),
+ Error::ParameterizedExpansion(ref e) => Some(e),
+ _ => None,
+ }
+ }
+}
+
+impl From<Error> for io::Error {
+ fn from(err: Error) -> io::Error {
+ let kind = match err {
+ Error::Io(ref e) => e.kind(),
+ _ => io::ErrorKind::Other,
+ };
+ io::Error::new(kind, err)
+ }
+}
+
+impl std::convert::From<io::Error> for Error {
+ fn from(val: io::Error) -> Self {
+ Error::Io(val)
+ }
+}
+
+impl std::convert::From<terminfo::Error> for Error {
+ fn from(val: terminfo::Error) -> Self {
+ Error::TerminfoParsing(val)
+ }
+}
+
+impl std::convert::From<terminfo::parm::Error> for Error {
+ fn from(val: terminfo::parm::Error) -> Self {
+ Error::ParameterizedExpansion(val)
+ }
+}
+
+/// A terminal with similar capabilities to an ANSI Terminal
+/// (foreground/background colors etc).
+pub trait Terminal: Write {
+ /// The terminal's output writer type.
+ type Output: Write;
+
+ /// Sets the foreground color to the given color.
+ ///
+ /// If the color is a bright color, but the terminal only supports 8 colors,
+ /// the corresponding normal color will be used instead.
+ ///
+ /// Returns `Ok(())` if the color change code was sent to the terminal, or `Err(e)` if there
+ /// was an error.
+ fn fg(&mut self, color: color::Color) -> Result<()>;
+
+ /// Sets the background color to the given color.
+ ///
+ /// If the color is a bright color, but the terminal only supports 8 colors,
+ /// the corresponding normal color will be used instead.
+ ///
+ /// Returns `Ok(())` if the color change code was sent to the terminal, or `Err(e)` if there
+ /// was an error.
+ fn bg(&mut self, color: color::Color) -> Result<()>;
+
+ /// Sets the given terminal attribute, if supported. Returns `Ok(())` if the attribute is
+ /// supported and was sent to the terminal, or `Err(e)` if there was an error or the attribute
+ /// wasn't supported.
+ fn attr(&mut self, attr: Attr) -> Result<()>;
+
+ /// Returns whether the given terminal attribute is supported.
+ fn supports_attr(&self, attr: Attr) -> bool;
+
+ /// Resets all terminal attributes and colors to their defaults.
+ ///
+ /// Returns `Ok(())` if the reset code was printed, or `Err(e)` if there was an error.
+ ///
+ /// *Note: This does not flush.*
+ ///
+ /// That means the reset command may get buffered so, if you aren't planning on doing anything
+ /// else that might flush stdout's buffer (e.g. writing a line of text), you should flush after
+ /// calling reset.
+ fn reset(&mut self) -> Result<()>;
+
+ /// Returns true if reset is supported.
+ fn supports_reset(&self) -> bool;
+
+ /// Returns true if color is fully supported.
+ ///
+ /// If this function returns `true`, `bg`, `fg`, and `reset` will never
+ /// return `Err(Error::NotSupported)`.
+ fn supports_color(&self) -> bool;
+
+ /// Moves the cursor up one line.
+ ///
+ /// Returns `Ok(())` if the cursor movement code was printed, or `Err(e)` if there was an
+ /// error.
+ fn cursor_up(&mut self) -> Result<()>;
+
+ /// Deletes the text from the cursor location to the end of the line.
+ ///
+ /// Returns `Ok(())` if the deletion code was printed, or `Err(e)` if there was an error.
+ fn delete_line(&mut self) -> Result<()>;
+
+ /// Moves the cursor to the left edge of the current line.
+ ///
+ /// Returns `Ok(true)` if the deletion code was printed, or `Err(e)` if there was an error.
+ fn carriage_return(&mut self) -> Result<()>;
+
+ /// Gets an immutable reference to the stream inside
+ fn get_ref(&self) -> &Self::Output;
+
+ /// Gets a mutable reference to the stream inside
+ fn get_mut(&mut self) -> &mut Self::Output;
+
+ /// Returns the contained stream, destroying the `Terminal`
+ fn into_inner(self) -> Self::Output
+ where
+ Self: Sized;
+}
diff --git a/vendor/term/src/terminfo/mod.rs b/vendor/term/src/terminfo/mod.rs
new file mode 100644
index 000000000..804c8dc01
--- /dev/null
+++ b/vendor/term/src/terminfo/mod.rs
@@ -0,0 +1,400 @@
+// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
+// 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.
+
+//! Terminfo database interface.
+
+use std::collections::HashMap;
+use std::env;
+use std::fs::File;
+use std::io;
+use std::io::prelude::*;
+use std::io::BufReader;
+use std::path::Path;
+
+#[cfg(windows)]
+use crate::win;
+
+use self::parm::{expand, Param, Variables};
+use self::parser::compiled::parse;
+use self::searcher::get_dbpath_for_term;
+use self::Error::*;
+use crate::color;
+use crate::Attr;
+use crate::Result;
+use crate::Terminal;
+
+/// Returns true if the named terminal supports basic ANSI escape codes.
+fn is_ansi(name: &str) -> bool {
+ // SORTED! We binary search this.
+ static ANSI_TERM_PREFIX: &[&str] = &[
+ "Eterm", "ansi", "eterm", "iterm", "konsole", "linux", "mrxvt", "msyscon", "rxvt",
+ "screen", "tmux", "xterm",
+ ];
+ match ANSI_TERM_PREFIX.binary_search(&name) {
+ Ok(_) => true,
+ Err(0) => false,
+ Err(idx) => name.starts_with(ANSI_TERM_PREFIX[idx - 1]),
+ }
+}
+
+/// A parsed terminfo database entry.
+#[derive(Debug, Clone)]
+pub struct TermInfo {
+ /// Names for the terminal
+ pub names: Vec<String>,
+ /// Map of capability name to boolean value
+ pub bools: HashMap<&'static str, bool>,
+ /// Map of capability name to numeric value
+ pub numbers: HashMap<&'static str, u32>,
+ /// Map of capability name to raw (unexpanded) string
+ pub strings: HashMap<&'static str, Vec<u8>>,
+}
+
+impl TermInfo {
+ /// Create a `TermInfo` based on current environment.
+ pub fn from_env() -> Result<TermInfo> {
+ let term_var = env::var("TERM").ok();
+ let term_name = term_var.as_ref().map(|s| &**s).or_else(|| {
+ env::var("MSYSCON").ok().and_then(|s| {
+ if s == "mintty.exe" {
+ Some("msyscon")
+ } else {
+ None
+ }
+ })
+ });
+
+ #[cfg(windows)]
+ {
+ if term_name.is_none() && win::supports_ansi() {
+ // Microsoft people seem to be fine with pretending to be xterm:
+ // https://github.com/Microsoft/WSL/issues/1446
+ // The basic ANSI fallback terminal will be uses.
+ return TermInfo::from_name("xterm");
+ }
+ }
+
+ if let Some(term_name) = term_name {
+ TermInfo::from_name(term_name)
+ } else {
+ Err(crate::Error::TermUnset)
+ }
+ }
+
+ /// Create a `TermInfo` for the named terminal.
+ pub fn from_name(name: &str) -> Result<TermInfo> {
+ if let Some(path) = get_dbpath_for_term(name) {
+ match TermInfo::from_path(&path) {
+ Ok(term) => return Ok(term),
+ // Skip IO Errors (e.g., permission denied).
+ Err(crate::Error::Io(_)) => {}
+ // Don't ignore malformed terminfo databases.
+ Err(e) => return Err(e),
+ }
+ }
+ // Basic ANSI fallback terminal.
+ if is_ansi(name) {
+ let mut strings = HashMap::new();
+ strings.insert("sgr0", b"\x1B[0m".to_vec());
+ strings.insert("bold", b"\x1B[1m".to_vec());
+ strings.insert("setaf", b"\x1B[3%p1%dm".to_vec());
+ strings.insert("setab", b"\x1B[4%p1%dm".to_vec());
+
+ let mut numbers = HashMap::new();
+ numbers.insert("colors", 8);
+
+ Ok(TermInfo {
+ names: vec![name.to_owned()],
+ bools: HashMap::new(),
+ numbers: numbers,
+ strings: strings,
+ })
+ } else {
+ Err(crate::Error::TerminfoEntryNotFound)
+ }
+ }
+
+ /// Parse the given `TermInfo`.
+ pub fn from_path<P: AsRef<Path>>(path: P) -> Result<TermInfo> {
+ Self::_from_path(path.as_ref())
+ }
+ // Keep the metadata small
+ // (That is, this uses a &Path so that this function need not be instantiated
+ // for every type
+ // which implements AsRef<Path>. One day, if/when rustc is a bit smarter, it
+ // might do this for
+ // us. Alas. )
+ fn _from_path(path: &Path) -> Result<TermInfo> {
+ let file = File::open(path).map_err(crate::Error::Io)?;
+ let mut reader = BufReader::new(file);
+ parse(&mut reader, false)
+ }
+
+ /// Retrieve a capability `cmd` and expand it with `params`, writing result to `out`.
+ pub fn apply_cap(&self, cmd: &str, params: &[Param], out: &mut dyn io::Write) -> Result<()> {
+ match self.strings.get(cmd) {
+ Some(cmd) => match expand(cmd, params, &mut Variables::new()) {
+ Ok(s) => {
+ out.write_all(&s)?;
+ Ok(())
+ }
+ Err(e) => Err(e.into()),
+ },
+ None => Err(crate::Error::NotSupported),
+ }
+ }
+
+ /// Write the reset string to `out`.
+ pub fn reset(&self, out: &mut dyn io::Write) -> Result<()> {
+ // are there any terminals that have color/attrs and not sgr0?
+ // Try falling back to sgr, then op
+ let cmd = match [
+ ("sgr0", &[] as &[Param]),
+ ("sgr", &[Param::Number(0)]),
+ ("op", &[]),
+ ]
+ .iter()
+ .filter_map(|&(cap, params)| self.strings.get(cap).map(|c| (c, params)))
+ .next()
+ {
+ Some((op, params)) => match expand(op, params, &mut Variables::new()) {
+ Ok(cmd) => cmd,
+ Err(e) => return Err(e.into()),
+ },
+ None => return Err(crate::Error::NotSupported),
+ };
+ out.write_all(&cmd)?;
+ Ok(())
+ }
+}
+
+#[derive(Debug, Eq, PartialEq)]
+/// An error from parsing a terminfo entry
+pub enum Error {
+ /// The "magic" number at the start of the file was wrong.
+ ///
+ /// It should be `0x11A` (16bit numbers) or `0x21e` (32bit numbers)
+ BadMagic(u16),
+ /// The names in the file were not valid UTF-8.
+ ///
+ /// In theory these should only be ASCII, but to work with the Rust `str` type, we treat them
+ /// as UTF-8. This is valid, except when a terminfo file decides to be invalid. This hasn't
+ /// been encountered in the wild.
+ NotUtf8(::std::str::Utf8Error),
+ /// The names section of the file was empty
+ ShortNames,
+ /// More boolean parameters are present in the file than this crate knows how to interpret.
+ TooManyBools,
+ /// More number parameters are present in the file than this crate knows how to interpret.
+ TooManyNumbers,
+ /// More string parameters are present in the file than this crate knows how to interpret.
+ TooManyStrings,
+ /// The length of some field was not >= -1.
+ InvalidLength,
+ /// The names table was missing a trailing null terminator.
+ NamesMissingNull,
+ /// The strings table was missing a trailing null terminator.
+ StringsMissingNull,
+}
+
+impl ::std::fmt::Display for Error {
+ fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+ match *self {
+ BadMagic(v) => write!(f, "bad magic number {:x} in terminfo header", v),
+ ShortNames => f.write_str("no names exposed, need at least one"),
+ TooManyBools => f.write_str("more boolean properties than libterm knows about"),
+ TooManyNumbers => f.write_str("more number properties than libterm knows about"),
+ TooManyStrings => f.write_str("more string properties than libterm knows about"),
+ InvalidLength => f.write_str("invalid length field value, must be >= -1"),
+ NotUtf8(ref e) => e.fmt(f),
+ NamesMissingNull => f.write_str("names table missing NUL terminator"),
+ StringsMissingNull => f.write_str("string table missing NUL terminator"),
+ }
+ }
+}
+
+impl ::std::convert::From<::std::string::FromUtf8Error> for Error {
+ fn from(v: ::std::string::FromUtf8Error) -> Self {
+ NotUtf8(v.utf8_error())
+ }
+}
+
+impl ::std::error::Error for Error {
+ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+ match *self {
+ NotUtf8(ref e) => Some(e),
+ _ => None,
+ }
+ }
+}
+
+pub mod searcher;
+
+/// `TermInfo` format parsing.
+pub mod parser {
+ //! ncurses-compatible compiled terminfo format parsing (term(5))
+ pub mod compiled;
+ mod names;
+}
+pub mod parm;
+
+fn cap_for_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.
+#[derive(Clone, Debug)]
+pub struct TerminfoTerminal<T> {
+ num_colors: u32,
+ out: T,
+ ti: TermInfo,
+}
+
+impl<T: Write> Terminal for TerminfoTerminal<T> {
+ type Output = T;
+ fn fg(&mut self, color: color::Color) -> Result<()> {
+ let color = self.dim_if_necessary(color);
+ if self.num_colors > color {
+ return self
+ .ti
+ .apply_cap("setaf", &[Param::Number(color as i32)], &mut self.out);
+ }
+ Err(crate::Error::ColorOutOfRange)
+ }
+
+ fn bg(&mut self, color: color::Color) -> Result<()> {
+ let color = self.dim_if_necessary(color);
+ if self.num_colors > color {
+ return self
+ .ti
+ .apply_cap("setab", &[Param::Number(color as i32)], &mut self.out);
+ }
+ Err(crate::Error::ColorOutOfRange)
+ }
+
+ fn attr(&mut self, attr: Attr) -> Result<()> {
+ match attr {
+ Attr::ForegroundColor(c) => self.fg(c),
+ Attr::BackgroundColor(c) => self.bg(c),
+ _ => self.ti.apply_cap(cap_for_attr(attr), &[], &mut self.out),
+ }
+ }
+
+ fn supports_attr(&self, attr: Attr) -> bool {
+ match attr {
+ Attr::ForegroundColor(_) | Attr::BackgroundColor(_) => self.num_colors > 0,
+ _ => {
+ let cap = cap_for_attr(attr);
+ self.ti.strings.get(cap).is_some()
+ }
+ }
+ }
+
+ fn reset(&mut self) -> Result<()> {
+ self.ti.reset(&mut self.out)
+ }
+
+ fn supports_reset(&self) -> bool {
+ ["sgr0", "sgr", "op"]
+ .iter()
+ .any(|&cap| self.ti.strings.get(cap).is_some())
+ }
+
+ fn supports_color(&self) -> bool {
+ self.num_colors > 0 && self.supports_reset()
+ }
+
+ fn cursor_up(&mut self) -> Result<()> {
+ self.ti.apply_cap("cuu1", &[], &mut self.out)
+ }
+
+ fn delete_line(&mut self) -> Result<()> {
+ self.ti.apply_cap("el", &[], &mut self.out)
+ }
+
+ fn carriage_return(&mut self) -> Result<()> {
+ self.ti.apply_cap("cr", &[], &mut self.out)
+ }
+
+ fn get_ref(&self) -> &T {
+ &self.out
+ }
+
+ fn get_mut(&mut self) -> &mut T {
+ &mut self.out
+ }
+
+ fn into_inner(self) -> T
+ where
+ Self: Sized,
+ {
+ self.out
+ }
+}
+
+impl<T: Write> TerminfoTerminal<T> {
+ /// Create a new TerminfoTerminal with the given TermInfo and Write.
+ pub 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: out,
+ ti: terminfo,
+ num_colors: nc as u32,
+ }
+ }
+
+ /// Create a new TerminfoTerminal for the current environment with the given Write.
+ ///
+ /// Returns `None` when the terminfo cannot be found or parsed.
+ pub 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
+ }
+ }
+}
+
+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()
+ }
+}
diff --git a/vendor/term/src/terminfo/parm.rs b/vendor/term/src/terminfo/parm.rs
new file mode 100644
index 000000000..a7f0b5087
--- /dev/null
+++ b/vendor/term/src/terminfo/parm.rs
@@ -0,0 +1,741 @@
+// Copyright 2019 The Rust Project Developers. See the COPYRIGHT
+// 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.
+
+//! Parameterized string expansion
+
+use self::Param::*;
+use self::States::*;
+
+use std::iter::repeat;
+
+#[derive(Clone, Copy, PartialEq)]
+enum States {
+ Nothing,
+ Delay,
+ Percent,
+ SetVar,
+ GetVar,
+ PushParam,
+ CharConstant,
+ CharClose,
+ IntConstant(i32),
+ FormatPattern(Flags, FormatState),
+ SeekIfElse(usize),
+ SeekIfElsePercent(usize),
+ SeekIfEnd(usize),
+ SeekIfEndPercent(usize),
+}
+
+#[derive(Copy, PartialEq, Clone)]
+enum FormatState {
+ Flags,
+ Width,
+ Precision,
+}
+
+/// Types of parameters a capability can use
+#[allow(missing_docs)]
+#[derive(Clone)]
+pub enum Param {
+ Number(i32),
+ Words(String),
+}
+
+impl Default for Param {
+ fn default() -> Self {
+ Param::Number(0)
+ }
+}
+
+/// An error from interpreting a parameterized string.
+#[derive(Debug, Eq, PartialEq)]
+pub enum Error {
+ /// Data was requested from the stack, but the stack didn't have enough elements.
+ StackUnderflow,
+ /// The type of the element(s) on top of the stack did not match the type that the operator
+ /// wanted.
+ TypeMismatch,
+ /// An unrecognized format option was used.
+ UnrecognizedFormatOption(char),
+ /// An invalid variable name was used.
+ InvalidVariableName(char),
+ /// An invalid parameter index was used.
+ InvalidParameterIndex(char),
+ /// A malformed character constant was used.
+ MalformedCharacterConstant,
+ /// An integer constant was too large (overflowed an i32)
+ IntegerConstantOverflow,
+ /// A malformed integer constant was used.
+ MalformedIntegerConstant,
+ /// A format width constant was too large (overflowed a usize)
+ FormatWidthOverflow,
+ /// A format precision constant was too large (overflowed a usize)
+ FormatPrecisionOverflow,
+}
+
+impl ::std::fmt::Display for Error {
+ fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+ use self::Error::*;
+ match *self {
+ StackUnderflow => f.write_str("not enough elements on the stack"),
+ TypeMismatch => f.write_str("type mismatch"),
+ UnrecognizedFormatOption(_) => f.write_str("unrecognized format option"),
+ InvalidVariableName(_) => f.write_str("invalid variable name"),
+ InvalidParameterIndex(_) => f.write_str("invalid parameter index"),
+ MalformedCharacterConstant => f.write_str("malformed character constant"),
+ IntegerConstantOverflow => f.write_str("integer constant computation overflowed"),
+ MalformedIntegerConstant => f.write_str("malformed integer constant"),
+ FormatWidthOverflow => f.write_str("format width constant computation overflowed"),
+ FormatPrecisionOverflow => {
+ f.write_str("format precision constant computation overflowed")
+ }
+ }
+ }
+}
+
+impl ::std::error::Error for Error {}
+
+/// Container for static and dynamic variable arrays
+#[derive(Default)]
+pub struct Variables {
+ /// Static variables A-Z
+ sta_vars: [Param; 26],
+ /// Dynamic variables a-z
+ dyn_vars: [Param; 26],
+}
+
+impl Variables {
+ /// Return a new zero-initialized Variables
+ pub fn new() -> Variables {
+ Default::default()
+ }
+}
+
+/// Expand a parameterized capability
+///
+/// # Arguments
+/// * `cap` - string to expand
+/// * `params` - vector of params for %p1 etc
+/// * `vars` - Variables struct for %Pa etc
+///
+/// To be compatible with ncurses, `vars` should be the same between calls to `expand` for
+/// multiple capabilities for the same terminal.
+pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables) -> Result<Vec<u8>, Error> {
+ let mut state = Nothing;
+
+ // expanded cap will only rarely be larger than the cap itself
+ let mut output = Vec::with_capacity(cap.len());
+
+ let mut stack: Vec<Param> = Vec::new();
+
+ // Copy parameters into a local vector for mutability
+ let mut mparams = [
+ Number(0),
+ Number(0),
+ Number(0),
+ Number(0),
+ Number(0),
+ Number(0),
+ Number(0),
+ Number(0),
+ Number(0),
+ ];
+ for (dst, src) in mparams.iter_mut().zip(params.iter()) {
+ *dst = (*src).clone();
+ }
+
+ for &c in cap.iter() {
+ let cur = c as char;
+ let mut old_state = state;
+ match state {
+ Nothing => {
+ if cur == '%' {
+ state = Percent;
+ } else if cur == '$' {
+ state = Delay;
+ } else {
+ output.push(c);
+ }
+ }
+ Delay => {
+ old_state = Nothing;
+ if cur == '>' {
+ state = Nothing;
+ }
+ }
+ Percent => {
+ match cur {
+ '%' => {
+ output.push(c);
+ state = Nothing
+ }
+ 'c' => {
+ match stack.pop() {
+ // if c is 0, use 0200 (128) for ncurses compatibility
+ Some(Number(0)) => output.push(128u8),
+ // Don't check bounds. ncurses just casts and truncates.
+ Some(Number(c)) => output.push(c as u8),
+ Some(_) => return Err(Error::TypeMismatch),
+ None => return Err(Error::StackUnderflow),
+ }
+ }
+ 'p' => state = PushParam,
+ 'P' => state = SetVar,
+ 'g' => state = GetVar,
+ '\'' => state = CharConstant,
+ '{' => state = IntConstant(0),
+ 'l' => match stack.pop() {
+ Some(Words(s)) => stack.push(Number(s.len() as i32)),
+ Some(_) => return Err(Error::TypeMismatch),
+ None => return Err(Error::StackUnderflow),
+ },
+ '+' | '-' | '/' | '*' | '^' | '&' | '|' | 'm' => {
+ match (stack.pop(), stack.pop()) {
+ (Some(Number(y)), Some(Number(x))) => stack.push(Number(match cur {
+ '+' => x + y,
+ '-' => x - y,
+ '*' => x * y,
+ '/' => x / y,
+ '|' => x | y,
+ '&' => x & y,
+ '^' => x ^ y,
+ 'm' => x % y,
+ _ => unreachable!("logic error"),
+ })),
+ (Some(_), Some(_)) => return Err(Error::TypeMismatch),
+ _ => return Err(Error::StackUnderflow),
+ }
+ }
+ '=' | '>' | '<' | 'A' | 'O' => match (stack.pop(), stack.pop()) {
+ (Some(Number(y)), Some(Number(x))) => stack.push(Number(
+ if match cur {
+ '=' => x == y,
+ '<' => x < y,
+ '>' => x > y,
+ 'A' => x > 0 && y > 0,
+ 'O' => x > 0 || y > 0,
+ _ => unreachable!("logic error"),
+ } {
+ 1
+ } else {
+ 0
+ },
+ )),
+ (Some(_), Some(_)) => return Err(Error::TypeMismatch),
+ _ => return Err(Error::StackUnderflow),
+ },
+ '!' | '~' => match stack.pop() {
+ Some(Number(x)) => stack.push(Number(match cur {
+ '!' if x > 0 => 0,
+ '!' => 1,
+ '~' => !x,
+ _ => unreachable!("logic error"),
+ })),
+ Some(_) => return Err(Error::TypeMismatch),
+ None => return Err(Error::StackUnderflow),
+ },
+ 'i' => match (&mparams[0], &mparams[1]) {
+ (&Number(x), &Number(y)) => {
+ mparams[0] = Number(x + 1);
+ mparams[1] = Number(y + 1);
+ }
+ (_, _) => return Err(Error::TypeMismatch),
+ },
+
+ // printf-style support for %doxXs
+ 'd' | 'o' | 'x' | 'X' | 's' => {
+ if let Some(arg) = stack.pop() {
+ let flags = Flags::default();
+ let res = format(arg, FormatOp::from_char(cur), flags)?;
+ output.extend(res);
+ } else {
+ return Err(Error::StackUnderflow);
+ }
+ }
+ ':' | '#' | ' ' | '.' | '0'..='9' => {
+ let mut flags = Flags::default();
+ let mut fstate = FormatState::Flags;
+ match cur {
+ ':' => (),
+ '#' => flags.alternate = true,
+ ' ' => flags.space = true,
+ '.' => fstate = FormatState::Precision,
+ '0'..='9' => {
+ flags.width = cur as usize - '0' as usize;
+ fstate = FormatState::Width;
+ }
+ _ => unreachable!("logic error"),
+ }
+ state = FormatPattern(flags, fstate);
+ }
+
+ // conditionals
+ '?' | ';' => (),
+ 't' => match stack.pop() {
+ Some(Number(0)) => state = SeekIfElse(0),
+ Some(Number(_)) => (),
+ Some(_) => return Err(Error::TypeMismatch),
+ None => return Err(Error::StackUnderflow),
+ },
+ 'e' => state = SeekIfEnd(0),
+ c => return Err(Error::UnrecognizedFormatOption(c)),
+ }
+ }
+ PushParam => {
+ // params are 1-indexed
+ stack.push(
+ mparams[match cur.to_digit(10) {
+ Some(d) => d as usize - 1,
+ None => return Err(Error::InvalidParameterIndex(cur)),
+ }]
+ .clone(),
+ );
+ }
+ SetVar => {
+ if cur >= 'A' && cur <= 'Z' {
+ if let Some(arg) = stack.pop() {
+ let idx = (cur as u8) - b'A';
+ vars.sta_vars[idx as usize] = arg;
+ } else {
+ return Err(Error::StackUnderflow);
+ }
+ } else if cur >= 'a' && cur <= 'z' {
+ if let Some(arg) = stack.pop() {
+ let idx = (cur as u8) - b'a';
+ vars.dyn_vars[idx as usize] = arg;
+ } else {
+ return Err(Error::StackUnderflow);
+ }
+ } else {
+ return Err(Error::InvalidVariableName(cur));
+ }
+ }
+ GetVar => {
+ if cur >= 'A' && cur <= 'Z' {
+ let idx = (cur as u8) - b'A';
+ stack.push(vars.sta_vars[idx as usize].clone());
+ } else if cur >= 'a' && cur <= 'z' {
+ let idx = (cur as u8) - b'a';
+ stack.push(vars.dyn_vars[idx as usize].clone());
+ } else {
+ return Err(Error::InvalidVariableName(cur));
+ }
+ }
+ CharConstant => {
+ stack.push(Number(i32::from(c)));
+ state = CharClose;
+ }
+ CharClose => {
+ if cur != '\'' {
+ return Err(Error::MalformedCharacterConstant);
+ }
+ }
+ IntConstant(i) => {
+ if cur == '}' {
+ stack.push(Number(i));
+ state = Nothing;
+ } else if let Some(digit) = cur.to_digit(10) {
+ match i
+ .checked_mul(10)
+ .and_then(|i_ten| i_ten.checked_add(digit as i32))
+ {
+ Some(i) => {
+ state = IntConstant(i);
+ old_state = Nothing;
+ }
+ None => return Err(Error::IntegerConstantOverflow),
+ }
+ } else {
+ return Err(Error::MalformedIntegerConstant);
+ }
+ }
+ FormatPattern(ref mut flags, ref mut fstate) => {
+ old_state = Nothing;
+ match (*fstate, cur) {
+ (_, 'd') | (_, 'o') | (_, 'x') | (_, 'X') | (_, 's') => {
+ if let Some(arg) = stack.pop() {
+ let res = format(arg, FormatOp::from_char(cur), *flags)?;
+ output.extend(res);
+ // will cause state to go to Nothing
+ old_state = FormatPattern(*flags, *fstate);
+ } else {
+ return Err(Error::StackUnderflow);
+ }
+ }
+ (FormatState::Flags, '#') => {
+ flags.alternate = true;
+ }
+ (FormatState::Flags, '-') => {
+ flags.left = true;
+ }
+ (FormatState::Flags, '+') => {
+ flags.sign = true;
+ }
+ (FormatState::Flags, ' ') => {
+ flags.space = true;
+ }
+ (FormatState::Flags, '0'..='9') => {
+ flags.width = cur as usize - '0' as usize;
+ *fstate = FormatState::Width;
+ }
+ (FormatState::Width, '0'..='9') => {
+ flags.width = match flags
+ .width
+ .checked_mul(10)
+ .and_then(|w| w.checked_add(cur as usize - '0' as usize))
+ {
+ Some(width) => width,
+ None => return Err(Error::FormatWidthOverflow),
+ }
+ }
+ (FormatState::Width, '.') | (FormatState::Flags, '.') => {
+ *fstate = FormatState::Precision;
+ }
+ (FormatState::Precision, '0'..='9') => {
+ flags.precision = match flags
+ .precision
+ .checked_mul(10)
+ .and_then(|w| w.checked_add(cur as usize - '0' as usize))
+ {
+ Some(precision) => precision,
+ None => return Err(Error::FormatPrecisionOverflow),
+ }
+ }
+ _ => return Err(Error::UnrecognizedFormatOption(cur)),
+ }
+ }
+ SeekIfElse(level) => {
+ if cur == '%' {
+ state = SeekIfElsePercent(level);
+ }
+ old_state = Nothing;
+ }
+ SeekIfElsePercent(level) => {
+ if cur == ';' {
+ if level == 0 {
+ state = Nothing;
+ } else {
+ state = SeekIfElse(level - 1);
+ }
+ } else if cur == 'e' && level == 0 {
+ state = Nothing;
+ } else if cur == '?' {
+ state = SeekIfElse(level + 1);
+ } else {
+ state = SeekIfElse(level);
+ }
+ }
+ SeekIfEnd(level) => {
+ if cur == '%' {
+ state = SeekIfEndPercent(level);
+ }
+ old_state = Nothing;
+ }
+ SeekIfEndPercent(level) => {
+ if cur == ';' {
+ if level == 0 {
+ state = Nothing;
+ } else {
+ state = SeekIfEnd(level - 1);
+ }
+ } else if cur == '?' {
+ state = SeekIfEnd(level + 1);
+ } else {
+ state = SeekIfEnd(level);
+ }
+ }
+ }
+ if state == old_state {
+ state = Nothing;
+ }
+ }
+ Ok(output)
+}
+
+#[derive(Copy, PartialEq, Clone, Default)]
+struct Flags {
+ width: usize,
+ precision: usize,
+ alternate: bool,
+ left: bool,
+ sign: bool,
+ space: bool,
+}
+
+#[derive(Copy, Clone)]
+enum FormatOp {
+ Digit,
+ Octal,
+ Hex,
+ HEX,
+ String,
+}
+
+impl FormatOp {
+ fn from_char(c: char) -> FormatOp {
+ use self::FormatOp::*;
+ match c {
+ 'd' => Digit,
+ 'o' => Octal,
+ 'x' => Hex,
+ 'X' => HEX,
+ 's' => String,
+ _ => panic!("bad FormatOp char"),
+ }
+ }
+}
+
+fn format(val: Param, op: FormatOp, flags: Flags) -> Result<Vec<u8>, Error> {
+ use self::FormatOp::*;
+ let mut s = match val {
+ Number(d) => {
+ match op {
+ Digit => {
+ if flags.sign {
+ format!("{:+01$}", d, flags.precision)
+ } else if d < 0 {
+ // C doesn't take sign into account in precision calculation.
+ format!("{:01$}", d, flags.precision + 1)
+ } else if flags.space {
+ format!(" {:01$}", d, flags.precision)
+ } else {
+ format!("{:01$}", d, flags.precision)
+ }
+ }
+ Octal => {
+ if flags.alternate {
+ // Leading octal zero counts against precision.
+ format!("0{:01$o}", d, flags.precision.saturating_sub(1))
+ } else {
+ format!("{:01$o}", d, flags.precision)
+ }
+ }
+ Hex => {
+ if flags.alternate && d != 0 {
+ format!("0x{:01$x}", d, flags.precision)
+ } else {
+ format!("{:01$x}", d, flags.precision)
+ }
+ }
+ HEX => {
+ if flags.alternate && d != 0 {
+ format!("0X{:01$X}", d, flags.precision)
+ } else {
+ format!("{:01$X}", d, flags.precision)
+ }
+ }
+ String => return Err(Error::TypeMismatch),
+ }
+ .into_bytes()
+ }
+ Words(s) => match op {
+ String => {
+ let mut s = s.into_bytes();
+ if flags.precision > 0 && flags.precision < s.len() {
+ s.truncate(flags.precision);
+ }
+ s
+ }
+ _ => return Err(Error::TypeMismatch),
+ },
+ };
+ if flags.width > s.len() {
+ let n = flags.width - s.len();
+ if flags.left {
+ s.extend(repeat(b' ').take(n));
+ } else {
+ let mut s_ = Vec::with_capacity(flags.width);
+ s_.extend(repeat(b' ').take(n));
+ s_.extend(s.into_iter());
+ s = s_;
+ }
+ }
+ Ok(s)
+}
+
+#[cfg(test)]
+mod test {
+ use super::Param::{self, Number, Words};
+ use super::{expand, Variables};
+ use std::result::Result::Ok;
+
+ #[test]
+ fn test_basic_setabf() {
+ let s = b"\\E[48;5;%p1%dm";
+ assert_eq!(
+ expand(s, &[Number(1)], &mut Variables::new()).unwrap(),
+ "\\E[48;5;1m".bytes().collect::<Vec<_>>()
+ );
+ }
+
+ #[test]
+ fn test_multiple_int_constants() {
+ assert_eq!(
+ expand(b"%{1}%{2}%d%d", &[], &mut Variables::new()).unwrap(),
+ "21".bytes().collect::<Vec<_>>()
+ );
+ }
+
+ #[test]
+ fn test_op_i() {
+ let mut vars = Variables::new();
+ assert_eq!(
+ expand(
+ b"%p1%d%p2%d%p3%d%i%p1%d%p2%d%p3%d",
+ &[Number(1), Number(2), Number(3)],
+ &mut vars
+ ),
+ Ok("123233".bytes().collect::<Vec<_>>())
+ );
+ assert_eq!(
+ expand(b"%p1%d%p2%d%i%p1%d%p2%d", &[], &mut vars),
+ Ok("0011".bytes().collect::<Vec<_>>())
+ );
+ }
+
+ #[test]
+ fn test_param_stack_failure_conditions() {
+ let mut varstruct = Variables::new();
+ let vars = &mut varstruct;
+ fn get_res(
+ fmt: &str,
+ cap: &str,
+ params: &[Param],
+ vars: &mut Variables,
+ ) -> Result<Vec<u8>, super::Error> {
+ let mut u8v: Vec<_> = fmt.bytes().collect();
+ u8v.extend(cap.as_bytes().iter().cloned());
+ expand(&u8v, params, vars)
+ }
+
+ let caps = ["%d", "%c", "%s", "%Pa", "%l", "%!", "%~"];
+ for &cap in &caps {
+ let res = get_res("", cap, &[], vars);
+ assert!(
+ res.is_err(),
+ "Op {} succeeded incorrectly with 0 stack entries",
+ cap
+ );
+ let p = if cap == "%s" || cap == "%l" {
+ Words("foo".to_owned())
+ } else {
+ Number(97)
+ };
+ let res = get_res("%p1", cap, &[p], vars);
+ assert!(
+ res.is_ok(),
+ "Op {} failed with 1 stack entry: {}",
+ cap,
+ res.err().unwrap()
+ );
+ }
+ let caps = ["%+", "%-", "%*", "%/", "%m", "%&", "%|", "%A", "%O"];
+ for &cap in &caps {
+ let res = expand(cap.as_bytes(), &[], vars);
+ assert!(
+ res.is_err(),
+ "Binop {} succeeded incorrectly with 0 stack entries",
+ cap
+ );
+ let res = get_res("%{1}", cap, &[], vars);
+ assert!(
+ res.is_err(),
+ "Binop {} succeeded incorrectly with 1 stack entry",
+ cap
+ );
+ let res = get_res("%{1}%{2}", cap, &[], vars);
+ assert!(
+ res.is_ok(),
+ "Binop {} failed with 2 stack entries: {}",
+ cap,
+ res.err().unwrap()
+ );
+ }
+ }
+
+ #[test]
+ fn test_push_bad_param() {
+ assert!(expand(b"%pa", &[], &mut Variables::new()).is_err());
+ }
+
+ #[test]
+ fn test_comparison_ops() {
+ let v = [
+ ('<', [1u8, 0u8, 0u8]),
+ ('=', [0u8, 1u8, 0u8]),
+ ('>', [0u8, 0u8, 1u8]),
+ ];
+ for &(op, bs) in &v {
+ let s = format!("%{{1}}%{{2}}%{}%d", op);
+ let res = expand(s.as_bytes(), &[], &mut Variables::new());
+ assert!(res.is_ok(), res.err().unwrap());
+ assert_eq!(res.unwrap(), vec![b'0' + bs[0]]);
+ let s = format!("%{{1}}%{{1}}%{}%d", op);
+ let res = expand(s.as_bytes(), &[], &mut Variables::new());
+ assert!(res.is_ok(), res.err().unwrap());
+ assert_eq!(res.unwrap(), vec![b'0' + bs[1]]);
+ let s = format!("%{{2}}%{{1}}%{}%d", op);
+ let res = expand(s.as_bytes(), &[], &mut Variables::new());
+ assert!(res.is_ok(), res.err().unwrap());
+ assert_eq!(res.unwrap(), vec![b'0' + bs[2]]);
+ }
+ }
+
+ #[test]
+ fn test_conditionals() {
+ let mut vars = Variables::new();
+ let s = b"\\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m";
+ let res = expand(s, &[Number(1)], &mut vars);
+ assert!(res.is_ok(), res.err().unwrap());
+ assert_eq!(res.unwrap(), "\\E[31m".bytes().collect::<Vec<_>>());
+ let res = expand(s, &[Number(8)], &mut vars);
+ assert!(res.is_ok(), res.err().unwrap());
+ assert_eq!(res.unwrap(), "\\E[90m".bytes().collect::<Vec<_>>());
+ let res = expand(s, &[Number(42)], &mut vars);
+ assert!(res.is_ok(), res.err().unwrap());
+ assert_eq!(res.unwrap(), "\\E[38;5;42m".bytes().collect::<Vec<_>>());
+ }
+
+ #[test]
+ fn test_format() {
+ let mut varstruct = Variables::new();
+ let vars = &mut varstruct;
+ assert_eq!(
+ expand(
+ b"%p1%s%p2%2s%p3%2s%p4%.2s",
+ &[
+ Words("foo".to_owned()),
+ Words("foo".to_owned()),
+ Words("f".to_owned()),
+ Words("foo".to_owned())
+ ],
+ vars
+ ),
+ Ok("foofoo ffo".bytes().collect::<Vec<_>>())
+ );
+ assert_eq!(
+ expand(b"%p1%:-4.2s", &[Words("foo".to_owned())], vars),
+ Ok("fo ".bytes().collect::<Vec<_>>())
+ );
+
+ assert_eq!(
+ expand(b"%p1%d%p1%.3d%p1%5d%p1%:+d", &[Number(1)], vars),
+ Ok("1001 1+1".bytes().collect::<Vec<_>>())
+ );
+ assert_eq!(
+ expand(
+ b"%p1%o%p1%#o%p2%6.4x%p2%#6.4X",
+ &[Number(15), Number(27)],
+ vars
+ ),
+ Ok("17017 001b0X001B".bytes().collect::<Vec<_>>())
+ );
+ }
+}
diff --git a/vendor/term/src/terminfo/parser/compiled.rs b/vendor/term/src/terminfo/parser/compiled.rs
new file mode 100644
index 000000000..d84e21b00
--- /dev/null
+++ b/vendor/term/src/terminfo/parser/compiled.rs
@@ -0,0 +1,204 @@
+// Copyright 2019 The Rust Project Developers. See the COPYRIGHT
+// 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.
+
+//! ncurses-compatible compiled terminfo format parsing (term(5))
+
+use std::collections::HashMap;
+use std::io;
+use std::io::prelude::*;
+
+use crate::terminfo::Error::*;
+use crate::terminfo::TermInfo;
+use crate::Result;
+
+pub use crate::terminfo::parser::names::*;
+
+// These are the orders ncurses uses in its compiled format (as of 5.9). Not
+// sure if portable.
+
+fn read_le_u16(r: &mut dyn io::Read) -> io::Result<u32> {
+ let mut buf = [0; 2];
+ r.read_exact(&mut buf)
+ .map(|()| u32::from(u16::from_le_bytes(buf)))
+}
+
+fn read_le_u32(r: &mut dyn io::Read) -> io::Result<u32> {
+ let mut buf = [0; 4];
+ r.read_exact(&mut buf).map(|()| u32::from_le_bytes(buf))
+}
+
+fn read_byte(r: &mut dyn io::Read) -> io::Result<u8> {
+ match r.bytes().next() {
+ Some(s) => s,
+ None => Err(io::Error::new(io::ErrorKind::Other, "end of file")),
+ }
+}
+
+/// Parse a compiled terminfo entry, using long capability names if `longnames`
+/// is true
+pub fn parse(file: &mut dyn io::Read, longnames: bool) -> Result<TermInfo> {
+ let (bnames, snames, nnames) = if longnames {
+ (boolfnames, stringfnames, numfnames)
+ } else {
+ (boolnames, stringnames, numnames)
+ };
+
+ // Check magic number
+ let mut buf = [0; 2];
+ file.read_exact(&mut buf)?;
+ let magic = u16::from_le_bytes(buf);
+
+ let read_number = match magic {
+ 0x011A => read_le_u16,
+ 0x021e => read_le_u32,
+ _ => return Err(BadMagic(magic).into()),
+ };
+
+ // According to the spec, these fields must be >= -1 where -1 means that the
+ // feature is not
+ // supported. Using 0 instead of -1 works because we skip sections with length
+ // 0.
+ macro_rules! read_nonneg {
+ () => {{
+ match read_le_u16(file)? as i16 {
+ n if n >= 0 => n as usize,
+ -1 => 0,
+ _ => return Err(InvalidLength.into()),
+ }
+ }};
+ }
+
+ let names_bytes = read_nonneg!();
+ let bools_bytes = read_nonneg!();
+ let numbers_count = read_nonneg!();
+ let string_offsets_count = read_nonneg!();
+ let string_table_bytes = read_nonneg!();
+
+ if names_bytes == 0 {
+ return Err(ShortNames.into());
+ }
+
+ if bools_bytes > boolnames.len() {
+ return Err(TooManyBools.into());
+ }
+
+ if numbers_count > numnames.len() {
+ return Err(TooManyNumbers.into());
+ }
+
+ if string_offsets_count > stringnames.len() {
+ return Err(TooManyStrings.into());
+ }
+
+ // don't read NUL
+ let mut bytes = Vec::new();
+ file.take((names_bytes - 1) as u64)
+ .read_to_end(&mut bytes)?;
+ let names_str = match String::from_utf8(bytes) {
+ Ok(s) => s,
+ Err(e) => return Err(NotUtf8(e.utf8_error()).into()),
+ };
+
+ let term_names: Vec<String> = names_str.split('|').map(|s| s.to_owned()).collect();
+ // consume NUL
+ if read_byte(file)? != b'\0' {
+ return Err(NamesMissingNull.into());
+ }
+
+ let bools_map = (0..bools_bytes)
+ .filter_map(|i| match read_byte(file) {
+ Err(e) => Some(Err(e)),
+ Ok(1) => Some(Ok((bnames[i], true))),
+ Ok(_) => None,
+ })
+ .collect::<io::Result<HashMap<_, _>>>()?;
+
+ if (bools_bytes + names_bytes) % 2 == 1 {
+ read_byte(file)?; // compensate for padding
+ }
+
+ let numbers_map = (0..numbers_count)
+ .filter_map(|i| match read_number(file) {
+ Ok(0xFFFF) => None,
+ Ok(n) => Some(Ok((nnames[i], n))),
+ Err(e) => Some(Err(e)),
+ })
+ .collect::<io::Result<HashMap<_, _>>>()?;
+
+ let string_map: HashMap<&str, Vec<u8>> = if string_offsets_count > 0 {
+ let string_offsets = (0..string_offsets_count)
+ .map(|_| {
+ let mut buf = [0; 2];
+ file.read_exact(&mut buf).map(|()| u16::from_le_bytes(buf))
+ })
+ .collect::<io::Result<Vec<_>>>()?;
+
+ let mut string_table = Vec::new();
+ file.take(string_table_bytes as u64)
+ .read_to_end(&mut string_table)?;
+
+ string_offsets
+ .into_iter()
+ .enumerate()
+ .filter(|&(_, offset)| {
+ // non-entry
+ offset != 0xFFFF
+ })
+ .map(|(i, offset)| {
+ let offset = offset as usize;
+
+ let name = if snames[i] == "_" {
+ stringfnames[i]
+ } else {
+ snames[i]
+ };
+
+ if offset == 0xFFFE {
+ // undocumented: FFFE indicates cap@, which means the capability
+ // is not present
+ // unsure if the handling for this is correct
+ return Ok((name, Vec::new()));
+ }
+
+ // Find the offset of the NUL we want to go to
+ let nulpos = string_table[offset..string_table_bytes]
+ .iter()
+ .position(|&b| b == 0);
+ match nulpos {
+ Some(len) => Ok((name, string_table[offset..offset + len].to_vec())),
+ None => Err(crate::Error::TerminfoParsing(StringsMissingNull)),
+ }
+ })
+ .collect::<Result<HashMap<_, _>>>()?
+ } else {
+ HashMap::new()
+ };
+
+ // And that's all there is to it
+ Ok(TermInfo {
+ names: term_names,
+ bools: bools_map,
+ numbers: numbers_map,
+ strings: string_map,
+ })
+}
+
+#[cfg(test)]
+mod test {
+
+ use super::{boolfnames, boolnames, numfnames, numnames, stringfnames, stringnames};
+
+ #[test]
+ fn test_veclens() {
+ assert_eq!(boolfnames.len(), boolnames.len());
+ assert_eq!(numfnames.len(), numnames.len());
+ assert_eq!(stringfnames.len(), stringnames.len());
+ }
+}
diff --git a/vendor/term/src/terminfo/parser/names.rs b/vendor/term/src/terminfo/parser/names.rs
new file mode 100644
index 000000000..c706b941a
--- /dev/null
+++ b/vendor/term/src/terminfo/parser/names.rs
@@ -0,0 +1,553 @@
+#![allow(non_upper_case_globals, missing_docs)]
+#![cfg_attr(rustfmt, rustfmt_skip)]
+
+pub static boolfnames: &[&str] = &["auto_left_margin",
+ "auto_right_margin",
+ "no_esc_ctlc",
+ "ceol_standout_glitch",
+ "eat_newline_glitch",
+ "erase_overstrike",
+ "generic_type",
+ "hard_copy",
+ "has_meta_key",
+ "has_status_line",
+ "insert_null_glitch",
+ "memory_above",
+ "memory_below",
+ "move_insert_mode",
+ "move_standout_mode",
+ "over_strike",
+ "status_line_esc_ok",
+ "dest_tabs_magic_smso",
+ "tilde_glitch",
+ "transparent_underline",
+ "xon_xoff",
+ "needs_xon_xoff",
+ "prtr_silent",
+ "hard_cursor",
+ "non_rev_rmcup",
+ "no_pad_char",
+ "non_dest_scroll_region",
+ "can_change",
+ "back_color_erase",
+ "hue_lightness_saturation",
+ "col_addr_glitch",
+ "cr_cancels_micro_mode",
+ "has_print_wheel",
+ "row_addr_glitch",
+ "semi_auto_right_margin",
+ "cpi_changes_res",
+ "lpi_changes_res",
+ "backspaces_with_bs",
+ "crt_no_scrolling",
+ "no_correctly_working_cr",
+ "gnu_has_meta_key",
+ "linefeed_is_newline",
+ "has_hardware_tabs",
+ "return_does_clr_eol"];
+
+pub static boolnames: &[&str] =
+ &["bw", "am", "xsb", "xhp", "xenl", "eo", "gn", "hc", "km", "hs", "in", "db", "da", "mir",
+ "msgr", "os", "eslok", "xt", "hz", "ul", "xon", "nxon", "mc5i", "chts", "nrrmc", "npc",
+ "ndscr", "ccc", "bce", "hls", "xhpa", "crxm", "daisy", "xvpa", "sam", "cpix", "lpix",
+ "OTbs", "OTns", "OTnc", "OTMT", "OTNL", "OTpt", "OTxr"];
+
+pub static numfnames: &[&str] = &["columns",
+ "init_tabs",
+ "lines",
+ "lines_of_memory",
+ "magic_cookie_glitch",
+ "padding_baud_rate",
+ "virtual_terminal",
+ "width_status_line",
+ "num_labels",
+ "label_height",
+ "label_width",
+ "max_attributes",
+ "maximum_windows",
+ "max_colors",
+ "max_pairs",
+ "no_color_video",
+ "buffer_capacity",
+ "dot_vert_spacing",
+ "dot_horz_spacing",
+ "max_micro_address",
+ "max_micro_jump",
+ "micro_col_size",
+ "micro_line_size",
+ "number_of_pins",
+ "output_res_char",
+ "output_res_line",
+ "output_res_horz_inch",
+ "output_res_vert_inch",
+ "print_rate",
+ "wide_char_size",
+ "buttons",
+ "bit_image_entwining",
+ "bit_image_type",
+ "magic_cookie_glitch_ul",
+ "carriage_return_delay",
+ "new_line_delay",
+ "backspace_delay",
+ "horizontal_tab_delay",
+ "number_of_function_keys"];
+
+pub static numnames: &[&str] =
+ &["cols", "it", "lines", "lm", "xmc", "pb", "vt", "wsl", "nlab", "lh", "lw", "ma", "wnum",
+ "colors", "pairs", "ncv", "bufsz", "spinv", "spinh", "maddr", "mjump", "mcs", "mls",
+ "npins", "orc", "orl", "orhi", "orvi", "cps", "widcs", "btns", "bitwin", "bitype", "UTug",
+ "OTdC", "OTdN", "OTdB", "OTdT", "OTkn"];
+
+pub static stringfnames: &[&str] = &["back_tab",
+ "bell",
+ "carriage_return",
+ "change_scroll_region",
+ "clear_all_tabs",
+ "clear_screen",
+ "clr_eol",
+ "clr_eos",
+ "column_address",
+ "command_character",
+ "cursor_address",
+ "cursor_down",
+ "cursor_home",
+ "cursor_invisible",
+ "cursor_left",
+ "cursor_mem_address",
+ "cursor_normal",
+ "cursor_right",
+ "cursor_to_ll",
+ "cursor_up",
+ "cursor_visible",
+ "delete_character",
+ "delete_line",
+ "dis_status_line",
+ "down_half_line",
+ "enter_alt_charset_mode",
+ "enter_blink_mode",
+ "enter_bold_mode",
+ "enter_ca_mode",
+ "enter_delete_mode",
+ "enter_dim_mode",
+ "enter_insert_mode",
+ "enter_secure_mode",
+ "enter_protected_mode",
+ "enter_reverse_mode",
+ "enter_standout_mode",
+ "enter_underline_mode",
+ "erase_chars",
+ "exit_alt_charset_mode",
+ "exit_attribute_mode",
+ "exit_ca_mode",
+ "exit_delete_mode",
+ "exit_insert_mode",
+ "exit_standout_mode",
+ "exit_underline_mode",
+ "flash_screen",
+ "form_feed",
+ "from_status_line",
+ "init_1string",
+ "init_2string",
+ "init_3string",
+ "init_file",
+ "insert_character",
+ "insert_line",
+ "insert_padding",
+ "key_backspace",
+ "key_catab",
+ "key_clear",
+ "key_ctab",
+ "key_dc",
+ "key_dl",
+ "key_down",
+ "key_eic",
+ "key_eol",
+ "key_eos",
+ "key_f0",
+ "key_f1",
+ "key_f10",
+ "key_f2",
+ "key_f3",
+ "key_f4",
+ "key_f5",
+ "key_f6",
+ "key_f7",
+ "key_f8",
+ "key_f9",
+ "key_home",
+ "key_ic",
+ "key_il",
+ "key_left",
+ "key_ll",
+ "key_npage",
+ "key_ppage",
+ "key_right",
+ "key_sf",
+ "key_sr",
+ "key_stab",
+ "key_up",
+ "keypad_local",
+ "keypad_xmit",
+ "lab_f0",
+ "lab_f1",
+ "lab_f10",
+ "lab_f2",
+ "lab_f3",
+ "lab_f4",
+ "lab_f5",
+ "lab_f6",
+ "lab_f7",
+ "lab_f8",
+ "lab_f9",
+ "meta_off",
+ "meta_on",
+ "newline",
+ "pad_char",
+ "parm_dch",
+ "parm_delete_line",
+ "parm_down_cursor",
+ "parm_ich",
+ "parm_index",
+ "parm_insert_line",
+ "parm_left_cursor",
+ "parm_right_cursor",
+ "parm_rindex",
+ "parm_up_cursor",
+ "pkey_key",
+ "pkey_local",
+ "pkey_xmit",
+ "print_screen",
+ "prtr_off",
+ "prtr_on",
+ "repeat_char",
+ "reset_1string",
+ "reset_2string",
+ "reset_3string",
+ "reset_file",
+ "restore_cursor",
+ "row_address",
+ "save_cursor",
+ "scroll_forward",
+ "scroll_reverse",
+ "set_attributes",
+ "set_tab",
+ "set_window",
+ "tab",
+ "to_status_line",
+ "underline_char",
+ "up_half_line",
+ "init_prog",
+ "key_a1",
+ "key_a3",
+ "key_b2",
+ "key_c1",
+ "key_c3",
+ "prtr_non",
+ "char_padding",
+ "acs_chars",
+ "plab_norm",
+ "key_btab",
+ "enter_xon_mode",
+ "exit_xon_mode",
+ "enter_am_mode",
+ "exit_am_mode",
+ "xon_character",
+ "xoff_character",
+ "ena_acs",
+ "label_on",
+ "label_off",
+ "key_beg",
+ "key_cancel",
+ "key_close",
+ "key_command",
+ "key_copy",
+ "key_create",
+ "key_end",
+ "key_enter",
+ "key_exit",
+ "key_find",
+ "key_help",
+ "key_mark",
+ "key_message",
+ "key_move",
+ "key_next",
+ "key_open",
+ "key_options",
+ "key_previous",
+ "key_print",
+ "key_redo",
+ "key_reference",
+ "key_refresh",
+ "key_replace",
+ "key_restart",
+ "key_resume",
+ "key_save",
+ "key_suspend",
+ "key_undo",
+ "key_sbeg",
+ "key_scancel",
+ "key_scommand",
+ "key_scopy",
+ "key_screate",
+ "key_sdc",
+ "key_sdl",
+ "key_select",
+ "key_send",
+ "key_seol",
+ "key_sexit",
+ "key_sfind",
+ "key_shelp",
+ "key_shome",
+ "key_sic",
+ "key_sleft",
+ "key_smessage",
+ "key_smove",
+ "key_snext",
+ "key_soptions",
+ "key_sprevious",
+ "key_sprint",
+ "key_sredo",
+ "key_sreplace",
+ "key_sright",
+ "key_srsume",
+ "key_ssave",
+ "key_ssuspend",
+ "key_sundo",
+ "req_for_input",
+ "key_f11",
+ "key_f12",
+ "key_f13",
+ "key_f14",
+ "key_f15",
+ "key_f16",
+ "key_f17",
+ "key_f18",
+ "key_f19",
+ "key_f20",
+ "key_f21",
+ "key_f22",
+ "key_f23",
+ "key_f24",
+ "key_f25",
+ "key_f26",
+ "key_f27",
+ "key_f28",
+ "key_f29",
+ "key_f30",
+ "key_f31",
+ "key_f32",
+ "key_f33",
+ "key_f34",
+ "key_f35",
+ "key_f36",
+ "key_f37",
+ "key_f38",
+ "key_f39",
+ "key_f40",
+ "key_f41",
+ "key_f42",
+ "key_f43",
+ "key_f44",
+ "key_f45",
+ "key_f46",
+ "key_f47",
+ "key_f48",
+ "key_f49",
+ "key_f50",
+ "key_f51",
+ "key_f52",
+ "key_f53",
+ "key_f54",
+ "key_f55",
+ "key_f56",
+ "key_f57",
+ "key_f58",
+ "key_f59",
+ "key_f60",
+ "key_f61",
+ "key_f62",
+ "key_f63",
+ "clr_bol",
+ "clear_margins",
+ "set_left_margin",
+ "set_right_margin",
+ "label_format",
+ "set_clock",
+ "display_clock",
+ "remove_clock",
+ "create_window",
+ "goto_window",
+ "hangup",
+ "dial_phone",
+ "quick_dial",
+ "tone",
+ "pulse",
+ "flash_hook",
+ "fixed_pause",
+ "wait_tone",
+ "user0",
+ "user1",
+ "user2",
+ "user3",
+ "user4",
+ "user5",
+ "user6",
+ "user7",
+ "user8",
+ "user9",
+ "orig_pair",
+ "orig_colors",
+ "initialize_color",
+ "initialize_pair",
+ "set_color_pair",
+ "set_foreground",
+ "set_background",
+ "change_char_pitch",
+ "change_line_pitch",
+ "change_res_horz",
+ "change_res_vert",
+ "define_char",
+ "enter_doublewide_mode",
+ "enter_draft_quality",
+ "enter_italics_mode",
+ "enter_leftward_mode",
+ "enter_micro_mode",
+ "enter_near_letter_quality",
+ "enter_normal_quality",
+ "enter_shadow_mode",
+ "enter_subscript_mode",
+ "enter_superscript_mode",
+ "enter_upward_mode",
+ "exit_doublewide_mode",
+ "exit_italics_mode",
+ "exit_leftward_mode",
+ "exit_micro_mode",
+ "exit_shadow_mode",
+ "exit_subscript_mode",
+ "exit_superscript_mode",
+ "exit_upward_mode",
+ "micro_column_address",
+ "micro_down",
+ "micro_left",
+ "micro_right",
+ "micro_row_address",
+ "micro_up",
+ "order_of_pins",
+ "parm_down_micro",
+ "parm_left_micro",
+ "parm_right_micro",
+ "parm_up_micro",
+ "select_char_set",
+ "set_bottom_margin",
+ "set_bottom_margin_parm",
+ "set_left_margin_parm",
+ "set_right_margin_parm",
+ "set_top_margin",
+ "set_top_margin_parm",
+ "start_bit_image",
+ "start_char_set_def",
+ "stop_bit_image",
+ "stop_char_set_def",
+ "subscript_characters",
+ "superscript_characters",
+ "these_cause_cr",
+ "zero_motion",
+ "char_set_names",
+ "key_mouse",
+ "mouse_info",
+ "req_mouse_pos",
+ "get_mouse",
+ "set_a_foreground",
+ "set_a_background",
+ "pkey_plab",
+ "device_type",
+ "code_set_init",
+ "set0_des_seq",
+ "set1_des_seq",
+ "set2_des_seq",
+ "set3_des_seq",
+ "set_lr_margin",
+ "set_tb_margin",
+ "bit_image_repeat",
+ "bit_image_newline",
+ "bit_image_carriage_return",
+ "color_names",
+ "define_bit_image_region",
+ "end_bit_image_region",
+ "set_color_band",
+ "set_page_length",
+ "display_pc_char",
+ "enter_pc_charset_mode",
+ "exit_pc_charset_mode",
+ "enter_scancode_mode",
+ "exit_scancode_mode",
+ "pc_term_options",
+ "scancode_escape",
+ "alt_scancode_esc",
+ "enter_horizontal_hl_mode",
+ "enter_left_hl_mode",
+ "enter_low_hl_mode",
+ "enter_right_hl_mode",
+ "enter_top_hl_mode",
+ "enter_vertical_hl_mode",
+ "set_a_attributes",
+ "set_pglen_inch",
+ "termcap_init2",
+ "termcap_reset",
+ "linefeed_if_not_lf",
+ "backspace_if_not_bs",
+ "other_non_function_keys",
+ "arrow_key_map",
+ "acs_ulcorner",
+ "acs_llcorner",
+ "acs_urcorner",
+ "acs_lrcorner",
+ "acs_ltee",
+ "acs_rtee",
+ "acs_btee",
+ "acs_ttee",
+ "acs_hline",
+ "acs_vline",
+ "acs_plus",
+ "memory_lock",
+ "memory_unlock",
+ "box_chars_1"];
+
+pub static stringnames: &[&str] =
+ &["cbt", "bel", "cr", "csr", "tbc", "clear", "el", "ed", "hpa", "cmdch", "cup", "cud1",
+ "home", "civis", "cub1", "mrcup", "cnorm", "cuf1", "ll", "cuu1", "cvvis", "dch1", "dl1",
+ "dsl", "hd", "smacs", "blink", "bold", "smcup", "smdc", "dim", "smir", "invis", "prot",
+ "rev", "smso", "smul", "ech", "rmacs", "sgr0", "rmcup", "rmdc", "rmir", "rmso", "rmul",
+ "flash", "ff", "fsl", "is1", "is2", "is3", "if", "ich1", "il1", "ip", "kbs", "ktbc", "kclr",
+ "kctab", "kdch1", "kdl1", "kcud1", "krmir", "kel", "ked", "kf0", "kf1", "kf10", "kf2",
+ "kf3", "kf4", "kf5", "kf6", "kf7", "kf8", "kf9", "khome", "kich1", "kil1", "kcub1", "kll",
+ "knp", "kpp", "kcuf1", "kind", "kri", "khts", "kcuu1", "rmkx", "smkx", "lf0", "lf1", "lf10",
+ "lf2", "lf3", "lf4", "lf5", "lf6", "lf7", "lf8", "lf9", "rmm", "smm", "nel", "pad", "dch",
+ "dl", "cud", "ich", "indn", "il", "cub", "cuf", "rin", "cuu", "pfkey", "pfloc", "pfx",
+ "mc0", "mc4", "mc5", "rep", "rs1", "rs2", "rs3", "rf", "rc", "vpa", "sc", "ind", "ri",
+ "sgr", "hts", "wind", "ht", "tsl", "uc", "hu", "iprog", "ka1", "ka3", "kb2", "kc1", "kc3",
+ "mc5p", "rmp", "acsc", "pln", "kcbt", "smxon", "rmxon", "smam", "rmam", "xonc", "xoffc",
+ "enacs", "smln", "rmln", "kbeg", "kcan", "kclo", "kcmd", "kcpy", "kcrt", "kend", "kent",
+ "kext", "kfnd", "khlp", "kmrk", "kmsg", "kmov", "knxt", "kopn", "kopt", "kprv", "kprt",
+ "krdo", "kref", "krfr", "krpl", "krst", "kres", "ksav", "kspd", "kund", "kBEG", "kCAN",
+ "kCMD", "kCPY", "kCRT", "kDC", "kDL", "kslt", "kEND", "kEOL", "kEXT", "kFND", "kHLP",
+ "kHOM", "kIC", "kLFT", "kMSG", "kMOV", "kNXT", "kOPT", "kPRV", "kPRT", "kRDO", "kRPL",
+ "kRIT", "kRES", "kSAV", "kSPD", "kUND", "rfi", "kf11", "kf12", "kf13", "kf14", "kf15",
+ "kf16", "kf17", "kf18", "kf19", "kf20", "kf21", "kf22", "kf23", "kf24", "kf25", "kf26",
+ "kf27", "kf28", "kf29", "kf30", "kf31", "kf32", "kf33", "kf34", "kf35", "kf36", "kf37",
+ "kf38", "kf39", "kf40", "kf41", "kf42", "kf43", "kf44", "kf45", "kf46", "kf47", "kf48",
+ "kf49", "kf50", "kf51", "kf52", "kf53", "kf54", "kf55", "kf56", "kf57", "kf58", "kf59",
+ "kf60", "kf61", "kf62", "kf63", "el1", "mgc", "smgl", "smgr", "fln", "sclk", "dclk",
+ "rmclk", "cwin", "wingo", "hup", "dial", "qdial", "tone", "pulse", "hook", "pause", "wait",
+ "u0", "u1", "u2", "u3", "u4", "u5", "u6", "u7", "u8", "u9", "op", "oc", "initc", "initp",
+ "scp", "setf", "setb", "cpi", "lpi", "chr", "cvr", "defc", "swidm", "sdrfq", "sitm", "slm",
+ "smicm", "snlq", "snrmq", "sshm", "ssubm", "ssupm", "sum", "rwidm", "ritm", "rlm", "rmicm",
+ "rshm", "rsubm", "rsupm", "rum", "mhpa", "mcud1", "mcub1", "mcuf1", "mvpa", "mcuu1",
+ "porder", "mcud", "mcub", "mcuf", "mcuu", "scs", "smgb", "smgbp", "smglp", "smgrp", "smgt",
+ "smgtp", "sbim", "scsd", "rbim", "rcsd", "subcs", "supcs", "docr", "zerom", "csnm", "kmous",
+ "minfo", "reqmp", "getm", "setaf", "setab", "pfxl", "devt", "csin", "s0ds", "s1ds", "s2ds",
+ "s3ds", "smglr", "smgtb", "birep", "binel", "bicr", "colornm", "defbi", "endbi", "setcolor",
+ "slines", "dispc", "smpch", "rmpch", "smsc", "rmsc", "pctrm", "scesc", "scesa", "ehhlm",
+ "elhlm", "elohlm", "erhlm", "ethlm", "evhlm", "sgr1", "slength", "OTi2", "OTrs", "OTnl",
+ "OTbs", "OTko", "OTma", "OTG2", "OTG3", "OTG1", "OTG4", "OTGR", "OTGL", "OTGU", "OTGD",
+ "OTGH", "OTGV", "OTGC", "meml", "memu", "box1"];
diff --git a/vendor/term/src/terminfo/searcher.rs b/vendor/term/src/terminfo/searcher.rs
new file mode 100644
index 000000000..fca3061f4
--- /dev/null
+++ b/vendor/term/src/terminfo/searcher.rs
@@ -0,0 +1,93 @@
+// Copyright 2019 The Rust Project Developers. See the COPYRIGHT
+// 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.
+
+//! ncurses-compatible database discovery
+//!
+//! Does not support hashed database, only filesystem!
+
+use std::env;
+use std::fs;
+use std::path::PathBuf;
+
+use dirs_next as dirs;
+
+/// Return path to database entry for `term`
+pub fn get_dbpath_for_term(term: &str) -> Option<PathBuf> {
+ let mut dirs_to_search = Vec::new();
+ let first_char = match term.chars().next() {
+ Some(c) => c,
+ None => return None,
+ };
+
+ // Find search directory
+ // The terminfo manual says:
+ //
+ // > If the environment variable TERMINFO is set, it is interpreted
+ // > as the pathname of a directory containing the compiled description
+ // > you are working on. Only that directory is searched.
+ //
+ // However, the ncurses manual says:
+ //
+ // > If the environment variable TERMINFO is defined, any program using
+ // > curses checks for a local terminal definition before checking in
+ // > the standard place.
+ //
+ // Given that ncurses is the defacto standard, we follow the ncurses manual.
+ if let Some(dir) = env::var_os("TERMINFO") {
+ dirs_to_search.push(PathBuf::from(dir));
+ }
+
+ if let Ok(dirs) = env::var("TERMINFO_DIRS") {
+ for i in dirs.split(':') {
+ if i == "" {
+ dirs_to_search.push(PathBuf::from("/usr/share/terminfo"));
+ } else {
+ dirs_to_search.push(PathBuf::from(i));
+ }
+ }
+ } else {
+ // Found nothing in TERMINFO_DIRS, use the default paths:
+ // According to /etc/terminfo/README, after looking at
+ // ~/.terminfo, ncurses will search /etc/terminfo, then
+ // /lib/terminfo, and eventually /usr/share/terminfo.
+ // On Haiku the database can be found at /boot/system/data/terminfo
+ if let Some(mut homedir) = dirs::home_dir() {
+ homedir.push(".terminfo");
+ dirs_to_search.push(homedir)
+ }
+
+ dirs_to_search.push(PathBuf::from("/etc/terminfo"));
+ dirs_to_search.push(PathBuf::from("/lib/terminfo"));
+ dirs_to_search.push(PathBuf::from("/usr/share/terminfo"));
+ dirs_to_search.push(PathBuf::from("/boot/system/data/terminfo"));
+ }
+
+ // Look for the terminal in all of the search directories
+ for mut p in dirs_to_search {
+ if fs::metadata(&p).is_ok() {
+ p.push(&first_char.to_string());
+ p.push(&term);
+ if fs::metadata(&p).is_ok() {
+ return Some(p);
+ }
+ p.pop();
+ p.pop();
+
+ // on some installations the dir is named after the hex of the char
+ // (e.g. OS X)
+ p.push(&format!("{:x}", first_char as usize));
+ p.push(term);
+ if fs::metadata(&p).is_ok() {
+ return Some(p);
+ }
+ }
+ }
+ None
+}
diff --git a/vendor/term/src/win.rs b/vendor/term/src/win.rs
new file mode 100644
index 000000000..48ece2db2
--- /dev/null
+++ b/vendor/term/src/win.rs
@@ -0,0 +1,434 @@
+// Copyright 2013-2019 The Rust Project Developers. See the COPYRIGHT
+// 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.
+
+//! Windows console handling
+
+// FIXME (#13400): this is only a tiny fraction of the Windows console api
+
+use crate::color;
+use crate::Attr;
+use crate::Error;
+use crate::Result;
+use crate::Terminal;
+use std::io;
+use std::io::prelude::*;
+use std::ops::Deref;
+use std::ptr;
+
+use winapi::shared::minwindef::{DWORD, WORD};
+use winapi::um::consoleapi::{GetConsoleMode, SetConsoleMode};
+use winapi::um::fileapi::{CreateFileA, OPEN_EXISTING};
+use winapi::um::handleapi::{CloseHandle, INVALID_HANDLE_VALUE};
+use winapi::um::wincon::FillConsoleOutputAttribute;
+use winapi::um::wincon::{
+ FillConsoleOutputCharacterW, GetConsoleScreenBufferInfo, CONSOLE_SCREEN_BUFFER_INFO, COORD,
+};
+use winapi::um::wincon::{SetConsoleCursorPosition, SetConsoleTextAttribute};
+use winapi::um::wincon::{BACKGROUND_INTENSITY, ENABLE_VIRTUAL_TERMINAL_PROCESSING};
+use winapi::um::winnt::{FILE_SHARE_WRITE, GENERIC_READ, GENERIC_WRITE, HANDLE};
+
+/// Console info which can be used by a Terminal implementation
+/// which uses the Win32 Console API.
+pub struct WinConsoleInfo {
+ def_foreground: color::Color,
+ def_background: color::Color,
+ foreground: color::Color,
+ background: color::Color,
+ reverse: bool,
+ secure: bool,
+ standout: bool,
+}
+
+/// A Terminal implementation which uses the Win32 Console API.
+pub struct WinConsole<T> {
+ buf: T,
+ info: WinConsoleInfo,
+}
+
+fn color_to_bits(color: color::Color) -> u16 {
+ // magic numbers from mingw-w64's wincon.h
+
+ let bits = match color % 8 {
+ color::BLACK => 0,
+ color::BLUE => 0x1,
+ color::GREEN => 0x2,
+ color::RED => 0x4,
+ color::YELLOW => 0x2 | 0x4,
+ color::MAGENTA => 0x1 | 0x4,
+ color::CYAN => 0x1 | 0x2,
+ color::WHITE => 0x1 | 0x2 | 0x4,
+ _ => unreachable!(),
+ };
+
+ if color >= 8 {
+ bits | 0x8
+ } else {
+ bits
+ }
+}
+
+fn bits_to_color(bits: u16) -> color::Color {
+ let color = match bits & 0x7 {
+ 0 => color::BLACK,
+ 0x1 => color::BLUE,
+ 0x2 => color::GREEN,
+ 0x4 => color::RED,
+ 0x6 => color::YELLOW,
+ 0x5 => color::MAGENTA,
+ 0x3 => color::CYAN,
+ 0x7 => color::WHITE,
+ _ => unreachable!(),
+ };
+
+ color | (bits as u32 & 0x8) // copy the hi-intensity bit
+}
+
+struct HandleWrapper {
+ inner: HANDLE,
+}
+
+impl HandleWrapper {
+ fn new(h: HANDLE) -> HandleWrapper {
+ HandleWrapper { inner: h }
+ }
+}
+
+impl Drop for HandleWrapper {
+ fn drop(&mut self) {
+ if self.inner != INVALID_HANDLE_VALUE {
+ unsafe {
+ CloseHandle(self.inner);
+ }
+ }
+ }
+}
+
+impl Deref for HandleWrapper {
+ type Target = HANDLE;
+ fn deref(&self) -> &HANDLE {
+ &self.inner
+ }
+}
+
+/// Just get a handle to the current console buffer whatever it is
+fn conout() -> io::Result<HandleWrapper> {
+ let name = b"CONOUT$\0";
+ let handle = unsafe {
+ CreateFileA(
+ name.as_ptr() as *const i8,
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_WRITE,
+ ptr::null_mut(),
+ OPEN_EXISTING,
+ 0,
+ ptr::null_mut(),
+ )
+ };
+ if handle == INVALID_HANDLE_VALUE {
+ Err(io::Error::last_os_error())
+ } else {
+ Ok(HandleWrapper::new(handle))
+ }
+}
+
+unsafe fn set_flag(handle: HANDLE, flag: DWORD) -> io::Result<()> {
+ let mut curr_mode: DWORD = 0;
+ if GetConsoleMode(handle, &mut curr_mode) == 0 {
+ return Err(io::Error::last_os_error());
+ }
+
+ if SetConsoleMode(handle, curr_mode | flag) == 0 {
+ return Err(io::Error::last_os_error());
+ }
+ return Ok(());
+}
+
+/// Check if console supports ansi codes (should succeed on Windows 10)
+pub fn supports_ansi() -> bool {
+ conout()
+ .and_then(|handle| unsafe { set_flag(*handle, ENABLE_VIRTUAL_TERMINAL_PROCESSING) })
+ .is_ok()
+}
+
+// This test will only pass if it is running in an actual console, probably
+#[test]
+fn test_conout() {
+ assert!(conout().is_ok())
+}
+
+#[rustversion::before(1.36)]
+unsafe fn get_console_screen_buffer_info(handle: HANDLE) -> io::Result<CONSOLE_SCREEN_BUFFER_INFO> {
+ let mut buffer_info = ::std::mem::uninitialized();
+ if GetConsoleScreenBufferInfo(handle, &mut buffer_info) == 0 {
+ Err(io::Error::last_os_error())
+ } else {
+ Ok(buffer_info)
+ }
+}
+#[rustversion::since(1.36)]
+unsafe fn get_console_screen_buffer_info(handle: HANDLE) -> io::Result<CONSOLE_SCREEN_BUFFER_INFO> {
+ let mut buffer_info = ::std::mem::MaybeUninit::uninit();
+ if GetConsoleScreenBufferInfo(handle, buffer_info.as_mut_ptr()) == 0 {
+ Err(io::Error::last_os_error())
+ } else {
+ Ok(buffer_info.assume_init())
+ }
+}
+
+// This test will only pass if it is running in an actual console, probably
+#[test]
+fn test_get_console_screen_buffer_info() {
+ let handle = conout().unwrap();
+ unsafe {
+ let buffer_info = get_console_screen_buffer_info(*handle);
+ assert!(buffer_info.is_ok());
+ }
+}
+
+impl WinConsoleInfo {
+ /// Returns `Err` whenever console info cannot be retrieved for some
+ /// reason.
+ pub fn from_env() -> io::Result<WinConsoleInfo> {
+ let fg;
+ let bg;
+ let handle = conout()?;
+ unsafe {
+ let buffer_info = get_console_screen_buffer_info(*handle)?;
+ fg = bits_to_color(buffer_info.wAttributes);
+ bg = bits_to_color(buffer_info.wAttributes >> 4);
+ }
+ Ok(WinConsoleInfo {
+ def_foreground: fg,
+ def_background: bg,
+ foreground: fg,
+ background: bg,
+ reverse: false,
+ secure: false,
+ standout: false,
+ })
+ }
+}
+
+impl<T: Write + Send> WinConsole<T> {
+ fn apply(&mut self) -> io::Result<()> {
+ let out = conout()?;
+ let _unused = self.buf.flush();
+
+ let (mut fg, bg) = if self.info.reverse {
+ (self.info.background, self.info.foreground)
+ } else {
+ (self.info.foreground, self.info.background)
+ };
+
+ if self.info.secure {
+ fg = bg;
+ }
+
+ let mut accum: WORD = 0;
+
+ accum |= color_to_bits(fg);
+ accum |= color_to_bits(bg) << 4;
+
+ if self.info.standout {
+ accum |= BACKGROUND_INTENSITY;
+ } else {
+ accum &= BACKGROUND_INTENSITY ^ 0xFF;
+ }
+
+ unsafe {
+ SetConsoleTextAttribute(*out, accum);
+ }
+ Ok(())
+ }
+
+ /// Create a new WinConsole with the given WinConsoleInfo and out
+ pub fn new_with_consoleinfo(out: T, info: WinConsoleInfo) -> WinConsole<T> {
+ WinConsole { buf: out, info }
+ }
+
+ /// Returns `Err` whenever the terminal cannot be created for some
+ /// reason.
+ pub fn new(out: T) -> io::Result<WinConsole<T>> {
+ let info = WinConsoleInfo::from_env()?;
+ Ok(Self::new_with_consoleinfo(out, info))
+ }
+}
+
+impl<T: Write> Write for WinConsole<T> {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ self.buf.write(buf)
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ self.buf.flush()
+ }
+}
+
+impl<T: Write + Send> Terminal for WinConsole<T> {
+ type Output = T;
+
+ fn fg(&mut self, color: color::Color) -> Result<()> {
+ self.info.foreground = color;
+ self.apply()?;
+
+ Ok(())
+ }
+
+ fn bg(&mut self, color: color::Color) -> Result<()> {
+ self.info.background = color;
+ self.apply()?;
+
+ Ok(())
+ }
+
+ fn attr(&mut self, attr: Attr) -> Result<()> {
+ match attr {
+ Attr::ForegroundColor(f) => {
+ self.info.foreground = f;
+ self.apply()?;
+ Ok(())
+ }
+ Attr::BackgroundColor(b) => {
+ self.info.background = b;
+ self.apply()?;
+ Ok(())
+ }
+ Attr::Reverse => {
+ self.info.reverse = true;
+ self.apply()?;
+ Ok(())
+ }
+ Attr::Secure => {
+ self.info.secure = true;
+ self.apply()?;
+ Ok(())
+ }
+ Attr::Standout(v) => {
+ self.info.standout = v;
+ self.apply()?;
+ Ok(())
+ }
+ _ => Err(Error::NotSupported),
+ }
+ }
+
+ fn supports_attr(&self, attr: Attr) -> bool {
+ match attr {
+ Attr::ForegroundColor(_)
+ | Attr::BackgroundColor(_)
+ | Attr::Standout(_)
+ | Attr::Reverse
+ | Attr::Secure => true,
+ _ => false,
+ }
+ }
+
+ fn reset(&mut self) -> Result<()> {
+ self.info.foreground = self.info.def_foreground;
+ self.info.background = self.info.def_background;
+ self.info.reverse = false;
+ self.info.secure = false;
+ self.info.standout = false;
+ self.apply()?;
+
+ Ok(())
+ }
+
+ fn supports_reset(&self) -> bool {
+ true
+ }
+
+ fn supports_color(&self) -> bool {
+ true
+ }
+
+ fn cursor_up(&mut self) -> Result<()> {
+ let _unused = self.buf.flush();
+ let handle = conout()?;
+ unsafe {
+ let buffer_info = get_console_screen_buffer_info(*handle)?;
+ let (x, y) = (
+ buffer_info.dwCursorPosition.X,
+ buffer_info.dwCursorPosition.Y,
+ );
+ if y == 0 {
+ // Even though this might want to be a CursorPositionInvalid, on Unix there
+ // is no checking to see if the cursor is already on the first line.
+ // I'm not sure what the ideal behavior is, but I think it'd be silly to have
+ // cursor_up fail in this case.
+ Ok(())
+ } else {
+ let pos = COORD { X: x, Y: y - 1 };
+ if SetConsoleCursorPosition(*handle, pos) != 0 {
+ Ok(())
+ } else {
+ Err(io::Error::last_os_error().into())
+ }
+ }
+ }
+ }
+
+ fn delete_line(&mut self) -> Result<()> {
+ let _unused = self.buf.flush();
+ let handle = conout()?;
+ unsafe {
+ let buffer_info = get_console_screen_buffer_info(*handle)?;
+ let pos = buffer_info.dwCursorPosition;
+ let size = buffer_info.dwSize;
+ let num = (size.X - pos.X) as DWORD;
+ let mut written = 0;
+ // 0x0020u16 is ' ' (space) in UTF-16 (same as ascii)
+ if FillConsoleOutputCharacterW(*handle, 0x0020, num, pos, &mut written) == 0 {
+ return Err(io::Error::last_os_error().into());
+ }
+ if FillConsoleOutputAttribute(*handle, 0, num, pos, &mut written) == 0 {
+ return Err(io::Error::last_os_error().into());
+ }
+ // Similar reasoning for not failing as in cursor_up -- it doesn't even make
+ // sense to
+ // me that these APIs could have written 0, unless the terminal is width zero.
+ Ok(())
+ }
+ }
+
+ fn carriage_return(&mut self) -> Result<()> {
+ let _unused = self.buf.flush();
+ let handle = conout()?;
+ unsafe {
+ let buffer_info = get_console_screen_buffer_info(*handle)?;
+ let COORD { X: x, Y: y } = buffer_info.dwCursorPosition;
+ if x == 0 {
+ Err(Error::CursorDestinationInvalid)
+ } else {
+ let pos = COORD { X: 0, Y: y };
+ if SetConsoleCursorPosition(*handle, pos) != 0 {
+ Ok(())
+ } else {
+ Err(io::Error::last_os_error().into())
+ }
+ }
+ }
+ }
+
+ fn get_ref<'a>(&'a self) -> &'a T {
+ &self.buf
+ }
+
+ fn get_mut<'a>(&'a mut self) -> &'a mut T {
+ &mut self.buf
+ }
+
+ fn into_inner(self) -> T
+ where
+ Self: Sized,
+ {
+ self.buf
+ }
+}