# automatically generated for all stage/host/target combinations.
################################################################################
-TARGET_CRATES := std extra green rustuv native flate arena glob
+TARGET_CRATES := std extra green rustuv native flate arena glob term
HOST_CRATES := syntax rustc rustdoc
CRATES := $(TARGET_CRATES) $(HOST_CRATES)
TOOLS := compiletest rustdoc rustc
DEPS_std := native:rustrt
-DEPS_extra := std
+DEPS_extra := std term
DEPS_green := std
DEPS_rustuv := std native:uv native:uv_support
DEPS_native := std
-DEPS_syntax := std extra
+DEPS_syntax := std extra term
DEPS_rustc := syntax native:rustllvm flate arena
DEPS_rustdoc := rustc native:sundown
DEPS_flate := std native:miniz
DEPS_arena := std extra
DEPS_glob := std
+DEPS_term := std
TOOL_DEPS_compiletest := extra green rustuv
TOOL_DEPS_rustdoc := rustdoc green rustuv
* [The `arena` allocation library](arena/index.html)
* [The `flate` compression library](flate/index.html)
* [The `glob` file path matching library](glob/index.html)
+* [The `term` terminal-handling library](term/index.html)
# Tooling
-// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
+// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
pub mod getopts;
pub mod json;
pub mod tempfile;
-pub mod term;
pub mod time;
pub mod base64;
pub mod workcache;
#[cfg(unicode)]
mod unicode;
-pub mod terminfo;
-
// Compiler support modules
pub mod test;
+++ /dev/null
-// Copyright 2013 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.
-
-//! Simple ANSI color library
-
-#[allow(missing_doc)];
-
-
-use std::os;
-use terminfo::*;
-use terminfo::searcher::open;
-use terminfo::parser::compiled::{parse, msys_terminfo};
-use terminfo::parm::{expand, Number, Variables};
-
-// FIXME (#2807): Windows support.
-
-pub mod color {
- pub type Color = u16;
-
- pub static BLACK: Color = 0u16;
- pub static RED: Color = 1u16;
- pub static GREEN: Color = 2u16;
- pub static YELLOW: Color = 3u16;
- pub static BLUE: Color = 4u16;
- pub static MAGENTA: Color = 5u16;
- pub static CYAN: Color = 6u16;
- pub static WHITE: Color = 7u16;
-
- pub static BRIGHT_BLACK: Color = 8u16;
- pub static BRIGHT_RED: Color = 9u16;
- pub static BRIGHT_GREEN: Color = 10u16;
- pub static BRIGHT_YELLOW: Color = 11u16;
- pub static BRIGHT_BLUE: Color = 12u16;
- pub static BRIGHT_MAGENTA: Color = 13u16;
- pub static BRIGHT_CYAN: Color = 14u16;
- pub static BRIGHT_WHITE: Color = 15u16;
-}
-
-pub mod 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.
- 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)
- }
-}
-
-fn cap_for_attr(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"
- }
-}
-
-pub struct Terminal<T> {
- priv num_colors: u16,
- priv out: T,
- priv ti: ~TermInfo
-}
-
-impl<T: Writer> Terminal<T> {
- pub fn new(out: T) -> Result<Terminal<T>, ~str> {
- let term = match os::getenv("TERM") {
- Some(t) => t,
- None => return Err(~"TERM environment variable undefined")
- };
-
- let entry = open(term);
- if entry.is_err() {
- if "cygwin" == term { // msys terminal
- return Ok(Terminal {out: out, ti: msys_terminfo(), num_colors: 8});
- }
- return Err(entry.unwrap_err());
- }
-
- let mut file = entry.unwrap();
- let ti = parse(&mut file, false);
- if ti.is_err() {
- return Err(ti.unwrap_err());
- }
-
- let inf = ti.unwrap();
- let nc = if inf.strings.find_equiv(&("setaf")).is_some()
- && inf.strings.find_equiv(&("setab")).is_some() {
- inf.numbers.find_equiv(&("colors")).map_or(0, |&n| n)
- } else { 0 };
-
- return Ok(Terminal {out: out, ti: inf, num_colors: nc});
- }
- /// Sets the foreground color to the given color.
- ///
- /// If the color is a bright color, but the terminal only supports 8 colors,
- /// the corresponding normal color will be used instead.
- ///
- /// Returns true if the color was set, false otherwise.
- pub fn fg(&mut self, color: color::Color) -> bool {
- let color = self.dim_if_necessary(color);
- if self.num_colors > color {
- let s = expand(*self.ti.strings.find_equiv(&("setaf")).unwrap(),
- [Number(color as int)], &mut Variables::new());
- if s.is_ok() {
- self.out.write(s.unwrap());
- return true
- } else {
- warn!("{}", s.unwrap_err());
- }
- }
- false
- }
- /// 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 true if the color was set, false otherwise.
- pub fn bg(&mut self, color: color::Color) -> bool {
- let color = self.dim_if_necessary(color);
- if self.num_colors > color {
- let s = expand(*self.ti.strings.find_equiv(&("setab")).unwrap(),
- [Number(color as int)], &mut Variables::new());
- if s.is_ok() {
- self.out.write(s.unwrap());
- return true
- } else {
- warn!("{}", s.unwrap_err());
- }
- }
- false
- }
-
- /// Sets the given terminal attribute, if supported.
- /// Returns true if the attribute was supported, false otherwise.
- pub fn attr(&mut self, attr: attr::Attr) -> 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.find_equiv(&cap);
- if parm.is_some() {
- let s = expand(*parm.unwrap(), [], &mut Variables::new());
- if s.is_ok() {
- self.out.write(s.unwrap());
- return true
- } else {
- warn!("{}", s.unwrap_err());
- }
- }
- false
- }
- }
- }
-
- /// Returns whether the given terminal attribute is supported.
- pub fn supports_attr(&self, attr: attr::Attr) -> bool {
- match attr {
- attr::ForegroundColor(_) | attr::BackgroundColor(_) => {
- self.num_colors > 0
- }
- _ => {
- let cap = cap_for_attr(attr);
- self.ti.strings.find_equiv(&cap).is_some()
- }
- }
- }
-
- /// Resets all terminal attributes and color to the default.
- pub fn reset(&mut self) {
- let mut cap = self.ti.strings.find_equiv(&("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.find_equiv(&("sgr"));
- if cap.is_none() {
- cap = self.ti.strings.find_equiv(&("op"));
- }
- }
- let s = cap.map_or(Err(~"can't find terminfo capability `sgr0`"), |op| {
- expand(*op, [], &mut Variables::new())
- });
- if s.is_ok() {
- self.out.write(s.unwrap());
- } else if self.num_colors > 0 {
- warn!("{}", s.unwrap_err());
- } else {
- // if we support attributes but not color, it would be nice to still warn!()
- // but it's not worth testing all known attributes just for this.
- debug!("{}", s.unwrap_err());
- }
- }
-
- fn dim_if_necessary(&self, color: color::Color) -> color::Color {
- if color >= self.num_colors && color >= 8 && color < 16 {
- color-8
- } else { color }
- }
-
- pub fn unwrap(self) -> T { self.out }
-
- pub fn get_ref<'a>(&'a self) -> &'a T { &self.out }
-
- pub fn get_mut<'a>(&'a mut self) -> &'a mut T { &mut self.out }
-}
-
-impl<T: Writer> Writer for Terminal<T> {
- fn write(&mut self, buf: &[u8]) {
- self.out.write(buf);
- }
-
- fn flush(&mut self) {
- self.out.flush();
- }
-}
+++ /dev/null
-// Copyright 2012 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(missing_doc)];
-
-use std::hashmap::HashMap;
-
-/// A parsed terminfo entry.
-pub struct TermInfo {
- /// Names for the terminal
- priv names: ~[~str],
- /// Map of capability name to boolean value
- priv bools: HashMap<~str, bool>,
- /// Map of capability name to numeric value
- numbers: HashMap<~str, u16>,
- /// Map of capability name to raw (unexpanded) string
- strings: HashMap<~str, ~[u8]>
-}
-
-pub mod searcher;
-pub mod parser {
- pub mod compiled;
-}
-pub mod parm;
+++ /dev/null
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-//! Parameterized string expansion
-
-use std::{char, vec, util};
-use std::num::strconv::{SignNone,SignNeg,SignAll,int_to_str_bytes_common};
-
-#[deriving(Eq)]
-enum States {
- Nothing,
- Percent,
- SetVar,
- GetVar,
- PushParam,
- CharConstant,
- CharClose,
- IntConstant(int),
- FormatPattern(Flags, FormatState),
- SeekIfElse(int),
- SeekIfElsePercent(int),
- SeekIfEnd(int),
- SeekIfEndPercent(int)
-}
-
-#[deriving(Eq)]
-enum FormatState {
- FormatStateFlags,
- FormatStateWidth,
- FormatStatePrecision
-}
-
-/// Types of parameters a capability can use
-#[deriving(Clone)]
-#[allow(missing_doc)]
-pub enum Param {
- String(~str),
- Number(int)
-}
-
-/// Container for static and dynamic variable arrays
-pub struct Variables {
- /// Static variables A-Z
- priv sta: [Param, ..26],
- /// Dynamic variables a-z
- priv dyn: [Param, ..26]
-}
-
-impl Variables {
- /// Return a new zero-initialized Variables
- pub fn new() -> Variables {
- Variables {
- sta: [
- Number(0), Number(0), Number(0), Number(0), Number(0),
- Number(0), Number(0), Number(0), Number(0), Number(0),
- Number(0), Number(0), Number(0), Number(0), Number(0),
- Number(0), Number(0), Number(0), Number(0), Number(0),
- Number(0), Number(0), Number(0), Number(0), Number(0),
- Number(0),
- ],
- dyn: [
- Number(0), Number(0), Number(0), Number(0), Number(0),
- Number(0), Number(0), Number(0), Number(0), Number(0),
- Number(0), Number(0), Number(0), Number(0), Number(0),
- Number(0), Number(0), Number(0), Number(0), Number(0),
- Number(0), Number(0), Number(0), Number(0), Number(0),
- Number(0),
- ],
- }
- }
-}
-
-/**
- Expand a parameterized capability
-
- # Arguments
- * `cap` - string to expand
- * `params` - vector of params for %p1 etc
- * `vars` - Variables struct for %Pa etc
-
- To be compatible with ncurses, `vars` should be the same between calls to `expand` for
- multiple capabilities for the same terminal.
- */
-pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables)
- -> Result<~[u8], ~str> {
- let mut state = Nothing;
-
- // expanded cap will only rarely be larger than the cap itself
- let mut output = vec::with_capacity(cap.len());
-
- let mut stack: ~[Param] = ~[];
-
- // 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.mut_iter().zip(params.iter()) {
- *dst = (*src).clone();
- }
-
- for c in cap.iter().map(|&x| x) {
- let cur = c as char;
- let mut old_state = state;
- match state {
- Nothing => {
- if cur == '%' {
- state = Percent;
- } else {
- output.push(c);
- }
- },
- Percent => {
- match cur {
- '%' => { output.push(c); state = Nothing },
- 'c' => if stack.len() > 0 {
- 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")
- }
- } else { return Err(~"stack is empty") },
- 'p' => state = PushParam,
- 'P' => state = SetVar,
- 'g' => state = GetVar,
- '\'' => state = CharConstant,
- '{' => state = IntConstant(0),
- 'l' => if stack.len() > 0 {
- match stack.pop().unwrap() {
- String(s) => stack.push(Number(s.len() as int)),
- _ => return Err(~"a non-str was used with %l")
- }
- } else { return Err(~"stack is empty") },
- '+' => 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 +")
- }
- } else { return Err(~"stack is empty") },
- '-' => 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 -")
- }
- } else { return Err(~"stack is empty") },
- '*' => 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 *")
- }
- } else { return Err(~"stack is empty") },
- '/' => 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 /")
- }
- } else { return Err(~"stack is empty") },
- '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 %")
- }
- } else { return Err(~"stack is empty") },
- '&' => 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 &")
- }
- } else { return Err(~"stack is empty") },
- '|' => 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 |")
- }
- } else { return Err(~"stack is empty") },
- '^' => 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 ^")
- }
- } else { return Err(~"stack is empty") },
- '=' => 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 =")
- }
- } else { return Err(~"stack is empty") },
- '>' => 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 >")
- }
- } else { return Err(~"stack is empty") },
- '<' => 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 <")
- }
- } else { return Err(~"stack is empty") },
- '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")
- }
- } else { return Err(~"stack is empty") },
- '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")
- }
- } else { return Err(~"stack is empty") },
- '!' => if stack.len() > 0 {
- match stack.pop().unwrap() {
- Number(0) => stack.push(Number(1)),
- Number(_) => stack.push(Number(0)),
- _ => return Err(~"non-number on stack with logical not")
- }
- } else { return Err(~"stack is empty") },
- '~' => if stack.len() > 0 {
- match stack.pop().unwrap() {
- Number(x) => stack.push(Number(!x)),
- _ => return Err(~"non-number on stack with %~")
- }
- } else { return Err(~"stack is empty") },
- 'i' => match (mparams[0].clone(), mparams[1].clone()) {
- (Number(x), Number(y)) => {
- mparams[0] = Number(x+1);
- mparams[1] = Number(y+1);
- },
- (_, _) => return Err(~"first two params not numbers with %i")
- },
-
- // printf-style support for %doxXs
- 'd'|'o'|'x'|'X'|'s' => if stack.len() > 0 {
- 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())
- } else { return Err(~"stack is empty") },
- ':'|'#'|' '|'.'|'0'..'9' => {
- let mut flags = Flags::new();
- let mut fstate = FormatStateFlags;
- match cur {
- ':' => (),
- '#' => flags.alternate = true,
- ' ' => flags.space = true,
- '.' => fstate = FormatStatePrecision,
- '0'..'9' => {
- flags.width = (cur as uint - '0' as uint);
- fstate = FormatStateWidth;
- }
- _ => unreachable!()
- }
- state = FormatPattern(flags, fstate);
- }
-
- // conditionals
- '?' => (),
- 't' => if stack.len() > 0 {
- match stack.pop().unwrap() {
- Number(0) => state = SeekIfElse(0),
- Number(_) => (),
- _ => return Err(~"non-number on stack with conditional")
- }
- } else { return Err(~"stack is empty") },
- 'e' => state = SeekIfEnd(0),
- ';' => (),
-
- _ => return Err(format!("unrecognized format option {}", cur))
- }
- },
- PushParam => {
- // params are 1-indexed
- stack.push(mparams[match char::to_digit(cur, 10) {
- Some(d) => d - 1,
- None => return Err(~"bad param number")
- }].clone());
- },
- SetVar => {
- if cur >= 'A' && cur <= 'Z' {
- if stack.len() > 0 {
- let idx = (cur as u8) - ('A' as u8);
- vars.sta[idx] = stack.pop().unwrap();
- } else { return Err(~"stack is empty") }
- } else if cur >= 'a' && cur <= 'z' {
- if stack.len() > 0 {
- let idx = (cur as u8) - ('a' as u8);
- vars.dyn[idx] = stack.pop().unwrap();
- } else { return Err(~"stack is empty") }
- } else {
- return Err(~"bad variable name in %P");
- }
- },
- GetVar => {
- if cur >= 'A' && cur <= 'Z' {
- let idx = (cur as u8) - ('A' as u8);
- stack.push(vars.sta[idx].clone());
- } else if cur >= 'a' && cur <= 'z' {
- let idx = (cur as u8) - ('a' as u8);
- stack.push(vars.dyn[idx].clone());
- } else {
- return Err(~"bad variable name in %g");
- }
- },
- CharConstant => {
- stack.push(Number(c as int));
- state = CharClose;
- },
- CharClose => {
- if cur != '\'' {
- return Err(~"malformed character constant");
- }
- },
- IntConstant(i) => {
- match cur {
- '}' => {
- stack.push(Number(i));
- state = Nothing;
- }
- '0'..'9' => {
- state = IntConstant(i*10 + (cur as int - '0' as int));
- old_state = Nothing;
- }
- _ => return Err(~"bad int constant")
- }
- }
- FormatPattern(ref mut flags, ref mut fstate) => {
- old_state = Nothing;
- match (*fstate, cur) {
- (_,'d')|(_,'o')|(_,'x')|(_,'X')|(_,'s') => if stack.len() > 0 {
- let res = format(stack.pop().unwrap(), FormatOp::from_char(cur), *flags);
- if res.is_err() { return res }
- output.push_all(res.unwrap());
- old_state = state; // will cause state to go to Nothing
- } else { return Err(~"stack is empty") },
- (FormatStateFlags,'#') => {
- flags.alternate = true;
- }
- (FormatStateFlags,'-') => {
- flags.left = true;
- }
- (FormatStateFlags,'+') => {
- flags.sign = true;
- }
- (FormatStateFlags,' ') => {
- flags.space = true;
- }
- (FormatStateFlags,'0'..'9') => {
- flags.width = (cur as uint - '0' as uint);
- *fstate = FormatStateWidth;
- }
- (FormatStateFlags,'.') => {
- *fstate = FormatStatePrecision;
- }
- (FormatStateWidth,'0'..'9') => {
- let old = flags.width;
- flags.width = flags.width * 10 + (cur as uint - '0' as uint);
- if flags.width < old { return Err(~"format width overflow") }
- }
- (FormatStateWidth,'.') => {
- *fstate = FormatStatePrecision;
- }
- (FormatStatePrecision,'0'..'9') => {
- let old = flags.precision;
- flags.precision = flags.precision * 10 + (cur as uint - '0' as uint);
- if flags.precision < old { return Err(~"format precision overflow") }
- }
- _ => return Err(~"invalid format specifier")
- }
- }
- SeekIfElse(level) => {
- if cur == '%' {
- state = SeekIfElsePercent(level);
- }
- old_state = Nothing;
- }
- SeekIfElsePercent(level) => {
- if cur == ';' {
- if level == 0 {
- state = Nothing;
- } else {
- state = SeekIfElse(level-1);
- }
- } else if cur == 'e' && level == 0 {
- state = Nothing;
- } else if cur == '?' {
- state = SeekIfElse(level+1);
- } else {
- state = SeekIfElse(level);
- }
- }
- SeekIfEnd(level) => {
- if cur == '%' {
- state = SeekIfEndPercent(level);
- }
- old_state = Nothing;
- }
- SeekIfEndPercent(level) => {
- if cur == ';' {
- if level == 0 {
- state = Nothing;
- } else {
- state = SeekIfEnd(level-1);
- }
- } else if cur == '?' {
- state = SeekIfEnd(level+1);
- } else {
- state = SeekIfEnd(level);
- }
- }
- }
- if state == old_state {
- state = Nothing;
- }
- }
- Ok(output)
-}
-
-#[deriving(Eq)]
-struct Flags {
- width: uint,
- precision: uint,
- alternate: bool,
- left: bool,
- sign: bool,
- space: bool
-}
-
-impl Flags {
- fn new() -> Flags {
- Flags{ width: 0, precision: 0, alternate: false,
- left: false, sign: false, space: false }
- }
-}
-
-enum FormatOp {
- FormatDigit,
- FormatOctal,
- FormatHex,
- FormatHEX,
- FormatString
-}
-
-impl FormatOp {
- fn from_char(c: char) -> FormatOp {
- match c {
- 'd' => FormatDigit,
- 'o' => FormatOctal,
- 'x' => FormatHex,
- 'X' => FormatHEX,
- 's' => FormatString,
- _ => fail!("bad FormatOp char")
- }
- }
- fn to_char(self) -> char {
- match self {
- FormatDigit => 'd',
- FormatOctal => 'o',
- FormatHex => 'x',
- FormatHEX => 'X',
- FormatString => 's'
- }
- }
-}
-
-fn format(val: Param, op: FormatOp, flags: Flags) -> Result<~[u8],~str> {
- let mut s = match val {
- Number(d) => {
- match op {
- FormatString => {
- return Err(~"non-number on stack with %s")
- }
- _ => {
- let radix = match op {
- FormatDigit => 10,
- FormatOctal => 8,
- FormatHex|FormatHEX => 16,
- FormatString => unreachable!()
- };
- let mut s = ~[];
- match op {
- FormatDigit => {
- let sign = if flags.sign { SignAll } else { SignNeg };
- int_to_str_bytes_common(d, radix, sign, |c| {
- s.push(c);
- })
- }
- _ => {
- int_to_str_bytes_common(d as uint, radix, SignNone, |c| {
- s.push(c);
- })
- }
- };
- if flags.precision > s.len() {
- let mut s_ = vec::with_capacity(flags.precision);
- let n = flags.precision - s.len();
- s_.grow(n, &('0' as u8));
- s_.push_all_move(s);
- s = s_;
- }
- assert!(!s.is_empty(), "string conversion produced empty result");
- match op {
- FormatDigit => {
- if flags.space && !(s[0] == '-' as u8 || s[0] == '+' as u8) {
- s.unshift(' ' as u8);
- }
- }
- FormatOctal => {
- if flags.alternate && s[0] != '0' as u8 {
- s.unshift('0' as u8);
- }
- }
- FormatHex => {
- if flags.alternate {
- let s_ = util::replace(&mut s, ~['0' as u8, 'x' as u8]);
- s.push_all_move(s_);
- }
- }
- FormatHEX => {
- s = s.into_ascii().to_upper().into_bytes();
- if flags.alternate {
- let s_ = util::replace(&mut s, ~['0' as u8, 'X' as u8]);
- s.push_all_move(s_);
- }
- }
- FormatString => unreachable!()
- }
- s
- }
- }
- }
- String(s) => {
- match op {
- FormatString => {
- let mut s = s.as_bytes().to_owned();
- if flags.precision > 0 && flags.precision < s.len() {
- s.truncate(flags.precision);
- }
- s
- }
- _ => {
- return Err(format!("non-string on stack with %{}", op.to_char()))
- }
- }
- }
- };
- if flags.width > s.len() {
- let n = flags.width - s.len();
- if flags.left {
- s.grow(n, &(' ' as u8));
- } else {
- let mut s_ = vec::with_capacity(flags.width);
- s_.grow(n, &(' ' as u8));
- s_.push_all_move(s);
- s = s_;
- }
- }
- Ok(s)
-}
-
-#[cfg(test)]
-mod test {
- use super::*;
- use std::result::Ok;
-
- #[test]
- fn test_basic_setabf() {
- let s = bytes!("\\E[48;5;%p1%dm");
- assert_eq!(expand(s, [Number(1)], &mut Variables::new()).unwrap(),
- bytes!("\\E[48;5;1m").to_owned());
- }
-
- #[test]
- fn test_multiple_int_constants() {
- assert_eq!(expand(bytes!("%{1}%{2}%d%d"), [], &mut Variables::new()).unwrap(),
- bytes!("21").to_owned());
- }
-
- #[test]
- fn test_op_i() {
- let mut vars = Variables::new();
- assert_eq!(expand(bytes!("%p1%d%p2%d%p3%d%i%p1%d%p2%d%p3%d"),
- [Number(1),Number(2),Number(3)], &mut vars),
- Ok(bytes!("123233").to_owned()));
- assert_eq!(expand(bytes!("%p1%d%p2%d%i%p1%d%p2%d"), [], &mut vars),
- Ok(bytes!("0011").to_owned()));
- }
-
- #[test]
- fn test_param_stack_failure_conditions() {
- let mut varstruct = Variables::new();
- let vars = &mut varstruct;
- let caps = ["%d", "%c", "%s", "%Pa", "%l", "%!", "%~"];
- for cap in caps.iter() {
- let res = expand(cap.as_bytes(), [], vars);
- assert!(res.is_err(),
- "Op {} succeeded incorrectly with 0 stack entries", *cap);
- let p = if *cap == "%s" || *cap == "%l" { String(~"foo") } else { Number(97) };
- let res = expand((bytes!("%p1")).to_owned() + cap.as_bytes(), [p], vars);
- assert!(res.is_ok(),
- "Op {} failed with 1 stack entry: {}", *cap, res.unwrap_err());
- }
- let caps = ["%+", "%-", "%*", "%/", "%m", "%&", "%|", "%A", "%O"];
- for cap in caps.iter() {
- let res = expand(cap.as_bytes(), [], vars);
- assert!(res.is_err(),
- "Binop {} succeeded incorrectly with 0 stack entries", *cap);
- let res = expand((bytes!("%{1}")).to_owned() + cap.as_bytes(), [], vars);
- assert!(res.is_err(),
- "Binop {} succeeded incorrectly with 1 stack entry", *cap);
- let res = expand((bytes!("%{1}%{2}")).to_owned() + cap.as_bytes(), [], vars);
- assert!(res.is_ok(),
- "Binop {} failed with 2 stack entries: {}", *cap, res.unwrap_err());
- }
- }
-
- #[test]
- fn test_push_bad_param() {
- assert!(expand(bytes!("%pa"), [], &mut Variables::new()).is_err());
- }
-
- #[test]
- fn test_comparison_ops() {
- let v = [('<', [1u8, 0u8, 0u8]), ('=', [0u8, 1u8, 0u8]), ('>', [0u8, 0u8, 1u8])];
- for &(op, bs) in v.iter() {
- let s = format!("%\\{1\\}%\\{2\\}%{}%d", op);
- let res = expand(s.as_bytes(), [], &mut Variables::new());
- assert!(res.is_ok(), res.unwrap_err());
- assert_eq!(res.unwrap(), ~['0' as u8 + bs[0]]);
- let s = format!("%\\{1\\}%\\{1\\}%{}%d", op);
- let res = expand(s.as_bytes(), [], &mut Variables::new());
- assert!(res.is_ok(), res.unwrap_err());
- assert_eq!(res.unwrap(), ~['0' as u8 + bs[1]]);
- let s = format!("%\\{2\\}%\\{1\\}%{}%d", op);
- let res = expand(s.as_bytes(), [], &mut Variables::new());
- assert!(res.is_ok(), res.unwrap_err());
- assert_eq!(res.unwrap(), ~['0' as u8 + bs[2]]);
- }
- }
-
- #[test]
- fn test_conditionals() {
- let mut vars = Variables::new();
- let s = bytes!("\\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m");
- let res = expand(s, [Number(1)], &mut vars);
- assert!(res.is_ok(), res.unwrap_err());
- assert_eq!(res.unwrap(), bytes!("\\E[31m").to_owned());
- let res = expand(s, [Number(8)], &mut vars);
- assert!(res.is_ok(), res.unwrap_err());
- assert_eq!(res.unwrap(), bytes!("\\E[90m").to_owned());
- let res = expand(s, [Number(42)], &mut vars);
- assert!(res.is_ok(), res.unwrap_err());
- assert_eq!(res.unwrap(), bytes!("\\E[38;5;42m").to_owned());
- }
-
- #[test]
- fn test_format() {
- let mut varstruct = Variables::new();
- let vars = &mut varstruct;
- assert_eq!(expand(bytes!("%p1%s%p2%2s%p3%2s%p4%.2s"),
- [String(~"foo"), String(~"foo"), String(~"f"), String(~"foo")], vars),
- Ok(bytes!("foofoo ffo").to_owned()));
- assert_eq!(expand(bytes!("%p1%:-4.2s"), [String(~"foo")], vars),
- Ok(bytes!("fo ").to_owned()));
-
- assert_eq!(expand(bytes!("%p1%d%p1%.3d%p1%5d%p1%:+d"), [Number(1)], vars),
- Ok(bytes!("1001 1+1").to_owned()));
- assert_eq!(expand(bytes!("%p1%o%p1%#o%p2%6.4x%p2%#6.4X"), [Number(15), Number(27)], vars),
- Ok(bytes!("17017 001b0X001B").to_owned()));
- }
-}
+++ /dev/null
-// Copyright 2013 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_uppercase_statics)];
-
-/// ncurses-compatible compiled terminfo format parsing (term(5))
-
-
-use std::{vec, str};
-use std::hashmap::HashMap;
-use std::io;
-use super::super::TermInfo;
-
-// These are the orders ncurses uses in its compiled format (as of 5.9). Not sure if portable.
-
-pub static boolfnames: &'static[&'static str] = &'static["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] = &'static["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] = &'static[ "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] = &'static[ "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] = &'static[ "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] = &'static[ "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"];
-
-/// Parse a compiled terminfo entry, using long capability names if `longnames` is true
-pub fn parse(file: &mut io::Reader,
- longnames: bool) -> Result<~TermInfo, ~str> {
- let bnames;
- let snames;
- let nnames;
-
- if longnames {
- bnames = boolfnames;
- snames = stringfnames;
- nnames = numfnames;
- } else {
- bnames = boolnames;
- snames = stringnames;
- nnames = numnames;
- }
-
- // Check magic number
- let magic = file.read_le_u16();
- if magic != 0x011A {
- return Err(format!("invalid magic number: expected {:x} but found {:x}",
- 0x011A, magic as uint));
- }
-
- let names_bytes = file.read_le_i16() as int;
- let bools_bytes = file.read_le_i16() as int;
- let numbers_count = file.read_le_i16() as int;
- let string_offsets_count = file.read_le_i16() as int;
- let string_table_bytes = file.read_le_i16() as int;
-
- assert!(names_bytes > 0);
-
- debug!("names_bytes = {}", names_bytes);
- debug!("bools_bytes = {}", bools_bytes);
- debug!("numbers_count = {}", numbers_count);
- debug!("string_offsets_count = {}", string_offsets_count);
- debug!("string_table_bytes = {}", string_table_bytes);
-
- if (bools_bytes as uint) > boolnames.len() {
- error!("expected bools_bytes to be less than {} but found {}", boolnames.len(),
- bools_bytes);
- return Err(~"incompatible file: more booleans than expected");
- }
-
- if (numbers_count as uint) > numnames.len() {
- error!("expected numbers_count to be less than {} but found {}", numnames.len(),
- numbers_count);
- return Err(~"incompatible file: more numbers than expected");
- }
-
- if (string_offsets_count as uint) > stringnames.len() {
- error!("expected string_offsets_count to be less than {} but found {}", stringnames.len(),
- string_offsets_count);
- return Err(~"incompatible file: more string offsets than expected");
- }
-
- // don't read NUL
- let names_str = str::from_utf8_owned(file.read_bytes(names_bytes as uint - 1)).unwrap();
-
- let term_names: ~[~str] = names_str.split('|').map(|s| s.to_owned()).collect();
-
- file.read_byte(); // consume NUL
-
- debug!("term names: {:?}", term_names);
-
- let mut bools_map = HashMap::new();
- if bools_bytes != 0 {
- for i in range(0, bools_bytes) {
- let b = file.read_byte().unwrap();
- if b < 0 {
- error!("EOF reading bools after {} entries", i);
- return Err(~"error: expected more bools but hit EOF");
- } else if b == 1 {
- debug!("{} set", bnames[i]);
- bools_map.insert(bnames[i].to_owned(), true);
- }
- }
- }
-
- debug!("bools: {:?}", bools_map);
-
- if (bools_bytes + names_bytes) % 2 == 1 {
- debug!("adjusting for padding between bools and numbers");
- file.read_byte(); // compensate for padding
- }
-
- let mut numbers_map = HashMap::new();
- if numbers_count != 0 {
- for i in range(0, numbers_count) {
- let n = file.read_le_u16();
- if n != 0xFFFF {
- debug!("{}\\#{}", nnames[i], n);
- numbers_map.insert(nnames[i].to_owned(), n);
- }
- }
- }
-
- debug!("numbers: {:?}", numbers_map);
-
- let mut string_map = HashMap::new();
-
- if string_offsets_count != 0 {
- let mut string_offsets = vec::with_capacity(10);
- for _ in range(0, string_offsets_count) {
- string_offsets.push(file.read_le_u16());
- }
-
- debug!("offsets: {:?}", string_offsets);
-
- let string_table = file.read_bytes(string_table_bytes as uint);
-
- if string_table.len() != string_table_bytes as uint {
- error!("EOF reading string table after {} bytes, wanted {}", string_table.len(),
- string_table_bytes);
- return Err(~"error: hit EOF before end of string table");
- }
-
- for (i, v) in string_offsets.iter().enumerate() {
- let offset = *v;
- if offset == 0xFFFF { // non-entry
- continue;
- }
-
- let name = if snames[i] == "_" {
- stringfnames[i]
- } else {
- snames[i]
- };
-
- if offset == 0xFFFE {
- // undocumented: FFFE indicates cap@, which means the capability is not present
- // unsure if the handling for this is correct
- string_map.insert(name.to_owned(), ~[]);
- continue;
- }
-
-
- // Find the offset of the NUL we want to go to
- let nulpos = string_table.slice(offset as uint, string_table_bytes as uint)
- .iter().position(|&b| b == 0);
- match nulpos {
- Some(len) => {
- string_map.insert(name.to_owned(),
- string_table.slice(offset as uint,
- offset as uint + len).to_owned())
- },
- None => {
- return Err(~"invalid file: missing NUL in string_table");
- }
- };
- }
- }
-
- // And that's all there is to it
- Ok(~TermInfo {names: term_names, bools: bools_map, numbers: numbers_map, strings: string_map })
-}
-
-/// Create a dummy TermInfo struct for msys terminals
-pub fn msys_terminfo() -> ~TermInfo {
- let mut strings = HashMap::new();
- strings.insert(~"sgr0", bytes!("\x1b[0m").to_owned());
- strings.insert(~"bold", bytes!("\x1b[1m").to_owned());
- strings.insert(~"setaf", bytes!("\x1b[3%p1%dm").to_owned());
- strings.insert(~"setab", bytes!("\x1b[4%p1%dm").to_owned());
- ~TermInfo {
- names: ~[~"cygwin"], // msys is a fork of an older cygwin version
- bools: HashMap::new(),
- numbers: HashMap::new(),
- strings: strings
- }
-}
-
-#[cfg(test)]
-mod test {
- use super::*;
-
- #[test]
- fn test_veclens() {
- assert_eq!(boolfnames.len(), boolnames.len());
- assert_eq!(numfnames.len(), numnames.len());
- assert_eq!(stringfnames.len(), stringnames.len());
- }
-
- #[test]
- #[ignore(reason = "no ncurses on buildbots, needs a bundled terminfo file to test against")]
- fn test_parse() {
- // FIXME #6870: Distribute a compiled file in src/tests and test there
- // parse(io::fs_reader(&p("/usr/share/terminfo/r/rxvt-256color")).unwrap(), false);
- }
-}
+++ /dev/null
-// Copyright 2012 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.
-
-/// Implement ncurses-compatible database discovery
-/// Does not support hashed database, only filesystem!
-
-use std::io::File;
-use std::os::getenv;
-use std::{os, str};
-
-/// Return path to database entry for `term`
-pub fn get_dbpath_for_term(term: &str) -> Option<~Path> {
- if term.len() == 0 {
- return None;
- }
-
- let homedir = os::homedir();
-
- let mut dirs_to_search = ~[];
- let first_char = term.char_at(0);
-
- // Find search directory
- match getenv("TERMINFO") {
- Some(dir) => dirs_to_search.push(Path::new(dir)),
- None => {
- if homedir.is_some() {
- // ncurses compatability;
- dirs_to_search.push(homedir.unwrap().join(".terminfo"))
- }
- match getenv("TERMINFO_DIRS") {
- Some(dirs) => for i in dirs.split(':') {
- if i == "" {
- dirs_to_search.push(Path::new("/usr/share/terminfo"));
- } else {
- dirs_to_search.push(Path::new(i.to_owned()));
- }
- },
- // Found nothing, use the default paths
- // /usr/share/terminfo is the de facto location, but it seems
- // Ubuntu puts it in /lib/terminfo
- None => {
- dirs_to_search.push(Path::new("/usr/share/terminfo"));
- dirs_to_search.push(Path::new("/lib/terminfo"));
- }
- }
- }
- };
-
- // Look for the terminal in all of the search directories
- for p in dirs_to_search.iter() {
- if p.exists() {
- let f = str::from_char(first_char);
- let newp = p.join_many([f.as_slice(), term]);
- if newp.exists() {
- return Some(~newp);
- }
- // on some installations the dir is named after the hex of the char (e.g. OS X)
- let f = format!("{:x}", first_char as uint);
- let newp = p.join_many([f.as_slice(), term]);
- if newp.exists() {
- return Some(~newp);
- }
- }
- }
- None
-}
-
-/// Return open file for `term`
-pub fn open(term: &str) -> Result<File, ~str> {
- match get_dbpath_for_term(term) {
- Some(x) => {
- match File::open(x) {
- Some(file) => Ok(file),
- None => Err(~"error opening file"),
- }
- }
- None => Err(format!("could not find terminfo entry for {}", term))
- }
-}
-
-#[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::os::{setenv, unsetenv};
- // FIXME (#9639): This needs to handle non-utf8 paths
- fn x(t: &str) -> ~str {
- let p = get_dbpath_for_term(t).expect("no terminfo entry found");
- p.as_str().unwrap().to_owned()
- };
- assert!(x("screen") == ~"/usr/share/terminfo/s/screen");
- assert!(get_dbpath_for_term("") == None);
- setenv("TERMINFO_DIRS", ":");
- assert!(x("screen") == ~"/usr/share/terminfo/s/screen");
- unsetenv("TERMINFO_DIRS");
-}
-
-#[test]
-#[ignore(reason = "see test_get_dbpath_for_term")]
-fn test_open() {
- open("screen");
- let t = open("nonexistent terminal that hopefully does not exist");
- assert!(t.is_err());
-}
// simplest interface possible for representing and running tests
// while providing a base that other test frameworks may build off of.
+extern mod term;
use getopts;
use getopts::groups;
use serialize::Decodable;
use stats::Stats;
use stats;
-use term;
use time::precise_time_ns;
use treemap::TreeMap;
use std::io::stdio::StdWriter;
use std::iter::range;
use std::local_data;
-use extra::term;
+use term;
static BUG_REPORT_URL: &'static str =
"http://static.rust-lang.org/doc/master/complement-bugreport.html";
#[deny(non_camel_case_types)];
extern mod extra;
+extern mod term;
pub mod util {
pub mod interner;
--- /dev/null
+// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Simple ANSI color library
+
+#[crate_id = "term#0.10-pre"];
+#[comment = "Simple ANSI color library"];
+#[license = "MIT/ASL2"];
+#[crate_type = "rlib"];
+#[crate_type = "dylib"];
+#[doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk.png",
+ html_favicon_url = "http://www.rust-lang.org/favicon.ico",
+ html_root_url = "http://static.rust-lang.org/doc/master")];
+
+#[deny(non_camel_case_types)];
+#[allow(missing_doc)];
+
+use std::os;
+use terminfo::TermInfo;
+use terminfo::searcher::open;
+use terminfo::parser::compiled::{parse, msys_terminfo};
+use terminfo::parm::{expand, Number, Variables};
+
+pub mod terminfo;
+
+// FIXME (#2807): Windows support.
+
+pub mod color {
+ pub type Color = u16;
+
+ pub static BLACK: Color = 0u16;
+ pub static RED: Color = 1u16;
+ pub static GREEN: Color = 2u16;
+ pub static YELLOW: Color = 3u16;
+ pub static BLUE: Color = 4u16;
+ pub static MAGENTA: Color = 5u16;
+ pub static CYAN: Color = 6u16;
+ pub static WHITE: Color = 7u16;
+
+ pub static BRIGHT_BLACK: Color = 8u16;
+ pub static BRIGHT_RED: Color = 9u16;
+ pub static BRIGHT_GREEN: Color = 10u16;
+ pub static BRIGHT_YELLOW: Color = 11u16;
+ pub static BRIGHT_BLUE: Color = 12u16;
+ pub static BRIGHT_MAGENTA: Color = 13u16;
+ pub static BRIGHT_CYAN: Color = 14u16;
+ pub static BRIGHT_WHITE: Color = 15u16;
+}
+
+pub mod 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.
+ 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)
+ }
+}
+
+fn cap_for_attr(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"
+ }
+}
+
+pub struct Terminal<T> {
+ priv num_colors: u16,
+ priv out: T,
+ priv ti: ~TermInfo
+}
+
+impl<T: Writer> Terminal<T> {
+ pub fn new(out: T) -> Result<Terminal<T>, ~str> {
+ let term = match os::getenv("TERM") {
+ Some(t) => t,
+ None => return Err(~"TERM environment variable undefined")
+ };
+
+ let entry = open(term);
+ if entry.is_err() {
+ if "cygwin" == term { // msys terminal
+ return Ok(Terminal {out: out, ti: msys_terminfo(), num_colors: 8});
+ }
+ return Err(entry.unwrap_err());
+ }
+
+ let mut file = entry.unwrap();
+ let ti = parse(&mut file, false);
+ if ti.is_err() {
+ return Err(ti.unwrap_err());
+ }
+
+ let inf = ti.unwrap();
+ let nc = if inf.strings.find_equiv(&("setaf")).is_some()
+ && inf.strings.find_equiv(&("setab")).is_some() {
+ inf.numbers.find_equiv(&("colors")).map_or(0, |&n| n)
+ } else { 0 };
+
+ return Ok(Terminal {out: out, ti: inf, num_colors: nc});
+ }
+ /// Sets the foreground color to the given color.
+ ///
+ /// If the color is a bright color, but the terminal only supports 8 colors,
+ /// the corresponding normal color will be used instead.
+ ///
+ /// Returns true if the color was set, false otherwise.
+ pub fn fg(&mut self, color: color::Color) -> bool {
+ let color = self.dim_if_necessary(color);
+ if self.num_colors > color {
+ let s = expand(*self.ti.strings.find_equiv(&("setaf")).unwrap(),
+ [Number(color as int)], &mut Variables::new());
+ if s.is_ok() {
+ self.out.write(s.unwrap());
+ return true
+ } else {
+ warn!("{}", s.unwrap_err());
+ }
+ }
+ false
+ }
+ /// 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 true if the color was set, false otherwise.
+ pub fn bg(&mut self, color: color::Color) -> bool {
+ let color = self.dim_if_necessary(color);
+ if self.num_colors > color {
+ let s = expand(*self.ti.strings.find_equiv(&("setab")).unwrap(),
+ [Number(color as int)], &mut Variables::new());
+ if s.is_ok() {
+ self.out.write(s.unwrap());
+ return true
+ } else {
+ warn!("{}", s.unwrap_err());
+ }
+ }
+ false
+ }
+
+ /// Sets the given terminal attribute, if supported.
+ /// Returns true if the attribute was supported, false otherwise.
+ pub fn attr(&mut self, attr: attr::Attr) -> 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.find_equiv(&cap);
+ if parm.is_some() {
+ let s = expand(*parm.unwrap(), [], &mut Variables::new());
+ if s.is_ok() {
+ self.out.write(s.unwrap());
+ return true
+ } else {
+ warn!("{}", s.unwrap_err());
+ }
+ }
+ false
+ }
+ }
+ }
+
+ /// Returns whether the given terminal attribute is supported.
+ pub fn supports_attr(&self, attr: attr::Attr) -> bool {
+ match attr {
+ attr::ForegroundColor(_) | attr::BackgroundColor(_) => {
+ self.num_colors > 0
+ }
+ _ => {
+ let cap = cap_for_attr(attr);
+ self.ti.strings.find_equiv(&cap).is_some()
+ }
+ }
+ }
+
+ /// Resets all terminal attributes and color to the default.
+ pub fn reset(&mut self) {
+ let mut cap = self.ti.strings.find_equiv(&("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.find_equiv(&("sgr"));
+ if cap.is_none() {
+ cap = self.ti.strings.find_equiv(&("op"));
+ }
+ }
+ let s = cap.map_or(Err(~"can't find terminfo capability `sgr0`"), |op| {
+ expand(*op, [], &mut Variables::new())
+ });
+ if s.is_ok() {
+ self.out.write(s.unwrap());
+ } else if self.num_colors > 0 {
+ warn!("{}", s.unwrap_err());
+ } else {
+ // if we support attributes but not color, it would be nice to still warn!()
+ // but it's not worth testing all known attributes just for this.
+ debug!("{}", s.unwrap_err());
+ }
+ }
+
+ fn dim_if_necessary(&self, color: color::Color) -> color::Color {
+ if color >= self.num_colors && color >= 8 && color < 16 {
+ color-8
+ } else { color }
+ }
+
+ pub fn unwrap(self) -> T { self.out }
+
+ pub fn get_ref<'a>(&'a self) -> &'a T { &self.out }
+
+ pub fn get_mut<'a>(&'a mut self) -> &'a mut T { &mut self.out }
+}
+
+impl<T: Writer> Writer for Terminal<T> {
+ fn write(&mut self, buf: &[u8]) {
+ self.out.write(buf);
+ }
+
+ fn flush(&mut self) {
+ self.out.flush();
+ }
+}
--- /dev/null
+// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#[allow(missing_doc)];
+
+use std::hashmap::HashMap;
+
+/// A parsed terminfo entry.
+pub struct TermInfo {
+ /// Names for the terminal
+ priv names: ~[~str],
+ /// Map of capability name to boolean value
+ priv bools: HashMap<~str, bool>,
+ /// Map of capability name to numeric value
+ numbers: HashMap<~str, u16>,
+ /// Map of capability name to raw (unexpanded) string
+ strings: HashMap<~str, ~[u8]>
+}
+
+pub mod searcher;
+pub mod parser {
+ pub mod compiled;
+}
+pub mod parm;
--- /dev/null
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Parameterized string expansion
+
+use std::{char, vec, util};
+use std::num::strconv::{SignNone,SignNeg,SignAll,int_to_str_bytes_common};
+
+#[deriving(Eq)]
+enum States {
+ Nothing,
+ Percent,
+ SetVar,
+ GetVar,
+ PushParam,
+ CharConstant,
+ CharClose,
+ IntConstant(int),
+ FormatPattern(Flags, FormatState),
+ SeekIfElse(int),
+ SeekIfElsePercent(int),
+ SeekIfEnd(int),
+ SeekIfEndPercent(int)
+}
+
+#[deriving(Eq)]
+enum FormatState {
+ FormatStateFlags,
+ FormatStateWidth,
+ FormatStatePrecision
+}
+
+/// Types of parameters a capability can use
+#[deriving(Clone)]
+#[allow(missing_doc)]
+pub enum Param {
+ String(~str),
+ Number(int)
+}
+
+/// Container for static and dynamic variable arrays
+pub struct Variables {
+ /// Static variables A-Z
+ priv sta: [Param, ..26],
+ /// Dynamic variables a-z
+ priv dyn: [Param, ..26]
+}
+
+impl Variables {
+ /// Return a new zero-initialized Variables
+ pub fn new() -> Variables {
+ Variables {
+ sta: [
+ Number(0), Number(0), Number(0), Number(0), Number(0),
+ Number(0), Number(0), Number(0), Number(0), Number(0),
+ Number(0), Number(0), Number(0), Number(0), Number(0),
+ Number(0), Number(0), Number(0), Number(0), Number(0),
+ Number(0), Number(0), Number(0), Number(0), Number(0),
+ Number(0),
+ ],
+ dyn: [
+ Number(0), Number(0), Number(0), Number(0), Number(0),
+ Number(0), Number(0), Number(0), Number(0), Number(0),
+ Number(0), Number(0), Number(0), Number(0), Number(0),
+ Number(0), Number(0), Number(0), Number(0), Number(0),
+ Number(0), Number(0), Number(0), Number(0), Number(0),
+ Number(0),
+ ],
+ }
+ }
+}
+
+/**
+ Expand a parameterized capability
+
+ # Arguments
+ * `cap` - string to expand
+ * `params` - vector of params for %p1 etc
+ * `vars` - Variables struct for %Pa etc
+
+ To be compatible with ncurses, `vars` should be the same between calls to `expand` for
+ multiple capabilities for the same terminal.
+ */
+pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables)
+ -> Result<~[u8], ~str> {
+ let mut state = Nothing;
+
+ // expanded cap will only rarely be larger than the cap itself
+ let mut output = vec::with_capacity(cap.len());
+
+ let mut stack: ~[Param] = ~[];
+
+ // 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.mut_iter().zip(params.iter()) {
+ *dst = (*src).clone();
+ }
+
+ for c in cap.iter().map(|&x| x) {
+ let cur = c as char;
+ let mut old_state = state;
+ match state {
+ Nothing => {
+ if cur == '%' {
+ state = Percent;
+ } else {
+ output.push(c);
+ }
+ },
+ Percent => {
+ match cur {
+ '%' => { output.push(c); state = Nothing },
+ 'c' => if stack.len() > 0 {
+ 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")
+ }
+ } else { return Err(~"stack is empty") },
+ 'p' => state = PushParam,
+ 'P' => state = SetVar,
+ 'g' => state = GetVar,
+ '\'' => state = CharConstant,
+ '{' => state = IntConstant(0),
+ 'l' => if stack.len() > 0 {
+ match stack.pop().unwrap() {
+ String(s) => stack.push(Number(s.len() as int)),
+ _ => return Err(~"a non-str was used with %l")
+ }
+ } else { return Err(~"stack is empty") },
+ '+' => 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 +")
+ }
+ } else { return Err(~"stack is empty") },
+ '-' => 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 -")
+ }
+ } else { return Err(~"stack is empty") },
+ '*' => 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 *")
+ }
+ } else { return Err(~"stack is empty") },
+ '/' => 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 /")
+ }
+ } else { return Err(~"stack is empty") },
+ '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 %")
+ }
+ } else { return Err(~"stack is empty") },
+ '&' => 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 &")
+ }
+ } else { return Err(~"stack is empty") },
+ '|' => 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 |")
+ }
+ } else { return Err(~"stack is empty") },
+ '^' => 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 ^")
+ }
+ } else { return Err(~"stack is empty") },
+ '=' => 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 =")
+ }
+ } else { return Err(~"stack is empty") },
+ '>' => 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 >")
+ }
+ } else { return Err(~"stack is empty") },
+ '<' => 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 <")
+ }
+ } else { return Err(~"stack is empty") },
+ '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")
+ }
+ } else { return Err(~"stack is empty") },
+ '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")
+ }
+ } else { return Err(~"stack is empty") },
+ '!' => if stack.len() > 0 {
+ match stack.pop().unwrap() {
+ Number(0) => stack.push(Number(1)),
+ Number(_) => stack.push(Number(0)),
+ _ => return Err(~"non-number on stack with logical not")
+ }
+ } else { return Err(~"stack is empty") },
+ '~' => if stack.len() > 0 {
+ match stack.pop().unwrap() {
+ Number(x) => stack.push(Number(!x)),
+ _ => return Err(~"non-number on stack with %~")
+ }
+ } else { return Err(~"stack is empty") },
+ 'i' => match (mparams[0].clone(), mparams[1].clone()) {
+ (Number(x), Number(y)) => {
+ mparams[0] = Number(x+1);
+ mparams[1] = Number(y+1);
+ },
+ (_, _) => return Err(~"first two params not numbers with %i")
+ },
+
+ // printf-style support for %doxXs
+ 'd'|'o'|'x'|'X'|'s' => if stack.len() > 0 {
+ 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())
+ } else { return Err(~"stack is empty") },
+ ':'|'#'|' '|'.'|'0'..'9' => {
+ let mut flags = Flags::new();
+ let mut fstate = FormatStateFlags;
+ match cur {
+ ':' => (),
+ '#' => flags.alternate = true,
+ ' ' => flags.space = true,
+ '.' => fstate = FormatStatePrecision,
+ '0'..'9' => {
+ flags.width = (cur as uint - '0' as uint);
+ fstate = FormatStateWidth;
+ }
+ _ => unreachable!()
+ }
+ state = FormatPattern(flags, fstate);
+ }
+
+ // conditionals
+ '?' => (),
+ 't' => if stack.len() > 0 {
+ match stack.pop().unwrap() {
+ Number(0) => state = SeekIfElse(0),
+ Number(_) => (),
+ _ => return Err(~"non-number on stack with conditional")
+ }
+ } else { return Err(~"stack is empty") },
+ 'e' => state = SeekIfEnd(0),
+ ';' => (),
+
+ _ => return Err(format!("unrecognized format option {}", cur))
+ }
+ },
+ PushParam => {
+ // params are 1-indexed
+ stack.push(mparams[match char::to_digit(cur, 10) {
+ Some(d) => d - 1,
+ None => return Err(~"bad param number")
+ }].clone());
+ },
+ SetVar => {
+ if cur >= 'A' && cur <= 'Z' {
+ if stack.len() > 0 {
+ let idx = (cur as u8) - ('A' as u8);
+ vars.sta[idx] = stack.pop().unwrap();
+ } else { return Err(~"stack is empty") }
+ } else if cur >= 'a' && cur <= 'z' {
+ if stack.len() > 0 {
+ let idx = (cur as u8) - ('a' as u8);
+ vars.dyn[idx] = stack.pop().unwrap();
+ } else { return Err(~"stack is empty") }
+ } else {
+ return Err(~"bad variable name in %P");
+ }
+ },
+ GetVar => {
+ if cur >= 'A' && cur <= 'Z' {
+ let idx = (cur as u8) - ('A' as u8);
+ stack.push(vars.sta[idx].clone());
+ } else if cur >= 'a' && cur <= 'z' {
+ let idx = (cur as u8) - ('a' as u8);
+ stack.push(vars.dyn[idx].clone());
+ } else {
+ return Err(~"bad variable name in %g");
+ }
+ },
+ CharConstant => {
+ stack.push(Number(c as int));
+ state = CharClose;
+ },
+ CharClose => {
+ if cur != '\'' {
+ return Err(~"malformed character constant");
+ }
+ },
+ IntConstant(i) => {
+ match cur {
+ '}' => {
+ stack.push(Number(i));
+ state = Nothing;
+ }
+ '0'..'9' => {
+ state = IntConstant(i*10 + (cur as int - '0' as int));
+ old_state = Nothing;
+ }
+ _ => return Err(~"bad int constant")
+ }
+ }
+ FormatPattern(ref mut flags, ref mut fstate) => {
+ old_state = Nothing;
+ match (*fstate, cur) {
+ (_,'d')|(_,'o')|(_,'x')|(_,'X')|(_,'s') => if stack.len() > 0 {
+ let res = format(stack.pop().unwrap(), FormatOp::from_char(cur), *flags);
+ if res.is_err() { return res }
+ output.push_all(res.unwrap());
+ old_state = state; // will cause state to go to Nothing
+ } else { return Err(~"stack is empty") },
+ (FormatStateFlags,'#') => {
+ flags.alternate = true;
+ }
+ (FormatStateFlags,'-') => {
+ flags.left = true;
+ }
+ (FormatStateFlags,'+') => {
+ flags.sign = true;
+ }
+ (FormatStateFlags,' ') => {
+ flags.space = true;
+ }
+ (FormatStateFlags,'0'..'9') => {
+ flags.width = (cur as uint - '0' as uint);
+ *fstate = FormatStateWidth;
+ }
+ (FormatStateFlags,'.') => {
+ *fstate = FormatStatePrecision;
+ }
+ (FormatStateWidth,'0'..'9') => {
+ let old = flags.width;
+ flags.width = flags.width * 10 + (cur as uint - '0' as uint);
+ if flags.width < old { return Err(~"format width overflow") }
+ }
+ (FormatStateWidth,'.') => {
+ *fstate = FormatStatePrecision;
+ }
+ (FormatStatePrecision,'0'..'9') => {
+ let old = flags.precision;
+ flags.precision = flags.precision * 10 + (cur as uint - '0' as uint);
+ if flags.precision < old { return Err(~"format precision overflow") }
+ }
+ _ => return Err(~"invalid format specifier")
+ }
+ }
+ SeekIfElse(level) => {
+ if cur == '%' {
+ state = SeekIfElsePercent(level);
+ }
+ old_state = Nothing;
+ }
+ SeekIfElsePercent(level) => {
+ if cur == ';' {
+ if level == 0 {
+ state = Nothing;
+ } else {
+ state = SeekIfElse(level-1);
+ }
+ } else if cur == 'e' && level == 0 {
+ state = Nothing;
+ } else if cur == '?' {
+ state = SeekIfElse(level+1);
+ } else {
+ state = SeekIfElse(level);
+ }
+ }
+ SeekIfEnd(level) => {
+ if cur == '%' {
+ state = SeekIfEndPercent(level);
+ }
+ old_state = Nothing;
+ }
+ SeekIfEndPercent(level) => {
+ if cur == ';' {
+ if level == 0 {
+ state = Nothing;
+ } else {
+ state = SeekIfEnd(level-1);
+ }
+ } else if cur == '?' {
+ state = SeekIfEnd(level+1);
+ } else {
+ state = SeekIfEnd(level);
+ }
+ }
+ }
+ if state == old_state {
+ state = Nothing;
+ }
+ }
+ Ok(output)
+}
+
+#[deriving(Eq)]
+struct Flags {
+ width: uint,
+ precision: uint,
+ alternate: bool,
+ left: bool,
+ sign: bool,
+ space: bool
+}
+
+impl Flags {
+ fn new() -> Flags {
+ Flags{ width: 0, precision: 0, alternate: false,
+ left: false, sign: false, space: false }
+ }
+}
+
+enum FormatOp {
+ FormatDigit,
+ FormatOctal,
+ FormatHex,
+ FormatHEX,
+ FormatString
+}
+
+impl FormatOp {
+ fn from_char(c: char) -> FormatOp {
+ match c {
+ 'd' => FormatDigit,
+ 'o' => FormatOctal,
+ 'x' => FormatHex,
+ 'X' => FormatHEX,
+ 's' => FormatString,
+ _ => fail!("bad FormatOp char")
+ }
+ }
+ fn to_char(self) -> char {
+ match self {
+ FormatDigit => 'd',
+ FormatOctal => 'o',
+ FormatHex => 'x',
+ FormatHEX => 'X',
+ FormatString => 's'
+ }
+ }
+}
+
+fn format(val: Param, op: FormatOp, flags: Flags) -> Result<~[u8],~str> {
+ let mut s = match val {
+ Number(d) => {
+ match op {
+ FormatString => {
+ return Err(~"non-number on stack with %s")
+ }
+ _ => {
+ let radix = match op {
+ FormatDigit => 10,
+ FormatOctal => 8,
+ FormatHex|FormatHEX => 16,
+ FormatString => unreachable!()
+ };
+ let mut s = ~[];
+ match op {
+ FormatDigit => {
+ let sign = if flags.sign { SignAll } else { SignNeg };
+ int_to_str_bytes_common(d, radix, sign, |c| {
+ s.push(c);
+ })
+ }
+ _ => {
+ int_to_str_bytes_common(d as uint, radix, SignNone, |c| {
+ s.push(c);
+ })
+ }
+ };
+ if flags.precision > s.len() {
+ let mut s_ = vec::with_capacity(flags.precision);
+ let n = flags.precision - s.len();
+ s_.grow(n, &('0' as u8));
+ s_.push_all_move(s);
+ s = s_;
+ }
+ assert!(!s.is_empty(), "string conversion produced empty result");
+ match op {
+ FormatDigit => {
+ if flags.space && !(s[0] == '-' as u8 || s[0] == '+' as u8) {
+ s.unshift(' ' as u8);
+ }
+ }
+ FormatOctal => {
+ if flags.alternate && s[0] != '0' as u8 {
+ s.unshift('0' as u8);
+ }
+ }
+ FormatHex => {
+ if flags.alternate {
+ let s_ = util::replace(&mut s, ~['0' as u8, 'x' as u8]);
+ s.push_all_move(s_);
+ }
+ }
+ FormatHEX => {
+ s = s.into_ascii().to_upper().into_bytes();
+ if flags.alternate {
+ let s_ = util::replace(&mut s, ~['0' as u8, 'X' as u8]);
+ s.push_all_move(s_);
+ }
+ }
+ FormatString => unreachable!()
+ }
+ s
+ }
+ }
+ }
+ String(s) => {
+ match op {
+ FormatString => {
+ let mut s = s.as_bytes().to_owned();
+ if flags.precision > 0 && flags.precision < s.len() {
+ s.truncate(flags.precision);
+ }
+ s
+ }
+ _ => {
+ return Err(format!("non-string on stack with %{}", op.to_char()))
+ }
+ }
+ }
+ };
+ if flags.width > s.len() {
+ let n = flags.width - s.len();
+ if flags.left {
+ s.grow(n, &(' ' as u8));
+ } else {
+ let mut s_ = vec::with_capacity(flags.width);
+ s_.grow(n, &(' ' as u8));
+ s_.push_all_move(s);
+ s = s_;
+ }
+ }
+ Ok(s)
+}
+
+#[cfg(test)]
+mod test {
+ use super::{expand,String,Variables,Number};
+ use std::result::Ok;
+
+ #[test]
+ fn test_basic_setabf() {
+ let s = bytes!("\\E[48;5;%p1%dm");
+ assert_eq!(expand(s, [Number(1)], &mut Variables::new()).unwrap(),
+ bytes!("\\E[48;5;1m").to_owned());
+ }
+
+ #[test]
+ fn test_multiple_int_constants() {
+ assert_eq!(expand(bytes!("%{1}%{2}%d%d"), [], &mut Variables::new()).unwrap(),
+ bytes!("21").to_owned());
+ }
+
+ #[test]
+ fn test_op_i() {
+ let mut vars = Variables::new();
+ assert_eq!(expand(bytes!("%p1%d%p2%d%p3%d%i%p1%d%p2%d%p3%d"),
+ [Number(1),Number(2),Number(3)], &mut vars),
+ Ok(bytes!("123233").to_owned()));
+ assert_eq!(expand(bytes!("%p1%d%p2%d%i%p1%d%p2%d"), [], &mut vars),
+ Ok(bytes!("0011").to_owned()));
+ }
+
+ #[test]
+ fn test_param_stack_failure_conditions() {
+ let mut varstruct = Variables::new();
+ let vars = &mut varstruct;
+ let caps = ["%d", "%c", "%s", "%Pa", "%l", "%!", "%~"];
+ for cap in caps.iter() {
+ let res = expand(cap.as_bytes(), [], vars);
+ assert!(res.is_err(),
+ "Op {} succeeded incorrectly with 0 stack entries", *cap);
+ let p = if *cap == "%s" || *cap == "%l" { String(~"foo") } else { Number(97) };
+ let res = expand((bytes!("%p1")).to_owned() + cap.as_bytes(), [p], vars);
+ assert!(res.is_ok(),
+ "Op {} failed with 1 stack entry: {}", *cap, res.unwrap_err());
+ }
+ let caps = ["%+", "%-", "%*", "%/", "%m", "%&", "%|", "%A", "%O"];
+ for cap in caps.iter() {
+ let res = expand(cap.as_bytes(), [], vars);
+ assert!(res.is_err(),
+ "Binop {} succeeded incorrectly with 0 stack entries", *cap);
+ let res = expand((bytes!("%{1}")).to_owned() + cap.as_bytes(), [], vars);
+ assert!(res.is_err(),
+ "Binop {} succeeded incorrectly with 1 stack entry", *cap);
+ let res = expand((bytes!("%{1}%{2}")).to_owned() + cap.as_bytes(), [], vars);
+ assert!(res.is_ok(),
+ "Binop {} failed with 2 stack entries: {}", *cap, res.unwrap_err());
+ }
+ }
+
+ #[test]
+ fn test_push_bad_param() {
+ assert!(expand(bytes!("%pa"), [], &mut Variables::new()).is_err());
+ }
+
+ #[test]
+ fn test_comparison_ops() {
+ let v = [('<', [1u8, 0u8, 0u8]), ('=', [0u8, 1u8, 0u8]), ('>', [0u8, 0u8, 1u8])];
+ for &(op, bs) in v.iter() {
+ let s = format!("%\\{1\\}%\\{2\\}%{}%d", op);
+ let res = expand(s.as_bytes(), [], &mut Variables::new());
+ assert!(res.is_ok(), res.unwrap_err());
+ assert_eq!(res.unwrap(), ~['0' as u8 + bs[0]]);
+ let s = format!("%\\{1\\}%\\{1\\}%{}%d", op);
+ let res = expand(s.as_bytes(), [], &mut Variables::new());
+ assert!(res.is_ok(), res.unwrap_err());
+ assert_eq!(res.unwrap(), ~['0' as u8 + bs[1]]);
+ let s = format!("%\\{2\\}%\\{1\\}%{}%d", op);
+ let res = expand(s.as_bytes(), [], &mut Variables::new());
+ assert!(res.is_ok(), res.unwrap_err());
+ assert_eq!(res.unwrap(), ~['0' as u8 + bs[2]]);
+ }
+ }
+
+ #[test]
+ fn test_conditionals() {
+ let mut vars = Variables::new();
+ let s = bytes!("\\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m");
+ let res = expand(s, [Number(1)], &mut vars);
+ assert!(res.is_ok(), res.unwrap_err());
+ assert_eq!(res.unwrap(), bytes!("\\E[31m").to_owned());
+ let res = expand(s, [Number(8)], &mut vars);
+ assert!(res.is_ok(), res.unwrap_err());
+ assert_eq!(res.unwrap(), bytes!("\\E[90m").to_owned());
+ let res = expand(s, [Number(42)], &mut vars);
+ assert!(res.is_ok(), res.unwrap_err());
+ assert_eq!(res.unwrap(), bytes!("\\E[38;5;42m").to_owned());
+ }
+
+ #[test]
+ fn test_format() {
+ let mut varstruct = Variables::new();
+ let vars = &mut varstruct;
+ assert_eq!(expand(bytes!("%p1%s%p2%2s%p3%2s%p4%.2s"),
+ [String(~"foo"), String(~"foo"), String(~"f"), String(~"foo")], vars),
+ Ok(bytes!("foofoo ffo").to_owned()));
+ assert_eq!(expand(bytes!("%p1%:-4.2s"), [String(~"foo")], vars),
+ Ok(bytes!("fo ").to_owned()));
+
+ assert_eq!(expand(bytes!("%p1%d%p1%.3d%p1%5d%p1%:+d"), [Number(1)], vars),
+ Ok(bytes!("1001 1+1").to_owned()));
+ assert_eq!(expand(bytes!("%p1%o%p1%#o%p2%6.4x%p2%#6.4X"), [Number(15), Number(27)], vars),
+ Ok(bytes!("17017 001b0X001B").to_owned()));
+ }
+}
--- /dev/null
+// Copyright 2013 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_uppercase_statics)];
+
+/// ncurses-compatible compiled terminfo format parsing (term(5))
+
+
+use std::{vec, str};
+use std::hashmap::HashMap;
+use std::io;
+use super::super::TermInfo;
+
+// These are the orders ncurses uses in its compiled format (as of 5.9). Not sure if portable.
+
+pub static boolfnames: &'static[&'static str] = &'static["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] = &'static["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] = &'static[ "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] = &'static[ "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] = &'static[ "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] = &'static[ "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"];
+
+/// Parse a compiled terminfo entry, using long capability names if `longnames` is true
+pub fn parse(file: &mut io::Reader,
+ longnames: bool) -> Result<~TermInfo, ~str> {
+ let bnames;
+ let snames;
+ let nnames;
+
+ if longnames {
+ bnames = boolfnames;
+ snames = stringfnames;
+ nnames = numfnames;
+ } else {
+ bnames = boolnames;
+ snames = stringnames;
+ nnames = numnames;
+ }
+
+ // Check magic number
+ let magic = file.read_le_u16();
+ if magic != 0x011A {
+ return Err(format!("invalid magic number: expected {:x} but found {:x}",
+ 0x011A, magic as uint));
+ }
+
+ let names_bytes = file.read_le_i16() as int;
+ let bools_bytes = file.read_le_i16() as int;
+ let numbers_count = file.read_le_i16() as int;
+ let string_offsets_count = file.read_le_i16() as int;
+ let string_table_bytes = file.read_le_i16() as int;
+
+ assert!(names_bytes > 0);
+
+ debug!("names_bytes = {}", names_bytes);
+ debug!("bools_bytes = {}", bools_bytes);
+ debug!("numbers_count = {}", numbers_count);
+ debug!("string_offsets_count = {}", string_offsets_count);
+ debug!("string_table_bytes = {}", string_table_bytes);
+
+ if (bools_bytes as uint) > boolnames.len() {
+ error!("expected bools_bytes to be less than {} but found {}", boolnames.len(),
+ bools_bytes);
+ return Err(~"incompatible file: more booleans than expected");
+ }
+
+ if (numbers_count as uint) > numnames.len() {
+ error!("expected numbers_count to be less than {} but found {}", numnames.len(),
+ numbers_count);
+ return Err(~"incompatible file: more numbers than expected");
+ }
+
+ if (string_offsets_count as uint) > stringnames.len() {
+ error!("expected string_offsets_count to be less than {} but found {}", stringnames.len(),
+ string_offsets_count);
+ return Err(~"incompatible file: more string offsets than expected");
+ }
+
+ // don't read NUL
+ let names_str = str::from_utf8_owned(file.read_bytes(names_bytes as uint - 1)).unwrap();
+
+ let term_names: ~[~str] = names_str.split('|').map(|s| s.to_owned()).collect();
+
+ file.read_byte(); // consume NUL
+
+ debug!("term names: {:?}", term_names);
+
+ let mut bools_map = HashMap::new();
+ if bools_bytes != 0 {
+ for i in range(0, bools_bytes) {
+ let b = file.read_byte().unwrap();
+ if b < 0 {
+ error!("EOF reading bools after {} entries", i);
+ return Err(~"error: expected more bools but hit EOF");
+ } else if b == 1 {
+ debug!("{} set", bnames[i]);
+ bools_map.insert(bnames[i].to_owned(), true);
+ }
+ }
+ }
+
+ debug!("bools: {:?}", bools_map);
+
+ if (bools_bytes + names_bytes) % 2 == 1 {
+ debug!("adjusting for padding between bools and numbers");
+ file.read_byte(); // compensate for padding
+ }
+
+ let mut numbers_map = HashMap::new();
+ if numbers_count != 0 {
+ for i in range(0, numbers_count) {
+ let n = file.read_le_u16();
+ if n != 0xFFFF {
+ debug!("{}\\#{}", nnames[i], n);
+ numbers_map.insert(nnames[i].to_owned(), n);
+ }
+ }
+ }
+
+ debug!("numbers: {:?}", numbers_map);
+
+ let mut string_map = HashMap::new();
+
+ if string_offsets_count != 0 {
+ let mut string_offsets = vec::with_capacity(10);
+ for _ in range(0, string_offsets_count) {
+ string_offsets.push(file.read_le_u16());
+ }
+
+ debug!("offsets: {:?}", string_offsets);
+
+ let string_table = file.read_bytes(string_table_bytes as uint);
+
+ if string_table.len() != string_table_bytes as uint {
+ error!("EOF reading string table after {} bytes, wanted {}", string_table.len(),
+ string_table_bytes);
+ return Err(~"error: hit EOF before end of string table");
+ }
+
+ for (i, v) in string_offsets.iter().enumerate() {
+ let offset = *v;
+ if offset == 0xFFFF { // non-entry
+ continue;
+ }
+
+ let name = if snames[i] == "_" {
+ stringfnames[i]
+ } else {
+ snames[i]
+ };
+
+ if offset == 0xFFFE {
+ // undocumented: FFFE indicates cap@, which means the capability is not present
+ // unsure if the handling for this is correct
+ string_map.insert(name.to_owned(), ~[]);
+ continue;
+ }
+
+
+ // Find the offset of the NUL we want to go to
+ let nulpos = string_table.slice(offset as uint, string_table_bytes as uint)
+ .iter().position(|&b| b == 0);
+ match nulpos {
+ Some(len) => {
+ string_map.insert(name.to_owned(),
+ string_table.slice(offset as uint,
+ offset as uint + len).to_owned())
+ },
+ None => {
+ return Err(~"invalid file: missing NUL in string_table");
+ }
+ };
+ }
+ }
+
+ // And that's all there is to it
+ Ok(~TermInfo {names: term_names, bools: bools_map, numbers: numbers_map, strings: string_map })
+}
+
+/// Create a dummy TermInfo struct for msys terminals
+pub fn msys_terminfo() -> ~TermInfo {
+ let mut strings = HashMap::new();
+ strings.insert(~"sgr0", bytes!("\x1b[0m").to_owned());
+ strings.insert(~"bold", bytes!("\x1b[1m").to_owned());
+ strings.insert(~"setaf", bytes!("\x1b[3%p1%dm").to_owned());
+ strings.insert(~"setab", bytes!("\x1b[4%p1%dm").to_owned());
+ ~TermInfo {
+ names: ~[~"cygwin"], // msys is a fork of an older cygwin version
+ bools: HashMap::new(),
+ numbers: HashMap::new(),
+ strings: strings
+ }
+}
+
+#[cfg(test)]
+mod test {
+
+ use super::{boolnames, boolfnames, numnames, numfnames, stringnames, stringfnames};
+
+ #[test]
+ fn test_veclens() {
+ assert_eq!(boolfnames.len(), boolnames.len());
+ assert_eq!(numfnames.len(), numnames.len());
+ assert_eq!(stringfnames.len(), stringnames.len());
+ }
+
+ #[test]
+ #[ignore(reason = "no ncurses on buildbots, needs a bundled terminfo file to test against")]
+ fn test_parse() {
+ // FIXME #6870: Distribute a compiled file in src/tests and test there
+ // parse(io::fs_reader(&p("/usr/share/terminfo/r/rxvt-256color")).unwrap(), false);
+ }
+}
--- /dev/null
+// Copyright 2012 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.
+
+/// Implement ncurses-compatible database discovery
+/// Does not support hashed database, only filesystem!
+
+use std::io::File;
+use std::os::getenv;
+use std::{os, str};
+
+/// Return path to database entry for `term`
+pub fn get_dbpath_for_term(term: &str) -> Option<~Path> {
+ if term.len() == 0 {
+ return None;
+ }
+
+ let homedir = os::homedir();
+
+ let mut dirs_to_search = ~[];
+ let first_char = term.char_at(0);
+
+ // Find search directory
+ match getenv("TERMINFO") {
+ Some(dir) => dirs_to_search.push(Path::new(dir)),
+ None => {
+ if homedir.is_some() {
+ // ncurses compatability;
+ dirs_to_search.push(homedir.unwrap().join(".terminfo"))
+ }
+ match getenv("TERMINFO_DIRS") {
+ Some(dirs) => for i in dirs.split(':') {
+ if i == "" {
+ dirs_to_search.push(Path::new("/usr/share/terminfo"));
+ } else {
+ dirs_to_search.push(Path::new(i.to_owned()));
+ }
+ },
+ // Found nothing, use the default paths
+ // /usr/share/terminfo is the de facto location, but it seems
+ // Ubuntu puts it in /lib/terminfo
+ None => {
+ dirs_to_search.push(Path::new("/usr/share/terminfo"));
+ dirs_to_search.push(Path::new("/lib/terminfo"));
+ }
+ }
+ }
+ };
+
+ // Look for the terminal in all of the search directories
+ for p in dirs_to_search.iter() {
+ if p.exists() {
+ let f = str::from_char(first_char);
+ let newp = p.join_many([f.as_slice(), term]);
+ if newp.exists() {
+ return Some(~newp);
+ }
+ // on some installations the dir is named after the hex of the char (e.g. OS X)
+ let f = format!("{:x}", first_char as uint);
+ let newp = p.join_many([f.as_slice(), term]);
+ if newp.exists() {
+ return Some(~newp);
+ }
+ }
+ }
+ None
+}
+
+/// Return open file for `term`
+pub fn open(term: &str) -> Result<File, ~str> {
+ match get_dbpath_for_term(term) {
+ Some(x) => {
+ match File::open(x) {
+ Some(file) => Ok(file),
+ None => Err(~"error opening file"),
+ }
+ }
+ None => Err(format!("could not find terminfo entry for {}", term))
+ }
+}
+
+#[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::os::{setenv, unsetenv};
+ // FIXME (#9639): This needs to handle non-utf8 paths
+ fn x(t: &str) -> ~str {
+ let p = get_dbpath_for_term(t).expect("no terminfo entry found");
+ p.as_str().unwrap().to_owned()
+ };
+ assert!(x("screen") == ~"/usr/share/terminfo/s/screen");
+ assert!(get_dbpath_for_term("") == None);
+ setenv("TERMINFO_DIRS", ":");
+ assert!(x("screen") == ~"/usr/share/terminfo/s/screen");
+ unsetenv("TERMINFO_DIRS");
+}
+
+#[test]
+#[ignore(reason = "see test_get_dbpath_for_term")]
+fn test_open() {
+ open("screen");
+ let t = open("nonexistent terminal that hopefully does not exist");
+ assert!(t.is_err());
+}