From 7f1b2822a2b956d47ebad667dd4c427888bb5a27 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Tue, 26 Jan 2016 09:01:45 -0800 Subject: [PATCH 1/5] term: Remove unnecessary #[allow(deprecated)] and broken test --- src/libterm/terminfo/searcher.rs | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/src/libterm/terminfo/searcher.rs b/src/libterm/terminfo/searcher.rs index e869c5083373c..ccd152c66fd4b 100644 --- a/src/libterm/terminfo/searcher.rs +++ b/src/libterm/terminfo/searcher.rs @@ -17,7 +17,6 @@ use std::fs; use std::path::PathBuf; /// Return path to database entry for `term` -#[allow(deprecated)] pub fn get_dbpath_for_term(term: &str) -> Option { let mut dirs_to_search = Vec::new(); let first_char = match term.chars().next() { @@ -79,21 +78,3 @@ pub fn get_dbpath_for_term(term: &str) -> Option { } None } - -#[test] -#[ignore(reason = "buildbots don't have ncurses installed and I can't mock everything I need")] -fn test_get_dbpath_for_term() { - // woefully inadequate test coverage - // note: current tests won't work with non-standard terminfo hierarchies (e.g. OS X's) - use std::env; - // FIXME (#9639): This needs to handle non-utf8 paths - fn x(t: &str) -> String { - let p = get_dbpath_for_term(t).expect("no terminfo entry found"); - p.to_str().unwrap().to_string() - } - assert!(x("screen") == "/usr/share/terminfo/s/screen"); - assert!(get_dbpath_for_term("") == None); - env::set_var("TERMINFO_DIRS", ":"); - assert!(x("screen") == "/usr/share/terminfo/s/screen"); - env::remove_var("TERMINFO_DIRS"); -} From cc238a30ab4682413ae926c16da33cb045bb5feb Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Thu, 4 Feb 2016 07:43:19 -0800 Subject: [PATCH 2/5] term: synchronize with public "term" crate v0.4.4 The only point of divergence from the public crate is that this patch uses `std::sys::windows` instead of the `winapi` and `kernel32` crate to access the Windows Console APIs. --- src/libterm/lib.rs | 207 +++++++++++++++++-- src/libterm/terminfo/mod.rs | 196 +++++++++++------- src/libterm/terminfo/parm.rs | 158 +++++++++------ src/libterm/terminfo/parser/compiled.rs | 252 ++++++------------------ src/libterm/terminfo/parser/names.rs | 164 +++++++++++++++ src/libterm/win.rs | 212 ++++++++++++++------ 6 files changed, 791 insertions(+), 398 deletions(-) create mode 100644 src/libterm/terminfo/parser/names.rs diff --git a/src/libterm/lib.rs b/src/libterm/lib.rs index 771e24704558d..e8870840b8c09 100644 --- a/src/libterm/lib.rs +++ b/src/libterm/lib.rs @@ -32,7 +32,7 @@ //! t.fg(term::color::RED).unwrap(); //! writeln!(t, "world!").unwrap(); //! -//! assert!(t.reset().unwrap()); +//! t.reset().unwrap(); //! } //! ``` //! @@ -166,6 +166,161 @@ pub enum Attr { 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 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 = std::result::Result; + +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + use std::error::Error; + if let &::Error::Io(ref e) = self { + write!(f, "{}", e) + } else { + f.write_str(self.description()) + } + } +} + +impl std::error::Error for Error { + fn description(&self) -> &str { + use Error::*; + use std::error::Error; + match self { + &Io(ref io) => io.description(), + &TerminfoParsing(ref e) => e.description(), + &ParameterizedExpansion(ref e) => e.description(), + &NotSupported => "operation not supported by the terminal", + &TermUnset => "TERM environment variable unset, unable to detect a terminal", + &TerminfoEntryNotFound => "could not find a terminfo entry for this terminal", + &CursorDestinationInvalid => "could not move cursor to requested position", + &ColorOutOfRange => "color not supported by the terminal", + &__Nonexhaustive => "placeholder variant that shouldn't be used", + } + } + + fn cause(&self) -> Option<&std::error::Error> { + match self { + &Error::Io(ref io) => Some(io), + &Error::TerminfoParsing(ref e) => Some(e), + &Error::ParameterizedExpansion(ref e) => Some(e), + _ => None, + } + } +} + +impl From 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 for Error { + fn from(val: io::Error) -> Self { + Error::Io(val) + } +} + +impl std::convert::From for Error { + fn from(val: terminfo::Error) -> Self { + Error::TerminfoParsing(val) + } +} + +impl std::convert::From 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 { @@ -177,38 +332,62 @@ pub trait Terminal: Write { /// If the color is a bright color, but the terminal only supports 8 colors, /// the corresponding normal color will be used instead. /// - /// Returns `Ok(true)` if the color was set, `Ok(false)` otherwise, and `Err(e)` - /// if there was an I/O error. - fn fg(&mut self, color: color::Color) -> io::Result; + /// 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(true)` if the color was set, `Ok(false)` otherwise, and `Err(e)` - /// if there was an I/O error. - fn bg(&mut self, color: color::Color) -> io::Result; + /// 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(true)` - /// if the attribute was supported, `Ok(false)` otherwise, and `Err(e)` if - /// there was an I/O error. - fn attr(&mut self, attr: Attr) -> io::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(true)` if the terminal was reset, `Ok(false)` otherwise, and `Err(e)` if there - /// was an I/O error. + /// 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) -> io::Result; + 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<'a>(&'a self) -> &'a Self::Output; diff --git a/src/libterm/terminfo/mod.rs b/src/libterm/terminfo/mod.rs index 04486132c84de..fdc7f4ac6ed85 100644 --- a/src/libterm/terminfo/mod.rs +++ b/src/libterm/terminfo/mod.rs @@ -12,8 +12,6 @@ use std::collections::HashMap; use std::env; -use std::error; -use std::fmt; use std::fs::File; use std::io::prelude::*; use std::io; @@ -23,9 +21,11 @@ use std::path::Path; use Attr; use color; use Terminal; +use Result; use self::searcher::get_dbpath_for_term; use self::parser::compiled::{parse, msys_terminfo}; use self::parm::{expand, Variables, Param}; +use self::Error::*; /// A parsed terminfo database entry. @@ -41,49 +41,12 @@ pub struct TermInfo { pub strings: HashMap>, } -/// A terminfo creation error. -#[derive(Debug)] -pub 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 description(&self) -> &str { - "failed to create TermInfo" - } - - fn cause(&self) -> Option<&error::Error> { - use self::Error::*; - match self { - &IoError(ref e) => Some(e), - _ => None, - } - } -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use self::Error::*; - match self { - &TermUnset => Ok(()), - &MalformedTerminfo(ref e) => e.fmt(f), - &IoError(ref e) => e.fmt(f), - } - } -} - impl TermInfo { /// Create a TermInfo based on current environment. - pub fn from_env() -> Result { + pub fn from_env() -> Result { let term = match env::var("TERM") { Ok(name) => TermInfo::from_name(&name), - Err(..) => return Err(Error::TermUnset), + Err(..) => return Err(::Error::TermUnset), }; if term.is_err() && env::var("MSYSCON").ok().map_or(false, |s| "mintty.exe" == s) { @@ -95,23 +58,95 @@ impl TermInfo { } /// Create a TermInfo for the named terminal. - pub fn from_name(name: &str) -> Result { + pub fn from_name(name: &str) -> Result { 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))) + .ok_or_else(|| ::Error::TerminfoEntryNotFound) + .and_then(|p| TermInfo::from_path(&p)) } /// Parse the given TermInfo. - pub fn from_path>(path: P) -> Result { + pub fn from_path>(path: P) -> Result { Self::_from_path(path.as_ref()) } // Keep the metadata small - fn _from_path(path: &Path) -> Result { - let file = try!(File::open(path).map_err(|e| Error::IoError(e))); + // (That is, this uses a &Path so that this function need not be instantiated + // for every type + // which implements AsRef. One day, if/when rustc is a bit smarter, it + // might do this for + // us. Alas. ) + fn _from_path(path: &Path) -> Result { + let file = try!(File::open(path).map_err(|e| ::Error::Io(e))); let mut reader = BufReader::new(file); - parse(&mut reader, false).map_err(|e| Error::MalformedTerminfo(e)) + parse(&mut reader, false) + } +} + +#[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` + 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 { + use std::error::Error; + match self { + &NotUtf8(e) => write!(f, "{}", e), + &BadMagic(v) => write!(f, "bad magic number {:x} in terminfo header", v), + _ => f.write_str(self.description()), + } + } +} + +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 description(&self) -> &str { + match self { + &BadMagic(..) => "incorrect magic number at start of file", + &ShortNames => "no names exposed, need at least one", + &TooManyBools => "more boolean properties than libterm knows about", + &TooManyNumbers => "more number properties than libterm knows about", + &TooManyStrings => "more string properties than libterm knows about", + &InvalidLength => "invalid length field value, must be >= -1", + &NotUtf8(ref e) => e.description(), + &NamesMissingNull => "names table missing NUL terminator", + &StringsMissingNull => "string table missing NUL terminator", + } + } + + fn cause(&self) -> Option<&::std::error::Error> { + match self { + &NotUtf8(ref e) => Some(e), + _ => None, + } } } @@ -121,6 +156,7 @@ pub mod searcher; pub mod parser { //! ncurses-compatible compiled terminfo format parsing (term(5)) pub mod compiled; + mod names; } pub mod parm; @@ -153,23 +189,23 @@ pub struct TerminfoTerminal { impl Terminal for TerminfoTerminal { type Output = T; - fn fg(&mut self, color: color::Color) -> io::Result { + fn fg(&mut self, color: color::Color) -> Result<()> { let color = self.dim_if_necessary(color); if self.num_colors > color { return self.apply_cap("setaf", &[Param::Number(color as i32)]); } - Ok(false) + Err(::Error::ColorOutOfRange) } - fn bg(&mut self, color: color::Color) -> io::Result { + fn bg(&mut self, color: color::Color) -> Result<()> { let color = self.dim_if_necessary(color); if self.num_colors > color { return self.apply_cap("setab", &[Param::Number(color as i32)]); } - Ok(false) + Err(::Error::ColorOutOfRange) } - fn attr(&mut self, attr: Attr) -> io::Result { + fn attr(&mut self, attr: Attr) -> Result<()> { match attr { Attr::ForegroundColor(c) => self.fg(c), Attr::BackgroundColor(c) => self.bg(c), @@ -187,22 +223,45 @@ impl Terminal for TerminfoTerminal { } } - fn reset(&mut self) -> io::Result { + fn reset(&mut self) -> Result<()> { // are there any terminals that have color/attrs and not sgr0? // Try falling back to sgr, then op - let cmd = match ["sg0", "sgr", "op"] + let cmd = match [("sgr0", &[] as &[Param]), + ("sgr", &[Param::Number(0)]), + ("op", &[])] .iter() - .filter_map(|cap| self.ti.strings.get(*cap)) + .filter_map(|&(cap, params)| self.ti.strings.get(cap).map(|c| (c, params))) .next() { - Some(op) => { - match expand(&op, &[], &mut Variables::new()) { + Some((op, params)) => { + match expand(op, params, &mut Variables::new()) { Ok(cmd) => cmd, - Err(e) => return Err(io::Error::new(io::ErrorKind::InvalidData, e)), + Err(e) => return Err(e.into()), } } - None => return Ok(false), + None => return Err(::Error::NotSupported), }; - self.out.write_all(&cmd).and(Ok(true)) + try!(self.out.write_all(&cmd)); + Ok(()) + } + + 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.apply_cap("cuu1", &[]) + } + + fn delete_line(&mut self) -> Result<()> { + self.apply_cap("dl", &[]) + } + + fn carriage_return(&mut self) -> Result<()> { + self.apply_cap("cr", &[]) } fn get_ref<'a>(&'a self) -> &'a T { @@ -252,15 +311,18 @@ impl TerminfoTerminal { } } - fn apply_cap(&mut self, cmd: &str, params: &[Param]) -> io::Result { + fn apply_cap(&mut self, cmd: &str, params: &[Param]) -> Result<()> { 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)), + Ok(s) => { + try!(self.out.write_all(&s)); + Ok(()) + } + Err(e) => Err(e.into()), } } - None => Ok(false), + None => Err(::Error::NotSupported), } } } diff --git a/src/libterm/terminfo/parm.rs b/src/libterm/terminfo/parm.rs index aceaa0c10bcce..86f1411f675e8 100644 --- a/src/libterm/terminfo/parm.rs +++ b/src/libterm/terminfo/parm.rs @@ -49,6 +49,61 @@ pub enum Param { Number(i32), } +/// 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 std::error::Error; + f.write_str(self.description()) + } +} + +impl ::std::error::Error for Error { + fn description(&self) -> &str { + use self::Error::*; + match self { + &StackUnderflow => "not enough elements on the stack", + &TypeMismatch => "type mismatch", + &UnrecognizedFormatOption(_) => "unrecognized format option", + &InvalidVariableName(_) => "invalid variable name", + &InvalidParameterIndex(_) => "invalid parameter index", + &MalformedCharacterConstant => "malformed character constant", + &IntegerConstantOverflow => "integer constant computation overflowed", + &MalformedIntegerConstant => "malformed integer constant", + &FormatWidthOverflow => "format width constant computation overflowed", + &FormatPrecisionOverflow => "format precision constant computation overflowed", + } + } + + fn cause(&self) -> Option<&::std::error::Error> { + None + } +} + /// Container for static and dynamic variable arrays pub struct Variables { /// Static variables A-Z @@ -82,7 +137,7 @@ impl Variables { /// /// 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, String> { +pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables) -> Result, Error> { let mut state = Nothing; // expanded cap will only rarely be larger than the cap itself @@ -120,8 +175,8 @@ pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables) -> Result output.push(128u8), // Don't check bounds. ncurses just casts and truncates. Some(Number(c)) => output.push(c as u8), - Some(_) => return Err("a non-char was used with %c".to_string()), - None => return Err("stack is empty".to_string()), + Some(_) => return Err(Error::TypeMismatch), + None => return Err(Error::StackUnderflow), } } 'p' => state = PushParam, @@ -132,8 +187,8 @@ pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables) -> Result { match stack.pop() { Some(Words(s)) => stack.push(Number(s.len() as i32)), - Some(_) => return Err("a non-str was used with %l".to_string()), - None => return Err("stack is empty".to_string()), + Some(_) => return Err(Error::TypeMismatch), + None => return Err(Error::StackUnderflow), } } '+' | '-' | '/' | '*' | '^' | '&' | '|' | 'm' => { @@ -148,13 +203,11 @@ pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables) -> Result x & y, '^' => x ^ y, 'm' => x % y, - _ => unreachable!("All cases handled"), + _ => unreachable!("logic error"), })) } - (Some(_), Some(_)) => { - return Err(format!("non-numbers on stack with {}", cur)) - } - _ => return Err("stack is empty".to_string()), + (Some(_), Some(_)) => return Err(Error::TypeMismatch), + _ => return Err(Error::StackUnderflow), } } '=' | '>' | '<' | 'A' | 'O' => { @@ -166,17 +219,15 @@ pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables) -> Result' => x > y, 'A' => x > 0 && y > 0, 'O' => x > 0 || y > 0, - _ => unreachable!(), + _ => unreachable!("logic error"), } { 1 } else { 0 })) } - (Some(_), Some(_)) => { - return Err(format!("non-numbers on stack with {}", cur)) - } - _ => return Err("stack is empty".to_string()), + (Some(_), Some(_)) => return Err(Error::TypeMismatch), + _ => return Err(Error::StackUnderflow), } } '!' | '~' => { @@ -186,11 +237,11 @@ pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables) -> Result 0 => 0, '!' => 1, '~' => !x, - _ => unreachable!(), + _ => unreachable!("logic error"), })) } - Some(_) => return Err(format!("non-numbers on stack with {}", cur)), - None => return Err("stack is empty".to_string()), + Some(_) => return Err(Error::TypeMismatch), + None => return Err(Error::StackUnderflow), } } 'i' => { @@ -199,9 +250,7 @@ pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables) -> Result { - return Err("first two params not numbers with %i".to_string()) - } + (_, _) => return Err(Error::TypeMismatch), } } @@ -212,7 +261,7 @@ pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables) -> Result { @@ -227,7 +276,7 @@ pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables) -> Result unreachable!(), + _ => unreachable!("logic error"), } state = FormatPattern(flags, fstate); } @@ -238,22 +287,20 @@ pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables) -> Result state = SeekIfElse(0), Some(Number(_)) => (), - Some(_) => { - return Err("non-number on stack with conditional".to_string()) - } - None => return Err("stack is empty".to_string()), + Some(_) => return Err(Error::TypeMismatch), + None => return Err(Error::StackUnderflow), } } 'e' => state = SeekIfEnd(0), ';' => (), - _ => return Err(format!("unrecognized format option {}", cur)), + 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("bad param number".to_string()), + None => return Err(Error::InvalidParameterIndex(cur)), }] .clone()); } @@ -263,17 +310,17 @@ pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables) -> Result= 'a' && cur <= 'z' { if let Some(arg) = stack.pop() { let idx = (cur as u8) - b'a'; vars.dyn[idx as usize] = arg; } else { - return Err("stack is empty".to_string()); + return Err(Error::StackUnderflow); } } else { - return Err("bad variable name in %P".to_string()); + return Err(Error::InvalidVariableName(cur)); } } GetVar => { @@ -284,7 +331,7 @@ pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables) -> Result { @@ -293,7 +340,7 @@ pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables) -> Result { if cur != '\'' { - return Err("malformed character constant".to_string()); + return Err(Error::MalformedCharacterConstant); } } IntConstant(i) => { @@ -306,10 +353,10 @@ pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables) -> Result return Err("int constant too large".to_string()), + None => return Err(Error::IntegerConstantOverflow), } } else { - return Err("bad int constant".to_string()); + return Err(Error::MalformedIntegerConstant); } } FormatPattern(ref mut flags, ref mut fstate) => { @@ -322,7 +369,7 @@ pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables) -> Result { @@ -345,23 +392,25 @@ pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables) -> Result { - let old = flags.width; - flags.width = flags.width * 10 + (cur as usize - '0' as usize); - if flags.width < old { - return Err("format width overflow".to_string()); + 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), } } (FormatStateWidth, '.') => { *fstate = FormatStatePrecision; } (FormatStatePrecision, '0'...'9') => { - let old = flags.precision; - flags.precision = flags.precision * 10 + (cur as usize - '0' as usize); - if flags.precision < old { - return Err("format precision overflow".to_string()); + 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("invalid format specifier".to_string()), + _ => return Err(Error::UnrecognizedFormatOption(cur)), } } SeekIfElse(level) => { @@ -455,18 +504,9 @@ impl FormatOp { _ => panic!("bad FormatOp char"), } } - fn to_char(self) -> char { - match self { - FormatDigit => 'd', - FormatOctal => 'o', - FormatHex => 'x', - FormatHEX => 'X', - FormatString => 's', - } - } } -fn format(val: Param, op: FormatOp, flags: Flags) -> Result, String> { +fn format(val: Param, op: FormatOp, flags: Flags) -> Result, Error> { let mut s = match val { Number(d) => { match op { @@ -504,7 +544,7 @@ fn format(val: Param, op: FormatOp, flags: Flags) -> Result, String> { format!("{:01$X}", d, flags.precision) } } - FormatString => return Err("non-number on stack with %s".to_string()), + FormatString => return Err(Error::TypeMismatch), } .into_bytes() } @@ -517,7 +557,7 @@ fn format(val: Param, op: FormatOp, flags: Flags) -> Result, String> { } s } - _ => return Err(format!("non-string on stack with %{}", op.to_char())), + _ => return Err(Error::TypeMismatch), } } }; @@ -573,7 +613,7 @@ mod test { cap: &str, params: &[Param], vars: &mut Variables) - -> Result, String> { + -> Result, super::Error> { let mut u8v: Vec<_> = fmt.bytes().collect(); u8v.extend(cap.as_bytes().iter().map(|&b| b)); expand(&u8v, params, vars) diff --git a/src/libterm/terminfo/parser/compiled.rs b/src/libterm/terminfo/parser/compiled.rs index 558d35c2ae27b..9827ade3d0d5a 100644 --- a/src/libterm/terminfo/parser/compiled.rs +++ b/src/libterm/terminfo/parser/compiled.rs @@ -8,161 +8,20 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![allow(non_upper_case_globals, missing_docs)] - //! ncurses-compatible compiled terminfo format parsing (term(5)) use std::collections::HashMap; use std::io::prelude::*; use std::io; -use super::super::TermInfo; - -// These are the orders ncurses uses in its compiled format (as of 5.9). Not sure if portable. - -#[rustfmt_skip] -pub static boolfnames: &'static[&'static 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"]; - -#[rustfmt_skip] -pub static boolnames: &'static[&'static 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"]; - -#[rustfmt_skip] -pub static numfnames: &'static[&'static 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"]; - -#[rustfmt_skip] -pub static numnames: &'static[&'static 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"]; - -#[rustfmt_skip] -pub static stringfnames: &'static[&'static 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"]; - -#[rustfmt_skip] -pub static stringnames: &'static[&'static str] = &[ "cbt", "_", "cr", "csr", "tbc", "clear", - "_", "_", "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", "_", "_", "kcud1", "_", "_", "_", "_", "_", "_", "_", "_", "_", - "_", "_", "_", "_", "_", "khome", "_", "_", "kcub1", "_", "knp", "kpp", "kcuf1", "_", "_", - "khts", "_", "rmkx", "smkx", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "rmm", "_", - "_", "pad", "dch", "dl", "cud", "ich", "indn", "il", "cub", "cuf", "rin", "cuu", "pfkey", - "pfloc", "pfx", "mc0", "mc4", "_", "rep", "rs1", "rs2", "rs3", "rf", "rc", "vpa", "sc", "ind", - "ri", "sgr", "_", "wind", "_", "tsl", "uc", "hu", "iprog", "_", "_", "_", "_", "_", "mc5p", - "rmp", "acsc", "pln", "kcbt", "smxon", "rmxon", "smam", "rmam", "xonc", "xoffc", "_", "smln", - "rmln", "_", "kcan", "kclo", "kcmd", "kcpy", "kcrt", "_", "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", "_", "_", - "kslt", "kEND", "kEOL", "kEXT", "kFND", "kHLP", "kHOM", "_", "kLFT", "kMSG", "kMOV", "kNXT", - "kOPT", "kPRV", "kPRT", "kRDO", "kRPL", "kRIT", "kRES", "kSAV", "kSPD", "kUND", "rfi", "_", "_", - "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", - "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", - "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", - "dclk", "rmclk", "cwin", "wingo", "_", "dial", "qdial", "_", "_", "hook", "pause", "wait", "_", - "_", "_", "_", "_", "_", "_", "_", "_", "_", "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"]; + +use terminfo::Error::*; +use terminfo::TermInfo; +use Result; + +pub use 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 io::Read) -> io::Result { let mut b = [0; 2]; @@ -185,14 +44,7 @@ fn read_byte(r: &mut io::Read) -> io::Result { /// Parse a compiled terminfo entry, using long capability names if `longnames` /// is true -pub fn parse(file: &mut io::Read, longnames: bool) -> Result { - macro_rules! try( ($e:expr) => ( - match $e { - Ok(e) => e, - Err(e) => return Err(format!("{}", e)) - } - ) ); - +pub fn parse(file: &mut io::Read, longnames: bool) -> Result { let (bnames, snames, nnames) = if longnames { (boolfnames, stringfnames, numfnames) } else { @@ -202,19 +54,19 @@ pub fn parse(file: &mut io::Read, longnames: bool) -> Result { // Check magic number let magic = try!(read_le_u16(file)); if magic != 0x011A { - return Err(format!("invalid magic number: expected {:x}, found {:x}", - 0x011A, - magic)); + 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. + // 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 try!(read_le_u16(file)) as i16 { n if n >= 0 => n as usize, -1 => 0, - _ => return Err("incompatible file: length fields must be >= -1".to_string()), + _ => return Err(InvalidLength.into()), } }} } @@ -226,19 +78,19 @@ pub fn parse(file: &mut io::Read, longnames: bool) -> Result { let string_table_bytes = read_nonneg!(); if names_bytes == 0 { - return Err("incompatible file: names field must be at least 1 byte wide".to_string()); + return Err(ShortNames.into()); } if bools_bytes > boolnames.len() { - return Err("incompatible file: more booleans than expected".to_string()); + return Err(TooManyBools.into()); } if numbers_count > numnames.len() { - return Err("incompatible file: more numbers than expected".to_string()); + return Err(TooManyNumbers.into()); } if string_offsets_count > stringnames.len() { - return Err("incompatible file: more string offsets than expected".to_string()); + return Err(TooManyStrings.into()); } // don't read NUL @@ -246,7 +98,7 @@ pub fn parse(file: &mut io::Read, longnames: bool) -> Result { try!(file.take((names_bytes - 1) as u64).read_to_end(&mut bytes)); let names_str = match String::from_utf8(bytes) { Ok(s) => s, - Err(_) => return Err("input not utf-8".to_string()), + Err(e) => return Err(NotUtf8(e.utf8_error()).into()), }; let term_names: Vec = names_str.split('|') @@ -254,7 +106,7 @@ pub fn parse(file: &mut io::Read, longnames: bool) -> Result { .collect(); // consume NUL if try!(read_byte(file)) != b'\0' { - return Err("incompatible file: missing null terminator for names section".to_string()); + return Err(NamesMissingNull.into()); } let bools_map: HashMap = try! { @@ -285,31 +137,41 @@ pub fn parse(file: &mut io::Read, longnames: bool) -> Result { let mut string_table = Vec::new(); try!(file.take(string_table_bytes as u64).read_to_end(&mut string_table)); - try!(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.to_string(), 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.to_string(), string_table[offset..offset + len].to_vec())), - None => Err("invalid file: missing NUL in string_table".to_string()), - } - }).collect()) + try!(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.to_string(), 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.to_string(), + string_table[offset..offset + len].to_vec())) + } + None => return Err(::Error::TerminfoParsing(StringsMissingNull)), + } + }) + .collect()) } else { HashMap::new() }; diff --git a/src/libterm/terminfo/parser/names.rs b/src/libterm/terminfo/parser/names.rs new file mode 100644 index 0000000000000..ed3ca902b4f25 --- /dev/null +++ b/src/libterm/terminfo/parser/names.rs @@ -0,0 +1,164 @@ +// Copyright 2013-2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(non_upper_case_globals, missing_docs)] +#![cfg_attr(rustfmt, rustfmt_skip)] + +pub static boolfnames: &'static [&'static 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: &'static [&'static 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: &'static [&'static 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: &'static [&'static 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: &'static [&'static 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: &'static [&'static str] = &[ + "cbt", "_", "cr", "csr", "tbc", "clear", "_", "_", "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", "_", "_", + "kcud1", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "khome", "_", + "_", "kcub1", "_", "knp", "kpp", "kcuf1", "_", "_", "khts", "_", "rmkx", "smkx", "_", "_", "_", + "_", "_", "_", "_", "_", "_", "_", "_", "rmm", "_", "_", "pad", "dch", "dl", "cud", "ich", + "indn", "il", "cub", "cuf", "rin", "cuu", "pfkey", "pfloc", "pfx", "mc0", "mc4", "_", "rep", + "rs1", "rs2", "rs3", "rf", "rc", "vpa", "sc", "ind", "ri", "sgr", "_", "wind", "_", "tsl", + "uc", "hu", "iprog", "_", "_", "_", "_", "_", "mc5p", "rmp", "acsc", "pln", "kcbt", "smxon", + "rmxon", "smam", "rmam", "xonc", "xoffc", "_", "smln", "rmln", "_", "kcan", "kclo", "kcmd", + "kcpy", "kcrt", "_", "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", "_", "_", "kslt", "kEND", "kEOL", "kEXT", "kFND", + "kHLP", "kHOM", "_", "kLFT", "kMSG", "kMOV", "kNXT", "kOPT", "kPRV", "kPRT", "kRDO", "kRPL", + "kRIT", "kRES", "kSAV", "kSPD", "kUND", "rfi", "_", "_", "_", "_", "_", "_", "_", "_", "_", + "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", + "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", + "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "dclk", "rmclk", "cwin", "wingo", + "_", "dial", "qdial", "_", "_", "hook", "pause", "wait", "_", "_", "_", "_", "_", "_", "_", + "_", "_", "_", "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/src/libterm/win.rs b/src/libterm/win.rs index d36b182710b97..bd553c26b3c54 100644 --- a/src/libterm/win.rs +++ b/src/libterm/win.rs @@ -1,4 +1,4 @@ -// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT +// Copyright 2013-2015 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -12,14 +12,17 @@ // FIXME (#13400): this is only a tiny fraction of the Windows console api -extern crate libc; - -use std::io; use std::io::prelude::*; +use std::io; +use std::ptr; +use std::sys::windows::c as kernel32; +use std::sys::windows::c as winapi; use Attr; -use color; +use Error; +use Result; use Terminal; +use color; /// A Terminal implementation which uses the Win32 Console API. pub struct WinConsole { @@ -30,29 +33,6 @@ pub struct WinConsole { background: color::Color, } -type WORD = u16; -type DWORD = u32; -type BOOL = i32; -type HANDLE = *mut u8; - -#[allow(non_snake_case)] -#[repr(C)] -struct CONSOLE_SCREEN_BUFFER_INFO { - dwSize: [libc::c_short; 2], - dwCursorPosition: [libc::c_short; 2], - wAttributes: WORD, - srWindow: [libc::c_short; 4], - dwMaximumWindowSize: [libc::c_short; 2], -} - -#[allow(non_snake_case)] -#[link(name = "kernel32")] -extern "system" { - fn SetConsoleTextAttribute(handle: HANDLE, attr: WORD) -> BOOL; - fn GetStdHandle(which: DWORD) -> HANDLE; - fn GetConsoleScreenBufferInfo(handle: HANDLE, info: *mut CONSOLE_SCREEN_BUFFER_INFO) -> BOOL; -} - fn color_to_bits(color: color::Color) -> u16 { // magic numbers from mingw-w64's wincon.h @@ -91,41 +71,57 @@ fn bits_to_color(bits: u16) -> color::Color { color | (bits & 0x8) // copy the hi-intensity bit } -impl WinConsole { - fn apply(&mut self) { +// Just get a handle to the current console buffer whatever it is +fn conout() -> io::Result { + let name = b"CONOUT$\0"; + let handle = unsafe { + kernel32::CreateFileA(name.as_ptr() as *const i8, + winapi::GENERIC_READ | winapi::GENERIC_WRITE, + winapi::FILE_SHARE_WRITE, + ptr::null_mut(), + winapi::OPEN_EXISTING, + 0, + ptr::null_mut()) + }; + if handle == winapi::INVALID_HANDLE_VALUE { + Err(io::Error::last_os_error()) + } else { + Ok(handle) + } +} + +// This test will only pass if it is running in an actual console, probably +#[test] +fn test_conout() { + assert!(conout().is_ok()) +} + +impl WinConsole { + fn apply(&mut self) -> io::Result<()> { + let out = try!(conout()); let _unused = self.buf.flush(); - let mut accum: WORD = 0; + let mut accum: winapi::WORD = 0; accum |= color_to_bits(self.foreground); accum |= color_to_bits(self.background) << 4; - unsafe { - // Magic -11 means stdout, from - // http://msdn.microsoft.com/en-us/library/windows/desktop/ms683231%28v=vs.85%29.aspx - // - // You may be wondering, "but what about stderr?", and the answer - // to that is that setting terminal attributes on the stdout - // handle also sets them for stderr, since they go to the same - // terminal! Admittedly, this is fragile, since stderr could be - // redirected to a different console. This is good enough for - // rustc though. See #13400. - let out = GetStdHandle(-11i32 as DWORD); - SetConsoleTextAttribute(out, accum); + kernel32::SetConsoleTextAttribute(out, accum); } + Ok(()) } - /// Returns `None` whenever the terminal cannot be created for some + /// Returns `Err` whenever the terminal cannot be created for some /// reason. pub fn new(out: T) -> io::Result> { let fg; let bg; + let handle = try!(conout()); unsafe { let mut buffer_info = ::std::mem::uninitialized(); - if GetConsoleScreenBufferInfo(GetStdHandle(-11i32 as DWORD), &mut buffer_info) != 0 { + if kernel32::GetConsoleScreenBufferInfo(handle, &mut buffer_info) != 0 { fg = bits_to_color(buffer_info.wAttributes); bg = bits_to_color(buffer_info.wAttributes >> 4); } else { - fg = color::WHITE; - bg = color::BLACK; + return Err(io::Error::last_os_error()); } } Ok(WinConsole { @@ -148,36 +144,36 @@ impl Write for WinConsole { } } -impl Terminal for WinConsole { +impl Terminal for WinConsole { type Output = T; - fn fg(&mut self, color: color::Color) -> io::Result { + fn fg(&mut self, color: color::Color) -> Result<()> { self.foreground = color; - self.apply(); + try!(self.apply()); - Ok(true) + Ok(()) } - fn bg(&mut self, color: color::Color) -> io::Result { + fn bg(&mut self, color: color::Color) -> Result<()> { self.background = color; - self.apply(); + try!(self.apply()); - Ok(true) + Ok(()) } - fn attr(&mut self, attr: Attr) -> io::Result { + fn attr(&mut self, attr: Attr) -> Result<()> { match attr { Attr::ForegroundColor(f) => { self.foreground = f; - self.apply(); - Ok(true) + try!(self.apply()); + Ok(()) } Attr::BackgroundColor(b) => { self.background = b; - self.apply(); - Ok(true) + try!(self.apply()); + Ok(()) } - _ => Ok(false), + _ => Err(Error::NotSupported), } } @@ -190,12 +186,102 @@ impl Terminal for WinConsole { } } - fn reset(&mut self) -> io::Result { + fn reset(&mut self) -> Result<()> { self.foreground = self.def_foreground; self.background = self.def_background; - self.apply(); + try!(self.apply()); + + Ok(()) + } + + fn supports_reset(&self) -> bool { + true + } + + fn supports_color(&self) -> bool { + true + } - Ok(true) + fn cursor_up(&mut self) -> Result<()> { + let _unused = self.buf.flush(); + let handle = try!(conout()); + unsafe { + let mut buffer_info = ::std::mem::uninitialized(); + if kernel32::GetConsoleScreenBufferInfo(handle, &mut buffer_info) != 0 { + 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 = winapi::COORD { + X: x, + Y: y - 1, + }; + if kernel32::SetConsoleCursorPosition(handle, pos) != 0 { + Ok(()) + } else { + Err(io::Error::last_os_error().into()) + } + } + } else { + Err(io::Error::last_os_error().into()) + } + } + } + + fn delete_line(&mut self) -> Result<()> { + let _unused = self.buf.flush(); + let handle = try!(conout()); + unsafe { + let mut buffer_info = ::std::mem::uninitialized(); + if kernel32::GetConsoleScreenBufferInfo(handle, &mut buffer_info) == 0 { + return Err(io::Error::last_os_error().into()); + } + let pos = buffer_info.dwCursorPosition; + let size = buffer_info.dwSize; + let num = (size.X - pos.X) as winapi::DWORD; + let mut written = 0; + if kernel32::FillConsoleOutputCharacterW(handle, 0, num, pos, &mut written) == 0 { + return Err(io::Error::last_os_error().into()); + } + if kernel32::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 = try!(conout()); + unsafe { + let mut buffer_info = ::std::mem::uninitialized(); + if kernel32::GetConsoleScreenBufferInfo(handle, &mut buffer_info) != 0 { + let winapi::COORD { X: x, Y: y } = buffer_info.dwCursorPosition; + if x == 0 { + Err(Error::CursorDestinationInvalid) + } else { + let pos = winapi::COORD { + X: 0, + Y: y, + }; + if kernel32::SetConsoleCursorPosition(handle, pos) != 0 { + Ok(()) + } else { + Err(io::Error::last_os_error().into()) + } + } + } else { + Err(io::Error::last_os_error().into()) + } + } } fn get_ref<'a>(&'a self) -> &'a T { From 1602ca570f3422bb5c281c30e7be02a700c8d0c7 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Thu, 4 Feb 2016 07:44:08 -0800 Subject: [PATCH 3/5] test: Remove superflouous return --- src/libtest/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libtest/lib.rs b/src/libtest/lib.rs index 5693cc10a0f72..a7cade125f785 100644 --- a/src/libtest/lib.rs +++ b/src/libtest/lib.rs @@ -733,7 +733,7 @@ pub fn run_tests_console(opts: &TestOpts, tests: Vec) -> io::Resu None => {} } try!(run_tests(opts, tests, |x| callback(&x, &mut st))); - return st.write_run_finish(); + st.write_run_finish() } #[test] From 869a8957f255ede3c55572be862f870874c08c44 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Thu, 4 Feb 2016 07:44:35 -0800 Subject: [PATCH 4/5] syntax: Replace glob import to reduce risk of name collisions --- src/libsyntax/errors/emitter.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/libsyntax/errors/emitter.rs b/src/libsyntax/errors/emitter.rs index 4272f281edb44..934f809ef77da 100644 --- a/src/libsyntax/errors/emitter.rs +++ b/src/libsyntax/errors/emitter.rs @@ -15,7 +15,6 @@ use diagnostics; use errors::{Level, RenderSpan, CodeSuggestion, DiagnosticBuilder}; use errors::RenderSpan::*; -use errors::Level::*; use std::{cmp, fmt}; use std::io::prelude::*; @@ -207,7 +206,7 @@ impl EmitterWriter { if let Some(code) = code { if let Some(_) = self.registry.as_ref() .and_then(|registry| registry.find_description(code)) { - try!(print_diagnostic(&mut self.dst, &ss[..], Help, + try!(print_diagnostic(&mut self.dst, &ss[..], Level::Help, &format!("run `rustc --explain {}` to see a \ detailed explanation", code), None)); } @@ -612,7 +611,7 @@ impl EmitterWriter { } let snippet = self.cm.span_to_string(span); - try!(print_diagnostic(&mut self.dst, &snippet, Note, &diag_string, None)); + try!(print_diagnostic(&mut self.dst, &snippet, Level::Note, &diag_string, None)); } last_span = span; } From 388cf4e40ab1f18340ac1fe8c6baa444809801a3 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Wed, 17 Feb 2016 20:16:56 -0800 Subject: [PATCH 5/5] syntax: Only use color output if the terminal supports it --- src/libsyntax/errors/emitter.rs | 10 ++++++++-- src/libterm/terminfo/mod.rs | 4 +++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/libsyntax/errors/emitter.rs b/src/libsyntax/errors/emitter.rs index 934f809ef77da..2ba6ee3a17b51 100644 --- a/src/libsyntax/errors/emitter.rs +++ b/src/libsyntax/errors/emitter.rs @@ -683,8 +683,14 @@ enum Destination { impl Destination { fn from_stderr() -> Destination { match term::stderr() { - Some(t) => Terminal(t), - None => Raw(Box::new(io::stderr())), + Some(t) => { + if t.supports_color() { + Terminal(t) + } else { + Raw(Box::new(io::stderr())) + } + } + None => Raw(Box::new(io::stderr())), } } diff --git a/src/libterm/terminfo/mod.rs b/src/libterm/terminfo/mod.rs index fdc7f4ac6ed85..ecde4848fd16a 100644 --- a/src/libterm/terminfo/mod.rs +++ b/src/libterm/terminfo/mod.rs @@ -230,7 +230,9 @@ impl Terminal for TerminfoTerminal { ("sgr", &[Param::Number(0)]), ("op", &[])] .iter() - .filter_map(|&(cap, params)| self.ti.strings.get(cap).map(|c| (c, params))) + .filter_map(|&(cap, params)| { + self.ti.strings.get(cap).map(|c| (c, params)) + }) .next() { Some((op, params)) => { match expand(op, params, &mut Variables::new()) {