Skip to content

Commit 82502b9

Browse files
committed
Windows: draft traits for raw args and custom escapers
1 parent 29d21a8 commit 82502b9

File tree

2 files changed

+57
-11
lines changed

2 files changed

+57
-11
lines changed

library/std/src/sys/windows/process.rs

Lines changed: 56 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -98,11 +98,13 @@ struct DropGuard<'a> {
9898
lock: &'a Mutex,
9999
}
100100

101-
enum Problem {
101+
pub enum Problem {
102102
SawNul,
103103
Oversized,
104104
}
105105

106+
pub struct RawArg<'a>(&'a OsStr);
107+
106108
impl Command {
107109
pub fn new(program: &OsStr) -> Command {
108110
Command {
@@ -136,7 +138,7 @@ impl Command {
136138

137139
self.args.push(arg.to_os_string());
138140
self.cmdline.push(' ' as u16);
139-
let result = append_arg(&mut self.cmdline, arg, false);
141+
let result = arg.append_to(&mut self.cmdline, false);
140142
match result {
141143
Err(err) => {
142144
self.cmdline.truncate(self.cmdline.len() - 1);
@@ -203,7 +205,7 @@ impl Command {
203205
// Prepare and terminate the application name and the cmdline
204206
// XXX: this won't work for 16-bit, might be preferable to do a extend_from_slice
205207
let mut program_str: Vec<u16> = Vec::new();
206-
append_arg(&mut program_str, program, true)?;
208+
program.as_os_str().append_to(&mut program_str, true)?;
207209
program_str.push(0);
208210
self.cmdline.push(0);
209211

@@ -385,6 +387,31 @@ impl From<Problem> for Error {
385387
}
386388
}
387389

390+
pub trait Arg {
391+
fn append_to(&self, cmd: &mut Vec<u16>, force_quotes: bool) -> Result<usize, Problem>;
392+
fn arg_len(&self, force_quotes: bool) -> Result<usize, Problem>;
393+
}
394+
395+
impl Arg for &OsStr {
396+
fn append_to(&self, cmd: &mut Vec<u16>, force_quotes: bool) -> Result<usize, Problem> {
397+
append_arg(&mut Some(cmd), &self, force_quotes)
398+
}
399+
fn arg_len(&self, force_quotes: bool) -> Result<usize, Problem> {
400+
append_arg(&mut None, &self, force_quotes)
401+
}
402+
}
403+
404+
#[allow(dead_code)]
405+
impl Arg for RawArg<'_> {
406+
fn append_to(&self, cmd: &mut Vec<u16>, _fq: bool) -> Result<usize, Problem> {
407+
cmd.extend(self.0.encode_wide());
408+
self.arg_len(_fq)
409+
}
410+
fn arg_len(&self, _: bool) -> Result<usize, Problem> {
411+
Ok(self.0.encode_wide().count())
412+
}
413+
}
414+
388415
////////////////////////////////////////////////////////////////////////////////
389416
// Processes
390417
////////////////////////////////////////////////////////////////////////////////
@@ -523,7 +550,23 @@ fn zeroed_process_information() -> c::PROCESS_INFORMATION {
523550
}
524551
}
525552

526-
fn append_arg(cmd: &mut Vec<u16>, arg: &OsStr, force_quotes: bool) -> Result<usize, Problem> {
553+
macro_rules! if_some {
554+
($e: expr, $id:ident, $b:block) => {
555+
if let &mut Some(ref mut $id) = $e
556+
$b
557+
};
558+
($e: expr, $id:ident, $s:stmt) => {
559+
if_some!($e, $id, { $s })
560+
};
561+
}
562+
563+
// This is effed up. Yeah, how the heck do I pass an optional, mutable reference around?
564+
// @see https://users.rust-lang.org/t/idiomatic-way-for-passing-an-optional-mutable-reference-around/7947
565+
fn append_arg(
566+
maybe_cmd: &mut Option<&mut Vec<u16>>,
567+
arg: &OsStr,
568+
force_quotes: bool,
569+
) -> Result<usize, Problem> {
527570
let mut addsize: usize = 0;
528571
// If an argument has 0 characters then we need to quote it to ensure
529572
// that it actually gets passed through on the command line or otherwise
@@ -533,7 +576,7 @@ fn append_arg(cmd: &mut Vec<u16>, arg: &OsStr, force_quotes: bool) -> Result<usi
533576
let quote =
534577
force_quotes || arg_bytes.iter().any(|c| *c == b' ' || *c == b'\t') || arg_bytes.is_empty();
535578
if quote {
536-
cmd.push('"' as u16);
579+
if_some!(maybe_cmd, cmd, cmd.push('"' as u16));
537580
addsize += 1;
538581
}
539582

@@ -544,18 +587,20 @@ fn append_arg(cmd: &mut Vec<u16>, arg: &OsStr, force_quotes: bool) -> Result<usi
544587
} else {
545588
if x == '"' as u16 {
546589
// Add n+1 backslashes to total 2n+1 before internal '"'.
547-
cmd.extend((0..=backslashes).map(|_| '\\' as u16));
590+
if_some!(maybe_cmd, cmd, cmd.extend((0..=backslashes).map(|_| '\\' as u16)));
548591
addsize += backslashes + 1;
549592
}
550593
backslashes = 0;
551594
}
552-
cmd.push(x);
595+
if_some!(maybe_cmd, cmd, cmd.push(x));
553596
}
554597

555598
if quote {
556599
// Add n backslashes to total 2n before ending '"'.
557-
cmd.extend((0..backslashes).map(|_| '\\' as u16));
558-
cmd.push('"' as u16);
600+
if_some!(maybe_cmd, cmd, {
601+
cmd.extend((0..backslashes).map(|_| '\\' as u16));
602+
cmd.push('"' as u16);
603+
});
559604
addsize += backslashes + 1;
560605
}
561606
Ok(addsize)
@@ -570,10 +615,10 @@ fn make_command_line(prog: &OsStr, args: &[OsString]) -> io::Result<Vec<u16>> {
570615
let mut cmd: Vec<u16> = Vec::new();
571616
// Always quote the program name so CreateProcess doesn't interpret args as
572617
// part of the name if the binary wasn't found first time.
573-
append_arg(&mut cmd, prog, true)?;
618+
prog.append_to(&mut cmd, true)?;
574619
for arg in args {
575620
cmd.push(' ' as u16);
576-
append_arg(&mut cmd, arg, false)?;
621+
arg.as_os_str().append_to(&mut cmd, false)?;
577622
}
578623
return Ok(cmd);
579624
}

src/stdarch

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Subproject commit d10eefc62284c40c5a95a2eed19fc1f414a5364d

0 commit comments

Comments
 (0)