aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/main.rs4
-rw-r--r--src/parse/mod.rs45
-rw-r--r--src/run/builtin.rs84
-rw-r--r--src/run/mod.rs60
4 files changed, 188 insertions, 5 deletions
diff --git a/src/main.rs b/src/main.rs
index 92ce812..5e1d847 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -38,7 +38,7 @@ use crate::completion::{PathCache, completion};
use crate::ctrlc::CtrlC;
use crate::cursor::{Direction, move_cursor};
use crate::history::HistoryEntry;
-use crate::parse::Block;
+use crate::parse::{Block, ExpString};
macro_rules! print {
($($x:tt)*) => {{
@@ -83,6 +83,7 @@ pub struct Session {
builtins: HashMap<BString, &'static dyn run::Builtin>,
vars: HashMap<BString, BString>,
funs: HashMap<BString, Block>,
+ aliases: run::Aliases,
socket_running: Option<export_fun::SocketRunning>,
path_cache: PathCache,
ctrlc: CtrlC,
@@ -281,6 +282,7 @@ fn event_loop() {
socket_running: None,
vars: HashMap::new(),
funs: HashMap::new(),
+ aliases: run::Aliases::new(),
path_cache: Default::default(),
ctrlc: Default::default(),
};
diff --git a/src/parse/mod.rs b/src/parse/mod.rs
index 8682f04..184a514 100644
--- a/src/parse/mod.rs
+++ b/src/parse/mod.rs
@@ -1,4 +1,4 @@
-use crate::BString;
+use crate::{BString, bstr};
#[cfg(test)]
mod test;
@@ -30,6 +30,17 @@ pub trait Expander {
type Error;
fn expand_var(&mut self, v: BString, default: Option<BString>) -> Res<BString, Self::Error>;
fn expand_cmd(&mut self, c: Ast<PostExpansion>) -> Res<BString, Self::Error>;
+
+ type AliasAge;
+ fn expand_alias(
+ &mut self,
+ cmd: &bstr,
+ age: Option<Self::AliasAge>,
+ ) -> Res<Option<(Self::AliasAge, Vec<ExpString>)>, Self::Error> {
+ let _ = cmd;
+ let _ = age;
+ Ok(None)
+ }
}
#[derive(Debug, Clone, PartialEq)]
@@ -857,7 +868,30 @@ pub struct Command<T: Stage> {
}
impl Command<PreExpansion> {
- fn expand<E: Expander>(self, e: &mut E) -> Res<Command<PostExpansion>, E::Error> {
+ fn full_alias_expansion<E: Expander>(&mut self, e: &mut E) -> Res<(), E::Error> {
+ self.args.reverse();
+ let mut age = None;
+
+ while self.cmd.parts.len() == 1
+ && let StringPart::Boring(s) = &self.cmd.parts[0]
+ {
+ if let Some((new_age, exp)) = e.expand_alias(&s, age.take())? {
+ age = Some(new_age);
+ self.cmd = exp.first().unwrap().clone();
+ for e in exp.into_iter().skip(1).rev() {
+ self.args.push(e);
+ }
+ } else {
+ break;
+ }
+ }
+ self.args.reverse();
+
+ Ok(())
+ }
+
+ fn expand<E: Expander>(mut self, e: &mut E) -> Res<Command<PostExpansion>, E::Error> {
+ self.full_alias_expansion(e)?;
let cmd = self.cmd.expand(e)?;
let mut args = Vec::with_capacity(self.args.len());
for arg in self.args.into_iter() {
@@ -999,8 +1033,11 @@ pub fn completion_context<E: Expander>(x: &[u8], e: &mut E) -> CompletionContext
ast.completion(e)
}
-trait Parse: Sized {
+pub trait Parse: Sized {
fn parse(b: &mut Cursor<'_>) -> Result<Self>;
+ fn parse_from_bytes(x: &[u8]) -> Result<Self> {
+ Self::parse(&mut Cursor::new(x, ParseMode::Command))
+ }
}
enum ParseMode {
@@ -1008,7 +1045,7 @@ enum ParseMode {
Completion,
}
-struct Cursor<'a> {
+pub struct Cursor<'a> {
buf: &'a [u8],
mode: ParseMode,
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> {