Implemented scoping for expansions
This commit is contained in:
@@ -2,52 +2,82 @@ use shellenv::jobs::{ChildProc, JobBldr};
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
bitflags! {
|
||||
#[derive(Debug,Clone,Copy)]
|
||||
pub struct EchoFlags: u32 {
|
||||
const USE_ESCAPE = 0b0001;
|
||||
const NO_ESCAPE = 0b0010;
|
||||
const STDERR = 0b0100;
|
||||
const NO_NEWLINE = 0b1000;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn echo(node: Node, shenv: &mut ShEnv) -> ShResult<()> {
|
||||
let rule = node.into_rule();
|
||||
|
||||
if let NdRule::Command { argv, redirs } = rule {
|
||||
let argv = argv.drop_first().as_strings(shenv);
|
||||
let mut formatted = argv.join(" ");
|
||||
formatted.push('\n');
|
||||
|
||||
if shenv.ctx().flags().contains(ExecFlags::NO_FORK) {
|
||||
shenv.collect_redirs(redirs);
|
||||
if let Err(e) = shenv.ctx_mut().activate_rdrs() {
|
||||
eprintln!("{:?}",e);
|
||||
exit(1);
|
||||
}
|
||||
if let Err(e) = write_out(formatted) {
|
||||
eprintln!("{:?}",e);
|
||||
exit(1);
|
||||
}
|
||||
exit(0);
|
||||
} else {
|
||||
match unsafe { fork()? } {
|
||||
Child => {
|
||||
shenv.collect_redirs(redirs);
|
||||
if let Err(e) = shenv.ctx_mut().activate_rdrs() {
|
||||
eprintln!("{:?}",e);
|
||||
exit(1);
|
||||
let mut argv_iter = argv.into_iter().skip(1).peekable();
|
||||
let mut echo_flags = EchoFlags::empty();
|
||||
while let Some(arg) = argv_iter.peek() {
|
||||
let blame = arg.span();
|
||||
let raw = arg.as_raw(shenv);
|
||||
if raw.starts_with('-') {
|
||||
let _ = argv_iter.next();
|
||||
let mut options = raw.strip_prefix('-').unwrap().chars();
|
||||
while let Some(opt) = options.next() {
|
||||
match opt {
|
||||
'r' => echo_flags |= EchoFlags::STDERR,
|
||||
'n' => echo_flags |= EchoFlags::NO_NEWLINE,
|
||||
'e' => {
|
||||
if echo_flags.contains(EchoFlags::NO_ESCAPE) {
|
||||
return Err(
|
||||
ShErr::full(
|
||||
ShErrKind::ExecFail,
|
||||
"the 'e' and 'E' flags are mutually exclusive",
|
||||
shenv.get_input(),
|
||||
blame
|
||||
)
|
||||
)
|
||||
}
|
||||
echo_flags |= EchoFlags::USE_ESCAPE;
|
||||
}
|
||||
'E' => {
|
||||
if echo_flags.contains(EchoFlags::USE_ESCAPE) {
|
||||
return Err(
|
||||
ShErr::full(
|
||||
ShErrKind::ExecFail,
|
||||
"the 'e' and 'E' flags are mutually exclusive",
|
||||
shenv.get_input(),
|
||||
blame
|
||||
)
|
||||
)
|
||||
}
|
||||
echo_flags |= EchoFlags::NO_ESCAPE;
|
||||
}
|
||||
_ => return Err(
|
||||
ShErr::full(
|
||||
ShErrKind::ExecFail,
|
||||
format!("Unrecognized echo option"),
|
||||
shenv.get_input(),
|
||||
blame
|
||||
)
|
||||
)
|
||||
}
|
||||
if let Err(e) = write_out(formatted) {
|
||||
eprintln!("{:?}",e);
|
||||
exit(1);
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
Parent { child } => {
|
||||
shenv.reset_io()?;
|
||||
let children = vec![
|
||||
ChildProc::new(child, Some("echo"), Some(child))?
|
||||
];
|
||||
let job = JobBldr::new()
|
||||
.with_children(children)
|
||||
.with_pgid(child)
|
||||
.build();
|
||||
wait_fg(job, shenv)?;
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
let argv = argv_iter.collect::<Vec<_>>().as_strings(shenv);
|
||||
let mut formatted = argv.join(" ");
|
||||
if !echo_flags.contains(EchoFlags::NO_NEWLINE) {
|
||||
formatted.push('\n');
|
||||
}
|
||||
|
||||
shenv.collect_redirs(redirs);
|
||||
shenv.ctx_mut().activate_rdrs()?;
|
||||
write_out(formatted)?;
|
||||
|
||||
} else { unreachable!() }
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -162,7 +162,7 @@ pub fn jobs(node: Node, shenv: &mut ShEnv) -> ShResult<()> {
|
||||
flags |= flag
|
||||
}
|
||||
}
|
||||
read_jobs(|j| j.print_jobs(flags))?;
|
||||
write_jobs(|j| j.print_jobs(flags))?;
|
||||
shenv.set_code(0);
|
||||
} else { unreachable!() }
|
||||
|
||||
|
||||
@@ -21,15 +21,18 @@ pub fn exec_input<S: Into<String>>(input: S, shenv: &mut ShEnv) -> ShResult<()>
|
||||
|
||||
let syn_tree = Parser::new(token_stream,shenv).parse()?;
|
||||
let exec_start = std::time::Instant::now();
|
||||
shenv.save_io()?;
|
||||
if let Err(e) = Executor::new(syn_tree, shenv).walk() {
|
||||
if let ShErrKind::CleanExit = e.kind() {
|
||||
let code = shenv.get_code();
|
||||
sh_quit(code);
|
||||
} else {
|
||||
shenv.reset_io()?;
|
||||
return Err(e.into())
|
||||
}
|
||||
}
|
||||
log!(INFO, "Executing done in {:?}", exec_start.elapsed());
|
||||
shenv.reset_io()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -43,7 +46,7 @@ impl<'a> Executor<'a> {
|
||||
Self { ast, shenv }
|
||||
}
|
||||
pub fn walk(&mut self) -> ShResult<()> {
|
||||
self.shenv.inputman_mut().save_state();
|
||||
self.shenv.inputman_mut().push_state();
|
||||
log!(TRACE, "Starting walk");
|
||||
while let Some(node) = self.ast.next_node() {
|
||||
if let NdRule::CmdList { cmds } = node.clone().into_rule() {
|
||||
@@ -51,7 +54,7 @@ impl<'a> Executor<'a> {
|
||||
exec_list(cmds, self.shenv).try_blame(node.as_raw(self.shenv),node.span())?
|
||||
} else { unreachable!() }
|
||||
}
|
||||
self.shenv.inputman_mut().load_state();
|
||||
self.shenv.inputman_mut().pop_state();
|
||||
log!(TRACE, "passed");
|
||||
Ok(())
|
||||
}
|
||||
@@ -288,7 +291,7 @@ fn exec_assignment(node: Node, shenv: &mut ShEnv) -> ShResult<()> {
|
||||
if EXPANSIONS.contains(&val_rule) {
|
||||
let exp = match val_rule {
|
||||
TkRule::ArithSub => expand_arith_string(val,shenv)?,
|
||||
TkRule::DQuote => expand_string(val, shenv),
|
||||
TkRule::DQuote => expand_string(val, shenv)?,
|
||||
TkRule::TildeSub => expand_tilde_string(val),
|
||||
TkRule::VarSub => {
|
||||
let val = shenv.vars().get_var(var);
|
||||
@@ -312,7 +315,7 @@ fn exec_assignment(node: Node, shenv: &mut ShEnv) -> ShResult<()> {
|
||||
if EXPANSIONS.contains(&val_rule) {
|
||||
let exp = match val_rule {
|
||||
TkRule::ArithSub => expand_arith_string(val,shenv)?,
|
||||
TkRule::DQuote => expand_string(val, shenv),
|
||||
TkRule::DQuote => expand_string(val, shenv)?,
|
||||
TkRule::TildeSub => expand_tilde_string(val),
|
||||
TkRule::VarSub => {
|
||||
let val = shenv.vars().get_var(var);
|
||||
|
||||
@@ -169,7 +169,7 @@ pub fn expand_arith_token(token: Token, shenv: &mut ShEnv) -> ShResult<Token> {
|
||||
}
|
||||
|
||||
pub fn expand_arith_string(s: &str,shenv: &mut ShEnv) -> ShResult<String> {
|
||||
let mut exp = expand_string(s,shenv);
|
||||
let mut exp = expand_string(s,shenv)?;
|
||||
if exp.starts_with('`') && s.ends_with('`') {
|
||||
exp = exp[1..exp.len() - 1].to_string();
|
||||
}
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
use crate::prelude::*;
|
||||
|
||||
pub fn expand_cmdsub(token: Token, shenv: &mut ShEnv) -> ShResult<Vec<Token>> {
|
||||
let new_tokens = vec![];
|
||||
pub fn expand_cmdsub_token(token: Token, shenv: &mut ShEnv) -> ShResult<Vec<Token>> {
|
||||
let cmdsub_raw = token.as_raw(shenv);
|
||||
let body = &cmdsub_raw[2..cmdsub_raw.len() - 1].to_string(); // From '$(this)' to 'this'
|
||||
let output = expand_cmdsub_string(&cmdsub_raw, shenv)?;
|
||||
let new_tokens = shenv.expand_input(&output, token.span());
|
||||
|
||||
Ok(new_tokens)
|
||||
}
|
||||
|
||||
pub fn expand_cmdsub_string(mut s: &str, shenv: &mut ShEnv) -> ShResult<String> {
|
||||
if s.starts_with("$(") && s.ends_with(')') {
|
||||
s = &s[2..s.len() - 1]; // From '$(this)' to 'this'
|
||||
}
|
||||
|
||||
let (r_pipe,w_pipe) = c_pipe()?;
|
||||
let pipe_redir = Redir::output(1, w_pipe);
|
||||
@@ -14,17 +22,12 @@ pub fn expand_cmdsub(token: Token, shenv: &mut ShEnv) -> ShResult<Vec<Token>> {
|
||||
match unsafe { fork()? } {
|
||||
Child => {
|
||||
close(r_pipe).ok();
|
||||
exec_input(body, shenv).abort_if_err();
|
||||
sh_quit(0);
|
||||
exec_input(s, &mut sub_shenv).abort_if_err();
|
||||
exit(0);
|
||||
}
|
||||
Parent { child } => {
|
||||
Parent { child: _ } => {
|
||||
close(w_pipe).ok();
|
||||
}
|
||||
}
|
||||
let output = read_to_string(r_pipe)?;
|
||||
if !output.is_empty() {
|
||||
let lex_input = Rc::new(output);
|
||||
}
|
||||
|
||||
Ok(new_tokens)
|
||||
Ok(read_to_string(r_pipe)?)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ pub mod cmdsub;
|
||||
pub mod arithmetic;
|
||||
|
||||
use arithmetic::expand_arith_token;
|
||||
use cmdsub::expand_cmdsub_token;
|
||||
use vars::{expand_string, expand_var};
|
||||
use tilde::expand_tilde_token;
|
||||
|
||||
@@ -27,7 +28,7 @@ pub fn expand_token(token: Token, shenv: &mut ShEnv) -> ShResult<Vec<Token>> {
|
||||
log!(INFO, "rule: {:?}", token.rule());
|
||||
match token.rule() {
|
||||
TkRule::DQuote => {
|
||||
let dquote_exp = expand_string(&token.as_raw(shenv), shenv);
|
||||
let dquote_exp = expand_string(&token.as_raw(shenv), shenv)?;
|
||||
let mut expanded = shenv.expand_input(&dquote_exp, token.span());
|
||||
processed.append(&mut expanded);
|
||||
}
|
||||
@@ -43,6 +44,10 @@ pub fn expand_token(token: Token, shenv: &mut ShEnv) -> ShResult<Vec<Token>> {
|
||||
let arith_exp = expand_arith_token(token.clone(), shenv)?;
|
||||
processed.push(arith_exp);
|
||||
}
|
||||
TkRule::CmdSub => {
|
||||
let mut cmdsub_exp = expand_cmdsub_token(token.clone(), shenv)?;
|
||||
processed.append(&mut cmdsub_exp);
|
||||
}
|
||||
_ => {
|
||||
if token.rule() != TkRule::Ident {
|
||||
log!(WARN, "found this in expand_token: {:?}", token.rule());
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
use crate::{parse::lex::Token, prelude::*};
|
||||
|
||||
use super::cmdsub::expand_cmdsub_string;
|
||||
|
||||
pub fn expand_var(var_sub: Token, shenv: &mut ShEnv) -> Vec<Token> {
|
||||
let var_name = var_sub.as_raw(shenv);
|
||||
let var_name = var_name.trim_start_matches('$').trim_matches(['{','}']);
|
||||
@@ -8,7 +10,7 @@ pub fn expand_var(var_sub: Token, shenv: &mut ShEnv) -> Vec<Token> {
|
||||
shenv.expand_input(&value, var_sub.span())
|
||||
}
|
||||
|
||||
pub fn expand_string(s: &str, shenv: &mut ShEnv) -> String {
|
||||
pub fn expand_string(s: &str, shenv: &mut ShEnv) -> ShResult<String> {
|
||||
let mut result = String::new();
|
||||
let mut var_name = String::new();
|
||||
let mut chars = s.chars().peekable();
|
||||
@@ -38,6 +40,30 @@ pub fn expand_string(s: &str, shenv: &mut ShEnv) -> String {
|
||||
expanded = true;
|
||||
break
|
||||
}
|
||||
'(' if var_name.is_empty() => {
|
||||
let mut paren_count = 1;
|
||||
var_name.push_str("$(");
|
||||
while let Some(ch) = chars.next() {
|
||||
match ch {
|
||||
'(' => {
|
||||
paren_count += 1;
|
||||
var_name.push(ch);
|
||||
}
|
||||
')' => {
|
||||
paren_count -= 1;
|
||||
var_name.push(ch);
|
||||
if paren_count == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
_ => var_name.push(ch)
|
||||
}
|
||||
}
|
||||
let value = expand_cmdsub_string(&var_name, shenv)?;
|
||||
result.push_str(&value);
|
||||
expanded = true;
|
||||
break
|
||||
}
|
||||
_ if ch.is_ascii_digit() && var_name.is_empty() && !in_brace => {
|
||||
var_name.push(ch);
|
||||
let value = shenv.vars().get_var(&var_name);
|
||||
@@ -72,5 +98,5 @@ pub fn expand_string(s: &str, shenv: &mut ShEnv) -> String {
|
||||
_ => result.push(ch)
|
||||
}
|
||||
}
|
||||
result
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
use std::fmt::Display;
|
||||
use std::{fmt::Display, os::fd::AsRawFd};
|
||||
|
||||
use nix::sys::termios;
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
@@ -46,6 +48,9 @@ pub fn sh_quit(code: i32) -> ! {
|
||||
job.killpg(Signal::SIGTERM).ok();
|
||||
}
|
||||
});
|
||||
if let Some(termios) = crate::get_saved_termios() {
|
||||
termios::tcsetattr(std::io::stdin(), termios::SetArg::TCSANOW, &termios).unwrap();
|
||||
}
|
||||
exit(code);
|
||||
}
|
||||
|
||||
|
||||
40
src/main.rs
40
src/main.rs
@@ -9,13 +9,46 @@ pub mod signal;
|
||||
pub mod prompt;
|
||||
pub mod builtin;
|
||||
pub mod expand;
|
||||
pub mod tests;
|
||||
|
||||
use std::os::fd::AsRawFd;
|
||||
|
||||
use nix::sys::termios::{self, LocalFlags, Termios};
|
||||
use signal::sig_setup;
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
pub static mut SAVED_TERMIOS: Option<Option<Termios>> = None;
|
||||
|
||||
pub fn save_termios() {
|
||||
unsafe {
|
||||
SAVED_TERMIOS = Some(if isatty(std::io::stdin().as_raw_fd()).unwrap() {
|
||||
let mut termios = termios::tcgetattr(std::io::stdin()).unwrap();
|
||||
termios.local_flags &= !LocalFlags::ECHOCTL;
|
||||
termios::tcsetattr(std::io::stdin(), nix::sys::termios::SetArg::TCSANOW, &termios).unwrap();
|
||||
Some(termios)
|
||||
} else {
|
||||
None
|
||||
});
|
||||
}
|
||||
}
|
||||
pub fn get_saved_termios() -> Option<Termios> {
|
||||
unsafe {
|
||||
SAVED_TERMIOS.clone().flatten()
|
||||
}
|
||||
}
|
||||
fn set_termios() {
|
||||
if isatty(std::io::stdin().as_raw_fd()).unwrap() {
|
||||
let mut termios = termios::tcgetattr(std::io::stdin()).unwrap();
|
||||
termios.local_flags &= !LocalFlags::ECHOCTL;
|
||||
termios::tcsetattr(std::io::stdin(), nix::sys::termios::SetArg::TCSANOW, &termios).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
sig_setup();
|
||||
save_termios();
|
||||
set_termios();
|
||||
let mut shenv = ShEnv::new();
|
||||
if let Err(e) = shenv.source_rc() {
|
||||
eprintln!("Error sourcing rc file: {}", e.to_string());
|
||||
@@ -23,13 +56,14 @@ pub fn main() {
|
||||
|
||||
loop {
|
||||
log!(TRACE, "Entered loop");
|
||||
let line = match prompt::read_line(&mut shenv) {
|
||||
Ok(line) => line,
|
||||
match prompt::read_line(&mut shenv) {
|
||||
Ok(line) => {
|
||||
let _ = exec_input(line, &mut shenv).eprint();
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("{}",e);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let _ = exec_input(line, &mut shenv).eprint();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,6 +175,12 @@ impl Span {
|
||||
pub fn shift_end(&mut self, delta: isize) {
|
||||
self.end = self.end.saturating_add_signed(delta)
|
||||
}
|
||||
pub fn set_start(&mut self, start: usize) {
|
||||
self.start = start
|
||||
}
|
||||
pub fn set_end(&mut self, end: usize) {
|
||||
self.end = end
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! try_match {
|
||||
@@ -251,9 +257,9 @@ impl TkRule {
|
||||
// Generalized rules come last
|
||||
try_match!(Whitespace,input);
|
||||
try_match!(Comment,input);
|
||||
try_match!(CmdSub,input);
|
||||
try_match!(VarSub,input);
|
||||
try_match!(ProcSub,input);
|
||||
try_match!(CmdSub,input);
|
||||
try_match!(ArithSub,input);
|
||||
try_match!(AndOp,input);
|
||||
try_match!(OrOp,input);
|
||||
|
||||
@@ -391,7 +391,6 @@ ndrule_def!(Case, shenv, |mut tokens: &[Token], shenv: &mut ShEnv| {
|
||||
node_toks.push(token.clone());
|
||||
tokens = &tokens[1..];
|
||||
if token.as_raw(shenv) != "in" {
|
||||
panic!();
|
||||
return Err(err("Expected `in` after case statement pattern", token.span(), shenv))
|
||||
} else {
|
||||
closed = true;
|
||||
@@ -405,7 +404,6 @@ ndrule_def!(Case, shenv, |mut tokens: &[Token], shenv: &mut ShEnv| {
|
||||
_ => {
|
||||
if closed { break }
|
||||
log!(ERROR, token);
|
||||
panic!();
|
||||
return Err(err("Expected `in` after case statement pattern", token.span(), shenv))
|
||||
}
|
||||
}
|
||||
@@ -440,12 +438,8 @@ ndrule_def!(Case, shenv, |mut tokens: &[Token], shenv: &mut ShEnv| {
|
||||
let mut lists_iter = lists.iter().peekable();
|
||||
while let Some(list) = lists_iter.next() {
|
||||
node_toks.extend(list.tokens.clone());
|
||||
if lists_iter.peek().is_none() {
|
||||
for token in list.tokens() {
|
||||
}
|
||||
}
|
||||
if let Some(token) = list.tokens().last() {
|
||||
if lists_iter.peek().is_none() && (token.rule() != TkRule::Sep || token.as_raw(shenv).trim() != ";;") {
|
||||
if lists_iter.peek().is_none() && (token.rule() != TkRule::Sep || !token.as_raw(shenv).trim().ends_with(";;")) {
|
||||
log!(ERROR, "{:?}",list.tokens());
|
||||
log!(ERROR, token);
|
||||
log!(ERROR, "{}",token.as_raw(shenv).trim());
|
||||
@@ -1149,6 +1143,7 @@ ndrule_def!(Command, shenv, |tokens: &[Token], shenv: &mut ShEnv| {
|
||||
TkRule::DQuote |
|
||||
TkRule::TildeSub |
|
||||
TkRule::ArithSub |
|
||||
TkRule::CmdSub |
|
||||
TkRule::VarSub => {
|
||||
argv.push(token.clone());
|
||||
}
|
||||
|
||||
@@ -82,6 +82,16 @@ impl<'a> Highlighter for SynHelper<'a> {
|
||||
is_command = false;
|
||||
result.push_str(&rebuilt);
|
||||
}
|
||||
TkRule::CmdSub => {
|
||||
let body = &raw[2..raw.len() - 1];
|
||||
let highlighted = self.highlight(body, 0).to_string();
|
||||
let styled_o_paren = "$(".styled(Style::BrightBlue);
|
||||
let styled_c_paren = ")".styled(Style::BrightBlue);
|
||||
let rebuilt = format!("{styled_o_paren}{highlighted}{styled_c_paren}");
|
||||
|
||||
is_command = false;
|
||||
result.push_str(&rebuilt);
|
||||
}
|
||||
TkRule::Subshell => {
|
||||
let body = &raw[1..raw.len() - 1];
|
||||
let highlighted = self.highlight(body, 0).to_string();
|
||||
|
||||
@@ -11,7 +11,6 @@ bitflags! {
|
||||
pub struct ExecCtx {
|
||||
redirs: Vec<Redir>,
|
||||
depth: usize,
|
||||
state_stack: Vec<Self>,
|
||||
max_depth: usize,
|
||||
flags: ExecFlags,
|
||||
io_masks: IoMasks,
|
||||
@@ -23,36 +22,12 @@ impl ExecCtx {
|
||||
Self {
|
||||
redirs: vec![],
|
||||
depth: 0,
|
||||
state_stack: vec![],
|
||||
max_depth: 1500,
|
||||
flags: ExecFlags::empty(),
|
||||
io_masks: IoMasks::new(),
|
||||
saved_io: None
|
||||
}
|
||||
}
|
||||
pub fn push_state(&mut self) {
|
||||
self.state_stack.push(self.clone());
|
||||
}
|
||||
pub fn pop_state(&mut self) {
|
||||
if let Some(state) = self.state_stack.pop() {
|
||||
*self = state;
|
||||
}
|
||||
}
|
||||
pub fn descend(&mut self) -> ShResult<()> {
|
||||
self.push_state();
|
||||
self.depth += 1;
|
||||
log!(DEBUG, "{}",self.depth);
|
||||
if self.depth > self.max_depth {
|
||||
return Err(
|
||||
ShErr::simple(ShErrKind::ExecFail,"Exceeded maximum execution depth")
|
||||
)
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub fn ascend(&mut self) {
|
||||
self.pop_state();
|
||||
self.depth = self.depth.saturating_sub(1);
|
||||
}
|
||||
pub fn as_cond(&self) -> Self {
|
||||
let mut clone = self.clone();
|
||||
let (cond_redirs,_) = self.sort_redirs();
|
||||
|
||||
@@ -1,16 +1,41 @@
|
||||
use crate::prelude::*;
|
||||
|
||||
#[derive(Clone,Debug,PartialEq)]
|
||||
pub struct SavedSpan {
|
||||
pointer: Rc<RefCell<Span>>,
|
||||
start: usize,
|
||||
end: usize,
|
||||
expanded: bool
|
||||
}
|
||||
|
||||
impl SavedSpan {
|
||||
pub fn from_span(pointer: Rc<RefCell<Span>>) -> Self {
|
||||
let expanded = pointer.borrow().expanded;
|
||||
let start = pointer.borrow().start();
|
||||
let end = pointer.borrow().end();
|
||||
Self { pointer, start, end, expanded }
|
||||
}
|
||||
pub fn restore(&self) {
|
||||
let mut deref = self.pointer.borrow_mut();
|
||||
deref.set_start(self.start);
|
||||
deref.set_end(self.end);
|
||||
deref.expanded = self.expanded
|
||||
}
|
||||
pub fn into_span(self) -> Rc<RefCell<Span>> {
|
||||
self.pointer
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone,Debug,PartialEq)]
|
||||
pub struct InputMan {
|
||||
input: Option<String>,
|
||||
saved_input: Option<String>,
|
||||
spans: Vec<Rc<RefCell<Span>>>,
|
||||
saved_spans: Vec<Span>
|
||||
saved_states: Vec<(String,Vec<SavedSpan>)>,
|
||||
}
|
||||
|
||||
impl InputMan {
|
||||
pub fn new() -> Self {
|
||||
Self { input: None, saved_input: None, spans: vec![], saved_spans: vec![] }
|
||||
Self { input: None, spans: vec![], saved_states: vec![] }
|
||||
}
|
||||
pub fn clear(&mut self) {
|
||||
*self = Self::new();
|
||||
@@ -24,22 +49,27 @@ impl InputMan {
|
||||
pub fn get_input_mut(&mut self) -> Option<&mut String> {
|
||||
self.input.as_mut()
|
||||
}
|
||||
pub fn save_state(&mut self) {
|
||||
self.saved_input = self.input.clone();
|
||||
self.saved_spans.clear();
|
||||
for span in &self.spans {
|
||||
self.saved_spans.push(span.borrow().clone());
|
||||
pub fn push_state(&mut self) {
|
||||
if let Some(input) = &self.input {
|
||||
let saved_input = input.clone();
|
||||
let mut saved_spans = vec![];
|
||||
for span in &self.spans {
|
||||
let saved_span = SavedSpan::from_span(span.clone());
|
||||
saved_spans.push(saved_span);
|
||||
}
|
||||
self.saved_states.push((saved_input,saved_spans));
|
||||
}
|
||||
}
|
||||
pub fn load_state(&mut self) {
|
||||
if self.saved_input.is_some() {
|
||||
self.input = self.saved_input.take();
|
||||
|
||||
for (span, saved_span) in self.spans.iter_mut().zip(self.saved_spans.iter()) {
|
||||
*span.borrow_mut() = saved_span.clone();
|
||||
pub fn pop_state(&mut self) {
|
||||
if let Some((saved_input, saved_spans)) = self.saved_states.pop() {
|
||||
self.input = Some(saved_input);
|
||||
let mut restored_spans = vec![];
|
||||
for saved_span in saved_spans.into_iter() {
|
||||
saved_span.restore();
|
||||
let span = saved_span.into_span();
|
||||
restored_spans.push(span);
|
||||
}
|
||||
|
||||
self.saved_spans.clear();
|
||||
self.spans = restored_spans;
|
||||
}
|
||||
}
|
||||
pub fn new_span(&mut self, start: usize, end: usize) -> Rc<RefCell<Span>> {
|
||||
|
||||
@@ -538,7 +538,7 @@ impl JobTab {
|
||||
None
|
||||
}
|
||||
}
|
||||
pub fn print_jobs(&self, flags: JobCmdFlags) -> ShResult<()> {
|
||||
pub fn print_jobs(&mut self, flags: JobCmdFlags) -> ShResult<()> {
|
||||
let jobs = if flags.contains(JobCmdFlags::NEW_ONLY) {
|
||||
&self.jobs
|
||||
.iter()
|
||||
@@ -551,6 +551,7 @@ impl JobTab {
|
||||
.map(|job| job.as_ref())
|
||||
.collect::<Vec<Option<&Job>>>()
|
||||
};
|
||||
let mut jobs_to_remove = vec![];
|
||||
for job in jobs.iter().flatten() {
|
||||
// Skip foreground job
|
||||
let id = job.tabid().unwrap();
|
||||
@@ -563,6 +564,12 @@ impl JobTab {
|
||||
}
|
||||
// Print the job in the selected format
|
||||
write(borrow_fd(1), format!("{}\n",job.display(&self.order,flags)).as_bytes())?;
|
||||
if job.get_stats().iter().all(|stat| matches!(stat,WtStat::Exited(_, _))) {
|
||||
jobs_to_remove.push(JobID::TableID(id));
|
||||
}
|
||||
}
|
||||
for id in jobs_to_remove {
|
||||
self.remove_job(id);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use libc::{STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO};
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
#[derive(Clone,Debug)]
|
||||
@@ -51,12 +53,9 @@ impl ShEnv {
|
||||
if repl_span.borrow().expanded {
|
||||
return vec![];
|
||||
}
|
||||
log!(INFO, repl_span);
|
||||
log!(INFO, new);
|
||||
repl_span.borrow_mut().expanded = true;
|
||||
let saved_spans = self.input_man.spans_mut().clone();
|
||||
let mut new_tokens = Lexer::new(new.to_string(), self).lex();
|
||||
log!(INFO, new_tokens);
|
||||
*self.input_man.spans_mut() = saved_spans;
|
||||
|
||||
let offset = repl_span.borrow().start();
|
||||
@@ -71,9 +70,8 @@ impl ShEnv {
|
||||
if let Some(input) = self.input_man.get_input_mut() {
|
||||
let old = &input[range.clone()];
|
||||
let delta: isize = new.len() as isize - old.len() as isize;
|
||||
log!(INFO, input);
|
||||
log!(INFO, range);
|
||||
input.replace_range(range, new);
|
||||
log!(INFO,input);
|
||||
|
||||
for span in self.input_man.spans_mut() {
|
||||
let mut span_mut = span.borrow_mut();
|
||||
@@ -168,11 +166,11 @@ impl ShEnv {
|
||||
let saved_in = saved.stdin;
|
||||
let saved_out = saved.stdout;
|
||||
let saved_err = saved.stderr;
|
||||
dup2(0,saved_in)?;
|
||||
dup2(saved_in,STDIN_FILENO)?;
|
||||
close(saved_in)?;
|
||||
dup2(1,saved_out)?;
|
||||
dup2(saved_out,STDOUT_FILENO)?;
|
||||
close(saved_out)?;
|
||||
dup2(2,saved_err)?;
|
||||
dup2(saved_err,STDERR_FILENO)?;
|
||||
close(saved_err)?;
|
||||
}
|
||||
Ok(())
|
||||
|
||||
@@ -53,12 +53,7 @@ pub extern "C" fn ignore_sigchld(_: libc::c_int) {
|
||||
}
|
||||
|
||||
extern "C" fn handle_sigquit(_: libc::c_int) {
|
||||
write_jobs(|j| {
|
||||
for job in j.jobs_mut().iter_mut().flatten() {
|
||||
job.killpg(Signal::SIGTERM).ok();
|
||||
}
|
||||
});
|
||||
exit(0);
|
||||
sh_quit(0)
|
||||
}
|
||||
|
||||
pub extern "C" fn handle_sigchld(_: libc::c_int) {
|
||||
@@ -125,11 +120,11 @@ pub fn child_exited(pid: Pid, status: WtStat) -> ShResult<()> {
|
||||
* We can reasonably assume that if it is not a foreground job, then it exists in the job table
|
||||
* If this assumption is incorrect, the code has gone wrong somewhere.
|
||||
*/
|
||||
let (
|
||||
if let Some((
|
||||
pgid,
|
||||
is_fg,
|
||||
is_finished
|
||||
) = write_jobs(|j| {
|
||||
)) = write_jobs(|j| {
|
||||
let fg_pgid = j.get_fg().map(|job| job.pgid());
|
||||
if let Some(job) = j.query_mut(JobID::Pid(pid)) {
|
||||
let pgid = job.pgid();
|
||||
@@ -141,21 +136,22 @@ pub fn child_exited(pid: Pid, status: WtStat) -> ShResult<()> {
|
||||
child.set_stat(status);
|
||||
}
|
||||
|
||||
Ok((pgid, is_fg, is_finished))
|
||||
Some((pgid, is_fg, is_finished))
|
||||
} else {
|
||||
Err(ShErr::simple(ShErrKind::InternalErr, "Job not found"))
|
||||
None
|
||||
}
|
||||
})?;
|
||||
}) {
|
||||
|
||||
if is_finished {
|
||||
if is_fg {
|
||||
take_term()?;
|
||||
} else {
|
||||
println!();
|
||||
let job_order = read_jobs(|j| j.order().to_vec());
|
||||
let result = read_jobs(|j| j.query(JobID::Pgid(pgid)).cloned());
|
||||
if let Some(job) = result {
|
||||
println!("{}",job.display(&job_order,shellenv::jobs::JobCmdFlags::PIDS))
|
||||
if is_finished {
|
||||
if is_fg {
|
||||
take_term()?;
|
||||
} else {
|
||||
println!();
|
||||
let job_order = read_jobs(|j| j.order().to_vec());
|
||||
let result = read_jobs(|j| j.query(JobID::Pgid(pgid)).cloned());
|
||||
if let Some(job) = result {
|
||||
println!("{}",job.display(&job_order,shellenv::jobs::JobCmdFlags::PIDS))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
7
src/tests.rs
Normal file
7
src/tests.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
pub mod lex_test {
|
||||
|
||||
}
|
||||
|
||||
pub mod parse_test {
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user