use crate::ported::params::{getaparam, getsparam, setsparam};
pub fn _set_command() -> i32 {
crate::compsys::ported::shared::call_compfn("_set_command", &[], || _set_command_impl())
}
pub fn _set_command_impl() -> i32 {
let _fn_scope = crate::compsys::ported::shared::FnScope::enter("_set_command");
let words = getaparam("words").unwrap_or_default();
let command = words.first().cloned().unwrap_or_default();
if command.is_empty() {
return 0;
}
crate::vm_helper::mark_module_param_used("builtins");
crate::vm_helper::mark_module_param_used("functions");
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..];
crate::vm_helper::mark_module_param_used("commands");
let resolved: String =
crate::ported::modules::parameter::getpmcommand(std::ptr::null_mut(), bare)
.and_then(|pm| pm.u_str.clone())
.unwrap_or_default();
let _ = setsparam("_comp_command2", &resolved);
let _ = setsparam("_comp_command1", bare);
let _ = setsparam("_comp_command", &resolved);
return 0;
}
if command.starts_with('.') && command.trim_start_matches('.').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;
}
crate::vm_helper::mark_module_param_used("commands");
let cmd2: String =
crate::ported::modules::parameter::getpmcommand(std::ptr::null_mut(), &command)
.and_then(|pm| pm.u_str.clone())
.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::compsys::ported::shared::plus_builtins(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_zero_and_publishes_nothing() {
let _g = crate::test_util::global_state_lock();
setaparam("words", Vec::new());
let _ = setsparam("_comp_command", "outer");
assert_eq!(_set_command_impl(), 0);
assert_eq!(getsparam("_comp_command").as_deref(), Some("outer"));
}
#[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_impl();
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_impl();
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_impl();
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"));
}
#[test]
fn single_dot_relative_path_uses_pwd_prefix() {
let _g = crate::test_util::global_state_lock();
let _ = crate::ported::params::setsparam("PWD", "/here");
setaparam("words", vec!["./runme".to_string()]);
let _ = _set_command_impl();
assert_eq!(getsparam("_comp_command1").as_deref(), Some("/here/./runme"));
assert_eq!(getsparam("_comp_command2").as_deref(), Some("runme"));
assert_eq!(getsparam("_comp_command").as_deref(), Some("runme"));
}
#[test]
fn module_builtin_name_falls_through_to_the_commands_branch() {
let _g = crate::test_util::global_state_lock();
let dir = std::env::temp_dir().join(format!(
"zshrs_set_command_{}_{}",
std::process::id(),
"chmodbranch"
));
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir_all(&dir).expect("scratch PATH dir");
let exe = dir.join("chmod");
std::fs::write(&exe, b"#!/bin/sh\nexit 0\n").expect("scratch chmod");
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
std::fs::set_permissions(&exe, std::fs::Permissions::from_mode(0o755))
.expect("chmod +x the scratch chmod");
}
let dir_s = dir.to_string_lossy().to_string();
crate::ported::hashtable::emptycmdnamtable();
crate::ported::hashtable::fillcmdnamtable(&[dir_s.clone()]);
assert!(
!is_known_builtin("chmod"),
"premise: zsh/files is unloaded, so `$+builtins[chmod]` is 0 \
(c:Src/Modules/parameter.c:784-792)"
);
setaparam("words", vec!["chmod".to_string(), "arg".to_string()]);
let _ = setsparam("_comp_command1", "");
let _ = setsparam("_comp_command2", "");
let _ = setsparam("_comp_command", "");
let rc = _set_command_impl();
let c1 = getsparam("_comp_command1").unwrap_or_default();
let c2 = getsparam("_comp_command2").unwrap_or_default();
let c = getsparam("_comp_command").unwrap_or_default();
crate::ported::hashtable::emptycmdnamtable();
let _ = std::fs::remove_dir_all(&dir);
assert_eq!(rc, 0);
assert_eq!(c1, "chmod", "sh:28");
assert_eq!(
c2,
format!("{}/chmod", dir_s),
"sh:29 — `_comp_command2` is `$commands[chmod]`; empty here means \
sh:12 wrongly claimed `chmod` is a builtin"
);
assert_eq!(c, "chmod", "sh:30");
}
}