Auto merge of #28907 - SingingTree:rustfmt_libterm, r=nrc
Hey hey, This is the result of running rustfmt over the libterm module. The first commit reflects the unaltered changes from rustfmt, and the commit message contains some notes on areas where I thought rustfmt had behaved strangely. The second commit attempts to fix the strange areas from the first commit. Clarification edit: there are still some areas where I think rustfmt has made changes which may merit discussion (one is noted in the comments below). My second commit only undoes the changes that I figured would not warrant discussion (based on my opinion of the style, which is of course subjective). r? @nrc
This commit is contained in:
commit
2ba44607fe
src/libterm
@ -63,8 +63,12 @@
|
||||
#![feature(str_char)]
|
||||
#![feature(vec_push_all)]
|
||||
#![cfg_attr(windows, feature(libc))]
|
||||
// Handle rustfmt skips
|
||||
#![feature(custom_attribute)]
|
||||
#![allow(unused_attributes)]
|
||||
|
||||
#[macro_use] extern crate log;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
pub use terminfo::TerminfoTerminal;
|
||||
#[cfg(windows)]
|
||||
@ -100,26 +104,18 @@ impl Write for WriterWrapper {
|
||||
/// Return a Terminal wrapping stdout, or None if a terminal couldn't be
|
||||
/// opened.
|
||||
pub fn stdout() -> Option<Box<Terminal<WriterWrapper> + Send>> {
|
||||
TerminfoTerminal::new(WriterWrapper {
|
||||
wrapped: box std::io::stdout(),
|
||||
})
|
||||
TerminfoTerminal::new(WriterWrapper { wrapped: box std::io::stdout() })
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
/// Return a Terminal wrapping stdout, or None if a terminal couldn't be
|
||||
/// opened.
|
||||
pub fn stdout() -> Option<Box<Terminal<WriterWrapper> + Send>> {
|
||||
let ti = TerminfoTerminal::new(WriterWrapper {
|
||||
wrapped: box std::io::stdout(),
|
||||
});
|
||||
let ti = TerminfoTerminal::new(WriterWrapper { wrapped: box std::io::stdout() });
|
||||
|
||||
match ti {
|
||||
Some(t) => Some(t),
|
||||
None => {
|
||||
WinConsole::new(WriterWrapper {
|
||||
wrapped: box std::io::stdout(),
|
||||
})
|
||||
}
|
||||
None => WinConsole::new(WriterWrapper { wrapped: box std::io::stdout() }),
|
||||
}
|
||||
}
|
||||
|
||||
@ -127,26 +123,18 @@ pub fn stdout() -> Option<Box<Terminal<WriterWrapper> + Send>> {
|
||||
/// Return a Terminal wrapping stderr, or None if a terminal couldn't be
|
||||
/// opened.
|
||||
pub fn stderr() -> Option<Box<Terminal<WriterWrapper> + Send>> {
|
||||
TerminfoTerminal::new(WriterWrapper {
|
||||
wrapped: box std::io::stderr(),
|
||||
})
|
||||
TerminfoTerminal::new(WriterWrapper { wrapped: box std::io::stderr() })
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
/// Return a Terminal wrapping stderr, or None if a terminal couldn't be
|
||||
/// opened.
|
||||
pub fn stderr() -> Option<Box<Terminal<WriterWrapper> + Send>> {
|
||||
let ti = TerminfoTerminal::new(WriterWrapper {
|
||||
wrapped: box std::io::stderr(),
|
||||
});
|
||||
let ti = TerminfoTerminal::new(WriterWrapper { wrapped: box std::io::stderr() });
|
||||
|
||||
match ti {
|
||||
Some(t) => Some(t),
|
||||
None => {
|
||||
WinConsole::new(WriterWrapper {
|
||||
wrapped: box std::io::stderr(),
|
||||
})
|
||||
}
|
||||
None => WinConsole::new(WriterWrapper { wrapped: box std::io::stderr() }),
|
||||
}
|
||||
}
|
||||
|
||||
@ -157,23 +145,23 @@ pub mod color {
|
||||
/// Number for a terminal color
|
||||
pub type Color = u16;
|
||||
|
||||
pub const BLACK: Color = 0;
|
||||
pub const RED: Color = 1;
|
||||
pub const GREEN: Color = 2;
|
||||
pub const YELLOW: Color = 3;
|
||||
pub const BLUE: Color = 4;
|
||||
pub const BLACK: Color = 0;
|
||||
pub const RED: Color = 1;
|
||||
pub const GREEN: Color = 2;
|
||||
pub const YELLOW: Color = 3;
|
||||
pub const BLUE: Color = 4;
|
||||
pub const MAGENTA: Color = 5;
|
||||
pub const CYAN: Color = 6;
|
||||
pub const WHITE: Color = 7;
|
||||
pub const CYAN: Color = 6;
|
||||
pub const WHITE: Color = 7;
|
||||
|
||||
pub const BRIGHT_BLACK: Color = 8;
|
||||
pub const BRIGHT_RED: Color = 9;
|
||||
pub const BRIGHT_GREEN: Color = 10;
|
||||
pub const BRIGHT_YELLOW: Color = 11;
|
||||
pub const BRIGHT_BLUE: Color = 12;
|
||||
pub const BRIGHT_BLACK: Color = 8;
|
||||
pub const BRIGHT_RED: Color = 9;
|
||||
pub const BRIGHT_GREEN: Color = 10;
|
||||
pub const BRIGHT_YELLOW: Color = 11;
|
||||
pub const BRIGHT_BLUE: Color = 12;
|
||||
pub const BRIGHT_MAGENTA: Color = 13;
|
||||
pub const BRIGHT_CYAN: Color = 14;
|
||||
pub const BRIGHT_WHITE: Color = 15;
|
||||
pub const BRIGHT_CYAN: Color = 14;
|
||||
pub const BRIGHT_WHITE: Color = 15;
|
||||
}
|
||||
|
||||
/// Terminal attributes
|
||||
@ -206,7 +194,7 @@ pub mod attr {
|
||||
/// Convenience attribute to set the foreground color
|
||||
ForegroundColor(super::color::Color),
|
||||
/// Convenience attribute to set the background color
|
||||
BackgroundColor(super::color::Color)
|
||||
BackgroundColor(super::color::Color),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,13 +28,13 @@ use self::parm::{expand, Number, Variables};
|
||||
#[derive(Debug)]
|
||||
pub struct TermInfo {
|
||||
/// Names for the terminal
|
||||
pub names: Vec<String> ,
|
||||
pub names: Vec<String>,
|
||||
/// Map of capability name to boolean value
|
||||
pub bools: HashMap<String, bool>,
|
||||
/// Map of capability name to numeric value
|
||||
pub numbers: HashMap<String, u16>,
|
||||
/// Map of capability name to raw (unexpanded) string
|
||||
pub strings: HashMap<String, Vec<u8> >
|
||||
pub strings: HashMap<String, Vec<u8>>,
|
||||
}
|
||||
|
||||
pub mod searcher;
|
||||
@ -49,19 +49,19 @@ 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::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"
|
||||
attr::BackgroundColor(_) => "setab",
|
||||
}
|
||||
}
|
||||
|
||||
@ -70,7 +70,7 @@ fn cap_for_attr(attr: attr::Attr) -> &'static str {
|
||||
pub struct TerminfoTerminal<T> {
|
||||
num_colors: u16,
|
||||
out: T,
|
||||
ti: Box<TermInfo>
|
||||
ti: Box<TermInfo>,
|
||||
}
|
||||
|
||||
impl<T: Write+Send+'static> Terminal<T> for TerminfoTerminal<T> {
|
||||
@ -80,12 +80,12 @@ impl<T: Write+Send+'static> Terminal<T> for TerminfoTerminal<T> {
|
||||
let s = expand(self.ti
|
||||
.strings
|
||||
.get("setaf")
|
||||
.unwrap()
|
||||
,
|
||||
&[Number(color as isize)], &mut Variables::new());
|
||||
.unwrap(),
|
||||
&[Number(color as isize)],
|
||||
&mut Variables::new());
|
||||
if s.is_ok() {
|
||||
try!(self.out.write_all(&s.unwrap()));
|
||||
return Ok(true)
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
Ok(false)
|
||||
@ -97,12 +97,12 @@ impl<T: Write+Send+'static> Terminal<T> for TerminfoTerminal<T> {
|
||||
let s = expand(self.ti
|
||||
.strings
|
||||
.get("setab")
|
||||
.unwrap()
|
||||
,
|
||||
&[Number(color as isize)], &mut Variables::new());
|
||||
.unwrap(),
|
||||
&[Number(color as isize)],
|
||||
&mut Variables::new());
|
||||
if s.is_ok() {
|
||||
try!(self.out.write_all(&s.unwrap()));
|
||||
return Ok(true)
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
Ok(false)
|
||||
@ -116,12 +116,10 @@ impl<T: Write+Send+'static> Terminal<T> for TerminfoTerminal<T> {
|
||||
let cap = cap_for_attr(attr);
|
||||
let parm = self.ti.strings.get(cap);
|
||||
if parm.is_some() {
|
||||
let s = expand(parm.unwrap(),
|
||||
&[],
|
||||
&mut Variables::new());
|
||||
let s = expand(parm.unwrap(), &[], &mut Variables::new());
|
||||
if s.is_ok() {
|
||||
try!(self.out.write_all(&s.unwrap()));
|
||||
return Ok(true)
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
Ok(false)
|
||||
@ -131,9 +129,7 @@ impl<T: Write+Send+'static> Terminal<T> for TerminfoTerminal<T> {
|
||||
|
||||
fn supports_attr(&self, attr: attr::Attr) -> bool {
|
||||
match attr {
|
||||
attr::ForegroundColor(_) | attr::BackgroundColor(_) => {
|
||||
self.num_colors > 0
|
||||
}
|
||||
attr::ForegroundColor(_) | attr::BackgroundColor(_) => self.num_colors > 0,
|
||||
_ => {
|
||||
let cap = cap_for_attr(attr);
|
||||
self.ti.strings.get(cap).is_some()
|
||||
@ -151,28 +147,33 @@ impl<T: Write+Send+'static> Terminal<T> for TerminfoTerminal<T> {
|
||||
cap = self.ti.strings.get("op");
|
||||
}
|
||||
}
|
||||
let s = cap.map_or(Err("can't find terminfo capability `sgr0`".to_owned()), |op| {
|
||||
expand(op, &[], &mut Variables::new())
|
||||
});
|
||||
let s = cap.map_or(Err("can't find terminfo capability `sgr0`".to_owned()),
|
||||
|op| expand(op, &[], &mut Variables::new()));
|
||||
if s.is_ok() {
|
||||
return self.out.write_all(&s.unwrap())
|
||||
return self.out.write_all(&s.unwrap());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_ref<'a>(&'a self) -> &'a 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 }
|
||||
fn get_mut<'a>(&'a mut self) -> &'a mut T {
|
||||
&mut self.out
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Write+Send+'static> UnwrappableTerminal<T> for TerminfoTerminal<T> {
|
||||
fn unwrap(self) -> T { self.out }
|
||||
fn unwrap(self) -> T {
|
||||
self.out
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Write+Send+'static> TerminfoTerminal<T> {
|
||||
/// Returns `None` whenever the terminal cannot be created for some
|
||||
/// reason.
|
||||
pub fn new(out: T) -> Option<Box<Terminal<T>+Send+'static>> {
|
||||
pub fn new(out: T) -> Option<Box<Terminal<T> + Send + 'static>> {
|
||||
let term = match env::var("TERM") {
|
||||
Ok(t) => t,
|
||||
Err(..) => {
|
||||
@ -183,20 +184,22 @@ impl<T: Write+Send+'static> TerminfoTerminal<T> {
|
||||
|
||||
let mut file = match open(&term[..]) {
|
||||
Ok(f) => f,
|
||||
Err(err) => return match env::var("MSYSCON") {
|
||||
Ok(ref val) if &val[..] == "mintty.exe" => {
|
||||
// msys terminal
|
||||
Some(box TerminfoTerminal{
|
||||
out: out,
|
||||
ti: msys_terminfo(),
|
||||
num_colors: 8,
|
||||
})
|
||||
},
|
||||
_ => {
|
||||
debug!("error finding terminfo entry: {:?}", err);
|
||||
None
|
||||
},
|
||||
},
|
||||
Err(err) => {
|
||||
return match env::var("MSYSCON") {
|
||||
Ok(ref val) if &val[..] == "mintty.exe" => {
|
||||
// msys terminal
|
||||
Some(box TerminfoTerminal {
|
||||
out: out,
|
||||
ti: msys_terminfo(),
|
||||
num_colors: 8,
|
||||
})
|
||||
}
|
||||
_ => {
|
||||
debug!("error finding terminfo entry: {:?}", err);
|
||||
None
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
let ti = parse(&mut file, false);
|
||||
@ -206,20 +209,25 @@ impl<T: Write+Send+'static> TerminfoTerminal<T> {
|
||||
}
|
||||
|
||||
let inf = ti.unwrap();
|
||||
let nc = if inf.strings.get("setaf").is_some()
|
||||
&& inf.strings.get("setab").is_some() {
|
||||
inf.numbers.get("colors").map_or(0, |&n| n)
|
||||
} else { 0 };
|
||||
let nc = if inf.strings.get("setaf").is_some() && inf.strings.get("setab").is_some() {
|
||||
inf.numbers.get("colors").map_or(0, |&n| n)
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
Some(box TerminfoTerminal {out: out,
|
||||
ti: inf,
|
||||
num_colors: nc})
|
||||
Some(box TerminfoTerminal {
|
||||
out: out,
|
||||
ti: inf,
|
||||
num_colors: nc,
|
||||
})
|
||||
}
|
||||
|
||||
fn dim_if_necessary(&self, color: color::Color) -> color::Color {
|
||||
if color >= self.num_colors && color >= 8 && color < 16 {
|
||||
color-8
|
||||
} else { color }
|
||||
color - 8
|
||||
} else {
|
||||
color
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -32,14 +32,14 @@ enum States {
|
||||
SeekIfElse(isize),
|
||||
SeekIfElsePercent(isize),
|
||||
SeekIfEnd(isize),
|
||||
SeekIfEndPercent(isize)
|
||||
SeekIfEndPercent(isize),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
enum FormatState {
|
||||
FormatStateFlags,
|
||||
FormatStateWidth,
|
||||
FormatStatePrecision
|
||||
FormatStatePrecision,
|
||||
}
|
||||
|
||||
/// Types of parameters a capability can use
|
||||
@ -47,7 +47,7 @@ enum FormatState {
|
||||
#[derive(Clone)]
|
||||
pub enum Param {
|
||||
Words(String),
|
||||
Number(isize)
|
||||
Number(isize),
|
||||
}
|
||||
|
||||
/// Container for static and dynamic variable arrays
|
||||
@ -55,29 +55,21 @@ pub struct Variables {
|
||||
/// Static variables A-Z
|
||||
sta: [Param; 26],
|
||||
/// Dynamic variables a-z
|
||||
dyn: [Param; 26]
|
||||
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),
|
||||
],
|
||||
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)],
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -91,8 +83,7 @@ impl Variables {
|
||||
///
|
||||
/// 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<Vec<u8> , String> {
|
||||
pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables) -> Result<Vec<u8>, String> {
|
||||
let mut state = Nothing;
|
||||
|
||||
// expanded cap will only rarely be larger than the cap itself
|
||||
@ -101,10 +92,8 @@ pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables)
|
||||
let mut stack: Vec<Param> = Vec::new();
|
||||
|
||||
// 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),
|
||||
];
|
||||
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.iter_mut().zip(params) {
|
||||
*dst = (*src).clone();
|
||||
}
|
||||
@ -119,147 +108,241 @@ pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables)
|
||||
} else {
|
||||
output.push(c);
|
||||
}
|
||||
},
|
||||
}
|
||||
Percent => {
|
||||
match cur {
|
||||
'%' => { output.push(c); state = Nothing },
|
||||
'c' => if !stack.is_empty() {
|
||||
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
|
||||
})
|
||||
'%' => {
|
||||
output.push(c);
|
||||
state = Nothing
|
||||
}
|
||||
'c' => {
|
||||
if !stack.is_empty() {
|
||||
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".to_owned()),
|
||||
}
|
||||
_ => return Err("a non-char was used with %c".to_owned())
|
||||
} else {
|
||||
return Err("stack is empty".to_owned());
|
||||
}
|
||||
} else { return Err("stack is empty".to_owned()) },
|
||||
}
|
||||
'p' => state = PushParam,
|
||||
'P' => state = SetVar,
|
||||
'g' => state = GetVar,
|
||||
'\'' => state = CharConstant,
|
||||
'{' => state = IntConstant(0),
|
||||
'l' => if !stack.is_empty() {
|
||||
match stack.pop().unwrap() {
|
||||
Words(s) => stack.push(Number(s.len() as isize)),
|
||||
_ => return Err("a non-str was used with %l".to_owned())
|
||||
'l' => {
|
||||
if !stack.is_empty() {
|
||||
match stack.pop().unwrap() {
|
||||
Words(s) => stack.push(Number(s.len() as isize)),
|
||||
_ => return Err("a non-str was used with %l".to_owned()),
|
||||
}
|
||||
} else {
|
||||
return Err("stack is empty".to_owned());
|
||||
}
|
||||
} else { return Err("stack is empty".to_owned()) },
|
||||
'+' => 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 +".to_owned())
|
||||
}
|
||||
'+' => {
|
||||
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 +".to_owned()),
|
||||
}
|
||||
} else {
|
||||
return Err("stack is empty".to_owned());
|
||||
}
|
||||
} else { return Err("stack is empty".to_owned()) },
|
||||
'-' => 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 -".to_owned())
|
||||
}
|
||||
'-' => {
|
||||
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 -".to_owned()),
|
||||
}
|
||||
} else {
|
||||
return Err("stack is empty".to_owned());
|
||||
}
|
||||
} else { return Err("stack is empty".to_owned()) },
|
||||
'*' => 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 *".to_owned())
|
||||
}
|
||||
'*' => {
|
||||
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 *".to_owned()),
|
||||
}
|
||||
} else {
|
||||
return Err("stack is empty".to_owned());
|
||||
}
|
||||
} else { return Err("stack is empty".to_owned()) },
|
||||
'/' => 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 /".to_owned())
|
||||
}
|
||||
'/' => {
|
||||
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 /".to_owned()),
|
||||
}
|
||||
} else {
|
||||
return Err("stack is empty".to_owned());
|
||||
}
|
||||
} else { return Err("stack is empty".to_owned()) },
|
||||
'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 %".to_owned())
|
||||
}
|
||||
'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 %".to_owned()),
|
||||
}
|
||||
} else {
|
||||
return Err("stack is empty".to_owned());
|
||||
}
|
||||
} else { return Err("stack is empty".to_owned()) },
|
||||
'&' => 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 &".to_owned())
|
||||
}
|
||||
'&' => {
|
||||
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 &".to_owned()),
|
||||
}
|
||||
} else {
|
||||
return Err("stack is empty".to_owned());
|
||||
}
|
||||
} else { return Err("stack is empty".to_owned()) },
|
||||
'|' => 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 |".to_owned())
|
||||
}
|
||||
'|' => {
|
||||
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 |".to_owned()),
|
||||
}
|
||||
} else {
|
||||
return Err("stack is empty".to_owned());
|
||||
}
|
||||
} else { return Err("stack is empty".to_owned()) },
|
||||
'^' => 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 ^".to_owned())
|
||||
}
|
||||
'^' => {
|
||||
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 ^".to_owned()),
|
||||
}
|
||||
} else {
|
||||
return Err("stack is empty".to_owned());
|
||||
}
|
||||
} else { return Err("stack is empty".to_owned()) },
|
||||
'=' => 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 =".to_owned())
|
||||
}
|
||||
'=' => {
|
||||
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 =".to_owned()),
|
||||
}
|
||||
} else {
|
||||
return Err("stack is empty".to_owned());
|
||||
}
|
||||
} else { return Err("stack is empty".to_owned()) },
|
||||
'>' => 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 >".to_owned())
|
||||
}
|
||||
'>' => {
|
||||
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 >".to_owned()),
|
||||
}
|
||||
} else {
|
||||
return Err("stack is empty".to_owned());
|
||||
}
|
||||
} else { return Err("stack is empty".to_owned()) },
|
||||
'<' => 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 <".to_owned())
|
||||
}
|
||||
'<' => {
|
||||
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 <".to_owned()),
|
||||
}
|
||||
} else {
|
||||
return Err("stack is empty".to_owned());
|
||||
}
|
||||
} else { return Err("stack is empty".to_owned()) },
|
||||
'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".to_owned())
|
||||
}
|
||||
'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".to_owned()),
|
||||
}
|
||||
} else {
|
||||
return Err("stack is empty".to_owned());
|
||||
}
|
||||
} else { return Err("stack is empty".to_owned()) },
|
||||
'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".to_owned())
|
||||
}
|
||||
'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".to_owned()),
|
||||
}
|
||||
} else {
|
||||
return Err("stack is empty".to_owned());
|
||||
}
|
||||
} else { return Err("stack is empty".to_owned()) },
|
||||
'!' => if !stack.is_empty() {
|
||||
match stack.pop().unwrap() {
|
||||
Number(0) => stack.push(Number(1)),
|
||||
Number(_) => stack.push(Number(0)),
|
||||
_ => return Err("non-number on stack with logical not".to_owned())
|
||||
}
|
||||
'!' => {
|
||||
if !stack.is_empty() {
|
||||
match stack.pop().unwrap() {
|
||||
Number(0) => stack.push(Number(1)),
|
||||
Number(_) => stack.push(Number(0)),
|
||||
_ => return Err("non-number on stack with logical not".to_owned()),
|
||||
}
|
||||
} else {
|
||||
return Err("stack is empty".to_owned());
|
||||
}
|
||||
} else { return Err("stack is empty".to_owned()) },
|
||||
'~' => if !stack.is_empty() {
|
||||
match stack.pop().unwrap() {
|
||||
Number(x) => stack.push(Number(!x)),
|
||||
_ => return Err("non-number on stack with %~".to_owned())
|
||||
}
|
||||
'~' => {
|
||||
if !stack.is_empty() {
|
||||
match stack.pop().unwrap() {
|
||||
Number(x) => stack.push(Number(!x)),
|
||||
_ => return Err("non-number on stack with %~".to_owned()),
|
||||
}
|
||||
} else {
|
||||
return Err("stack is empty".to_owned());
|
||||
}
|
||||
} else { return Err("stack is empty".to_owned()) },
|
||||
'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".to_owned())
|
||||
},
|
||||
}
|
||||
'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".to_owned()),
|
||||
}
|
||||
}
|
||||
|
||||
// printf-style support for %doxXs
|
||||
'd'|'o'|'x'|'X'|'s' => if !stack.is_empty() {
|
||||
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".to_owned()) },
|
||||
':'|'#'|' '|'.'|'0'...'9' => {
|
||||
'd' | 'o' | 'x' | 'X' | 's' => {
|
||||
if !stack.is_empty() {
|
||||
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".to_owned());
|
||||
}
|
||||
}
|
||||
':' | '#' | ' ' | '.' | '0'...'9' => {
|
||||
let mut flags = Flags::new();
|
||||
let mut fstate = FormatStateFlags;
|
||||
match cur {
|
||||
@ -271,51 +354,57 @@ pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables)
|
||||
flags.width = cur as usize - '0' as usize;
|
||||
fstate = FormatStateWidth;
|
||||
}
|
||||
_ => unreachable!()
|
||||
_ => unreachable!(),
|
||||
}
|
||||
state = FormatPattern(flags, fstate);
|
||||
}
|
||||
|
||||
// conditionals
|
||||
'?' => (),
|
||||
't' => if !stack.is_empty() {
|
||||
match stack.pop().unwrap() {
|
||||
Number(0) => state = SeekIfElse(0),
|
||||
Number(_) => (),
|
||||
_ => return Err("non-number on stack \
|
||||
with conditional".to_owned())
|
||||
't' => {
|
||||
if !stack.is_empty() {
|
||||
match stack.pop().unwrap() {
|
||||
Number(0) => state = SeekIfElse(0),
|
||||
Number(_) => (),
|
||||
_ => return Err("non-number on stack with conditional".to_owned()),
|
||||
}
|
||||
} else {
|
||||
return Err("stack is empty".to_owned());
|
||||
}
|
||||
} else { return Err("stack is empty".to_owned()) },
|
||||
}
|
||||
'e' => state = SeekIfEnd(0),
|
||||
';' => (),
|
||||
|
||||
_ => {
|
||||
return Err(format!("unrecognized format option {:?}", cur))
|
||||
}
|
||||
_ => return Err(format!("unrecognized format option {:?}", cur)),
|
||||
}
|
||||
},
|
||||
}
|
||||
PushParam => {
|
||||
// params are 1-indexed
|
||||
stack.push(mparams[match cur.to_digit(10) {
|
||||
Some(d) => d as usize - 1,
|
||||
None => return Err("bad param number".to_owned())
|
||||
}].clone());
|
||||
},
|
||||
Some(d) => d as usize - 1,
|
||||
None => return Err("bad param number".to_owned()),
|
||||
}]
|
||||
.clone());
|
||||
}
|
||||
SetVar => {
|
||||
if cur >= 'A' && cur <= 'Z' {
|
||||
if !stack.is_empty() {
|
||||
let idx = (cur as u8) - b'A';
|
||||
vars.sta[idx as usize] = stack.pop().unwrap();
|
||||
} else { return Err("stack is empty".to_owned()) }
|
||||
} else {
|
||||
return Err("stack is empty".to_owned());
|
||||
}
|
||||
} else if cur >= 'a' && cur <= 'z' {
|
||||
if !stack.is_empty() {
|
||||
let idx = (cur as u8) - b'a';
|
||||
vars.dyn[idx as usize] = stack.pop().unwrap();
|
||||
} else { return Err("stack is empty".to_owned()) }
|
||||
} else {
|
||||
return Err("stack is empty".to_owned());
|
||||
}
|
||||
} else {
|
||||
return Err("bad variable name in %P".to_owned());
|
||||
}
|
||||
},
|
||||
}
|
||||
GetVar => {
|
||||
if cur >= 'A' && cur <= 'Z' {
|
||||
let idx = (cur as u8) - b'A';
|
||||
@ -326,16 +415,16 @@ pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables)
|
||||
} else {
|
||||
return Err("bad variable name in %g".to_owned());
|
||||
}
|
||||
},
|
||||
}
|
||||
CharConstant => {
|
||||
stack.push(Number(c as isize));
|
||||
state = CharClose;
|
||||
},
|
||||
}
|
||||
CharClose => {
|
||||
if cur != '\'' {
|
||||
return Err("malformed character constant".to_owned());
|
||||
}
|
||||
},
|
||||
}
|
||||
IntConstant(i) => {
|
||||
match cur {
|
||||
'}' => {
|
||||
@ -343,57 +432,67 @@ pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables)
|
||||
state = Nothing;
|
||||
}
|
||||
'0'...'9' => {
|
||||
state = IntConstant(i*10 + (cur as isize - '0' as isize));
|
||||
state = IntConstant(i * 10 + (cur as isize - '0' as isize));
|
||||
old_state = Nothing;
|
||||
}
|
||||
_ => return Err("bad isize constant".to_owned())
|
||||
_ => return Err("bad isize constant".to_owned()),
|
||||
}
|
||||
}
|
||||
FormatPattern(ref mut flags, ref mut fstate) => {
|
||||
old_state = Nothing;
|
||||
match (*fstate, cur) {
|
||||
(_,'d')|(_,'o')|(_,'x')|(_,'X')|(_,'s') => if !stack.is_empty() {
|
||||
let res = format(stack.pop().unwrap(), FormatOp::from_char(cur), *flags);
|
||||
if res.is_err() { return res }
|
||||
output.push_all(&res.unwrap());
|
||||
// will cause state to go to Nothing
|
||||
old_state = FormatPattern(*flags, *fstate);
|
||||
} else { return Err("stack is empty".to_owned()) },
|
||||
(FormatStateFlags,'#') => {
|
||||
(_, 'd') | (_, 'o') | (_, 'x') | (_, 'X') | (_, 's') => {
|
||||
if !stack.is_empty() {
|
||||
let res = format(stack.pop().unwrap(),
|
||||
FormatOp::from_char(cur),
|
||||
*flags);
|
||||
if res.is_err() {
|
||||
return res;
|
||||
}
|
||||
output.push_all(&res.unwrap());
|
||||
// will cause state to go to Nothing
|
||||
old_state = FormatPattern(*flags, *fstate);
|
||||
} else {
|
||||
return Err("stack is empty".to_owned());
|
||||
}
|
||||
}
|
||||
(FormatStateFlags, '#') => {
|
||||
flags.alternate = true;
|
||||
}
|
||||
(FormatStateFlags,'-') => {
|
||||
(FormatStateFlags, '-') => {
|
||||
flags.left = true;
|
||||
}
|
||||
(FormatStateFlags,'+') => {
|
||||
(FormatStateFlags, '+') => {
|
||||
flags.sign = true;
|
||||
}
|
||||
(FormatStateFlags,' ') => {
|
||||
(FormatStateFlags, ' ') => {
|
||||
flags.space = true;
|
||||
}
|
||||
(FormatStateFlags,'0'...'9') => {
|
||||
(FormatStateFlags, '0'...'9') => {
|
||||
flags.width = cur as usize - '0' as usize;
|
||||
*fstate = FormatStateWidth;
|
||||
}
|
||||
(FormatStateFlags,'.') => {
|
||||
(FormatStateFlags, '.') => {
|
||||
*fstate = FormatStatePrecision;
|
||||
}
|
||||
(FormatStateWidth,'0'...'9') => {
|
||||
(FormatStateWidth, '0'...'9') => {
|
||||
let old = flags.width;
|
||||
flags.width = flags.width * 10 + (cur as usize - '0' as usize);
|
||||
if flags.width < old { return Err("format width overflow".to_owned()) }
|
||||
if flags.width < old {
|
||||
return Err("format width overflow".to_owned());
|
||||
}
|
||||
}
|
||||
(FormatStateWidth,'.') => {
|
||||
(FormatStateWidth, '.') => {
|
||||
*fstate = FormatStatePrecision;
|
||||
}
|
||||
(FormatStatePrecision,'0'...'9') => {
|
||||
(FormatStatePrecision, '0'...'9') => {
|
||||
let old = flags.precision;
|
||||
flags.precision = flags.precision * 10 + (cur as usize - '0' as usize);
|
||||
if flags.precision < old {
|
||||
return Err("format precision overflow".to_owned())
|
||||
return Err("format precision overflow".to_owned());
|
||||
}
|
||||
}
|
||||
_ => return Err("invalid format specifier".to_owned())
|
||||
_ => return Err("invalid format specifier".to_owned()),
|
||||
}
|
||||
}
|
||||
SeekIfElse(level) => {
|
||||
@ -407,12 +506,12 @@ pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables)
|
||||
if level == 0 {
|
||||
state = Nothing;
|
||||
} else {
|
||||
state = SeekIfElse(level-1);
|
||||
state = SeekIfElse(level - 1);
|
||||
}
|
||||
} else if cur == 'e' && level == 0 {
|
||||
state = Nothing;
|
||||
} else if cur == '?' {
|
||||
state = SeekIfElse(level+1);
|
||||
state = SeekIfElse(level + 1);
|
||||
} else {
|
||||
state = SeekIfElse(level);
|
||||
}
|
||||
@ -428,10 +527,10 @@ pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables)
|
||||
if level == 0 {
|
||||
state = Nothing;
|
||||
} else {
|
||||
state = SeekIfEnd(level-1);
|
||||
state = SeekIfEnd(level - 1);
|
||||
}
|
||||
} else if cur == '?' {
|
||||
state = SeekIfEnd(level+1);
|
||||
state = SeekIfEnd(level + 1);
|
||||
} else {
|
||||
state = SeekIfEnd(level);
|
||||
}
|
||||
@ -451,13 +550,19 @@ struct Flags {
|
||||
alternate: bool,
|
||||
left: bool,
|
||||
sign: bool,
|
||||
space: bool
|
||||
space: bool,
|
||||
}
|
||||
|
||||
impl Flags {
|
||||
fn new() -> Flags {
|
||||
Flags{ width: 0, precision: 0, alternate: false,
|
||||
left: false, sign: false, space: false }
|
||||
Flags {
|
||||
width: 0,
|
||||
precision: 0,
|
||||
alternate: false,
|
||||
left: false,
|
||||
sign: false,
|
||||
space: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -467,7 +572,7 @@ enum FormatOp {
|
||||
FormatOctal,
|
||||
FormatHex,
|
||||
FormatHEX,
|
||||
FormatString
|
||||
FormatString,
|
||||
}
|
||||
|
||||
impl FormatOp {
|
||||
@ -478,7 +583,7 @@ impl FormatOp {
|
||||
'x' => FormatHex,
|
||||
'X' => FormatHEX,
|
||||
's' => FormatString,
|
||||
_ => panic!("bad FormatOp char")
|
||||
_ => panic!("bad FormatOp char"),
|
||||
}
|
||||
}
|
||||
fn to_char(self) -> char {
|
||||
@ -487,23 +592,21 @@ impl FormatOp {
|
||||
FormatOctal => 'o',
|
||||
FormatHex => 'x',
|
||||
FormatHEX => 'X',
|
||||
FormatString => 's'
|
||||
FormatString => 's',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn format(val: Param, op: FormatOp, flags: Flags) -> Result<Vec<u8> ,String> {
|
||||
fn format(val: Param, op: FormatOp, flags: Flags) -> Result<Vec<u8>, String> {
|
||||
let mut s = match val {
|
||||
Number(d) => {
|
||||
let s = match (op, flags.sign) {
|
||||
(FormatDigit, true) => format!("{:+}", d).into_bytes(),
|
||||
(FormatDigit, true) => format!("{:+}", d).into_bytes(),
|
||||
(FormatDigit, false) => format!("{}", d).into_bytes(),
|
||||
(FormatOctal, _) => format!("{:o}", d).into_bytes(),
|
||||
(FormatHex, _) => format!("{:x}", d).into_bytes(),
|
||||
(FormatHEX, _) => format!("{:X}", d).into_bytes(),
|
||||
(FormatString, _) => {
|
||||
return Err("non-number on stack with %s".to_owned())
|
||||
}
|
||||
(FormatOctal, _) => format!("{:o}", d).into_bytes(),
|
||||
(FormatHex, _) => format!("{:x}", d).into_bytes(),
|
||||
(FormatHEX, _) => format!("{:X}", d).into_bytes(),
|
||||
(FormatString, _) => return Err("non-number on stack with %s".to_owned()),
|
||||
};
|
||||
let mut s: Vec<u8> = s.into_iter().collect();
|
||||
if flags.precision > s.len() {
|
||||
@ -516,7 +619,7 @@ fn format(val: Param, op: FormatOp, flags: Flags) -> Result<Vec<u8> ,String> {
|
||||
assert!(!s.is_empty(), "string conversion produced empty result");
|
||||
match op {
|
||||
FormatDigit => {
|
||||
if flags.space && !(s[0] == b'-' || s[0] == b'+' ) {
|
||||
if flags.space && !(s[0] == b'-' || s[0] == b'+') {
|
||||
s.insert(0, b' ');
|
||||
}
|
||||
}
|
||||
@ -527,18 +630,18 @@ fn format(val: Param, op: FormatOp, flags: Flags) -> Result<Vec<u8> ,String> {
|
||||
}
|
||||
FormatHex => {
|
||||
if flags.alternate {
|
||||
let s_ = replace(&mut s, vec!(b'0', b'x'));
|
||||
let s_ = replace(&mut s, vec![b'0', b'x']);
|
||||
s.extend(s_);
|
||||
}
|
||||
}
|
||||
FormatHEX => {
|
||||
s = s.to_ascii_uppercase();
|
||||
if flags.alternate {
|
||||
let s_ = replace(&mut s, vec!(b'0', b'X'));
|
||||
let s_ = replace(&mut s, vec![b'0', b'X']);
|
||||
s.extend(s_);
|
||||
}
|
||||
}
|
||||
FormatString => unreachable!()
|
||||
FormatString => unreachable!(),
|
||||
}
|
||||
s
|
||||
}
|
||||
@ -551,10 +654,7 @@ fn format(val: Param, op: FormatOp, flags: Flags) -> Result<Vec<u8> ,String> {
|
||||
}
|
||||
s
|
||||
}
|
||||
_ => {
|
||||
return Err(format!("non-string on stack with %{:?}",
|
||||
op.to_char()))
|
||||
}
|
||||
_ => return Err(format!("non-string on stack with %{:?}", op.to_char())),
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -574,7 +674,7 @@ fn format(val: Param, op: FormatOp, flags: Flags) -> Result<Vec<u8> ,String> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{expand,Param,Words,Variables,Number};
|
||||
use super::{expand, Param, Words, Variables, Number};
|
||||
use std::result::Result::Ok;
|
||||
|
||||
#[test]
|
||||
@ -594,7 +694,8 @@ mod tests {
|
||||
fn test_op_i() {
|
||||
let mut vars = Variables::new();
|
||||
assert_eq!(expand(b"%p1%d%p2%d%p3%d%i%p1%d%p2%d%p3%d",
|
||||
&[Number(1),Number(2),Number(3)], &mut vars),
|
||||
&[Number(1), Number(2), Number(3)],
|
||||
&mut vars),
|
||||
Ok("123233".bytes().collect::<Vec<_>>()));
|
||||
assert_eq!(expand(b"%p1%d%p2%d%i%p1%d%p2%d", &[], &mut vars),
|
||||
Ok("0011".bytes().collect::<Vec<_>>()));
|
||||
@ -604,9 +705,11 @@ mod tests {
|
||||
fn test_param_stack_failure_conditions() {
|
||||
let mut varstruct = Variables::new();
|
||||
let vars = &mut varstruct;
|
||||
fn get_res(fmt: &str, cap: &str, params: &[Param], vars: &mut Variables) ->
|
||||
Result<Vec<u8>, String>
|
||||
{
|
||||
fn get_res(fmt: &str,
|
||||
cap: &str,
|
||||
params: &[Param],
|
||||
vars: &mut Variables)
|
||||
-> Result<Vec<u8>, String> {
|
||||
let mut u8v: Vec<_> = fmt.bytes().collect();
|
||||
u8v.extend(cap.bytes());
|
||||
expand(&u8v, params, vars)
|
||||
@ -616,7 +719,8 @@ mod tests {
|
||||
for &cap in &caps {
|
||||
let res = get_res("", cap, &[], vars);
|
||||
assert!(res.is_err(),
|
||||
"Op {} succeeded incorrectly with 0 stack entries", cap);
|
||||
"Op {} succeeded incorrectly with 0 stack entries",
|
||||
cap);
|
||||
let p = if cap == "%s" || cap == "%l" {
|
||||
Words("foo".to_string())
|
||||
} else {
|
||||
@ -624,19 +728,25 @@ mod tests {
|
||||
};
|
||||
let res = get_res("%p1", cap, &[p], vars);
|
||||
assert!(res.is_ok(),
|
||||
"Op {} failed with 1 stack entry: {}", cap, res.err().unwrap());
|
||||
"Op {} failed with 1 stack entry: {}",
|
||||
cap,
|
||||
res.err().unwrap());
|
||||
}
|
||||
let caps = ["%+", "%-", "%*", "%/", "%m", "%&", "%|", "%A", "%O"];
|
||||
for &cap in &caps {
|
||||
let res = expand(cap.as_bytes(), &[], vars);
|
||||
assert!(res.is_err(),
|
||||
"Binop {} succeeded incorrectly with 0 stack entries", cap);
|
||||
"Binop {} succeeded incorrectly with 0 stack entries",
|
||||
cap);
|
||||
let res = get_res("%{1}", cap, &[], vars);
|
||||
assert!(res.is_err(),
|
||||
"Binop {} succeeded incorrectly with 1 stack entry", cap);
|
||||
"Binop {} succeeded incorrectly with 1 stack entry",
|
||||
cap);
|
||||
let res = get_res("%{1}%{2}", cap, &[], vars);
|
||||
assert!(res.is_ok(),
|
||||
"Binop {} failed with 2 stack entries: {:?}", cap, res.err().unwrap());
|
||||
"Binop {} failed with 2 stack entries: {:?}",
|
||||
cap,
|
||||
res.err().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
@ -670,16 +780,13 @@ mod tests {
|
||||
let s = b"\\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.err().unwrap());
|
||||
assert_eq!(res.unwrap(),
|
||||
"\\E[31m".bytes().collect::<Vec<_>>());
|
||||
assert_eq!(res.unwrap(), "\\E[31m".bytes().collect::<Vec<_>>());
|
||||
let res = expand(s, &[Number(8)], &mut vars);
|
||||
assert!(res.is_ok(), res.err().unwrap());
|
||||
assert_eq!(res.unwrap(),
|
||||
"\\E[90m".bytes().collect::<Vec<_>>());
|
||||
assert_eq!(res.unwrap(), "\\E[90m".bytes().collect::<Vec<_>>());
|
||||
let res = expand(s, &[Number(42)], &mut vars);
|
||||
assert!(res.is_ok(), res.err().unwrap());
|
||||
assert_eq!(res.unwrap(),
|
||||
"\\E[38;5;42m".bytes().collect::<Vec<_>>());
|
||||
assert_eq!(res.unwrap(), "\\E[38;5;42m".bytes().collect::<Vec<_>>());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -690,14 +797,17 @@ mod tests {
|
||||
&[Words("foo".to_string()),
|
||||
Words("foo".to_string()),
|
||||
Words("f".to_string()),
|
||||
Words("foo".to_string())], vars),
|
||||
Words("foo".to_string())],
|
||||
vars),
|
||||
Ok("foofoo ffo".bytes().collect::<Vec<_>>()));
|
||||
assert_eq!(expand(b"%p1%:-4.2s", &[Words("foo".to_owned())], vars),
|
||||
Ok("fo ".bytes().collect::<Vec<_>>()));
|
||||
|
||||
assert_eq!(expand(b"%p1%d%p1%.3d%p1%5d%p1%:+d", &[Number(1)], vars),
|
||||
Ok("1001 1+1".bytes().collect::<Vec<_>>()));
|
||||
assert_eq!(expand(b"%p1%o%p1%#o%p2%6.4x%p2%#6.4X", &[Number(15), Number(27)], vars),
|
||||
assert_eq!(expand(b"%p1%o%p1%#o%p2%6.4x%p2%#6.4X",
|
||||
&[Number(15), Number(27)],
|
||||
vars),
|
||||
Ok("17017 001b0X001B".bytes().collect::<Vec<_>>()));
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ use super::super::TermInfo;
|
||||
|
||||
// These are the orders ncurses uses in its compiled format (as of 5.9). Not sure if portable.
|
||||
|
||||
#[rustfmt_skip]
|
||||
#[allow(missing_docs)]
|
||||
pub static boolfnames: &'static[&'static str] = &["auto_left_margin", "auto_right_margin",
|
||||
"no_esc_ctlc", "ceol_standout_glitch", "eat_newline_glitch", "erase_overstrike", "generic_type",
|
||||
@ -32,12 +33,14 @@ pub static boolfnames: &'static[&'static str] = &["auto_left_margin", "auto_righ
|
||||
"no_correctly_working_cr", "gnu_has_meta_key", "linefeed_is_newline", "has_hardware_tabs",
|
||||
"return_does_clr_eol"];
|
||||
|
||||
#[rustfmt_skip]
|
||||
#[allow(missing_docs)]
|
||||
pub static boolnames: &'static[&'static str] = &["bw", "am", "xsb", "xhp", "xenl", "eo",
|
||||
"gn", "hc", "km", "hs", "in", "db", "da", "mir", "msgr", "os", "eslok", "xt", "hz", "ul", "xon",
|
||||
"nxon", "mc5i", "chts", "nrrmc", "npc", "ndscr", "ccc", "bce", "hls", "xhpa", "crxm", "daisy",
|
||||
"xvpa", "sam", "cpix", "lpix", "OTbs", "OTns", "OTnc", "OTMT", "OTNL", "OTpt", "OTxr"];
|
||||
|
||||
#[rustfmt_skip]
|
||||
#[allow(missing_docs)]
|
||||
pub static numfnames: &'static[&'static str] = &[ "columns", "init_tabs", "lines",
|
||||
"lines_of_memory", "magic_cookie_glitch", "padding_baud_rate", "virtual_terminal",
|
||||
@ -49,12 +52,14 @@ pub static numfnames: &'static[&'static str] = &[ "columns", "init_tabs", "lines
|
||||
"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"];
|
||||
|
||||
#[rustfmt_skip]
|
||||
#[allow(missing_docs)]
|
||||
pub static numnames: &'static[&'static str] = &[ "cols", "it", "lines", "lm", "xmc", "pb",
|
||||
"vt", "wsl", "nlab", "lh", "lw", "ma", "wnum", "colors", "pairs", "ncv", "bufsz", "spinv",
|
||||
"spinh", "maddr", "mjump", "mcs", "mls", "npins", "orc", "orl", "orhi", "orvi", "cps", "widcs",
|
||||
"btns", "bitwin", "bitype", "UTug", "OTdC", "OTdN", "OTdB", "OTdT", "OTkn"];
|
||||
|
||||
#[rustfmt_skip]
|
||||
#[allow(missing_docs)]
|
||||
pub static stringfnames: &'static[&'static str] = &[ "back_tab", "bell", "carriage_return",
|
||||
"change_scroll_region", "clear_all_tabs", "clear_screen", "clr_eol", "clr_eos",
|
||||
@ -129,6 +134,7 @@ pub static stringfnames: &'static[&'static str] = &[ "back_tab", "bell", "carria
|
||||
"acs_lrcorner", "acs_ltee", "acs_rtee", "acs_btee", "acs_ttee", "acs_hline", "acs_vline",
|
||||
"acs_plus", "memory_lock", "memory_unlock", "box_chars_1"];
|
||||
|
||||
#[rustfmt_skip]
|
||||
#[allow(missing_docs)]
|
||||
pub static stringnames: &'static[&'static str] = &[ "cbt", "_", "cr", "csr", "tbc", "clear",
|
||||
"_", "_", "hpa", "cmdch", "cup", "cud1", "home", "civis", "cub1", "mrcup", "cnorm", "cuf1",
|
||||
@ -165,8 +171,7 @@ pub static stringnames: &'static[&'static str] = &[ "cbt", "_", "cr", "csr", "tb
|
||||
"box1"];
|
||||
|
||||
/// Parse a compiled terminfo entry, using long capability names if `longnames` is true
|
||||
pub fn parse(file: &mut Read, longnames: bool)
|
||||
-> Result<Box<TermInfo>, String> {
|
||||
pub fn parse(file: &mut Read, longnames: bool) -> Result<Box<TermInfo>, String> {
|
||||
macro_rules! try { ($e:expr) => (
|
||||
match $e {
|
||||
Ok(e) => e,
|
||||
@ -192,36 +197,34 @@ pub fn parse(file: &mut Read, longnames: bool)
|
||||
let magic = try!(read_le_u16(file));
|
||||
if magic != 0x011A {
|
||||
return Err(format!("invalid magic number: expected {:x}, found {:x}",
|
||||
0x011A_usize, magic as usize));
|
||||
0x011A_usize,
|
||||
magic as usize));
|
||||
}
|
||||
|
||||
let names_bytes = try!(read_le_u16(file)) as isize;
|
||||
let bools_bytes = try!(read_le_u16(file)) as isize;
|
||||
let numbers_count = try!(read_le_u16(file)) as isize;
|
||||
let names_bytes = try!(read_le_u16(file)) as isize;
|
||||
let bools_bytes = try!(read_le_u16(file)) as isize;
|
||||
let numbers_count = try!(read_le_u16(file)) as isize;
|
||||
let string_offsets_count = try!(read_le_u16(file)) as isize;
|
||||
let string_table_bytes = try!(read_le_u16(file)) as isize;
|
||||
let string_table_bytes = try!(read_le_u16(file)) as isize;
|
||||
|
||||
assert!(names_bytes > 0);
|
||||
assert!(names_bytes > 0);
|
||||
|
||||
if (bools_bytes as usize) > boolnames.len() {
|
||||
return Err("incompatible file: more booleans than \
|
||||
expected".to_owned());
|
||||
return Err("incompatible file: more booleans than expected".to_owned());
|
||||
}
|
||||
|
||||
if (numbers_count as usize) > numnames.len() {
|
||||
return Err("incompatible file: more numbers than \
|
||||
expected".to_owned());
|
||||
return Err("incompatible file: more numbers than expected".to_owned());
|
||||
}
|
||||
|
||||
if (string_offsets_count as usize) > stringnames.len() {
|
||||
return Err("incompatible file: more string offsets than \
|
||||
expected".to_owned());
|
||||
return Err("incompatible file: more string offsets than expected".to_owned());
|
||||
}
|
||||
|
||||
// don't read NUL
|
||||
let bytes = try!(read_exact(file, names_bytes as usize - 1));
|
||||
let names_str = match String::from_utf8(bytes) {
|
||||
Ok(s) => s,
|
||||
Ok(s) => s,
|
||||
Err(_) => return Err("input not utf-8".to_owned()),
|
||||
};
|
||||
|
||||
@ -266,13 +269,13 @@ pub fn parse(file: &mut Read, longnames: bool)
|
||||
let string_table = try!(read_exact(file, string_table_bytes as usize));
|
||||
|
||||
if string_table.len() != string_table_bytes as usize {
|
||||
return Err("error: hit EOF before end of string \
|
||||
table".to_owned());
|
||||
return Err("error: hit EOF before end of string table".to_owned());
|
||||
}
|
||||
|
||||
for (i, v) in string_offsets.iter().enumerate() {
|
||||
let offset = *v;
|
||||
if offset == 0xFFFF { // non-entry
|
||||
if offset == 0xFFFF {
|
||||
// non-entry
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -291,17 +294,17 @@ pub fn parse(file: &mut Read, longnames: bool)
|
||||
|
||||
|
||||
// Find the offset of the NUL we want to go to
|
||||
let nulpos = string_table[offset as usize .. string_table_bytes as usize]
|
||||
.iter().position(|&b| b == 0);
|
||||
let nulpos = string_table[offset as usize..string_table_bytes as usize]
|
||||
.iter()
|
||||
.position(|&b| b == 0);
|
||||
match nulpos {
|
||||
Some(len) => {
|
||||
string_map.insert(name.to_string(),
|
||||
string_table[offset as usize ..
|
||||
(offset as usize + len)].to_vec())
|
||||
},
|
||||
string_table[offset as usize..(offset as usize + len)]
|
||||
.to_vec())
|
||||
}
|
||||
None => {
|
||||
return Err("invalid file: missing NUL in \
|
||||
string_table".to_owned());
|
||||
return Err("invalid file: missing NUL in string_table".to_owned());
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -312,7 +315,7 @@ pub fn parse(file: &mut Read, longnames: bool)
|
||||
names: term_names,
|
||||
bools: bools_map,
|
||||
numbers: numbers_map,
|
||||
strings: string_map
|
||||
strings: string_map,
|
||||
})
|
||||
}
|
||||
|
||||
@ -343,10 +346,10 @@ pub fn msys_terminfo() -> Box<TermInfo> {
|
||||
strings.insert("setaf".to_owned(), b"\x1B[3%p1%dm".to_vec());
|
||||
strings.insert("setab".to_owned(), b"\x1B[4%p1%dm".to_vec());
|
||||
box TermInfo {
|
||||
names: vec!("cygwin".to_owned()), // msys is a fork of an older cygwin version
|
||||
names: vec!["cygwin".to_owned()], // msys is a fork of an older cygwin version
|
||||
bools: HashMap::new(),
|
||||
numbers: HashMap::new(),
|
||||
strings: strings
|
||||
strings: strings,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,13 +38,15 @@ pub fn get_dbpath_for_term(term: &str) -> Option<Box<PathBuf>> {
|
||||
dirs_to_search.push(homedir.unwrap().join(".terminfo"))
|
||||
}
|
||||
match env::var("TERMINFO_DIRS") {
|
||||
Ok(dirs) => for i in dirs.split(':') {
|
||||
if i == "" {
|
||||
dirs_to_search.push(PathBuf::from("/usr/share/terminfo"));
|
||||
} else {
|
||||
dirs_to_search.push(PathBuf::from(i));
|
||||
Ok(dirs) => {
|
||||
for i in dirs.split(':') {
|
||||
if i == "" {
|
||||
dirs_to_search.push(PathBuf::from("/usr/share/terminfo"));
|
||||
} else {
|
||||
dirs_to_search.push(PathBuf::from(i));
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
// Found nothing in TERMINFO_DIRS, use the default paths:
|
||||
// According to /etc/terminfo/README, after looking at
|
||||
// ~/.terminfo, ncurses will search /etc/terminfo, then
|
||||
@ -86,9 +88,7 @@ pub fn open(term: &str) -> Result<File, String> {
|
||||
Err(e) => Err(format!("error opening file: {:?}", e)),
|
||||
}
|
||||
}
|
||||
None => {
|
||||
Err(format!("could not find terminfo entry for {:?}", term))
|
||||
}
|
||||
None => Err(format!("could not find terminfo entry for {:?}", term)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,7 @@ use std::io::prelude::*;
|
||||
|
||||
use attr;
|
||||
use color;
|
||||
use {Terminal,UnwrappableTerminal};
|
||||
use {Terminal, UnwrappableTerminal};
|
||||
|
||||
/// A Terminal implementation which uses the Win32 Console API.
|
||||
pub struct WinConsole<T> {
|
||||
@ -50,23 +50,22 @@ struct CONSOLE_SCREEN_BUFFER_INFO {
|
||||
extern "system" {
|
||||
fn SetConsoleTextAttribute(handle: HANDLE, attr: WORD) -> BOOL;
|
||||
fn GetStdHandle(which: DWORD) -> HANDLE;
|
||||
fn GetConsoleScreenBufferInfo(handle: HANDLE,
|
||||
info: *mut CONSOLE_SCREEN_BUFFER_INFO) -> BOOL;
|
||||
fn GetConsoleScreenBufferInfo(handle: HANDLE, info: *mut CONSOLE_SCREEN_BUFFER_INFO) -> BOOL;
|
||||
}
|
||||
|
||||
fn color_to_bits(color: color::Color) -> u16 {
|
||||
// magic numbers from mingw-w64's wincon.h
|
||||
|
||||
let bits = match color % 8 {
|
||||
color::BLACK => 0,
|
||||
color::BLUE => 0x1,
|
||||
color::GREEN => 0x2,
|
||||
color::RED => 0x4,
|
||||
color::YELLOW => 0x2 | 0x4,
|
||||
color::BLACK => 0,
|
||||
color::BLUE => 0x1,
|
||||
color::GREEN => 0x2,
|
||||
color::RED => 0x4,
|
||||
color::YELLOW => 0x2 | 0x4,
|
||||
color::MAGENTA => 0x1 | 0x4,
|
||||
color::CYAN => 0x1 | 0x2,
|
||||
color::WHITE => 0x1 | 0x2 | 0x4,
|
||||
_ => unreachable!()
|
||||
color::CYAN => 0x1 | 0x2,
|
||||
color::WHITE => 0x1 | 0x2 | 0x4,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
if color >= 8 {
|
||||
@ -86,7 +85,7 @@ fn bits_to_color(bits: u16) -> color::Color {
|
||||
0x5 => color::MAGENTA,
|
||||
0x3 => color::CYAN,
|
||||
0x7 => color::WHITE,
|
||||
_ => unreachable!()
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
color | (bits & 0x8) // copy the hi-intensity bit
|
||||
@ -116,13 +115,12 @@ impl<T: Write+Send+'static> WinConsole<T> {
|
||||
|
||||
/// Returns `None` whenever the terminal cannot be created for some
|
||||
/// reason.
|
||||
pub fn new(out: T) -> Option<Box<Terminal<T>+Send+'static>> {
|
||||
pub fn new(out: T) -> Option<Box<Terminal<T> + Send + 'static>> {
|
||||
let fg;
|
||||
let bg;
|
||||
unsafe {
|
||||
let mut buffer_info = ::std::mem::uninitialized();
|
||||
if GetConsoleScreenBufferInfo(GetStdHandle(-11i32 as DWORD),
|
||||
&mut buffer_info) != 0 {
|
||||
if GetConsoleScreenBufferInfo(GetStdHandle(-11i32 as DWORD), &mut buffer_info) != 0 {
|
||||
fg = bits_to_color(buffer_info.wAttributes);
|
||||
bg = bits_to_color(buffer_info.wAttributes >> 4);
|
||||
} else {
|
||||
@ -130,9 +128,13 @@ impl<T: Write+Send+'static> WinConsole<T> {
|
||||
bg = color::BLACK;
|
||||
}
|
||||
}
|
||||
Some(box WinConsole { buf: out,
|
||||
def_foreground: fg, def_background: bg,
|
||||
foreground: fg, background: bg })
|
||||
Some(box WinConsole {
|
||||
buf: out,
|
||||
def_foreground: fg,
|
||||
def_background: bg,
|
||||
foreground: fg,
|
||||
background: bg,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -167,13 +169,13 @@ impl<T: Write+Send+'static> Terminal<T> for WinConsole<T> {
|
||||
self.foreground = f;
|
||||
self.apply();
|
||||
Ok(true)
|
||||
},
|
||||
}
|
||||
attr::BackgroundColor(b) => {
|
||||
self.background = b;
|
||||
self.apply();
|
||||
Ok(true)
|
||||
},
|
||||
_ => Ok(false)
|
||||
}
|
||||
_ => Ok(false),
|
||||
}
|
||||
}
|
||||
|
||||
@ -182,7 +184,7 @@ impl<T: Write+Send+'static> Terminal<T> for WinConsole<T> {
|
||||
// it to do anything -cmr
|
||||
match attr {
|
||||
attr::ForegroundColor(_) | attr::BackgroundColor(_) => true,
|
||||
_ => false
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
@ -194,11 +196,17 @@ impl<T: Write+Send+'static> Terminal<T> for WinConsole<T> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_ref<'a>(&'a self) -> &'a T { &self.buf }
|
||||
fn get_ref<'a>(&'a self) -> &'a T {
|
||||
&self.buf
|
||||
}
|
||||
|
||||
fn get_mut<'a>(&'a mut self) -> &'a mut T { &mut self.buf }
|
||||
fn get_mut<'a>(&'a mut self) -> &'a mut T {
|
||||
&mut self.buf
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Write+Send+'static> UnwrappableTerminal<T> for WinConsole<T> {
|
||||
fn unwrap(self) -> T { self.buf }
|
||||
fn unwrap(self) -> T {
|
||||
self.buf
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user