mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-27 19:17:43 +00:00
env: ignore flags after the command or -- argument
This commit is contained in:
parent
d68e288602
commit
814e82ea4e
2 changed files with 120 additions and 22 deletions
87
src/uu/env/src/env.rs
vendored
87
src/uu/env/src/env.rs
vendored
|
@ -78,6 +78,18 @@ const ABOUT: &str = help_about!("env.md");
|
|||
const USAGE: &str = help_usage!("env.md");
|
||||
const AFTER_HELP: &str = help_section!("after help", "env.md");
|
||||
|
||||
mod options {
|
||||
pub const IGNORE_ENVIRONMENT: &str = "ignore-environment";
|
||||
pub const CHDIR: &str = "chdir";
|
||||
pub const NULL: &str = "null";
|
||||
pub const FILE: &str = "file";
|
||||
pub const UNSET: &str = "unset";
|
||||
pub const DEBUG: &str = "debug";
|
||||
pub const SPLIT_STRING: &str = "split-string";
|
||||
pub const ARGV0: &str = "argv0";
|
||||
pub const IGNORE_SIGNAL: &str = "ignore-signal";
|
||||
}
|
||||
|
||||
const ERROR_MSG_S_SHEBANG: &str = "use -[v]S to pass options in shebang lines";
|
||||
|
||||
struct Options<'a> {
|
||||
|
@ -216,16 +228,16 @@ pub fn uu_app() -> Command {
|
|||
.infer_long_args(true)
|
||||
.trailing_var_arg(true)
|
||||
.arg(
|
||||
Arg::new("ignore-environment")
|
||||
Arg::new(options::IGNORE_ENVIRONMENT)
|
||||
.short('i')
|
||||
.long("ignore-environment")
|
||||
.long(options::IGNORE_ENVIRONMENT)
|
||||
.help("start with an empty environment")
|
||||
.action(ArgAction::SetTrue),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("chdir")
|
||||
Arg::new(options::CHDIR)
|
||||
.short('C') // GNU env compatibility
|
||||
.long("chdir")
|
||||
.long(options::CHDIR)
|
||||
.number_of_values(1)
|
||||
.value_name("DIR")
|
||||
.value_parser(ValueParser::os_string())
|
||||
|
@ -233,9 +245,9 @@ pub fn uu_app() -> Command {
|
|||
.help("change working directory to DIR"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("null")
|
||||
Arg::new(options::NULL)
|
||||
.short('0')
|
||||
.long("null")
|
||||
.long(options::NULL)
|
||||
.help(
|
||||
"end each output line with a 0 byte rather than a newline (only \
|
||||
valid when printing the environment)",
|
||||
|
@ -243,9 +255,9 @@ pub fn uu_app() -> Command {
|
|||
.action(ArgAction::SetTrue),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("file")
|
||||
Arg::new(options::FILE)
|
||||
.short('f')
|
||||
.long("file")
|
||||
.long(options::FILE)
|
||||
.value_name("PATH")
|
||||
.value_hint(clap::ValueHint::FilePath)
|
||||
.value_parser(ValueParser::os_string())
|
||||
|
@ -256,34 +268,34 @@ pub fn uu_app() -> Command {
|
|||
),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("unset")
|
||||
Arg::new(options::UNSET)
|
||||
.short('u')
|
||||
.long("unset")
|
||||
.long(options::UNSET)
|
||||
.value_name("NAME")
|
||||
.action(ArgAction::Append)
|
||||
.value_parser(ValueParser::os_string())
|
||||
.help("remove variable from the environment"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("debug")
|
||||
Arg::new(options::DEBUG)
|
||||
.short('v')
|
||||
.long("debug")
|
||||
.long(options::DEBUG)
|
||||
.action(ArgAction::Count)
|
||||
.help("print verbose information for each processing step"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("split-string") // split string handling is implemented directly, not using CLAP. But this entry here is needed for the help information output.
|
||||
Arg::new(options::SPLIT_STRING) // split string handling is implemented directly, not using CLAP. But this entry here is needed for the help information output.
|
||||
.short('S')
|
||||
.long("split-string")
|
||||
.long(options::SPLIT_STRING)
|
||||
.value_name("S")
|
||||
.action(ArgAction::Set)
|
||||
.value_parser(ValueParser::os_string())
|
||||
.help("process and split S into separate arguments; used to pass multiple arguments on shebang lines")
|
||||
).arg(
|
||||
Arg::new("argv0")
|
||||
.overrides_with("argv0")
|
||||
Arg::new(options::ARGV0)
|
||||
.overrides_with(options::ARGV0)
|
||||
.short('a')
|
||||
.long("argv0")
|
||||
.long(options::ARGV0)
|
||||
.value_name("a")
|
||||
.action(ArgAction::Set)
|
||||
.value_parser(ValueParser::os_string())
|
||||
|
@ -296,8 +308,8 @@ pub fn uu_app() -> Command {
|
|||
.value_parser(ValueParser::os_string())
|
||||
)
|
||||
.arg(
|
||||
Arg::new("ignore-signal")
|
||||
.long("ignore-signal")
|
||||
Arg::new(options::IGNORE_SIGNAL)
|
||||
.long(options::IGNORE_SIGNAL)
|
||||
.value_name("SIG")
|
||||
.action(ArgAction::Append)
|
||||
.value_parser(ValueParser::os_string())
|
||||
|
@ -387,7 +399,31 @@ impl EnvAppData {
|
|||
original_args: &Vec<OsString>,
|
||||
) -> UResult<Vec<OsString>> {
|
||||
let mut all_args: Vec<OsString> = Vec::new();
|
||||
for arg in original_args {
|
||||
let mut process_flags = true;
|
||||
let mut expecting_arg = false;
|
||||
// Leave out split-string since it's a special case below
|
||||
let flags_with_args = [
|
||||
options::ARGV0,
|
||||
options::CHDIR,
|
||||
options::FILE,
|
||||
options::IGNORE_SIGNAL,
|
||||
options::UNSET,
|
||||
];
|
||||
let short_flags_with_args = ['a', 'C', 'f', 'u'];
|
||||
for (n, arg) in original_args.iter().enumerate() {
|
||||
let arg_str = arg.to_string_lossy();
|
||||
// Stop processing env flags once we reach the command or -- argument
|
||||
if 0 < n
|
||||
&& !expecting_arg
|
||||
&& (arg == "--" || !(arg_str.starts_with('-') || arg_str.contains('=')))
|
||||
{
|
||||
process_flags = false;
|
||||
}
|
||||
if !process_flags {
|
||||
all_args.push(arg.clone());
|
||||
continue;
|
||||
}
|
||||
expecting_arg = false;
|
||||
match arg {
|
||||
b if check_and_handle_string_args(b, "--split-string", &mut all_args, None)? => {
|
||||
self.had_string_argument = true;
|
||||
|
@ -411,8 +447,15 @@ impl EnvAppData {
|
|||
self.had_string_argument = true;
|
||||
}
|
||||
_ => {
|
||||
let arg_str = arg.to_string_lossy();
|
||||
|
||||
if let Some(flag) = arg_str.strip_prefix("--") {
|
||||
if flags_with_args.contains(&flag) {
|
||||
expecting_arg = true;
|
||||
}
|
||||
} else if let Some(flag) = arg_str.strip_prefix("-") {
|
||||
for c in flag.chars() {
|
||||
expecting_arg = short_flags_with_args.contains(&c);
|
||||
}
|
||||
}
|
||||
// Short unset option (-u) is not allowed to contain '='
|
||||
if arg_str.contains('=')
|
||||
&& arg_str.starts_with("-u")
|
||||
|
|
|
@ -66,6 +66,61 @@ fn test_invalid_arg() {
|
|||
new_ucmd!().arg("--definitely-invalid").fails_with_code(125);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
fn test_flags_after_command() {
|
||||
new_ucmd!()
|
||||
// This would cause an error if -u=v were processed because it's malformed
|
||||
.args(&["echo", "-u=v"])
|
||||
.succeeds()
|
||||
.no_stderr()
|
||||
.stdout_is("-u=v\n");
|
||||
|
||||
new_ucmd!()
|
||||
// Ensure the string isn't split
|
||||
// cSpell:disable
|
||||
.args(&["printf", "%s-%s", "-Sfoo bar"])
|
||||
.succeeds()
|
||||
.no_stderr()
|
||||
.stdout_is("-Sfoo bar-");
|
||||
// cSpell:enable
|
||||
|
||||
new_ucmd!()
|
||||
// Ensure -- is recognized
|
||||
.args(&["-i", "--", "-u=v"])
|
||||
.succeeds()
|
||||
.no_stderr()
|
||||
.stdout_is("-u=v\n");
|
||||
|
||||
new_ucmd!()
|
||||
// Recognize echo as the command after a flag that takes a value
|
||||
.args(&["-C", "..", "echo", "-u=v"])
|
||||
.succeeds()
|
||||
.no_stderr()
|
||||
.stdout_is("-u=v\n");
|
||||
|
||||
new_ucmd!()
|
||||
// Recognize echo as the command after a flag that takes an inline value
|
||||
.args(&["-C..", "echo", "-u=v"])
|
||||
.succeeds()
|
||||
.no_stderr()
|
||||
.stdout_is("-u=v\n");
|
||||
|
||||
new_ucmd!()
|
||||
// Recognize echo as the command after a flag that takes a value after another flag
|
||||
.args(&["-iC", "..", "echo", "-u=v"])
|
||||
.succeeds()
|
||||
.no_stderr()
|
||||
.stdout_is("-u=v\n");
|
||||
|
||||
new_ucmd!()
|
||||
// Similar to the last two combined
|
||||
.args(&["-iC..", "echo", "-u=v"])
|
||||
.succeeds()
|
||||
.no_stderr()
|
||||
.stdout_is("-u=v\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_env_help() {
|
||||
new_ucmd!()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue