diff options
author | Jesse Luehrs <doy@tozt.net> | 2022-01-11 01:16:17 -0500 |
---|---|---|
committer | Jesse Luehrs <doy@tozt.net> | 2022-01-11 01:16:17 -0500 |
commit | 6f2f4e779671bbd26b2afc203210295e32f97f86 (patch) | |
tree | 7a88d2610068f520bc6dea1b4478aff597b9ccb0 /src/parse | |
parent | 798b412537fd2e93e86c2c305be9405b79d2dcb4 (diff) | |
download | nbsh-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.rs | 49 |
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; |