extra::term: better error handling and win32 compat

This commit is contained in:
Corey Richardson 2013-05-30 19:14:40 -04:00
parent 100ee84097
commit 5311d59023
7 changed files with 99 additions and 42 deletions

View File

@ -43,12 +43,20 @@ pub static color_bright_magenta: u8 = 13u8;
pub static color_bright_cyan: u8 = 14u8;
pub static color_bright_white: u8 = 15u8;
#[cfg(not(target_os = "win32"))]
pub struct Terminal {
color_supported: bool,
priv out: @io::Writer,
priv ti: ~TermInfo
}
#[cfg(target_os = "win32")]
pub struct Terminal {
color_supported: bool,
priv out: @io::Writer,
}
#[cfg(not(target_os = "win32"))]
pub impl Terminal {
pub fn new(out: @io::Writer) -> Result<Terminal, ~str> {
let term = os::getenv("TERM");
@ -74,19 +82,50 @@ pub impl Terminal {
}
fn fg(&self, color: u8) {
if self.color_supported {
self.out.write(expand(*self.ti.strings.find_equiv(&("setaf")).unwrap(),
[Number(color as int)], [], []));
let s = expand(*self.ti.strings.find_equiv(&("setaf")).unwrap(),
[Number(color as int)], [], []);
if s.is_ok() {
self.out.write(s.get());
} else {
warn!(s.get_err());
}
}
}
fn bg(&self, color: u8) {
if self.color_supported {
self.out.write(expand(*self.ti.strings.find_equiv(&("setab")).unwrap(),
[Number(color as int)], [], []));
let s = expand(*self.ti.strings.find_equiv(&("setab")).unwrap(),
[Number(color as int)], [], []);
if s.is_ok() {
self.out.write(s.get());
} else {
warn!(s.get_err());
}
}
}
fn reset(&self) {
if self.color_supported {
self.out.write(expand(*self.ti.strings.find_equiv(&("op")).unwrap(), [], [], []));
let s = expand(*self.ti.strings.find_equiv(&("op")).unwrap(), [], [], []);
if s.is_ok() {
self.out.write(s.get());
} else {
warn!(s.get_err());
}
}
}
}
#[cfg(target_os = "win32")]
pub impl Terminal {
pub fn new(out: @io::Writer) -> Result<Terminal, ~str> {
return Ok(Terminal {out: out, color_supported: false});
}
fn fg(&self, color: u8) {
}
fn bg(&self, color: u8) {
}
fn reset(&self) {
}
}

View File

@ -27,13 +27,27 @@ enum States {
IfBody
}
/// Types of parameters a capability can use
pub enum Param {
String(~str),
Char(char),
Number(int)
}
pub fn expand(cap: &[u8], params: &mut [Param], sta: &mut [Param], dyn: &mut [Param]) -> ~[u8] {
/**
Expand a parameterized capability
# Arguments
* `cap` - string to expand
* `params` - vector of params for %p1 etc
* `sta` - vector of params corresponding to static variables
* `dyn` - vector of params corresponding to stativ variables
To be compatible with ncurses, `sta` and `dyn` should be the same between calls to `expand` for
multiple capabilities for the same terminal.
*/
pub fn expand(cap: &[u8], params: &mut [Param], sta: &mut [Param], dyn: &mut [Param])
-> Result<~[u8], ~str> {
assert!(cap.len() != 0, "expanding an empty capability makes no sense");
assert!(params.len() <= 9, "only 9 parameters are supported by capability strings");
@ -68,15 +82,15 @@ pub fn expand(cap: &[u8], params: &mut [Param], sta: &mut [Param], dyn: &mut [Pa
'%' => { output.push(cap[i]); state = Nothing },
'c' => match stack.pop() {
Char(c) => output.push(c as u8),
_ => fail!("a non-char was used with %c")
_ => return Err(~"a non-char was used with %c")
},
's' => match stack.pop() {
String(s) => output.push_all(s.to_bytes()),
_ => fail!("a non-str was used with %s")
_ => return Err(~"a non-str was used with %s")
},
'd' => match stack.pop() {
Number(x) => output.push_all(x.to_str().to_bytes()),
_ => fail!("a non-number was used with %d")
_ => return Err(~"a non-number was used with %d")
},
'p' => state = PushParam,
'P' => state = SetVar,
@ -85,52 +99,52 @@ pub fn expand(cap: &[u8], params: &mut [Param], sta: &mut [Param], dyn: &mut [Pa
'{' => state = IntConstant,
'l' => match stack.pop() {
String(s) => stack.push(Number(s.len() as int)),
_ => fail!("a non-str was used with %l")
_ => return Err(~"a non-str was used with %l")
},
'+' => match (stack.pop(), stack.pop()) {
(Number(x), Number(y)) => stack.push(Number(x + y)),
(_, _) => fail!("non-numbers on stack with +")
(_, _) => return Err(~"non-numbers on stack with +")
},
'-' => match (stack.pop(), stack.pop()) {
(Number(x), Number(y)) => stack.push(Number(x - y)),
(_, _) => fail!("non-numbers on stack with -")
(_, _) => return Err(~"non-numbers on stack with -")
},
'*' => match (stack.pop(), stack.pop()) {
(Number(x), Number(y)) => stack.push(Number(x * y)),
(_, _) => fail!("non-numbers on stack with *")
(_, _) => return Err(~"non-numbers on stack with *")
},
'/' => match (stack.pop(), stack.pop()) {
(Number(x), Number(y)) => stack.push(Number(x / y)),
(_, _) => fail!("non-numbers on stack with /")
(_, _) => return Err(~"non-numbers on stack with /")
},
'm' => match (stack.pop(), stack.pop()) {
(Number(x), Number(y)) => stack.push(Number(x % y)),
(_, _) => fail!("non-numbers on stack with %")
(_, _) => return Err(~"non-numbers on stack with %")
},
'&' => match (stack.pop(), stack.pop()) {
(Number(x), Number(y)) => stack.push(Number(x & y)),
(_, _) => fail!("non-numbers on stack with &")
(_, _) => return Err(~"non-numbers on stack with &")
},
'|' => match (stack.pop(), stack.pop()) {
(Number(x), Number(y)) => stack.push(Number(x | y)),
(_, _) => fail!("non-numbers on stack with |")
(_, _) => return Err(~"non-numbers on stack with |")
},
'A' => fail!("logical operations unimplemented"),
'O' => fail!("logical operations unimplemented"),
'!' => fail!("logical operations unimplemented"),
'A' => return Err(~"logical operations unimplemented"),
'O' => return Err(~"logical operations unimplemented"),
'!' => return Err(~"logical operations unimplemented"),
'~' => match stack.pop() {
Number(x) => stack.push(Number(!x)),
_ => fail!("non-number on stack with %~")
_ => return Err(~"non-number on stack with %~")
},
'i' => match (copy params[0], copy params[1]) {
(Number(x), Number(y)) => {
params[0] = Number(x + 1);
params[1] = Number(y + 1);
},
(_, _) => fail!("first two params not numbers with %i")
(_, _) => return Err(~"first two params not numbers with %i")
},
'?' => state = fail!("if expressions unimplemented"),
_ => fail!("unrecognized format option %c", cur)
'?' => state = return Err(fmt!("if expressions unimplemented (%?)", cap)),
_ => return Err(fmt!("unrecognized format option %c", cur))
}
},
PushParam => {
@ -145,7 +159,7 @@ pub fn expand(cap: &[u8], params: &mut [Param], sta: &mut [Param], dyn: &mut [Pa
let idx = (cur as u8) - ('a' as u8);
dyn[idx] = stack.pop();
} else {
fail!("bad variable name in %P");
return Err(~"bad variable name in %P");
}
},
GetVar => {
@ -156,7 +170,7 @@ pub fn expand(cap: &[u8], params: &mut [Param], sta: &mut [Param], dyn: &mut [Pa
let idx = (cur as u8) - ('a' as u8);
stack.push(copy dyn[idx]);
} else {
fail!("bad variable name in %g");
return Err(~"bad variable name in %g");
}
},
CharConstant => {
@ -174,14 +188,14 @@ pub fn expand(cap: &[u8], params: &mut [Param], sta: &mut [Param], dyn: &mut [Pa
intstate.push(cur as u8);
old_state = Nothing;
}
_ => fail!("unimplemented state")
_ => return Err(~"unimplemented state")
}
if state == old_state {
state = Nothing;
}
i += 1;
}
output
Ok(output)
}
#[cfg(test)]

View File

@ -1,6 +1,4 @@
/// ncurses-compatible compiled terminfo format parsing (term(5))
///
/// does *not* handle obsolete termcap capabilities!
use core::prelude::*;
@ -150,6 +148,7 @@ pub static stringnames: &'static[&'static str] = &'static[ "cbt", "_", "cr", "cs
"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: @Reader, longnames: bool) -> Result<~TermInfo, ~str> {
let bnames, snames, nnames;

View File

@ -12,10 +12,10 @@
/// Does not support hashed database, only filesystem!
use core::prelude::*;
use core::{os, str, vec};
use core::{os, str};
use core::os::getenv;
use core::io::{file_reader, Reader};
use path = core::path::PosixPath;
use path = core::path::Path;
/// Return path to database entry for `term`
pub fn get_dbpath_for_term(term: &str) -> Option<~path> {

View File

@ -10,10 +10,15 @@
use core::hashmap::HashMap;
/// A parsed terminfo entry.
pub struct TermInfo {
/// Names for the terminal
names: ~[~str],
/// Map of capability name to boolean value
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]>
}

View File

@ -280,7 +280,7 @@ pub fn need_dir(s: &Path) {
fn pretty_message<'a>(msg: &'a str, prefix: &'a str, color: u8, out: @io::Writer) {
let term = term::Terminal::new(out);
match term {
Ok(ref t) if t.color_supported => {
Ok(ref t) => {
t.fg(color);
out.write_str(prefix);
t.reset();

View File

@ -191,7 +191,7 @@ fn diagnosticcolor(lvl: level) -> u8 {
}
fn print_diagnostic(topic: &str, lvl: level, msg: &str) {
let term = term::Terminal::new(io::stderr());
let t = term::Terminal::new(io::stderr());
let stderr = io::stderr();
@ -199,18 +199,18 @@ fn print_diagnostic(topic: &str, lvl: level, msg: &str) {
stderr.write_str(fmt!("%s ", topic));
}
match term {
Ok(t) => {
match t {
Ok(term) => {
if stderr.get_type() == io::Screen {
t.fg(diagnosticcolor(lvl));
term.fg(diagnosticcolor(lvl));
stderr.write_str(fmt!("%s: ", diagnosticstr(lvl)));
t.reset();
term.reset();
stderr.write_str(fmt!("%s\n", msg));
} else {
stderr.write_str(fmt!("%s: %s\n", diagnosticstr(lvl), msg));
}
}
Err(_) => {
stderr.write_str(fmt!("%s: %s\n", diagnosticstr(lvl), msg));
}
},
_ => stderr.write_str(fmt!("%s: %s\n", diagnosticstr(lvl), msg))
}
}