Rustfmt libterm, skip long static in compiled.rs
This commit is contained in:
parent
685e098561
commit
7d54c32c94
@ -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