mirror of
https://github.com/sxyazi/yazi.git
synced 2024-12-18 14:21:32 +03:00
fix: handle shell arguments on Windows (#214)
This commit is contained in:
parent
b48edfd764
commit
77988d47a4
8
Cargo.lock
generated
8
Cargo.lock
generated
@ -1837,9 +1837,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.8.1"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bc1433177506450fe920e46a4f9812d0c211f5dd556da10e731a0a3dfa151f0"
|
||||
checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d"
|
||||
dependencies = [
|
||||
"indexmap 2.0.2",
|
||||
"serde",
|
||||
@ -1859,9 +1859,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.20.1"
|
||||
version = "0.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca676d9ba1a322c1b64eb8045a5ec5c0cfb0c9d08e15e9ff622589ad5221c8fe"
|
||||
checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338"
|
||||
dependencies = [
|
||||
"indexmap 2.0.2",
|
||||
"serde",
|
||||
|
177
core/src/external/shell.rs
vendored
177
core/src/external/shell.rs
vendored
@ -44,15 +44,170 @@ pub fn shell(opt: ShellOpt) -> Result<Child> {
|
||||
);
|
||||
|
||||
#[cfg(windows)]
|
||||
return Ok(
|
||||
Command::new("cmd")
|
||||
.stdin(opt.stdio())
|
||||
.stdout(opt.stdio())
|
||||
.stderr(opt.stdio())
|
||||
.arg("/C")
|
||||
.arg(opt.cmd)
|
||||
.args(opt.args)
|
||||
.kill_on_drop(true)
|
||||
.spawn()?,
|
||||
);
|
||||
{
|
||||
let args: Vec<String> = opt.args.iter().map(|s| s.to_string_lossy().to_string()).collect();
|
||||
let args_: Vec<&str> = args.iter().map(|s| s.as_ref()).collect();
|
||||
let expanded = parser::parse(opt.cmd.to_string_lossy().as_ref(), &args_);
|
||||
Ok(
|
||||
Command::new("cmd")
|
||||
.arg("/C")
|
||||
.args(&expanded)
|
||||
.stdin(opt.stdio())
|
||||
.stdout(opt.stdio())
|
||||
.stderr(opt.stdio())
|
||||
.kill_on_drop(!opt.orphan)
|
||||
.spawn()?,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
mod parser {
|
||||
use std::{iter::Peekable, str::Chars};
|
||||
|
||||
pub(super) fn parse(cmd: &str, args: &[&str]) -> Vec<String> {
|
||||
let mut it = cmd.chars().peekable();
|
||||
let mut expanded = Vec::new();
|
||||
|
||||
while let Some(c) = it.next() {
|
||||
if c.is_whitespace() {
|
||||
continue;
|
||||
}
|
||||
let mut s = String::new();
|
||||
|
||||
if c == '\'' {
|
||||
while let Some(c) = it.next() {
|
||||
if c == '\'' {
|
||||
break;
|
||||
}
|
||||
next_string(&mut it, args, &mut s, c);
|
||||
}
|
||||
expanded.push(s);
|
||||
} else if c == '"' {
|
||||
while let Some(c) = it.next() {
|
||||
if c == '"' {
|
||||
break;
|
||||
}
|
||||
next_string(&mut it, args, &mut s, c);
|
||||
}
|
||||
expanded.push(s);
|
||||
} else if c == '%' && it.peek().is_some_and(|&c| c == '*') {
|
||||
it.next();
|
||||
for arg in args {
|
||||
expanded.push(arg.to_string());
|
||||
}
|
||||
} else {
|
||||
next_string(&mut it, args, &mut s, c);
|
||||
|
||||
while let Some(c) = it.next() {
|
||||
if c.is_whitespace() {
|
||||
break;
|
||||
}
|
||||
next_string(&mut it, args, &mut s, c);
|
||||
}
|
||||
expanded.push(s);
|
||||
}
|
||||
}
|
||||
|
||||
expanded
|
||||
}
|
||||
|
||||
fn next_string(it: &mut Peekable<Chars<'_>>, args: &[&str], s: &mut String, c: char) {
|
||||
if c == '\\' {
|
||||
match it.next() {
|
||||
Some('\\') => s.push('\\'), // \\ ==> \
|
||||
Some('\'') => s.push('\''), // \' ==> '
|
||||
Some('"') => s.push('"'), // \" ==> "
|
||||
Some('%') => s.push('%'), // \% ==> %
|
||||
Some('n') => s.push('\n'), // \n ==> '\n'
|
||||
Some('t') => s.push('\t'), // \t ==> '\t'
|
||||
Some('r') => s.push('\r'), // \r ==> '\r'
|
||||
Some(c) => {
|
||||
s.push('\\');
|
||||
s.push(c);
|
||||
}
|
||||
None => s.push('\\'),
|
||||
}
|
||||
} else if c == '%' {
|
||||
match it.peek() {
|
||||
Some('*') => {
|
||||
s.push_str(&args.join(" "));
|
||||
it.next();
|
||||
}
|
||||
Some(n) if n.is_ascii_digit() => {
|
||||
let mut pos = n.to_string();
|
||||
|
||||
it.next();
|
||||
while let Some(&n) = it.peek() {
|
||||
if n.is_ascii_digit() {
|
||||
pos.push(it.next().unwrap());
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let pos = pos.parse::<usize>().unwrap();
|
||||
if pos > 0 {
|
||||
s.push_str(args.get(pos - 1).unwrap_or(&""));
|
||||
}
|
||||
}
|
||||
_ => s.push('%'),
|
||||
}
|
||||
} else {
|
||||
s.push(c);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_no_quote() {
|
||||
let args = parse("echo abc xyz %0 %2", &["111", "222"]);
|
||||
assert_eq!(args, ["echo", "abc", "xyz", "", "222"]);
|
||||
|
||||
let args = parse(" echo abc xyz %1 %2 ", &["111", "222"]);
|
||||
assert_eq!(args, ["echo", "abc", "xyz", "111", "222"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_single_quote() {
|
||||
let args = parse("echo 'abc xyz' '%1' %2", &["111", "222"]);
|
||||
assert_eq!(args, ["echo", "abc xyz", "111", "222"]);
|
||||
|
||||
let args = parse("echo 'abc \"\"xyz' '%1' %2", &["111", "222"]);
|
||||
assert_eq!(args, ["echo", "abc \"\"xyz", "111", "222"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_double_quote() {
|
||||
let args = parse("echo \"abc ' 'xyz\" \"%1\" %2 %3", &["111", "222"]);
|
||||
assert_eq!(args, ["echo", "abc ' 'xyz", "111", "222", ""]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_escaped() {
|
||||
let args = parse("echo \"a\tbc ' 'x\nyz\" \"\\%1\" %2 %3", &["111", "22 2"]);
|
||||
assert_eq!(args, ["echo", "a\tbc ' 'x\nyz", "%1", "22 2", ""]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_percent_star() {
|
||||
let args = parse("echo %* xyz", &["111", "222"]);
|
||||
assert_eq!(args, ["echo", "111", "222", "xyz"]);
|
||||
|
||||
let args = parse("echo '%*' xyz", &["111", "222"]);
|
||||
assert_eq!(args, ["echo", "111 222", "xyz"]);
|
||||
|
||||
let args = parse("echo -C%* xyz", &["111", "222"]);
|
||||
assert_eq!(args, ["echo", "-C111 222", "xyz"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_env_var() {
|
||||
let args = parse(" %EDITOR% %* xyz", &["111", "222"]);
|
||||
assert_eq!(args, ["%EDITOR%", "111", "222", "xyz"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user