Add array support for local/export/readonly builtins
Add array length syntax ${arr[#]}
Map read path now expands variables before splitting on ., fixing map "$node" with dotted paths
Map assignment path uses quote-aware token splitting, enabling quoted keys like "--type="
Completion errors now display above prompt instead of being overwritten
Fix nested if/fi parser bug when closing keywords appear on separate lines
Add QuoteState enum, replacing ad-hoc quote tracking booleans across lexer, highlighter, and expansion
Add split_tk_at/split_tk for quote-aware token splitting with span preservation
Refactor setup_builtin to accept optional argv for deferred expansion
Add ariadne dependency (not yet wired up)
This commit is contained in:
@@ -18,7 +18,8 @@ pub fn alias(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResult<
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
let (argv, _guard) = setup_builtin(argv, job, Some((io_stack, node.redirs)))?;
|
||||
let (argv, _guard) = setup_builtin(Some(argv), job, Some((io_stack, node.redirs)))?;
|
||||
let argv = argv.unwrap();
|
||||
|
||||
if argv.is_empty() {
|
||||
// Display the environment variables
|
||||
@@ -67,7 +68,8 @@ pub fn unalias(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResul
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
let (argv, _guard) = setup_builtin(argv, job, Some((io_stack, node.redirs)))?;
|
||||
let (argv, _guard) = setup_builtin(Some(argv), job, Some((io_stack, node.redirs)))?;
|
||||
let argv = argv.unwrap();
|
||||
|
||||
if argv.is_empty() {
|
||||
// Display the environment variables
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::iter::Peekable;
|
||||
|
||||
use crate::{
|
||||
getopt::{Opt, OptSpec, get_opts_from_tokens}, jobs::JobBldr, libsh::error::{ShErr, ShErrKind, ShResult}, parse::{NdRule, Node}, prelude::*, procio::{IoStack, borrow_fd}, state::{self, VarFlags, VarKind, write_vars}
|
||||
};
|
||||
@@ -51,7 +53,8 @@ fn arr_pop_inner(node: Node, io_stack: &mut IoStack, job: &mut JobBldr, end: End
|
||||
|
||||
let (argv, opts) = get_opts_from_tokens(argv, &arr_op_optspec())?;
|
||||
let arr_op_opts = get_arr_op_opts(opts)?;
|
||||
let (argv, _guard) = setup_builtin(argv, job, Some((io_stack, node.redirs)))?;
|
||||
let (argv, _guard) = setup_builtin(Some(argv), job, Some((io_stack, node.redirs)))?;
|
||||
let argv = argv.unwrap();
|
||||
let stdout = borrow_fd(STDOUT_FILENO);
|
||||
let mut status = 0;
|
||||
|
||||
@@ -91,7 +94,8 @@ fn arr_push_inner(node: Node, io_stack: &mut IoStack, job: &mut JobBldr, end: En
|
||||
|
||||
let (argv, opts) = get_opts_from_tokens(argv, &arr_op_optspec())?;
|
||||
let _arr_op_opts = get_arr_op_opts(opts)?;
|
||||
let (argv, _guard) = setup_builtin(argv, job, Some((io_stack, node.redirs)))?;
|
||||
let (argv, _guard) = setup_builtin(Some(argv), job, Some((io_stack, node.redirs)))?;
|
||||
let argv = argv.unwrap();
|
||||
|
||||
let mut argv = argv.into_iter();
|
||||
let Some((name, _)) = argv.next() else {
|
||||
@@ -140,7 +144,8 @@ pub fn arr_rotate(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShRe
|
||||
|
||||
let (argv, opts) = get_opts_from_tokens(argv, &arr_op_optspec())?;
|
||||
let arr_op_opts = get_arr_op_opts(opts)?;
|
||||
let (argv, _guard) = setup_builtin(argv, job, Some((io_stack, node.redirs)))?;
|
||||
let (argv, _guard) = setup_builtin(Some(argv), job, Some((io_stack, node.redirs)))?;
|
||||
let argv = argv.unwrap();
|
||||
|
||||
for (arg, _) in argv {
|
||||
write_vars(|v| -> ShResult<()> {
|
||||
|
||||
@@ -18,7 +18,8 @@ pub fn cd(node: Node, job: &mut JobBldr) -> ShResult<()> {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
let (argv, _) = setup_builtin(argv, job, None)?;
|
||||
let (argv, _) = setup_builtin(Some(argv), job, None)?;
|
||||
let argv = argv.unwrap();
|
||||
|
||||
let new_dir = if let Some((arg, _)) = argv.into_iter().next() {
|
||||
PathBuf::from(arg)
|
||||
|
||||
@@ -168,7 +168,8 @@ pub fn complete_builtin(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -
|
||||
|
||||
let (argv, opts) = get_opts_from_tokens(argv, &COMP_OPTS)?;
|
||||
let comp_opts = get_comp_opts(opts)?;
|
||||
let (argv, _) = setup_builtin(argv, job, Some((io_stack, node.redirs)))?;
|
||||
let (argv, _) = setup_builtin(Some(argv), job, Some((io_stack, node.redirs)))?;
|
||||
let argv = argv.unwrap();
|
||||
|
||||
if comp_opts.flags.contains(CompFlags::PRINT) {
|
||||
if argv.is_empty() {
|
||||
@@ -242,7 +243,7 @@ pub fn compgen_builtin(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) ->
|
||||
let (argv, opts) = get_opts_from_tokens(argv, &COMPGEN_OPTS)?;
|
||||
let prefix = argv.clone().into_iter().nth(1).unwrap_or_default();
|
||||
let comp_opts = get_comp_opts(opts)?;
|
||||
let (_, _guard) = setup_builtin(argv, job, Some((io_stack, node.redirs)))?;
|
||||
let (_, _guard) = setup_builtin(Some(argv), job, Some((io_stack, node.redirs)))?;
|
||||
|
||||
let comp_spec = BashCompSpec::from_comp_opts(comp_opts).with_source(src);
|
||||
|
||||
|
||||
@@ -127,7 +127,8 @@ pub fn pushd(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResult<
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
let (argv, _guard) = setup_builtin(argv, job, Some((io_stack, node.redirs)))?;
|
||||
let (argv, _guard) = setup_builtin(Some(argv), job, Some((io_stack, node.redirs)))?;
|
||||
let argv = argv.unwrap();
|
||||
|
||||
let mut dir = None;
|
||||
let mut rotate_idx = None;
|
||||
@@ -213,7 +214,8 @@ pub fn popd(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResult<(
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
let (argv, _guard) = setup_builtin(argv, job, Some((io_stack, node.redirs)))?;
|
||||
let (argv, _guard) = setup_builtin(Some(argv), job, Some((io_stack, node.redirs)))?;
|
||||
let argv = argv.unwrap();
|
||||
|
||||
let mut remove_idx = None;
|
||||
let mut no_cd = false;
|
||||
@@ -316,7 +318,8 @@ pub fn dirs(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResult<(
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
let (argv, _guard) = setup_builtin(argv, job, Some((io_stack, node.redirs)))?;
|
||||
let (argv, _guard) = setup_builtin(Some(argv), job, Some((io_stack, node.redirs)))?;
|
||||
let argv = argv.unwrap();
|
||||
|
||||
let mut abbreviate_home = true;
|
||||
let mut one_per_line = false;
|
||||
|
||||
@@ -51,7 +51,8 @@ pub fn echo(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResult<(
|
||||
assert!(!argv.is_empty());
|
||||
let (argv, opts) = get_opts_from_tokens(argv, &ECHO_OPTS)?;
|
||||
let flags = get_echo_flags(opts).blame(blame)?;
|
||||
let (argv, _guard) = setup_builtin(argv, job, Some((io_stack, node.redirs)))?;
|
||||
let (argv, _guard) = setup_builtin(Some(argv), job, Some((io_stack, node.redirs)))?;
|
||||
let argv = argv.unwrap();
|
||||
|
||||
let output_channel = if flags.contains(EchoFlags::USE_STDERR) {
|
||||
borrow_fd(STDERR_FILENO)
|
||||
|
||||
@@ -16,7 +16,8 @@ pub fn eval(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResult<(
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
let (expanded_argv, _guard) = setup_builtin(argv, job, Some((io_stack, node.redirs)))?;
|
||||
let (expanded_argv, _guard) = setup_builtin(Some(argv), job, Some((io_stack, node.redirs)))?;
|
||||
let expanded_argv = expanded_argv.unwrap();
|
||||
|
||||
if expanded_argv.is_empty() {
|
||||
state::set_status(0);
|
||||
|
||||
@@ -18,7 +18,8 @@ pub fn exec_builtin(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> Sh
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
let (expanded_argv, guard) = setup_builtin(argv, job, Some((io_stack, node.redirs)))?;
|
||||
let (expanded_argv, guard) = setup_builtin(Some(argv), job, Some((io_stack, node.redirs)))?;
|
||||
let expanded_argv = expanded_argv.unwrap();
|
||||
if let Some(g) = guard {
|
||||
// Persist redirections so they affect the entire shell,
|
||||
// not just this command call
|
||||
|
||||
@@ -28,7 +28,8 @@ pub fn continue_job(node: Node, job: &mut JobBldr, behavior: JobBehavior) -> ShR
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
let (argv, _) = setup_builtin(argv, job, None)?;
|
||||
let (argv, _) = setup_builtin(Some(argv), job, None)?;
|
||||
let argv = argv.unwrap();
|
||||
let mut argv = argv.into_iter();
|
||||
|
||||
if read_jobs(|j| j.get_fg().is_some()) {
|
||||
@@ -143,7 +144,8 @@ pub fn jobs(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResult<(
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
let (argv, _guard) = setup_builtin(argv, job, Some((io_stack, node.redirs)))?;
|
||||
let (argv, _guard) = setup_builtin(Some(argv), job, Some((io_stack, node.redirs)))?;
|
||||
let argv = argv.unwrap();
|
||||
|
||||
let mut flags = JobCmdFlags::empty();
|
||||
for (arg, span) in argv {
|
||||
@@ -190,7 +192,8 @@ pub fn disown(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResult
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
let (argv, _guard) = setup_builtin(argv, job, Some((io_stack, node.redirs)))?;
|
||||
let (argv, _guard) = setup_builtin(Some(argv), job, Some((io_stack, node.redirs)))?;
|
||||
let argv = argv.unwrap();
|
||||
let mut argv = argv.into_iter();
|
||||
|
||||
let curr_job_id = if let Some(id) = read_jobs(|j| j.curr_job()) {
|
||||
|
||||
@@ -5,7 +5,7 @@ use nix::{libc::STDOUT_FILENO, unistd::write};
|
||||
use serde_json::{Map, Value};
|
||||
|
||||
use crate::{
|
||||
expand::expand_cmd_sub, getopt::{Opt, OptSpec, get_opts_from_tokens}, jobs::JobBldr, libsh::error::{ShErr, ShErrKind, ShResult}, parse::{NdRule, Node, lex::{split_all_unescaped, split_at_unescaped}}, procio::{IoStack, borrow_fd}, state::{self, read_vars, write_vars}
|
||||
expand::expand_cmd_sub, getopt::{Opt, OptSpec, get_opts_from_tokens}, jobs::JobBldr, libsh::error::{ShErr, ShErrKind, ShResult}, parse::{NdRule, Node, lex::{split_tk, split_tk_at}}, procio::{IoStack, borrow_fd}, state::{self, read_vars, write_vars}
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -252,17 +252,23 @@ pub fn map(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResult<()
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
let (argv, opts) = get_opts_from_tokens(argv, &map_opts_spec())?;
|
||||
let (mut argv, opts) = get_opts_from_tokens(argv, &map_opts_spec())?;
|
||||
let map_opts = get_map_opts(opts);
|
||||
let (argv, _guard) = setup_builtin(argv, job, Some((io_stack, node.redirs)))?;
|
||||
let (_, _guard) = setup_builtin(None, job, Some((io_stack, node.redirs)))?;
|
||||
if !argv.is_empty() {
|
||||
argv.remove(0); // remove "map" command from argv
|
||||
}
|
||||
|
||||
for (arg,_) in argv {
|
||||
if let Some((lhs,rhs)) = split_at_unescaped(&arg, "=") {
|
||||
let path = split_all_unescaped(&lhs, ".");
|
||||
for arg in argv {
|
||||
if let Some((lhs,rhs)) = split_tk_at(&arg, "=") {
|
||||
let path = split_tk(&lhs, ".")
|
||||
.into_iter()
|
||||
.map(|s| s.expand().map(|exp| exp.get_words().join(" ")))
|
||||
.collect::<ShResult<Vec<String>>>()?;
|
||||
let Some(name) = path.first() else {
|
||||
return Err(ShErr::simple(
|
||||
ShErrKind::InternalErr,
|
||||
format!("invalid map path: {}", lhs)
|
||||
format!("invalid map path: {}", lhs.as_str())
|
||||
));
|
||||
};
|
||||
|
||||
@@ -271,39 +277,42 @@ pub fn map(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResult<()
|
||||
let make_leaf = |s: String| {
|
||||
if is_func { MapNode::DynamicLeaf(s) } else { MapNode::StaticLeaf(s) }
|
||||
};
|
||||
let found = write_vars(|v| {
|
||||
let expanded = rhs.expand()?.get_words().join(" ");
|
||||
let found = write_vars(|v| -> ShResult<bool> {
|
||||
if let Some(map) = v.get_map_mut(name) {
|
||||
if is_json {
|
||||
if let Ok(parsed) = serde_json::from_str::<Value>(&rhs) {
|
||||
if let Ok(parsed) = serde_json::from_str::<Value>(expanded.as_str()) {
|
||||
map.set(&path[1..], parsed.into());
|
||||
} else {
|
||||
map.set(&path[1..], make_leaf(rhs.clone()));
|
||||
map.set(&path[1..], make_leaf(expanded.clone()));
|
||||
}
|
||||
} else {
|
||||
map.set(&path[1..], make_leaf(rhs.clone()));
|
||||
map.set(&path[1..], make_leaf(expanded.clone()));
|
||||
}
|
||||
true
|
||||
Ok(true)
|
||||
} else {
|
||||
false
|
||||
Ok(false)
|
||||
}
|
||||
});
|
||||
|
||||
if !found {
|
||||
if !found? {
|
||||
let mut new = MapNode::default();
|
||||
if is_json && let Ok(parsed) = serde_json::from_str::<Value>(&rhs) {
|
||||
if is_json /*&& let Ok(parsed) = serde_json::from_str::<Value>(rhs.as_str()) */{
|
||||
let parsed = serde_json::from_str::<Value>(expanded.as_str()).unwrap();
|
||||
let node: MapNode = parsed.into();
|
||||
new.set(&path[1..], node);
|
||||
} else {
|
||||
new.set(&path[1..], make_leaf(rhs));
|
||||
new.set(&path[1..], make_leaf(expanded));
|
||||
}
|
||||
write_vars(|v| v.set_map(name, new, map_opts.flags.contains(MapFlags::LOCAL)));
|
||||
}
|
||||
} else {
|
||||
let path = split_all_unescaped(&arg, ".");
|
||||
let expanded = arg.expand()?.get_words().join(" ");
|
||||
let path: Vec<String> = expanded.split('.').map(|s| s.to_string()).collect();
|
||||
let Some(name) = path.first() else {
|
||||
return Err(ShErr::simple(
|
||||
ShErrKind::InternalErr,
|
||||
format!("invalid map path: {}", &arg)
|
||||
format!("invalid map path: {}", expanded)
|
||||
));
|
||||
};
|
||||
|
||||
|
||||
@@ -67,13 +67,13 @@ pub const BUILTINS: [&str; 41] = [
|
||||
/// * If redirections are given, the second field of the resulting tuple will
|
||||
/// *always* be `Some()`
|
||||
/// * If no redirections are given, the second field will *always* be `None`
|
||||
type SetupReturns = ShResult<(Vec<(String, Span)>, Option<RedirGuard>)>;
|
||||
type SetupReturns = ShResult<(Option<Vec<(String, Span)>>, Option<RedirGuard>)>;
|
||||
pub fn setup_builtin(
|
||||
argv: Vec<Tk>,
|
||||
argv: Option<Vec<Tk>>,
|
||||
job: &mut JobBldr,
|
||||
io_mode: Option<(&mut IoStack, Vec<Redir>)>,
|
||||
) -> SetupReturns {
|
||||
let mut argv: Vec<(String, Span)> = prepare_argv(argv)?;
|
||||
let mut argv = argv.map(|argv| prepare_argv(argv)).transpose()?;
|
||||
|
||||
let child_pgid = if let Some(pgid) = job.pgid() {
|
||||
pgid
|
||||
@@ -81,18 +81,22 @@ pub fn setup_builtin(
|
||||
job.set_pgid(Pid::this());
|
||||
Pid::this()
|
||||
};
|
||||
let cmd_name = argv.remove(0).0;
|
||||
let cmd_name = argv
|
||||
.as_mut()
|
||||
.and_then(|argv| {
|
||||
if argv.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(argv.remove(0).0)
|
||||
}
|
||||
}).unwrap_or_else(|| String::new());
|
||||
let child = ChildProc::new(Pid::this(), Some(&cmd_name), Some(child_pgid))?;
|
||||
job.push_child(child);
|
||||
|
||||
let guard = if let Some((io_stack, redirs)) = io_mode {
|
||||
io_stack.append_to_frame(redirs);
|
||||
let io_frame = io_stack.pop_frame();
|
||||
let guard = io_frame.redirect()?;
|
||||
Some(guard)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let guard = io_mode.map(|(io,rdrs)| {
|
||||
io.append_to_frame(rdrs);
|
||||
io.pop_frame().redirect()
|
||||
}).transpose()?;
|
||||
|
||||
// We return the io_frame because the caller needs to also call
|
||||
// io_frame.restore()
|
||||
|
||||
@@ -18,7 +18,7 @@ pub fn pwd(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResult<()
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
let (_, _guard) = setup_builtin(argv, job, Some((io_stack, node.redirs)))?;
|
||||
let (_, _guard) = setup_builtin(Some(argv), job, Some((io_stack, node.redirs)))?;
|
||||
|
||||
let stdout = borrow_fd(STDOUT_FILENO);
|
||||
|
||||
|
||||
@@ -75,7 +75,8 @@ pub fn read_builtin(node: Node, _io_stack: &mut IoStack, job: &mut JobBldr) -> S
|
||||
|
||||
let (argv, opts) = get_opts_from_tokens(argv, &READ_OPTS)?;
|
||||
let read_opts = get_read_flags(opts).blame(blame.clone())?;
|
||||
let (argv, _) = setup_builtin(argv, job, None).blame(blame.clone())?;
|
||||
let (argv, _) = setup_builtin(Some(argv), job, None).blame(blame.clone())?;
|
||||
let argv = argv.unwrap();
|
||||
|
||||
if let Some(prompt) = read_opts.prompt {
|
||||
write(borrow_fd(STDOUT_FILENO), prompt.as_bytes())?;
|
||||
|
||||
@@ -16,7 +16,8 @@ pub fn shift(node: Node, job: &mut JobBldr) -> ShResult<()> {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
let (argv, _) = setup_builtin(argv, job, None)?;
|
||||
let (argv, _) = setup_builtin(Some(argv), job, None)?;
|
||||
let argv = argv.unwrap();
|
||||
let mut argv = argv.into_iter();
|
||||
|
||||
if let Some((arg, span)) = argv.next() {
|
||||
|
||||
@@ -18,7 +18,8 @@ pub fn shopt(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResult<
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
let (argv, _guard) = setup_builtin(argv, job, Some((io_stack, node.redirs)))?;
|
||||
let (argv, _guard) = setup_builtin(Some(argv), job, Some((io_stack, node.redirs)))?;
|
||||
let argv = argv.unwrap();
|
||||
|
||||
if argv.is_empty() {
|
||||
let mut output = write_shopts(|s| s.display_opts())?;
|
||||
|
||||
@@ -17,7 +17,8 @@ pub fn source(node: Node, job: &mut JobBldr) -> ShResult<()> {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
let (argv, _) = setup_builtin(argv, job, None)?;
|
||||
let (argv, _) = setup_builtin(Some(argv), job, None)?;
|
||||
let argv = argv.unwrap();
|
||||
|
||||
for (arg, span) in argv {
|
||||
let path = PathBuf::from(arg);
|
||||
|
||||
@@ -123,7 +123,8 @@ pub fn trap(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResult<(
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
let (argv, _guard) = setup_builtin(argv, job, Some((io_stack, node.redirs)))?;
|
||||
let (argv, _guard) = setup_builtin(Some(argv), job, Some((io_stack, node.redirs)))?;
|
||||
let argv = argv.unwrap();
|
||||
|
||||
if argv.is_empty() {
|
||||
let stdout = borrow_fd(STDOUT_FILENO);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::{
|
||||
jobs::JobBldr,
|
||||
libsh::error::{ShErr, ShErrKind, ShResult},
|
||||
parse::{NdRule, Node},
|
||||
parse::{NdRule, Node, lex::split_tk_at},
|
||||
prelude::*,
|
||||
procio::{IoStack, borrow_fd},
|
||||
state::{self, VarFlags, VarKind, read_vars, write_vars},
|
||||
@@ -18,7 +18,10 @@ pub fn readonly(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResu
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
let (argv, _guard) = setup_builtin(argv, job, Some((io_stack, node.redirs)))?;
|
||||
let (_, _guard) = setup_builtin(None, job, Some((io_stack, node.redirs)))?;
|
||||
|
||||
// Remove "readonly" from argv
|
||||
let argv = if !argv.is_empty() { &argv[1..] } else { &argv[..] };
|
||||
|
||||
if argv.is_empty() {
|
||||
// Display the local variables
|
||||
@@ -38,10 +41,17 @@ pub fn readonly(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResu
|
||||
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, VarKind::Str(val.to_string()), VarFlags::READONLY))?;
|
||||
for tk in argv {
|
||||
if let Some((var_tk, val_tk)) = split_tk_at(tk, "=") {
|
||||
let var = var_tk.expand()?.get_words().join(" ");
|
||||
let val = if val_tk.as_str().starts_with('(') && val_tk.as_str().ends_with(')') {
|
||||
VarKind::arr_from_tk(val_tk.clone())?
|
||||
} else {
|
||||
VarKind::Str(val_tk.expand()?.get_words().join(" "))
|
||||
};
|
||||
write_vars(|v| v.set_var(&var, val, VarFlags::READONLY))?;
|
||||
} else {
|
||||
let arg = tk.clone().expand()?.get_words().join(" ");
|
||||
write_vars(|v| v.set_var(&arg, VarKind::Str(String::new()), VarFlags::READONLY))?;
|
||||
}
|
||||
}
|
||||
@@ -61,7 +71,8 @@ pub fn unset(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResult<
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
let (argv, _guard) = setup_builtin(argv, job, Some((io_stack, node.redirs)))?;
|
||||
let (argv, _guard) = setup_builtin(Some(argv), job, Some((io_stack, node.redirs)))?;
|
||||
let argv = argv.unwrap();
|
||||
|
||||
if argv.is_empty() {
|
||||
return Err(ShErr::full(
|
||||
@@ -95,7 +106,10 @@ pub fn export(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResult
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
let (argv, _guard) = setup_builtin(argv, job, Some((io_stack, node.redirs)))?;
|
||||
let (_, _guard) = setup_builtin(None, job, Some((io_stack, node.redirs)))?;
|
||||
|
||||
// Remove "export" from argv
|
||||
let argv = if !argv.is_empty() { &argv[1..] } else { &argv[..] };
|
||||
|
||||
if argv.is_empty() {
|
||||
// Display the environment variables
|
||||
@@ -109,12 +123,18 @@ pub fn export(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResult
|
||||
let stdout = borrow_fd(STDOUT_FILENO);
|
||||
write(stdout, env_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, VarKind::Str(val.to_string()), VarFlags::EXPORT))?;
|
||||
for tk in argv {
|
||||
if let Some((var_tk, val_tk)) = split_tk_at(tk, "=") {
|
||||
let var = var_tk.expand()?.get_words().join(" ");
|
||||
let val = if val_tk.as_str().starts_with('(') && val_tk.as_str().ends_with(')') {
|
||||
VarKind::arr_from_tk(val_tk.clone())?
|
||||
} else {
|
||||
VarKind::Str(val_tk.expand()?.get_words().join(" "))
|
||||
};
|
||||
write_vars(|v| v.set_var(&var, val, VarFlags::EXPORT))?;
|
||||
} else {
|
||||
write_vars(|v| v.export_var(&arg)); // Export an existing variable, if
|
||||
// any
|
||||
let arg = tk.clone().expand()?.get_words().join(" ");
|
||||
write_vars(|v| v.export_var(&arg)); // Export an existing variable, if any
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -131,7 +151,10 @@ pub fn local(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResult<
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
let (argv, _guard) = setup_builtin(argv, job, Some((io_stack, node.redirs)))?;
|
||||
let (_, _guard) = setup_builtin(None, job, Some((io_stack, node.redirs)))?;
|
||||
|
||||
// Remove "local" from argv
|
||||
let argv = if !argv.is_empty() { &argv[1..] } else { &argv[..] };
|
||||
|
||||
if argv.is_empty() {
|
||||
// Display the local variables
|
||||
@@ -150,10 +173,17 @@ pub fn local(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResult<
|
||||
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, VarKind::Str(val.to_string()), VarFlags::LOCAL))?;
|
||||
for tk in argv {
|
||||
if let Some((var_tk, val_tk)) = split_tk_at(tk, "=") {
|
||||
let var = var_tk.expand()?.get_words().join(" ");
|
||||
let val = if val_tk.as_str().starts_with('(') && val_tk.as_str().ends_with(')') {
|
||||
VarKind::arr_from_tk(val_tk.clone())?
|
||||
} else {
|
||||
VarKind::Str(val_tk.expand()?.get_words().join(" "))
|
||||
};
|
||||
write_vars(|v| v.set_var(&var, val, VarFlags::LOCAL))?;
|
||||
} else {
|
||||
let arg = tk.clone().expand()?.get_words().join(" ");
|
||||
write_vars(|v| v.set_var(&arg, VarKind::Str(String::new()), VarFlags::LOCAL))?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,7 +106,8 @@ pub fn zoltraak(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResu
|
||||
}
|
||||
}
|
||||
|
||||
let (argv, _guard) = setup_builtin(argv, job, Some((io_stack, node.redirs)))?;
|
||||
let (argv, _guard) = setup_builtin(Some(argv), job, Some((io_stack, node.redirs)))?;
|
||||
let argv = argv.unwrap();
|
||||
|
||||
for (arg, span) in argv {
|
||||
if &arg == "/" && !flags.contains(ZoltFlags::NO_PRESERVE_ROOT) {
|
||||
|
||||
Reference in New Issue
Block a user