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 {
matches!(
name,
"cd" | "echo" | "pwd" | "exit" | "set" | "unset" | "export" | "alias"
| "unalias" | "source" | "." | "eval" | "exec" | "test" | "[" | "true"
| "false" | "printf" | "read" | "shift" | "type" | "command"
| "builtin" | "let" | "return" | "break" | "continue" | "trap"
| "wait" | "kill" | "jobs" | "fg" | "bg" | "umask" | "ulimit"
)
}
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"));
}
}