mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-27 11:07:44 +00:00
refactor/uucore ~ correct implementation of executable!() for multicall
- Use an atomic bool to track whether the utility name is the second or the first argument. - Add tests
This commit is contained in:
parent
38f3d13f7f
commit
44981cab01
6 changed files with 117 additions and 43 deletions
|
@ -70,6 +70,7 @@ fn main() {
|
||||||
Some(OsString::from(*util))
|
Some(OsString::from(*util))
|
||||||
} else {
|
} else {
|
||||||
// unmatched binary name => regard as multi-binary container and advance argument list
|
// unmatched binary name => regard as multi-binary container and advance argument list
|
||||||
|
uucore::set_utility_is_second_arg();
|
||||||
args.next()
|
args.next()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
let name = util_name!();
|
let name = util_name!();
|
||||||
|
|
||||||
let config_result: Result<base_common::Config, String> =
|
let config_result: Result<base_common::Config, String> =
|
||||||
base_common::parse_base_cmd_args(args, name, VERSION, ABOUT, &usage);
|
base_common::parse_base_cmd_args(args, &name, VERSION, ABOUT, &usage);
|
||||||
let config = config_result.unwrap_or_else(|s| crash!(BASE_CMD_PARSE_ERROR, "{}", s));
|
let config = config_result.unwrap_or_else(|s| crash!(BASE_CMD_PARSE_ERROR, "{}", s));
|
||||||
|
|
||||||
// Create a reference to stdin so we can return a locked stdin from
|
// Create a reference to stdin so we can return a locked stdin from
|
||||||
|
@ -52,12 +52,12 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
config.wrap_cols,
|
config.wrap_cols,
|
||||||
config.ignore_garbage,
|
config.ignore_garbage,
|
||||||
config.decode,
|
config.decode,
|
||||||
name,
|
&name,
|
||||||
);
|
);
|
||||||
|
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn uu_app() -> App<'static, 'static> {
|
pub fn uu_app() -> App<'static, 'static> {
|
||||||
base_common::base_app(util_name!(), VERSION, ABOUT)
|
base_common::base_app(&util_name!(), VERSION, ABOUT)
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
let usage = usage();
|
let usage = usage();
|
||||||
let name = util_name!();
|
let name = util_name!();
|
||||||
let config_result: Result<base_common::Config, String> =
|
let config_result: Result<base_common::Config, String> =
|
||||||
base_common::parse_base_cmd_args(args, name, VERSION, ABOUT, &usage);
|
base_common::parse_base_cmd_args(args, &name, VERSION, ABOUT, &usage);
|
||||||
let config = config_result.unwrap_or_else(|s| crash!(BASE_CMD_PARSE_ERROR, "{}", s));
|
let config = config_result.unwrap_or_else(|s| crash!(BASE_CMD_PARSE_ERROR, "{}", s));
|
||||||
|
|
||||||
// Create a reference to stdin so we can return a locked stdin from
|
// Create a reference to stdin so we can return a locked stdin from
|
||||||
|
@ -52,7 +52,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
config.wrap_cols,
|
config.wrap_cols,
|
||||||
config.ignore_garbage,
|
config.ignore_garbage,
|
||||||
config.decode,
|
config.decode,
|
||||||
name,
|
&name,
|
||||||
);
|
);
|
||||||
|
|
||||||
0
|
0
|
||||||
|
|
|
@ -77,6 +77,15 @@ pub use crate::features::wide;
|
||||||
//## core functions
|
//## core functions
|
||||||
|
|
||||||
use std::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
|
pub fn get_utility_is_second_arg() -> bool {
|
||||||
|
crate::macros::UTILITY_IS_SECOND_ARG.load(Ordering::SeqCst)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_utility_is_second_arg() {
|
||||||
|
crate::macros::UTILITY_IS_SECOND_ARG.store(true, Ordering::SeqCst)
|
||||||
|
}
|
||||||
|
|
||||||
pub enum InvalidEncodingHandling {
|
pub enum InvalidEncodingHandling {
|
||||||
Ignore,
|
Ignore,
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::sync::atomic::AtomicBool;
|
||||||
|
|
||||||
// This file is part of the uutils coreutils package.
|
// This file is part of the uutils coreutils package.
|
||||||
//
|
//
|
||||||
// (c) Alex Lyon <arcterus@mail.com>
|
// (c) Alex Lyon <arcterus@mail.com>
|
||||||
|
@ -5,40 +7,22 @@
|
||||||
// For the full copyright and license information, please view the LICENSE
|
// For the full copyright and license information, please view the LICENSE
|
||||||
// file that was distributed with this source code.
|
// file that was distributed with this source code.
|
||||||
|
|
||||||
|
/// Whether we were called as a multicall binary ("coreutils <utility>")
|
||||||
|
pub static UTILITY_IS_SECOND_ARG: AtomicBool = AtomicBool::new(false);
|
||||||
|
|
||||||
/// Get the executable path (as `OsString`).
|
/// Get the executable path (as `OsString`).
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! executable_os(
|
macro_rules! executable_os(
|
||||||
() => ({
|
() => ({
|
||||||
&uucore::args_os().next().unwrap()
|
$crate::args_os().next().unwrap()
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Get the executable path (as `String`; lossless).
|
/// Get the executable path (as `String`).
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! executable(
|
macro_rules! executable(
|
||||||
() => ({
|
() => ({
|
||||||
let exe = match $crate::executable_os!().to_str() {
|
$crate::executable_os!().to_string_lossy().to_string()
|
||||||
// * UTF-8
|
|
||||||
Some(s) => s.to_string(),
|
|
||||||
// * "lossless" debug format (if/when `executable_os!()` is not well-formed UTF-8)
|
|
||||||
None => format!("{:?}", $crate::executable_os!())
|
|
||||||
};
|
|
||||||
&exe.to_owned()
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Get the executable name.
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! executable_name(
|
|
||||||
() => ({
|
|
||||||
let stem = &std::path::Path::new($crate::executable_os!()).file_stem().unwrap().to_owned();
|
|
||||||
let exe = match stem.to_str() {
|
|
||||||
// * UTF-8
|
|
||||||
Some(s) => s.to_string(),
|
|
||||||
// * "lossless" debug format (if/when `executable_os!()` is not well-formed UTF-8)
|
|
||||||
None => format!("{:?}", stem)
|
|
||||||
};
|
|
||||||
&exe.to_owned()
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -46,13 +30,11 @@ macro_rules! executable_name(
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! util_name(
|
macro_rules! util_name(
|
||||||
() => ({
|
() => ({
|
||||||
let crate_name = env!("CARGO_PKG_NAME");
|
if $crate::get_utility_is_second_arg() {
|
||||||
let name = if crate_name.starts_with("uu_") {
|
$crate::args_os().nth(1).unwrap().to_string_lossy().to_string()
|
||||||
&crate_name[3..]
|
|
||||||
} else {
|
} else {
|
||||||
crate_name
|
$crate::executable!()
|
||||||
};
|
}
|
||||||
&name.to_owned()
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -62,14 +44,15 @@ macro_rules! util_name(
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! execution_phrase(
|
macro_rules! execution_phrase(
|
||||||
() => ({
|
() => ({
|
||||||
let exe = if (executable_name!() == util_name!()) {
|
if $crate::get_utility_is_second_arg() {
|
||||||
executable!().to_string()
|
$crate::args_os()
|
||||||
} else {
|
.take(2)
|
||||||
format!("{} {}", executable!(), util_name!())
|
.map(|os_str| os_str.to_string_lossy().to_string())
|
||||||
.as_str()
|
.collect::<Vec<_>>()
|
||||||
.to_owned()
|
.join(" ")
|
||||||
};
|
} else {
|
||||||
&exe.to_owned()
|
$crate::executable!()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
81
tests/test_util_name.rs
Normal file
81
tests/test_util_name.rs
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
mod common;
|
||||||
|
|
||||||
|
use common::util::TestScenario;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(feature = "ls")]
|
||||||
|
fn execution_phrase_double() {
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
let scenario = TestScenario::new("ls");
|
||||||
|
let output = Command::new(&scenario.bin_path)
|
||||||
|
.arg("ls")
|
||||||
|
.arg("--some-invalid-arg")
|
||||||
|
.output()
|
||||||
|
.unwrap();
|
||||||
|
assert!(String::from_utf8(output.stderr)
|
||||||
|
.unwrap()
|
||||||
|
.contains(&format!("USAGE:\n {} ls", scenario.bin_path.display(),)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(feature = "ls")]
|
||||||
|
fn execution_phrase_single() {
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
let scenario = TestScenario::new("ls");
|
||||||
|
std::fs::copy(scenario.bin_path, scenario.fixtures.plus("uu-ls")).unwrap();
|
||||||
|
let output = Command::new(scenario.fixtures.plus("uu-ls"))
|
||||||
|
.arg("--some-invalid-arg")
|
||||||
|
.output()
|
||||||
|
.unwrap();
|
||||||
|
assert!(String::from_utf8(output.stderr).unwrap().contains(&format!(
|
||||||
|
"USAGE:\n {}",
|
||||||
|
scenario.fixtures.plus("uu-ls").display()
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(feature = "sort")]
|
||||||
|
fn util_name_double() {
|
||||||
|
use std::{
|
||||||
|
io::Write,
|
||||||
|
process::{Command, Stdio},
|
||||||
|
};
|
||||||
|
|
||||||
|
let scenario = TestScenario::new("sort");
|
||||||
|
let mut child = Command::new(&scenario.bin_path)
|
||||||
|
.arg("sort")
|
||||||
|
.stdin(Stdio::piped())
|
||||||
|
.stderr(Stdio::piped())
|
||||||
|
.spawn()
|
||||||
|
.unwrap();
|
||||||
|
// input invalid utf8 to cause an error
|
||||||
|
child.stdin.take().unwrap().write_all(&[255]).unwrap();
|
||||||
|
let output = child.wait_with_output().unwrap();
|
||||||
|
assert!(String::from_utf8(output.stderr).unwrap().contains("sort: "));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(feature = "sort")]
|
||||||
|
fn util_name_single() {
|
||||||
|
use std::{
|
||||||
|
io::Write,
|
||||||
|
process::{Command, Stdio},
|
||||||
|
};
|
||||||
|
|
||||||
|
let scenario = TestScenario::new("sort");
|
||||||
|
std::fs::copy(scenario.bin_path, scenario.fixtures.plus("uu-sort")).unwrap();
|
||||||
|
let mut child = Command::new(scenario.fixtures.plus("uu-sort"))
|
||||||
|
.stdin(Stdio::piped())
|
||||||
|
.stderr(Stdio::piped())
|
||||||
|
.spawn()
|
||||||
|
.unwrap();
|
||||||
|
// input invalid utf8 to cause an error
|
||||||
|
child.stdin.take().unwrap().write_all(&[255]).unwrap();
|
||||||
|
let output = child.wait_with_output().unwrap();
|
||||||
|
assert!(String::from_utf8(output.stderr).unwrap().contains(&format!(
|
||||||
|
"{}: ",
|
||||||
|
scenario.fixtures.plus("uu-sort").display()
|
||||||
|
)));
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue