rust: use terminalenv for SSH and local shell env
This commit is contained in:
@@ -17,34 +17,6 @@ pub fn default_shell() -> String {
|
||||
std::env::var("SHELL").ok().filter(|s| !s.is_empty()).unwrap_or_else(|| "/bin/sh".into())
|
||||
}
|
||||
|
||||
fn is_utf8_locale(v: &str) -> bool {
|
||||
let v = v.to_lowercase();
|
||||
v.contains("utf-8") || v.contains("utf8")
|
||||
}
|
||||
|
||||
/// TERM=xterm-256color and a UTF-8 character locale, keeping the user's environment otherwise.
|
||||
pub fn local_shell_env(env: &mut Vec<(String, String)>) {
|
||||
let set = |env: &mut Vec<(String, String)>, k: &str, v: &str| {
|
||||
env.retain(|(ek, _)| ek != k);
|
||||
env.push((k.into(), v.into()));
|
||||
};
|
||||
set(env, "TERM", "xterm-256color");
|
||||
let get = |k: &str| env.iter().find(|(ek, _)| ek == k).map(|(_, v)| v.clone());
|
||||
let effective = get("LC_ALL").or_else(|| get("LC_CTYPE")).or_else(|| get("LANG"));
|
||||
if effective.as_deref().is_some_and(is_utf8_locale) {
|
||||
return;
|
||||
}
|
||||
let locale = if cfg!(target_os = "macos") { "en_US.UTF-8" } else { "C.UTF-8" };
|
||||
let key = if get("LC_ALL").is_some() {
|
||||
"LC_ALL"
|
||||
} else if get("LC_CTYPE").is_some() || get("LANG").is_some() {
|
||||
"LC_CTYPE"
|
||||
} else {
|
||||
"LANG"
|
||||
};
|
||||
set(env, key, locale);
|
||||
}
|
||||
|
||||
struct Running {
|
||||
master: Box<dyn MasterPty + Send>,
|
||||
writer: Box<dyn Write + Send>,
|
||||
@@ -77,11 +49,12 @@ impl LocalShell {
|
||||
.openpty(PtySize { rows: st.3, cols: st.2, pixel_width: 0, pixel_height: 0 })
|
||||
.map_err(|e| format!("failed to start shell {:?}: {e}", self.shell))?;
|
||||
let mut cmd = CommandBuilder::new(&self.shell);
|
||||
let mut env: Vec<(String, String)> = std::env::vars().collect();
|
||||
local_shell_env(&mut env);
|
||||
cmd.env_clear();
|
||||
for (k, v) in env {
|
||||
cmd.env(k, v);
|
||||
let env = crate::terminalenv::local_shell_env(std::env::vars().map(|(k, v)| format!("{k}={v}")).collect());
|
||||
for kv in env {
|
||||
if let Some((k, v)) = kv.split_once('=') {
|
||||
cmd.env(k, v);
|
||||
}
|
||||
}
|
||||
cmd.cwd(&st.1);
|
||||
let mut child = pair.slave.spawn_command(cmd).map_err(|e| format!("failed to start shell {:?}: {e}", self.shell))?;
|
||||
@@ -215,12 +188,4 @@ mod tests {
|
||||
}
|
||||
assert_eq!(sh.working_directory(), PathBuf::from("/"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn env_gets_term_and_utf8() {
|
||||
let mut e = vec![("LANG".to_string(), "C".to_string())];
|
||||
local_shell_env(&mut e);
|
||||
assert!(e.contains(&("TERM".into(), "xterm-256color".into())));
|
||||
assert!(e.iter().any(|(k, v)| k == "LC_CTYPE" && v.ends_with("UTF-8")));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,16 +158,6 @@ async fn connect_agent() -> Result<Agent> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Profile env vars plus a UTF-8 LC_CTYPE when the profile sets no locale (Go's SSHEnvVars).
|
||||
fn ssh_env_vars(env: &std::collections::BTreeMap<String, String>) -> std::collections::BTreeMap<String, String> {
|
||||
let mut out: std::collections::BTreeMap<String, String> =
|
||||
env.iter().filter(|(k, _)| !k.trim().is_empty()).map(|(k, v)| (k.trim().to_string(), v.clone())).collect();
|
||||
if !["LC_ALL", "LC_CTYPE", "LANG"].iter().any(|k| out.contains_key(*k)) {
|
||||
out.insert("LC_CTYPE".into(), if cfg!(target_os = "macos") { "en_US.UTF-8" } else { "C.UTF-8" }.into());
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
fn is_totp_prompt(q: &str) -> bool {
|
||||
let q = q.to_lowercase();
|
||||
[
|
||||
@@ -496,7 +486,7 @@ impl SshClient {
|
||||
let handle = Arc::new(handle);
|
||||
|
||||
let ch = handle.channel_open_session().await.map_err(|e| format!("failed to create SSH session: {e}"))?;
|
||||
for (k, v) in ssh_env_vars(&sc.env_vars) {
|
||||
for (k, v) in crate::terminalenv::ssh_env_vars(&sc.env_vars) {
|
||||
let _ = ch.set_env(false, k, v).await;
|
||||
}
|
||||
let term = if sc.terminal_type.is_empty() { "xterm-256color" } else { &sc.terminal_type };
|
||||
|
||||
Reference in New Issue
Block a user