Implemented assignments, working on job control
This commit is contained in:
@@ -2,9 +2,14 @@ use std::collections::VecDeque;
|
||||
|
||||
use nix::sys::wait::WaitPidFlag;
|
||||
|
||||
use crate::{libsh::error::ShResult, prelude::*, procio::{IoFrame, IoPipe, IoStack}, state};
|
||||
use crate::{builtin::echo::echo, libsh::error::ShResult, prelude::*, procio::{IoFrame, IoPipe, IoStack}, state::{self, write_vars}};
|
||||
|
||||
use super::{lex::Tk, ConjunctNode, ConjunctOp, NdRule, Node, Redir, RedirType};
|
||||
use super::{lex::{Tk, TkFlags}, AssignKind, ConjunctNode, ConjunctOp, NdRule, Node, Redir, RedirType};
|
||||
|
||||
pub enum AssignBehavior {
|
||||
Export,
|
||||
Set
|
||||
}
|
||||
|
||||
/// Arguments to the execvpe function
|
||||
pub struct ExecArgs {
|
||||
@@ -14,8 +19,9 @@ pub struct ExecArgs {
|
||||
}
|
||||
|
||||
impl ExecArgs {
|
||||
pub fn new(argv: Vec<String>) -> Self {
|
||||
pub fn new(argv: Vec<Tk>) -> Self {
|
||||
assert!(!argv.is_empty());
|
||||
let argv = prepare_argv(argv);
|
||||
let cmd = Self::get_cmd(&argv);
|
||||
let argv = Self::get_argv(argv);
|
||||
let envp = Self::get_envp();
|
||||
@@ -42,22 +48,33 @@ impl<'t> Dispatcher<'t> {
|
||||
let nodes = VecDeque::from(nodes);
|
||||
Self { nodes, io_stack: IoStack::new() }
|
||||
}
|
||||
pub fn begin_dispatch(&mut self) -> ShResult<'t,()> {
|
||||
pub fn begin_dispatch(&mut self) -> ShResult<()> {
|
||||
flog!(TRACE, "beginning dispatch");
|
||||
while let Some(list) = self.nodes.pop_front() {
|
||||
self.dispatch_node(list)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub fn dispatch_node(&mut self, node: Node<'t>) -> ShResult<'t,()> {
|
||||
pub fn dispatch_node(&mut self, node: Node<'t>) -> ShResult<()> {
|
||||
match node.class {
|
||||
NdRule::CmdList {..} => self.exec_conjunction(node)?,
|
||||
NdRule::Pipeline {..} => self.exec_pipeline(node)?,
|
||||
NdRule::Command {..} => self.exec_cmd(node)?,
|
||||
NdRule::Command {..} => self.dispatch_cmd(node)?,
|
||||
_ => unreachable!()
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub fn exec_conjunction(&mut self, conjunction: Node<'t>) -> ShResult<'t,()> {
|
||||
pub fn dispatch_cmd(&mut self, node: Node<'t>) -> ShResult<()> {
|
||||
let Some(cmd) = node.get_command() else {
|
||||
return self.exec_cmd(node)
|
||||
};
|
||||
if cmd.flags.contains(TkFlags::BUILTIN) {
|
||||
self.exec_builtin(node)
|
||||
} else {
|
||||
self.exec_cmd(node)
|
||||
}
|
||||
}
|
||||
pub fn exec_conjunction(&mut self, conjunction: Node<'t>) -> ShResult<()> {
|
||||
let NdRule::CmdList { elements } = conjunction.class else {
|
||||
unreachable!()
|
||||
};
|
||||
@@ -76,7 +93,7 @@ impl<'t> Dispatcher<'t> {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub fn exec_pipeline(&mut self, pipeline: Node<'t>) -> ShResult<'t,()> {
|
||||
pub fn exec_pipeline(&mut self, pipeline: Node<'t>) -> ShResult<()> {
|
||||
let NdRule::Pipeline { cmds, pipe_err } = pipeline.class else {
|
||||
unreachable!()
|
||||
};
|
||||
@@ -96,15 +113,45 @@ impl<'t> Dispatcher<'t> {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub fn exec_cmd(&mut self, cmd: Node<'t>) -> ShResult<'t,()> {
|
||||
pub fn exec_builtin(&mut self, mut cmd: Node<'t>) -> ShResult<()> {
|
||||
let NdRule::Command { ref mut assignments, argv } = &mut cmd.class else {
|
||||
unreachable!()
|
||||
};
|
||||
let env_vars_to_unset = self.set_assignments(mem::take(assignments), AssignBehavior::Export);
|
||||
let cmd_raw = cmd.get_command().unwrap();
|
||||
flog!(TRACE, "doing builtin");
|
||||
let result = match cmd_raw.span.as_str() {
|
||||
"echo" => echo(cmd, &mut self.io_stack),
|
||||
_ => unimplemented!("Have not yet added support for builtin '{}'", cmd_raw.span.as_str())
|
||||
};
|
||||
|
||||
for var in env_vars_to_unset {
|
||||
env::set_var(&var, "");
|
||||
}
|
||||
|
||||
Ok(result?)
|
||||
}
|
||||
pub fn exec_cmd(&mut self, cmd: Node<'t>) -> ShResult<()> {
|
||||
let NdRule::Command { assignments, argv } = cmd.class else {
|
||||
unreachable!()
|
||||
};
|
||||
let mut env_vars_to_unset = vec![];
|
||||
if !assignments.is_empty() {
|
||||
let assign_behavior = if argv.is_empty() {
|
||||
AssignBehavior::Set
|
||||
} else {
|
||||
AssignBehavior::Export
|
||||
};
|
||||
env_vars_to_unset = self.set_assignments(assignments, assign_behavior);
|
||||
}
|
||||
for redir in cmd.redirs {
|
||||
self.io_stack.push_to_frame(redir);
|
||||
}
|
||||
if argv.is_empty() {
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
let exec_args = ExecArgs::new(prepare_argv(argv));
|
||||
let exec_args = ExecArgs::new(argv);
|
||||
let io_frame = self.io_stack.pop_frame();
|
||||
run_fork(
|
||||
io_frame,
|
||||
@@ -113,8 +160,51 @@ impl<'t> Dispatcher<'t> {
|
||||
def_parent_action
|
||||
)?;
|
||||
|
||||
for var in env_vars_to_unset {
|
||||
std::env::set_var(&var, "");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
pub fn set_assignments(&self, assigns: Vec<Node<'t>>, behavior: AssignBehavior) -> Vec<String> {
|
||||
let mut new_env_vars = vec![];
|
||||
match behavior {
|
||||
AssignBehavior::Export => {
|
||||
for assign in assigns {
|
||||
let NdRule::Assignment { kind, var, val } = assign.class else {
|
||||
unreachable!()
|
||||
};
|
||||
let var = var.span.as_str();
|
||||
let val = val.span.as_str();
|
||||
match kind {
|
||||
AssignKind::Eq => std::env::set_var(var, val),
|
||||
AssignKind::PlusEq => todo!(),
|
||||
AssignKind::MinusEq => todo!(),
|
||||
AssignKind::MultEq => todo!(),
|
||||
AssignKind::DivEq => todo!(),
|
||||
}
|
||||
new_env_vars.push(var.to_string());
|
||||
}
|
||||
}
|
||||
AssignBehavior::Set => {
|
||||
for assign in assigns {
|
||||
let NdRule::Assignment { kind, var, val } = assign.class else {
|
||||
unreachable!()
|
||||
};
|
||||
let var = var.span.as_str();
|
||||
let val = val.span.as_str();
|
||||
match kind {
|
||||
AssignKind::Eq => write_vars(|v| v.new_var(var, val)),
|
||||
AssignKind::PlusEq => todo!(),
|
||||
AssignKind::MinusEq => todo!(),
|
||||
AssignKind::MultEq => todo!(),
|
||||
AssignKind::DivEq => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
new_env_vars
|
||||
}
|
||||
}
|
||||
|
||||
pub fn prepare_argv(argv: Vec<Tk>) -> Vec<String> {
|
||||
@@ -134,15 +224,20 @@ pub fn run_fork<'t,C,P>(
|
||||
exec_args: ExecArgs,
|
||||
child_action: C,
|
||||
parent_action: P,
|
||||
) -> ShResult<'t,()>
|
||||
) -> ShResult<()>
|
||||
where
|
||||
C: Fn(IoFrame,ExecArgs),
|
||||
P: Fn(IoFrame,Pid) -> ShResult<'t,()>
|
||||
C: Fn(IoFrame,ExecArgs) -> Errno,
|
||||
P: Fn(IoFrame,Pid) -> ShResult<()>
|
||||
{
|
||||
match unsafe { fork()? } {
|
||||
ForkResult::Child => {
|
||||
child_action(io_frame,exec_args);
|
||||
exit(1);
|
||||
let cmd = &exec_args.cmd.to_str().unwrap().to_string();
|
||||
let errno = child_action(io_frame,exec_args);
|
||||
match errno {
|
||||
Errno::ENOENT => eprintln!("Command not found: {}", cmd),
|
||||
_ => eprintln!("{errno}")
|
||||
}
|
||||
exit(errno as i32);
|
||||
}
|
||||
ForkResult::Parent { child } => {
|
||||
parent_action(io_frame,child)
|
||||
@@ -151,18 +246,21 @@ where
|
||||
}
|
||||
|
||||
/// The default behavior for the child process after forking
|
||||
pub fn def_child_action<'t>(mut io_frame: IoFrame, exec_args: ExecArgs) {
|
||||
io_frame.redirect().unwrap();
|
||||
execvpe(&exec_args.cmd, &exec_args.argv, &exec_args.envp).unwrap();
|
||||
pub fn def_child_action<'t>(mut io_frame: IoFrame, exec_args: ExecArgs) -> Errno {
|
||||
if let Err(e) = io_frame.redirect() {
|
||||
eprintln!("{e}");
|
||||
}
|
||||
let Err(e) = execvpe(&exec_args.cmd, &exec_args.argv, &exec_args.envp);
|
||||
e
|
||||
}
|
||||
|
||||
/// The default behavior for the parent process after forking
|
||||
pub fn def_parent_action<'t>(io_frame: IoFrame, child: Pid) -> ShResult<'t,()> {
|
||||
let status = waitpid(child, Some(WaitPidFlag::WSTOPPED))?;
|
||||
pub fn def_parent_action<'t>(io_frame: IoFrame, child: Pid) -> ShResult<()> {
|
||||
let status = waitpid(child, Some(WtFlag::WSTOPPED))?;
|
||||
match status {
|
||||
WaitStatus::Exited(_, status) => state::set_status(status),
|
||||
WtStat::Exited(_, status) => state::set_status(status),
|
||||
_ => unimplemented!()
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user