diff --git a/src/builtin/export.rs b/src/builtin/export.rs index 6804996..6e1c4cd 100644 --- a/src/builtin/export.rs +++ b/src/builtin/export.rs @@ -1,4 +1,4 @@ -use crate::{jobs::JobBldr, libsh::error::{Note, ShErr, ShErrKind, ShResult}, parse::{NdRule, Node}, prelude::*, procio::{borrow_fd, IoStack}, state}; +use crate::{jobs::JobBldr, libsh::error::ShResult, parse::{NdRule, Node}, prelude::*, procio::{borrow_fd, IoStack}, state::{self, write_vars}}; use super::setup_builtin; @@ -21,20 +21,12 @@ 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,span) in argv { - let Some((var,val)) = arg.split_once('=') else { - return Err( - ShErr::full( - ShErrKind::SyntaxErr, - "export: Expected an assignment in export args", - span.into() - ) - .with_note( - Note::new("Arguments for export should be formatted like 'foo=bar'") - ) - ) - }; - env::set_var(var, val); + for (arg,_) in argv { + if let Some((var,val)) = arg.split_once('=') { + write_vars(|v| v.set_var(var, val, true)); // Export an assignment like 'foo=bar' + } else { + write_vars(|v| v.export_var(&arg)); // Export an existing variable, if any + } } } io_frame.unwrap().restore()?; diff --git a/src/parse/execute.rs b/src/parse/execute.rs index 025472c..4757042 100644 --- a/src/parse/execute.rs +++ b/src/parse/execute.rs @@ -447,7 +447,7 @@ impl Dispatcher { 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::Eq => write_vars(|v| v.set_var(var, val, false)), AssignKind::PlusEq => todo!(), AssignKind::MinusEq => todo!(), AssignKind::MultEq => todo!(), diff --git a/src/state.rs b/src/state.rs index 4ce8b4d..e85de88 100644 --- a/src/state.rs +++ b/src/state.rs @@ -87,9 +87,31 @@ impl LogTab { } } -#[derive(Clone)] +#[derive(Clone,Debug)] +pub struct Var { + export: bool, + value: String +} + +impl Var { + pub fn new(value: String) -> Self { + Self { export: false, value } + } + pub fn mark_for_export(&mut self) { + self.export = true; + } +} + +impl Deref for Var { + type Target = String; + fn deref(&self) -> &Self::Target { + &self.value + } +} + +#[derive(Clone,Debug)] pub struct VarTab { - vars: HashMap, + vars: HashMap, params: HashMap, sh_argv: VecDeque, // Using a VecDeque makes the implementation of `shift` straightforward } @@ -201,10 +223,10 @@ impl VarTab { self.update_arg_params(); arg } - pub fn vars(&self) -> &HashMap { + pub fn vars(&self) -> &HashMap { &self.vars } - pub fn vars_mut(&mut self) -> &mut HashMap { + pub fn vars_mut(&mut self) -> &mut HashMap { &mut self.vars } pub fn params(&self) -> &HashMap { @@ -213,6 +235,12 @@ impl VarTab { pub fn params_mut(&mut self) -> &mut HashMap { &mut self.params } + pub fn export_var(&mut self, var_name: &str) { + if let Some(var) = self.vars.get_mut(var_name) { + var.mark_for_export(); + env::set_var(var_name, &var.value); + } + } pub fn get_var(&self, var: &str) -> String { if var.chars().count() == 1 { let param = self.get_param(get_char(var, 0).unwrap()); @@ -226,8 +254,20 @@ impl VarTab { std::env::var(var).unwrap_or_default() } } - pub fn new_var(&mut self, var: &str, val: &str) { - self.vars.insert(var.to_string(), val.to_string()); + pub fn set_var(&mut self, var_name: &str, val: &str, export: bool) { + if let Some(var) = self.vars.get_mut(var_name) { + var.value = val.to_string(); + if var.export { + env::set_var(var_name, val); + } + } else { + let mut var = Var::new(val.to_string()); + if export { + var.mark_for_export(); + env::set_var(var_name, &*var); + } + self.vars.insert(var_name.to_string(), var); + } } pub fn set_param(&mut self, param: char, val: &str) { self.params.insert(param,val.to_string()); diff --git a/src/tests/expand.rs b/src/tests/expand.rs index b60b24d..7b81571 100644 --- a/src/tests/expand.rs +++ b/src/tests/expand.rs @@ -5,7 +5,7 @@ use super::*; #[test] fn simple_expansion() { let varsub = "$foo"; - write_vars(|v| v.new_var("foo", "this is the value of the variable".into())); + write_vars(|v| v.set_var("foo", "this is the value of the variable".into())); let mut tokens: Vec = LexStream::new(Arc::new(varsub.to_string()), LexFlags::empty()) .map(|tk| tk.unwrap())