]> git.lizzy.rs Git - rust.git/commitdiff
Move term, terminfo out of extra.
authorxales <xales@naveria.com>
Fri, 31 Jan 2014 00:15:10 +0000 (19:15 -0500)
committerxales <xales@naveria.com>
Sun, 2 Feb 2014 23:35:35 +0000 (18:35 -0500)
cc #8784

16 files changed:
mk/crates.mk
src/doc/index.md
src/libextra/lib.rs
src/libextra/term.rs [deleted file]
src/libextra/terminfo/mod.rs [deleted file]
src/libextra/terminfo/parm.rs [deleted file]
src/libextra/terminfo/parser/compiled.rs [deleted file]
src/libextra/terminfo/searcher.rs [deleted file]
src/libextra/test.rs
src/libsyntax/diagnostic.rs
src/libsyntax/lib.rs
src/libterm/lib.rs [new file with mode: 0644]
src/libterm/terminfo/mod.rs [new file with mode: 0644]
src/libterm/terminfo/parm.rs [new file with mode: 0644]
src/libterm/terminfo/parser/compiled.rs [new file with mode: 0644]
src/libterm/terminfo/searcher.rs [new file with mode: 0644]

index 7ac3c3b486b11138d8e2cf72d79f4c7b6c6d22b2..b447dbda9e08e13877f865ef88851dd6f27666a2 100644 (file)
 # 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
index d3270a96d803682a317ec6cbca01a3d4cc22fa73..730c9c744f6f623cb5e87678d3e95b4df1ced275 100644 (file)
@@ -40,6 +40,7 @@ li {list-style-type: none; }
 * [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
 
index bb89915dfd13563654442e932cd511f03ab46aa2..e2a4b52c810d240bdd5c06cbb6731f1efe97a4a0 100644 (file)
@@ -1,4 +1,4 @@
-// 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.
 //
@@ -67,7 +67,6 @@
 pub mod getopts;
 pub mod json;
 pub mod tempfile;
-pub mod term;
 pub mod time;
 pub mod base64;
 pub mod workcache;
@@ -87,8 +86,6 @@
 #[cfg(unicode)]
 mod unicode;
 
-pub mod terminfo;
-
 // Compiler support modules
 
 pub mod test;
diff --git a/src/libextra/term.rs b/src/libextra/term.rs
deleted file mode 100644 (file)
index ca5c4cf..0000000
+++ /dev/null
@@ -1,251 +0,0 @@
-// 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();
-    }
-}
diff --git a/src/libextra/terminfo/mod.rs b/src/libextra/terminfo/mod.rs
deleted file mode 100644 (file)
index 06bf6e4..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-// 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;
diff --git a/src/libextra/terminfo/parm.rs b/src/libextra/terminfo/parm.rs
deleted file mode 100644 (file)
index bd9eadc..0000000
+++ /dev/null
@@ -1,684 +0,0 @@
-// 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()));
-    }
-}
diff --git a/src/libextra/terminfo/parser/compiled.rs b/src/libextra/terminfo/parser/compiled.rs
deleted file mode 100644 (file)
index 2347872..0000000
+++ /dev/null
@@ -1,351 +0,0 @@
-// 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);
-    }
-}
diff --git a/src/libextra/terminfo/searcher.rs b/src/libextra/terminfo/searcher.rs
deleted file mode 100644 (file)
index 8cbb090..0000000
+++ /dev/null
@@ -1,112 +0,0 @@
-// 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());
-}
index cd04cddba4a6937a864d7a0c4b64cdfe29e5be8d..ef5a05b6a3e51ddafb92523749933313223e7acd 100644 (file)
@@ -15,6 +15,7 @@
 // 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;
@@ -23,7 +24,6 @@
 use serialize::Decodable;
 use stats::Stats;
 use stats;
-use term;
 use time::precise_time_ns;
 use treemap::TreeMap;
 
index fabc244e00af88aeafb69e1fe68d15a8befaf565..90fe121160baea0c702cbf678c54c675785790e9 100644 (file)
@@ -16,7 +16,7 @@
 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";
index 532a2a9a314935a148ad552c085b64226de8fa3c..e2460b0171a20776d7dec120fb6f2d6442e7f603 100644 (file)
@@ -31,6 +31,7 @@
 #[deny(non_camel_case_types)];
 
 extern mod extra;
+extern mod term;
 
 pub mod util {
     pub mod interner;
diff --git a/src/libterm/lib.rs b/src/libterm/lib.rs
new file mode 100644 (file)
index 0000000..01ebf58
--- /dev/null
@@ -0,0 +1,262 @@
+// 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();
+    }
+}
diff --git a/src/libterm/terminfo/mod.rs b/src/libterm/terminfo/mod.rs
new file mode 100644 (file)
index 0000000..439297d
--- /dev/null
@@ -0,0 +1,31 @@
+// 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;
diff --git a/src/libterm/terminfo/parm.rs b/src/libterm/terminfo/parm.rs
new file mode 100644 (file)
index 0000000..ff8855c
--- /dev/null
@@ -0,0 +1,684 @@
+// 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()));
+    }
+}
diff --git a/src/libterm/terminfo/parser/compiled.rs b/src/libterm/terminfo/parser/compiled.rs
new file mode 100644 (file)
index 0000000..b0104fa
--- /dev/null
@@ -0,0 +1,352 @@
+// 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);
+    }
+}
diff --git a/src/libterm/terminfo/searcher.rs b/src/libterm/terminfo/searcher.rs
new file mode 100644 (file)
index 0000000..8cbb090
--- /dev/null
@@ -0,0 +1,112 @@
+// 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());
+}