diff --git a/src/builtin/echo.rs b/src/builtin/echo.rs index faf22e1..afdc168 100644 --- a/src/builtin/echo.rs +++ b/src/builtin/echo.rs @@ -75,6 +75,7 @@ pub fn echo(node: Node, shenv: &mut ShEnv) -> ShResult<()> { } shenv.collect_redirs(redirs); + log!(DEBUG,"{:?}",shenv.ctx().redirs()); shenv.ctx_mut().activate_rdrs()?; write_out(formatted)?; diff --git a/src/builtin/pwd.rs b/src/builtin/pwd.rs index e07e3e2..8a22f96 100644 --- a/src/builtin/pwd.rs +++ b/src/builtin/pwd.rs @@ -8,43 +8,10 @@ pub fn pwd(node: Node, shenv: &mut ShEnv) -> ShResult<()> { let mut pwd = shenv.vars().get_var("PWD").to_string(); pwd.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(pwd) { - eprintln!("{:?}",e); - exit(1); - } - exit(0); - } else { - match unsafe { fork()? } { - Child => { - if let Err(e) = shenv.ctx_mut().activate_rdrs() { - eprintln!("{:?}",e); - exit(1); - } - if let Err(e) = write_out(pwd) { - 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)?; - } - } - } + shenv.collect_redirs(redirs); + shenv.ctx_mut().activate_rdrs()?; + write_out(pwd)?; + } else { unreachable!() } Ok(()) } diff --git a/src/execute/mod.rs b/src/execute/mod.rs index 4ed28bd..da7bd08 100644 --- a/src/execute/mod.rs +++ b/src/execute/mod.rs @@ -9,19 +9,24 @@ pub mod shellcmd; pub fn exec_input>(input: S, shenv: &mut ShEnv) -> ShResult<()> { let input = input.into(); shenv.new_input(&input); + let total_time = std::time::Instant::now(); + let token_time = std::time::Instant::now(); let token_stream = Lexer::new(input,shenv).lex(); - log!(INFO, token_stream); let token_stream = expand_aliases(token_stream, shenv); for token in &token_stream { log!(TRACE, token); log!(TRACE, "{}",token.as_raw(shenv)); } + log!(INFO, "Tokenizing done in {:?}", token_time.elapsed()); + let parse_time = std::time::Instant::now(); let syn_tree = Parser::new(token_stream,shenv).parse()?; - let exec_start = std::time::Instant::now(); + log!(INFO, "Parsing done in {:?}", parse_time.elapsed()); shenv.save_io()?; + + let exec_time = std::time::Instant::now(); if let Err(e) = Executor::new(syn_tree, shenv).walk() { if let ShErrKind::CleanExit = e.kind() { let code = shenv.get_code(); @@ -31,7 +36,8 @@ pub fn exec_input>(input: S, shenv: &mut ShEnv) -> ShResult<()> return Err(e.into()) } } - log!(INFO, "Executing done in {:?}", exec_start.elapsed()); + log!(INFO, "Executing done in {:?}", exec_time.elapsed()); + log!(INFO, "Total time spent: {:?}", total_time.elapsed()); shenv.reset_io()?; Ok(()) } @@ -187,6 +193,7 @@ fn exec_funcdef(node: Node, shenv: &mut ShEnv) -> ShResult<()> { fn exec_subshell(node: Node, shenv: &mut ShEnv) -> ShResult<()> { let snapshot = shenv.clone(); shenv.vars_mut().reset_params(); + let is_bg = node.flags().contains(NdFlag::BACKGROUND); let rule = node.into_rule(); if let NdRule::Subshell { body, argv, redirs } = rule { if shenv.ctx().flags().contains(ExecFlags::NO_FORK) { @@ -239,7 +246,7 @@ fn exec_subshell(node: Node, shenv: &mut ShEnv) -> ShResult<()> { .with_children(children) .with_pgid(child) .build(); - wait_fg(job, shenv)?; + dispatch_job(job, is_bg, shenv)?; } } } @@ -299,9 +306,9 @@ fn exec_assignment(node: Node, shenv: &mut ShEnv) -> ShResult<()> { } _ => unimplemented!() }; - shenv.vars_mut().set_var(var, &exp); + shenv.vars_mut().export(var, &exp); } else { - shenv.vars_mut().set_var(var, val); + shenv.vars_mut().export(var, val); } } } @@ -336,6 +343,7 @@ fn exec_assignment(node: Node, shenv: &mut ShEnv) -> ShResult<()> { fn exec_pipeline(node: Node, shenv: &mut ShEnv) -> ShResult<()> { log!(TRACE, "Executing pipeline"); + let is_bg = node.flags().contains(NdFlag::BACKGROUND); let rule = node.into_rule(); if let NdRule::Pipeline { cmds } = rule { let mut prev_rpipe: Option = None; @@ -415,7 +423,7 @@ fn exec_pipeline(node: Node, shenv: &mut ShEnv) -> ShResult<()> { .with_children(children) .with_pgid(pgid.unwrap()) .build(); - wait_fg(job, shenv)?; + dispatch_job(job, is_bg, shenv)?; } else { unreachable!() } Ok(()) } @@ -423,6 +431,7 @@ fn exec_pipeline(node: Node, shenv: &mut ShEnv) -> ShResult<()> { fn exec_cmd(node: Node, shenv: &mut ShEnv) -> ShResult<()> { log!(TRACE, "Executing command"); let blame = node.span(); + let is_bg = node.flags().contains(NdFlag::BACKGROUND); let rule = node.into_rule(); if let NdRule::Command { argv, redirs } = rule { @@ -466,7 +475,7 @@ fn exec_cmd(node: Node, shenv: &mut ShEnv) -> ShResult<()> { .with_pgid(child) .build(); log!(TRACE, "New job: {:?}", job); - wait_fg(job, shenv)?; + dispatch_job(job, is_bg, shenv)?; } } } diff --git a/src/expand/arithmetic.rs b/src/expand/arithmetic.rs index eafd449..d2d97f3 100644 --- a/src/expand/arithmetic.rs +++ b/src/expand/arithmetic.rs @@ -16,6 +16,7 @@ pub enum Op { Sub, Mul, Div, + IntDiv, Mod, Pow } @@ -24,7 +25,7 @@ impl Op { pub fn precedence(&self) -> u8 { match self { Op::Add | Op::Sub => 1, - Op::Mul | Op::Div | Op::Mod => 2, + Op::Mul | Op::Div | Op::IntDiv | Op::Mod => 2, Op::Pow => 3 } } @@ -49,7 +50,14 @@ fn tokenize_expr(expr: &str) -> ShResult> { tokens.push(ExprToken::Operator(Op::Mul)); } } - '/' => tokens.push(ExprToken::Operator(Op::Div)), + '/' => { + if chars.peek() == Some(&'/') { + chars.next(); + tokens.push(ExprToken::Operator(Op::IntDiv)); + } else { + tokens.push(ExprToken::Operator(Op::Div)); + } + } '%' => tokens.push(ExprToken::Operator(Op::Mod)), '(' => tokens.push(ExprToken::OpenParen), ')' => tokens.push(ExprToken::CloseParen), @@ -142,6 +150,12 @@ pub fn eval_rpn(tokens: Vec) -> ShResult { } lhs / rhs } + Op::IntDiv => { + if rhs == 0.0 { + return Err(ShErr::simple(ShErrKind::ParseErr, "Attempt to divide by zero in arithmetic expansion")) + } + (lhs as i64 / rhs as i64) as f64 + } }; stack.push(result); } @@ -154,10 +168,8 @@ pub fn eval_rpn(tokens: Vec) -> ShResult { } pub fn expand_arith_token(token: Token, shenv: &mut ShEnv) -> ShResult { - log!(INFO, "{}", token.as_raw(shenv)); // I mean hey it works let token_raw = token.as_raw(shenv); - log!(INFO, token_raw); let arith_raw = token_raw.trim_matches('`'); @@ -173,7 +185,6 @@ pub fn expand_arith_string(s: &str,shenv: &mut ShEnv) -> ShResult { if exp.starts_with('`') && s.ends_with('`') { exp = exp[1..exp.len() - 1].to_string(); } - log!(INFO,exp); let expr_tokens = shunting_yard(tokenize_expr(&exp)?)?; log!(DEBUG,expr_tokens); let result = eval_rpn(expr_tokens)?.to_string(); diff --git a/src/expand/mod.rs b/src/expand/mod.rs index 1c22d25..3f2d401 100644 --- a/src/expand/mod.rs +++ b/src/expand/mod.rs @@ -14,8 +14,8 @@ use crate::prelude::*; pub fn expand_argv(argv: Vec, shenv: &mut ShEnv) -> ShResult> { let mut processed = vec![]; for arg in argv { - log!(DEBUG, "{}",arg.as_raw(shenv)); - log!(DEBUG, processed); + log!(TRACE, "{}",arg.as_raw(shenv)); + log!(TRACE, processed); let mut expanded = expand_token(arg, shenv)?; processed.append(&mut expanded); } @@ -24,8 +24,6 @@ pub fn expand_argv(argv: Vec, shenv: &mut ShEnv) -> ShResult> pub fn expand_token(token: Token, shenv: &mut ShEnv) -> ShResult> { let mut processed = vec![]; - log!(INFO, "expanding argv"); - log!(INFO, "rule: {:?}", token.rule()); match token.rule() { TkRule::DQuote => { let dquote_exp = expand_string(&token.as_raw(shenv), shenv)?; diff --git a/src/expand/vars.rs b/src/expand/vars.rs index 5426ab4..a11ef77 100644 --- a/src/expand/vars.rs +++ b/src/expand/vars.rs @@ -11,6 +11,7 @@ pub fn expand_var(var_sub: Token, shenv: &mut ShEnv) -> Vec { } pub fn expand_string(s: &str, shenv: &mut ShEnv) -> ShResult { + log!(DEBUG, s); let mut result = String::new(); let mut var_name = String::new(); let mut chars = s.chars().peekable(); @@ -78,7 +79,7 @@ pub fn expand_string(s: &str, shenv: &mut ShEnv) -> ShResult { expanded = true; break } - ' ' | '\t' => { + ' ' | '\t' | '\n' | ';' => { let value = shenv.vars().get_var(&var_name); result.push_str(value); result.push(ch); @@ -89,7 +90,6 @@ pub fn expand_string(s: &str, shenv: &mut ShEnv) -> ShResult { } } if !expanded { - log!(INFO, var_name); let value = shenv.vars().get_var(&var_name); result.push_str(value); } diff --git a/src/libsh/utils.rs b/src/libsh/utils.rs index 5206df2..f462437 100644 --- a/src/libsh/utils.rs +++ b/src/libsh/utils.rs @@ -150,7 +150,7 @@ pub fn borrow_fd<'a>(fd: i32) -> BorrowedFd<'a> { } // TODO: add more of these -#[derive(Debug,Clone,Copy)] +#[derive(Debug,Clone,PartialEq,Copy)] pub enum RedirType { Input, Output, @@ -163,8 +163,11 @@ pub enum RedirType { pub enum RedirTarget { Fd(i32), File(PathBuf), + HereDoc(String), + HereString(String), } +#[derive(Debug,Clone)] pub struct RedirBldr { src: Option, op: Option, @@ -222,8 +225,12 @@ impl FromStr for RedirBldr { if chars.peek() == Some(&'<') { chars.next(); redir_bldr = redir_bldr.with_op(RedirType::HereString); + break } else { redir_bldr = redir_bldr.with_op(RedirType::HereDoc); + let body = extract_heredoc_body(raw)?; + redir_bldr = redir_bldr.with_tgt(RedirTarget::HereDoc(body)); + break } } else { redir_bldr = redir_bldr.with_op(RedirType::Input); @@ -233,8 +240,10 @@ impl FromStr for RedirBldr { if chars.peek() == Some(&'>') { chars.next(); redir_bldr = redir_bldr.with_op(RedirType::Append); + break } else { redir_bldr = redir_bldr.with_op(RedirType::Output); + break } } '&' => { @@ -275,21 +284,24 @@ impl Redir { pub struct CmdRedirs { open: Vec, targets_fd: Vec, - targets_file: Vec + targets_file: Vec, + targets_text: Vec, } impl CmdRedirs { pub fn new(mut redirs: Vec) -> Self { let mut targets_fd = vec![]; let mut targets_file = vec![]; + let mut targets_text = vec![]; while let Some(redir) = redirs.pop() { let Redir { src: _, op: _, tgt } = &redir; match tgt { RedirTarget::Fd(_) => targets_fd.push(redir), - RedirTarget::File(_) => targets_file.push(redir) + RedirTarget::File(_) => targets_file.push(redir), + _ => targets_text.push(redir) } } - Self { open: vec![], targets_fd, targets_file } + Self { open: vec![], targets_fd, targets_file, targets_text } } pub fn close_all(&mut self) -> ShResult<()> { while let Some(fd) = self.open.pop() { @@ -303,6 +315,26 @@ impl CmdRedirs { pub fn activate(&mut self) -> ShResult<()> { self.open_file_tgts()?; self.open_fd_tgts()?; + self.open_text_tgts()?; + Ok(()) + } + pub fn open_text_tgts(&mut self) -> ShResult<()> { + while let Some(redir) = self.targets_text.pop() { + let Redir { src, op: _, tgt } = redir; + let (rpipe, wpipe) = c_pipe()?; + let src = borrow_fd(src); + let wpipe_fd = borrow_fd(wpipe); + match tgt { + RedirTarget::HereDoc(body) | + RedirTarget::HereString(body) => { + write(wpipe_fd, body.as_bytes())?; + close(wpipe)?; + } + _ => unreachable!() + } + dup2(rpipe, src.as_raw_fd())?; + close(rpipe)?; + } Ok(()) } pub fn open_file_tgts(&mut self) -> ShResult<()> { @@ -342,6 +374,23 @@ impl CmdRedirs { } } +pub fn extract_heredoc_body(body: &str) -> ShResult { + log!(DEBUG,body); + if let Some(cleaned) = body.strip_prefix("<<") { + if let Some((delim,body)) = cleaned.split_once('\n') { + if let Some(body) = body.trim().strip_suffix(&delim) { + Ok(body.to_string()) + } else { + return Err(ShErr::simple(ShErrKind::ParseErr, "Malformed closing delimiter in heredoc")) + } + } else { + return Err(ShErr::simple(ShErrKind::ParseErr, "Invalid heredoc delimiter")) + } + } else { + return Err(ShErr::simple(ShErrKind::ParseErr, "Invalid heredoc operator")) + } +} + pub fn check_expansion(s: &str) -> Option { let rule = Lexer::get_rule(s); if EXPANSIONS.contains(&rule) { diff --git a/src/parse/lex.rs b/src/parse/lex.rs index 107a8bb..0470d5a 100644 --- a/src/parse/lex.rs +++ b/src/parse/lex.rs @@ -989,7 +989,7 @@ tkrule_def!(RedirOp, |input: &str| { try_match_inner!(RedirSimpleOut,input); // > try_match_inner!(RedirInOut,input); // <> try_match_inner!(RedirSimpleHerestring,input); // <<< - try_match_inner!(RedirSimpleHeredoc,input); // << + try_match_inner!(RedirHeredoc,input); // << try_match_inner!(RedirSimpleIn,input); // < try_match_inner!(RedirFdOutFd,input); // Ex: 2>&1 try_match_inner!(RedirFdInFd,input); // Ex: 2<&1 @@ -1018,9 +1018,41 @@ tkrule_def!(RedirInOut, |input: &str| { } }); -tkrule_def!(RedirSimpleHeredoc, |input: &str| { +tkrule_def!(RedirHeredoc, |mut input: &str| { if input.starts_with("<<") { - Some(2) + let mut len = 2; + input = &input[2..]; + let mut chars = input.chars(); + let mut delim = ""; + let mut delim_len = 0; + let mut body = ""; + let mut body_len = 1; + while let Some(ch) = chars.next() { + len += 1; + match ch { + ' ' | '\t' | '\n' | ';' if delim.is_empty() => return None, + _ if delim.is_empty() => { + delim_len += 1; + while let Some(ch) = chars.next() { + len += 1; + match ch { + '\n' => { + delim = &input[..delim_len]; + input = &input[delim_len..]; + break + } + _ => delim_len += 1, + } + } + } + _ => { + body_len += 1; + body = &input[..body_len]; + if body.ends_with(&delim) { break } + } + } + } + Some(len) } else { None } diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 952c837..0dd296a 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -2,7 +2,7 @@ pub mod lex; use std::{iter::Peekable, str::FromStr}; -use crate::prelude::*; +use crate::{expand::vars::expand_string, prelude::*}; use lex::{Span, TkRule, Token, KEYWORDS, OPERATORS, SEPARATORS}; @@ -208,30 +208,54 @@ fn get_redir(token: Token, token_slice: &[Token], shenv: &mut ShEnv) -> ShResult let mut tokens_eaten = 0; let mut tokens_iter = token_slice.into_iter(); let redir_raw = shenv.input_slice(token.span()); - let mut redir_bldr = RedirBldr::from_str(&redir_raw).unwrap(); + let mut redir_bldr = RedirBldr::from_str(&redir_raw)?; // If there isn't an FD target, get the next token and use it as the filename - if redir_bldr.tgt().is_none() { - if let Some(filename) = tokens_iter.next() { - // Make sure it's a word and not an operator or something - if !matches!(filename.rule(), TkRule::SQuote | TkRule::DQuote | TkRule::Ident) || KEYWORDS.contains(&filename.rule()) { + if redir_bldr.tgt().is_none() || redir_bldr.op() == Some(RedirType::HereDoc) { + if let Some(RedirType::HereString) = redir_bldr.op() { + if let Some(herestring) = tokens_iter.next() { + if !matches!(herestring.rule(), TkRule::SQuote | TkRule::DQuote) { + let mut err = ShErr::simple(ShErrKind::ParseErr, "Expected a string after herestring operator"); + let input = shenv.input_slice(token.span()).to_string(); + err.blame(input, token.span()); + return Err(err) + } + tokens_eaten += 1; + let raw = clean_string(herestring.as_raw(shenv)); + let exp = expand_string(&raw, shenv)?; + let tgt = RedirTarget::HereString(exp); + redir_bldr = redir_bldr.with_tgt(tgt); + } + } else if let Some(RedirType::HereDoc) = redir_bldr.op() { + if let Some(RedirTarget::HereDoc(body)) = redir_bldr.tgt() { + let body_exp = expand_string(body, shenv)?; + log!(DEBUG, body_exp); + let exp_tgt = RedirTarget::HereDoc(body_exp); + redir_bldr = redir_bldr.with_tgt(exp_tgt); + } else { unreachable!() } + } else { + if let Some(filename) = tokens_iter.next() { + // Make sure it's a word and not an operator or something + if !matches!(filename.rule(), TkRule::SQuote | TkRule::DQuote | TkRule::Ident) || KEYWORDS.contains(&filename.rule()) { + let mut err = ShErr::simple(ShErrKind::ParseErr, "Did not find a target for this redirection"); + let input = shenv.input_slice(token.span()).to_string(); + err.blame(input, token.span()); + return Err(err) + } + tokens_eaten += 1; + // Construct the Path object + let filename_raw = filename.as_raw(shenv); + let filename_path = PathBuf::from(filename_raw); + let tgt = RedirTarget::File(filename_path); + // Update the builder + redir_bldr = redir_bldr.with_tgt(tgt); + } else { let mut err = ShErr::simple(ShErrKind::ParseErr, "Did not find a target for this redirection"); let input = shenv.input_slice(token.span()).to_string(); err.blame(input, token.span()); return Err(err) } - tokens_eaten += 1; - // Construct the Path object - let filename_raw = shenv.input_slice(filename.span()).to_string(); - let filename_path = PathBuf::from(filename_raw); - let tgt = RedirTarget::File(filename_path); - // Update the builder - redir_bldr = redir_bldr.with_tgt(tgt); - } else { - let mut err = ShErr::simple(ShErrKind::ParseErr, "Did not find a target for this redirection"); - let input = shenv.input_slice(token.span()).to_string(); - err.blame(input, token.span()); - return Err(err) } + } Ok((tokens_eaten,redir_bldr.build())) } @@ -983,10 +1007,11 @@ ndrule_def!(FuncDef, shenv, |tokens: &[Token], shenv: &mut ShEnv| { }); ndrule_def!(Subshell, shenv, |tokens: &[Token], shenv: &mut ShEnv| { - let mut tokens_iter = tokens.into_iter(); + let mut tokens_iter = tokens.into_iter().peekable(); let mut node_toks = vec![]; let mut argv = vec![]; let mut redirs = vec![]; + let mut flags = NdFlag::empty(); if let Some(token) = tokens_iter.next() { if let TkRule::Subshell = token.rule() { node_toks.push(token.clone()); @@ -1011,6 +1036,16 @@ ndrule_def!(Subshell, shenv, |tokens: &[Token], shenv: &mut ShEnv| { node_toks.push(token.clone()); argv.push(token.clone()); } + TkRule::BgOp => { + flags |= NdFlag::BACKGROUND; + while let Some(token) = tokens_iter.peek() { + if token.rule() == TkRule::Sep { + let token = tokens_iter.next().unwrap(); + node_toks.push(token.clone()); + } else { break } + } + break + } TkRule::RedirOp => { node_toks.push(token.clone()); let slice = &tokens_iter.clone().map(|tk| tk.clone()).collect::>(); @@ -1030,7 +1065,7 @@ ndrule_def!(Subshell, shenv, |tokens: &[Token], shenv: &mut ShEnv| { node_rule: NdRule::Subshell { body, argv, redirs }, tokens: node_toks, span, - flags: NdFlag::empty() + flags }; return Ok(Some(node)) } else { @@ -1045,6 +1080,7 @@ ndrule_def!(Pipeline, shenv, |mut tokens: &[Token], shenv: &mut ShEnv| { let mut tokens_iter = tokens.iter().peekable(); let mut node_toks = vec![]; let mut cmds = vec![]; + let mut flags = NdFlag::empty(); while let Some(token) = tokens_iter.peek() { match token.rule() { @@ -1070,6 +1106,9 @@ ndrule_def!(Pipeline, shenv, |mut tokens: &[Token], shenv: &mut ShEnv| { for _ in 0..cmd.len() { tokens_iter.next(); } + if cmd.flags().contains(NdFlag::BACKGROUND) { + flags |= NdFlag::BACKGROUND; + } if let NdRule::Command { argv, redirs: _ } = cmd.rule() { if let Some(arg) = argv.first() { @@ -1116,7 +1155,7 @@ ndrule_def!(Pipeline, shenv, |mut tokens: &[Token], shenv: &mut ShEnv| { node_rule: NdRule::Pipeline { cmds }, tokens: node_toks, span, - flags: NdFlag::empty() + flags }; Ok(Some(node)) }); @@ -1127,6 +1166,7 @@ ndrule_def!(Command, shenv, |tokens: &[Token], shenv: &mut ShEnv| { let mut node_toks = vec![]; let mut argv = vec![]; let mut redirs = vec![]; + let mut flags = NdFlag::empty(); while let Some(token) = tokens_iter.peek() { match token.rule() { @@ -1144,6 +1184,7 @@ ndrule_def!(Command, shenv, |tokens: &[Token], shenv: &mut ShEnv| { TkRule::TildeSub | TkRule::ArithSub | TkRule::CmdSub | + TkRule::BraceGrp | TkRule::VarSub => { argv.push(token.clone()); } @@ -1152,11 +1193,22 @@ ndrule_def!(Command, shenv, |tokens: &[Token], shenv: &mut ShEnv| { let (used,redir) = get_redir(token.clone(), slice, shenv)?; for _ in 0..used { if let Some(token) = tokens_iter.next() { + // FIXME: this pushes literally anything, it should only push stuff that looks like a filename node_toks.push(token.clone()); } } redirs.push(redir); } + TkRule::BgOp => { + flags |= NdFlag::BACKGROUND; + while let Some(token) = tokens_iter.peek() { + if token.rule() == TkRule::Sep { + let token = tokens_iter.next().unwrap(); + node_toks.push(token.clone()); + } else { break } + } + break + } TkRule::Sep => break, _ => return Err( ShErr::full( @@ -1177,7 +1229,7 @@ ndrule_def!(Command, shenv, |tokens: &[Token], shenv: &mut ShEnv| { node_rule: NdRule::Command { argv, redirs }, tokens: node_toks, span, - flags: NdFlag::empty() + flags }; Ok(Some(node)) } else { @@ -1193,7 +1245,6 @@ ndrule_def!(Assignment, shenv, |tokens: &[Token], shenv: &mut ShEnv| { while let Some(token) = tokens.peek() { if matches!(token.rule(), TkRule::Ident | TkRule::ArithSub | TkRule::CmdSub | TkRule::DQuote) { let raw = token.as_raw(shenv); - log!(INFO, raw); // We are going to deconstruct this Ident into two separate tokens // This makes expanding it easier later if raw.split_once('=').is_some() { diff --git a/src/prelude.rs b/src/prelude.rs index c8b8230..046b0ed 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -136,7 +136,7 @@ pub use crate::{ }, shellenv::{ self, - wait_fg, + dispatch_job, log_level, attach_tty, term_ctlr, diff --git a/src/shellenv/exec_ctx.rs b/src/shellenv/exec_ctx.rs index 6666602..d7e6f1f 100644 --- a/src/shellenv/exec_ctx.rs +++ b/src/shellenv/exec_ctx.rs @@ -43,6 +43,9 @@ impl ExecCtx { pub fn redirs(&self) -> &Vec { &self.redirs } + pub fn clear_redirs(&mut self) { + self.redirs.clear() + } pub fn sort_redirs(&self) -> (Vec,Vec) { let mut cond_redirs = vec![]; let mut body_redirs = vec![]; diff --git a/src/shellenv/mod.rs b/src/shellenv/mod.rs index e6038c0..6ee7b08 100644 --- a/src/shellenv/mod.rs +++ b/src/shellenv/mod.rs @@ -11,6 +11,7 @@ pub mod meta; pub mod shenv; pub mod vars; pub mod input; +pub mod shopt; /// Calls attach_tty() on the shell's process group to retake control of the terminal pub fn take_term() -> ShResult<()> { @@ -56,6 +57,17 @@ pub fn wait_fg(job: Job, shenv: &mut ShEnv) -> ShResult<()> { Ok(()) } +pub fn dispatch_job(job: Job, is_bg: bool, shenv: &mut ShEnv) -> ShResult<()> { + if is_bg { + write_jobs(|j| { + j.insert_job(job, false) + })?; + } else { + wait_fg(job, shenv)?; + } + Ok(()) +} + pub fn log_level() -> crate::libsh::utils::LogLevel { let level = env::var("FERN_LOG_LEVEL").unwrap_or_default(); match level.to_lowercase().as_str() { @@ -80,7 +92,7 @@ pub fn attach_tty(pgid: Pid) -> ShResult<()> { if !isatty(0).unwrap_or(false) || pgid == term_ctlr() || killpg(pgid, None).is_err() { return Ok(()) } - log!(DEBUG, "Attaching tty to pgid: {}",pgid); + log!(TRACE, "Attaching tty to pgid: {}",pgid); if pgid == getpgrp() && term_ctlr() != getpgrp() { kill(term_ctlr(), Signal::SIGTTOU).ok(); diff --git a/src/shellenv/shenv.rs b/src/shellenv/shenv.rs index ad7e14d..62563f8 100644 --- a/src/shellenv/shenv.rs +++ b/src/shellenv/shenv.rs @@ -71,7 +71,6 @@ impl ShEnv { let old = &input[range.clone()]; let delta: isize = new.len() as isize - old.len() as isize; input.replace_range(range, new); - log!(INFO,input); for span in self.input_man.spans_mut() { let mut span_mut = span.borrow_mut(); @@ -162,6 +161,7 @@ impl ShEnv { } pub fn reset_io(&mut self) -> ShResult<()> { let ctx = self.ctx_mut(); + ctx.clear_redirs(); if let Some(saved) = ctx.saved_io().take() { let saved_in = saved.stdin; let saved_out = saved.stdout; diff --git a/src/shellenv/shopt.rs b/src/shellenv/shopt.rs new file mode 100644 index 0000000..fa62a9c --- /dev/null +++ b/src/shellenv/shopt.rs @@ -0,0 +1,11 @@ +pub struct ShOpts { + +} + +pub struct CoreOpts { + +} + +pub struct PromptOpts { + +}