about summary refs log tree commit diff
path: root/src/libterm/terminfo
diff options
context:
space:
mode:
authorCorey Richardson <corey@octayn.net>2014-04-08 11:18:10 -0400
committerCorey Richardson <corey@octayn.net>2014-05-16 09:57:32 -0700
commitf923b93694706730dd8395dec63e949ba441e0d9 (patch)
treee9c76d496c4431f5fe7380887913c7ca71a1c1e9 /src/libterm/terminfo
parente30198d9d4d4d016949625062bde002bc33aa574 (diff)
term: add docs and windows support
Closes #2807
Diffstat (limited to 'src/libterm/terminfo')
-rw-r--r--src/libterm/terminfo/mod.rs186
1 files changed, 186 insertions, 0 deletions
diff --git a/src/libterm/terminfo/mod.rs b/src/libterm/terminfo/mod.rs
index 46dd3978531..93a7657fae9 100644
--- a/src/libterm/terminfo/mod.rs
+++ b/src/libterm/terminfo/mod.rs
@@ -11,6 +11,16 @@
 //! Terminfo database interface.
 
 use collections::HashMap;
+use std::io::IoResult;
+use std::os;
+
+use attr;
+use color;
+use Terminal;
+use self::searcher::open;
+use self::parser::compiled::{parse, msys_terminfo};
+use self::parm::{expand, Number, Variables};
+
 
 /// A parsed terminfo database entry.
 pub struct TermInfo {
@@ -32,3 +42,179 @@ pub mod parser {
     pub mod compiled;
 }
 pub mod parm;
+
+
+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"
+    }
+}
+
+/// A Terminal that knows how many colors it supports, with a reference to its
+/// parsed Terminfo database record.
+pub struct TerminfoTerminal<T> {
+    num_colors: u16,
+    out: T,
+    ti: ~TermInfo
+}
+
+impl<T: Writer> Terminal<T> for TerminfoTerminal<T> {
+    fn new(out: T) -> Option<TerminfoTerminal<T>> {
+        let term = match os::getenv("TERM") {
+            Some(t) => t,
+            None => {
+                debug!("TERM environment variable not defined");
+                return None;
+            }
+        };
+
+        let entry = open(term);
+        if entry.is_err() {
+            if os::getenv("MSYSCON").map_or(false, |s| "mintty.exe" == s) {
+                // msys terminal
+                return Some(TerminfoTerminal {out: out, ti: msys_terminfo(), num_colors: 8});
+            }
+            debug!("error finding terminfo entry: {}", entry.unwrap_err());
+            return None;
+        }
+
+        let mut file = entry.unwrap();
+        let ti = parse(&mut file, false);
+        if ti.is_err() {
+            debug!("error parsing terminfo entry: {}", ti.unwrap_err());
+            return None;
+        }
+
+        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 Some(TerminfoTerminal {out: out, ti: inf, num_colors: nc});
+    }
+
+    fn fg(&mut self, color: color::Color) -> IoResult<bool> {
+        let color = self.dim_if_necessary(color);
+        if self.num_colors > color {
+            let s = expand(self.ti
+                               .strings
+                               .find_equiv(&("setaf"))
+                               .unwrap()
+                               .as_slice(),
+                           [Number(color as int)], &mut Variables::new());
+            if s.is_ok() {
+                try!(self.out.write(s.unwrap().as_slice()));
+                return Ok(true)
+            }
+        }
+        Ok(false)
+    }
+
+    fn bg(&mut self, color: color::Color) -> IoResult<bool> {
+        let color = self.dim_if_necessary(color);
+        if self.num_colors > color {
+            let s = expand(self.ti
+                               .strings
+                               .find_equiv(&("setab"))
+                               .unwrap()
+                               .as_slice(),
+                           [Number(color as int)], &mut Variables::new());
+            if s.is_ok() {
+                try!(self.out.write(s.unwrap().as_slice()));
+                return Ok(true)
+            }
+        }
+        Ok(false)
+    }
+
+    fn attr(&mut self, attr: attr::Attr) -> IoResult<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().as_slice(),
+                                   [],
+                                   &mut Variables::new());
+                    if s.is_ok() {
+                        try!(self.out.write(s.unwrap().as_slice()));
+                        return Ok(true)
+                    }
+                }
+                Ok(false)
+            }
+        }
+    }
+
+    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()
+            }
+        }
+    }
+
+    fn reset(&mut self) -> IoResult<()> {
+        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.as_slice(), [], &mut Variables::new())
+        });
+        if s.is_ok() {
+            return self.out.write(s.unwrap().as_slice())
+        }
+        Ok(())
+    }
+
+    fn unwrap(self) -> T { self.out }
+
+    fn get_ref<'a>(&'a self) -> &'a T { &self.out }
+
+    fn get_mut<'a>(&'a mut self) -> &'a mut T { &mut self.out }
+}
+
+impl<T: Writer> TerminfoTerminal<T> {
+    fn dim_if_necessary(&self, color: color::Color) -> color::Color {
+        if color >= self.num_colors && color >= 8 && color < 16 {
+            color-8
+        } else { color }
+    }
+}
+
+
+impl<T: Writer> Writer for TerminfoTerminal<T> {
+    fn write(&mut self, buf: &[u8]) -> IoResult<()> {
+        self.out.write(buf)
+    }
+
+    fn flush(&mut self) -> IoResult<()> {
+        self.out.flush()
+    }
+}
+