aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJonas Maier <jonas@x77.dev>2026-05-12 21:27:52 +0200
committerJonas Maier <jonas@x77.dev>2026-05-12 21:27:52 +0200
commit116b0ac3c99fd317d3800637ca3f88f4d3874c1b (patch)
tree29982dbac0ed23275c960ff1be24600e7468cbf6 /src
parent0e5d95663aaac8b7368c82290476694b561536d3 (diff)
downloadpish-116b0ac3c99fd317d3800637ca3f88f4d3874c1b.tar.gz
variable refactor: export builtin
Diffstat (limited to 'src')
-rw-r--r--src/lib.rs2
-rw-r--r--src/run/builtin.rs25
-rw-r--r--src/run/mod.rs8
-rw-r--r--src/run/var.rs52
4 files changed, 79 insertions, 8 deletions
diff --git a/src/lib.rs b/src/lib.rs
index ef62c44..014c518 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -180,7 +180,7 @@ fn pretty_cwd() -> BString {
impl Session {
fn load_unevaled_prompt(&self) -> BString {
match self.vars.lookup(&b"PROMPT"[..]) {
- Some(prompt) => prompt.clone(),
+ Some(prompt) => prompt.into_owned(),
None => {
let mut prompt = BString::new();
if cfg!(debug_assertions) {
diff --git a/src/run/builtin.rs b/src/run/builtin.rs
index e928d8b..c8224c0 100644
--- a/src/run/builtin.rs
+++ b/src/run/builtin.rs
@@ -461,7 +461,7 @@ impl Builtin for var {
}
let se = session.lock().unwrap();
if let Some(val) = se.vars.lookup(&args[0]) {
- stdout.write_all(val.as_slice())?;
+ stdout.write_all(val.as_ref())?;
Ok(())
} else {
Err(Error::Exit(1))
@@ -965,3 +965,26 @@ impl Builtin for logo {
Ok(())
}
}
+
+#[derive(Copy, Clone)]
+pub struct export;
+
+impl Builtin for export {
+ fn name(&self) -> &str {
+ "export"
+ }
+
+ fn io(
+ &self,
+ session: Arc<Mutex<Session>>,
+ args: &[BString],
+ _stdin: &mut dyn Read,
+ _stdout: &mut dyn Write,
+ ) -> Result {
+ let mut session = session.lock().unwrap();
+ for v in args.iter() {
+ session.vars.allow_export(&v, true);
+ }
+ Ok(())
+ }
+}
diff --git a/src/run/mod.rs b/src/run/mod.rs
index 55bdd29..8be5fca 100644
--- a/src/run/mod.rs
+++ b/src/run/mod.rs
@@ -262,6 +262,11 @@ impl Executor {
crate::export_fun::prepare_command(self.se.clone(), &mut command);
+ command.env_clear();
+ for (var, val) in self.se.lock().unwrap().vars.export() {
+ command.env(OsStr::from_bytes(var), OsStr::from_bytes(val.as_ref()));
+ }
+
match command.spawn() {
Ok(c) => SpawnedCmd::Child(ChildWaiter::new(c).unwrap()),
Err(e) => SpawnedCmd::SpawnError(e),
@@ -478,7 +483,7 @@ impl parse::Expander for Executor {
}
if let Some(val) = self.se.lock().unwrap().vars.lookup(&var) {
- return Ok(val);
+ return Ok(val.into_owned());
}
if let Some(dfl) = default {
@@ -697,6 +702,7 @@ const BUILTINS: &[&'static dyn BuiltinClone] = &[
&builtin::Continue,
&builtin::Here,
&builtin::logo,
+ &builtin::export,
];
pub fn builtin_map() -> HashMap<BString, &'static dyn BuiltinClone> {
diff --git a/src/run/var.rs b/src/run/var.rs
index 0e64ad7..d3e98f4 100644
--- a/src/run/var.rs
+++ b/src/run/var.rs
@@ -1,4 +1,7 @@
+use std::borrow::Cow;
use std::collections::{HashMap, HashSet};
+use std::sync::LazyLock;
+use std::time::Instant;
use crate::BString;
use crate::bstr;
@@ -7,6 +10,7 @@ pub struct Vars {
simple: HashMap<BString, BString>,
magic: HashMap<BString, fn() -> BString>,
all: HashSet<BString>,
+ export: HashSet<BString>,
}
impl Vars {
@@ -22,7 +26,15 @@ impl Vars {
.cloned()
.chain(magic.keys().cloned())
.collect();
- Self { simple, magic, all }
+ let export = std::env::vars_os()
+ .map(|x| x.0.into_encoded_bytes())
+ .collect();
+ Self {
+ simple,
+ magic,
+ all,
+ export,
+ }
}
pub fn set(&mut self, var: BString, val: BString) {
@@ -30,18 +42,36 @@ impl Vars {
self.all.insert(var);
}
- pub fn lookup(&self, var: &bstr) -> Option<BString> {
+ pub fn lookup<'a>(&'a self, var: &bstr) -> Option<Cow<'a, bstr>> {
if let Some(fun) = self.magic.get(var) {
- return Some(fun());
+ return Some(Cow::Owned(fun()));
}
if let Some(val) = self.simple.get(var) {
- return Some(val.clone());
+ return Some(Cow::Borrowed(val));
}
None
}
+ pub fn allow_export(&mut self, var: &bstr, allow: bool) {
+ if allow {
+ self.export.insert(var.to_vec());
+ } else {
+ self.export.remove(var);
+ }
+ }
+
+ pub fn export<'a>(&'a self) -> HashMap<&'a bstr, Cow<'a, bstr>> {
+ self.export
+ .iter()
+ .filter_map(|var| {
+ let val = self.lookup(var)?;
+ Some((var.as_ref(), val))
+ })
+ .collect()
+ }
+
pub fn vars(&self) -> &HashSet<BString> {
&self.all
}
@@ -62,9 +92,21 @@ impl Default for Vars {
b"PISH_COMMIT" => crate::consts::PISH_COMMIT.as_bytes().to_vec(),
b"PISH_DIRTY" => vec![crate::consts::PISH_DIRTY as u8 + b'0'],
};
+
let magic = map! {
- b"CWD_PRETTY" => crate::pretty_cwd as _
+ b"CWD_PRETTY" => crate::pretty_cwd as _,
+ b"SECONDS" => seconds_since_startup as _,
};
+
+ // call it once such that lazylock gets initialized
+ seconds_since_startup();
+
Self::new(simple, magic)
}
}
+
+static START: LazyLock<Instant> = LazyLock::new(Instant::now);
+
+fn seconds_since_startup() -> BString {
+ format!("{}", START.elapsed().as_secs_f64() as u64).into_bytes()
+}