Skip to content

libterm: synchronize with public "term" crate v0.4.0 #31205

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 5 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions src/libsyntax/errors/emitter.rs
Original file line number Diff line number Diff line change
@@ -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;
}
@@ -684,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())),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

}
}

207 changes: 193 additions & 14 deletions src/libterm/lib.rs
Original file line number Diff line number Diff line change
@@ -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<T> = std::result::Result<T, Error>;

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<Error> for io::Error {
fn from(err: Error) -> io::Error {
let kind = match &err {
&Error::Io(ref e) => e.kind(),
_ => io::ErrorKind::Other,
};
io::Error::new(kind, err)
}
}

impl std::convert::From<io::Error> for Error {
fn from(val: io::Error) -> Self {
Error::Io(val)
}
}

impl std::convert::From<terminfo::Error> for Error {
fn from(val: terminfo::Error) -> Self {
Error::TerminfoParsing(val)
}
}

impl std::convert::From<terminfo::parm::Error> for Error {
fn from(val: terminfo::parm::Error) -> Self {
Error::ParameterizedExpansion(val)
}
}

/// A terminal with similar capabilities to an ANSI Terminal
/// (foreground/background colors etc).
pub trait Terminal: Write {
@@ -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<bool>;
/// 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<bool>;
/// 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<bool>;
/// 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<bool>;
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;
198 changes: 131 additions & 67 deletions src/libterm/terminfo/mod.rs
Original file line number Diff line number Diff line change
@@ -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<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> {
pub fn from_env() -> Result<TermInfo> {
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<TermInfo, Error> {
pub fn from_name(name: &str) -> Result<TermInfo> {
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<P: AsRef<Path>>(path: P) -> Result<TermInfo, Error> {
pub fn from_path<P: AsRef<Path>>(path: P) -> Result<TermInfo> {
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)));
// (That is, this uses a &Path so that this function need not be instantiated
// for every type
// which implements AsRef<Path>. One day, if/when rustc is a bit smarter, it
// might do this for
// us. Alas. )
fn _from_path(path: &Path) -> Result<TermInfo> {
let file = 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<T> {

impl<T: Write + Send> Terminal for TerminfoTerminal<T> {
type Output = T;
fn fg(&mut self, color: color::Color) -> io::Result<bool> {
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<bool> {
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<bool> {
fn attr(&mut self, attr: Attr) -> Result<()> {
match attr {
Attr::ForegroundColor(c) => self.fg(c),
Attr::BackgroundColor(c) => self.bg(c),
@@ -187,22 +223,47 @@ impl<T: Write + Send> Terminal for TerminfoTerminal<T> {
}
}

fn reset(&mut self) -> io::Result<bool> {
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 +313,18 @@ impl<T: Write + Send> TerminfoTerminal<T> {
}
}

fn apply_cap(&mut self, cmd: &str, params: &[Param]) -> io::Result<bool> {
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),
}
}
}
158 changes: 99 additions & 59 deletions src/libterm/terminfo/parm.rs

Large diffs are not rendered by default.

252 changes: 57 additions & 195 deletions src/libterm/terminfo/parser/compiled.rs

Large diffs are not rendered by default.

164 changes: 164 additions & 0 deletions src/libterm/terminfo/parser/names.rs
Original file line number Diff line number Diff line change
@@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![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"
];
19 changes: 0 additions & 19 deletions src/libterm/terminfo/searcher.rs
Original file line number Diff line number Diff line change
@@ -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<PathBuf> {
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<PathBuf> {
}
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");
}
212 changes: 149 additions & 63 deletions src/libterm/win.rs
Original file line number Diff line number Diff line change
@@ -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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like this stuff casued the build failure

../src/libterm\win.rs:18:5: 18:37 error: unresolved import `std::sys::windows::c`. Could not find `windows` in `std::sys` [E0432]

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<T> {
@@ -30,29 +33,6 @@ pub struct WinConsole<T> {
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<T: Write + Send + 'static> WinConsole<T> {
fn apply(&mut self) {
// Just get a handle to the current console buffer whatever it is
fn conout() -> io::Result<winapi::HANDLE> {
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<T: Write + Send> WinConsole<T> {
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<WinConsole<T>> {
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<T: Write> Write for WinConsole<T> {
}
}

impl<T: Write + Send + 'static> Terminal for WinConsole<T> {
impl<T: Write + Send> Terminal for WinConsole<T> {
type Output = T;

fn fg(&mut self, color: color::Color) -> io::Result<bool> {
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<bool> {
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<bool> {
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<T: Write + Send + 'static> Terminal for WinConsole<T> {
}
}

fn reset(&mut self) -> io::Result<bool> {
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 {
2 changes: 1 addition & 1 deletion src/libtest/lib.rs
Original file line number Diff line number Diff line change
@@ -733,7 +733,7 @@ pub fn run_tests_console(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> io::Resu
None => {}
}
try!(run_tests(opts, tests, |x| callback(&x, &mut st)));
return st.write_run_finish();
st.write_run_finish()
}

#[test]