Implemented scoping for expansions
This commit is contained in:
@@ -2,52 +2,82 @@ use shellenv::jobs::{ChildProc, JobBldr};
|
|||||||
|
|
||||||
use crate::prelude::*;
|
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<()> {
|
pub fn echo(node: Node, shenv: &mut ShEnv) -> ShResult<()> {
|
||||||
let rule = node.into_rule();
|
let rule = node.into_rule();
|
||||||
|
|
||||||
if let NdRule::Command { argv, redirs } = rule {
|
if let NdRule::Command { argv, redirs } = rule {
|
||||||
let argv = argv.drop_first().as_strings(shenv);
|
let mut argv_iter = argv.into_iter().skip(1).peekable();
|
||||||
let mut formatted = argv.join(" ");
|
let mut echo_flags = EchoFlags::empty();
|
||||||
formatted.push('\n');
|
while let Some(arg) = argv_iter.peek() {
|
||||||
|
let blame = arg.span();
|
||||||
if shenv.ctx().flags().contains(ExecFlags::NO_FORK) {
|
let raw = arg.as_raw(shenv);
|
||||||
shenv.collect_redirs(redirs);
|
if raw.starts_with('-') {
|
||||||
if let Err(e) = shenv.ctx_mut().activate_rdrs() {
|
let _ = argv_iter.next();
|
||||||
eprintln!("{:?}",e);
|
let mut options = raw.strip_prefix('-').unwrap().chars();
|
||||||
exit(1);
|
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);
|
|
||||||
} else {
|
} else {
|
||||||
match unsafe { fork()? } {
|
break
|
||||||
Child => {
|
}
|
||||||
|
}
|
||||||
|
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.collect_redirs(redirs);
|
||||||
if let Err(e) = shenv.ctx_mut().activate_rdrs() {
|
shenv.ctx_mut().activate_rdrs()?;
|
||||||
eprintln!("{:?}",e);
|
write_out(formatted)?;
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
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 { unreachable!() }
|
} else { unreachable!() }
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ pub fn jobs(node: Node, shenv: &mut ShEnv) -> ShResult<()> {
|
|||||||
flags |= flag
|
flags |= flag
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
read_jobs(|j| j.print_jobs(flags))?;
|
write_jobs(|j| j.print_jobs(flags))?;
|
||||||
shenv.set_code(0);
|
shenv.set_code(0);
|
||||||
} else { unreachable!() }
|
} 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 syn_tree = Parser::new(token_stream,shenv).parse()?;
|
||||||
let exec_start = std::time::Instant::now();
|
let exec_start = std::time::Instant::now();
|
||||||
|
shenv.save_io()?;
|
||||||
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();
|
||||||
sh_quit(code);
|
sh_quit(code);
|
||||||
} else {
|
} else {
|
||||||
|
shenv.reset_io()?;
|
||||||
return Err(e.into())
|
return Err(e.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log!(INFO, "Executing done in {:?}", exec_start.elapsed());
|
log!(INFO, "Executing done in {:?}", exec_start.elapsed());
|
||||||
|
shenv.reset_io()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,7 +46,7 @@ impl<'a> Executor<'a> {
|
|||||||
Self { ast, shenv }
|
Self { ast, shenv }
|
||||||
}
|
}
|
||||||
pub fn walk(&mut self) -> ShResult<()> {
|
pub fn walk(&mut self) -> ShResult<()> {
|
||||||
self.shenv.inputman_mut().save_state();
|
self.shenv.inputman_mut().push_state();
|
||||||
log!(TRACE, "Starting walk");
|
log!(TRACE, "Starting walk");
|
||||||
while let Some(node) = self.ast.next_node() {
|
while let Some(node) = self.ast.next_node() {
|
||||||
if let NdRule::CmdList { cmds } = node.clone().into_rule() {
|
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())?
|
exec_list(cmds, self.shenv).try_blame(node.as_raw(self.shenv),node.span())?
|
||||||
} else { unreachable!() }
|
} else { unreachable!() }
|
||||||
}
|
}
|
||||||
self.shenv.inputman_mut().load_state();
|
self.shenv.inputman_mut().pop_state();
|
||||||
log!(TRACE, "passed");
|
log!(TRACE, "passed");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -288,7 +291,7 @@ fn exec_assignment(node: Node, shenv: &mut ShEnv) -> ShResult<()> {
|
|||||||
if EXPANSIONS.contains(&val_rule) {
|
if EXPANSIONS.contains(&val_rule) {
|
||||||
let exp = match val_rule {
|
let exp = match val_rule {
|
||||||
TkRule::ArithSub => expand_arith_string(val,shenv)?,
|
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::TildeSub => expand_tilde_string(val),
|
||||||
TkRule::VarSub => {
|
TkRule::VarSub => {
|
||||||
let val = shenv.vars().get_var(var);
|
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) {
|
if EXPANSIONS.contains(&val_rule) {
|
||||||
let exp = match val_rule {
|
let exp = match val_rule {
|
||||||
TkRule::ArithSub => expand_arith_string(val,shenv)?,
|
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::TildeSub => expand_tilde_string(val),
|
||||||
TkRule::VarSub => {
|
TkRule::VarSub => {
|
||||||
let val = shenv.vars().get_var(var);
|
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> {
|
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('`') {
|
if exp.starts_with('`') && s.ends_with('`') {
|
||||||
exp = exp[1..exp.len() - 1].to_string();
|
exp = exp[1..exp.len() - 1].to_string();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
pub fn expand_cmdsub(token: Token, shenv: &mut ShEnv) -> ShResult<Vec<Token>> {
|
pub fn expand_cmdsub_token(token: Token, shenv: &mut ShEnv) -> ShResult<Vec<Token>> {
|
||||||
let new_tokens = vec![];
|
|
||||||
let cmdsub_raw = token.as_raw(shenv);
|
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 (r_pipe,w_pipe) = c_pipe()?;
|
||||||
let pipe_redir = Redir::output(1, w_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()? } {
|
match unsafe { fork()? } {
|
||||||
Child => {
|
Child => {
|
||||||
close(r_pipe).ok();
|
close(r_pipe).ok();
|
||||||
exec_input(body, shenv).abort_if_err();
|
exec_input(s, &mut sub_shenv).abort_if_err();
|
||||||
sh_quit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
Parent { child } => {
|
Parent { child: _ } => {
|
||||||
close(w_pipe).ok();
|
close(w_pipe).ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let output = read_to_string(r_pipe)?;
|
Ok(read_to_string(r_pipe)?)
|
||||||
if !output.is_empty() {
|
|
||||||
let lex_input = Rc::new(output);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(new_tokens)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ pub mod cmdsub;
|
|||||||
pub mod arithmetic;
|
pub mod arithmetic;
|
||||||
|
|
||||||
use arithmetic::expand_arith_token;
|
use arithmetic::expand_arith_token;
|
||||||
|
use cmdsub::expand_cmdsub_token;
|
||||||
use vars::{expand_string, expand_var};
|
use vars::{expand_string, expand_var};
|
||||||
use tilde::expand_tilde_token;
|
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());
|
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)?;
|
||||||
let mut expanded = shenv.expand_input(&dquote_exp, token.span());
|
let mut expanded = shenv.expand_input(&dquote_exp, token.span());
|
||||||
processed.append(&mut expanded);
|
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)?;
|
let arith_exp = expand_arith_token(token.clone(), shenv)?;
|
||||||
processed.push(arith_exp);
|
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 {
|
if token.rule() != TkRule::Ident {
|
||||||
log!(WARN, "found this in expand_token: {:?}", token.rule());
|
log!(WARN, "found this in expand_token: {:?}", token.rule());
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
use crate::{parse::lex::Token, prelude::*};
|
use crate::{parse::lex::Token, prelude::*};
|
||||||
|
|
||||||
|
use super::cmdsub::expand_cmdsub_string;
|
||||||
|
|
||||||
pub fn expand_var(var_sub: Token, shenv: &mut ShEnv) -> Vec<Token> {
|
pub fn expand_var(var_sub: Token, shenv: &mut ShEnv) -> Vec<Token> {
|
||||||
let var_name = var_sub.as_raw(shenv);
|
let var_name = var_sub.as_raw(shenv);
|
||||||
let var_name = var_name.trim_start_matches('$').trim_matches(['{','}']);
|
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())
|
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 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();
|
||||||
@@ -38,6 +40,30 @@ pub fn expand_string(s: &str, shenv: &mut ShEnv) -> String {
|
|||||||
expanded = true;
|
expanded = true;
|
||||||
break
|
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 => {
|
_ if ch.is_ascii_digit() && var_name.is_empty() && !in_brace => {
|
||||||
var_name.push(ch);
|
var_name.push(ch);
|
||||||
let value = shenv.vars().get_var(&var_name);
|
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.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::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
@@ -46,6 +48,9 @@ pub fn sh_quit(code: i32) -> ! {
|
|||||||
job.killpg(Signal::SIGTERM).ok();
|
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);
|
exit(code);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
40
src/main.rs
40
src/main.rs
@@ -9,13 +9,46 @@ pub mod signal;
|
|||||||
pub mod prompt;
|
pub mod prompt;
|
||||||
pub mod builtin;
|
pub mod builtin;
|
||||||
pub mod expand;
|
pub mod expand;
|
||||||
|
pub mod tests;
|
||||||
|
|
||||||
|
use std::os::fd::AsRawFd;
|
||||||
|
|
||||||
|
use nix::sys::termios::{self, LocalFlags, Termios};
|
||||||
use signal::sig_setup;
|
use signal::sig_setup;
|
||||||
|
|
||||||
use crate::prelude::*;
|
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() {
|
pub fn main() {
|
||||||
sig_setup();
|
sig_setup();
|
||||||
|
save_termios();
|
||||||
|
set_termios();
|
||||||
let mut shenv = ShEnv::new();
|
let mut shenv = ShEnv::new();
|
||||||
if let Err(e) = shenv.source_rc() {
|
if let Err(e) = shenv.source_rc() {
|
||||||
eprintln!("Error sourcing rc file: {}", e.to_string());
|
eprintln!("Error sourcing rc file: {}", e.to_string());
|
||||||
@@ -23,13 +56,14 @@ pub fn main() {
|
|||||||
|
|
||||||
loop {
|
loop {
|
||||||
log!(TRACE, "Entered loop");
|
log!(TRACE, "Entered loop");
|
||||||
let line = match prompt::read_line(&mut shenv) {
|
match prompt::read_line(&mut shenv) {
|
||||||
Ok(line) => line,
|
Ok(line) => {
|
||||||
|
let _ = exec_input(line, &mut shenv).eprint();
|
||||||
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("{}",e);
|
eprintln!("{}",e);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let _ = exec_input(line, &mut shenv).eprint();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -175,6 +175,12 @@ impl Span {
|
|||||||
pub fn shift_end(&mut self, delta: isize) {
|
pub fn shift_end(&mut self, delta: isize) {
|
||||||
self.end = self.end.saturating_add_signed(delta)
|
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 {
|
macro_rules! try_match {
|
||||||
@@ -251,9 +257,9 @@ impl TkRule {
|
|||||||
// Generalized rules come last
|
// Generalized rules come last
|
||||||
try_match!(Whitespace,input);
|
try_match!(Whitespace,input);
|
||||||
try_match!(Comment,input);
|
try_match!(Comment,input);
|
||||||
|
try_match!(CmdSub,input);
|
||||||
try_match!(VarSub,input);
|
try_match!(VarSub,input);
|
||||||
try_match!(ProcSub,input);
|
try_match!(ProcSub,input);
|
||||||
try_match!(CmdSub,input);
|
|
||||||
try_match!(ArithSub,input);
|
try_match!(ArithSub,input);
|
||||||
try_match!(AndOp,input);
|
try_match!(AndOp,input);
|
||||||
try_match!(OrOp,input);
|
try_match!(OrOp,input);
|
||||||
|
|||||||
@@ -391,7 +391,6 @@ ndrule_def!(Case, shenv, |mut tokens: &[Token], shenv: &mut ShEnv| {
|
|||||||
node_toks.push(token.clone());
|
node_toks.push(token.clone());
|
||||||
tokens = &tokens[1..];
|
tokens = &tokens[1..];
|
||||||
if token.as_raw(shenv) != "in" {
|
if token.as_raw(shenv) != "in" {
|
||||||
panic!();
|
|
||||||
return Err(err("Expected `in` after case statement pattern", token.span(), shenv))
|
return Err(err("Expected `in` after case statement pattern", token.span(), shenv))
|
||||||
} else {
|
} else {
|
||||||
closed = true;
|
closed = true;
|
||||||
@@ -405,7 +404,6 @@ ndrule_def!(Case, shenv, |mut tokens: &[Token], shenv: &mut ShEnv| {
|
|||||||
_ => {
|
_ => {
|
||||||
if closed { break }
|
if closed { break }
|
||||||
log!(ERROR, token);
|
log!(ERROR, token);
|
||||||
panic!();
|
|
||||||
return Err(err("Expected `in` after case statement pattern", token.span(), shenv))
|
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();
|
let mut lists_iter = lists.iter().peekable();
|
||||||
while let Some(list) = lists_iter.next() {
|
while let Some(list) = lists_iter.next() {
|
||||||
node_toks.extend(list.tokens.clone());
|
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 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, "{:?}",list.tokens());
|
||||||
log!(ERROR, token);
|
log!(ERROR, token);
|
||||||
log!(ERROR, "{}",token.as_raw(shenv).trim());
|
log!(ERROR, "{}",token.as_raw(shenv).trim());
|
||||||
@@ -1149,6 +1143,7 @@ ndrule_def!(Command, shenv, |tokens: &[Token], shenv: &mut ShEnv| {
|
|||||||
TkRule::DQuote |
|
TkRule::DQuote |
|
||||||
TkRule::TildeSub |
|
TkRule::TildeSub |
|
||||||
TkRule::ArithSub |
|
TkRule::ArithSub |
|
||||||
|
TkRule::CmdSub |
|
||||||
TkRule::VarSub => {
|
TkRule::VarSub => {
|
||||||
argv.push(token.clone());
|
argv.push(token.clone());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,6 +82,16 @@ impl<'a> Highlighter for SynHelper<'a> {
|
|||||||
is_command = false;
|
is_command = false;
|
||||||
result.push_str(&rebuilt);
|
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 => {
|
TkRule::Subshell => {
|
||||||
let body = &raw[1..raw.len() - 1];
|
let body = &raw[1..raw.len() - 1];
|
||||||
let highlighted = self.highlight(body, 0).to_string();
|
let highlighted = self.highlight(body, 0).to_string();
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ bitflags! {
|
|||||||
pub struct ExecCtx {
|
pub struct ExecCtx {
|
||||||
redirs: Vec<Redir>,
|
redirs: Vec<Redir>,
|
||||||
depth: usize,
|
depth: usize,
|
||||||
state_stack: Vec<Self>,
|
|
||||||
max_depth: usize,
|
max_depth: usize,
|
||||||
flags: ExecFlags,
|
flags: ExecFlags,
|
||||||
io_masks: IoMasks,
|
io_masks: IoMasks,
|
||||||
@@ -23,36 +22,12 @@ impl ExecCtx {
|
|||||||
Self {
|
Self {
|
||||||
redirs: vec![],
|
redirs: vec![],
|
||||||
depth: 0,
|
depth: 0,
|
||||||
state_stack: vec![],
|
|
||||||
max_depth: 1500,
|
max_depth: 1500,
|
||||||
flags: ExecFlags::empty(),
|
flags: ExecFlags::empty(),
|
||||||
io_masks: IoMasks::new(),
|
io_masks: IoMasks::new(),
|
||||||
saved_io: None
|
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 {
|
pub fn as_cond(&self) -> Self {
|
||||||
let mut clone = self.clone();
|
let mut clone = self.clone();
|
||||||
let (cond_redirs,_) = self.sort_redirs();
|
let (cond_redirs,_) = self.sort_redirs();
|
||||||
|
|||||||
@@ -1,16 +1,41 @@
|
|||||||
use crate::prelude::*;
|
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)]
|
#[derive(Clone,Debug,PartialEq)]
|
||||||
pub struct InputMan {
|
pub struct InputMan {
|
||||||
input: Option<String>,
|
input: Option<String>,
|
||||||
saved_input: Option<String>,
|
|
||||||
spans: Vec<Rc<RefCell<Span>>>,
|
spans: Vec<Rc<RefCell<Span>>>,
|
||||||
saved_spans: Vec<Span>
|
saved_states: Vec<(String,Vec<SavedSpan>)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InputMan {
|
impl InputMan {
|
||||||
pub fn new() -> Self {
|
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) {
|
pub fn clear(&mut self) {
|
||||||
*self = Self::new();
|
*self = Self::new();
|
||||||
@@ -24,22 +49,27 @@ impl InputMan {
|
|||||||
pub fn get_input_mut(&mut self) -> Option<&mut String> {
|
pub fn get_input_mut(&mut self) -> Option<&mut String> {
|
||||||
self.input.as_mut()
|
self.input.as_mut()
|
||||||
}
|
}
|
||||||
pub fn save_state(&mut self) {
|
pub fn push_state(&mut self) {
|
||||||
self.saved_input = self.input.clone();
|
if let Some(input) = &self.input {
|
||||||
self.saved_spans.clear();
|
let saved_input = input.clone();
|
||||||
|
let mut saved_spans = vec![];
|
||||||
for span in &self.spans {
|
for span in &self.spans {
|
||||||
self.saved_spans.push(span.borrow().clone());
|
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) {
|
pub fn pop_state(&mut self) {
|
||||||
if self.saved_input.is_some() {
|
if let Some((saved_input, saved_spans)) = self.saved_states.pop() {
|
||||||
self.input = self.saved_input.take();
|
self.input = Some(saved_input);
|
||||||
|
let mut restored_spans = vec![];
|
||||||
for (span, saved_span) in self.spans.iter_mut().zip(self.saved_spans.iter()) {
|
for saved_span in saved_spans.into_iter() {
|
||||||
*span.borrow_mut() = saved_span.clone();
|
saved_span.restore();
|
||||||
|
let span = saved_span.into_span();
|
||||||
|
restored_spans.push(span);
|
||||||
}
|
}
|
||||||
|
self.spans = restored_spans;
|
||||||
self.saved_spans.clear();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn new_span(&mut self, start: usize, end: usize) -> Rc<RefCell<Span>> {
|
pub fn new_span(&mut self, start: usize, end: usize) -> Rc<RefCell<Span>> {
|
||||||
|
|||||||
@@ -538,7 +538,7 @@ impl JobTab {
|
|||||||
None
|
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) {
|
let jobs = if flags.contains(JobCmdFlags::NEW_ONLY) {
|
||||||
&self.jobs
|
&self.jobs
|
||||||
.iter()
|
.iter()
|
||||||
@@ -551,6 +551,7 @@ impl JobTab {
|
|||||||
.map(|job| job.as_ref())
|
.map(|job| job.as_ref())
|
||||||
.collect::<Vec<Option<&Job>>>()
|
.collect::<Vec<Option<&Job>>>()
|
||||||
};
|
};
|
||||||
|
let mut jobs_to_remove = vec![];
|
||||||
for job in jobs.iter().flatten() {
|
for job in jobs.iter().flatten() {
|
||||||
// Skip foreground job
|
// Skip foreground job
|
||||||
let id = job.tabid().unwrap();
|
let id = job.tabid().unwrap();
|
||||||
@@ -563,6 +564,12 @@ impl JobTab {
|
|||||||
}
|
}
|
||||||
// Print the job in the selected format
|
// Print the job in the selected format
|
||||||
write(borrow_fd(1), format!("{}\n",job.display(&self.order,flags)).as_bytes())?;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
use libc::{STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO};
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
#[derive(Clone,Debug)]
|
#[derive(Clone,Debug)]
|
||||||
@@ -51,12 +53,9 @@ impl ShEnv {
|
|||||||
if repl_span.borrow().expanded {
|
if repl_span.borrow().expanded {
|
||||||
return vec![];
|
return vec![];
|
||||||
}
|
}
|
||||||
log!(INFO, repl_span);
|
|
||||||
log!(INFO, new);
|
|
||||||
repl_span.borrow_mut().expanded = true;
|
repl_span.borrow_mut().expanded = true;
|
||||||
let saved_spans = self.input_man.spans_mut().clone();
|
let saved_spans = self.input_man.spans_mut().clone();
|
||||||
let mut new_tokens = Lexer::new(new.to_string(), self).lex();
|
let mut new_tokens = Lexer::new(new.to_string(), self).lex();
|
||||||
log!(INFO, new_tokens);
|
|
||||||
*self.input_man.spans_mut() = saved_spans;
|
*self.input_man.spans_mut() = saved_spans;
|
||||||
|
|
||||||
let offset = repl_span.borrow().start();
|
let offset = repl_span.borrow().start();
|
||||||
@@ -71,9 +70,8 @@ impl ShEnv {
|
|||||||
if let Some(input) = self.input_man.get_input_mut() {
|
if let Some(input) = self.input_man.get_input_mut() {
|
||||||
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;
|
||||||
log!(INFO, input);
|
|
||||||
log!(INFO, range);
|
|
||||||
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();
|
||||||
@@ -168,11 +166,11 @@ impl ShEnv {
|
|||||||
let saved_in = saved.stdin;
|
let saved_in = saved.stdin;
|
||||||
let saved_out = saved.stdout;
|
let saved_out = saved.stdout;
|
||||||
let saved_err = saved.stderr;
|
let saved_err = saved.stderr;
|
||||||
dup2(0,saved_in)?;
|
dup2(saved_in,STDIN_FILENO)?;
|
||||||
close(saved_in)?;
|
close(saved_in)?;
|
||||||
dup2(1,saved_out)?;
|
dup2(saved_out,STDOUT_FILENO)?;
|
||||||
close(saved_out)?;
|
close(saved_out)?;
|
||||||
dup2(2,saved_err)?;
|
dup2(saved_err,STDERR_FILENO)?;
|
||||||
close(saved_err)?;
|
close(saved_err)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -53,12 +53,7 @@ pub extern "C" fn ignore_sigchld(_: libc::c_int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extern "C" fn handle_sigquit(_: libc::c_int) {
|
extern "C" fn handle_sigquit(_: libc::c_int) {
|
||||||
write_jobs(|j| {
|
sh_quit(0)
|
||||||
for job in j.jobs_mut().iter_mut().flatten() {
|
|
||||||
job.killpg(Signal::SIGTERM).ok();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
exit(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub extern "C" fn handle_sigchld(_: libc::c_int) {
|
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
|
* 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.
|
* If this assumption is incorrect, the code has gone wrong somewhere.
|
||||||
*/
|
*/
|
||||||
let (
|
if let Some((
|
||||||
pgid,
|
pgid,
|
||||||
is_fg,
|
is_fg,
|
||||||
is_finished
|
is_finished
|
||||||
) = write_jobs(|j| {
|
)) = write_jobs(|j| {
|
||||||
let fg_pgid = j.get_fg().map(|job| job.pgid());
|
let fg_pgid = j.get_fg().map(|job| job.pgid());
|
||||||
if let Some(job) = j.query_mut(JobID::Pid(pid)) {
|
if let Some(job) = j.query_mut(JobID::Pid(pid)) {
|
||||||
let pgid = job.pgid();
|
let pgid = job.pgid();
|
||||||
@@ -141,11 +136,11 @@ pub fn child_exited(pid: Pid, status: WtStat) -> ShResult<()> {
|
|||||||
child.set_stat(status);
|
child.set_stat(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok((pgid, is_fg, is_finished))
|
Some((pgid, is_fg, is_finished))
|
||||||
} else {
|
} else {
|
||||||
Err(ShErr::simple(ShErrKind::InternalErr, "Job not found"))
|
None
|
||||||
}
|
}
|
||||||
})?;
|
}) {
|
||||||
|
|
||||||
if is_finished {
|
if is_finished {
|
||||||
if is_fg {
|
if is_fg {
|
||||||
@@ -159,5 +154,6 @@ pub fn child_exited(pid: Pid, status: WtStat) -> ShResult<()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
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