use crate::ported::params::{getaparam, getsparam, setsparam};
pub fn _set_command() -> i32 {
let words = getaparam("words").unwrap_or_default();
let command = words.first().cloned().unwrap_or_default();
if command.is_empty() {
return 1;
}
let is_function = crate::ported::utils::getshfunc(&command).is_some();
let is_builtin = is_known_builtin(&command);
if is_function || is_builtin {
let _ = setsparam("_comp_command1", &command);
let _ = setsparam("_comp_command", &command);
return 0;
}
if command.starts_with('=') {
let bare = &command[1..];
let commands = getaparam("commands").unwrap_or_default();
let resolved: String = commands
.chunks(2)
.find(|kv| kv.first().map(|k| k == bare).unwrap_or(false))
.and_then(|kv| kv.get(1).cloned())
.unwrap_or_default();
let _ = setsparam("_comp_command2", &resolved);
let _ = setsparam("_comp_command1", bare);
let _ = setsparam("_comp_command", &resolved);
return 0;
}
if let Some(stripped) = command.strip_prefix("..") {
if stripped.starts_with('/') {
let pwd = getsparam("PWD").unwrap_or_default();
let _ = setsparam("_comp_command1", &format!("{}/{}", pwd, command));
let tail = basename(&command);
let _ = setsparam("_comp_command2", &tail);
let _ = setsparam("_comp_command", &tail);
return 0;
}
}
if command.contains('/') {
let _ = setsparam("_comp_command1", &command);
let tail = basename(&command);
let _ = setsparam("_comp_command2", &tail);
let _ = setsparam("_comp_command", &tail);
return 0;
}
let commands = getaparam("commands").unwrap_or_default();
let cmd2: String = commands
.chunks(2)
.find(|kv| kv.first().map(|k| k == &command).unwrap_or(false))
.and_then(|kv| kv.get(1).cloned())
.unwrap_or_default();
let _ = setsparam("_comp_command1", &command);
let _ = setsparam("_comp_command2", &cmd2);
let _ = setsparam("_comp_command", &command);
0
}
fn is_known_builtin(name: &str) -> bool {
crate::ported::builtin::createbuiltintable().contains_key(name)
}
fn basename(s: &str) -> String {
s.rsplit('/').next().unwrap_or("").to_string()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ported::params::setaparam;
#[test]
fn empty_words_returns_one() {
let _g = crate::test_util::global_state_lock();
setaparam("words", Vec::new());
assert_eq!(_set_command(), 1);
}
#[test]
fn slash_path_uses_basename() {
let _g = crate::test_util::global_state_lock();
setaparam("words", vec!["/usr/bin/ls".to_string()]);
let _ = _set_command();
assert_eq!(getsparam("_comp_command1").as_deref(), Some("/usr/bin/ls"));
assert_eq!(getsparam("_comp_command2").as_deref(), Some("ls"));
assert_eq!(getsparam("_comp_command").as_deref(), Some("ls"));
}
#[test]
fn known_builtin_takes_builtin_branch() {
let _g = crate::test_util::global_state_lock();
setaparam("words", vec!["cd".to_string()]);
let _ = _set_command();
assert_eq!(getsparam("_comp_command1").as_deref(), Some("cd"));
assert_eq!(getsparam("_comp_command").as_deref(), Some("cd"));
}
#[test]
fn known_builtins_include_navigation_and_io() {
for name in ["cd", "echo", "pwd", "printf", "read"] {
assert!(
is_known_builtin(name),
"expected {} to register as builtin",
name
);
}
}
#[test]
fn known_builtins_include_set_and_unset_family() {
for name in ["set", "unset", "export", "alias", "unalias"] {
assert!(is_known_builtin(name), "{} missing from builtin set", name);
}
}
#[test]
fn known_builtins_include_control_flow() {
for name in ["return", "break", "continue", "trap"] {
assert!(is_known_builtin(name), "{} missing from builtin set", name);
}
}
#[test]
fn known_builtins_include_test_aliases() {
assert!(is_known_builtin("test"));
assert!(is_known_builtin("["));
}
#[test]
fn known_builtins_includes_source_and_dot_alias() {
assert!(is_known_builtin("source"));
assert!(is_known_builtin("."));
}
#[test]
fn unknown_command_is_not_a_builtin() {
assert!(!is_known_builtin("ls"));
assert!(!is_known_builtin("grep"));
assert!(!is_known_builtin(""));
assert!(!is_known_builtin("foo-bar-baz"));
}
#[test]
fn builtin_name_is_case_sensitive() {
assert!(!is_known_builtin("CD"));
assert!(!is_known_builtin("Echo"));
}
#[test]
fn basename_strips_leading_directory_components() {
assert_eq!(basename("/usr/bin/ls"), "ls");
assert_eq!(basename("foo/bar/baz"), "baz");
}
#[test]
fn basename_passes_through_when_no_slash() {
assert_eq!(basename("plain"), "plain");
}
#[test]
fn basename_empty_input_returns_empty() {
assert_eq!(basename(""), "");
}
#[test]
fn basename_trailing_slash_yields_empty_string() {
assert_eq!(basename("foo/"), "");
}
#[test]
fn basename_handles_root_only() {
assert_eq!(basename("/"), "");
}
#[test]
fn relative_dotdot_path_uses_pwd_prefix_and_basename() {
let _g = crate::test_util::global_state_lock();
let _ = crate::ported::params::setsparam("PWD", "/here");
setaparam("words", vec!["../tools/runme".to_string()]);
let _ = _set_command();
assert_eq!(
getsparam("_comp_command1").as_deref(),
Some("/here/../tools/runme")
);
assert_eq!(getsparam("_comp_command2").as_deref(), Some("runme"));
assert_eq!(getsparam("_comp_command").as_deref(), Some("runme"));
}
}