-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
720 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
// This file is part of the uutils coreutils package. | ||
// | ||
// For the full copyright and license information, please view the LICENSE | ||
// file that was distributed with this source code. | ||
|
||
// spell-checker:ignore (vars) krate | ||
|
||
use std::env; | ||
use std::fs::File; | ||
use std::io::Write; | ||
use std::path::Path; | ||
|
||
pub fn main() { | ||
if let Ok(profile) = env::var("PROFILE") { | ||
println!("cargo:rustc-cfg=build={profile:?}"); | ||
} | ||
|
||
const ENV_FEATURE_PREFIX: &str = "CARGO_FEATURE_"; | ||
const FEATURE_PREFIX: &str = "feat_"; | ||
const OVERRIDE_PREFIX: &str = "uu_"; | ||
|
||
let out_dir = env::var("OUT_DIR").unwrap(); | ||
|
||
let mut crates = Vec::new(); | ||
for (key, val) in env::vars() { | ||
if val == "1" && key.starts_with(ENV_FEATURE_PREFIX) { | ||
let krate = key[ENV_FEATURE_PREFIX.len()..].to_lowercase(); | ||
// Allow this as we have a bunch of info in the comments | ||
#[allow(clippy::match_same_arms)] | ||
match krate.as_ref() { | ||
"default" | "macos" | "unix" | "windows" | "selinux" | "zip" => continue, // common/standard feature names | ||
"nightly" | "test_unimplemented" => continue, // crate-local custom features | ||
"uudoc" => continue, // is not a utility | ||
"test" => continue, // over-ridden with 'uu_test' to avoid collision with rust core crate 'test' | ||
s if s.starts_with(FEATURE_PREFIX) => continue, // crate feature sets | ||
_ => {} // util feature name | ||
} | ||
crates.push(krate); | ||
} | ||
} | ||
crates.sort(); | ||
|
||
let mut mf = File::create(Path::new(&out_dir).join("uutils_map.rs")).unwrap(); | ||
|
||
mf.write_all( | ||
"type UtilityMap<T> = phf::OrderedMap<&'static str, (fn(T) -> i32, fn() -> Command)>;\n\ | ||
\n\ | ||
#[allow(clippy::too_many_lines)] | ||
fn util_map<T: uucore::Args>() -> UtilityMap<T> {\n" | ||
.as_bytes(), | ||
) | ||
.unwrap(); | ||
|
||
let mut phf_map = phf_codegen::OrderedMap::<&str>::new(); | ||
for krate in &crates { | ||
let map_value = format!("({krate}::uumain, {krate}::uu_app)"); | ||
match krate.as_ref() { | ||
// 'test' is named uu_test to avoid collision with rust core crate 'test'. | ||
// It can also be invoked by name '[' for the '[ expr ] syntax'. | ||
"uu_test" => { | ||
phf_map.entry("test", &map_value); | ||
phf_map.entry("[", &map_value); | ||
} | ||
k if k.starts_with(OVERRIDE_PREFIX) => { | ||
phf_map.entry(&k[OVERRIDE_PREFIX.len()..], &map_value); | ||
} | ||
"false" | "true" => { | ||
phf_map.entry(krate, &format!("(r#{krate}::uumain, r#{krate}::uu_app)")); | ||
} | ||
"hashsum" => { | ||
phf_map.entry(krate, &format!("({krate}::uumain, {krate}::uu_app_custom)")); | ||
|
||
let map_value = format!("({krate}::uumain, {krate}::uu_app_common)"); | ||
let map_value_bits = format!("({krate}::uumain, {krate}::uu_app_bits)"); | ||
let map_value_b3sum = format!("({krate}::uumain, {krate}::uu_app_b3sum)"); | ||
phf_map.entry("md5sum", &map_value); | ||
phf_map.entry("sha1sum", &map_value); | ||
phf_map.entry("sha224sum", &map_value); | ||
phf_map.entry("sha256sum", &map_value); | ||
phf_map.entry("sha384sum", &map_value); | ||
phf_map.entry("sha512sum", &map_value); | ||
phf_map.entry("sha3sum", &map_value_bits); | ||
phf_map.entry("sha3-224sum", &map_value); | ||
phf_map.entry("sha3-256sum", &map_value); | ||
phf_map.entry("sha3-384sum", &map_value); | ||
phf_map.entry("sha3-512sum", &map_value); | ||
phf_map.entry("shake128sum", &map_value_bits); | ||
phf_map.entry("shake256sum", &map_value_bits); | ||
phf_map.entry("b2sum", &map_value); | ||
phf_map.entry("b3sum", &map_value_b3sum); | ||
} | ||
_ => { | ||
phf_map.entry(krate, &map_value); | ||
} | ||
} | ||
} | ||
write!(mf, "{}", phf_map.build()).unwrap(); | ||
mf.write_all(b"\n}\n").unwrap(); | ||
|
||
mf.flush().unwrap(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,224 @@ | ||
// This file is part of the uutils coreutils package. | ||
// | ||
// For the full copyright and license information, please view the LICENSE | ||
// file that was distributed with this source code. | ||
|
||
// spell-checker:ignore manpages mangen | ||
|
||
use clap::{Arg, Command}; | ||
use clap_complete::Shell; | ||
use std::cmp; | ||
use std::ffi::OsStr; | ||
use std::ffi::OsString; | ||
use std::io::{self, Write}; | ||
use std::path::{Path, PathBuf}; | ||
use std::process; | ||
use uucore::display::Quotable; | ||
|
||
const VERSION: &str = env!("CARGO_PKG_VERSION"); | ||
|
||
include!(concat!(env!("OUT_DIR"), "/uutils_map.rs")); | ||
|
||
fn usage<T>(utils: &UtilityMap<T>, name: &str) { | ||
println!("{name} {VERSION} (multi-call binary)\n"); | ||
println!("Usage: {name} [function [arguments...]]\n"); | ||
println!("Currently defined functions:\n"); | ||
#[allow(clippy::map_clone)] | ||
let mut utils: Vec<&str> = utils.keys().map(|&s| s).collect(); | ||
utils.sort_unstable(); | ||
let display_list = utils.join(", "); | ||
let width = cmp::min(textwrap::termwidth(), 100) - 4 * 2; // (opinion/heuristic) max 100 chars wide with 4 character side indentions | ||
println!( | ||
"{}", | ||
textwrap::indent(&textwrap::fill(&display_list, width), " ") | ||
); | ||
} | ||
|
||
fn binary_path(args: &mut impl Iterator<Item = OsString>) -> PathBuf { | ||
match args.next() { | ||
Some(ref s) if !s.is_empty() => PathBuf::from(s), | ||
_ => std::env::current_exe().unwrap(), | ||
} | ||
} | ||
|
||
fn name(binary_path: &Path) -> Option<&str> { | ||
binary_path.file_stem()?.to_str() | ||
} | ||
|
||
#[allow(clippy::cognitive_complexity)] | ||
fn main() { | ||
uucore::panic::mute_sigpipe_panic(); | ||
|
||
let utils = util_map(); | ||
let mut args = uucore::args_os(); | ||
|
||
let binary = binary_path(&mut args); | ||
let binary_as_util = name(&binary).unwrap_or_else(|| { | ||
usage(&utils, "<unknown binary name>"); | ||
process::exit(0); | ||
}); | ||
|
||
// binary name equals util name? | ||
if let Some(&(uumain, _)) = utils.get(binary_as_util) { | ||
process::exit(uumain((vec![binary.into()].into_iter()).chain(args))); | ||
} | ||
|
||
// binary name equals prefixed util name? | ||
// * prefix/stem may be any string ending in a non-alphanumeric character | ||
let util_name = if let Some(util) = utils.keys().find(|util| { | ||
binary_as_util.ends_with(*util) | ||
&& !binary_as_util[..binary_as_util.len() - (*util).len()] | ||
.ends_with(char::is_alphanumeric) | ||
}) { | ||
// prefixed util => replace 0th (aka, executable name) argument | ||
Some(OsString::from(*util)) | ||
} else { | ||
// unmatched binary name => regard as multi-binary container and advance argument list | ||
uucore::set_utility_is_second_arg(); | ||
args.next() | ||
}; | ||
|
||
// 0th argument equals util name? | ||
if let Some(util_os) = util_name { | ||
fn not_found(util: &OsStr) -> ! { | ||
println!("{}: function/utility not found", util.maybe_quote()); | ||
process::exit(1); | ||
} | ||
|
||
let util = match util_os.to_str() { | ||
Some(util) => util, | ||
None => not_found(&util_os), | ||
}; | ||
|
||
if util == "completion" { | ||
gen_completions(args, &utils); | ||
} | ||
|
||
if util == "manpage" { | ||
gen_manpage(args, &utils); | ||
} | ||
|
||
match utils.get(util) { | ||
Some(&(uumain, _)) => { | ||
process::exit(uumain((vec![util_os].into_iter()).chain(args))); | ||
} | ||
None => { | ||
if util == "--help" || util == "-h" { | ||
// see if they want help on a specific util | ||
if let Some(util_os) = args.next() { | ||
let util = match util_os.to_str() { | ||
Some(util) => util, | ||
None => not_found(&util_os), | ||
}; | ||
|
||
match utils.get(util) { | ||
Some(&(uumain, _)) => { | ||
let code = uumain( | ||
(vec![util_os, OsString::from("--help")].into_iter()) | ||
.chain(args), | ||
); | ||
io::stdout().flush().expect("could not flush stdout"); | ||
process::exit(code); | ||
} | ||
None => not_found(&util_os), | ||
} | ||
} | ||
usage(&utils, binary_as_util); | ||
process::exit(0); | ||
} else { | ||
not_found(&util_os); | ||
} | ||
} | ||
} | ||
} else { | ||
// no arguments provided | ||
usage(&utils, binary_as_util); | ||
process::exit(0); | ||
} | ||
} | ||
|
||
/// Prints completions for the utility in the first parameter for the shell in the second parameter to stdout | ||
fn gen_completions<T: uucore::Args>( | ||
args: impl Iterator<Item = OsString>, | ||
util_map: &UtilityMap<T>, | ||
) -> ! { | ||
let all_utilities: Vec<_> = std::iter::once("coreutils") | ||
.chain(util_map.keys().copied()) | ||
.collect(); | ||
|
||
let matches = Command::new("completion") | ||
.about("Prints completions to stdout") | ||
.arg( | ||
Arg::new("utility") | ||
.value_parser(clap::builder::PossibleValuesParser::new(all_utilities)) | ||
.required(true), | ||
) | ||
.arg( | ||
Arg::new("shell") | ||
.value_parser(clap::builder::EnumValueParser::<Shell>::new()) | ||
.required(true), | ||
) | ||
.get_matches_from(std::iter::once(OsString::from("completion")).chain(args)); | ||
|
||
let utility = matches.get_one::<String>("utility").unwrap(); | ||
let shell = *matches.get_one::<Shell>("shell").unwrap(); | ||
|
||
let mut command = if utility == "coreutils" { | ||
gen_coreutils_app(util_map) | ||
} else { | ||
util_map.get(utility).unwrap().1() | ||
}; | ||
let bin_name = std::env::var("PROG_PREFIX").unwrap_or_default() + utility; | ||
|
||
clap_complete::generate(shell, &mut command, bin_name, &mut io::stdout()); | ||
io::stdout().flush().unwrap(); | ||
process::exit(0); | ||
} | ||
|
||
/// Generate the manpage for the utility in the first parameter | ||
fn gen_manpage<T: uucore::Args>( | ||
args: impl Iterator<Item = OsString>, | ||
util_map: &UtilityMap<T>, | ||
) -> ! { | ||
let all_utilities: Vec<_> = std::iter::once("coreutils") | ||
.chain(util_map.keys().copied()) | ||
.collect(); | ||
|
||
let matches = Command::new("manpage") | ||
.about("Prints manpage to stdout") | ||
.arg( | ||
Arg::new("utility") | ||
.value_parser(clap::builder::PossibleValuesParser::new(all_utilities)) | ||
.required(true), | ||
) | ||
.get_matches_from(std::iter::once(OsString::from("manpage")).chain(args)); | ||
|
||
let utility = matches.get_one::<String>("utility").unwrap(); | ||
|
||
let command = if utility == "coreutils" { | ||
gen_coreutils_app(util_map) | ||
} else { | ||
util_map.get(utility).unwrap().1() | ||
}; | ||
|
||
let man = clap_mangen::Man::new(command); | ||
man.render(&mut io::stdout()) | ||
.expect("Man page generation failed"); | ||
io::stdout().flush().unwrap(); | ||
process::exit(0); | ||
} | ||
|
||
fn gen_coreutils_app<T: uucore::Args>(util_map: &UtilityMap<T>) -> Command { | ||
let mut command = Command::new("coreutils"); | ||
for (name, (_, sub_app)) in util_map { | ||
// Recreate a small subcommand with only the relevant info | ||
// (name & short description) | ||
let about = sub_app() | ||
.get_about() | ||
.expect("Could not get the 'about'") | ||
.to_string(); | ||
let sub_app = Command::new(name).about(about); | ||
command = command.subcommand(sub_app); | ||
} | ||
command | ||
} |
Oops, something went wrong.