summaryrefslogtreecommitdiffstats
path: root/src/parse
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2022-01-11 01:16:17 -0500
committerJesse Luehrs <doy@tozt.net>2022-01-11 01:16:17 -0500
commit6f2f4e779671bbd26b2afc203210295e32f97f86 (patch)
tree7a88d2610068f520bc6dea1b4478aff597b9ccb0 /src/parse
parent798b412537fd2e93e86c2c305be9405b79d2dcb4 (diff)
downloadnbsh-6f2f4e779671bbd26b2afc203210295e32f97f86.tar.gz
nbsh-6f2f4e779671bbd26b2afc203210295e32f97f86.zip
move home directory expansion from cd to eval
Diffstat (limited to 'src/parse')
-rw-r--r--src/parse/ast.rs49
1 files changed, 49 insertions, 0 deletions
diff --git a/src/parse/ast.rs b/src/parse/ast.rs
index 6a3ab8e..207a786 100644
--- a/src/parse/ast.rs
+++ b/src/parse/ast.rs
@@ -227,6 +227,9 @@ impl Word {
let mut s = String::new();
let mut pat = String::new();
let mut is_glob = false;
+ let initial_bareword = word
+ .get(0)
+ .map_or(false, |part| matches!(part, WordPart::Bareword(_)));
for part in word {
match part {
WordPart::Alternation(_) => unreachable!(),
@@ -247,6 +250,10 @@ impl Word {
}
}
}
+ if initial_bareword {
+ s = expand_home(&s)?;
+ pat = expand_home(&pat)?;
+ }
if is_glob {
let mut found = false;
for file in glob::glob_with(&pat, opts)? {
@@ -464,6 +471,48 @@ fn parse_fd(s: &str) -> std::os::unix::io::RawFd {
}
}
+fn expand_home(dir: &str) -> anyhow::Result<String> {
+ if dir.starts_with('~') {
+ let path: std::path::PathBuf = dir.into();
+ if let std::path::Component::Normal(prefix) =
+ path.components().next().unwrap()
+ {
+ let prefix_bytes = prefix.as_bytes();
+ let name = if prefix_bytes == b"~" {
+ None
+ } else {
+ Some(std::ffi::OsStr::from_bytes(&prefix_bytes[1..]))
+ };
+ if let Some(home) = home(name) {
+ Ok(home
+ .join(path.strip_prefix(prefix).unwrap())
+ .to_str()
+ .unwrap()
+ .to_string())
+ } else {
+ anyhow::bail!(
+ "no such user: {}",
+ name.map(std::ffi::OsStr::to_string_lossy)
+ .as_ref()
+ .unwrap_or(&std::borrow::Cow::Borrowed("(deleted)"))
+ );
+ }
+ } else {
+ unreachable!()
+ }
+ } else {
+ Ok(dir.to_string())
+ }
+}
+
+fn home(user: Option<&std::ffi::OsStr>) -> Option<std::path::PathBuf> {
+ let user = user.map_or_else(
+ || users::get_user_by_uid(users::get_current_uid()),
+ users::get_user_by_name,
+ );
+ user.map(|user| user.home_dir().to_path_buf())
+}
+
#[cfg(test)]
#[path = "test_ast.rs"]
mod test;