Skip to content

Commit 28d3ac1

Browse files
committed
feature: env argv0 overwrite (unix only)
1 parent 56e59da commit 28d3ac1

File tree

2 files changed

+297
-18
lines changed

2 files changed

+297
-18
lines changed

src/uu/env/src/env.rs

Lines changed: 66 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use std::io::{self, Write};
2727
use std::ops::Deref;
2828

2929
#[cfg(unix)]
30-
use std::os::unix::process::ExitStatusExt;
30+
use std::os::unix::process::{CommandExt, ExitStatusExt};
3131
use std::process::{self};
3232
use uucore::display::Quotable;
3333
use uucore::error::{ExitCode, UError, UResult, USimpleError, UUsageError};
@@ -48,6 +48,7 @@ struct Options<'a> {
4848
unsets: Vec<&'a OsStr>,
4949
sets: Vec<(Cow<'a, OsStr>, Cow<'a, OsStr>)>,
5050
program: Vec<&'a OsStr>,
51+
argv0: Option<&'a OsStr>,
5152
}
5253

5354
// print name=value env pairs on screen
@@ -173,7 +174,7 @@ pub fn uu_app() -> Command {
173174
Arg::new("debug")
174175
.short('v')
175176
.long("debug")
176-
.action(ArgAction::SetTrue)
177+
.action(ArgAction::Count)
177178
.help("print verbose information for each processing step"),
178179
)
179180
.arg(
@@ -184,6 +185,16 @@ pub fn uu_app() -> Command {
184185
.action(ArgAction::Set)
185186
.value_parser(ValueParser::os_string())
186187
.help("process and split S into separate arguments; used to pass multiple arguments on shebang lines")
188+
).arg(
189+
Arg::new("argv0")
190+
.overrides_with("argv0")
191+
.short('a')
192+
.long("argv0")
193+
.value_name("a")
194+
.action(ArgAction::Set)
195+
.value_parser(ValueParser::os_string())
196+
.help("Override the zeroth argument passed to the command being executed.\
197+
Without this option a default value of `command` is used.")
187198
)
188199
.arg(
189200
Arg::new("vars")
@@ -248,6 +259,7 @@ fn check_and_handle_string_args(
248259
#[derive(Default)]
249260
struct EnvAppData {
250261
do_debug_printing: bool,
262+
do_input_debug_printing: Option<bool>,
251263
had_string_argument: bool,
252264
}
253265

@@ -273,14 +285,19 @@ impl EnvAppData {
273285
b if check_and_handle_string_args(b, "-S", &mut all_args, None)? => {
274286
self.had_string_argument = true;
275287
}
288+
b if check_and_handle_string_args(b, "-vS", &mut all_args, None)? => {
289+
self.do_debug_printing = true;
290+
self.had_string_argument = true;
291+
}
276292
b if check_and_handle_string_args(
277293
b,
278-
"-vS",
294+
"-vvS",
279295
&mut all_args,
280296
Some(original_args),
281297
)? =>
282298
{
283299
self.do_debug_printing = true;
300+
self.do_input_debug_printing = Some(false); // already done
284301
self.had_string_argument = true;
285302
}
286303
_ => {
@@ -323,10 +340,15 @@ impl EnvAppData {
323340
fn run_env(&mut self, original_args: impl uucore::Args) -> UResult<()> {
324341
let (original_args, matches) = self.parse_arguments(original_args)?;
325342

326-
let did_debug_printing_before = self.do_debug_printing; // could have been done already as part of the "-vS" string parsing
327-
let do_debug_printing = self.do_debug_printing || matches.get_flag("debug");
328-
if do_debug_printing && !did_debug_printing_before {
329-
debug_print_args(&original_args);
343+
self.do_debug_printing = self.do_debug_printing || (0 != matches.get_count("debug"));
344+
self.do_input_debug_printing = self
345+
.do_input_debug_printing
346+
.or(Some(matches.get_count("debug") >= 2));
347+
if let Some(value) = self.do_input_debug_printing {
348+
if value {
349+
debug_print_args(&original_args);
350+
self.do_input_debug_printing = Some(false);
351+
}
330352
}
331353

332354
let mut opts = make_options(&matches)?;
@@ -349,7 +371,7 @@ impl EnvAppData {
349371
// no program provided, so just dump all env vars to stdout
350372
print_env(opts.line_ending);
351373
} else {
352-
return self.run_program(opts, do_debug_printing);
374+
return self.run_program(opts, self.do_debug_printing);
353375
}
354376

355377
Ok(())
@@ -361,22 +383,48 @@ impl EnvAppData {
361383
do_debug_printing: bool,
362384
) -> Result<(), Box<dyn UError>> {
363385
let prog = Cow::from(opts.program[0]);
386+
#[cfg(unix)]
387+
let mut arg0 = prog.clone();
388+
#[cfg(not(unix))]
389+
let arg0 = prog.clone();
364390
let args = &opts.program[1..];
365-
if do_debug_printing {
366-
eprintln!("executable: {}", prog.quote());
367-
for (i, arg) in args.iter().enumerate() {
368-
eprintln!("arg[{}]: {}", i, arg.quote());
369-
}
370-
}
371-
// we need to execute a command
372391

373392
/*
374393
* On Unix-like systems Command::status either ends up calling either fork or posix_spawnp
375394
* (which ends up calling clone). Keep using the current process would be ideal, but the
376395
* standard library contains many checks and fail-safes to ensure the process ends up being
377396
* created. This is much simpler than dealing with the hassles of calling execvp directly.
378397
*/
379-
match process::Command::new(&*prog).args(args).status() {
398+
let mut cmd = process::Command::new(&*prog);
399+
cmd.args(args);
400+
401+
if let Some(_argv0) = opts.argv0 {
402+
#[cfg(unix)]
403+
{
404+
cmd.arg0(_argv0);
405+
arg0 = Cow::Borrowed(_argv0);
406+
if do_debug_printing {
407+
eprintln!("argv0: {}", arg0.quote());
408+
}
409+
}
410+
411+
#[cfg(not(unix))]
412+
return Err(USimpleError::new(
413+
2,
414+
"--argv0 is currently not supported on this platform",
415+
));
416+
}
417+
418+
if do_debug_printing {
419+
eprintln!("executing: {}", prog.maybe_quote());
420+
let arg_prefix = " arg";
421+
eprintln!("{}[{}]= {}", arg_prefix, 0, arg0.quote());
422+
for (i, arg) in args.iter().enumerate() {
423+
eprintln!("{}[{}]= {}", arg_prefix, i + 1, arg.quote());
424+
}
425+
}
426+
427+
match cmd.status() {
380428
Ok(exit) if !exit.success() => {
381429
#[cfg(unix)]
382430
if let Some(exit_code) = exit.code() {
@@ -443,6 +491,7 @@ fn make_options(matches: &clap::ArgMatches) -> UResult<Options<'_>> {
443491
Some(v) => v.map(|s| s.as_os_str()).collect(),
444492
None => Vec::with_capacity(0),
445493
};
494+
let argv0 = matches.get_one::<OsString>("argv0").map(|s| s.as_os_str());
446495

447496
let mut opts = Options {
448497
ignore_env,
@@ -452,6 +501,7 @@ fn make_options(matches: &clap::ArgMatches) -> UResult<Options<'_>> {
452501
unsets,
453502
sets: vec![],
454503
program: vec![],
504+
argv0,
455505
};
456506

457507
let mut begin_prog_opts = false;

0 commit comments

Comments
 (0)