use std::env; use std::path::PathBuf; use std::process::Command; /// Execute a shell command line and print output directly to stdout/stderr. /// Returns the exit code. pub fn execute(input: &str) -> i32 { let trimmed = input.trim(); // Handle cd specially - it must affect our process if trimmed == "cd" || trimmed.starts_with("cd ") { return handle_cd(trimmed); } // Run everything else through sh -c for full shell semantics // (pipes, redirects, globs, etc.) let status = Command::new("sh") .arg("-c") .arg(trimmed) .status(); match status { Ok(s) => s.code().unwrap_or(1), Err(e) => { eprintln!("aishell: {}", e); 127 } } } fn handle_cd(input: &str) -> i32 { let target = input.strip_prefix("cd").unwrap().trim(); let dir: PathBuf = if target.is_empty() { // cd with no args → home directory match env::var("HOME") { Ok(home) => PathBuf::from(home), Err(_) => { eprintln!("aishell: cd: HOME not set"); return 1; } } } else if target.starts_with('~') { // Expand ~ to HOME match env::var("HOME") { Ok(home) => PathBuf::from(target.replacen('~', &home, 1)), Err(_) => { eprintln!("aishell: cd: HOME not set"); return 1; } } } else if target == "-" { // cd - → previous directory match env::var("OLDPWD") { Ok(old) => { println!("{}", old); PathBuf::from(old) } Err(_) => { eprintln!("aishell: cd: OLDPWD not set"); return 1; } } } else { PathBuf::from(target) }; // Save current dir as OLDPWD if let Ok(cwd) = env::current_dir() { env::set_var("OLDPWD", cwd); } match env::set_current_dir(&dir) { Ok(_) => 0, Err(e) => { eprintln!("aishell: cd: {}: {}", dir.display(), e); 1 } } }