Extends shell wrapper stripping in TUI to handle `zsh -lc` in addition to `bash -lc`. Currently, Linux users (and macOS users with zsh profiles) see cluttered command headers like `• Ran zsh -lc "echo hello"` instead of `• Ran echo hello`. This happens because `codex-rs/tui/src/exec_command.rs` only checks for literal `"bash"`, ignoring `zsh` and absolute paths like `/usr/bin/zsh`. **Changes:** - Added `is_login_shell_with_lc` helper that extracts shell basename and matches against `bash` or `zsh` - Updated pattern matching to use the helper instead of hardcoded check - Added test coverage for zsh and absolute paths (`/usr/bin/zsh`, `/bin/bash`) **Testing:** ```bash cd codex-rs cargo test strip_bash_lc_and_escape -p codex-tui ``` All 4 test cases pass (bash, zsh, and absolute paths for both). Closes #4201
78 lines
2.4 KiB
Rust
78 lines
2.4 KiB
Rust
use std::path::Path;
|
||
use std::path::PathBuf;
|
||
|
||
use dirs::home_dir;
|
||
use shlex::try_join;
|
||
|
||
pub(crate) fn escape_command(command: &[String]) -> String {
|
||
try_join(command.iter().map(String::as_str)).unwrap_or_else(|_| command.join(" "))
|
||
}
|
||
|
||
fn is_login_shell_with_lc(shell: &str) -> bool {
|
||
let shell_name = std::path::Path::new(shell)
|
||
.file_name()
|
||
.and_then(|s| s.to_str())
|
||
.unwrap_or(shell);
|
||
matches!(shell_name, "bash" | "zsh")
|
||
}
|
||
|
||
pub(crate) fn strip_bash_lc_and_escape(command: &[String]) -> String {
|
||
match command {
|
||
[first, second, third] if is_login_shell_with_lc(first) && second == "-lc" => third.clone(),
|
||
_ => escape_command(command),
|
||
}
|
||
}
|
||
|
||
/// If `path` is absolute and inside $HOME, return the part *after* the home
|
||
/// directory; otherwise, return the path as-is. Note if `path` is the homedir,
|
||
/// this will return and empty path.
|
||
pub(crate) fn relativize_to_home<P>(path: P) -> Option<PathBuf>
|
||
where
|
||
P: AsRef<Path>,
|
||
{
|
||
let path = path.as_ref();
|
||
if !path.is_absolute() {
|
||
// If the path is not absolute, we can’t do anything with it.
|
||
return None;
|
||
}
|
||
|
||
let home_dir = home_dir()?;
|
||
let rel = path.strip_prefix(&home_dir).ok()?;
|
||
Some(rel.to_path_buf())
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
|
||
#[test]
|
||
fn test_escape_command() {
|
||
let args = vec!["foo".into(), "bar baz".into(), "weird&stuff".into()];
|
||
let cmdline = escape_command(&args);
|
||
assert_eq!(cmdline, "foo 'bar baz' 'weird&stuff'");
|
||
}
|
||
|
||
#[test]
|
||
fn test_strip_bash_lc_and_escape() {
|
||
// Test bash
|
||
let args = vec!["bash".into(), "-lc".into(), "echo hello".into()];
|
||
let cmdline = strip_bash_lc_and_escape(&args);
|
||
assert_eq!(cmdline, "echo hello");
|
||
|
||
// Test zsh
|
||
let args = vec!["zsh".into(), "-lc".into(), "echo hello".into()];
|
||
let cmdline = strip_bash_lc_and_escape(&args);
|
||
assert_eq!(cmdline, "echo hello");
|
||
|
||
// Test absolute path to zsh
|
||
let args = vec!["/usr/bin/zsh".into(), "-lc".into(), "echo hello".into()];
|
||
let cmdline = strip_bash_lc_and_escape(&args);
|
||
assert_eq!(cmdline, "echo hello");
|
||
|
||
// Test absolute path to bash
|
||
let args = vec!["/bin/bash".into(), "-lc".into(), "echo hello".into()];
|
||
let cmdline = strip_bash_lc_and_escape(&args);
|
||
assert_eq!(cmdline, "echo hello");
|
||
}
|
||
}
|