aboutsummaryrefslogtreecommitdiffstats
path: root/src/run
diff options
context:
space:
mode:
authorJonas Maier <>2026-04-17 17:56:58 +0200
committerJonas Maier <>2026-04-17 17:56:58 +0200
commitb677713a7caf179b144674ff4c6e6b7171ad1337 (patch)
tree073c32bac54fe2b960203ddd307530a5e761ebad /src/run
parentddd2627360ee7a30b26cd3b7fab19ede55c574d7 (diff)
downloadpish-b677713a7caf179b144674ff4c6e6b7171ad1337.tar.gz
aliases
Diffstat (limited to 'src/run')
-rw-r--r--src/run/builtin.rs84
-rw-r--r--src/run/mod.rs60
2 files changed, 144 insertions, 0 deletions
diff --git a/src/run/builtin.rs b/src/run/builtin.rs
index f0cb999..47a10c2 100644
--- a/src/run/builtin.rs
+++ b/src/run/builtin.rs
@@ -447,3 +447,87 @@ impl Builtin for var {
}
}
}
+
+pub struct alias;
+impl Builtin for alias {
+ fn name(&self) -> &str {
+ "alias"
+ }
+
+ fn io(
+ &self,
+ session: Arc<Mutex<Session>>,
+ mut args: &[BString],
+ _stdin: &mut dyn Read,
+ stdout: &mut dyn Write,
+ ) -> Result {
+ let Some(alias_name) = args.first() else {
+ writeln!(stdout, "alias: expected alias name")?;
+ return Err(Error::Exit(-1));
+ };
+
+ args = &args[1..];
+ if let Some(b"=") = args.first().map(|x| x.as_slice()) {
+ args = &args[1..];
+ }
+
+ if args.is_empty() {
+ writeln!(stdout, "alias: expected alias definition")?;
+ return Err(Error::Exit(-1));
+ };
+
+ let mut parse_fail = false;
+ let mut alias_args = Vec::new();
+ for arg in args {
+ match <crate::parse::ExpString as crate::parse::Parse>::parse_from_bytes(&arg[..]) {
+ Ok(parsed) => {
+ alias_args.push(parsed);
+ }
+ Err(err) => {
+ writeln!(
+ stdout,
+ "alias: unparseable argument ({err:?}): {}",
+ arg.escape_ascii(),
+ )?;
+ parse_fail = true;
+ }
+ }
+ }
+
+ if parse_fail {
+ return Err(Error::Exit(-1));
+ }
+
+ session
+ .lock()
+ .unwrap()
+ .aliases
+ .insert(alias_name.clone(), alias_args);
+
+ Ok(())
+ }
+}
+
+pub struct unalias;
+impl Builtin for unalias {
+ fn name(&self) -> &str {
+ "unalias"
+ }
+
+ fn io(
+ &self,
+ session: Arc<Mutex<Session>>,
+ args: &[BString],
+ _stdin: &mut dyn Read,
+ stdout: &mut dyn Write,
+ ) -> Result {
+ if args.len() != 1 {
+ writeln!(stdout, "unalias: expecting exactly one argument")?;
+ return Err(Error::Exit(-1));
+ }
+
+ session.lock().unwrap().aliases.remove(&args[0]);
+
+ Ok(())
+ }
+} \ No newline at end of file
diff --git a/src/run/mod.rs b/src/run/mod.rs
index 091c662..d52c4e7 100644
--- a/src/run/mod.rs
+++ b/src/run/mod.rs
@@ -18,6 +18,7 @@ pub enum ExecError {
IO(std::io::Error),
ErrorStack(Vec<ExecError>),
Panic,
+ AliasDepthExceeded,
}
impl ExecError {
@@ -59,6 +60,7 @@ impl ExecError {
}
out
}
+ ExecError::AliasDepthExceeded => "alias depth exceeded".to_string(),
}
}
}
@@ -404,6 +406,62 @@ impl parse::Expander for Executor {
}
Ok(buf)
}
+
+ type AliasAge = AliasAge;
+
+ fn expand_alias(
+ &mut self,
+ cmd: &bstr,
+ older_than: Option<AliasAge>,
+ ) -> Result<Option<(AliasAge, Vec<ExpString>)>, Self::Error> {
+ Ok(self
+ .se
+ .lock()
+ .unwrap()
+ .aliases
+ .get(cmd, older_than.unwrap_or(AliasAge::MAX)))
+ }
+}
+
+type AliasAge = u32;
+type AliasBody = Vec<ExpString>;
+type AliasSet = Vec<(AliasAge, AliasBody)>;
+
+pub struct Aliases {
+ age: u32,
+ aliases: HashMap<BString, AliasSet>,
+}
+
+impl Aliases {
+ pub fn new() -> Self {
+ Self {
+ age: 0,
+ aliases: HashMap::new(),
+ }
+ }
+
+ fn insert(&mut self, name: BString, body: AliasBody) {
+ let age = self.age;
+ self.age += 1;
+ let new = (age, body);
+ if let Some(entry) = self.aliases.get_mut(&name) {
+ entry.push(new);
+ } else {
+ self.aliases.insert(name, vec![new]);
+ }
+ }
+
+ fn get(&mut self, name: &bstr, older_than: AliasAge) -> Option<(AliasAge, AliasBody)> {
+ let Some(alias_set) = self.aliases.get(name) else {
+ return None;
+ };
+
+ alias_set.iter().rev().find(|e| e.0 < older_than).cloned()
+ }
+
+ fn remove(&mut self, name: &bstr) {
+ self.aliases.remove(name);
+ }
}
fn exec(se: Arc<Mutex<Session>>, ast: Ast<PreExpansion>) -> Result<(), ExecError> {
@@ -479,6 +537,8 @@ const BUILTINS: &[&'static dyn Builtin] = &[
&builtin::null,
&builtin::var,
&builtin::completion,
+ &builtin::alias,
+ &builtin::unalias,
];
pub fn builtin_map() -> HashMap<BString, &'static dyn Builtin> {