2023-08-03 21:26:41 +02:00
|
|
|
use std::process::exit;
|
2023-07-30 18:40:18 +02:00
|
|
|
|
2023-08-06 12:03:27 +02:00
|
|
|
use std::sync::mpsc::channel;
|
|
|
|
|
use std::thread;
|
|
|
|
|
use std::time::Duration;
|
|
|
|
|
|
2023-07-31 14:12:45 +02:00
|
|
|
pub const PRIVILEGE_LIST: [&str; 2] = ["sudo", "doas"];
|
2023-07-30 18:40:18 +02:00
|
|
|
|
|
|
|
|
pub fn command_output(shell: &str, command: &str) -> String {
|
2023-08-06 12:03:27 +02:00
|
|
|
let (sender, receiver) = channel();
|
|
|
|
|
|
|
|
|
|
let _shell = shell.to_owned();
|
|
|
|
|
let _command = command.to_owned();
|
|
|
|
|
thread::spawn(move || {
|
2023-08-07 20:21:02 +02:00
|
|
|
sender
|
|
|
|
|
.send(
|
|
|
|
|
std::process::Command::new(_shell)
|
|
|
|
|
.arg("-c")
|
|
|
|
|
.arg(_command)
|
|
|
|
|
.env("LC_ALL", "C")
|
|
|
|
|
.output()
|
|
|
|
|
.expect("failed to execute process"),
|
|
|
|
|
)
|
2023-08-06 12:03:27 +02:00
|
|
|
.expect("failed to send output");
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
match receiver.recv_timeout(Duration::from_secs(3)) {
|
2024-09-26 02:20:19 +02:00
|
|
|
Ok(output) => match output.stderr.is_empty() {
|
|
|
|
|
true => String::from_utf8_lossy(&output.stdout).to_lowercase(),
|
|
|
|
|
false => String::from_utf8_lossy(&output.stderr).to_lowercase(),
|
2024-10-19 16:48:34 +02:00
|
|
|
},
|
2023-08-06 12:03:27 +02:00
|
|
|
Err(_) => {
|
|
|
|
|
use colored::*;
|
|
|
|
|
eprintln!("Timeout while executing command: {}", command.red());
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-07-30 18:40:18 +02:00
|
|
|
}
|
|
|
|
|
|
2024-11-16 00:25:28 +01:00
|
|
|
pub fn last_command(shell: &str) -> String {
|
2023-08-09 16:56:17 +02:00
|
|
|
let last_command = match std::env::var("_PR_LAST_COMMAND") {
|
|
|
|
|
Ok(command) => command,
|
|
|
|
|
Err(_) => {
|
2024-09-25 17:55:55 +02:00
|
|
|
eprintln!(
|
|
|
|
|
"{}",
|
|
|
|
|
t!(
|
|
|
|
|
"no-env-setup",
|
|
|
|
|
var = "_PR_LAST_COMMAND",
|
|
|
|
|
help = "pay-respects -h"
|
|
|
|
|
)
|
|
|
|
|
);
|
2023-08-09 16:56:17 +02:00
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2023-07-31 14:12:45 +02:00
|
|
|
match shell {
|
2024-11-28 02:21:48 +08:00
|
|
|
"bash" => {
|
|
|
|
|
let first_line = last_command.lines().next().unwrap().trim();
|
|
|
|
|
first_line.split_once(' ').unwrap().1.to_string()
|
|
|
|
|
}
|
2023-07-31 14:12:45 +02:00
|
|
|
"zsh" => last_command,
|
|
|
|
|
"fish" => last_command,
|
2023-07-31 15:06:30 +02:00
|
|
|
"nu" => last_command,
|
2024-11-18 22:08:49 +01:00
|
|
|
_ => last_command,
|
2023-07-31 14:12:45 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-16 00:25:28 +01:00
|
|
|
pub fn expand_alias(shell: &str, full_command: &str) -> String {
|
2024-11-18 22:08:49 +01:00
|
|
|
let alias_env = std::env::var("_PR_ALIAS");
|
|
|
|
|
if alias_env.is_err() {
|
|
|
|
|
return full_command.to_string();
|
|
|
|
|
}
|
|
|
|
|
let alias = alias_env.unwrap();
|
2023-07-31 14:12:45 +02:00
|
|
|
if alias.is_empty() {
|
2024-11-16 00:25:28 +01:00
|
|
|
return full_command.to_string();
|
2023-07-31 14:12:45 +02:00
|
|
|
}
|
|
|
|
|
|
2024-11-16 00:25:28 +01:00
|
|
|
let split_command = full_command.split_whitespace().collect::<Vec<&str>>();
|
2024-11-17 14:57:22 +01:00
|
|
|
let (command, pure_command) = if PRIVILEGE_LIST.contains(&split_command[0]) {
|
|
|
|
|
(split_command[1], Some(split_command[1..].join(" ")))
|
2023-07-31 14:12:45 +02:00
|
|
|
} else {
|
2024-11-17 14:57:22 +01:00
|
|
|
(split_command[0], None)
|
2023-07-31 18:46:35 +02:00
|
|
|
};
|
2023-07-31 14:12:45 +02:00
|
|
|
|
2024-11-16 00:25:28 +01:00
|
|
|
let mut expanded_command = Option::None;
|
2023-07-31 14:12:45 +02:00
|
|
|
|
|
|
|
|
match shell {
|
|
|
|
|
"bash" => {
|
|
|
|
|
for line in alias.lines() {
|
|
|
|
|
if line.starts_with(format!("alias {}=", command).as_str()) {
|
|
|
|
|
let alias = line.replace(format!("alias {}='", command).as_str(), "");
|
|
|
|
|
let alias = alias.trim_end_matches('\'').trim_start_matches('\'');
|
|
|
|
|
|
2024-11-16 00:25:28 +01:00
|
|
|
expanded_command = Some(alias.to_string());
|
2023-07-31 14:12:45 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
"zsh" => {
|
|
|
|
|
for line in alias.lines() {
|
|
|
|
|
if line.starts_with(format!("{}=", command).as_str()) {
|
|
|
|
|
let alias = line.replace(format!("{}=", command).as_str(), "");
|
|
|
|
|
let alias = alias.trim_start_matches('\'').trim_end_matches('\'');
|
|
|
|
|
|
2024-11-16 00:25:28 +01:00
|
|
|
expanded_command = Some(alias.to_string());
|
2023-07-31 14:12:45 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
"fish" => {
|
|
|
|
|
for line in alias.lines() {
|
|
|
|
|
if line.starts_with(format!("alias {} ", command).as_str()) {
|
|
|
|
|
let alias = line.replace(format!("alias {} ", command).as_str(), "");
|
|
|
|
|
let alias = alias.trim_start_matches('\'').trim_end_matches('\'');
|
|
|
|
|
|
2024-11-16 00:25:28 +01:00
|
|
|
expanded_command = Some(alias.to_string());
|
2023-07-31 14:12:45 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
_ => {
|
|
|
|
|
eprintln!("Unsupported shell: {}", shell);
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
};
|
2023-07-30 18:40:18 +02:00
|
|
|
|
2024-11-16 00:25:28 +01:00
|
|
|
if expanded_command.is_none() {
|
2024-11-17 14:57:22 +01:00
|
|
|
return full_command.to_string();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let expanded_command = expanded_command.unwrap();
|
|
|
|
|
|
|
|
|
|
if pure_command.is_some() {
|
|
|
|
|
let pure_command = pure_command.unwrap();
|
|
|
|
|
if pure_command.starts_with(&expanded_command) {
|
|
|
|
|
return full_command.to_string();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
full_command.replacen(command, &expanded_command, 1)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn expand_alias_multiline(shell: &str, full_command: &str) -> String {
|
|
|
|
|
let lines = full_command.lines().collect::<Vec<&str>>();
|
|
|
|
|
let mut expanded = String::new();
|
|
|
|
|
for line in lines {
|
|
|
|
|
expanded = format!("{}\n{}", expanded, expand_alias(shell, line));
|
2024-11-16 00:25:28 +01:00
|
|
|
}
|
2024-11-17 14:57:22 +01:00
|
|
|
expanded
|
2023-07-30 18:40:18 +02:00
|
|
|
}
|
2023-08-02 03:11:03 +02:00
|
|
|
|
2024-11-23 22:51:43 +01:00
|
|
|
pub fn initialization(shell: &str, binary_path: &str, auto_alias: &str, cnf: bool) {
|
2023-08-03 21:26:41 +02:00
|
|
|
let last_command;
|
|
|
|
|
let alias;
|
2023-08-02 03:11:03 +02:00
|
|
|
|
2023-08-03 21:26:41 +02:00
|
|
|
match shell {
|
|
|
|
|
"bash" => {
|
2024-11-28 02:21:48 +08:00
|
|
|
last_command = "$(history 2)";
|
2023-08-03 21:26:41 +02:00
|
|
|
alias = "$(alias)"
|
|
|
|
|
}
|
|
|
|
|
"zsh" => {
|
|
|
|
|
last_command = "$(fc -ln -1)";
|
|
|
|
|
alias = "$(alias)"
|
|
|
|
|
}
|
|
|
|
|
"fish" => {
|
|
|
|
|
last_command = "$(history | head -n 1)";
|
|
|
|
|
alias = "$(alias)";
|
|
|
|
|
}
|
|
|
|
|
"nu" | "nush" | "nushell" => {
|
|
|
|
|
last_command = "(history | last).command";
|
|
|
|
|
alias = "\"\"";
|
2023-08-02 03:11:03 +02:00
|
|
|
}
|
2024-11-25 21:31:40 +08:00
|
|
|
"pwsh" | "powershell" => {
|
|
|
|
|
last_command = "Get-History | Select-Object -Last 1 | ForEach-Object {$_.CommandLine}";
|
|
|
|
|
alias = ";";
|
|
|
|
|
}
|
2023-08-03 21:26:41 +02:00
|
|
|
_ => {
|
|
|
|
|
println!("Unknown shell: {}", shell);
|
|
|
|
|
std::process::exit(1);
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-08-02 03:11:03 +02:00
|
|
|
|
2024-11-18 13:56:19 +01:00
|
|
|
if shell == "nu" || shell == "nush" || shell == "nushell" {
|
2024-11-18 13:53:16 +01:00
|
|
|
let pr_alias = if auto_alias.is_empty() {
|
|
|
|
|
"f"
|
|
|
|
|
} else {
|
|
|
|
|
auto_alias
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let init = format!(
|
2024-11-18 22:08:49 +01:00
|
|
|
r#"
|
|
|
|
|
def --env {} [] {{
|
|
|
|
|
let dir = (with-env {{ _PR_LAST_COMMAND: {}, _PR_SHELL: nu }} {{ {} }})
|
2024-11-18 13:53:16 +01:00
|
|
|
cd $dir
|
2024-11-18 22:08:49 +01:00
|
|
|
}}
|
|
|
|
|
"#,
|
|
|
|
|
pr_alias, last_command, binary_path
|
2024-11-18 13:53:16 +01:00
|
|
|
);
|
|
|
|
|
println!("{}", init);
|
|
|
|
|
std::process::exit(0);
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-25 21:31:40 +08:00
|
|
|
let mut init = match shell {
|
|
|
|
|
"bash" | "zsh" | "fish" => format!(
|
|
|
|
|
"\
|
2023-08-12 22:55:31 +02:00
|
|
|
eval $(_PR_LAST_COMMAND=\"{}\" \
|
2023-08-02 03:11:03 +02:00
|
|
|
_PR_ALIAS=\"{}\" \
|
|
|
|
|
_PR_SHELL=\"{}\" \
|
2023-08-12 22:55:31 +02:00
|
|
|
\"{}\")",
|
2024-11-25 21:31:40 +08:00
|
|
|
last_command, alias, shell, binary_path
|
|
|
|
|
),
|
|
|
|
|
"pwsh" | "powershell" => format!(
|
|
|
|
|
r#"& {{
|
|
|
|
|
try {{
|
|
|
|
|
# fetch command and error from session history only when not in cnf mode
|
|
|
|
|
if ($env:_PR_MODE -ne 'cnf') {{
|
|
|
|
|
$env:_PR_LAST_COMMAND = ({});
|
|
|
|
|
$err = Get-Error;
|
|
|
|
|
if ($env:_PR_LAST_COMMAND -eq $err.InvocationInfo.Line) {{
|
|
|
|
|
$env:_PR_ERROR_MSG = $err.Exception.Message
|
|
|
|
|
}}
|
|
|
|
|
}}
|
|
|
|
|
$env:_PR_SHELL = '{}';
|
|
|
|
|
&'{}';
|
|
|
|
|
}}
|
|
|
|
|
finally {{
|
|
|
|
|
# restore mode from cnf
|
|
|
|
|
if ($env:_PR_MODE -eq 'cnf') {{
|
|
|
|
|
$env:_PR_MODE = $env:_PR_PWSH_ORIGIN_MODE;
|
|
|
|
|
$env:_PR_PWSH_ORIGIN_MODE = $null;
|
|
|
|
|
}}
|
|
|
|
|
}}
|
|
|
|
|
}}
|
|
|
|
|
"#,
|
|
|
|
|
last_command, shell, binary_path
|
|
|
|
|
),
|
|
|
|
|
_ => {
|
|
|
|
|
println!("Unsupported shell: {}", shell);
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
};
|
2023-08-04 15:40:45 +02:00
|
|
|
if auto_alias.is_empty() {
|
|
|
|
|
println!("{}", init);
|
|
|
|
|
std::process::exit(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
match shell {
|
|
|
|
|
"bash" | "zsh" => {
|
2023-08-12 23:00:23 +02:00
|
|
|
init = format!(r#"alias {}='{}'"#, auto_alias, init);
|
2023-08-04 15:40:45 +02:00
|
|
|
}
|
|
|
|
|
"fish" => {
|
|
|
|
|
init = format!(
|
2023-08-04 16:31:17 +02:00
|
|
|
r#"
|
|
|
|
|
function {} -d "Terminal command correction"
|
2023-08-12 23:09:56 +02:00
|
|
|
eval $({})
|
2023-08-04 16:31:17 +02:00
|
|
|
end
|
|
|
|
|
"#,
|
2023-08-04 15:40:45 +02:00
|
|
|
auto_alias, init
|
|
|
|
|
);
|
|
|
|
|
}
|
2024-11-25 21:31:40 +08:00
|
|
|
"pwsh" | "powershell" => {
|
|
|
|
|
init = format!(
|
|
|
|
|
"function {} {{\n{}",
|
|
|
|
|
auto_alias,
|
|
|
|
|
init.split_once("\n").unwrap().1,
|
|
|
|
|
);
|
|
|
|
|
}
|
2023-08-04 15:40:45 +02:00
|
|
|
_ => {
|
|
|
|
|
println!("Unsupported shell: {}", shell);
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-23 22:51:43 +01:00
|
|
|
if cnf {
|
|
|
|
|
match shell {
|
2024-11-27 19:34:58 +01:00
|
|
|
"bash" => {
|
2024-11-23 22:51:43 +01:00
|
|
|
init = format!(
|
|
|
|
|
r#"
|
|
|
|
|
command_not_found_handler() {{
|
2024-11-27 19:34:58 +01:00
|
|
|
eval $(_PR_LAST_COMMAND="_ $@" _PR_SHELL="{}" _PR_MODE="cnf" "{}")
|
|
|
|
|
}}
|
|
|
|
|
|
|
|
|
|
{}
|
|
|
|
|
"#,
|
|
|
|
|
shell, binary_path, init
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
"zsh" => {
|
|
|
|
|
init = format!(
|
|
|
|
|
r#"
|
|
|
|
|
command_not_found_handler() {{
|
|
|
|
|
eval $(_PR_LAST_COMMAND="$@" _PR_SHELL="{}" _PR_MODE="cnf" "{}")
|
2024-11-23 22:51:43 +01:00
|
|
|
}}
|
|
|
|
|
|
|
|
|
|
{}
|
|
|
|
|
"#,
|
|
|
|
|
shell, binary_path, init
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
"fish" => {
|
|
|
|
|
init = format!(
|
|
|
|
|
r#"
|
|
|
|
|
function fish_command_not_found --on-event fish_command_not_found
|
2024-11-27 19:34:58 +01:00
|
|
|
eval $(_PR_LAST_COMMAND="$argv" _PR_SHELL="{}" _PR_MODE="cnf" "{}")
|
2024-11-23 22:51:43 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
{}
|
|
|
|
|
"#,
|
|
|
|
|
shell, binary_path, init
|
|
|
|
|
);
|
|
|
|
|
}
|
2024-11-25 21:31:40 +08:00
|
|
|
"pwsh" | "powershell" => {
|
|
|
|
|
init = format!(
|
|
|
|
|
r#"{}
|
|
|
|
|
$ExecutionContext.InvokeCommand.CommandNotFoundAction =
|
|
|
|
|
{{
|
|
|
|
|
param(
|
|
|
|
|
[string]
|
|
|
|
|
$commandName,
|
|
|
|
|
[System.Management.Automation.CommandLookupEventArgs]
|
|
|
|
|
$eventArgs
|
|
|
|
|
)
|
|
|
|
|
# powershell does not support run command with specific environment variables
|
|
|
|
|
# but you must set global variables. so we are memorizing the current mode and the alias function will reset it later.
|
|
|
|
|
$env:_PR_PWSH_ORIGIN_MODE=$env:_PR_MODE;
|
|
|
|
|
$env:_PR_MODE='cnf';
|
|
|
|
|
# powershell may search command with prefix 'get-' or '.\' first when this hook is hit, strip them
|
|
|
|
|
$env:_PR_LAST_COMMAND=$commandName -replace '^get-|\.\\','';
|
|
|
|
|
$eventArgs.Command = (Get-Command {});
|
|
|
|
|
$eventArgs.StopSearch = $True;
|
|
|
|
|
}}
|
|
|
|
|
"#,
|
|
|
|
|
init, auto_alias
|
|
|
|
|
)
|
|
|
|
|
}
|
2024-11-23 22:51:43 +01:00
|
|
|
_ => {
|
|
|
|
|
println!("Unsupported shell: {}", shell);
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-04 15:40:45 +02:00
|
|
|
println!("{}", init);
|
|
|
|
|
|
2023-08-03 21:26:41 +02:00
|
|
|
std::process::exit(0);
|
2023-08-02 03:11:03 +02:00
|
|
|
}
|
2024-11-18 13:11:13 +01:00
|
|
|
|
2024-11-23 22:51:43 +01:00
|
|
|
pub fn get_shell() -> String {
|
|
|
|
|
match std::env::var("_PR_SHELL") {
|
|
|
|
|
Ok(shell) => shell,
|
|
|
|
|
Err(_) => {
|
|
|
|
|
eprintln!(
|
|
|
|
|
"{}",
|
|
|
|
|
t!("no-env-setup", var = "_PR_SHELL", help = "pay-respects -h")
|
|
|
|
|
);
|
|
|
|
|
std::process::exit(1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-18 13:11:13 +01:00
|
|
|
pub fn shell_syntax(shell: &str, command: &mut String) {
|
|
|
|
|
#[allow(clippy::single_match)]
|
|
|
|
|
match shell {
|
2024-11-18 13:38:21 +01:00
|
|
|
"nu" => {
|
2024-11-18 13:11:13 +01:00
|
|
|
*command = command.replace(" && ", " and ");
|
|
|
|
|
}
|
|
|
|
|
_ => {}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn shell_evaluated_commands(shell: &str, command: &str) -> Option<String> {
|
|
|
|
|
let lines = command
|
|
|
|
|
.lines()
|
|
|
|
|
.map(|line| line.trim().trim_end_matches(['\\', ';', '|', '&']))
|
|
|
|
|
.collect::<Vec<&str>>();
|
|
|
|
|
let mut dirs = Vec::new();
|
|
|
|
|
for line in lines {
|
|
|
|
|
if let Some(dir) = line.strip_prefix("cd ") {
|
|
|
|
|
dirs.push(dir.to_string());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let cd_dir = dirs.join("");
|
|
|
|
|
if cd_dir.is_empty() {
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[allow(clippy::single_match)]
|
|
|
|
|
match shell {
|
2024-11-18 13:38:21 +01:00
|
|
|
"nu" => Some(cd_dir),
|
2024-11-18 13:11:13 +01:00
|
|
|
_ => Some(format!("cd {}", cd_dir)),
|
|
|
|
|
}
|
|
|
|
|
}
|