Implemented heredocs and herestrings

This commit is contained in:
2025-03-09 00:34:49 -05:00
parent cdcfb23edb
commit 90a188d4b2
14 changed files with 233 additions and 89 deletions

View File

@@ -75,6 +75,7 @@ pub fn echo(node: Node, shenv: &mut ShEnv) -> ShResult<()> {
} }
shenv.collect_redirs(redirs); shenv.collect_redirs(redirs);
log!(DEBUG,"{:?}",shenv.ctx().redirs());
shenv.ctx_mut().activate_rdrs()?; shenv.ctx_mut().activate_rdrs()?;
write_out(formatted)?; write_out(formatted)?;

View File

@@ -8,43 +8,10 @@ pub fn pwd(node: Node, shenv: &mut ShEnv) -> ShResult<()> {
let mut pwd = shenv.vars().get_var("PWD").to_string(); let mut pwd = shenv.vars().get_var("PWD").to_string();
pwd.push('\n'); pwd.push('\n');
if shenv.ctx().flags().contains(ExecFlags::NO_FORK) {
shenv.collect_redirs(redirs); shenv.collect_redirs(redirs);
if let Err(e) = shenv.ctx_mut().activate_rdrs() { shenv.ctx_mut().activate_rdrs()?;
eprintln!("{:?}",e); write_out(pwd)?;
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)?;
}
}
}
} else { unreachable!() } } else { unreachable!() }
Ok(()) Ok(())
} }

View File

@@ -9,19 +9,24 @@ pub mod shellcmd;
pub fn exec_input<S: Into<String>>(input: S, shenv: &mut ShEnv) -> ShResult<()> { pub fn exec_input<S: Into<String>>(input: S, shenv: &mut ShEnv) -> ShResult<()> {
let input = input.into(); let input = input.into();
shenv.new_input(&input); 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(); let token_stream = Lexer::new(input,shenv).lex();
log!(INFO, token_stream);
let token_stream = expand_aliases(token_stream, shenv); let token_stream = expand_aliases(token_stream, shenv);
for token in &token_stream { for token in &token_stream {
log!(TRACE, token); log!(TRACE, token);
log!(TRACE, "{}",token.as_raw(shenv)); 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 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()?; shenv.save_io()?;
let exec_time = std::time::Instant::now();
if let Err(e) = Executor::new(syn_tree, shenv).walk() { if let Err(e) = Executor::new(syn_tree, shenv).walk() {
if let ShErrKind::CleanExit = e.kind() { if let ShErrKind::CleanExit = e.kind() {
let code = shenv.get_code(); let code = shenv.get_code();
@@ -31,7 +36,8 @@ pub fn exec_input<S: Into<String>>(input: S, shenv: &mut ShEnv) -> ShResult<()>
return Err(e.into()) 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()?; shenv.reset_io()?;
Ok(()) Ok(())
} }
@@ -187,6 +193,7 @@ fn exec_funcdef(node: Node, shenv: &mut ShEnv) -> ShResult<()> {
fn exec_subshell(node: Node, shenv: &mut ShEnv) -> ShResult<()> { fn exec_subshell(node: Node, shenv: &mut ShEnv) -> ShResult<()> {
let snapshot = shenv.clone(); let snapshot = shenv.clone();
shenv.vars_mut().reset_params(); shenv.vars_mut().reset_params();
let is_bg = node.flags().contains(NdFlag::BACKGROUND);
let rule = node.into_rule(); let rule = node.into_rule();
if let NdRule::Subshell { body, argv, redirs } = rule { if let NdRule::Subshell { body, argv, redirs } = rule {
if shenv.ctx().flags().contains(ExecFlags::NO_FORK) { 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_children(children)
.with_pgid(child) .with_pgid(child)
.build(); .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!() _ => unimplemented!()
}; };
shenv.vars_mut().set_var(var, &exp); shenv.vars_mut().export(var, &exp);
} else { } 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<()> { fn exec_pipeline(node: Node, shenv: &mut ShEnv) -> ShResult<()> {
log!(TRACE, "Executing pipeline"); log!(TRACE, "Executing pipeline");
let is_bg = node.flags().contains(NdFlag::BACKGROUND);
let rule = node.into_rule(); let rule = node.into_rule();
if let NdRule::Pipeline { cmds } = rule { if let NdRule::Pipeline { cmds } = rule {
let mut prev_rpipe: Option<i32> = None; let mut prev_rpipe: Option<i32> = None;
@@ -415,7 +423,7 @@ fn exec_pipeline(node: Node, shenv: &mut ShEnv) -> ShResult<()> {
.with_children(children) .with_children(children)
.with_pgid(pgid.unwrap()) .with_pgid(pgid.unwrap())
.build(); .build();
wait_fg(job, shenv)?; dispatch_job(job, is_bg, shenv)?;
} else { unreachable!() } } else { unreachable!() }
Ok(()) Ok(())
} }
@@ -423,6 +431,7 @@ fn exec_pipeline(node: Node, shenv: &mut ShEnv) -> ShResult<()> {
fn exec_cmd(node: Node, shenv: &mut ShEnv) -> ShResult<()> { fn exec_cmd(node: Node, shenv: &mut ShEnv) -> ShResult<()> {
log!(TRACE, "Executing command"); log!(TRACE, "Executing command");
let blame = node.span(); let blame = node.span();
let is_bg = node.flags().contains(NdFlag::BACKGROUND);
let rule = node.into_rule(); let rule = node.into_rule();
if let NdRule::Command { argv, redirs } = rule { if let NdRule::Command { argv, redirs } = rule {
@@ -466,7 +475,7 @@ fn exec_cmd(node: Node, shenv: &mut ShEnv) -> ShResult<()> {
.with_pgid(child) .with_pgid(child)
.build(); .build();
log!(TRACE, "New job: {:?}", job); log!(TRACE, "New job: {:?}", job);
wait_fg(job, shenv)?; dispatch_job(job, is_bg, shenv)?;
} }
} }
} }

View File

@@ -16,6 +16,7 @@ pub enum Op {
Sub, Sub,
Mul, Mul,
Div, Div,
IntDiv,
Mod, Mod,
Pow Pow
} }
@@ -24,7 +25,7 @@ impl Op {
pub fn precedence(&self) -> u8 { pub fn precedence(&self) -> u8 {
match self { match self {
Op::Add | Op::Sub => 1, Op::Add | Op::Sub => 1,
Op::Mul | Op::Div | Op::Mod => 2, Op::Mul | Op::Div | Op::IntDiv | Op::Mod => 2,
Op::Pow => 3 Op::Pow => 3
} }
} }
@@ -49,7 +50,14 @@ fn tokenize_expr(expr: &str) -> ShResult<Vec<ExprToken>> {
tokens.push(ExprToken::Operator(Op::Mul)); 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::Operator(Op::Mod)),
'(' => tokens.push(ExprToken::OpenParen), '(' => tokens.push(ExprToken::OpenParen),
')' => tokens.push(ExprToken::CloseParen), ')' => tokens.push(ExprToken::CloseParen),
@@ -142,6 +150,12 @@ pub fn eval_rpn(tokens: Vec<ExprToken>) -> ShResult<f64> {
} }
lhs / rhs 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); stack.push(result);
} }
@@ -154,10 +168,8 @@ pub fn eval_rpn(tokens: Vec<ExprToken>) -> ShResult<f64> {
} }
pub fn expand_arith_token(token: Token, shenv: &mut ShEnv) -> ShResult<Token> { pub fn expand_arith_token(token: Token, shenv: &mut ShEnv) -> ShResult<Token> {
log!(INFO, "{}", token.as_raw(shenv));
// I mean hey it works // I mean hey it works
let token_raw = token.as_raw(shenv); let token_raw = token.as_raw(shenv);
log!(INFO, token_raw);
let arith_raw = token_raw.trim_matches('`'); let arith_raw = token_raw.trim_matches('`');
@@ -173,7 +185,6 @@ pub fn expand_arith_string(s: &str,shenv: &mut ShEnv) -> ShResult<String> {
if exp.starts_with('`') && s.ends_with('`') { if exp.starts_with('`') && s.ends_with('`') {
exp = exp[1..exp.len() - 1].to_string(); exp = exp[1..exp.len() - 1].to_string();
} }
log!(INFO,exp);
let expr_tokens = shunting_yard(tokenize_expr(&exp)?)?; let expr_tokens = shunting_yard(tokenize_expr(&exp)?)?;
log!(DEBUG,expr_tokens); log!(DEBUG,expr_tokens);
let result = eval_rpn(expr_tokens)?.to_string(); let result = eval_rpn(expr_tokens)?.to_string();

View File

@@ -14,8 +14,8 @@ use crate::prelude::*;
pub fn expand_argv(argv: Vec<Token>, shenv: &mut ShEnv) -> ShResult<Vec<Token>> { pub fn expand_argv(argv: Vec<Token>, shenv: &mut ShEnv) -> ShResult<Vec<Token>> {
let mut processed = vec![]; let mut processed = vec![];
for arg in argv { for arg in argv {
log!(DEBUG, "{}",arg.as_raw(shenv)); log!(TRACE, "{}",arg.as_raw(shenv));
log!(DEBUG, processed); log!(TRACE, processed);
let mut expanded = expand_token(arg, shenv)?; let mut expanded = expand_token(arg, shenv)?;
processed.append(&mut expanded); processed.append(&mut expanded);
} }
@@ -24,8 +24,6 @@ pub fn expand_argv(argv: Vec<Token>, shenv: &mut ShEnv) -> ShResult<Vec<Token>>
pub fn expand_token(token: Token, shenv: &mut ShEnv) -> ShResult<Vec<Token>> { pub fn expand_token(token: Token, shenv: &mut ShEnv) -> ShResult<Vec<Token>> {
let mut processed = vec![]; let mut processed = vec![];
log!(INFO, "expanding argv");
log!(INFO, "rule: {:?}", token.rule());
match token.rule() { match token.rule() {
TkRule::DQuote => { TkRule::DQuote => {
let dquote_exp = expand_string(&token.as_raw(shenv), shenv)?; let dquote_exp = expand_string(&token.as_raw(shenv), shenv)?;

View File

@@ -11,6 +11,7 @@ pub fn expand_var(var_sub: Token, shenv: &mut ShEnv) -> Vec<Token> {
} }
pub fn expand_string(s: &str, shenv: &mut ShEnv) -> ShResult<String> { pub fn expand_string(s: &str, shenv: &mut ShEnv) -> ShResult<String> {
log!(DEBUG, s);
let mut result = String::new(); let mut result = String::new();
let mut var_name = String::new(); let mut var_name = String::new();
let mut chars = s.chars().peekable(); let mut chars = s.chars().peekable();
@@ -78,7 +79,7 @@ pub fn expand_string(s: &str, shenv: &mut ShEnv) -> ShResult<String> {
expanded = true; expanded = true;
break break
} }
' ' | '\t' => { ' ' | '\t' | '\n' | ';' => {
let value = shenv.vars().get_var(&var_name); let value = shenv.vars().get_var(&var_name);
result.push_str(value); result.push_str(value);
result.push(ch); result.push(ch);
@@ -89,7 +90,6 @@ pub fn expand_string(s: &str, shenv: &mut ShEnv) -> ShResult<String> {
} }
} }
if !expanded { if !expanded {
log!(INFO, var_name);
let value = shenv.vars().get_var(&var_name); let value = shenv.vars().get_var(&var_name);
result.push_str(value); result.push_str(value);
} }

View File

@@ -150,7 +150,7 @@ pub fn borrow_fd<'a>(fd: i32) -> BorrowedFd<'a> {
} }
// TODO: add more of these // TODO: add more of these
#[derive(Debug,Clone,Copy)] #[derive(Debug,Clone,PartialEq,Copy)]
pub enum RedirType { pub enum RedirType {
Input, Input,
Output, Output,
@@ -163,8 +163,11 @@ pub enum RedirType {
pub enum RedirTarget { pub enum RedirTarget {
Fd(i32), Fd(i32),
File(PathBuf), File(PathBuf),
HereDoc(String),
HereString(String),
} }
#[derive(Debug,Clone)]
pub struct RedirBldr { pub struct RedirBldr {
src: Option<i32>, src: Option<i32>,
op: Option<RedirType>, op: Option<RedirType>,
@@ -222,8 +225,12 @@ impl FromStr for RedirBldr {
if chars.peek() == Some(&'<') { if chars.peek() == Some(&'<') {
chars.next(); chars.next();
redir_bldr = redir_bldr.with_op(RedirType::HereString); redir_bldr = redir_bldr.with_op(RedirType::HereString);
break
} else { } else {
redir_bldr = redir_bldr.with_op(RedirType::HereDoc); 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 { } else {
redir_bldr = redir_bldr.with_op(RedirType::Input); redir_bldr = redir_bldr.with_op(RedirType::Input);
@@ -233,8 +240,10 @@ impl FromStr for RedirBldr {
if chars.peek() == Some(&'>') { if chars.peek() == Some(&'>') {
chars.next(); chars.next();
redir_bldr = redir_bldr.with_op(RedirType::Append); redir_bldr = redir_bldr.with_op(RedirType::Append);
break
} else { } else {
redir_bldr = redir_bldr.with_op(RedirType::Output); redir_bldr = redir_bldr.with_op(RedirType::Output);
break
} }
} }
'&' => { '&' => {
@@ -275,21 +284,24 @@ impl Redir {
pub struct CmdRedirs { pub struct CmdRedirs {
open: Vec<RawFd>, open: Vec<RawFd>,
targets_fd: Vec<Redir>, targets_fd: Vec<Redir>,
targets_file: Vec<Redir> targets_file: Vec<Redir>,
targets_text: Vec<Redir>,
} }
impl CmdRedirs { impl CmdRedirs {
pub fn new(mut redirs: Vec<Redir>) -> Self { pub fn new(mut redirs: Vec<Redir>) -> Self {
let mut targets_fd = vec![]; let mut targets_fd = vec![];
let mut targets_file = vec![]; let mut targets_file = vec![];
let mut targets_text = vec![];
while let Some(redir) = redirs.pop() { while let Some(redir) = redirs.pop() {
let Redir { src: _, op: _, tgt } = &redir; let Redir { src: _, op: _, tgt } = &redir;
match tgt { match tgt {
RedirTarget::Fd(_) => targets_fd.push(redir), 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<()> { pub fn close_all(&mut self) -> ShResult<()> {
while let Some(fd) = self.open.pop() { while let Some(fd) = self.open.pop() {
@@ -303,6 +315,26 @@ impl CmdRedirs {
pub fn activate(&mut self) -> ShResult<()> { pub fn activate(&mut self) -> ShResult<()> {
self.open_file_tgts()?; self.open_file_tgts()?;
self.open_fd_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(()) Ok(())
} }
pub fn open_file_tgts(&mut self) -> ShResult<()> { pub fn open_file_tgts(&mut self) -> ShResult<()> {
@@ -342,6 +374,23 @@ impl CmdRedirs {
} }
} }
pub fn extract_heredoc_body(body: &str) -> ShResult<String> {
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<TkRule> { pub fn check_expansion(s: &str) -> Option<TkRule> {
let rule = Lexer::get_rule(s); let rule = Lexer::get_rule(s);
if EXPANSIONS.contains(&rule) { if EXPANSIONS.contains(&rule) {

View File

@@ -989,7 +989,7 @@ tkrule_def!(RedirOp, |input: &str| {
try_match_inner!(RedirSimpleOut,input); // > try_match_inner!(RedirSimpleOut,input); // >
try_match_inner!(RedirInOut,input); // <> try_match_inner!(RedirInOut,input); // <>
try_match_inner!(RedirSimpleHerestring,input); // <<< try_match_inner!(RedirSimpleHerestring,input); // <<<
try_match_inner!(RedirSimpleHeredoc,input); // << try_match_inner!(RedirHeredoc,input); // <<
try_match_inner!(RedirSimpleIn,input); // < try_match_inner!(RedirSimpleIn,input); // <
try_match_inner!(RedirFdOutFd,input); // Ex: 2>&1 try_match_inner!(RedirFdOutFd,input); // Ex: 2>&1
try_match_inner!(RedirFdInFd,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("<<") { 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 { } else {
None None
} }

View File

@@ -2,7 +2,7 @@ pub mod lex;
use std::{iter::Peekable, str::FromStr}; use std::{iter::Peekable, str::FromStr};
use crate::prelude::*; use crate::{expand::vars::expand_string, prelude::*};
use lex::{Span, TkRule, Token, KEYWORDS, OPERATORS, SEPARATORS}; use lex::{Span, TkRule, Token, KEYWORDS, OPERATORS, SEPARATORS};
@@ -208,9 +208,31 @@ fn get_redir(token: Token, token_slice: &[Token], shenv: &mut ShEnv) -> ShResult
let mut tokens_eaten = 0; let mut tokens_eaten = 0;
let mut tokens_iter = token_slice.into_iter(); let mut tokens_iter = token_slice.into_iter();
let redir_raw = shenv.input_slice(token.span()); 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 there isn't an FD target, get the next token and use it as the filename
if redir_bldr.tgt().is_none() { 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() { if let Some(filename) = tokens_iter.next() {
// Make sure it's a word and not an operator or something // 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 !matches!(filename.rule(), TkRule::SQuote | TkRule::DQuote | TkRule::Ident) || KEYWORDS.contains(&filename.rule()) {
@@ -221,7 +243,7 @@ fn get_redir(token: Token, token_slice: &[Token], shenv: &mut ShEnv) -> ShResult
} }
tokens_eaten += 1; tokens_eaten += 1;
// Construct the Path object // Construct the Path object
let filename_raw = shenv.input_slice(filename.span()).to_string(); let filename_raw = filename.as_raw(shenv);
let filename_path = PathBuf::from(filename_raw); let filename_path = PathBuf::from(filename_raw);
let tgt = RedirTarget::File(filename_path); let tgt = RedirTarget::File(filename_path);
// Update the builder // Update the builder
@@ -233,6 +255,8 @@ fn get_redir(token: Token, token_slice: &[Token], shenv: &mut ShEnv) -> ShResult
return Err(err) return Err(err)
} }
} }
}
Ok((tokens_eaten,redir_bldr.build())) 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| { 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 node_toks = vec![];
let mut argv = vec![]; let mut argv = vec![];
let mut redirs = vec![]; let mut redirs = vec![];
let mut flags = NdFlag::empty();
if let Some(token) = tokens_iter.next() { if let Some(token) = tokens_iter.next() {
if let TkRule::Subshell = token.rule() { if let TkRule::Subshell = token.rule() {
node_toks.push(token.clone()); node_toks.push(token.clone());
@@ -1011,6 +1036,16 @@ ndrule_def!(Subshell, shenv, |tokens: &[Token], shenv: &mut ShEnv| {
node_toks.push(token.clone()); node_toks.push(token.clone());
argv.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 => { TkRule::RedirOp => {
node_toks.push(token.clone()); node_toks.push(token.clone());
let slice = &tokens_iter.clone().map(|tk| tk.clone()).collect::<Vec<_>>(); let slice = &tokens_iter.clone().map(|tk| tk.clone()).collect::<Vec<_>>();
@@ -1030,7 +1065,7 @@ ndrule_def!(Subshell, shenv, |tokens: &[Token], shenv: &mut ShEnv| {
node_rule: NdRule::Subshell { body, argv, redirs }, node_rule: NdRule::Subshell { body, argv, redirs },
tokens: node_toks, tokens: node_toks,
span, span,
flags: NdFlag::empty() flags
}; };
return Ok(Some(node)) return Ok(Some(node))
} else { } else {
@@ -1045,6 +1080,7 @@ ndrule_def!(Pipeline, shenv, |mut tokens: &[Token], shenv: &mut ShEnv| {
let mut tokens_iter = tokens.iter().peekable(); let mut tokens_iter = tokens.iter().peekable();
let mut node_toks = vec![]; let mut node_toks = vec![];
let mut cmds = vec![]; let mut cmds = vec![];
let mut flags = NdFlag::empty();
while let Some(token) = tokens_iter.peek() { while let Some(token) = tokens_iter.peek() {
match token.rule() { match token.rule() {
@@ -1070,6 +1106,9 @@ ndrule_def!(Pipeline, shenv, |mut tokens: &[Token], shenv: &mut ShEnv| {
for _ in 0..cmd.len() { for _ in 0..cmd.len() {
tokens_iter.next(); tokens_iter.next();
} }
if cmd.flags().contains(NdFlag::BACKGROUND) {
flags |= NdFlag::BACKGROUND;
}
if let NdRule::Command { argv, redirs: _ } = cmd.rule() { if let NdRule::Command { argv, redirs: _ } = cmd.rule() {
if let Some(arg) = argv.first() { 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 }, node_rule: NdRule::Pipeline { cmds },
tokens: node_toks, tokens: node_toks,
span, span,
flags: NdFlag::empty() flags
}; };
Ok(Some(node)) Ok(Some(node))
}); });
@@ -1127,6 +1166,7 @@ ndrule_def!(Command, shenv, |tokens: &[Token], shenv: &mut ShEnv| {
let mut node_toks = vec![]; let mut node_toks = vec![];
let mut argv = vec![]; let mut argv = vec![];
let mut redirs = vec![]; let mut redirs = vec![];
let mut flags = NdFlag::empty();
while let Some(token) = tokens_iter.peek() { while let Some(token) = tokens_iter.peek() {
match token.rule() { match token.rule() {
@@ -1144,6 +1184,7 @@ ndrule_def!(Command, shenv, |tokens: &[Token], shenv: &mut ShEnv| {
TkRule::TildeSub | TkRule::TildeSub |
TkRule::ArithSub | TkRule::ArithSub |
TkRule::CmdSub | TkRule::CmdSub |
TkRule::BraceGrp |
TkRule::VarSub => { TkRule::VarSub => {
argv.push(token.clone()); 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)?; let (used,redir) = get_redir(token.clone(), slice, shenv)?;
for _ in 0..used { for _ in 0..used {
if let Some(token) = tokens_iter.next() { 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()); node_toks.push(token.clone());
} }
} }
redirs.push(redir); 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, TkRule::Sep => break,
_ => return Err( _ => return Err(
ShErr::full( ShErr::full(
@@ -1177,7 +1229,7 @@ ndrule_def!(Command, shenv, |tokens: &[Token], shenv: &mut ShEnv| {
node_rule: NdRule::Command { argv, redirs }, node_rule: NdRule::Command { argv, redirs },
tokens: node_toks, tokens: node_toks,
span, span,
flags: NdFlag::empty() flags
}; };
Ok(Some(node)) Ok(Some(node))
} else { } else {
@@ -1193,7 +1245,6 @@ ndrule_def!(Assignment, shenv, |tokens: &[Token], shenv: &mut ShEnv| {
while let Some(token) = tokens.peek() { while let Some(token) = tokens.peek() {
if matches!(token.rule(), TkRule::Ident | TkRule::ArithSub | TkRule::CmdSub | TkRule::DQuote) { if matches!(token.rule(), TkRule::Ident | TkRule::ArithSub | TkRule::CmdSub | TkRule::DQuote) {
let raw = token.as_raw(shenv); let raw = token.as_raw(shenv);
log!(INFO, raw);
// We are going to deconstruct this Ident into two separate tokens // We are going to deconstruct this Ident into two separate tokens
// This makes expanding it easier later // This makes expanding it easier later
if raw.split_once('=').is_some() { if raw.split_once('=').is_some() {

View File

@@ -136,7 +136,7 @@ pub use crate::{
}, },
shellenv::{ shellenv::{
self, self,
wait_fg, dispatch_job,
log_level, log_level,
attach_tty, attach_tty,
term_ctlr, term_ctlr,

View File

@@ -43,6 +43,9 @@ impl ExecCtx {
pub fn redirs(&self) -> &Vec<Redir> { pub fn redirs(&self) -> &Vec<Redir> {
&self.redirs &self.redirs
} }
pub fn clear_redirs(&mut self) {
self.redirs.clear()
}
pub fn sort_redirs(&self) -> (Vec<Redir>,Vec<Redir>) { pub fn sort_redirs(&self) -> (Vec<Redir>,Vec<Redir>) {
let mut cond_redirs = vec![]; let mut cond_redirs = vec![];
let mut body_redirs = vec![]; let mut body_redirs = vec![];

View File

@@ -11,6 +11,7 @@ pub mod meta;
pub mod shenv; pub mod shenv;
pub mod vars; pub mod vars;
pub mod input; pub mod input;
pub mod shopt;
/// Calls attach_tty() on the shell's process group to retake control of the terminal /// Calls attach_tty() on the shell's process group to retake control of the terminal
pub fn take_term() -> ShResult<()> { pub fn take_term() -> ShResult<()> {
@@ -56,6 +57,17 @@ pub fn wait_fg(job: Job, shenv: &mut ShEnv) -> ShResult<()> {
Ok(()) 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 { pub fn log_level() -> crate::libsh::utils::LogLevel {
let level = env::var("FERN_LOG_LEVEL").unwrap_or_default(); let level = env::var("FERN_LOG_LEVEL").unwrap_or_default();
match level.to_lowercase().as_str() { 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() { if !isatty(0).unwrap_or(false) || pgid == term_ctlr() || killpg(pgid, None).is_err() {
return Ok(()) return Ok(())
} }
log!(DEBUG, "Attaching tty to pgid: {}",pgid); log!(TRACE, "Attaching tty to pgid: {}",pgid);
if pgid == getpgrp() && term_ctlr() != getpgrp() { if pgid == getpgrp() && term_ctlr() != getpgrp() {
kill(term_ctlr(), Signal::SIGTTOU).ok(); kill(term_ctlr(), Signal::SIGTTOU).ok();

View File

@@ -71,7 +71,6 @@ impl ShEnv {
let old = &input[range.clone()]; let old = &input[range.clone()];
let delta: isize = new.len() as isize - old.len() as isize; let delta: isize = new.len() as isize - old.len() as isize;
input.replace_range(range, new); input.replace_range(range, new);
log!(INFO,input);
for span in self.input_man.spans_mut() { for span in self.input_man.spans_mut() {
let mut span_mut = span.borrow_mut(); let mut span_mut = span.borrow_mut();
@@ -162,6 +161,7 @@ impl ShEnv {
} }
pub fn reset_io(&mut self) -> ShResult<()> { pub fn reset_io(&mut self) -> ShResult<()> {
let ctx = self.ctx_mut(); let ctx = self.ctx_mut();
ctx.clear_redirs();
if let Some(saved) = ctx.saved_io().take() { if let Some(saved) = ctx.saved_io().take() {
let saved_in = saved.stdin; let saved_in = saved.stdin;
let saved_out = saved.stdout; let saved_out = saved.stdout;

11
src/shellenv/shopt.rs Normal file
View File

@@ -0,0 +1,11 @@
pub struct ShOpts {
}
pub struct CoreOpts {
}
pub struct PromptOpts {
}