From cdcfb23edb1d9b4e38c5988f42a518e988a6a205 Mon Sep 17 00:00:00 2001 From: pagedmov Date: Sat, 8 Mar 2025 01:38:42 -0500 Subject: [PATCH] Implemented scoping for expansions --- src/builtin/echo.rs | 106 +++++++++++++++++++++++++-------------- src/builtin/jobctl.rs | 2 +- src/execute/mod.rs | 11 ++-- src/expand/arithmetic.rs | 2 +- src/expand/cmdsub.rs | 27 +++++----- src/expand/mod.rs | 7 ++- src/expand/vars.rs | 30 ++++++++++- src/libsh/sys.rs | 7 ++- src/main.rs | 40 +++++++++++++-- src/parse/lex.rs | 8 ++- src/parse/mod.rs | 9 +--- src/prompt/highlight.rs | 10 ++++ src/shellenv/exec_ctx.rs | 25 --------- src/shellenv/input.rs | 62 +++++++++++++++++------ src/shellenv/jobs.rs | 9 +++- src/shellenv/shenv.rs | 14 +++--- src/signal.rs | 36 ++++++------- src/tests.rs | 7 +++ 18 files changed, 271 insertions(+), 141 deletions(-) create mode 100644 src/tests.rs diff --git a/src/builtin/echo.rs b/src/builtin/echo.rs index 8b3b85f..faf22e1 100644 --- a/src/builtin/echo.rs +++ b/src/builtin/echo.rs @@ -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::>().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(()) } diff --git a/src/builtin/jobctl.rs b/src/builtin/jobctl.rs index 873cffd..f0419be 100644 --- a/src/builtin/jobctl.rs +++ b/src/builtin/jobctl.rs @@ -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!() } diff --git a/src/execute/mod.rs b/src/execute/mod.rs index 6cf9da7..4ed28bd 100644 --- a/src/execute/mod.rs +++ b/src/execute/mod.rs @@ -21,15 +21,18 @@ pub fn exec_input>(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); diff --git a/src/expand/arithmetic.rs b/src/expand/arithmetic.rs index 8decdfb..eafd449 100644 --- a/src/expand/arithmetic.rs +++ b/src/expand/arithmetic.rs @@ -169,7 +169,7 @@ pub fn expand_arith_token(token: Token, shenv: &mut ShEnv) -> ShResult { } pub fn expand_arith_string(s: &str,shenv: &mut ShEnv) -> ShResult { - 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(); } diff --git a/src/expand/cmdsub.rs b/src/expand/cmdsub.rs index 0dc7a64..fd2ff13 100644 --- a/src/expand/cmdsub.rs +++ b/src/expand/cmdsub.rs @@ -1,9 +1,17 @@ use crate::prelude::*; -pub fn expand_cmdsub(token: Token, shenv: &mut ShEnv) -> ShResult> { - let new_tokens = vec![]; +pub fn expand_cmdsub_token(token: Token, shenv: &mut ShEnv) -> ShResult> { 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 { + 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> { 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)?) } diff --git a/src/expand/mod.rs b/src/expand/mod.rs index eb9619e..1c22d25 100644 --- a/src/expand/mod.rs +++ b/src/expand/mod.rs @@ -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> { 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> { 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()); diff --git a/src/expand/vars.rs b/src/expand/vars.rs index 0f78cf7..5426ab4 100644 --- a/src/expand/vars.rs +++ b/src/expand/vars.rs @@ -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 { 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 { 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 { 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) } diff --git a/src/libsh/sys.rs b/src/libsh/sys.rs index 6e82d2e..1f331bb 100644 --- a/src/libsh/sys.rs +++ b/src/libsh/sys.rs @@ -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); } diff --git a/src/main.rs b/src/main.rs index c1f0eb8..1eceacc 100644 --- a/src/main.rs +++ b/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> = 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 { + 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(); } } diff --git a/src/parse/lex.rs b/src/parse/lex.rs index b503db0..107a8bb 100644 --- a/src/parse/lex.rs +++ b/src/parse/lex.rs @@ -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); diff --git a/src/parse/mod.rs b/src/parse/mod.rs index e28b986..952c837 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -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()); } diff --git a/src/prompt/highlight.rs b/src/prompt/highlight.rs index 4aafc25..5987bb0 100644 --- a/src/prompt/highlight.rs +++ b/src/prompt/highlight.rs @@ -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(); diff --git a/src/shellenv/exec_ctx.rs b/src/shellenv/exec_ctx.rs index cd5510f..6666602 100644 --- a/src/shellenv/exec_ctx.rs +++ b/src/shellenv/exec_ctx.rs @@ -11,7 +11,6 @@ bitflags! { pub struct ExecCtx { redirs: Vec, depth: usize, - state_stack: Vec, 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(); diff --git a/src/shellenv/input.rs b/src/shellenv/input.rs index 5f05894..1b05821 100644 --- a/src/shellenv/input.rs +++ b/src/shellenv/input.rs @@ -1,16 +1,41 @@ use crate::prelude::*; +#[derive(Clone,Debug,PartialEq)] +pub struct SavedSpan { + pointer: Rc>, + start: usize, + end: usize, + expanded: bool +} + +impl SavedSpan { + pub fn from_span(pointer: Rc>) -> 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> { + self.pointer + } +} + #[derive(Clone,Debug,PartialEq)] pub struct InputMan { input: Option, - saved_input: Option, spans: Vec>>, - saved_spans: Vec + saved_states: Vec<(String,Vec)>, } 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> { diff --git a/src/shellenv/jobs.rs b/src/shellenv/jobs.rs index f826c42..4d119f9 100644 --- a/src/shellenv/jobs.rs +++ b/src/shellenv/jobs.rs @@ -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::>>() }; + 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(()) } diff --git a/src/shellenv/shenv.rs b/src/shellenv/shenv.rs index 3690e4c..ad7e14d 100644 --- a/src/shellenv/shenv.rs +++ b/src/shellenv/shenv.rs @@ -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(()) diff --git a/src/signal.rs b/src/signal.rs index 030666a..3cf9d89 100644 --- a/src/signal.rs +++ b/src/signal.rs @@ -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)) + } } } } diff --git a/src/tests.rs b/src/tests.rs new file mode 100644 index 0000000..a2faac9 --- /dev/null +++ b/src/tests.rs @@ -0,0 +1,7 @@ +pub mod lex_test { + +} + +pub mod parse_test { + +}