implemented builtins: readonly, unset, true, false, and : (no-op)
This commit is contained in:
@@ -1,87 +0,0 @@
|
|||||||
use crate::{
|
|
||||||
jobs::JobBldr,
|
|
||||||
libsh::error::ShResult,
|
|
||||||
parse::{NdRule, Node},
|
|
||||||
prelude::*,
|
|
||||||
procio::{borrow_fd, IoStack},
|
|
||||||
state::{self, read_vars, write_vars, VarFlags},
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::setup_builtin;
|
|
||||||
|
|
||||||
pub fn export(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 environment variables
|
|
||||||
let mut env_output = env::vars()
|
|
||||||
.map(|var| format!("{}={}", var.0, var.1)) // Get all of them, zip them into one string
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
env_output.sort(); // Sort them alphabetically
|
|
||||||
let mut env_output = env_output.join("\n"); // Join them with newlines
|
|
||||||
env_output.push('\n'); // Push a final newline
|
|
||||||
|
|
||||||
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, val, VarFlags::EXPORT)); // Export an assignment like
|
|
||||||
// 'foo=bar'
|
|
||||||
} else {
|
|
||||||
write_vars(|v| v.export_var(&arg)); // Export an existing variable, if
|
|
||||||
// any
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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(())
|
|
||||||
}
|
|
||||||
@@ -8,13 +8,13 @@ use crate::{
|
|||||||
execute::prepare_argv,
|
execute::prepare_argv,
|
||||||
lex::{Span, Tk},
|
lex::{Span, Tk},
|
||||||
},
|
},
|
||||||
procio::{IoFrame, IoStack, RedirGuard},
|
procio::{IoFrame, IoStack, RedirGuard}, state,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod alias;
|
pub mod alias;
|
||||||
pub mod cd;
|
pub mod cd;
|
||||||
pub mod echo;
|
pub mod echo;
|
||||||
pub mod export;
|
pub mod varcmds;
|
||||||
pub mod flowctl;
|
pub mod flowctl;
|
||||||
pub mod jobctl;
|
pub mod jobctl;
|
||||||
pub mod pwd;
|
pub mod pwd;
|
||||||
@@ -29,10 +29,10 @@ pub mod dirstack;
|
|||||||
pub mod exec;
|
pub mod exec;
|
||||||
pub mod eval;
|
pub mod eval;
|
||||||
|
|
||||||
pub const BUILTINS: [&str; 28] = [
|
pub const BUILTINS: [&str; 33] = [
|
||||||
"echo", "cd", "read", "export", "local", "pwd", "source", "shift", "jobs", "fg", "bg", "disown", "alias", "unalias",
|
"echo", "cd", "read", "export", "local", "pwd", "source", "shift", "jobs", "fg", "bg", "disown", "alias", "unalias",
|
||||||
"return", "break", "continue", "exit", "zoltraak", "shopt", "builtin", "command", "trap",
|
"return", "break", "continue", "exit", "zoltraak", "shopt", "builtin", "command", "trap",
|
||||||
"pushd", "popd", "dirs", "exec", "eval"
|
"pushd", "popd", "dirs", "exec", "eval", "true", "false", ":", "readonly", "unset"
|
||||||
];
|
];
|
||||||
|
|
||||||
/// Sets up a builtin command
|
/// Sets up a builtin command
|
||||||
@@ -93,3 +93,18 @@ pub fn setup_builtin(
|
|||||||
// io_frame.restore()
|
// io_frame.restore()
|
||||||
Ok((argv, guard))
|
Ok((argv, guard))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn true_builtin() -> ShResult<()> {
|
||||||
|
state::set_status(0);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn false_builtin() -> ShResult<()> {
|
||||||
|
state::set_status(1);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn noop_builtin() -> ShResult<()> {
|
||||||
|
state::set_status(0);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|||||||
163
src/builtin/varcmds.rs
Normal file
163
src/builtin/varcmds.rs
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
use crate::{
|
||||||
|
jobs::JobBldr,
|
||||||
|
libsh::error::{ShErr, ShErrKind, ShResult},
|
||||||
|
parse::{NdRule, Node},
|
||||||
|
prelude::*,
|
||||||
|
procio::{IoStack, borrow_fd},
|
||||||
|
state::{self, VarFlags, read_vars, write_vars},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::setup_builtin;
|
||||||
|
|
||||||
|
pub fn readonly(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()
|
||||||
|
.filter(|(_, v)| v.flags().contains(VarFlags::READONLY))
|
||||||
|
.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::READONLY))?;
|
||||||
|
} else {
|
||||||
|
write_vars(|v| v.set_var(&arg, "", VarFlags::READONLY))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
state::set_status(0);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unset(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)))?;
|
||||||
|
|
||||||
|
if argv.is_empty() {
|
||||||
|
return Err(ShErr::full(
|
||||||
|
ShErrKind::SyntaxErr,
|
||||||
|
"unset: Expected at least one argument",
|
||||||
|
blame
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (arg,span) in argv {
|
||||||
|
if !read_vars(|v| v.var_exists(&arg)) {
|
||||||
|
return Err(ShErr::full(
|
||||||
|
ShErrKind::ExecFail,
|
||||||
|
format!("unset: No such variable '{arg}'"),
|
||||||
|
span
|
||||||
|
));
|
||||||
|
}
|
||||||
|
write_vars(|v| v.unset_var(&arg))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
state::set_status(0);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn export(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 environment variables
|
||||||
|
let mut env_output = env::vars()
|
||||||
|
.map(|var| format!("{}={}", var.0, var.1)) // Get all of them, zip them into one string
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
env_output.sort(); // Sort them alphabetically
|
||||||
|
let mut env_output = env_output.join("\n"); // Join them with newlines
|
||||||
|
env_output.push('\n'); // Push a final newline
|
||||||
|
|
||||||
|
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, val, VarFlags::EXPORT))?;
|
||||||
|
} else {
|
||||||
|
write_vars(|v| v.export_var(&arg)); // Export an existing variable, if
|
||||||
|
// any
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state::set_status(0);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@ use std::{collections::{HashSet, VecDeque}, os::unix::fs::PermissionsExt};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
builtin::{
|
builtin::{
|
||||||
alias::{alias, unalias}, cd::cd, dirstack::{dirs, popd, pushd}, echo::echo, eval, exec, export::{export, local}, flowctl::flowctl, jobctl::{JobBehavior, continue_job, disown, jobs}, pwd::pwd, read::read_builtin, shift::shift, shopt::shopt, source::source, test::double_bracket_test, trap::{TrapTarget, trap}, zoltraak::zoltraak
|
alias::{alias, unalias}, cd::cd, dirstack::{dirs, popd, pushd}, echo::echo, eval, exec, flowctl::flowctl, jobctl::{JobBehavior, continue_job, disown, jobs}, pwd::pwd, read::read_builtin, shift::shift, shopt::shopt, source::source, test::double_bracket_test, trap::{TrapTarget, trap}, true_builtin, varcmds::{export, local, readonly, unset}, zoltraak::zoltraak
|
||||||
},
|
},
|
||||||
expand::{expand_aliases, glob_to_regex},
|
expand::{expand_aliases, glob_to_regex},
|
||||||
jobs::{ChildProc, JobStack, dispatch_job},
|
jobs::{ChildProc, JobStack, dispatch_job},
|
||||||
@@ -90,7 +90,7 @@ impl Drop for VarCtxGuard {
|
|||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
write_vars(|v| {
|
write_vars(|v| {
|
||||||
for var in &self.vars {
|
for var in &self.vars {
|
||||||
v.unset_var(var);
|
v.unset_var(var).ok();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -569,7 +569,7 @@ impl Dispatcher {
|
|||||||
.zip(chunk.iter().chain(std::iter::repeat(&empty)));
|
.zip(chunk.iter().chain(std::iter::repeat(&empty)));
|
||||||
|
|
||||||
for (var, val) in chunk_iter {
|
for (var, val) in chunk_iter {
|
||||||
write_vars(|v| v.set_var(&var.to_string(), &val.to_string(), VarFlags::NONE));
|
write_vars(|v| v.set_var(&var.to_string(), &val.to_string(), VarFlags::NONE))?;
|
||||||
for_guard.vars.insert(var.to_string());
|
for_guard.vars.insert(var.to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -769,6 +769,16 @@ impl Dispatcher {
|
|||||||
"dirs" => dirs(cmd, io_stack_mut, curr_job_mut),
|
"dirs" => dirs(cmd, io_stack_mut, curr_job_mut),
|
||||||
"exec" => exec::exec_builtin(cmd, io_stack_mut, curr_job_mut),
|
"exec" => exec::exec_builtin(cmd, io_stack_mut, curr_job_mut),
|
||||||
"eval" => eval::eval(cmd, io_stack_mut, curr_job_mut),
|
"eval" => eval::eval(cmd, io_stack_mut, curr_job_mut),
|
||||||
|
"readonly" => readonly(cmd, io_stack_mut, curr_job_mut),
|
||||||
|
"unset" => unset(cmd, io_stack_mut, curr_job_mut),
|
||||||
|
"true" | ":" => {
|
||||||
|
state::set_status(0);
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
"false" => {
|
||||||
|
state::set_status(1);
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
_ => unimplemented!(
|
_ => unimplemented!(
|
||||||
"Have not yet added support for builtin '{}'",
|
"Have not yet added support for builtin '{}'",
|
||||||
cmd_raw
|
cmd_raw
|
||||||
@@ -885,7 +895,7 @@ impl Dispatcher {
|
|||||||
let var = var.span.as_str();
|
let var = var.span.as_str();
|
||||||
let val = val.expand()?.get_words().join(" ");
|
let val = val.expand()?.get_words().join(" ");
|
||||||
match kind {
|
match kind {
|
||||||
AssignKind::Eq => write_vars(|v| v.set_var(var, &val, VarFlags::EXPORT)),
|
AssignKind::Eq => write_vars(|v| v.set_var(var, &val, VarFlags::EXPORT))?,
|
||||||
AssignKind::PlusEq => todo!(),
|
AssignKind::PlusEq => todo!(),
|
||||||
AssignKind::MinusEq => todo!(),
|
AssignKind::MinusEq => todo!(),
|
||||||
AssignKind::MultEq => todo!(),
|
AssignKind::MultEq => todo!(),
|
||||||
@@ -902,7 +912,7 @@ impl Dispatcher {
|
|||||||
let var = var.span.as_str();
|
let var = var.span.as_str();
|
||||||
let val = val.expand()?.get_words().join(" ");
|
let val = val.expand()?.get_words().join(" ");
|
||||||
match kind {
|
match kind {
|
||||||
AssignKind::Eq => write_vars(|v| v.set_var(var, &val, VarFlags::NONE)),
|
AssignKind::Eq => write_vars(|v| v.set_var(var, &val, VarFlags::NONE))?,
|
||||||
AssignKind::PlusEq => todo!(),
|
AssignKind::PlusEq => todo!(),
|
||||||
AssignKind::MinusEq => todo!(),
|
AssignKind::MinusEq => todo!(),
|
||||||
AssignKind::MultEq => todo!(),
|
AssignKind::MultEq => todo!(),
|
||||||
|
|||||||
49
src/state.rs
49
src/state.rs
@@ -151,13 +151,16 @@ impl ScopeStack {
|
|||||||
pub fn cur_scope_mut(&mut self) -> &mut VarTab {
|
pub fn cur_scope_mut(&mut self) -> &mut VarTab {
|
||||||
self.scopes.last_mut().unwrap()
|
self.scopes.last_mut().unwrap()
|
||||||
}
|
}
|
||||||
pub fn unset_var(&mut self, var_name: &str) {
|
pub fn unset_var(&mut self, var_name: &str) -> ShResult<()> {
|
||||||
for scope in self.scopes.iter_mut().rev() {
|
for scope in self.scopes.iter_mut().rev() {
|
||||||
if scope.var_exists(var_name) {
|
if scope.var_exists(var_name) {
|
||||||
scope.unset_var(var_name);
|
return scope.unset_var(var_name);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Err(ShErr::simple(
|
||||||
|
ShErrKind::ExecFail,
|
||||||
|
format!("Variable '{}' not found", var_name)
|
||||||
|
))
|
||||||
}
|
}
|
||||||
pub fn export_var(&mut self, var_name: &str) {
|
pub fn export_var(&mut self, var_name: &str) {
|
||||||
for scope in self.scopes.iter_mut().rev() {
|
for scope in self.scopes.iter_mut().rev() {
|
||||||
@@ -187,22 +190,26 @@ impl ScopeStack {
|
|||||||
}
|
}
|
||||||
flat_vars
|
flat_vars
|
||||||
}
|
}
|
||||||
pub fn set_var(&mut self, var_name: &str, val: &str, flags: VarFlags) {
|
pub fn set_var(&mut self, var_name: &str, val: &str, flags: VarFlags) -> ShResult<()> {
|
||||||
let is_local = self.is_local_var(var_name);
|
let is_local = self.is_local_var(var_name);
|
||||||
if flags.contains(VarFlags::LOCAL) || is_local {
|
if flags.contains(VarFlags::LOCAL) || is_local {
|
||||||
self.set_var_local(var_name, val, flags);
|
self.set_var_local(var_name, val, flags)
|
||||||
} else {
|
} else {
|
||||||
self.set_var_global(var_name, val, flags);
|
self.set_var_global(var_name, val, flags)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn set_var_global(&mut self, var_name: &str, val: &str, flags: VarFlags) {
|
fn set_var_global(&mut self, var_name: &str, val: &str, flags: VarFlags) -> ShResult<()> {
|
||||||
if let Some(scope) = self.scopes.first_mut() {
|
if let Some(scope) = self.scopes.first_mut() {
|
||||||
scope.set_var(var_name, val, flags);
|
scope.set_var(var_name, val, flags)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn set_var_local(&mut self, var_name: &str, val: &str, flags: VarFlags) {
|
fn set_var_local(&mut self, var_name: &str, val: &str, flags: VarFlags) -> ShResult<()> {
|
||||||
if let Some(scope) = self.scopes.last_mut() {
|
if let Some(scope) = self.scopes.last_mut() {
|
||||||
scope.set_var(var_name, val, flags);
|
scope.set_var(var_name, val, flags)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn get_var(&self, var_name: &str) -> String {
|
pub fn get_var(&self, var_name: &str) -> String {
|
||||||
@@ -477,6 +484,9 @@ impl Var {
|
|||||||
pub fn mark_for_export(&mut self) {
|
pub fn mark_for_export(&mut self) {
|
||||||
self.flags.set(VarFlags::EXPORT, true);
|
self.flags.set(VarFlags::EXPORT, true);
|
||||||
}
|
}
|
||||||
|
pub fn flags(&self) -> VarFlags {
|
||||||
|
self.flags
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Var {
|
impl Display for Var {
|
||||||
@@ -654,13 +664,27 @@ impl VarTab {
|
|||||||
pub fn get_var_flags(&self, var_name: &str) -> Option<VarFlags> {
|
pub fn get_var_flags(&self, var_name: &str) -> Option<VarFlags> {
|
||||||
self.vars.get(var_name).map(|var| var.flags)
|
self.vars.get(var_name).map(|var| var.flags)
|
||||||
}
|
}
|
||||||
pub fn unset_var(&mut self, var_name: &str) {
|
pub fn unset_var(&mut self, var_name: &str) -> ShResult<()> {
|
||||||
|
if let Some(var) = self.vars.get(var_name) && var.flags.contains(VarFlags::READONLY) {
|
||||||
|
return Err(ShErr::simple(
|
||||||
|
ShErrKind::ExecFail,
|
||||||
|
format!("cannot unset readonly variable '{}'", var_name)
|
||||||
|
));
|
||||||
|
}
|
||||||
self.vars.remove(var_name);
|
self.vars.remove(var_name);
|
||||||
unsafe { env::remove_var(var_name) };
|
unsafe { env::remove_var(var_name) };
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
pub fn set_var(&mut self, var_name: &str, val: &str, flags: VarFlags) {
|
pub fn set_var(&mut self, var_name: &str, val: &str, flags: VarFlags) -> ShResult<()> {
|
||||||
if let Some(var) = self.vars.get_mut(var_name) {
|
if let Some(var) = self.vars.get_mut(var_name) {
|
||||||
|
if var.flags.contains(VarFlags::READONLY) && !flags.contains(VarFlags::READONLY) {
|
||||||
|
return Err(ShErr::simple(
|
||||||
|
ShErrKind::ExecFail,
|
||||||
|
format!("Variable '{}' is readonly", var_name)
|
||||||
|
));
|
||||||
|
}
|
||||||
var.kind = VarKind::Str(val.to_string());
|
var.kind = VarKind::Str(val.to_string());
|
||||||
|
var.flags |= flags;
|
||||||
if var.flags.contains(VarFlags::EXPORT) || flags.contains(VarFlags::EXPORT) {
|
if var.flags.contains(VarFlags::EXPORT) || flags.contains(VarFlags::EXPORT) {
|
||||||
if flags.contains(VarFlags::EXPORT) && !var.flags.contains(VarFlags::EXPORT) {
|
if flags.contains(VarFlags::EXPORT) && !var.flags.contains(VarFlags::EXPORT) {
|
||||||
var.mark_for_export();
|
var.mark_for_export();
|
||||||
@@ -675,6 +699,7 @@ impl VarTab {
|
|||||||
}
|
}
|
||||||
self.vars.insert(var_name.to_string(), var);
|
self.vars.insert(var_name.to_string(), var);
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
pub fn var_exists(&self, var_name: &str) -> bool {
|
pub fn var_exists(&self, var_name: &str) -> bool {
|
||||||
if let Ok(param) = var_name.parse::<ShellParam>() {
|
if let Ok(param) = var_name.parse::<ShellParam>() {
|
||||||
|
|||||||
Reference in New Issue
Block a user