]> git.lizzy.rs Git - rust.git/commitdiff
libterm: bring across changes from term
authorBryce Van Dyk <bryce@vandyk.net.nz>
Mon, 23 Nov 2015 09:03:52 +0000 (22:03 +1300)
committerBryce Van Dyk <bryce@vandyk.net.nz>
Thu, 3 Dec 2015 06:27:59 +0000 (19:27 +1300)
This brings across changes made to the term library to libterm. This
includes removing instances or unwrap, fixing format string handling, and
removing a TODO.

This fix does not bring all changes across, as term now relies on cargo
deps that cannot be brought into the rust build at this stage, but has
attempted as best to cross port changes not relying on this. This notably
limits extra functionality since implemented int he Terminal trait in
Term.

This is in partly in response to rust issue #29992.

src/libsyntax/diagnostic.rs
src/libterm/lib.rs
src/libterm/terminfo/mod.rs
src/libterm/terminfo/parm.rs
src/libterm/terminfo/parser/compiled.rs
src/libterm/terminfo/searcher.rs
src/libterm/win.rs
src/libtest/lib.rs

index 870dea02212d1d3e2e5e771c914e368053dbdd11..b854a2f2a0a1730fe09667d3fcb9f8be1e4fd3d6 100644 (file)
@@ -20,7 +20,7 @@
 use std::{cmp, error, fmt};
 use std::io::prelude::*;
 use std::io;
-use term::{self, WriterWrapper};
+use term;
 
 /// maximum number of lines we will print for each error; arbitrary.
 const MAX_LINES: usize = 6;
@@ -318,7 +318,7 @@ pub struct EmitterWriter {
 }
 
 enum Destination {
-    Terminal(Box<term::Terminal<WriterWrapper> + Send>),
+    Terminal(Box<term::StderrTerminal>),
     Raw(Box<Write + Send>),
 }
 
@@ -365,7 +365,7 @@ pub fn new(dst: Box<Write + Send>,
 
     fn print_maybe_styled(&mut self,
                           args: fmt::Arguments,
-                          color: term::attr::Attr,
+                          color: term::Attr,
                           print_newline_at_end: bool) -> io::Result<()> {
         match self.dst {
             Terminal(ref mut t) => {
@@ -408,13 +408,13 @@ fn print_diagnostic(&mut self, topic: &str, lvl: Level,
             try!(write!(&mut self.dst, "{} ", topic));
         }
 
-        try!(print_maybe_styled!(self, term::attr::ForegroundColor(lvl.color()),
+        try!(print_maybe_styled!(self, term::Attr::ForegroundColor(lvl.color()),
                                  "{}: ", lvl.to_string()));
-        try!(print_maybe_styled!(self, term::attr::Bold, "{}", msg));
+        try!(print_maybe_styled!(self, term::Attr::Bold, "{}", msg));
 
         match code {
             Some(code) => {
-                let style = term::attr::ForegroundColor(term::color::BRIGHT_MAGENTA);
+                let style = term::Attr::ForegroundColor(term::color::BRIGHT_MAGENTA);
                 try!(print_maybe_styled!(self, style, " [{}]", code.clone()));
             }
             None => ()
@@ -646,7 +646,7 @@ fn highlight_lines(&mut self,
                     s.pop();
                 }
 
-                try!(println_maybe_styled!(self, term::attr::ForegroundColor(lvl.color()),
+                try!(println_maybe_styled!(self, term::Attr::ForegroundColor(lvl.color()),
                                            "{}", s));
             }
         }
@@ -719,7 +719,7 @@ fn end_highlight_lines(&mut self,
             }
         }
         s.push('^');
-        println_maybe_styled!(self, term::attr::ForegroundColor(lvl.color()),
+        println_maybe_styled!(self, term::Attr::ForegroundColor(lvl.color()),
                               "{}", s)
     }
 
index 478b0b846edda76029fc59e8442efcf4d51167ed..69ad55d79089931d4302dd92fd7fa5623ad7873d 100644 (file)
@@ -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.
 //
@@ -11,9 +11,9 @@
 //! 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
+//! Terminal][ansi] to provide color printing, among other things. There are two
+//! implementations, the `TerminfoTerminal`, which uses control characters from
+//! [terminfo][ti] database, and `WinConsole`, which uses the [Win32 Console
 //! API][win].
 //!
 //! # Examples
 //! ```no_run
 //! # #![feature(rustc_private)]
 //! 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();
+//!     write!(t, "hello, ").unwrap();
 //!
 //!     t.fg(term::color::RED).unwrap();
-//!     (writeln!(t, "world!")).unwrap();
+//!     writeln!(t, "world!").unwrap();
 //!
-//!     t.reset().unwrap();
+//!     assert!(t.reset().unwrap());
 //! }
 //! ```
 //!
 #![deny(missing_docs)]
 
 #![feature(box_syntax)]
-#![feature(rustc_private)]
 #![feature(staged_api)]
-#![feature(str_char)]
-#![feature(vec_push_all)]
 #![cfg_attr(windows, feature(libc))]
 // Handle rustfmt skips
 #![feature(custom_attribute)]
 #![allow(unused_attributes)]
 
-#[macro_use]
-extern crate log;
+use std::io::prelude::*;
 
 pub use terminfo::TerminfoTerminal;
 #[cfg(windows)]
 pub use win::WinConsole;
 
-use std::io::prelude::*;
-use std::io;
+use std::io::{self, Stdout, Stderr};
 
 pub mod terminfo;
 
 #[cfg(windows)]
 mod win;
 
-/// A hack to work around the fact that `Box<Write + Send>` does not
-/// currently implement `Write`.
-pub struct WriterWrapper {
-    wrapped: Box<Write + Send>,
-}
-
-impl Write for WriterWrapper {
-    #[inline]
-    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
-        self.wrapped.write(buf)
-    }
-
-    #[inline]
-    fn flush(&mut self) -> io::Result<()> {
-        self.wrapped.flush()
-    }
-}
+/// Alias for stdout terminals.
+pub type StdoutTerminal = Terminal<Output=Stdout> + Send;
+/// Alias for stderr terminals.
+pub type StderrTerminal = 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<Terminal<WriterWrapper> + Send>> {
-    TerminfoTerminal::new(WriterWrapper { wrapped: box std::io::stdout() })
+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<Terminal<WriterWrapper> + Send>> {
-    let ti = TerminfoTerminal::new(WriterWrapper { wrapped: box std::io::stdout() });
-
-    match ti {
-        Some(t) => Some(t),
-        None => WinConsole::new(WriterWrapper { wrapped: box std::io::stdout() }),
-    }
+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<Terminal<WriterWrapper> + Send>> {
-    TerminfoTerminal::new(WriterWrapper { wrapped: box std::io::stderr() })
+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<Terminal<WriterWrapper> + Send>> {
-    let ti = TerminfoTerminal::new(WriterWrapper { wrapped: box std::io::stderr() });
-
-    match ti {
-        Some(t) => Some(t),
-        None => WinConsole::new(WriterWrapper { wrapped: box std::io::stderr() }),
-    }
+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>))
 }
 
 
@@ -164,43 +139,41 @@ pub mod color {
     pub const BRIGHT_WHITE: Color = 15;
 }
 
-/// Terminal attributes
-pub mod attr {
-    pub use self::Attr::*;
-
-    /// 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(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(super::color::Color),
-        /// Convenience attribute to set the background color
-        BackgroundColor(super::color::Color),
-    }
+/// 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, 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),
 }
 
 /// A terminal with similar capabilities to an ANSI Terminal
 /// (foreground/background colors etc).
-pub trait Terminal<T: Write>: Write {
+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,
@@ -222,24 +195,29 @@ pub trait Terminal<T: Write>: Write {
     /// 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::Attr) -> io::Result<bool>;
+    fn attr(&mut self, attr: Attr) -> io::Result<bool>;
 
     /// Returns whether the given terminal attribute is supported.
-    fn supports_attr(&self, attr: attr::Attr) -> bool;
+    fn supports_attr(&self, attr: Attr) -> bool;
 
-    /// Resets all terminal attributes and color to the default.
-    /// Returns `Ok()`.
-    fn reset(&mut self) -> io::Result<()>;
+    /// 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.
+    ///
+    /// *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<bool>;
 
     /// Gets an immutable reference to the stream inside
-    fn get_ref<'a>(&'a self) -> &'a T;
+    fn get_ref<'a>(&'a self) -> &'a Self::Output;
 
     /// Gets a mutable reference to the stream inside
-    fn get_mut<'a>(&'a mut self) -> &'a mut T;
-}
+    fn get_mut<'a>(&'a mut self) -> &'a mut Self::Output;
 
-/// A terminal which can be unwrapped.
-pub trait UnwrappableTerminal<T: Write>: Terminal<T> {
     /// Returns the contained stream, destroying the `Terminal`
-    fn unwrap(self) -> T;
+    fn into_inner(self) -> Self::Output where Self: Sized;
 }
index 89c22e555ca95f879e4db375d24213c6b5bfa72b..a4e5d00ee7a77646e68082cde6c28a5e1e8e7413 100644 (file)
 
 use std::collections::HashMap;
 use std::env;
+use std::error;
+use std::fmt;
+use std::fs::File;
 use std::io::prelude::*;
 use std::io;
+use std::io::BufReader;
+use std::path::Path;
 
-use attr;
+use Attr;
 use color;
 use Terminal;
-use UnwrappableTerminal;
-use self::searcher::open;
+use self::searcher::get_dbpath_for_term;
 use self::parser::compiled::{parse, msys_terminfo};
-use self::parm::{expand, Number, Variables};
+use self::parm::{expand, Variables, Param};
 
 
 /// A parsed terminfo database entry.
@@ -37,6 +41,80 @@ pub struct TermInfo {
     pub strings: HashMap<String, Vec<u8>>,
 }
 
+/// 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<TermInfo, Error> {
+        let term = match env::var("TERM") {
+            Ok(name) => TermInfo::from_name(&name),
+            Err(..) => return Err(Error::TermUnset),
+        };
+
+        if term.is_err() && env::var("MSYSCON").ok().map_or(false, |s| "mintty.exe" == s) {
+            // msys terminal
+            Ok(msys_terminfo())
+        } else {
+            term
+        }
+    }
+
+    /// Create a TermInfo for the named terminal.
+    pub fn from_name(name: &str) -> Result<TermInfo, Error> {
+        get_dbpath_for_term(name)
+            .ok_or_else(|| {
+                Error::IoError(io::Error::new(io::ErrorKind::NotFound, "terminfo file not found"))
+            })
+            .and_then(|p| TermInfo::from_path(&(*p)))
+    }
+
+    /// Parse the given TermInfo.
+    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<TermInfo, Error> {
+        Self::_from_path(path.as_ref())
+    }
+    // Keep the metadata small
+    fn _from_path(path: &Path) -> Result<TermInfo, Error> {
+        let file = try!(File::open(path).map_err(|e| Error::IoError(e)));
+        let mut reader = BufReader::new(file);
+        parse(&mut reader, false).map_err(|e| Error::MalformedTerminfo(e))
+    }
+}
+
 pub mod searcher;
 
 /// TermInfo format parsing.
@@ -47,21 +125,21 @@ pub mod parser {
 pub mod parm;
 
 
-fn cap_for_attr(attr: attr::Attr) -> &'static str {
+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",
+        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",
     }
 }
 
@@ -70,23 +148,15 @@ fn cap_for_attr(attr: attr::Attr) -> &'static str {
 pub struct TerminfoTerminal<T> {
     num_colors: u16,
     out: T,
-    ti: Box<TermInfo>,
+    ti: TermInfo,
 }
 
-impl<T: Write+Send+'static> Terminal<T> for TerminfoTerminal<T> {
+impl<T: Write+Send> Terminal for TerminfoTerminal<T> {
+    type Output = T;
     fn fg(&mut self, color: color::Color) -> io::Result<bool> {
         let color = self.dim_if_necessary(color);
         if self.num_colors > color {
-            let s = expand(self.ti
-                               .strings
-                               .get("setaf")
-                               .unwrap(),
-                           &[Number(color as isize)],
-                           &mut Variables::new());
-            if s.is_ok() {
-                try!(self.out.write_all(&s.unwrap()));
-                return Ok(true);
-            }
+            return self.apply_cap("setaf", &[Param::Number(color as i32)]);
         }
         Ok(false)
     }
@@ -94,42 +164,22 @@ fn fg(&mut self, color: color::Color) -> io::Result<bool> {
     fn bg(&mut self, color: color::Color) -> io::Result<bool> {
         let color = self.dim_if_necessary(color);
         if self.num_colors > color {
-            let s = expand(self.ti
-                               .strings
-                               .get("setab")
-                               .unwrap(),
-                           &[Number(color as isize)],
-                           &mut Variables::new());
-            if s.is_ok() {
-                try!(self.out.write_all(&s.unwrap()));
-                return Ok(true);
-            }
+            return self.apply_cap("setab", &[Param::Number(color as i32)]);
         }
         Ok(false)
     }
 
-    fn attr(&mut self, attr: attr::Attr) -> io::Result<bool> {
+    fn attr(&mut self, attr: Attr) -> io::Result<bool> {
         match attr {
-            attr::ForegroundColor(c) => self.fg(c),
-            attr::BackgroundColor(c) => self.bg(c),
-            _ => {
-                let cap = cap_for_attr(attr);
-                let parm = self.ti.strings.get(cap);
-                if parm.is_some() {
-                    let s = expand(parm.unwrap(), &[], &mut Variables::new());
-                    if s.is_ok() {
-                        try!(self.out.write_all(&s.unwrap()));
-                        return Ok(true);
-                    }
-                }
-                Ok(false)
-            }
+            Attr::ForegroundColor(c) => self.fg(c),
+            Attr::BackgroundColor(c) => self.bg(c),
+            _ => self.apply_cap(cap_for_attr(attr), &[]),
         }
     }
 
-    fn supports_attr(&self, attr: attr::Attr) -> bool {
+    fn supports_attr(&self, attr: Attr) -> bool {
         match attr {
-            attr::ForegroundColor(_) | attr::BackgroundColor(_) => self.num_colors > 0,
+            Attr::ForegroundColor(_) | Attr::BackgroundColor(_) => self.num_colors > 0,
             _ => {
                 let cap = cap_for_attr(attr);
                 self.ti.strings.get(cap).is_some()
@@ -137,22 +187,22 @@ fn supports_attr(&self, attr: attr::Attr) -> bool {
         }
     }
 
-    fn reset(&mut self) -> io::Result<()> {
-        let mut cap = self.ti.strings.get("sgr0");
-        if cap.is_none() {
-            // are there any terminals that have color/attrs and not sgr0?
-            // Try falling back to sgr, then op
-            cap = self.ti.strings.get("sgr");
-            if cap.is_none() {
-                cap = self.ti.strings.get("op");
+    fn reset(&mut self) -> io::Result<bool> {
+        // are there any terminals that have color/attrs and not sgr0?
+        // Try falling back to sgr, then op
+        let cmd = match ["sg0", "sgr", "op"]
+                            .iter()
+                            .filter_map(|cap| self.ti.strings.get(*cap))
+                            .next() {
+            Some(op) => {
+                match expand(&op, &[], &mut Variables::new()) {
+                    Ok(cmd) => cmd,
+                    Err(e) => return Err(io::Error::new(io::ErrorKind::InvalidData, e)),
+                }
             }
-        }
-        let s = cap.map_or(Err("can't find terminfo capability `sgr0`".to_owned()),
-                           |op| expand(op, &[], &mut Variables::new()));
-        if s.is_ok() {
-            return self.out.write_all(&s.unwrap());
-        }
-        Ok(())
+            None => return Ok(false),
+        };
+        self.out.write_all(&cmd).and(Ok(true))
     }
 
     fn get_ref<'a>(&'a self) -> &'a T {
@@ -162,64 +212,36 @@ fn get_ref<'a>(&'a self) -> &'a T {
     fn get_mut<'a>(&'a mut self) -> &'a mut T {
         &mut self.out
     }
-}
 
-impl<T: Write+Send+'static> UnwrappableTerminal<T> for TerminfoTerminal<T> {
-    fn unwrap(self) -> T {
+    fn into_inner(self) -> T
+        where Self: Sized
+    {
         self.out
     }
 }
 
-impl<T: Write+Send+'static> TerminfoTerminal<T> {
-    /// Returns `None` whenever the terminal cannot be created for some
-    /// reason.
-    pub fn new(out: T) -> Option<Box<Terminal<T> + Send + 'static>> {
-        let term = match env::var("TERM") {
-            Ok(t) => t,
-            Err(..) => {
-                debug!("TERM environment variable not defined");
-                return None;
-            }
-        };
-
-        let mut file = match open(&term[..]) {
-            Ok(f) => f,
-            Err(err) => {
-                return match env::var("MSYSCON") {
-                    Ok(ref val) if &val[..] == "mintty.exe" => {
-                        // msys terminal
-                        Some(box TerminfoTerminal {
-                            out: out,
-                            ti: msys_terminfo(),
-                            num_colors: 8,
-                        })
-                    }
-                    _ => {
-                        debug!("error finding terminfo entry: {:?}", err);
-                        None
-                    }
-                };
-            }
-        };
-
-        let ti = parse(&mut file, false);
-        if ti.is_err() {
-            debug!("error parsing terminfo entry: {:?}", ti.err().unwrap());
-            return None;
-        }
-
-        let inf = ti.unwrap();
-        let nc = if inf.strings.get("setaf").is_some() && inf.strings.get("setab").is_some() {
-            inf.numbers.get("colors").map_or(0, |&n| n)
+impl<T: Write+Send> 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
         };
 
-        Some(box TerminfoTerminal {
+        TerminfoTerminal {
             out: out,
-            ti: inf,
+            ti: terminfo,
             num_colors: nc,
-        })
+        }
+    }
+
+    /// 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 {
@@ -229,6 +251,18 @@ fn dim_if_necessary(&self, color: color::Color) -> color::Color {
             color
         }
     }
+
+    fn apply_cap(&mut self, cmd: &str, params: &[Param]) -> io::Result<bool> {
+        match self.ti.strings.get(cmd) {
+            Some(cmd) => {
+                match expand(&cmd, params, &mut Variables::new()) {
+                    Ok(s) => self.out.write_all(&s).and(Ok(true)),
+                    Err(e) => Err(io::Error::new(io::ErrorKind::InvalidData, e)),
+                }
+            }
+            None => Ok(false),
+        }
+    }
 }
 
 
index 49db096ce7332c88ac782c38b7b3acb0a4f0b922..aceaa0c10bcce847e22fbbd7a25224561c6a0098 100644 (file)
 
 //! Parameterized string expansion
 
-pub use self::Param::*;
+use self::Param::*;
 use self::States::*;
 use self::FormatState::*;
 use self::FormatOp::*;
-use std::ascii::AsciiExt;
-use std::mem::replace;
+
 use std::iter::repeat;
 
-#[derive(Copy, Clone, PartialEq)]
+#[derive(Clone, Copy, PartialEq)]
 enum States {
     Nothing,
     Percent,
@@ -27,15 +26,15 @@ enum States {
     PushParam,
     CharConstant,
     CharClose,
-    IntConstant(isize),
+    IntConstant(i32),
     FormatPattern(Flags, FormatState),
-    SeekIfElse(isize),
-    SeekIfElsePercent(isize),
-    SeekIfEnd(isize),
-    SeekIfEndPercent(isize),
+    SeekIfElse(usize),
+    SeekIfElsePercent(usize),
+    SeekIfEnd(usize),
+    SeekIfEndPercent(usize),
 }
 
-#[derive(Copy, Clone, PartialEq)]
+#[derive(Copy, PartialEq, Clone)]
 enum FormatState {
     FormatStateFlags,
     FormatStateWidth,
@@ -47,7 +46,7 @@ enum FormatState {
 #[derive(Clone)]
 pub enum Param {
     Words(String),
-    Number(isize),
+    Number(i32),
 }
 
 /// Container for static and dynamic variable arrays
@@ -94,11 +93,11 @@ pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables) -> Result<Vec<
     // 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) {
+    for (dst, src) in mparams.iter_mut().zip(params.iter()) {
         *dst = (*src).clone();
     }
 
-    for &c in cap {
+    for &c in cap.iter() {
         let cur = c as char;
         let mut old_state = state;
         match state {
@@ -116,20 +115,13 @@ pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables) -> Result<Vec<
                         state = Nothing
                     }
                     'c' => {
-                        if !stack.is_empty() {
-                            match stack.pop().unwrap() {
-                                // if c is 0, use 0200 (128) for ncurses compatibility
-                                Number(c) => {
-                                    output.push(if c == 0 {
-                                        128
-                                    } else {
-                                        c as u8
-                                    })
-                                }
-                                _ => return Err("a non-char was used with %c".to_owned()),
-                            }
-                        } else {
-                            return Err("stack is empty".to_owned());
+                        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("a non-char was used with %c".to_string()),
+                            None => return Err("stack is empty".to_string()),
                         }
                     }
                     'p' => state = PushParam,
@@ -138,208 +130,89 @@ pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables) -> Result<Vec<
                     '\'' => state = CharConstant,
                     '{' => state = IntConstant(0),
                     'l' => {
-                        if !stack.is_empty() {
-                            match stack.pop().unwrap() {
-                                Words(s) => stack.push(Number(s.len() as isize)),
-                                _ => return Err("a non-str was used with %l".to_owned()),
-                            }
-                        } else {
-                            return Err("stack is empty".to_owned());
-                        }
-                    }
-                    '+' => {
-                        if stack.len() > 1 {
-                            match (stack.pop().unwrap(), stack.pop().unwrap()) {
-                                (Number(y), Number(x)) => stack.push(Number(x + y)),
-                                _ => return Err("non-numbers on stack with +".to_owned()),
-                            }
-                        } else {
-                            return Err("stack is empty".to_owned());
-                        }
-                    }
-                    '-' => {
-                        if stack.len() > 1 {
-                            match (stack.pop().unwrap(), stack.pop().unwrap()) {
-                                (Number(y), Number(x)) => stack.push(Number(x - y)),
-                                _ => return Err("non-numbers on stack with -".to_owned()),
-                            }
-                        } else {
-                            return Err("stack is empty".to_owned());
-                        }
-                    }
-                    '*' => {
-                        if stack.len() > 1 {
-                            match (stack.pop().unwrap(), stack.pop().unwrap()) {
-                                (Number(y), Number(x)) => stack.push(Number(x * y)),
-                                _ => return Err("non-numbers on stack with *".to_owned()),
-                            }
-                        } else {
-                            return Err("stack is empty".to_owned());
-                        }
-                    }
-                    '/' => {
-                        if stack.len() > 1 {
-                            match (stack.pop().unwrap(), stack.pop().unwrap()) {
-                                (Number(y), Number(x)) => stack.push(Number(x / y)),
-                                _ => return Err("non-numbers on stack with /".to_owned()),
-                            }
-                        } else {
-                            return Err("stack is empty".to_owned());
-                        }
-                    }
-                    'm' => {
-                        if stack.len() > 1 {
-                            match (stack.pop().unwrap(), stack.pop().unwrap()) {
-                                (Number(y), Number(x)) => stack.push(Number(x % y)),
-                                _ => return Err("non-numbers on stack with %".to_owned()),
-                            }
-                        } else {
-                            return Err("stack is empty".to_owned());
-                        }
-                    }
-                    '&' => {
-                        if stack.len() > 1 {
-                            match (stack.pop().unwrap(), stack.pop().unwrap()) {
-                                (Number(y), Number(x)) => stack.push(Number(x & y)),
-                                _ => return Err("non-numbers on stack with &".to_owned()),
-                            }
-                        } else {
-                            return Err("stack is empty".to_owned());
-                        }
-                    }
-                    '|' => {
-                        if stack.len() > 1 {
-                            match (stack.pop().unwrap(), stack.pop().unwrap()) {
-                                (Number(y), Number(x)) => stack.push(Number(x | y)),
-                                _ => return Err("non-numbers on stack with |".to_owned()),
-                            }
-                        } else {
-                            return Err("stack is empty".to_owned());
-                        }
-                    }
-                    '^' => {
-                        if stack.len() > 1 {
-                            match (stack.pop().unwrap(), stack.pop().unwrap()) {
-                                (Number(y), Number(x)) => stack.push(Number(x ^ y)),
-                                _ => return Err("non-numbers on stack with ^".to_owned()),
-                            }
-                        } else {
-                            return Err("stack is empty".to_owned());
+                        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()),
                         }
                     }
-                    '=' => {
-                        if stack.len() > 1 {
-                            match (stack.pop().unwrap(), stack.pop().unwrap()) {
-                                (Number(y), Number(x)) => {
-                                    stack.push(Number(if x == y {
-                                        1
-                                    } else {
-                                        0
-                                    }))
-                                }
-                                _ => return Err("non-numbers on stack with =".to_owned()),
+                    '+' | '-' | '/' | '*' | '^' | '&' | '|' | '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!("All cases handled"),
+                                }))
                             }
-                        } else {
-                            return Err("stack is empty".to_owned());
-                        }
-                    }
-                    '>' => {
-                        if stack.len() > 1 {
-                            match (stack.pop().unwrap(), stack.pop().unwrap()) {
-                                (Number(y), Number(x)) => {
-                                    stack.push(Number(if x > y {
-                                        1
-                                    } else {
-                                        0
-                                    }))
-                                }
-                                _ => return Err("non-numbers on stack with >".to_owned()),
+                            (Some(_), Some(_)) => {
+                                return Err(format!("non-numbers on stack with {}", cur))
                             }
-                        } else {
-                            return Err("stack is empty".to_owned());
+                            _ => return Err("stack is empty".to_string()),
                         }
                     }
-                    '<' => {
-                        if stack.len() > 1 {
-                            match (stack.pop().unwrap(), stack.pop().unwrap()) {
-                                (Number(y), Number(x)) => {
-                                    stack.push(Number(if x < y {
-                                        1
-                                    } else {
-                                        0
-                                    }))
-                                }
-                                _ => return Err("non-numbers on stack with <".to_owned()),
+                    '=' | '>' | '<' | '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!(),
+                                } {
+                                    1
+                                } else {
+                                    0
+                                }))
                             }
-                        } else {
-                            return Err("stack is empty".to_owned());
-                        }
-                    }
-                    'A' => {
-                        if stack.len() > 1 {
-                            match (stack.pop().unwrap(), stack.pop().unwrap()) {
-                                (Number(0), Number(_)) => stack.push(Number(0)),
-                                (Number(_), Number(0)) => stack.push(Number(0)),
-                                (Number(_), Number(_)) => stack.push(Number(1)),
-                                _ => return Err("non-numbers on stack with logical and".to_owned()),
+                            (Some(_), Some(_)) => {
+                                return Err(format!("non-numbers on stack with {}", cur))
                             }
-                        } else {
-                            return Err("stack is empty".to_owned());
+                            _ => return Err("stack is empty".to_string()),
                         }
                     }
-                    'O' => {
-                        if stack.len() > 1 {
-                            match (stack.pop().unwrap(), stack.pop().unwrap()) {
-                                (Number(0), Number(0)) => stack.push(Number(0)),
-                                (Number(_), Number(_)) => stack.push(Number(1)),
-                                _ => return Err("non-numbers on stack with logical or".to_owned()),
+                    '!' | '~' => {
+                        match stack.pop() {
+                            Some(Number(x)) => {
+                                stack.push(Number(match cur {
+                                    '!' if x > 0 => 0,
+                                    '!' => 1,
+                                    '~' => !x,
+                                    _ => unreachable!(),
+                                }))
                             }
-                        } else {
-                            return Err("stack is empty".to_owned());
-                        }
-                    }
-                    '!' => {
-                        if !stack.is_empty() {
-                            match stack.pop().unwrap() {
-                                Number(0) => stack.push(Number(1)),
-                                Number(_) => stack.push(Number(0)),
-                                _ => return Err("non-number on stack with logical not".to_owned()),
-                            }
-                        } else {
-                            return Err("stack is empty".to_owned());
-                        }
-                    }
-                    '~' => {
-                        if !stack.is_empty() {
-                            match stack.pop().unwrap() {
-                                Number(x) => stack.push(Number(!x)),
-                                _ => return Err("non-number on stack with %~".to_owned()),
-                            }
-                        } else {
-                            return Err("stack is empty".to_owned());
+                            Some(_) => return Err(format!("non-numbers on stack with {}", cur)),
+                            None => return Err("stack is empty".to_string()),
                         }
                     }
                     'i' => {
-                        match (mparams[0].clone(), mparams[1].clone()) {
-                            (Number(x), Number(y)) => {
+                        match (&mparams[0], &mparams[1]) {
+                            (&Number(x), &Number(y)) => {
                                 mparams[0] = Number(x + 1);
                                 mparams[1] = Number(y + 1);
                             }
-                            (_, _) => return Err("first two params not numbers with %i".to_owned()),
+                            (_, _) => {
+                                return Err("first two params not numbers with %i".to_string())
+                            }
                         }
                     }
 
                     // printf-style support for %doxXs
                     'd' | 'o' | 'x' | 'X' | 's' => {
-                        if !stack.is_empty() {
+                        if let Some(arg) = stack.pop() {
                             let flags = Flags::new();
-                            let res = format(stack.pop().unwrap(), FormatOp::from_char(cur), flags);
-                            if res.is_err() {
-                                return res;
-                            }
-                            output.push_all(&res.unwrap())
+                            let res = try!(format(arg, FormatOp::from_char(cur), flags));
+                            output.extend(res.iter().map(|x| *x));
                         } else {
-                            return Err("stack is empty".to_owned());
+                            return Err("stack is empty".to_string());
                         }
                     }
                     ':' | '#' | ' ' | '.' | '0'...'9' => {
@@ -362,47 +235,45 @@ pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables) -> Result<Vec<
                     // conditionals
                     '?' => (),
                     't' => {
-                        if !stack.is_empty() {
-                            match stack.pop().unwrap() {
-                                Number(0) => state = SeekIfElse(0),
-                                Number(_) => (),
-                                _ => return Err("non-number on stack with conditional".to_owned()),
+                        match stack.pop() {
+                            Some(Number(0)) => state = SeekIfElse(0),
+                            Some(Number(_)) => (),
+                            Some(_) => {
+                                return Err("non-number on stack with conditional".to_string())
                             }
-                        } else {
-                            return Err("stack is empty".to_owned());
+                            None => return Err("stack is empty".to_string()),
                         }
                     }
                     'e' => state = SeekIfEnd(0),
                     ';' => (),
-
-                    _ => return Err(format!("unrecognized format option {:?}", cur)),
+                    _ => return Err(format!("unrecognized format option {}", cur)),
                 }
             }
             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_owned()),
+                               None => return Err("bad param number".to_string()),
                            }]
                            .clone());
             }
             SetVar => {
                 if cur >= 'A' && cur <= 'Z' {
-                    if !stack.is_empty() {
+                    if let Some(arg) = stack.pop() {
                         let idx = (cur as u8) - b'A';
-                        vars.sta[idx as usize] = stack.pop().unwrap();
+                        vars.sta[idx as usize] = arg;
                     } else {
-                        return Err("stack is empty".to_owned());
+                        return Err("stack is empty".to_string());
                     }
                 } else if cur >= 'a' && cur <= 'z' {
-                    if !stack.is_empty() {
+                    if let Some(arg) = stack.pop() {
                         let idx = (cur as u8) - b'a';
-                        vars.dyn[idx as usize] = stack.pop().unwrap();
+                        vars.dyn[idx as usize] = arg;
                     } else {
-                        return Err("stack is empty".to_owned());
+                        return Err("stack is empty".to_string());
                     }
                 } else {
-                    return Err("bad variable name in %P".to_owned());
+                    return Err("bad variable name in %P".to_string());
                 }
             }
             GetVar => {
@@ -413,47 +284,45 @@ pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables) -> Result<Vec<
                     let idx = (cur as u8) - b'a';
                     stack.push(vars.dyn[idx as usize].clone());
                 } else {
-                    return Err("bad variable name in %g".to_owned());
+                    return Err("bad variable name in %g".to_string());
                 }
             }
             CharConstant => {
-                stack.push(Number(c as isize));
+                stack.push(Number(c as i32));
                 state = CharClose;
             }
             CharClose => {
                 if cur != '\'' {
-                    return Err("malformed character constant".to_owned());
+                    return Err("malformed character constant".to_string());
                 }
             }
             IntConstant(i) => {
-                match cur {
-                    '}' => {
-                        stack.push(Number(i));
-                        state = Nothing;
-                    }
-                    '0'...'9' => {
-                        state = IntConstant(i * 10 + (cur as isize - '0' as isize));
-                        old_state = Nothing;
+                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("int constant too large".to_string()),
                     }
-                    _ => return Err("bad isize constant".to_owned()),
+                } else {
+                    return Err("bad int constant".to_string());
                 }
             }
             FormatPattern(ref mut flags, ref mut fstate) => {
                 old_state = Nothing;
                 match (*fstate, cur) {
                     (_, 'd') | (_, 'o') | (_, 'x') | (_, 'X') | (_, 's') => {
-                        if !stack.is_empty() {
-                            let res = format(stack.pop().unwrap(),
-                                             FormatOp::from_char(cur),
-                                             *flags);
-                            if res.is_err() {
-                                return res;
-                            }
-                            output.push_all(&res.unwrap());
+                        if let Some(arg) = stack.pop() {
+                            let res = try!(format(arg, FormatOp::from_char(cur), *flags));
+                            output.extend(res.iter().map(|x| *x));
                             // will cause state to go to Nothing
                             old_state = FormatPattern(*flags, *fstate);
                         } else {
-                            return Err("stack is empty".to_owned());
+                            return Err("stack is empty".to_string());
                         }
                     }
                     (FormatStateFlags, '#') => {
@@ -479,7 +348,7 @@ pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables) -> Result<Vec<
                         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_owned());
+                            return Err("format width overflow".to_string());
                         }
                     }
                     (FormatStateWidth, '.') => {
@@ -489,10 +358,10 @@ pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables) -> Result<Vec<
                         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_owned());
+                            return Err("format precision overflow".to_string());
                         }
                     }
-                    _ => return Err("invalid format specifier".to_owned()),
+                    _ => return Err("invalid format specifier".to_string()),
                 }
             }
             SeekIfElse(level) => {
@@ -543,7 +412,7 @@ pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables) -> Result<Vec<
     Ok(output)
 }
 
-#[derive(Copy, Clone, PartialEq)]
+#[derive(Copy, PartialEq, Clone)]
 struct Flags {
     width: usize,
     precision: usize,
@@ -600,61 +469,55 @@ fn to_char(self) -> char {
 fn format(val: Param, op: FormatOp, flags: Flags) -> Result<Vec<u8>, String> {
     let mut s = match val {
         Number(d) => {
-            let s = match (op, flags.sign) {
-                (FormatDigit, true) => format!("{:+}", d).into_bytes(),
-                (FormatDigit, false) => format!("{}", d).into_bytes(),
-                (FormatOctal, _) => format!("{:o}", d).into_bytes(),
-                (FormatHex, _) => format!("{:x}", d).into_bytes(),
-                (FormatHEX, _) => format!("{:X}", d).into_bytes(),
-                (FormatString, _) => return Err("non-number on stack with %s".to_owned()),
-            };
-            let mut s: Vec<u8> = s.into_iter().collect();
-            if flags.precision > s.len() {
-                let mut s_ = Vec::with_capacity(flags.precision);
-                let n = flags.precision - s.len();
-                s_.extend(repeat(b'0').take(n));
-                s_.extend(s);
-                s = s_;
-            }
-            assert!(!s.is_empty(), "string conversion produced empty result");
             match op {
                 FormatDigit => {
-                    if flags.space && !(s[0] == b'-' || s[0] == b'+') {
-                        s.insert(0, b' ');
+                    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)
                     }
                 }
                 FormatOctal => {
-                    if flags.alternate && s[0] != b'0' {
-                        s.insert(0, b'0');
+                    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)
                     }
                 }
                 FormatHex => {
-                    if flags.alternate {
-                        let s_ = replace(&mut s, vec![b'0', b'x']);
-                        s.extend(s_);
+                    if flags.alternate && d != 0 {
+                        format!("0x{:01$x}", d, flags.precision)
+                    } else {
+                        format!("{:01$x}", d, flags.precision)
                     }
                 }
                 FormatHEX => {
-                    s = s.to_ascii_uppercase();
-                    if flags.alternate {
-                        let s_ = replace(&mut s, vec![b'0', b'X']);
-                        s.extend(s_);
+                    if flags.alternate && d != 0 {
+                        format!("0X{:01$X}", d, flags.precision)
+                    } else {
+                        format!("{:01$X}", d, flags.precision)
                     }
                 }
-                FormatString => unreachable!(),
+                FormatString => return Err("non-number on stack with %s".to_string()),
             }
-            s
+            .into_bytes()
         }
         Words(s) => {
             match op {
                 FormatString => {
-                    let mut s = s.as_bytes().to_vec();
+                    let mut s = s.into_bytes();
                     if flags.precision > 0 && flags.precision < s.len() {
                         s.truncate(flags.precision);
                     }
                     s
                 }
-                _ => return Err(format!("non-string on stack with %{:?}", op.to_char())),
+                _ => return Err(format!("non-string on stack with %{}", op.to_char())),
             }
         }
     };
@@ -665,7 +528,7 @@ fn format(val: Param, op: FormatOp, flags: Flags) -> Result<Vec<u8>, String> {
         } else {
             let mut s_ = Vec::with_capacity(flags.width);
             s_.extend(repeat(b' ').take(n));
-            s_.extend(s);
+            s_.extend(s.into_iter());
             s = s_;
         }
     }
@@ -673,8 +536,9 @@ fn format(val: Param, op: FormatOp, flags: Flags) -> Result<Vec<u8>, String> {
 }
 
 #[cfg(test)]
-mod tests {
-    use super::{expand, Param, Words, Variables, Number};
+mod test {
+    use super::{expand, Variables};
+    use super::Param::{self, Words, Number};
     use std::result::Result::Ok;
 
     #[test]
@@ -711,12 +575,12 @@ fn get_res(fmt: &str,
                    vars: &mut Variables)
                    -> Result<Vec<u8>, String> {
             let mut u8v: Vec<_> = fmt.bytes().collect();
-            u8v.extend(cap.bytes());
+            u8v.extend(cap.as_bytes().iter().map(|&b| b));
             expand(&u8v, params, vars)
         }
 
         let caps = ["%d", "%c", "%s", "%Pa", "%l", "%!", "%~"];
-        for &cap in &caps {
+        for &cap in caps.iter() {
             let res = get_res("", cap, &[], vars);
             assert!(res.is_err(),
                     "Op {} succeeded incorrectly with 0 stack entries",
@@ -733,7 +597,7 @@ fn get_res(fmt: &str,
                     res.err().unwrap());
         }
         let caps = ["%+", "%-", "%*", "%/", "%m", "%&", "%|", "%A", "%O"];
-        for &cap in &caps {
+        for &cap in caps.iter() {
             let res = expand(cap.as_bytes(), &[], vars);
             assert!(res.is_err(),
                     "Binop {} succeeded incorrectly with 0 stack entries",
@@ -744,7 +608,7 @@ fn get_res(fmt: &str,
                     cap);
             let res = get_res("%{1}%{2}", cap, &[], vars);
             assert!(res.is_ok(),
-                    "Binop {} failed with 2 stack entries: {:?}",
+                    "Binop {} failed with 2 stack entries: {}",
                     cap,
                     res.err().unwrap());
         }
@@ -757,20 +621,20 @@ fn test_push_bad_param() {
 
     #[test]
     fn test_comparison_ops() {
-        let v = [('<', [1, 0, 0]), ('=', [0, 1, 0]), ('>', [0, 0, 1])];
-        for &(op, bs) in &v {
+        let v = [('<', [1u8, 0u8, 0u8]), ('=', [0u8, 1u8, 0u8]), ('>', [0u8, 0u8, 1u8])];
+        for &(op, bs) in v.iter() {
             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(), [b'0' + bs[0]]);
+            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(), [b'0' + bs[1]]);
+            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(), [b'0' + bs[2]]);
+            assert_eq!(res.unwrap(), vec![b'0' + bs[2]]);
         }
     }
 
@@ -800,7 +664,7 @@ fn test_format() {
                             Words("foo".to_string())],
                           vars),
                    Ok("foofoo ffo".bytes().collect::<Vec<_>>()));
-        assert_eq!(expand(b"%p1%:-4.2s", &[Words("foo".to_owned())], vars),
+        assert_eq!(expand(b"%p1%:-4.2s", &[Words("foo".to_string())], vars),
                    Ok("fo  ".bytes().collect::<Vec<_>>()));
 
         assert_eq!(expand(b"%p1%d%p1%.3d%p1%5d%p1%:+d", &[Number(1)], vars),
index 7c8d9983e78f534883d3042570c2c4810bd23bc1..558d35c2ae27b9844e9b2105eb4cdcc6cb6d9802 100644 (file)
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![allow(non_upper_case_globals)]
+#![allow(non_upper_case_globals, missing_docs)]
 
 //! ncurses-compatible compiled terminfo format parsing (term(5))
 
@@ -20,7 +20,6 @@
 // These are the orders ncurses uses in its compiled format (as of 5.9). Not sure if portable.
 
 #[rustfmt_skip]
-#[allow(missing_docs)]
 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",
     "return_does_clr_eol"];
 
 #[rustfmt_skip]
-#[allow(missing_docs)]
 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]
-#[allow(missing_docs)]
 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",
     "new_line_delay", "backspace_delay", "horizontal_tab_delay", "number_of_function_keys"];
 
 #[rustfmt_skip]
-#[allow(missing_docs)]
 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]
-#[allow(missing_docs)]
 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",
     "acs_plus", "memory_lock", "memory_unlock", "box_chars_1"];
 
 #[rustfmt_skip]
-#[allow(missing_docs)]
 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",
     "OTG3", "OTG1", "OTG4", "OTGR", "OTGL", "OTGU", "OTGD", "OTGH", "OTGV", "OTGC", "meml", "memu",
     "box1"];
 
-/// Parse a compiled terminfo entry, using long capability names if `longnames` is true
-pub fn parse(file: &mut Read, longnames: bool) -> Result<Box<TermInfo>, String> {
-    macro_rules! try { ($e:expr) => (
+fn read_le_u16(r: &mut io::Read) -> io::Result<u16> {
+    let mut b = [0; 2];
+    let mut amt = 0;
+    while amt < b.len() {
+        match try!(r.read(&mut b[amt..])) {
+            0 => return Err(io::Error::new(io::ErrorKind::Other, "end of file")),
+            n => amt += n,
+        }
+    }
+    Ok((b[0] as u16) | ((b[1] as u16) << 8))
+}
+
+fn read_byte(r: &mut 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 io::Read, longnames: bool) -> Result<TermInfo, String> {
+    macro_rules! try( ($e:expr) => (
         match $e {
             Ok(e) => e,
-            Err(e) => return Err(format!("{:?}", e))
+            Err(e) => return Err(format!("{}", e))
         }
-    ) }
+    ) );
 
-    let bnames;
-    let snames;
-    let nnames;
-
-    if longnames {
-        bnames = boolfnames;
-        snames = stringfnames;
-        nnames = numfnames;
+    let (bnames, snames, nnames) = if longnames {
+        (boolfnames, stringfnames, numfnames)
     } else {
-        bnames = boolnames;
-        snames = stringnames;
-        nnames = numnames;
-    }
+        (boolnames, stringnames, numnames)
+    };
 
     // Check magic number
     let magic = try!(read_le_u16(file));
     if magic != 0x011A {
         return Err(format!("invalid magic number: expected {:x}, found {:x}",
-                           0x011A_usize,
-                           magic as usize));
+                           0x011A,
+                           magic));
     }
 
-    let names_bytes = try!(read_le_u16(file)) as isize;
-    let bools_bytes = try!(read_le_u16(file)) as isize;
-    let numbers_count = try!(read_le_u16(file)) as isize;
-    let string_offsets_count = try!(read_le_u16(file)) as isize;
-    let string_table_bytes = try!(read_le_u16(file)) as isize;
+    // 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()),
+            }
+        }}
+    }
+
+    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!();
 
-    assert!(names_bytes > 0);
+    if names_bytes == 0 {
+        return Err("incompatible file: names field must be at least 1 byte wide".to_string());
+    }
 
-    if (bools_bytes as usize) > boolnames.len() {
-        return Err("incompatible file: more booleans than expected".to_owned());
+    if bools_bytes > boolnames.len() {
+        return Err("incompatible file: more booleans than expected".to_string());
     }
 
-    if (numbers_count as usize) > numnames.len() {
-        return Err("incompatible file: more numbers than expected".to_owned());
+    if numbers_count > numnames.len() {
+        return Err("incompatible file: more numbers than expected".to_string());
     }
 
-    if (string_offsets_count as usize) > stringnames.len() {
-        return Err("incompatible file: more string offsets than expected".to_owned());
+    if string_offsets_count > stringnames.len() {
+        return Err("incompatible file: more string offsets than expected".to_string());
     }
 
     // don't read NUL
-    let bytes = try!(read_exact(file, names_bytes as usize - 1));
+    let mut bytes = Vec::new();
+    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_owned()),
+        Err(_) => return Err("input not utf-8".to_string()),
     };
 
     let term_names: Vec<String> = names_str.split('|')
-                                           .map(str::to_owned)
+                                           .map(|s| s.to_string())
                                            .collect();
-
-    try!(read_byte(file)); // consume NUL
-
-    let mut bools_map = HashMap::new();
-    if bools_bytes != 0 {
-        for i in 0..bools_bytes {
-            let b = try!(read_byte(file));
-            if b == 1 {
-                bools_map.insert(bnames[i as usize].to_owned(), true);
-            }
-        }
+    // consume NUL
+    if try!(read_byte(file)) != b'\0' {
+        return Err("incompatible file: missing null terminator for names section".to_string());
     }
 
+    let bools_map: HashMap<String, bool> = try! {
+        (0..bools_bytes).filter_map(|i| match read_byte(file) {
+            Err(e) => Some(Err(e)),
+            Ok(1) => Some(Ok((bnames[i].to_string(), true))),
+            Ok(_) => None
+        }).collect()
+    };
+
     if (bools_bytes + names_bytes) % 2 == 1 {
         try!(read_byte(file)); // compensate for padding
     }
 
-    let mut numbers_map = HashMap::new();
-    if numbers_count != 0 {
-        for i in 0..numbers_count {
-            let n = try!(read_le_u16(file));
-            if n != 0xFFFF {
-                numbers_map.insert(nnames[i as usize].to_owned(), n);
-            }
-        }
-    }
-
-    let mut string_map = HashMap::new();
-
-    if string_offsets_count != 0 {
-        let mut string_offsets = Vec::with_capacity(10);
-        for _ in 0..string_offsets_count {
-            string_offsets.push(try!(read_le_u16(file)));
-        }
+    let numbers_map: HashMap<String, u16> = try! {
+        (0..numbers_count).filter_map(|i| match read_le_u16(file) {
+            Ok(0xFFFF) => None,
+            Ok(n) => Some(Ok((nnames[i].to_string(), n))),
+            Err(e) => Some(Err(e))
+        }).collect()
+    };
 
-        let string_table = try!(read_exact(file, string_table_bytes as usize));
+    let string_map: HashMap<String, Vec<u8>> = if string_offsets_count > 0 {
+        let string_offsets: Vec<u16> = try!((0..string_offsets_count)
+                                                .map(|_| read_le_u16(file))
+                                                .collect());
 
-        if string_table.len() != string_table_bytes as usize {
-            return Err("error: hit EOF before end of string table".to_owned());
-        }
+        let mut string_table = Vec::new();
+        try!(file.take(string_table_bytes as u64).read_to_end(&mut string_table));
 
-        for (i, v) in string_offsets.iter().enumerate() {
-            let offset = *v;
-            if offset == 0xFFFF {
-                // non-entry
-                continue;
-            }
+        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]
@@ -288,30 +300,22 @@ macro_rules! try { ($e:expr) => (
             if offset == 0xFFFE {
                 // undocumented: FFFE indicates cap@, which means the capability is not present
                 // unsure if the handling for this is correct
-                string_map.insert(name.to_owned(), Vec::new());
-                continue;
+                return Ok((name.to_string(), Vec::new()));
             }
 
-
             // Find the offset of the NUL we want to go to
-            let nulpos = string_table[offset as usize..string_table_bytes as usize]
-                             .iter()
-                             .position(|&b| b == 0);
+            let nulpos = string_table[offset..string_table_bytes].iter().position(|&b| b == 0);
             match nulpos {
-                Some(len) => {
-                    string_map.insert(name.to_string(),
-                                      string_table[offset as usize..(offset as usize + len)]
-                                          .to_vec())
-                }
-                None => {
-                    return Err("invalid file: missing NUL in string_table".to_owned());
-                }
-            };
-        }
-    }
+                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())
+    } else {
+        HashMap::new()
+    };
 
     // And that's all there is to it
-    Ok(box TermInfo {
+    Ok(TermInfo {
         names: term_names,
         bools: bools_map,
         numbers: numbers_map,
@@ -319,42 +323,27 @@ macro_rules! try { ($e:expr) => (
     })
 }
 
-fn read_le_u16<R: Read + ?Sized>(r: &mut R) -> io::Result<u16> {
-    let mut b = [0; 2];
-    assert_eq!(try!(r.read(&mut b)), 2);
-    Ok((b[0] as u16) | ((b[1] as u16) << 8))
-}
-
-fn read_byte<R: Read + ?Sized>(r: &mut R) -> io::Result<u8> {
-    let mut b = [0; 1];
-    assert_eq!(try!(r.read(&mut b)), 1);
-    Ok(b[0])
-}
-
-fn read_exact<R: Read + ?Sized>(r: &mut R, sz: usize) -> io::Result<Vec<u8>> {
-    let mut v = Vec::with_capacity(sz);
-    try!(r.take(sz as u64).read_to_end(&mut v));
-    assert_eq!(v.len(), sz);
-    Ok(v)
-}
-
 /// Create a dummy TermInfo struct for msys terminals
-pub fn msys_terminfo() -> Box<TermInfo> {
+pub fn msys_terminfo() -> TermInfo {
     let mut strings = HashMap::new();
-    strings.insert("sgr0".to_owned(), b"\x1B[0m".to_vec());
-    strings.insert("bold".to_owned(), b"\x1B[1m".to_vec());
-    strings.insert("setaf".to_owned(), b"\x1B[3%p1%dm".to_vec());
-    strings.insert("setab".to_owned(), b"\x1B[4%p1%dm".to_vec());
-    box TermInfo {
-        names: vec!["cygwin".to_owned()], // msys is a fork of an older cygwin version
+    strings.insert("sgr0".to_string(), b"\x1B[0m".to_vec());
+    strings.insert("bold".to_string(), b"\x1B[1m".to_vec());
+    strings.insert("setaf".to_string(), b"\x1B[3%p1%dm".to_vec());
+    strings.insert("setab".to_string(), b"\x1B[4%p1%dm".to_vec());
+
+    let mut numbers = HashMap::new();
+    numbers.insert("colors".to_string(), 8u16);
+
+    TermInfo {
+        names: vec!["cygwin".to_string()], // msys is a fork of an older cygwin version
         bools: HashMap::new(),
-        numbers: HashMap::new(),
+        numbers: numbers,
         strings: strings,
     }
 }
 
 #[cfg(test)]
-mod tests {
+mod test {
 
     use super::{boolnames, boolfnames, numnames, numfnames, stringnames, stringfnames};
 
index 397e7aa22546cc6b744d8fc66389549dccbdaffb..e869c5083373ca4fa577e860cd40c8232d6de176 100644 (file)
 //! Does not support hashed database, only filesystem!
 
 use std::env;
-use std::fs::File;
-use std::io::prelude::*;
+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<Box<PathBuf>> {
-    if term.is_empty() {
-        return None;
-    }
-
-    let homedir = env::home_dir();
-
+pub fn get_dbpath_for_term(term: &str) -> Option<PathBuf> {
     let mut dirs_to_search = Vec::new();
-    let first_char = term.char_at(0);
+    let first_char = match term.chars().next() {
+        Some(c) => c,
+        None => return None,
+    };
 
     // Find search directory
     match env::var_os("TERMINFO") {
         Some(dir) => dirs_to_search.push(PathBuf::from(dir)),
         None => {
-            if homedir.is_some() {
+            if let Some(mut homedir) = env::home_dir() {
                 // ncurses compatibility;
-                dirs_to_search.push(homedir.unwrap().join(".terminfo"))
+                homedir.push(".terminfo");
+                dirs_to_search.push(homedir)
             }
             match env::var("TERMINFO_DIRS") {
                 Ok(dirs) => {
@@ -61,35 +58,26 @@ pub fn get_dbpath_for_term(term: &str) -> Option<Box<PathBuf>> {
     };
 
     // Look for the terminal in all of the search directories
-    for p in &dirs_to_search {
-        if p.exists() {
-            let f = first_char.to_string();
-            let newp = p.join(&f).join(term);
-            if newp.exists() {
-                return Some(box newp);
+    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);
             }
-            // on some installations the dir is named after the hex of the char (e.g. OS X)
-            let f = format!("{:x}", first_char as usize);
-            let newp = p.join(&f).join(term);
-            if newp.exists() {
-                return Some(box newp);
-            }
-        }
-    }
-    None
-}
+            p.pop();
+            p.pop();
 
-/// Return open file for `term`
-pub fn open(term: &str) -> Result<File, String> {
-    match get_dbpath_for_term(term) {
-        Some(x) => {
-            match File::open(&*x) {
-                Ok(file) => Ok(file),
-                Err(e) => Err(format!("error opening file: {:?}", e)),
+            // 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 => Err(format!("could not find terminfo entry for {:?}", term)),
     }
+    None
 }
 
 #[test]
@@ -109,11 +97,3 @@ fn x(t: &str) -> String {
     assert!(x("screen") == "/usr/share/terminfo/s/screen");
     env::remove_var("TERMINFO_DIRS");
 }
-
-#[test]
-#[ignore(reason = "see test_get_dbpath_for_term")]
-fn test_open() {
-    open("screen").unwrap();
-    let t = open("nonexistent terminal that hopefully does not exist");
-    assert!(t.is_err());
-}
index fa53d783194c83f8fc5504f574c0ae6437ce4cba..2cb7018669d3daacdfd212f2efc70d32cc3e004f 100644 (file)
@@ -17,9 +17,9 @@
 use std::io;
 use std::io::prelude::*;
 
-use attr;
+use Attr;
 use color;
-use {Terminal, UnwrappableTerminal};
+use Terminal;
 
 /// A Terminal implementation which uses the Win32 Console API.
 pub struct WinConsole<T> {
@@ -115,7 +115,7 @@ fn apply(&mut self) {
 
     /// Returns `None` whenever the terminal cannot be created for some
     /// reason.
-    pub fn new(out: T) -> Option<Box<Terminal<T> + Send + 'static>> {
+    pub fn new(out: T) -> io::Result<WinConsole<T>> {
         let fg;
         let bg;
         unsafe {
@@ -128,7 +128,7 @@ pub fn new(out: T) -> Option<Box<Terminal<T> + Send + 'static>> {
                 bg = color::BLACK;
             }
         }
-        Some(box WinConsole {
+        Ok(WinConsole {
             buf: out,
             def_foreground: fg,
             def_background: bg,
@@ -148,7 +148,9 @@ fn flush(&mut self) -> io::Result<()> {
     }
 }
 
-impl<T: Write+Send+'static> Terminal<T> for WinConsole<T> {
+impl<T: Write+Send+'static> Terminal for WinConsole<T> {
+    type Output = T;
+
     fn fg(&mut self, color: color::Color) -> io::Result<bool> {
         self.foreground = color;
         self.apply();
@@ -163,14 +165,14 @@ fn bg(&mut self, color: color::Color) -> io::Result<bool> {
         Ok(true)
     }
 
-    fn attr(&mut self, attr: attr::Attr) -> io::Result<bool> {
+    fn attr(&mut self, attr: Attr) -> io::Result<bool> {
         match attr {
-            attr::ForegroundColor(f) => {
+            Attr::ForegroundColor(f) => {
                 self.foreground = f;
                 self.apply();
                 Ok(true)
             }
-            attr::BackgroundColor(b) => {
+            Attr::BackgroundColor(b) => {
                 self.background = b;
                 self.apply();
                 Ok(true)
@@ -179,21 +181,21 @@ fn attr(&mut self, attr: attr::Attr) -> io::Result<bool> {
         }
     }
 
-    fn supports_attr(&self, attr: attr::Attr) -> bool {
+    fn supports_attr(&self, attr: Attr) -> bool {
         // it claims support for underscore and reverse video, but I can't get
         // it to do anything -cmr
         match attr {
-            attr::ForegroundColor(_) | attr::BackgroundColor(_) => true,
+            Attr::ForegroundColor(_) | Attr::BackgroundColor(_) => true,
             _ => false,
         }
     }
 
-    fn reset(&mut self) -> io::Result<()> {
+    fn reset(&mut self) -> io::Result<bool> {
         self.foreground = self.def_foreground;
         self.background = self.def_background;
         self.apply();
 
-        Ok(())
+        Ok(true)
     }
 
     fn get_ref<'a>(&'a self) -> &'a T {
@@ -203,10 +205,10 @@ fn get_ref<'a>(&'a self) -> &'a T {
     fn get_mut<'a>(&'a mut self) -> &'a mut T {
         &mut self.buf
     }
-}
 
-impl<T: Write+Send+'static> UnwrappableTerminal<T> for WinConsole<T> {
-    fn unwrap(self) -> T {
+    fn into_inner(self) -> T
+        where Self: Sized
+    {
         self.buf
     }
 }
index 125677dc9321cd805a7fc3901f3745ff50e593f7..71eddd80c74c84c381a15cf45e1b6cc7508dc1fd 100644 (file)
@@ -428,7 +428,7 @@ pub enum TestResult {
 unsafe impl Send for TestResult {}
 
 enum OutputLocation<T> {
-    Pretty(Box<term::Terminal<term::WriterWrapper> + Send>),
+    Pretty(Box<term::StdoutTerminal>),
     Raw(T),
 }