Created
September 1, 2018 07:30
-
-
Save andurilan/08054750b52a105ab7424a8f63f6ce64 to your computer and use it in GitHub Desktop.
pls.plus
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#![allow(unused_macros)] | |
use std:: | |
{ | |
collections::{ HashMap, HashSet }, | |
env::{ self }, | |
error::{Error}, | |
fs::{ self, File, OpenOptions, read_dir }, | |
io::{ self, Error as IO_Error, Read, Write }, | |
mem::{ self }, | |
os::unix:: | |
{ | |
io::{ FromRawFd, IntoRawFd }, | |
process::{ CommandExt }, | |
}, | |
path::{ Path, PathBuf }, | |
process::{ self, Command as Processor, Output, Stdio }, | |
str::{ self, FromStr }, | |
sync::{ Arc }, | |
}; | |
use super:: | |
{ | |
errno::{ errno }, | |
exec::{ self }, | |
glob::{ self }, | |
libc::{ self }, | |
linefeed:: | |
{ | |
Interface::{ self }, | |
complete::{ Completer, Completion }, | |
prompter::{ Prompter }, | |
ReadResult::{ self }, | |
terminal::{ DefaultTerminal, Terminal }, | |
}, | |
nix::{ sys::signal, unistd::pipe }, | |
nom::{ digit, IResult }, | |
regex::{ self, Regex }, | |
sqlite::{ self, State }, | |
}; | |
macro_rules! println_stderr | |
{ | |
($fmt:expr) => ( match writeln!( &mut ::std::io::stderr(), concat!( $fmt, "\n" ) ) | |
{ | |
Ok(_) => {} | |
Err(e) => println!( "write to stderr failed: {:?}", e ) | |
} ); | |
( $fmt:expr, $( $arg:tt )* ) => ( match writeln!( &mut ::std::io::stderr(), concat!( $fmt, "\n" ), $( $arg)* ) | |
{ | |
Ok(_) => {} | |
Err(e) => println!( "write to stderr failed: {:?}", e ) | |
} ); | |
} | |
#[macro_use] macro_rules! matches | |
{ | |
( $val:expr, default => $c:expr, $( $( $p:pat )|+ => $b:expr ),+ ) => { | |
{ | |
let val = $val; | |
let mut matched = false; | |
$( | |
#[allow(unreachable_patterns)] | |
match val | |
{ | |
$($p)|+ => { $b; matched = true; }, | |
_ => (), | |
} )+ | |
if !matched { $c } | |
} }; | |
($val:expr, $($($p:pat)|+ => $b:expr),+) => { | |
{ | |
let val = $val; | |
$( | |
#[allow(unreachable_patterns)] | |
match val | |
{ | |
$($p)|+ => { $b; }, | |
_ => (), | |
} | |
)+ | |
} }; | |
} | |
macro_rules! when | |
{ | |
($var:ident in $val:expr, ever => $c:expr, $($($p:pat)|+ => $b:expr),+) => { | |
{ | |
for $var in $val.iter() | |
{ | |
let mut matched = false; | |
let var = *$var; | |
$( | |
#[allow(unreachable_patterns)] | |
match var | |
{ | |
$( $p )|+ => { $b; matched = true; }, | |
_ => (), | |
} | |
)+ | |
if !matched { $c } | |
} | |
} }; | |
($var:ident in $val:expr, $($($p:pat)|+ => $b:expr),+) => { | |
{ | |
for $var in $val.iter() | |
{ | |
let var = *$var; | |
$( | |
#[allow(unreachable_patterns)] | |
match var | |
{ | |
$( $p )|+ => { $b; }, | |
_ => (), | |
} | |
)+ | |
} | |
} }; | |
} | |
macro_rules! fоr | |
{ | |
( $var:ident in $val:expr, $($($p:pat)|+ => $b:expr),+ ) => { | |
{ | |
for $var in $val.iter() | |
{ | |
let var = *$var; | |
#[allow(unreachable_patterns)] | |
match var | |
{ | |
$( $( $p )|+ => { $b; }),+ | |
_ => (), | |
} | |
} | |
} }; | |
} | |
macro_rules! constructing | |
{ | |
( $strυct:literal )=>({ println!("installer::constructing( {} )::", $strυct ); } ); | |
( $strυct:literal, $from:literal )=>({ println!("installer::constructing( {}, from {} )::", $strυct, $from ); } ); | |
( $strυct:literal, $from:literal, $msg:literal )=>({ println!("installer::constructing( {}, from {} )::`{}`::", $strυct, $from, $msg ); } ); | |
} | |
macro_rules! constructed | |
{ | |
( $strυct:literal )=>({ println!("installer::CONSTRUCTED( {} )::", $strυct ); } ); | |
( $strυct:literal, $from:literal )=>({ println!("installer::CONSTRUCTED( {}, from {} )::", $strυct, $from ); } ); | |
( $strυct:literal, $from:literal, $msg:literal )=>({ println!("installer::CONSTRUCTED( {}, from {} )::` {} `::", $strυct, $from, $msg ); } ); | |
} | |
macro_rules! functioning | |
{ | |
( $function:literal )=>({ println!("installer::functioning( {} )::", $function ); } ); | |
( $function:literal, $from:literal )=>({ println!("installer::functioning( {}, from {} )::", $function, $from ); } ); | |
( $function:literal, $from:literal, $msg:literal )=>({ println!("installer::{}( {} )...| {}", $function, $from, $msg ); } ); | |
} | |
macro_rules! functioned | |
{ | |
( $function:literal )=>({ println!("installer::FUNCTIONED( {} )::", $function ); } ); | |
( $function:literal, $with:literal )=>({ println!("installer::FUNCTIONED( {}, from {} )::", $function, $with ); } ); | |
( $function:literal, $with:literal, $msg:literal )=>({ println!("installer::{}( {} )...| {}", $function, $with, $msg ); } ); | |
} | |
named!( parens_i64<i64>, ws!(delimited!(tag!("("), expr_int, tag!(")")))); | |
named!( parens_f64<f64>, ws!( delimited!( tag!("("), expr_float, tag!(")") ) ) ); | |
named!( factor_f64<f64>, alt! | |
( | |
map_res!( | |
map_res!( | |
ws!(recognize!(alt!( | |
delimited!(digit, complete!(tag!(".")), opt!(complete!(digit))) | |
| delimited!(opt!(digit), complete!(tag!(".")), digit) | |
| digit | |
))), | |
str::from_utf8 | |
), | |
FromStr::from_str | |
) | parens_f64 | |
) ); | |
named!( factor_i64<i64>, alt! | |
( map_res!( | |
map_res! | |
( | |
ws!( recognize!( alt!( | |
delimited!(digit, complete!(tag!(".")), opt!(complete!(digit))) | | |
delimited!(opt!(digit), complete!(tag!(".")), digit ) | | |
digit ) ) ), | |
str::from_utf8 | |
), | |
FromStr::from_str ) | | |
parens_i64 | |
) ); | |
named!( term_i64<i64>, do_parse! | |
( init: factor_i64 >> res: fold_many0! | |
( pair!( alt!( tag!("*") | tag!("/") ), factor_i64 ), | |
init, | | |
acc, ( op, val ): (&[u8], i64)| | |
{ | |
if (op[0] as char) == '*' { ( acc * val ) as i64 } | |
else { (acc / val) as i64 } | |
} | |
) >> (res) | |
) ); | |
named!( term_f64<f64>, | |
do_parse! | |
( | |
init: factor_f64 | |
>> res: fold_many0!( | |
pair!(alt!(tag!("*") | tag!("/")), factor_f64), | |
init, | |
|acc, (op, val): (&[u8], f64)| { | |
if (op[0] as char) == '*' { | |
(acc * val) as f64 | |
} else { | |
(acc / val) as f64 | |
} | |
} | |
) >> (res) | |
) ); | |
named!( pub expr_int <i64>, do_parse! | |
( init: term_i64 >> res: fold_many0! | |
( | |
pair!(alt!(tag!("+") | tag!("-")), term_i64), | |
init, | |
| acc, (op, val): (&[u8], i64)| | |
{ | |
if (op[0] as char) == '+' { (acc + val) as i64 } | |
else { (acc - val) as i64 } | |
} | |
) >> (res) | |
) ); | |
named!( pub expr_float <f64>, do_parse! | |
( | |
init: term_f64 >> | |
res: fold_many0!( | |
pair!(alt!(tag!("+") | tag!("-")), term_f64), | |
init, | |
|acc, (op, val): (&[u8], f64)| { | |
if (op[0] as char) == '+' { (acc + val) as f64 } else { (acc - val) as f64 } | |
} | |
) >> | |
(res) | |
) ); | |
pub fn clog( s: &str ) | |
{ | |
let file; | |
if let Ok(x) = env::var("INSTALLER_LOG_FILE") { file = x; } | |
else { | |
return; | |
} | |
let mut cfile; | |
match OpenOptions::new().append(true).create(true).open( &file ) | |
{ | |
Ok(x) => cfile = x, | |
Err(e) => { println!("clog: open file {} failed: {:?}", &file, e); return; } | |
} | |
let pid = unsafe { libc::getpid() }; | |
let now = time::now(); | |
let s = format! | |
( | |
"[{:04}-{:02}-{:02} {:02}:{:02}:{:02}][{}]{}", | |
now.tm_year + 1900, | |
now.tm_mon + 1, | |
now.tm_mday, | |
now.tm_hour, | |
now.tm_min, | |
now.tm_sec, | |
pid, | |
s, | |
); | |
match cfile.write_all(s.as_bytes()) | |
{ | |
Ok(_) => {} | |
Err(e) => { println!("installer::clog: write_all failed: {:?}", e); return; } | |
} | |
} | |
macro_rules! log | |
{ | |
($fmt:expr) => ( clog( concat!( $fmt, "\n" ) ); ); | |
( $fmt:expr, $( $arg:tt )* ) => ( clog( format!( concat!( $fmt, "\n"), $( $arg )*).as_str() ); ); | |
} | |
const BLUE: &str = "\x01\x1B[34m\x02"; | |
const RED: &str = "\x01\x1B[31m\x02"; | |
const GREEN: &str = "\x01\x1B[32m\x02"; | |
const RESET: &str = "\x01\x1B[0m\x02"; | |
pub type Tokens = Vec<(String, String)>; | |
pub type Redirection = (String, String, String); | |
#[derive(Debug)] | |
pub struct Command | |
{ | |
pub tokens: Tokens, | |
pub redirects: Vec<Redirection>, | |
} | |
#[derive(Clone, Debug, Default)] | |
pub struct CommandResult { | |
pub status: i32, | |
pub stdout: String, | |
pub stderr: String, | |
} | |
impl CommandResult { | |
pub fn new() -> CommandResult { | |
CommandResult { | |
status: 0, | |
stdout: String::new(), | |
stderr: String::new(), | |
} | |
} | |
} | |
extern "C" fn handle_sigchld( _: i32 ) | |
{ | |
// When handle waitpid here & for commands like `ls | cmd-not-exist` | |
// will panic: "wait() should either return Ok or panic" | |
// which I currently don't know how to fix. | |
/* | |
unsafe { | |
let mut stat: i32 = 0; | |
let ptr: *mut i32 = &mut stat; | |
let pid = libc::waitpid(-1, ptr, libc::WNOHANG); | |
} | |
*/ | |
} | |
extern "C" { fn gethostname(name: *mut libc::c_char, size: libc::size_t) -> libc::c_int; } | |
#[derive(Debug, Clone)] | |
pub struct Agent | |
{ | |
pub alias: HashMap<String, String>, | |
pub profile:String, | |
pub dir: String, | |
pub previous_dir: String, | |
pub cmd: String, | |
pub previous_cmd: String, | |
pub status: i32, | |
pub previous_status: i32, | |
} | |
impl Agent | |
{ | |
pub fn new( status:i32 ) -> Agent | |
{ | |
constructing!( "Agent", "installer::main()", "New Agent" ); | |
let path = env::current_dir().unwrap(); | |
let mut agent = Agent | |
{ | |
alias: HashMap::new(), | |
profile:String::new(), | |
dir: format!("{}", path.display()), | |
previous_dir: format!("{}", path.display()), | |
cmd: String::new(), | |
previous_cmd: String::new(), | |
status, | |
previous_status: 0 | |
}; | |
constructing!( "Agent", "installer::main()", "Agent Constructed; Cloning" ); | |
agent.clone() | |
} | |
// pub fn comprises( &mut self, from: &str, to: &str ) -> bool | |
pub fn re_contains( &mut self, line: &str, ptn: &str ) -> bool | |
{ | |
let re; | |
match Regex::new( ptn ) | |
{ | |
Ok(x) => { re = x; } | |
Err(e) => | |
{ | |
println!("Regex new: {:?}", e); | |
return false; | |
} | |
} | |
re.is_match( line ) | |
} | |
pub fn get_user_home( &mut self ) -> String | |
{ | |
match env::var("HOME") | |
{ | |
Ok(x) => x, | |
Err(e) => | |
{ | |
println!("installer: env HOME error: {:?}", e); | |
String::new() | |
} | |
} | |
} | |
pub fn add_alias( &mut self, name: &str, value: &str ) | |
{ | |
self.alias.insert( name.to_string(), value.to_string() ); | |
} | |
pub fn get_alias_content( &mut self, name: &str) -> Option<String> | |
{ | |
let mut result; | |
match self.alias.get(name) { | |
Some(x) => { | |
result = x.to_string(); | |
} | |
None => { | |
result = String::new(); | |
} | |
} | |
self.pre_handle_cmd_line( &mut result ); | |
if result.is_empty() { None } else { Some(result) } | |
} | |
pub fn library_cmd_to_tokens( &mut self ) -> Agent | |
{ | |
self.clone() | |
} | |
pub fn line_tokens_to_cmd_tokens( &mut self, tokens: &Tokens) -> Vec<Tokens> | |
{ | |
let mut cmd = Vec::new(); | |
let mut cmds = Vec::new(); | |
for token in tokens { | |
let sep = &token.0; | |
let value = &token.1; | |
if sep.is_empty() && value == "|" { | |
if cmd.is_empty() { | |
return Vec::new(); | |
} | |
cmds.push(cmd.clone()); | |
cmd = Vec::new(); | |
} else { | |
cmd.push(token.clone()); | |
} | |
} | |
if cmd.is_empty() { | |
return Vec::new(); | |
} | |
cmds.push(cmd.clone()); | |
cmds | |
} | |
pub fn line_cmd_to_with_redirects( &mut self, tokens: &Tokens) -> Result<Command, String> | |
{ | |
let mut tokens_new = Vec::new(); | |
let mut redirects = Vec::new(); | |
let mut to_be_continued = false; | |
let mut to_be_continued_s1 = String::new(); | |
let mut to_be_continued_s2 = String::new(); | |
for token in tokens { | |
let sep = &token.0; | |
if !sep.is_empty() && !to_be_continued { | |
tokens_new.push(token.clone()); | |
continue; | |
} | |
let word = &token.1; | |
if to_be_continued { | |
if sep.is_empty() && word.starts_with('&') { | |
return Err(String::from("bad redirection syntax near &")); | |
} | |
let s3 = format!("{}{}{}", sep, word, sep); | |
if self.re_contains(&to_be_continued_s1, r"^\d+$") { | |
if to_be_continued_s1 != "1" && to_be_continued_s1 != "2" { | |
return Err(String::from("Bad file descriptor #3")); | |
} | |
let s1 = to_be_continued_s1.clone(); | |
let s2 = to_be_continued_s2.clone(); | |
redirects.push((s1, s2, s3)); | |
} else { | |
if to_be_continued_s1 != "" { | |
tokens_new.push((sep.clone(), to_be_continued_s1.to_string())); | |
} | |
redirects.push(("1".to_string(), to_be_continued_s2.clone(), s3)); | |
} | |
to_be_continued = false; | |
continue; | |
} | |
let ptn1 = r"^([^>]*)(>>?)([^>]+)$"; | |
let ptn2 = r"^([^>]*)(>>?)$"; | |
if !self.re_contains(word, r">") { | |
tokens_new.push(token.clone()); | |
} else if self.re_contains(word, ptn1) { | |
let re; | |
if let Ok(x) = Regex::new(ptn1) { | |
re = x; | |
} else { | |
return Err(String::from("Failed to build Regex")); | |
} | |
if let Some(caps) = re.captures(&word) { | |
let s1 = caps.get(1).unwrap().as_str(); | |
let s2 = caps.get(2).unwrap().as_str(); | |
let s3 = caps.get(3).unwrap().as_str(); | |
if s3.starts_with('&') && s3 != "&1" && s3 != "&2" { | |
return Err(String::from("Bad file descriptor #1")); | |
} | |
if self.re_contains(s1, r"^\d+$") { | |
if s1 != "1" && s1 != "2" { | |
return Err(String::from("Bad file descriptor #2")); | |
} | |
redirects.push((s1.to_string(), s2.to_string(), s3.to_string())); | |
} else { | |
if s1 != "" { | |
tokens_new.push((sep.clone(), s1.to_string())); | |
} | |
redirects.push((String::from("1"), s2.to_string(), s3.to_string())); | |
} | |
} | |
} else if self.re_contains(word, ptn2) { | |
let re; | |
if let Ok(x) = Regex::new(ptn2) { | |
re = x; | |
} else { | |
return Err(String::from("Failed to build Regex")); | |
} | |
if let Some(caps) = re.captures(&word) { | |
let s1 = caps.get(1).unwrap().as_str(); | |
let s2 = caps.get(2).unwrap().as_str(); | |
to_be_continued = true; | |
to_be_continued_s1 = s1.to_string(); | |
to_be_continued_s2 = s2.to_string(); | |
} | |
} | |
} | |
if to_be_continued { | |
return Err(String::from("redirection syntax error")); | |
} | |
Ok(Command { | |
tokens: tokens_new, | |
redirects, | |
}) | |
} | |
pub fn line_tokens_to_args( &mut self, tokens: &Vec<(String, String)>) -> Vec<String> | |
{ | |
let mut result = Vec::new(); | |
for s in tokens | |
{ | |
result.push(s.1.clone()); | |
} | |
result | |
} | |
pub fn lines_to_cmds( &mut self, line: &str) -> Vec<String> | |
{ | |
// Special characters: http://tldp.org/LDP/abs/html/special-chars.html | |
let mut result = Vec::new(); | |
let mut sep = String::new(); | |
let mut token = String::new(); | |
let len = line.len(); | |
for (i, c) in line.chars().enumerate() { | |
if c == '#' { | |
if sep.is_empty() { | |
break; | |
} else { | |
token.push(c); | |
continue; | |
} | |
} | |
if c == '\'' || c == '"' || c == '`' { | |
if sep.is_empty() { | |
sep.push(c); | |
token.push(c); | |
continue; | |
} else if sep == c.to_string() { | |
token.push(c); | |
sep = String::new(); | |
continue; | |
} else { | |
token.push(c); | |
continue; | |
} | |
} | |
if c == '&' || c == '|' { | |
// needs watch ahead here | |
if sep.is_empty() { | |
if i + 1 == len { | |
// for bg commands, e.g. `ls &` | |
token.push(c); | |
continue; | |
} else { | |
let c_next; | |
match line.chars().nth(i + 1) { | |
Some(x) => c_next = x, | |
None => { | |
println!("chars nth error - should never happen"); | |
continue; | |
} | |
} | |
if c_next != c { | |
token.push(c); | |
continue; | |
} | |
} | |
} | |
if sep.is_empty() { | |
sep.push(c); | |
continue; | |
} else if c.to_string() == sep { | |
let _token = token.trim().to_string(); | |
if !_token.is_empty() { | |
result.push(_token); | |
} | |
token = String::new(); | |
result.push(format!("{}{}", sep, sep)); | |
sep = String::new(); | |
continue; | |
} else { | |
token.push(c); | |
continue; | |
} | |
} | |
if c == ';' { | |
if sep.is_empty() { | |
let _token = token.trim().to_string(); | |
if !_token.is_empty() { | |
result.push(_token); | |
} | |
result.push(String::from(";")); | |
token = String::new(); | |
continue; | |
} else { | |
token.push(c); | |
continue; | |
} | |
} | |
token.push(c); | |
} | |
if !token.is_empty() { | |
result.push(token.trim().to_string()); | |
} | |
result | |
} | |
#[allow(cyclomatic_complexity)] | |
pub fn line_cmd_to_tokens( &mut self, line: &str ) -> Vec<(String, String)> | |
{ | |
let mut result = Vec::new(); | |
let mut sep = String::new(); | |
// `sep_second` is for commands like this: | |
// export DIR=`brew --prefix openssl`/include | |
// it only could have non-empty value when sep is empty. | |
let mut sep_second = String::new(); | |
let mut token = String::new(); | |
let mut has_backslash = false; | |
let mut new_round = true; | |
let mut skip_next = false; | |
let count_chars = line.chars().count(); | |
for (i, c) in line.chars().enumerate() { | |
if skip_next { | |
skip_next = false; | |
continue; | |
} | |
if c == '\\' && sep != "\'" { | |
if !has_backslash { | |
has_backslash = true; | |
} else { | |
has_backslash = false; | |
token.push(c); | |
} | |
continue; | |
} | |
if new_round { | |
if c == ' ' { | |
continue; | |
} else if c == '"' || c == '\'' || c == '`' { | |
sep = c.to_string(); | |
} else { | |
sep = String::new(); | |
// handle inline comments | |
if c == '#' { | |
if has_backslash { | |
has_backslash = false; | |
token.push(c); | |
continue; | |
} | |
break; | |
} | |
if c == '|' { | |
if i + 1 < count_chars && line.chars().nth(i + 1).unwrap() == '|' { | |
result.push((String::from(""), "||".to_string())); | |
skip_next = true; | |
} else { | |
result.push((String::from(""), "|".to_string())); | |
} | |
new_round = true; | |
continue; | |
} else { | |
token.push(c); | |
} | |
} | |
new_round = false; | |
continue; | |
} | |
if c == '|' && !has_backslash && sep.is_empty() { | |
result.push((String::from(""), token)); | |
result.push((String::from(""), "|".to_string())); | |
sep = String::new(); | |
sep_second = String::new(); | |
token = String::new(); | |
new_round = true; | |
continue; | |
} | |
if c == ' ' { | |
if has_backslash { | |
has_backslash = false; | |
token.push(c); | |
if sep.is_empty() { | |
sep = String::from("\""); | |
} | |
continue; | |
} | |
if sep.is_empty() { | |
if sep_second.is_empty() { | |
result.push((String::from(""), token)); | |
token = String::new(); | |
new_round = true; | |
continue; | |
} else { | |
token.push(c); | |
continue; | |
} | |
} else { | |
token.push(c); | |
continue; | |
} | |
} | |
if c == '\'' || c == '"' || c == '`' { | |
if has_backslash { | |
has_backslash = false; | |
token.push(c); | |
continue; | |
} | |
if sep.is_empty() { | |
if c == '\'' || c == '"' { | |
sep = c.to_string(); | |
continue; | |
} | |
token.push(c); | |
if sep_second.is_empty() { | |
sep_second = c.to_string(); | |
} else if sep_second == c.to_string() { | |
sep_second = String::new(); | |
} | |
continue; | |
} else if sep == c.to_string() { | |
result.push((c.to_string(), token)); | |
sep = String::new(); | |
sep_second = String::new(); | |
token = String::new(); | |
new_round = true; | |
continue; | |
} else { | |
token.push(c); | |
} | |
} else { | |
if has_backslash { | |
has_backslash = false; | |
if sep == "\"" || sep == "'" { | |
token.push('\\'); | |
} | |
} | |
token.push(c); | |
} | |
} | |
if !token.is_empty() { | |
result.push((sep, token)); | |
} | |
result | |
} | |
pub fn line_to_plain_tokens( &mut self, line: &str ) -> Vec<String> | |
{ | |
let mut result = Vec::new(); | |
let v = self.line_cmd_to_tokens( line ); | |
for (_, r) in v | |
{ | |
result.push(r); | |
} | |
result | |
} | |
pub fn unquote( &mut self, s: &str ) -> String | |
{ | |
let args = self.line_to_plain_tokens( s ); | |
if args.is_empty() | |
{ | |
return String::new(); | |
} | |
args[0].clone() | |
} | |
pub fn find_first_group( &mut self, ptn: &str, text: &str) -> Option<String> | |
{ | |
let re; | |
match regex::Regex::new(ptn) { | |
Ok(x) => re = x, | |
Err(_) => return None, | |
} | |
match re.captures( text ) | |
{ | |
Some(caps) => | |
{ | |
if let Some(x) = caps.get(1) { return Some(x.as_str().to_owned()); } | |
} | |
None => { return None; } | |
} | |
None | |
} | |
pub fn wrap_sep_string( &mut self, sep: &str, s: &str ) -> String | |
{ | |
let mut _token = String::new(); | |
for c in s.chars() | |
{ | |
if c.to_string() == sep { _token.push('\\'); } | |
_token.push(c); | |
} | |
format!("{}{}{}", sep, _token, sep) | |
} | |
// pub fn numerical( &mut self, line: &str ) -> bool | |
pub fn is_arithmetic( &mut self, line: &str ) -> bool { self.re_contains(line, r"^[ 0-9\.\(\)\+\-\*/]+$") } | |
pub fn is_env( &mut self, line: &str ) -> bool | |
{ | |
self.re_contains( line, r"^ *export +[a-zA-Z0-9_]+=.*$" ) | |
} | |
pub fn is_alias( &mut self, line: &str ) -> bool | |
{ | |
self.re_contains( line, r"^ *alias +[a-zA-Z0-9_\.-]+=.*$") | |
} | |
#[allow(trivial_regex)] | |
pub fn extend_bandband( &mut self, line: &mut String) | |
{ | |
if !self.re_contains(line, r"!!") { return; } | |
if self.previous_cmd.is_empty() { return; } | |
let re; | |
match Regex::new(r"!!") | |
{ | |
Ok(x) => { re = x; } | |
Err(e) => { println_stderr!("installer::extend_bandband: Regex new: {:?}", e); return; } | |
} | |
let mut replaced = false; | |
let mut new_line = String::new(); | |
let tokens = self.line_cmd_to_tokens(line); | |
for (sep, token) in tokens | |
{ | |
if !sep.is_empty() { new_line.push_str(&sep); } | |
if self.re_contains(&token, r"!!") && sep != "'" | |
{ | |
let line2 = token.clone(); | |
let result = re.replace_all(&line2, self.previous_cmd.as_str()); | |
new_line.push_str(&result); | |
replaced = true; | |
} | |
else { new_line.push_str(&token); } | |
if !sep.is_empty() { new_line.push_str(&sep); } | |
new_line.push(' '); | |
} | |
*line = new_line.trim_right().to_string(); | |
// print full line after extending | |
if replaced { println!("{}", line); } | |
} | |
// pub fn housing( &mut self, line: &str) -> bool | |
pub fn needs_extend_home( &mut self, line: &str) -> bool { | |
self.re_contains(line, r"( +~ +)|( +~/)|(^ *~/)|( +~ *$)") | |
} | |
pub fn needs_globbing( &mut self, line: &str ) -> bool { | |
if self.is_arithmetic(line){ return false; } | |
let re; | |
if let Ok(x) = Regex::new(r"\*+") { re = x; } | |
else { return false; } | |
let tokens = self.line_cmd_to_tokens(line); | |
for (sep, token) in tokens | |
{ | |
if !sep.is_empty() { continue; } | |
if re.is_match(&token) { return true; } | |
} | |
false | |
} | |
pub fn should_do_brace_command_extension( &mut self, line: &str ) -> bool { self.re_contains(line, r"\$\([^\)]+\)") } | |
pub fn should_extend_brace( &mut self, line: &str ) -> bool { | |
self.re_contains(line, r"\{.*,.*\}") | |
} | |
pub fn extend_home( &mut self, s: &mut String) | |
{ | |
if !self.needs_extend_home(s) { return; } | |
let v = vec![ | |
r"(?P<head> +)~(?P<tail> +)", | |
r"(?P<head> +)~(?P<tail>/)", | |
r"^(?P<head> *)~(?P<tail>/)", | |
r"(?P<head> +)~(?P<tail> *$)", | |
]; | |
for item in &v | |
{ | |
let re; | |
if let Ok( x ) = Regex::new( item ) { re = x; } | |
else { return; } | |
let home = self.get_user_home(); | |
let ss = s.clone(); | |
let to = format!("$head{}$tail", home); | |
let result = re.replace_all( ss.as_str(), to.as_str() ); | |
*s = result.to_string(); | |
} | |
} | |
pub fn do_brace_expansion( &mut self, line: &mut String) | |
{ | |
let _line = line.clone(); | |
let args = self.line_cmd_to_tokens(_line.as_str() ); | |
let mut result: Vec<String> = Vec::new(); | |
for (sep, token) in args | |
{ | |
if sep.is_empty() && self.should_extend_brace( token.as_str() ) | |
{ | |
let mut _prefix = String::new(); | |
let mut _token = String::new(); | |
let mut _result = Vec::new(); | |
let mut only_tail_left = false; | |
let mut start_sign_found = false; | |
for c in token.chars() | |
{ | |
if c == '{' | |
{ | |
start_sign_found = true; | |
continue; | |
} | |
if !start_sign_found { | |
_prefix.push(c); | |
continue; | |
} | |
if only_tail_left { | |
_token.push(c); | |
continue; | |
} | |
if c == '}' { | |
if !_token.is_empty() { | |
_result.push(_token); | |
_token = String::new(); | |
} | |
only_tail_left = true; | |
continue; | |
} | |
if c == ',' | |
{ | |
if !_token.is_empty() { | |
_result.push(_token); | |
_token = String::new(); | |
} | |
} | |
else { _token.push(c); } | |
} | |
for item in &mut _result { *item = format!("{}{}{}", _prefix, item, _token ); } | |
result.push( self.wrap_sep_string(sep.as_str(), _result.join(" ").as_str()) ); | |
} | |
else { result.push( self.wrap_sep_string(sep.as_str(), token.as_str()) ); } | |
} | |
*line = result.join(" "); | |
} | |
pub fn do_command_substitution_for_dollar( &mut self, line: &mut String) | |
{ | |
loop | |
{ | |
if !self.should_do_brace_command_extension(&line) { break; } | |
let ptn_cmd = r"\$\(([^\(]+)\)"; | |
let cmd; | |
match self.find_first_group( ptn_cmd, &line ) | |
{ | |
Some(x) => { cmd = x; } | |
None => { println_stderr!("installer: no first group"); return; } | |
} | |
let _args = self.line_cmd_to_tokens(&cmd); | |
let (_, _, output) = self.run_pipeline(_args, "", false, false, true, None); | |
let _stdout; | |
let output_txt; | |
if let Some(x) = output | |
{ | |
match String::from_utf8(x.stdout) | |
{ | |
Ok(stdout) => | |
{ | |
_stdout = stdout.clone(); | |
output_txt = _stdout.trim(); | |
} | |
Err(_) => { println_stderr!("installer: from_utf8 error"); return; } | |
} | |
} | |
else { println_stderr!("installer: command error"); return; } | |
let ptn = r"(?P<head>[^\$]*)\$\([^\(]+\)(?P<tail>.*)"; | |
let re; | |
if let Ok(x) = Regex::new( ptn ) { re = x; } | |
else { return; } | |
let to = format!("${{head}}{}${{tail}}", output_txt); | |
let line_ = line.clone(); | |
let result = re.replace(&line_, to.as_str()); | |
*line = result.to_string(); | |
} | |
} | |
pub fn do_command_substitution_for_dot( &mut self, line: &mut String) | |
{ | |
let tokens = self.line_cmd_to_tokens( &line ); | |
let mut result: Vec<String> = Vec::new(); | |
for (sep, token) in tokens | |
{ | |
if sep == "`" | |
{ | |
let _args = self.line_cmd_to_tokens(token.as_str()); | |
let (_, _, output) = self.run_pipeline(_args, "", false, false, true, None); | |
if let Some(x) = output | |
{ | |
match String::from_utf8(x.stdout) { | |
Ok(stdout) => { | |
let _txt = self.wrap_sep_string(sep.as_str(), stdout.trim()); | |
result.push(_txt); | |
} | |
Err(_) => { | |
println_stderr!("installer: from_utf8 error"); | |
result.push(self.wrap_sep_string(sep.as_str(), token.as_str())); | |
} | |
} | |
} | |
else { println_stderr!("installer: command error"); result.push( self.wrap_sep_string(sep.as_str(), token.as_str())); } | |
} | |
else if sep == "\"" || sep.is_empty() | |
{ | |
let re; | |
if let Ok(x) = Regex::new(r"^([^`]*)`([^`]+)`(.*)$") { re = x; } | |
else { println_stderr!("installer: re new error"); return; } | |
if !re.is_match( &token ) | |
{ | |
result.push( self.wrap_sep_string(sep.as_str(), token.as_str()) ); | |
continue; | |
} | |
let mut _token = token.clone(); | |
let mut _item = String::new(); | |
let mut _head = String::new(); | |
let mut _output = String::new(); | |
let mut _tail = String::new(); | |
loop | |
{ | |
if !re.is_match(&_token) | |
{ | |
if !_token.is_empty() { | |
_item = format!("{}{}", _item, _token); | |
} | |
break; | |
} | |
for cap in re.captures_iter(&_token) | |
{ | |
_head = cap[1].to_string(); | |
_tail = cap[3].to_string(); | |
let _args = self.line_cmd_to_tokens(&cap[2]); | |
let (_, _, output) = self.run_pipeline(_args, "", false, false, true, None); | |
if let Some(x) = output | |
{ | |
match String::from_utf8(x.stdout) | |
{ | |
Ok(stdout) => { | |
_output = stdout.trim().to_string(); | |
} | |
Err(_) => { | |
println_stderr!("installer: from_utf8 error"); | |
result.push( self.wrap_sep_string(sep.as_str(), token.as_str()) ); | |
return; | |
} | |
} | |
} | |
else | |
{ | |
println_stderr!("installer: command error: {}", token); | |
result.push( self.wrap_sep_string(sep.as_str(), token.as_str()) ); | |
return; | |
} | |
} | |
_item = format!("{}{}{}", _item, _head, _output); | |
if _tail.is_empty() { | |
break; | |
} | |
_token = _tail.clone(); | |
} | |
result.push( self.wrap_sep_string(sep.as_str(), &_item)); | |
} | |
else { result.push( self.wrap_sep_string(sep.as_str(), token.as_str()) ); } | |
} | |
*line = result.join(" "); | |
} | |
pub fn do_command_substitution( &mut self, line: &mut String ) | |
{ | |
self.do_command_substitution_for_dot(line); | |
self.do_command_substitution_for_dollar(line); | |
} | |
pub fn extend_glob( &mut self, line: &mut String ) | |
{ | |
if !self.needs_globbing( &line ) { return; } | |
let _line = line.clone(); | |
// XXX: spliting needs to consider cases like `echo 'a * b'` | |
let _tokens: Vec<&str> = _line.split(' ').collect(); | |
let mut result: Vec<String> = Vec::new(); | |
for item in &_tokens | |
{ | |
if !item.contains('*') || item.trim().starts_with('\'') || item.trim().starts_with('"') | |
{ result.push( item.to_string() ); } | |
else | |
{ | |
match glob::glob( item ) | |
{ | |
Ok(paths) => | |
{ | |
let mut is_empty = true; | |
for entry in paths | |
{ | |
match entry | |
{ | |
Ok(path) => | |
{ | |
let s = path.to_string_lossy(); | |
if !item.starts_with('.') && s.starts_with('.') && !s.contains('/') | |
{ | |
// skip hidden files, you may need to | |
// type `ls .*rc` instead of `ls *rc` | |
continue; | |
} | |
result.push(s.into_owned()); | |
is_empty = false; | |
} | |
Err(e) => { log!("installer::glob error: {:?}", e); } | |
} | |
} | |
if is_empty { result.push( item.to_string() ); } | |
} | |
Err(e) => | |
{ | |
println!("installer::glob error: {:?}", e); | |
result.push(item.to_string()); | |
return; | |
} | |
} | |
} | |
} | |
*line = result.join(" "); | |
} | |
pub fn in_env( &mut self ) -> bool | |
{ | |
if let Ok(x) = env::var("VIRTUAL_ENV") { if x != "" { return true; } } | |
false | |
} | |
pub fn get_envs_home( &mut self ) -> String | |
{ | |
let home_envs; | |
match env::var("VIRTUALENV_HOME") | |
{ | |
Ok( x ) => { home_envs = x; } | |
Err( _ ) => { home_envs = String::new(); } | |
} | |
home_envs | |
} | |
pub fn list_envs( &mut self ) -> i32 | |
{ | |
let home_envs = self.get_envs_home(); | |
if home_envs == "" { println!("installer::list_env::you need to set VIRTUALENV_HOME to use vox"); return 1; } | |
if !Path::new(home_envs.as_str()).exists() | |
{ | |
match fs::create_dir_all(home_envs.as_str()) | |
{ | |
Ok(_) => {} | |
Err(e) => println!("installer::list_env::fs create_dir_all failed: {:?}", e), | |
} | |
} | |
println!("Envs under: {}", home_envs); | |
let pdir = home_envs.clone(); | |
if let Ok(list) = read_dir(home_envs) | |
{ | |
for ent in list | |
{ | |
if let Ok(ent) = ent | |
{ | |
let ent_name = ent.file_name(); | |
if let Ok(path) = ent_name.into_string() | |
{ | |
let full_path = format!("{}/{}/bin/activate", pdir, path); | |
if !Path::new(full_path.as_str()).exists() { continue; } | |
println!("{}", path); | |
} | |
} | |
} | |
} | |
0 | |
} | |
pub fn enter_env( &mut self, path: &str ) -> i32 | |
{ | |
if self.in_env() { println!("vox: already in env"); return 1; } | |
let home_envs = self.get_envs_home(); | |
let full_path = format!("{}/{}/bin/activate", home_envs, path); | |
if !Path::new(full_path.as_str()).exists() { println!("no such env: {}", full_path); return 1; } | |
let path_env = format!("{}/{}", home_envs, path); | |
env::set_var("VIRTUAL_ENV", &path_env); | |
let mut path_new = String::from("${VIRTUAL_ENV}/bin:$PATH"); | |
self.extend_env( &mut path_new ); | |
env::set_var("PATH", &path_new); | |
0 | |
} | |
pub fn exit_env( &mut self ) -> i32 | |
{ | |
if !self.in_env() { println!("installer::vox: not in an env"); return 0; } | |
let env_path; | |
match env::var("PATH") | |
{ | |
Ok(x) => env_path = x, | |
Err(_) => { println!("installer::vox: cannot read PATH env"); return 1; } | |
} | |
let mut _tokens: Vec<&str> = env_path.split(':').collect(); | |
let mut path_virtual_env = String::from("${VIRTUAL_ENV}/bin"); | |
self.extend_env( &mut path_virtual_env ); | |
_tokens.iter() | |
.position(|&n| n == path_virtual_env) | |
.map(|e| _tokens.remove(e)); | |
let env_path_new = _tokens.join(":"); | |
env::set_var("PATH", &env_path_new); | |
env::set_var("VIRTUAL_ENV", ""); | |
0 | |
} | |
pub fn extend_env_blindly( &mut self, token: &str ) -> String | |
{ | |
let re; | |
if let Ok( x ) = Regex::new(r"([^\$]*)\$\{?([A-Za-z0-9\?\$_]+)\}?(.*)") { re = x; } | |
else{ println!("installer::extend_env_blindly: re new error"); return String::new(); } | |
if !re.is_match( token ) { return token.to_string(); } | |
let mut result = String::new(); | |
let mut _token = token.to_string(); | |
let mut _head = String::new(); | |
let mut _output = String::new(); | |
let mut _tail = String::new(); | |
loop | |
{ | |
if !re.is_match(&_token) | |
{ | |
if !_token.is_empty() { result.push_str( &_token ); } | |
break; | |
} | |
for cap in re.captures_iter( &_token ) | |
{ | |
_head = cap[1].to_string(); | |
_tail = cap[3].to_string(); | |
let _key = cap[2].to_string(); | |
if _key == "?" { result.push_str(format!("{}{}", _head, self.previous_status ).as_str()); } | |
else if _key == "$" | |
{ | |
unsafe | |
{ | |
let val = libc::getpid(); | |
result.push_str( format!("{}{}", _head, val).as_str() ); | |
} | |
} | |
else if let Ok( val ) = env::var( _key ) { result.push_str(format!("{}{}", _head, val).as_str()); } | |
else { result.push_str( &_head ); } | |
} | |
if _tail.is_empty(){ break; } | |
_token = _tail.clone(); | |
} | |
result | |
} | |
pub fn extend_env( &mut self, line: &mut String ) | |
{ | |
let mut result: Vec<String> = Vec::new(); | |
let _line = line.clone(); | |
let args = self.line_cmd_to_tokens( _line.as_str() ); | |
for (sep, token) in args | |
{ | |
if sep == "`" || sep == "'" { result.push( self.wrap_sep_string( &sep, &token ) ); } | |
else | |
{ | |
let _token = self.extend_env_blindly( &token ); | |
result.push( self.wrap_sep_string( &sep, &_token ) ); | |
} | |
} | |
*line = result.join(" "); | |
} | |
pub fn extend_alias( &mut self, line: &str ) -> String | |
{ | |
let cmds = self.lines_to_cmds( line ); | |
let mut seps_cmd: HashSet<&str> = HashSet::new(); | |
seps_cmd.insert(";"); | |
seps_cmd.insert("&&"); | |
seps_cmd.insert("||"); | |
let mut result = String::new(); | |
for ( _, cmd ) in cmds.iter().enumerate() | |
{ | |
if seps_cmd.contains(cmd.as_str()) { | |
result.push(' '); | |
result.push_str(cmd); | |
result.push(' '); | |
continue; | |
} | |
let tokens = self.line_cmd_to_tokens( cmd ); | |
let mut is_cmd = false; | |
for (i, token) in tokens.iter().enumerate() | |
{ | |
let sep = &token.0; | |
let arg = &token.1; | |
if !sep.is_empty() | |
{ | |
is_cmd = false; | |
result.push(' '); | |
result.push_str(&sep); | |
let replace_to = format!("\\{}", sep); | |
result.push_str(&arg.replace(sep, &replace_to)); | |
result.push_str(&sep); | |
continue; | |
} | |
if i == 0 { | |
is_cmd = true; | |
} else if arg == "|" | |
{ | |
result.push(' '); | |
result.push_str(&arg); | |
is_cmd = true; | |
continue; | |
} | |
if !is_cmd | |
{ | |
result.push(' '); | |
result.push_str(&arg); | |
continue; | |
} | |
let extended; | |
match self.get_alias_content(arg) | |
{ | |
Some(_extended) => { extended = _extended; } | |
None => { extended = arg.clone(); } | |
} | |
if i > 0 { result.push(' '); } | |
result.push_str(&extended); | |
is_cmd = false; | |
} | |
} | |
result | |
} | |
pub fn run_calc_int( &mut self, line: &str ) -> Result<i64, String> | |
{ | |
match expr_int(line.as_bytes()) { | |
IResult::Done(_, x) => Ok(x), | |
IResult::Error(e) => Err(e.description().to_owned()), | |
IResult::Incomplete(_) => Err(String::from("Incomplete arithmetic")), | |
} | |
} | |
pub fn run_calc_float( &mut self, line: &str ) -> Result<f64, String> | |
{ | |
match expr_float( line.as_bytes() ) | |
{ | |
IResult::Done( _, x ) => Ok( x ), | |
IResult::Error( e ) => Err( e.description().to_owned() ), | |
IResult::Incomplete( _ ) => Err( String::from("Incomplete arithmetic") ), | |
} | |
} | |
pub fn env_args_to_command_line( &mut self ) -> String | |
{ | |
let mut result = String::new(); | |
let env_args = env::args(); | |
if env_args.len() <= 1 { | |
return result; | |
} | |
for (i, arg) in env_args.enumerate() { | |
if i == 0 || arg == "-c" { | |
continue; | |
} | |
result.push_str(arg.as_str()); | |
} | |
result | |
} | |
pub fn remove_envs_from_line( &mut self, line: &str, envs: &mut HashMap<String, String> ) -> String | |
{ | |
let mut result = line.to_string(); | |
while let Some(x) = self.find_first_group(r"^( *[a-zA-Z][a-zA-Z0-9_]+=[^ ]*)", &result ) | |
{ | |
let v: Vec<&str> = x.split('=').collect(); | |
if v.len() != 2 { println_stderr!("remove envs error"); break; } | |
envs.insert(v[0].to_string(), v[1].to_string()); | |
result = result.trim().replace(&x, "").trim().to_owned(); | |
} | |
result | |
} | |
pub fn get_user_completer_dir( &mut self ) -> String { format!("{}/_.installer/completers", self.get_user_home() ) } | |
pub fn get_rc_file( &mut self ) -> String{ format!("{}/{}", self.get_user_home(), ".installerc") } | |
pub fn pre_handle_cmd_line( &mut self, line: &mut String ) | |
{ | |
self.extend_home(line); | |
self.do_brace_expansion( line ); | |
self.extend_glob( line ); | |
self.extend_env( line ); | |
self.do_command_substitution( line ); | |
} | |
pub fn historical( &mut self ) -> ( String, String ) | |
{ | |
( self.get_history_file(), self.get_history_table() ) | |
} | |
pub fn epoch( &mut self, rl: &mut Interface<DefaultTerminal>) | |
{ | |
let mut hist_size: usize = 999; | |
if let Ok(x) = env::var("HISTORY_SIZE") { if let Ok(y) = x.parse::<usize>() { hist_size = y; } } | |
rl.set_history_size(hist_size); | |
//let history_table = self.get_history_table(); let hfile = self.get_history_file(); | |
let ( hfile, history_table ) = self.historical(); | |
let path = Path::new(hfile.as_str()); | |
if !path.exists() | |
{ | |
let _parent; | |
match path.parent() { | |
Some(x) => _parent = x, | |
None => { | |
println!("installer::epoch: history init - no parent found"); | |
return; | |
} | |
} | |
let parent; | |
match _parent.to_str() | |
{ | |
Some(x) => parent = x, | |
None => { | |
println!("installer::epoch: parent to_str is None"); | |
return; | |
} | |
} | |
match fs::create_dir_all(parent) | |
{ | |
Ok(_) => {} | |
Err(e) => { | |
println!("installer::epoch: dirs create failed: {:?}", e); | |
return; | |
} | |
} | |
match fs::File::create(hfile.as_str()) { | |
Ok(_) => {} | |
Err(e) => { | |
println!("installer::epoch: file create failed: {:?}", e); | |
} | |
} | |
} | |
let mut histories: HashMap<String, bool> = HashMap::new(); | |
match sqlite::open(hfile.clone()) | |
{ | |
Ok(conn) => { | |
let sql_create = format!( | |
" | |
CREATE TABLE IF NOT EXISTS {} | |
(inp TEXT, | |
rtn INTEGER, | |
tsb REAL, | |
tse REAL, | |
sessionid TEXT, | |
out TEXT, | |
info TEXT | |
); | |
", | |
history_table | |
); | |
match conn.execute(sql_create) { | |
Ok(_) => {} | |
Err(e) => println_stderr!("installer: sqlite exec error - {:?}", e), | |
} | |
if let Ok(x) = env::var("HISTORY_DELETE_DUPS") { | |
if x == "1" { | |
self.delete_duplicated_histories(); | |
} | |
} | |
let sql_select = format!("SELECT inp FROM {} ORDER BY tsb;", history_table,); | |
match conn.iterate(sql_select, |pairs| { | |
for &(_, value) in pairs.iter() { | |
let inp; | |
match value { | |
Some(x) => inp = x, | |
None => { | |
println!("installer: sqlite pairs None"); | |
continue; | |
} | |
} | |
let _k = inp.to_string(); | |
if histories.contains_key(&_k) { | |
continue; | |
} | |
histories.insert(_k, true); | |
rl.add_history(inp.trim().to_string()); | |
} | |
true | |
}) { | |
Ok(_) => {} | |
Err(e) => println_stderr!("installer: sqlite select error - {:?}", e), | |
} | |
} | |
Err(e) =>{ println_stderr!("installer::epoch: sqlite conn error - {:?}", e); } | |
} | |
} | |
pub fn historic( &mut self, rl: &mut Interface<DefaultTerminal>, line: &str, status: i32, tsb: f64, tse: f64 ) | |
{ | |
self.previous_status = status; | |
if line == self.previous_cmd { return; } | |
rl.add_history(line.to_string()); | |
self.previous_cmd = line.to_string(); | |
let ( hfile, history_table ) = self.historical(); //let hfile = self.get_history_file(); let history_table = self.get_history_table(); | |
let conn; | |
match sqlite::open( hfile ) | |
{ | |
Ok(x) => conn = x, | |
Err(e) => | |
{ | |
println!("installer::historic: sqlite open db error: {:?}", e); | |
return; | |
} | |
} | |
let sql = format! | |
( | |
"INSERT INTO {} (inp, rtn, tsb, tse, sessionid) VALUES('{}', {}, {}, {}, '{}');", | |
history_table, str::replace( line.trim(), "'", "''" ), status, tsb, tse, "installer" | |
); | |
match conn.execute( sql ) | |
{ | |
Ok(_) => {} | |
Err(e) => println!("installer::historic: failed to save history: {:?}", e), | |
} | |
} | |
pub fn get_history_file( &mut self ) -> String | |
{ | |
if let Ok( hfile ) = env::var("HISTORY_FILE") { return hfile; } | |
else if let Ok( d ) = env::var("XDG_DATA_HOME") { return format!("{}/{}", d, "installer/history.sqlite"); } | |
else { let home = self.get_user_home(); return format!("{}/{}", home, ".local/share/installer/history.sqlite"); } | |
} | |
pub fn get_history_table( &mut self ) -> String | |
{ | |
if let Ok( hfile ) = env::var("HISTORY_TABLE") { return hfile; } | |
else { return String::from("installer_history"); } | |
} | |
pub fn delete_duplicated_histories( &mut self ) | |
{ | |
let ( hfile, history_table ) = self.historical(); //let hfile = self.get_history_file(); let history_table = self.get_history_table(); | |
match sqlite::open( hfile.clone() ) | |
{ | |
Ok( conn ) => | |
{ | |
let sql = format! | |
( | |
"DELETE FROM {} WHERE rowid NOT IN ( SELECT MAX(rowid) FROM {} GROUP BY inp)", | |
history_table, history_table | |
); | |
match conn.execute( sql ) | |
{ | |
Ok(_) => {} | |
Err(e) => println_stderr!("installer: sqlite exec error - {:?}", e), | |
} | |
} | |
Err(e) => println_stderr!("installer: sqlite open file error - {:?}", e), | |
} | |
} | |
pub fn list_current_history( &mut self, conn: &sqlite::Connection ) -> i32 | |
{ | |
let history_table = self.get_history_table(); | |
let q = format!( "SELECT inp FROM {} ORDER BY tsb desc limit 20;", history_table ); | |
match conn.prepare( q ) | |
{ | |
Ok(mut statement) => | |
{ | |
let mut vec = Vec::new(); | |
loop | |
{ | |
match statement.next() | |
{ | |
Ok( x ) => | |
{ | |
if let State::Row = x { if let Ok(_x) = statement.read::<String>(0) { vec.push( _x ); } } | |
else { break; } | |
} | |
Err(e) => { println_stderr!("history: statement.next error: {:?}", e); return 1; } | |
} | |
} | |
for (i, elem) in vec.iter().rev().enumerate() | |
{ | |
println!("{}: {}", i, elem); | |
} | |
} | |
Err(e) => { println_stderr!("history: prepare error - {:?}", e); return 1; } | |
} | |
0 | |
} | |
pub fn search_history( &mut self, conn: &sqlite::Connection, q: &str ) | |
{ | |
let history_table = self.get_history_table(); | |
let q = format!( "SELECT inp FROM {} WHERE inp like '%{}%' ORDER BY tsb desc limit 20;", history_table, q ); | |
match conn.prepare( q ) | |
{ | |
Ok( mut statement ) => | |
{ | |
let mut vec = Vec::new(); | |
loop | |
{ | |
match statement.next() | |
{ | |
Ok(x) => | |
{ | |
if let State::Row = x { if let Ok(_x) = statement.read::<String>(0) { vec.push(_x); } } | |
else { break; } | |
} | |
Err(e) => { println_stderr!("history: statement.next error: {:?}", e); return; } | |
} | |
} | |
for ( i, elem ) in vec.iter().rev().enumerate() | |
{ | |
println!( "{}: {}", i, elem ); | |
} | |
} | |
Err(e) => { println_stderr!( "history: prepare error - {:?}", e ); } | |
} | |
} | |
pub fn history( &mut self, tokens: &Vec<(String, String)>) -> i32 | |
{ | |
let args = self.line_tokens_to_args(&tokens ); | |
let hfile = self.get_history_file(); | |
let path = Path::new(hfile.as_str()); | |
if !path.exists() { println_stderr!("installer::history::no history file."); return 1; } | |
if let Ok( conn ) = sqlite::open( hfile.clone() ) | |
{ | |
if args.len() == 1 { return self.list_current_history( &conn ); } | |
else if args.len() == 2 { self.search_history( &conn, args[1].as_str() ); } | |
else { println_stderr!("installer::history: only take one arg"); } | |
} | |
else { println_stderr!("installer::history: history file open error."); return 1; } | |
0 | |
} | |
pub fn cd( &mut self, tokens: &Vec<(String, String)>) -> i32 | |
{ | |
let args = self.line_tokens_to_args(&tokens); | |
if args.len() > 2 { println!("installer::cd::invalid cd command"); return 1; } | |
let mut current_dir = PathBuf::new(); | |
match env::current_dir() | |
{ | |
Ok(x) => { current_dir = x; } | |
Err(e) => { println!("installer::cd::current_dir() failed: {}", e.description()); } | |
} | |
let mut str_current_dir = ""; | |
match current_dir.to_str() | |
{ | |
Some(x) => str_current_dir = x, | |
None => { println!("installer::cd::current_dir to str failed."); } | |
} | |
let mut dir_to = if args.len() == 1 | |
{ | |
let home = self.get_user_home(); | |
home.to_string() | |
} | |
else { args[1..].join("") }; | |
if dir_to == "-" | |
{ | |
if self.previous_dir == "" { println!("installer::cd::no previous dir"); return 0; } | |
dir_to = self.previous_dir.clone(); | |
} | |
else if !dir_to.starts_with('/') { dir_to = format!("{}/{}", str_current_dir, dir_to); } | |
if str_current_dir != dir_to { self.previous_dir = str_current_dir.to_string(); } | |
match env::set_current_dir(&dir_to) | |
{ | |
Ok(_) => 0, | |
Err(e) => { println!("installer::cd: {}", e.description()); 1 } | |
} | |
} | |
pub fn exports( &mut self, line: &str) -> i32 | |
{ | |
if !self.is_env( line ) | |
{ | |
println!("installer::exports: invalid command\nusage: export VARIABLE=VALUE"); | |
return 1; | |
} | |
let _line; | |
if let Ok( re ) = Regex::new(r"^ *export +") | |
{ | |
if !re.is_match(line) | |
{ | |
println_stderr!("installer::exports: invalid command?"); | |
return 1; | |
} | |
_line = re.replace_all( line, "" ); | |
} | |
else | |
{ | |
println_stderr!("installer::exports: re new error"); | |
return 2; | |
} | |
let args = self.line_cmd_to_tokens( &_line ); | |
for (sep, token) in args | |
{ | |
if sep == "`" { continue; } | |
if let Ok(re) = Regex::new(r" *([a-zA-Z0-9_]+)=(.*) *") | |
{ | |
if !re.is_match(&token) { continue; } | |
for cap in re.captures_iter( &token ) | |
{ | |
let mut _value = self.unquote( &cap[2] ); | |
if self.needs_extend_home( &_value ) | |
{ | |
self.extend_home( &mut _value ); | |
} | |
let value = self.extend_env_blindly( &_value ); | |
env::set_var( &cap[1], &value ); | |
} | |
} | |
else { println_stderr!("installer::exports: re new error"); return 2; } | |
} | |
0 | |
} | |
pub fn vox( &mut self, tokens: &Vec<(String, String)>) -> i32 | |
{ | |
let args = self.line_tokens_to_args(&tokens); | |
if args.len() == 2 && args[1] == "ls" { self.list_envs() } | |
else if args.len() == 3 && args[1] == "enter" { self.enter_env( args[2].as_str()) } | |
else if args.len() == 2 && args[1] == "exit" { self.exit_env() } | |
else | |
{ | |
println!("vox: invalid command"); | |
println!("usage: vox (ls | enter <env-name> | exit)"); | |
1 | |
} | |
} | |
// builtins::exec::run | |
pub fn does( &mut self, tokens: &Vec<(String, String)> ) -> i32 | |
{ | |
let args = self.line_tokens_to_args( &tokens ); | |
let len = args.len(); | |
if len == 1 { println!("installer::does: invalid command"); return 1; } | |
let mut cmd = exec::Command::new(&args[1]); | |
let err = cmd.args(&args[2..len]).exec(); | |
println!("installer::does: exec error: {:?}", err); | |
0 | |
} | |
pub fn get_release_value( &mut self, ptn: &str ) -> String | |
{ | |
let line = format!( "grep -i '{}' /etc/*release* | grep -o '=.*' | tr '\"=' ' '", ptn ); | |
match self.run(&line) | |
{ | |
Ok( x ) => { return x.stdout.trim().to_string(); } | |
Err(_) => { return String::new(); } | |
} | |
} | |
pub fn get_hostname( &mut self ) -> String | |
{ | |
let len = 255; | |
let mut buf = Vec::<u8>::with_capacity(len); | |
let ptr = buf.as_mut_slice().as_mut_ptr(); | |
let err = unsafe { gethostname(ptr as *mut libc::c_char, len as libc::size_t) } as i32; | |
match err | |
{ | |
0 => { | |
let real_len; | |
let mut i = 0; | |
loop { | |
let byte = unsafe { *(((ptr as u64) + (i as u64)) as *const u8) }; | |
if byte == 0 { | |
real_len = i; | |
break; | |
} | |
i += 1; | |
} | |
unsafe { buf.set_len(real_len) } | |
String::from_utf8_lossy(buf.as_slice()).into_owned() | |
} | |
_ => String::from("unknown"), | |
} | |
} | |
pub fn get_uname( &mut self ) -> String | |
{ | |
match self.run("uname") | |
{ | |
Ok(x) => { | |
return x.stdout.trim().to_string(); | |
} | |
Err(_) => { | |
return String::new(); | |
} | |
} | |
} | |
pub fn get_osx_codename( &mut self ) -> String | |
{ | |
match self.run("grep -o 'SOFTWARE LICENSE AGREEMENT FOR .*[a-zA-Z]' '/System/Library/CoreServices/Setup Assistant.app/Contents/Resources/en.lproj/OSXSoftwareLicense.rtf' | sed 's/SOFTWARE LICENSE AGREEMENT FOR *//'") | |
{ | |
Ok(x) => { return x.stdout.trim().to_string(); } | |
Err(_) => { return String::new(); } | |
} | |
} | |
pub fn get_osx_version( &mut self ) -> String | |
{ | |
match self.run("sw_vers -productVersion") | |
{ | |
Ok(x) => { return x.stdout.trim().to_string(); } | |
Err(_) => { return String::new(); } | |
} | |
} | |
pub fn get_macos_name( &mut self ) -> String | |
{ | |
let mut os_name = self.get_osx_codename(); | |
let ver = self.get_osx_version(); | |
if !ver.is_empty() { | |
os_name.push(' '); | |
os_name.push_str(&ver); | |
} | |
os_name | |
} | |
pub fn get_other_os_name( &mut self ) -> String | |
{ | |
let mut name = self.get_release_value("PRETTY_NAME"); | |
if !name.is_empty() { return name; } | |
name = self.get_release_value("DISTRIB_DESCRIPTION"); | |
if !name.is_empty() { return name; } | |
name = self.get_release_value("IMAGE_DESCRIPTION"); | |
if !name.is_empty() { return name; } | |
String::new() | |
} | |
pub fn get_os_name( &mut self ) -> String | |
{ | |
//let uname = get_uname(); | |
if self.get_uname().to_lowercase() == "darwin" { return self.get_macos_name(); } | |
else { return self.get_other_os_name(); } | |
} | |
pub fn informs( &mut self, _tokens: &Vec<(String, String)> ) -> i32 | |
{ | |
const VERSION: &str = env!("CARGO_PKG_VERSION"); | |
println!("Cicada Version: {}", VERSION); | |
println!("Commit: {}", env!("GIT_HASH")); | |
let os_name = self.get_os_name(); | |
println!( "OS: {}", os_name ); | |
println!("Built with: {}", env!("BUILD_RUSTC_VERSION")); | |
println!("Built at: {}", env!("BUILD_DATE")); | |
0 | |
} | |
pub fn exits( &mut self, tokens: &Vec<(String, String)>) -> i32 | |
{ | |
if tokens.len() > 2 { | |
println_stderr!("installer: exit: too many arguments"); | |
return 1; | |
} | |
let mut code = 0; | |
if tokens.len() == 2 { | |
let _code = &tokens[1].1; | |
match _code.parse::<i32>() { | |
Ok(x) => { | |
code = x; | |
} | |
Err(_) => { | |
println_stderr!("installer: exit: {}: numeric argument required", _code); | |
code = 255; | |
} | |
} | |
} | |
process::exit(code); | |
0 | |
} | |
pub fn handle_env( &mut self, line: &str ) | |
{ | |
self.exports( line ); | |
} | |
pub fn handle_alias( &mut self, line: &str ) | |
{ | |
let re; | |
match Regex::new( r"^ *alias +([a-zA-Z0-9_\.-]+)=(.*)$" ) { | |
Ok(x) => re = x, | |
Err(e) => { | |
println!( "installer::handle_alias: Regex error: {:?}", e ); | |
return; | |
} | |
} | |
for cap in re.captures_iter( line ) | |
{ | |
let name = self.unquote(&cap[1] ); | |
let value = self.unquote(&cap[2] ); | |
self.add_alias(name.as_str(), value.as_str() ); | |
} | |
} | |
pub fn handle_line( &mut self, line: &str ) | |
{ | |
if self.is_env( line ) | |
{ | |
self.handle_env( line ); | |
return; | |
} | |
if self.is_alias( line ) | |
{ | |
self.handle_alias( line ); | |
return; | |
} | |
return; | |
} | |
pub fn describes( &mut self, file_name: &str, append: bool) -> Result<Stdio, String> | |
{ | |
let mut oos = OpenOptions::new(); | |
if append { oos.append(true); } | |
else | |
{ | |
oos.write(true); | |
oos.truncate(true); | |
} | |
match oos.create(true).open(file_name) | |
{ | |
Ok(x) => { | |
let fd = x.into_raw_fd(); | |
let file_out = unsafe { Stdio::from_raw_fd(fd) }; | |
Ok(file_out) | |
} | |
Err(e) => Err(format!("failed to create fd from file: {:?}", e)), | |
} | |
} | |
pub unsafe fn give_terminal_to( &mut self, gid: i32) -> bool | |
{ | |
let mut mask: libc::sigset_t = mem::zeroed(); | |
let mut old_mask: libc::sigset_t = mem::zeroed(); | |
libc::sigemptyset(&mut mask); | |
libc::sigaddset(&mut mask, libc::SIGTSTP); | |
libc::sigaddset(&mut mask, libc::SIGTTIN); | |
libc::sigaddset(&mut mask, libc::SIGTTOU); | |
libc::sigaddset(&mut mask, libc::SIGCHLD); | |
let rcode = libc::pthread_sigmask(libc::SIG_BLOCK, &mask, &mut old_mask); | |
if rcode != 0 { log!("installer::give_terminal_to: failed to call pthread_sigmask"); } | |
let rcode = libc::tcsetpgrp(1, gid); | |
let given; | |
if rcode == -1 | |
{ | |
given = false; | |
let e = errno(); | |
let code = e.0; | |
log!("Error {}: {}", code, e); | |
} | |
else { given = true; } | |
let rcode = libc::pthread_sigmask(libc::SIG_SETMASK, &old_mask, &mut mask); | |
if rcode != 0 { log!("failed to call pthread_sigmask"); } | |
given | |
} | |
pub fn handle_non_tty( &mut self ) -> Agent | |
{ | |
let mut buffer = String::new(); | |
let stdin = io::stdin(); | |
let mut handle = stdin.lock(); | |
match handle.read_to_string( &mut buffer ) | |
{ | |
Ok(_) => | |
{ | |
log!("installer::handle_non_tty: run non tty command: {}", &buffer); | |
return self.run_procs( &buffer, false ); | |
} | |
Err( e ) => | |
{ | |
println!("installer::handle_non_tty: io stdin read_to_string failed: {:?}", e); | |
self.previous_status = self.status; self.status = 1; | |
return self.clone(); | |
} | |
} | |
} | |
#[allow(cyclomatic_complexity)] | |
pub fn run_pipeline( &mut self, tokens: Tokens, redirect_from: &str, background: bool, tty: bool, capture_output: bool, envs: Option<HashMap<String, String>>, ) -> (i32, bool, Option<Output>) | |
{ | |
if background && capture_output { println_stderr!("installer::run_pipeline: cannot capture output of background cmd"); return (1, false, None); } | |
// the defaults to return | |
let mut status = 0; | |
let mut term_given = false; | |
let mut output = None; | |
let sig_action = signal::SigAction::new | |
( | |
signal::SigHandler::Handler( handle_sigchld ), | |
signal::SaFlags::empty(), | |
signal::SigSet::empty(), | |
); | |
unsafe | |
{ | |
match signal::sigaction( signal::SIGCHLD, &sig_action ) | |
{ | |
Ok(_) => {} | |
Err(e) => println!("sigaction error: {:?}", e), | |
} | |
} | |
let cmds = self.line_tokens_to_cmd_tokens( &tokens ); | |
let length = cmds.len(); | |
// info is built here only for printing log | |
let mut info = String::new(); | |
for ( i, cmd ) in cmds.iter().enumerate() | |
{ | |
for item in cmd | |
{ | |
let sep = &item.0; | |
let token = &item.1; | |
info.push_str(sep); | |
info.push_str(token); | |
info.push_str(sep); | |
info.push(' '); | |
} | |
if length > 1 && i < length - 1 { info.push_str("| ") } | |
} | |
if length == 0 { println!("installer::run_pipeline: invalid command: cmds with empty length"); return (1, false, None); } | |
let mut pipes = Vec::new(); | |
for _ in 0..length - 1 | |
{ | |
let fds; | |
match pipe() | |
{ | |
Ok(x) => fds = x, | |
Err(e) => | |
{ | |
println!("pipe error: {:?}", e); | |
return (1, false, None); | |
} | |
} | |
pipes.push(fds); | |
} | |
if pipes.len() + 1 != length { println!("installer::run_pipeline: invalid command: unmatched pipes count"); return (1, false, None); } | |
let isatty = if tty { unsafe { libc::isatty(0) == 1 } } | |
else { false }; | |
let mut i = 0; | |
let mut pgid: u32 = 0; | |
let mut children: Vec<u32> = Vec::new(); | |
let mut _envs: HashMap<String, String> = HashMap::new(); | |
if let Some(x) = envs { _envs = x; } | |
for cmd in &cmds | |
{ | |
let cmd_new; | |
match self.line_cmd_to_with_redirects(&cmd ) | |
{ | |
Ok(x) => { cmd_new = x; } | |
Err(e) => { println!("installer::run_pipeline: cmd_to_with_redirects failed: {:?}", e); return (1, false, None); } | |
} | |
let cmd_ = self.line_tokens_to_args( &cmd_new.tokens ); | |
if cmd_.is_empty() | |
{ | |
println!("installer::run_pipeline: cmd_ is empty"); | |
return (1, false, None); | |
} | |
let program = &cmd_[0]; | |
// treat `(ls)` as `ls` | |
let mut p = Processor::new( program.trim_matches( |c| c == '(' || c == ')' ) ); | |
p.args( &cmd_[1..] ); | |
p.envs( &_envs ); | |
if isatty { p.before_exec( move || { | |
unsafe | |
{ | |
if i == 0 | |
{ | |
// set the first process as progress group leader | |
let pid = libc::getpid(); | |
libc::setpgid( 0, pid ); | |
} | |
else | |
{ | |
libc::setpgid( 0, pgid as i32 ); | |
} | |
} | |
Ok( ( ) ) } ); } | |
if i > 0 | |
{ | |
let fds_prev = pipes[i - 1]; | |
let pipe_in = unsafe { Stdio::from_raw_fd( fds_prev.0 ) }; | |
p.stdin( pipe_in ); | |
} | |
// all processes except the last one need to get stdout piped | |
if i < length - 1 | |
{ | |
let fds = pipes[i]; | |
let pipe_out = unsafe { Stdio::from_raw_fd(fds.1) }; | |
p.stdout( pipe_out ); | |
} | |
// capture output of last process if needed. | |
if i == length - 1 && capture_output | |
{ | |
p.stdout(Stdio::piped()); | |
p.stderr(Stdio::piped()); | |
} | |
for item in &cmd_new.redirects | |
{ | |
let from_ = &item.0; | |
let op_ = &item.1; | |
let to_ = &item.2; | |
if to_ == "&1" && from_ == "2" | |
{ | |
unsafe | |
{ | |
if i < length - 1 | |
{ | |
let fds = pipes[i]; | |
let pipe_out = Stdio::from_raw_fd( fds.1 ); | |
p.stderr(pipe_out); | |
} | |
else if !capture_output | |
{ | |
let fd = libc::dup(1); | |
p.stderr( Stdio::from_raw_fd( fd ) ); | |
} | |
else | |
{ | |
// note: capture output with redirections does not | |
// make much sense | |
} | |
} | |
} | |
else if to_ == "&2" && from_ == "1" | |
{ | |
unsafe | |
{ | |
if i < length - 1 || !capture_output | |
{ | |
let fd = libc::dup(2); | |
p.stdout(Stdio::from_raw_fd(fd)); | |
} | |
else | |
{ | |
// note: capture output with redirections does not | |
// make much sense | |
} | |
} | |
} | |
else | |
{ | |
let append = op_ == ">>"; | |
match self.describes( to_, append ) | |
{ | |
Ok( fd ) => | |
{ | |
if from_ == "1" { p.stdout( fd ); } | |
else { p.stderr( fd ); } | |
} | |
Err(e) => { println_stderr!("installer::run_pipeline: {}", e); return (1, false, None); } | |
} | |
} | |
} | |
if i == 0 && !redirect_from.is_empty() | |
{ | |
let path = Path::new( redirect_from ); | |
let display = path.display(); | |
let file = match File::open( &path ) | |
{ | |
Err(why) => panic!("couldn't open {}: {}", display, why.description()), | |
Ok(file) => file, | |
}; | |
let fd = file.into_raw_fd(); | |
let file_in = unsafe { Stdio::from_raw_fd(fd) }; | |
p.stdin( file_in ); | |
} | |
let mut child; | |
match p.spawn() | |
{ | |
Ok(x) => | |
{ | |
child = x; | |
if i != length - 1 { children.push( child.id() ); } | |
} | |
Err(e) => | |
{ | |
println!("{}: {}", program, e.description()); | |
status = 1; | |
continue; | |
} | |
} | |
if isatty && !background && i == 0 | |
{ | |
pgid = child.id(); | |
unsafe { term_given = self.give_terminal_to( pgid as i32 ); } | |
} | |
if !background && i == length - 1 | |
{ | |
if capture_output | |
{ | |
match child.wait_with_output() | |
{ | |
Ok( x ) => { output = Some( x ); } | |
Err( e ) => { println_stderr!("installer::run_pipeline: {:?}", e); output = None; } | |
} | |
} | |
else | |
{ | |
match child.wait() | |
{ | |
Ok(ecode) => | |
{ | |
if ecode.success() { status = 0; } | |
else | |
{ | |
match ecode.code() { | |
Some(x) => status = x, | |
None => status = 1, | |
} | |
} | |
} | |
Err(_) => | |
{ | |
match IO_Error::last_os_error().raw_os_error() | |
{ | |
Some(10) => { status = 0; } | |
Some(e) => { status = e; } | |
None => { status = 1; } | |
} | |
} | |
} | |
} | |
// ack of the zombies | |
// FIXME: better wait children in signal handler, but .. | |
// .. see comments in `handle_sigchld()` above. | |
for pid in &children | |
{ | |
unsafe | |
{ | |
let mut stat: i32 = 0; | |
let ptr: *mut i32 = &mut stat; | |
libc::waitpid(*pid as i32, ptr, 0); | |
} | |
} | |
} | |
i += 1; | |
} | |
(status, term_given, output) | |
} | |
pub fn run_default_proc( &mut self, line: &str, tty: bool, envs:&mut HashMap<String, String>, tokens:&mut Vec<(String, String)>, cmd:&str) -> i32 | |
{ | |
// for any other situations | |
functioning!( "Agent::run_default_proc", "...", "For unsupported installation commands" ); | |
let mut background = false; | |
let mut len = tokens.len(); | |
if len > 1 && tokens[len - 1].1 == "&" | |
{ | |
background = true; | |
tokens.pop(); | |
len -= 1; | |
} | |
let mut redirect_from = String::new(); | |
let has_redirect_from = tokens.iter().any(|x| x.1 == "<"); | |
if has_redirect_from | |
{ | |
if let Some(idx) = tokens.iter().position(|x| x.1 == "<") | |
{ | |
tokens.remove(idx); | |
len -= 1; | |
if len > idx | |
{ | |
redirect_from = tokens.remove(idx).1; | |
len -= 1; | |
} | |
else | |
{ | |
//println!("installer::run_default_proc: invalid command: cannot get redirect from"); | |
functioned!( "Agent::run_default_proc", "...", "Invalid command; cannot get redirect from command" ); | |
return 1; | |
} | |
} | |
} | |
if len == 0 { functioned!( "Agent::run_default_proc", "...", "if len == 0" ); return 0; } | |
let _envs = envs.clone(); | |
let (result, term_given, _) = self.run_pipeline | |
( tokens.clone(), | |
redirect_from.as_str(), | |
background, | |
tty, | |
false, | |
Some(_envs), | |
); | |
if term_given | |
{ | |
unsafe | |
{ | |
let gid = libc::getpgid(0); | |
self.give_terminal_to(gid); | |
} | |
} | |
functioned!( "Agent::run_default_proc", "...", "return result" ); | |
return result; | |
} | |
pub fn run_proc( &mut self, line: &str, tty: bool ) -> i32 | |
{ | |
let mut envs = HashMap::new(); | |
let cmd_line = self.remove_envs_from_line( line, &mut envs ); | |
let mut tokens = self.line_cmd_to_tokens( &cmd_line ); | |
if tokens.is_empty() { return 0; } | |
let cmd = tokens[0].1.clone(); | |
match cmd.as_str() | |
{ | |
"cd"=>{ return self.cd( &tokens ); } | |
"export"=>{ return self.exports( &cmd_line ); } | |
"vox"=>{ return self.vox( &tokens ); } | |
"history"=>{ return self.history( &tokens ); } | |
"exec"=>{ return self.does( &tokens ); } | |
"cinfo"=>{ return self.informs( &tokens ); } | |
"exit"=>{ return self.exits( &tokens ); } | |
_ => { return self.run_default_proc( line, tty, &mut envs.clone(), &mut tokens.clone(), &cmd.clone() ); } | |
}; | |
} | |
pub fn run_procs( &mut self, line: &str, tty: bool ) -> Agent | |
{ | |
if self.is_arithmetic( line ) | |
{ | |
if line.contains('.') | |
{ | |
match self.run_calc_float(line) | |
{ | |
Ok( x ) => | |
{ | |
println!("{}", x); | |
self.previous_status = self.status; self.status = 0; | |
return self.clone(); | |
} | |
Err(e) => | |
{ | |
println!("installer::run_proc: Error: {}", e); | |
self.previous_status = self.status; self.status = 1; | |
return self.clone(); | |
} | |
} | |
} | |
else | |
{ | |
match self.run_calc_int(line) | |
{ | |
Ok(x) => | |
{ | |
println!("{}", x); | |
self.previous_status = self.status; self.status = 0; | |
return self.clone(); | |
} | |
Err(e) => | |
{ | |
println!("installer::run_proc: Error: {}", e); | |
self.previous_status = self.status; self.status = 1; | |
return self.clone(); | |
} | |
} | |
} | |
} | |
let mut cmd_line: String = line.to_string(); | |
self.pre_handle_cmd_line( &mut cmd_line ); | |
cmd_line = self.extend_alias( &cmd_line ); | |
let mut status = 0; | |
let mut sep = String::new(); | |
for token in self.lines_to_cmds( &cmd_line ) | |
{ | |
if token == ";" || token == "&&" || token == "||" | |
{ | |
sep = token.clone(); | |
continue; | |
} | |
if sep == "&&" && status != 0 { break; } | |
if sep == "||" && status == 0 { break; } | |
status = self.run_proc( token.as_str(), tty ); | |
} | |
self.previous_status = self.status; self.status = status; | |
self.clone() | |
} | |
pub fn green( &mut self, s: &str) -> String { | |
return format!("{}{}{}", GREEN, s, RESET); | |
} | |
pub fn red( &mut self, s: &str) -> String { | |
return format!("{}{}{}", RED, s, RESET); | |
} | |
pub fn blue( &mut self, s: &str) -> String { | |
return format!("{}{}{}", BLUE, s, RESET); | |
} | |
pub fn prompts( &mut self ) -> String | |
{ | |
let home = self.get_user_home(); | |
let user; | |
match env::var("USER") | |
{ | |
Ok(x) => user = x, | |
Err(e) => | |
{ | |
println!("installer::get_prompt: env USER error: {:?}", e); | |
return String::from("installer >> "); | |
} | |
} | |
let hostname = self.get_hostname(); | |
let _current_dir; | |
match env::current_dir() | |
{ | |
Ok(x) => _current_dir = x, | |
Err(e) => | |
{ | |
println!("installer::get_prompt: env current_dir error: {}", e.description()); | |
return format!("({})$ ", self.red("installer: no current dir")); | |
} | |
} | |
let current_dir; | |
match _current_dir.to_str() | |
{ | |
Some(x) => current_dir = x, | |
None => { | |
println!("installer::get_prompt: to_str error"); | |
return String::from("installer >> "); | |
} | |
} | |
let _tokens: Vec<&str> = current_dir.split('/').collect(); | |
let last; | |
match _tokens.last() | |
{ | |
Some(x) => last = x, | |
None => { | |
println!("installer::get_prompt: prompt token last error"); | |
return String::from("installer >> "); | |
} | |
} | |
let pwd: String; | |
if last.is_empty() { pwd = String::from("/"); } | |
else if current_dir == home { pwd = String::from("~"); } | |
else { pwd = last.to_string(); } | |
let mut prompt = if self.status == 0 | |
{ | |
format!( | |
"{}@{}: {}$ ", | |
self.green(user.as_str()), | |
self.green(hostname.as_str()), | |
self.green(pwd.as_str()) | |
) | |
} else { | |
format!( | |
"{}@{}: {}$ ", | |
self.red(user.as_str()), | |
self.red(hostname.as_str()), | |
self.red(pwd.as_str()) | |
) | |
}; | |
if let Ok(x) = env::var("VIRTUAL_ENV") { | |
if x != "" { | |
let _tokens: Vec<&str> = x.split('/').collect(); | |
let env_name; | |
match _tokens.last() | |
{ | |
Some(x) => env_name = x, | |
None => { | |
println!("installer::get_prompt: prompt token last error"); | |
return String::from("installer >> "); | |
} | |
} | |
prompt = format!("({}){}", self.blue(env_name), prompt); | |
} | |
} | |
prompt | |
} | |
pub fn run(&mut self, line: &str) -> Result<CommandResult, &str> | |
{ | |
let mut envs = HashMap::new(); | |
let mut cmd_line = self.remove_envs_from_line(line, &mut envs); | |
//let sh = shell::Shell::new(); | |
self.pre_handle_cmd_line( &mut cmd_line ); | |
let mut tokens = self.line_cmd_to_tokens(&cmd_line); | |
if tokens.is_empty() { return Ok(CommandResult::new()); } | |
let mut len = tokens.len(); | |
if len > 1 && tokens[len - 1].1 == "&" | |
{ | |
tokens.pop(); | |
len -= 1; | |
} | |
let mut redirect_from = String::new(); | |
let has_redirect_from = tokens.iter().any(|x| x.1 == "<"); | |
if has_redirect_from { | |
if let Some(idx) = tokens.iter().position(|x| x.1 == "<") { | |
tokens.remove(idx); | |
len -= 1; | |
if len > idx { | |
redirect_from = tokens.remove(idx).1; | |
len -= 1; | |
} else { | |
return Err("installer: invalid command: cannot get redirect from"); | |
} | |
} | |
} | |
if len == 0 { | |
return Ok(CommandResult::new()); | |
} | |
let (status, _, output) = self.run_pipeline | |
( | |
tokens.clone(), | |
redirect_from.as_str(), | |
false, | |
false, | |
true, | |
Some(envs), | |
); | |
match output | |
{ | |
Some(x) => Ok(CommandResult { | |
status, | |
stdout: String::from_utf8_lossy(&x.stdout).into_owned(), | |
stderr: String::from_utf8_lossy(&x.stderr).into_owned(), | |
}), | |
None => Err("no output"), | |
} | |
} | |
pub fn dispatches( &mut self ) -> Agent | |
{ | |
let editor = unsafe { libc::isatty(libc::STDOUT_FILENO as i32) } == 0; | |
if editor { | |
println!("stdout is tty editor"); | |
} else { | |
println!("stdout is not tty editor"); | |
} | |
match true | |
{ | |
true if env::args().len() > 1 => | |
{ | |
println!("if env::args().len() > {:?}", env::args().len() ); | |
let line = self.env_args_to_command_line(); | |
return self.run_procs( &line, false ); | |
} | |
true if editor => | |
{ | |
println!( "installer::dispatches::libc::istty( {:?} )", editor ); | |
return self.handle_non_tty(); | |
} | |
_=> | |
{ | |
println!( "installer::dispatches: default" ); | |
let mut rl; | |
match Interface::new("installer") | |
{ | |
Ok(x) => rl = x, | |
Err(e) => | |
{ | |
// non-tty will raise errors here | |
println!("installer::dispatches: linefeed Interface Error: {:?}", e); | |
return self.clone(); | |
} | |
} | |
self.epoch( &mut rl ); | |
/*rl.set_completer( Arc::new(completers::CicadaCompleter { | |
sh: Arc::new(sh.clone()), | |
}));*/ | |
let mut status = self.status; | |
loop | |
{ | |
let prompt = self.prompts(); | |
match rl.set_prompt(&prompt) | |
{ | |
Ok(_) => {} | |
Err(e) => { println!("installer::dispatches: error when setting prompt: {:?}\n", e); } | |
} | |
println!("Reading Line"); | |
match rl.read_line() | |
{ | |
Ok(ReadResult::Input(line)) => | |
{ | |
if line.trim() == "" { continue; } | |
/* | |
if line=="^C" | |
{ | |
self.previous_status = self.status; self.status = 2; | |
return self.clone(); | |
} | |
*/ | |
let tsb_spec = time::get_time(); | |
let tsb = (tsb_spec.sec as f64) + tsb_spec.nsec as f64 / 1_000_000_000.0; | |
let mut line = line.clone(); | |
self.extend_bandband( &mut line ); | |
self.run_procs( &line, true ); | |
status = self.status; | |
let tse_spec = time::get_time(); | |
let tse = (tse_spec.sec as f64) + tse_spec.nsec as f64 / 1_000_000_000.0; | |
self.historic( &mut rl, &line, status, tsb, tse ); | |
} | |
Ok(ReadResult::Eof) => | |
{ | |
if let Ok(x) = env::var("NO_EXIT_ON_CTRL_D") | |
{ | |
if x == "1" | |
{ | |
println!("eol( 1 )"); | |
continue; | |
} | |
} | |
println!("installer::dispatches: exit"); | |
break; | |
} | |
Ok(ReadResult::Signal(s)) => { println!("installer::dispatches: readline signal: {:?}", s); } | |
Err(e) => { println!("installer::dispatches: readline error: {:?}", e); } | |
} | |
} | |
self.clone() | |
} | |
} | |
} | |
pub fn employs( &mut self ) -> Agent | |
{ | |
functioning!( "Agent::run_default_proc", "&mut self", "Employ The pls Installer" ); | |
self.profile = self.get_rc_file(); | |
let mut file; | |
match File::open( &mut self.profile ) | |
{ | |
Ok(x) => file = x, | |
Err(e) => | |
{ | |
println!( "installer: Open Profile Error: {:?}\nCreating New Profile Part @ Place( {} )", e, &mut self.profile ); | |
match File::create( &mut self.profile ) | |
{ | |
Ok( _ ) => { return self.employs(); } | |
_=> | |
{ | |
println!( "installer: Create Profile Error: {:?}", e ); | |
return self.clone(); | |
} | |
} | |
return self.employs(); | |
} | |
} | |
let mut text = String::new(); | |
match file.read_to_string( &mut text ) | |
{ | |
Ok(_) => {} | |
Err(e) => { | |
println!("installer: read_to_string error: {:?}", e); | |
return self.clone(); | |
} | |
} | |
for line in text.lines() | |
{ | |
self.handle_line( line ); | |
} | |
functioned!( "Agent::employs", "...", "pls Installer Employed" ); | |
self.clone() | |
} | |
} | |
pub use super::{ installer }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment