- fixed 'I' command in normal mode not moving to exact start of line

- Added disown builtin
- Fixed job table not hanging up child processes on exit
- Added target architecture and os to --version output
- Added local builtin for creating variables scoped to functions
This commit is contained in:
2026-02-23 16:10:49 -05:00
parent 723bfd8413
commit f8e02d31cd
10 changed files with 205 additions and 17 deletions

34
src/builtin/eval.rs Normal file
View File

@@ -0,0 +1,34 @@
use nix::{errno::Errno, unistd::execvpe};
use crate::{
builtin::setup_builtin,
jobs::JobBldr,
libsh::error::ShResult,
parse::{NdRule, Node, execute::exec_input},
procio::IoStack,
state,
};
pub fn eval(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResult<()> {
let NdRule::Command {
assignments: _,
argv,
} = node.class
else {
unreachable!()
};
let (expanded_argv, _guard) = setup_builtin(argv, job, Some((io_stack, node.redirs)))?;
if expanded_argv.is_empty() {
state::set_status(0);
return Ok(());
}
let joined_argv = expanded_argv.into_iter()
.map(|(s, _)| s)
.collect::<Vec<_>>()
.join(" ");
exec_input(joined_argv, None, false)
}

View File

@@ -4,7 +4,7 @@ use crate::{
parse::{NdRule, Node},
prelude::*,
procio::{IoStack, borrow_fd},
state::{self, VarFlags, write_vars},
state::{self, VarFlags, read_vars, write_vars},
};
use super::setup_builtin;
@@ -45,3 +45,42 @@ pub fn export(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResult
state::set_status(0);
Ok(())
}
pub fn local(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResult<()> {
let NdRule::Command {
assignments: _,
argv,
} = node.class
else {
unreachable!()
};
let (argv, _guard) = setup_builtin(argv, job, Some((io_stack, node.redirs)))?;
if argv.is_empty() {
// Display the local variables
let vars_output = read_vars(|v| {
let mut vars = v.flatten_vars()
.into_iter()
.map(|(k, v)| format!("{}={}", k, v))
.collect::<Vec<String>>();
vars.sort();
let mut vars_joined = vars.join("\n");
vars_joined.push('\n');
vars_joined
});
let stdout = borrow_fd(STDOUT_FILENO);
write(stdout, vars_output.as_bytes())?; // Write it
} else {
for (arg, _) in argv {
if let Some((var, val)) = arg.split_once('=') {
write_vars(|v| v.set_var(var, val, VarFlags::LOCAL));
} else {
write_vars(|v| v.set_var(&arg, "", VarFlags::LOCAL)); // Create an uninitialized local variable
}
}
}
state::set_status(0);
Ok(())
}

View File

@@ -179,3 +179,46 @@ pub fn jobs(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResult<(
Ok(())
}
pub fn disown(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResult<()> {
let blame = node.get_span().clone();
let NdRule::Command {
assignments: _,
argv,
} = node.class
else {
unreachable!()
};
let (argv, _guard) = setup_builtin(argv, job, Some((io_stack, node.redirs)))?;
let mut argv = argv.into_iter();
let curr_job_id = if let Some(id) = read_jobs(|j| j.curr_job()) {
id
} else {
return Err(ShErr::full(ShErrKind::ExecFail, "disown: No jobs to disown", blame));
};
let mut tabid = curr_job_id;
let mut nohup = false;
let mut disown_all = false;
while let Some((arg, span)) = argv.next() {
match arg.as_str() {
"-h" => nohup = true,
"-a" => disown_all = true,
_ => {
tabid = parse_job_id(&arg, span.clone())?;
}
}
}
if disown_all {
write_jobs(|j| j.disown_all(nohup))?;
} else {
write_jobs(|j| j.disown(JobID::TableID(tabid), nohup))?;
}
state::set_status(0);
Ok(())
}

1
src/builtin/local.rs Normal file
View File

@@ -0,0 +1 @@

View File

@@ -27,11 +27,12 @@ pub mod trap;
pub mod zoltraak;
pub mod dirstack;
pub mod exec;
pub mod eval;
pub const BUILTINS: [&str; 25] = [
"echo", "cd", "read", "export", "pwd", "source", "shift", "jobs", "fg", "bg", "alias", "unalias",
pub const BUILTINS: [&str; 28] = [
"echo", "cd", "read", "export", "local", "pwd", "source", "shift", "jobs", "fg", "bg", "disown", "alias", "unalias",
"return", "break", "continue", "exit", "zoltraak", "shopt", "builtin", "command", "trap",
"pushd", "popd", "dirs", "exec",
"pushd", "popd", "dirs", "exec", "eval"
];
/// Sets up a builtin command