Merge pull request #2414 from miDeb/completions2

uutils: implement shell completions
This commit is contained in:
Sylvestre Ledru
2021-06-26 21:26:23 +02:00
committed by GitHub
110 changed files with 3684 additions and 3280 deletions
Generated
+8
View File
@@ -218,6 +218,7 @@ version = "0.0.6"
dependencies = [
"atty",
"chrono",
"clap",
"conv",
"filetime",
"glob 0.3.0",
@@ -1959,6 +1960,7 @@ dependencies = [
name = "uu_expr"
version = "0.0.6"
dependencies = [
"clap",
"libc",
"num-bigint",
"num-traits",
@@ -1986,6 +1988,7 @@ dependencies = [
name = "uu_false"
version = "0.0.6"
dependencies = [
"clap",
"uucore",
"uucore_procs",
]
@@ -2051,6 +2054,7 @@ dependencies = [
name = "uu_hostid"
version = "0.0.6"
dependencies = [
"clap",
"libc",
"uucore",
"uucore_procs",
@@ -2327,6 +2331,7 @@ name = "uu_pr"
version = "0.0.6"
dependencies = [
"chrono",
"clap",
"getopts",
"itertools 0.10.0",
"quick-error 2.0.1",
@@ -2349,6 +2354,7 @@ dependencies = [
name = "uu_printf"
version = "0.0.6"
dependencies = [
"clap",
"itertools 0.8.2",
"uucore",
"uucore_procs",
@@ -2585,6 +2591,7 @@ dependencies = [
name = "uu_test"
version = "0.0.6"
dependencies = [
"clap",
"libc",
"redox_syscall 0.1.57",
"uucore",
@@ -2628,6 +2635,7 @@ dependencies = [
name = "uu_true"
version = "0.0.6"
dependencies = [
"clap",
"uucore",
"uucore_procs",
]
+1
View File
@@ -225,6 +225,7 @@ test = [ "uu_test" ]
[workspace]
[dependencies]
clap = "2.33.3"
lazy_static = { version="1.3" }
textwrap = { version="=0.11.0", features=["term_size"] } # !maint: [2020-05-10; rivy] unstable crate using undocumented features; pinned currently, will review
uucore = { version=">=0.0.8", package="uucore", path="src/uucore" }
+8
View File
@@ -314,6 +314,11 @@ else
endif
$(foreach man, $(filter $(INSTALLEES), $(basename $(notdir $(wildcard $(DOCSDIR)/_build/man/*)))), \
cat $(DOCSDIR)/_build/man/$(man).1 | gzip > $(INSTALLDIR_MAN)/$(PROG_PREFIX)$(man).1.gz &&) :
$(foreach prog, $(INSTALLEES), \
$(BUILDDIR)/coreutils completion $(prog) zsh > $(DESTDIR)$(PREFIX)/share/zsh/site-functions/_$(PROG_PREFIX)$(prog); \
$(BUILDDIR)/coreutils completion $(prog) bash > $(DESTDIR)$(PREFIX)/share/bash-completion/completions/$(PROG_PREFIX)$(prog); \
$(BUILDDIR)/coreutils completion $(prog) fish > $(DESTDIR)$(PREFIX)/share/fish/vendor_completions.d/$(PROG_PREFIX)$(prog).fish; \
)
uninstall:
ifeq (${MULTICALL}, y)
@@ -321,6 +326,9 @@ ifeq (${MULTICALL}, y)
endif
rm -f $(addprefix $(INSTALLDIR_MAN)/,$(PROG_PREFIX)coreutils.1.gz)
rm -f $(addprefix $(INSTALLDIR_BIN)/$(PROG_PREFIX),$(PROGS))
rm -f $(addprefix $(DESTDIR)$(PREFIX)/share/zsh/site-functions/_$(PROG_PREFIX),$(PROGS))
rm -f $(addprefix $(DESTDIR)$(PREFIX)/share/bash-completion/completions/$(PROG_PREFIX),$(PROGS))
rm -f $(addprefix $(DESTDIR)$(PREFIX)/share/fish/vendor_completions.d/$(PROG_PREFIX),$(addsuffix .fish,$(PROGS)))
rm -f $(addprefix $(INSTALLDIR_MAN)/$(PROG_PREFIX),$(addsuffix .1.gz,$(PROGS)))
.PHONY: all build build-coreutils build-pkgs build-docs test distclean clean busytest install uninstall
+24
View File
@@ -134,6 +134,9 @@ $ cargo install --path .
This command will install uutils into Cargo's *bin* folder (*e.g.* `$HOME/.cargo/bin`).
This does not install files necessary for shell completion. For shell completion to work,
use `GNU Make` or see `Manually install shell completions`.
### GNU Make
To install all available utilities:
@@ -179,6 +182,10 @@ Set install parent directory (default value is /usr/local):
$ make PREFIX=/my/path install
```
Installing with `make` installs shell completions for all installed utilities
for `bash`, `fish` and `zsh`. Completions for `elvish` and `powershell` can also
be generated; See `Manually install shell completions`.
### NixOS
The [standard package set](https://nixos.org/nixpkgs/manual/) of [NixOS](https://nixos.org/)
@@ -188,6 +195,23 @@ provides this package out of the box since 18.03:
$ nix-env -iA nixos.uutils-coreutils
```
### Manually install shell completions
The `coreutils` binary can generate completions for the `bash`, `elvish`, `fish`, `powershell`
and `zsh` shells. It prints the result to stdout.
The syntax is:
```bash
cargo run completion <utility> <shell>
```
So, to install completions for `ls` on `bash` to `/usr/local/share/bash-completion/completions/ls`,
run:
```bash
cargo run completion ls bash > /usr/local/share/bash-completion/completions/ls
```
## Un-installation Instructions
Un-installation differs depending on how you have installed uutils. If you used
+20 -20
View File
@@ -43,7 +43,7 @@ pub fn main() {
let mut tf = File::create(Path::new(&out_dir).join("test_modules.rs")).unwrap();
mf.write_all(
"type UtilityMap<T> = HashMap<&'static str, fn(T) -> i32>;\n\
"type UtilityMap<T> = HashMap<&'static str, (fn(T) -> i32, fn() -> App<'static, 'static>)>;\n\
\n\
fn util_map<T: uucore::Args>() -> UtilityMap<T> {\n\
\tlet mut map = UtilityMap::new();\n\
@@ -60,8 +60,8 @@ pub fn main() {
mf.write_all(
format!(
"\
\tmap.insert(\"test\", {krate}::uumain);\n\
\t\tmap.insert(\"[\", {krate}::uumain);\n\
\tmap.insert(\"test\", ({krate}::uumain, {krate}::uu_app));\n\
\t\tmap.insert(\"[\", ({krate}::uumain, {krate}::uu_app));\n\
",
krate = krate
)
@@ -80,7 +80,7 @@ pub fn main() {
k if k.starts_with(override_prefix) => {
mf.write_all(
format!(
"\tmap.insert(\"{k}\", {krate}::uumain);\n",
"\tmap.insert(\"{k}\", ({krate}::uumain, {krate}::uu_app));\n",
k = krate[override_prefix.len()..].to_string(),
krate = krate
)
@@ -100,7 +100,7 @@ pub fn main() {
"false" | "true" => {
mf.write_all(
format!(
"\tmap.insert(\"{krate}\", r#{krate}::uumain);\n",
"\tmap.insert(\"{krate}\", (r#{krate}::uumain, r#{krate}::uu_app));\n",
krate = krate
)
.as_bytes(),
@@ -120,20 +120,20 @@ pub fn main() {
mf.write_all(
format!(
"\
\tmap.insert(\"{krate}\", {krate}::uumain);\n\
\t\tmap.insert(\"md5sum\", {krate}::uumain);\n\
\t\tmap.insert(\"sha1sum\", {krate}::uumain);\n\
\t\tmap.insert(\"sha224sum\", {krate}::uumain);\n\
\t\tmap.insert(\"sha256sum\", {krate}::uumain);\n\
\t\tmap.insert(\"sha384sum\", {krate}::uumain);\n\
\t\tmap.insert(\"sha512sum\", {krate}::uumain);\n\
\t\tmap.insert(\"sha3sum\", {krate}::uumain);\n\
\t\tmap.insert(\"sha3-224sum\", {krate}::uumain);\n\
\t\tmap.insert(\"sha3-256sum\", {krate}::uumain);\n\
\t\tmap.insert(\"sha3-384sum\", {krate}::uumain);\n\
\t\tmap.insert(\"sha3-512sum\", {krate}::uumain);\n\
\t\tmap.insert(\"shake128sum\", {krate}::uumain);\n\
\t\tmap.insert(\"shake256sum\", {krate}::uumain);\n\
\tmap.insert(\"{krate}\", ({krate}::uumain, {krate}::uu_app_custom));\n\
\t\tmap.insert(\"md5sum\", ({krate}::uumain, {krate}::uu_app_common));\n\
\t\tmap.insert(\"sha1sum\", ({krate}::uumain, {krate}::uu_app_common));\n\
\t\tmap.insert(\"sha224sum\", ({krate}::uumain, {krate}::uu_app_common));\n\
\t\tmap.insert(\"sha256sum\", ({krate}::uumain, {krate}::uu_app_common));\n\
\t\tmap.insert(\"sha384sum\", ({krate}::uumain, {krate}::uu_app_common));\n\
\t\tmap.insert(\"sha512sum\", ({krate}::uumain, {krate}::uu_app_common));\n\
\t\tmap.insert(\"sha3sum\", ({krate}::uumain, {krate}::uu_app_common));\n\
\t\tmap.insert(\"sha3-224sum\", ({krate}::uumain, {krate}::uu_app_common));\n\
\t\tmap.insert(\"sha3-256sum\", ({krate}::uumain, {krate}::uu_app_common));\n\
\t\tmap.insert(\"sha3-384sum\", ({krate}::uumain, {krate}::uu_app_common));\n\
\t\tmap.insert(\"sha3-512sum\", ({krate}::uumain, {krate}::uu_app_common));\n\
\t\tmap.insert(\"shake128sum\", ({krate}::uumain, {krate}::uu_app_common));\n\
\t\tmap.insert(\"shake256sum\", ({krate}::uumain, {krate}::uu_app_common));\n\
",
krate = krate
)
@@ -153,7 +153,7 @@ pub fn main() {
_ => {
mf.write_all(
format!(
"\tmap.insert(\"{krate}\", {krate}::uumain);\n",
"\tmap.insert(\"{krate}\", ({krate}::uumain, {krate}::uu_app));\n",
krate = krate
)
.as_bytes(),
+49 -3
View File
@@ -5,6 +5,8 @@
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
use clap::App;
use clap::Shell;
use std::cmp;
use std::collections::hash_map::HashMap;
use std::ffi::OsString;
@@ -52,7 +54,7 @@ fn main() {
let binary_as_util = name(&binary);
// binary name equals util name?
if let Some(&uumain) = utils.get(binary_as_util) {
if let Some(&(uumain, _)) = utils.get(binary_as_util) {
process::exit(uumain((vec![binary.into()].into_iter()).chain(args)));
}
@@ -74,8 +76,12 @@ fn main() {
if let Some(util_os) = util_name {
let util = util_os.as_os_str().to_string_lossy();
if util == "completion" {
gen_completions(args, utils);
}
match utils.get(&util[..]) {
Some(&uumain) => {
Some(&(uumain, _)) => {
process::exit(uumain((vec![util_os].into_iter()).chain(args)));
}
None => {
@@ -85,7 +91,7 @@ fn main() {
let util = util_os.as_os_str().to_string_lossy();
match utils.get(&util[..]) {
Some(&uumain) => {
Some(&(uumain, _)) => {
let code = uumain(
(vec![util_os, OsString::from("--help")].into_iter())
.chain(args),
@@ -113,3 +119,43 @@ fn main() {
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>(
mut args: impl Iterator<Item = OsString>,
util_map: UtilityMap<T>,
) -> ! {
let utility = args
.next()
.expect("expected utility as the first parameter")
.to_str()
.expect("utility name was not valid utf-8")
.to_owned();
let shell = args
.next()
.expect("expected shell as the second parameter")
.to_str()
.expect("shell name was not valid utf-8")
.to_owned();
let mut app = if utility == "coreutils" {
gen_coreutils_app(util_map)
} else if let Some((_, app)) = util_map.get(utility.as_str()) {
app()
} else {
eprintln!("{} is not a valid utility", utility);
process::exit(1)
};
let shell: Shell = shell.parse().unwrap();
let bin_name = std::env::var("PROG_PREFIX").unwrap_or_default() + &utility;
app.gen_completions_to(bin_name, shell, &mut io::stdout());
io::stdout().flush().unwrap();
process::exit(0);
}
fn gen_coreutils_app<T: uucore::Args>(util_map: UtilityMap<T>) -> App<'static, 'static> {
let mut app = App::new("coreutils");
for (_, (_, sub_app)) in util_map {
app = app.subcommand(sub_app());
}
app
}
+8 -5
View File
@@ -17,13 +17,16 @@ static ABOUT: &str = "Display machine architecture";
static SUMMARY: &str = "Determine architecture name for current machine.";
pub fn uumain(args: impl uucore::Args) -> i32 {
App::new(executable!())
.version(crate_version!())
.about(ABOUT)
.after_help(SUMMARY)
.get_matches_from(args);
uu_app().get_matches_from(args);
let uts = return_if_err!(1, PlatformInfo::new());
println!("{}", uts.machine().trim());
0
}
pub fn uu_app() -> App<'static, 'static> {
App::new(executable!())
.version(crate_version!())
.about(ABOUT)
.after_help(SUMMARY)
}
+5
View File
@@ -10,6 +10,7 @@ extern crate uucore;
use std::io::{stdin, Read};
use clap::App;
use uucore::encoding::Format;
pub mod base_common;
@@ -56,3 +57,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
0
}
pub fn uu_app() -> App<'static, 'static> {
base_common::base_app(executable!(), VERSION, ABOUT)
}
+10 -7
View File
@@ -78,10 +78,17 @@ pub fn parse_base_cmd_args(
about: &str,
usage: &str,
) -> Result<Config, String> {
let app = App::new(name)
let app = base_app(name, version, about).usage(usage);
let arg_list = args
.collect_str(InvalidEncodingHandling::ConvertLossy)
.accept_any();
Config::from(app.get_matches_from(arg_list))
}
pub fn base_app<'a>(name: &str, version: &'a str, about: &'a str) -> App<'static, 'a> {
App::new(name)
.version(version)
.about(about)
.usage(usage)
// Format arguments.
.arg(
Arg::with_name(options::DECODE)
@@ -106,11 +113,7 @@ pub fn parse_base_cmd_args(
)
// "multiple" arguments are used to check whether there is more than one
// file passed in.
.arg(Arg::with_name(options::FILE).index(1).multiple(true));
let arg_list = args
.collect_str(InvalidEncodingHandling::ConvertLossy)
.accept_any();
Config::from(app.get_matches_from(arg_list))
.arg(Arg::with_name(options::FILE).index(1).multiple(true))
}
pub fn get_input<'a>(config: &Config, stdin_ref: &'a Stdin) -> Box<dyn Read + 'a> {
+1
View File
@@ -10,6 +10,7 @@
extern crate uucore;
use uu_base32::base_common;
pub use uu_base32::uu_app;
use uucore::encoding::Format;
+27 -25
View File
@@ -40,31 +40,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
//
// Argument parsing
//
let matches = App::new(executable!())
.version(crate_version!())
.about(SUMMARY)
.usage(&usage[..])
.arg(
Arg::with_name(options::MULTIPLE)
.short("a")
.long(options::MULTIPLE)
.help("support multiple arguments and treat each as a NAME"),
)
.arg(Arg::with_name(options::NAME).multiple(true).hidden(true))
.arg(
Arg::with_name(options::SUFFIX)
.short("s")
.long(options::SUFFIX)
.value_name("SUFFIX")
.help("remove a trailing SUFFIX; implies -a"),
)
.arg(
Arg::with_name(options::ZERO)
.short("z")
.long(options::ZERO)
.help("end each output line with NUL, not newline"),
)
.get_matches_from(args);
let matches = uu_app().usage(&usage[..]).get_matches_from(args);
// too few arguments
if !matches.is_present(options::NAME) {
@@ -116,6 +92,32 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
0
}
pub fn uu_app() -> App<'static, 'static> {
App::new(executable!())
.version(crate_version!())
.about(SUMMARY)
.arg(
Arg::with_name(options::MULTIPLE)
.short("a")
.long(options::MULTIPLE)
.help("support multiple arguments and treat each as a NAME"),
)
.arg(Arg::with_name(options::NAME).multiple(true).hidden(true))
.arg(
Arg::with_name(options::SUFFIX)
.short("s")
.long(options::SUFFIX)
.value_name("SUFFIX")
.help("remove a trailing SUFFIX; implies -a"),
)
.arg(
Arg::with_name(options::ZERO)
.short("z")
.long(options::ZERO)
.help("end each output line with NUL, not newline"),
)
}
fn basename(fullname: &str, suffix: &str) -> String {
// Remove all platform-specific path separators from the end
let path = fullname.trim_end_matches(is_separator);
+59 -56
View File
@@ -169,7 +169,65 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
.collect_str(InvalidEncodingHandling::Ignore)
.accept_any();
let matches = App::new(executable!())
let matches = uu_app().get_matches_from(args);
let number_mode = if matches.is_present(options::NUMBER_NONBLANK) {
NumberingMode::NonEmpty
} else if matches.is_present(options::NUMBER) {
NumberingMode::All
} else {
NumberingMode::None
};
let show_nonprint = vec![
options::SHOW_ALL.to_owned(),
options::SHOW_NONPRINTING_ENDS.to_owned(),
options::SHOW_NONPRINTING_TABS.to_owned(),
options::SHOW_NONPRINTING.to_owned(),
]
.iter()
.any(|v| matches.is_present(v));
let show_ends = vec![
options::SHOW_ENDS.to_owned(),
options::SHOW_ALL.to_owned(),
options::SHOW_NONPRINTING_ENDS.to_owned(),
]
.iter()
.any(|v| matches.is_present(v));
let show_tabs = vec![
options::SHOW_ALL.to_owned(),
options::SHOW_TABS.to_owned(),
options::SHOW_NONPRINTING_TABS.to_owned(),
]
.iter()
.any(|v| matches.is_present(v));
let squeeze_blank = matches.is_present(options::SQUEEZE_BLANK);
let files: Vec<String> = match matches.values_of(options::FILE) {
Some(v) => v.clone().map(|v| v.to_owned()).collect(),
None => vec!["-".to_owned()],
};
let options = OutputOptions {
show_ends,
number: number_mode,
show_nonprint,
show_tabs,
squeeze_blank,
};
let success = cat_files(files, &options).is_ok();
if success {
0
} else {
1
}
}
pub fn uu_app() -> App<'static, 'static> {
App::new(executable!())
.name(NAME)
.version(crate_version!())
.usage(SYNTAX)
@@ -229,61 +287,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
.long(options::SHOW_NONPRINTING)
.help("use ^ and M- notation, except for LF (\\n) and TAB (\\t)"),
)
.get_matches_from(args);
let number_mode = if matches.is_present(options::NUMBER_NONBLANK) {
NumberingMode::NonEmpty
} else if matches.is_present(options::NUMBER) {
NumberingMode::All
} else {
NumberingMode::None
};
let show_nonprint = vec![
options::SHOW_ALL.to_owned(),
options::SHOW_NONPRINTING_ENDS.to_owned(),
options::SHOW_NONPRINTING_TABS.to_owned(),
options::SHOW_NONPRINTING.to_owned(),
]
.iter()
.any(|v| matches.is_present(v));
let show_ends = vec![
options::SHOW_ENDS.to_owned(),
options::SHOW_ALL.to_owned(),
options::SHOW_NONPRINTING_ENDS.to_owned(),
]
.iter()
.any(|v| matches.is_present(v));
let show_tabs = vec![
options::SHOW_ALL.to_owned(),
options::SHOW_TABS.to_owned(),
options::SHOW_NONPRINTING_TABS.to_owned(),
]
.iter()
.any(|v| matches.is_present(v));
let squeeze_blank = matches.is_present(options::SQUEEZE_BLANK);
let files: Vec<String> = match matches.values_of(options::FILE) {
Some(v) => v.clone().map(|v| v.to_owned()).collect(),
None => vec!["-".to_owned()],
};
let options = OutputOptions {
show_ends,
number: number_mode,
show_nonprint,
show_tabs,
squeeze_blank,
};
let success = cat_files(files, &options).is_ok();
if success {
0
} else {
1
}
}
fn cat_handle<R: Read>(
+81 -78
View File
@@ -73,84 +73,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
let usage = get_usage();
let mut app = App::new(executable!())
.version(VERSION)
.about(ABOUT)
.usage(&usage[..])
.arg(
Arg::with_name(options::verbosity::CHANGES)
.short("c")
.long(options::verbosity::CHANGES)
.help("like verbose but report only when a change is made"),
)
.arg(
Arg::with_name(options::verbosity::SILENT)
.short("f")
.long(options::verbosity::SILENT),
)
.arg(
Arg::with_name(options::verbosity::QUIET)
.long(options::verbosity::QUIET)
.help("suppress most error messages"),
)
.arg(
Arg::with_name(options::verbosity::VERBOSE)
.short("v")
.long(options::verbosity::VERBOSE)
.help("output a diagnostic for every file processed"),
)
.arg(
Arg::with_name(options::dereference::DEREFERENCE)
.long(options::dereference::DEREFERENCE),
)
.arg(
Arg::with_name(options::dereference::NO_DEREFERENCE)
.short("h")
.long(options::dereference::NO_DEREFERENCE)
.help(
"affect symbolic links instead of any referenced file (useful only on systems that can change the ownership of a symlink)",
),
)
.arg(
Arg::with_name(options::preserve_root::PRESERVE)
.long(options::preserve_root::PRESERVE)
.help("fail to operate recursively on '/'"),
)
.arg(
Arg::with_name(options::preserve_root::NO_PRESERVE)
.long(options::preserve_root::NO_PRESERVE)
.help("do not treat '/' specially (the default)"),
)
.arg(
Arg::with_name(options::REFERENCE)
.long(options::REFERENCE)
.value_name("RFILE")
.help("use RFILE's group rather than specifying GROUP values")
.takes_value(true)
.multiple(false),
)
.arg(
Arg::with_name(options::RECURSIVE)
.short("R")
.long(options::RECURSIVE)
.help("operate on files and directories recursively"),
)
.arg(
Arg::with_name(options::traverse::TRAVERSE)
.short(options::traverse::TRAVERSE)
.help("if a command line argument is a symbolic link to a directory, traverse it"),
)
.arg(
Arg::with_name(options::traverse::NO_TRAVERSE)
.short(options::traverse::NO_TRAVERSE)
.help("do not traverse any symbolic links (default)")
.overrides_with_all(&[options::traverse::TRAVERSE, options::traverse::EVERY]),
)
.arg(
Arg::with_name(options::traverse::EVERY)
.short(options::traverse::EVERY)
.help("traverse every symbolic link to a directory encountered"),
);
let mut app = uu_app().usage(&usage[..]);
// we change the positional args based on whether
// --reference was used.
@@ -274,6 +197,86 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
executor.exec()
}
pub fn uu_app() -> App<'static, 'static> {
App::new(executable!())
.version(VERSION)
.about(ABOUT)
.arg(
Arg::with_name(options::verbosity::CHANGES)
.short("c")
.long(options::verbosity::CHANGES)
.help("like verbose but report only when a change is made"),
)
.arg(
Arg::with_name(options::verbosity::SILENT)
.short("f")
.long(options::verbosity::SILENT),
)
.arg(
Arg::with_name(options::verbosity::QUIET)
.long(options::verbosity::QUIET)
.help("suppress most error messages"),
)
.arg(
Arg::with_name(options::verbosity::VERBOSE)
.short("v")
.long(options::verbosity::VERBOSE)
.help("output a diagnostic for every file processed"),
)
.arg(
Arg::with_name(options::dereference::DEREFERENCE)
.long(options::dereference::DEREFERENCE),
)
.arg(
Arg::with_name(options::dereference::NO_DEREFERENCE)
.short("h")
.long(options::dereference::NO_DEREFERENCE)
.help(
"affect symbolic links instead of any referenced file (useful only on systems that can change the ownership of a symlink)",
),
)
.arg(
Arg::with_name(options::preserve_root::PRESERVE)
.long(options::preserve_root::PRESERVE)
.help("fail to operate recursively on '/'"),
)
.arg(
Arg::with_name(options::preserve_root::NO_PRESERVE)
.long(options::preserve_root::NO_PRESERVE)
.help("do not treat '/' specially (the default)"),
)
.arg(
Arg::with_name(options::REFERENCE)
.long(options::REFERENCE)
.value_name("RFILE")
.help("use RFILE's group rather than specifying GROUP values")
.takes_value(true)
.multiple(false),
)
.arg(
Arg::with_name(options::RECURSIVE)
.short("R")
.long(options::RECURSIVE)
.help("operate on files and directories recursively"),
)
.arg(
Arg::with_name(options::traverse::TRAVERSE)
.short(options::traverse::TRAVERSE)
.help("if a command line argument is a symbolic link to a directory, traverse it"),
)
.arg(
Arg::with_name(options::traverse::NO_TRAVERSE)
.short(options::traverse::NO_TRAVERSE)
.help("do not traverse any symbolic links (default)")
.overrides_with_all(&[options::traverse::TRAVERSE, options::traverse::EVERY]),
)
.arg(
Arg::with_name(options::traverse::EVERY)
.short(options::traverse::EVERY)
.help("traverse every symbolic link to a directory encountered"),
)
}
struct Chgrper {
dest_gid: gid_t,
bit_flag: u8,
+56 -52
View File
@@ -61,11 +61,64 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
let usage = get_usage();
let after_help = get_long_usage();
let matches = App::new(executable!())
.version(crate_version!())
.about(ABOUT)
let matches = uu_app()
.usage(&usage[..])
.after_help(&after_help[..])
.get_matches_from(args);
let changes = matches.is_present(options::CHANGES);
let quiet = matches.is_present(options::QUIET);
let verbose = matches.is_present(options::VERBOSE);
let preserve_root = matches.is_present(options::PRESERVE_ROOT);
let recursive = matches.is_present(options::RECURSIVE);
let fmode = matches
.value_of(options::REFERENCE)
.and_then(|fref| match fs::metadata(fref) {
Ok(meta) => Some(meta.mode()),
Err(err) => crash!(1, "cannot stat attributes of '{}': {}", fref, err),
});
let modes = matches.value_of(options::MODE).unwrap(); // should always be Some because required
let cmode = if mode_had_minus_prefix {
// clap parsing is finished, now put prefix back
format!("-{}", modes)
} else {
modes.to_string()
};
let mut files: Vec<String> = matches
.values_of(options::FILE)
.map(|v| v.map(ToString::to_string).collect())
.unwrap_or_default();
let cmode = if fmode.is_some() {
// "--reference" and MODE are mutually exclusive
// if "--reference" was used MODE needs to be interpreted as another FILE
// it wasn't possible to implement this behavior directly with clap
files.push(cmode);
None
} else {
Some(cmode)
};
let chmoder = Chmoder {
changes,
quiet,
verbose,
preserve_root,
recursive,
fmode,
cmode,
};
match chmoder.chmod(files) {
Ok(()) => {}
Err(e) => return e,
}
0
}
pub fn uu_app() -> App<'static, 'static> {
App::new(executable!())
.version(crate_version!())
.about(ABOUT)
.arg(
Arg::with_name(options::CHANGES)
.long(options::CHANGES)
@@ -120,55 +173,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
.required_unless(options::MODE)
.multiple(true),
)
.get_matches_from(args);
let changes = matches.is_present(options::CHANGES);
let quiet = matches.is_present(options::QUIET);
let verbose = matches.is_present(options::VERBOSE);
let preserve_root = matches.is_present(options::PRESERVE_ROOT);
let recursive = matches.is_present(options::RECURSIVE);
let fmode = matches
.value_of(options::REFERENCE)
.and_then(|fref| match fs::metadata(fref) {
Ok(meta) => Some(meta.mode()),
Err(err) => crash!(1, "cannot stat attributes of '{}': {}", fref, err),
});
let modes = matches.value_of(options::MODE).unwrap(); // should always be Some because required
let cmode = if mode_had_minus_prefix {
// clap parsing is finished, now put prefix back
format!("-{}", modes)
} else {
modes.to_string()
};
let mut files: Vec<String> = matches
.values_of(options::FILE)
.map(|v| v.map(ToString::to_string).collect())
.unwrap_or_default();
let cmode = if fmode.is_some() {
// "--reference" and MODE are mutually exclusive
// if "--reference" was used MODE needs to be interpreted as another FILE
// it wasn't possible to implement this behavior directly with clap
files.push(cmode);
None
} else {
Some(cmode)
};
let chmoder = Chmoder {
changes,
quiet,
verbose,
preserve_root,
recursive,
fmode,
cmode,
};
match chmoder.chmod(files) {
Ok(()) => {}
Err(e) => return e,
}
0
}
// Iterate 'args' and delete the first occurrence
+97 -95
View File
@@ -73,101 +73,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
let usage = get_usage();
let matches = App::new(executable!())
.version(crate_version!())
.about(ABOUT)
.usage(&usage[..])
.arg(
Arg::with_name(options::verbosity::CHANGES)
.short("c")
.long(options::verbosity::CHANGES)
.help("like verbose but report only when a change is made"),
)
.arg(Arg::with_name(options::dereference::DEREFERENCE).long(options::dereference::DEREFERENCE).help(
"affect the referent of each symbolic link (this is the default), rather than the symbolic link itself",
))
.arg(
Arg::with_name(options::dereference::NO_DEREFERENCE)
.short("h")
.long(options::dereference::NO_DEREFERENCE)
.help(
"affect symbolic links instead of any referenced file (useful only on systems that can change the ownership of a symlink)",
),
)
.arg(
Arg::with_name(options::FROM)
.long(options::FROM)
.help(
"change the owner and/or group of each file only if its current owner and/or group match those specified here. Either may be omitted, in which case a match is not required for the omitted attribute",
)
.value_name("CURRENT_OWNER:CURRENT_GROUP"),
)
.arg(
Arg::with_name(options::preserve_root::PRESERVE)
.long(options::preserve_root::PRESERVE)
.help("fail to operate recursively on '/'"),
)
.arg(
Arg::with_name(options::preserve_root::NO_PRESERVE)
.long(options::preserve_root::NO_PRESERVE)
.help("do not treat '/' specially (the default)"),
)
.arg(
Arg::with_name(options::verbosity::QUIET)
.long(options::verbosity::QUIET)
.help("suppress most error messages"),
)
.arg(
Arg::with_name(options::RECURSIVE)
.short("R")
.long(options::RECURSIVE)
.help("operate on files and directories recursively"),
)
.arg(
Arg::with_name(options::REFERENCE)
.long(options::REFERENCE)
.help("use RFILE's owner and group rather than specifying OWNER:GROUP values")
.value_name("RFILE")
.min_values(1),
)
.arg(Arg::with_name(options::verbosity::SILENT).short("f").long(options::verbosity::SILENT))
.arg(
Arg::with_name(options::traverse::TRAVERSE)
.short(options::traverse::TRAVERSE)
.help("if a command line argument is a symbolic link to a directory, traverse it")
.overrides_with_all(&[options::traverse::EVERY, options::traverse::NO_TRAVERSE]),
)
.arg(
Arg::with_name(options::traverse::EVERY)
.short(options::traverse::EVERY)
.help("traverse every symbolic link to a directory encountered")
.overrides_with_all(&[options::traverse::TRAVERSE, options::traverse::NO_TRAVERSE]),
)
.arg(
Arg::with_name(options::traverse::NO_TRAVERSE)
.short(options::traverse::NO_TRAVERSE)
.help("do not traverse any symbolic links (default)")
.overrides_with_all(&[options::traverse::TRAVERSE, options::traverse::EVERY]),
)
.arg(
Arg::with_name(options::verbosity::VERBOSE)
.long(options::verbosity::VERBOSE)
.help("output a diagnostic for every file processed"),
)
.arg(
Arg::with_name(ARG_OWNER)
.multiple(false)
.takes_value(true)
.required(true),
)
.arg(
Arg::with_name(ARG_FILES)
.multiple(true)
.takes_value(true)
.required(true)
.min_values(1),
)
.get_matches_from(args);
let matches = uu_app().usage(&usage[..]).get_matches_from(args);
/* First arg is the owner/group */
let owner = matches.value_of(ARG_OWNER).unwrap();
@@ -273,6 +179,102 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
executor.exec()
}
pub fn uu_app() -> App<'static, 'static> {
App::new(executable!())
.version(crate_version!())
.about(ABOUT)
.arg(
Arg::with_name(options::verbosity::CHANGES)
.short("c")
.long(options::verbosity::CHANGES)
.help("like verbose but report only when a change is made"),
)
.arg(Arg::with_name(options::dereference::DEREFERENCE).long(options::dereference::DEREFERENCE).help(
"affect the referent of each symbolic link (this is the default), rather than the symbolic link itself",
))
.arg(
Arg::with_name(options::dereference::NO_DEREFERENCE)
.short("h")
.long(options::dereference::NO_DEREFERENCE)
.help(
"affect symbolic links instead of any referenced file (useful only on systems that can change the ownership of a symlink)",
),
)
.arg(
Arg::with_name(options::FROM)
.long(options::FROM)
.help(
"change the owner and/or group of each file only if its current owner and/or group match those specified here. Either may be omitted, in which case a match is not required for the omitted attribute",
)
.value_name("CURRENT_OWNER:CURRENT_GROUP"),
)
.arg(
Arg::with_name(options::preserve_root::PRESERVE)
.long(options::preserve_root::PRESERVE)
.help("fail to operate recursively on '/'"),
)
.arg(
Arg::with_name(options::preserve_root::NO_PRESERVE)
.long(options::preserve_root::NO_PRESERVE)
.help("do not treat '/' specially (the default)"),
)
.arg(
Arg::with_name(options::verbosity::QUIET)
.long(options::verbosity::QUIET)
.help("suppress most error messages"),
)
.arg(
Arg::with_name(options::RECURSIVE)
.short("R")
.long(options::RECURSIVE)
.help("operate on files and directories recursively"),
)
.arg(
Arg::with_name(options::REFERENCE)
.long(options::REFERENCE)
.help("use RFILE's owner and group rather than specifying OWNER:GROUP values")
.value_name("RFILE")
.min_values(1),
)
.arg(Arg::with_name(options::verbosity::SILENT).short("f").long(options::verbosity::SILENT))
.arg(
Arg::with_name(options::traverse::TRAVERSE)
.short(options::traverse::TRAVERSE)
.help("if a command line argument is a symbolic link to a directory, traverse it")
.overrides_with_all(&[options::traverse::EVERY, options::traverse::NO_TRAVERSE]),
)
.arg(
Arg::with_name(options::traverse::EVERY)
.short(options::traverse::EVERY)
.help("traverse every symbolic link to a directory encountered")
.overrides_with_all(&[options::traverse::TRAVERSE, options::traverse::NO_TRAVERSE]),
)
.arg(
Arg::with_name(options::traverse::NO_TRAVERSE)
.short(options::traverse::NO_TRAVERSE)
.help("do not traverse any symbolic links (default)")
.overrides_with_all(&[options::traverse::TRAVERSE, options::traverse::EVERY]),
)
.arg(
Arg::with_name(options::verbosity::VERBOSE)
.long(options::verbosity::VERBOSE)
.help("output a diagnostic for every file processed"),
)
.arg(
Arg::with_name(ARG_OWNER)
.multiple(false)
.takes_value(true)
.required(true),
)
.arg(
Arg::with_name(ARG_FILES)
.multiple(true)
.takes_value(true)
.required(true)
.min_values(1),
)
}
fn parse_spec(spec: &str) -> Result<(Option<u32>, Option<u32>), String> {
let args = spec.split_terminator(':').collect::<Vec<_>>();
let usr_only = args.len() == 1 && !args[0].is_empty();
+51 -48
View File
@@ -36,54 +36,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
.collect_str(InvalidEncodingHandling::ConvertLossy)
.accept_any();
let matches = App::new(executable!())
.version(crate_version!())
.about(ABOUT)
.usage(SYNTAX)
.arg(
Arg::with_name(options::NEWROOT)
.hidden(true)
.required(true)
.index(1),
)
.arg(
Arg::with_name(options::USER)
.short("u")
.long(options::USER)
.help("User (ID or name) to switch before running the program")
.value_name("USER"),
)
.arg(
Arg::with_name(options::GROUP)
.short("g")
.long(options::GROUP)
.help("Group (ID or name) to switch to")
.value_name("GROUP"),
)
.arg(
Arg::with_name(options::GROUPS)
.short("G")
.long(options::GROUPS)
.help("Comma-separated list of groups to switch to")
.value_name("GROUP1,GROUP2..."),
)
.arg(
Arg::with_name(options::USERSPEC)
.long(options::USERSPEC)
.help(
"Colon-separated user and group to switch to. \
Same as -u USER -g GROUP. \
Userspec has higher preference than -u and/or -g",
)
.value_name("USER:GROUP"),
)
.arg(
Arg::with_name(options::COMMAND)
.hidden(true)
.multiple(true)
.index(2),
)
.get_matches_from(args);
let matches = uu_app().get_matches_from(args);
let default_shell: &'static str = "/bin/sh";
let default_option: &'static str = "-i";
@@ -138,6 +91,56 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
}
}
pub fn uu_app() -> App<'static, 'static> {
App::new(executable!())
.version(crate_version!())
.about(ABOUT)
.usage(SYNTAX)
.arg(
Arg::with_name(options::NEWROOT)
.hidden(true)
.required(true)
.index(1),
)
.arg(
Arg::with_name(options::USER)
.short("u")
.long(options::USER)
.help("User (ID or name) to switch before running the program")
.value_name("USER"),
)
.arg(
Arg::with_name(options::GROUP)
.short("g")
.long(options::GROUP)
.help("Group (ID or name) to switch to")
.value_name("GROUP"),
)
.arg(
Arg::with_name(options::GROUPS)
.short("G")
.long(options::GROUPS)
.help("Comma-separated list of groups to switch to")
.value_name("GROUP1,GROUP2..."),
)
.arg(
Arg::with_name(options::USERSPEC)
.long(options::USERSPEC)
.help(
"Colon-separated user and group to switch to. \
Same as -u USER -g GROUP. \
Userspec has higher preference than -u and/or -g",
)
.value_name("USER:GROUP"),
)
.arg(
Arg::with_name(options::COMMAND)
.hidden(true)
.multiple(true)
.index(2),
)
}
fn set_context(root: &Path, options: &clap::ArgMatches) {
let userspec_str = options.value_of(options::USERSPEC);
let user_str = options.value_of(options::USER).unwrap_or_default();
+10 -7
View File
@@ -180,13 +180,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
.collect_str(InvalidEncodingHandling::Ignore)
.accept_any();
let matches = App::new(executable!())
.name(NAME)
.version(crate_version!())
.about(SUMMARY)
.usage(SYNTAX)
.arg(Arg::with_name(options::FILE).hidden(true).multiple(true))
.get_matches_from(args);
let matches = uu_app().get_matches_from(args);
let files: Vec<String> = match matches.values_of(options::FILE) {
Some(v) => v.clone().map(|v| v.to_owned()).collect(),
@@ -217,3 +211,12 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
exit_code
}
pub fn uu_app() -> App<'static, 'static> {
App::new(executable!())
.name(NAME)
.version(crate_version!())
.about(SUMMARY)
.usage(SYNTAX)
.arg(Arg::with_name(options::FILE).hidden(true).multiple(true))
}
+12 -10
View File
@@ -137,10 +137,20 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
.collect_str(InvalidEncodingHandling::ConvertLossy)
.accept_any();
let matches = App::new(executable!())
let matches = uu_app().usage(&usage[..]).get_matches_from(args);
let mut f1 = open_file(matches.value_of(options::FILE_1).unwrap()).unwrap();
let mut f2 = open_file(matches.value_of(options::FILE_2).unwrap()).unwrap();
comm(&mut f1, &mut f2, &matches);
0
}
pub fn uu_app() -> App<'static, 'static> {
App::new(executable!())
.version(crate_version!())
.about(ABOUT)
.usage(&usage[..])
.after_help(LONG_HELP)
.arg(
Arg::with_name(options::COLUMN_1)
@@ -167,12 +177,4 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
)
.arg(Arg::with_name(options::FILE_1).required(true))
.arg(Arg::with_name(options::FILE_2).required(true))
.get_matches_from(args);
let mut f1 = open_file(matches.value_of(options::FILE_1).unwrap()).unwrap();
let mut f2 = open_file(matches.value_of(options::FILE_2).unwrap()).unwrap();
comm(&mut f1, &mut f2, &matches);
0
}
+15 -7
View File
@@ -290,13 +290,10 @@ static DEFAULT_ATTRIBUTES: &[Attribute] = &[
Attribute::Timestamps,
];
pub fn uumain(args: impl uucore::Args) -> i32 {
let usage = get_usage();
let matches = App::new(executable!())
pub fn uu_app() -> App<'static, 'static> {
App::new(executable!())
.version(crate_version!())
.about(ABOUT)
.after_help(&*format!("{}\n{}", LONG_HELP, backup_control::BACKUP_CONTROL_LONG_HELP))
.usage(&usage[..])
.arg(Arg::with_name(options::TARGET_DIRECTORY)
.short("t")
.conflicts_with(options::NO_TARGET_DIRECTORY)
@@ -378,7 +375,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
.arg(Arg::with_name(options::UPDATE)
.short("u")
.long(options::UPDATE)
.help("copy only when the SOURCE file is newer than the destination file\
.help("copy only when the SOURCE file is newer than the destination file \
or when the destination file is missing"))
.arg(Arg::with_name(options::REFLINK)
.long(options::REFLINK)
@@ -401,7 +398,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
.conflicts_with_all(&[options::PRESERVE_DEFAULT_ATTRIBUTES, options::NO_PRESERVE])
// -d sets this option
// --archive sets this option
.help("Preserve the specified attributes (default: mode(unix only),ownership,timestamps),\
.help("Preserve the specified attributes (default: mode (unix only), ownership, timestamps), \
if possible additional attributes: context, links, xattr, all"))
.arg(Arg::with_name(options::PRESERVE_DEFAULT_ATTRIBUTES)
.short("-p")
@@ -464,6 +461,17 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
.arg(Arg::with_name(options::PATHS)
.multiple(true))
}
pub fn uumain(args: impl uucore::Args) -> i32 {
let usage = get_usage();
let matches = uu_app()
.after_help(&*format!(
"{}\n{}",
LONG_HELP,
backup_control::BACKUP_CONTROL_LONG_HELP
))
.usage(&usage[..])
.get_matches_from(args);
let options = crash_if_err!(EXIT_ERR, Options::from_matches(&matches));
+29 -27
View File
@@ -711,10 +711,37 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
.collect_str(InvalidEncodingHandling::Ignore)
.accept_any();
let matches = App::new(executable!())
let matches = uu_app().usage(&usage[..]).get_matches_from(args);
// get the file to split
let file_name = matches.value_of(options::FILE).unwrap();
// get the patterns to split on
let patterns: Vec<String> = matches
.values_of(options::PATTERN)
.unwrap()
.map(str::to_string)
.collect();
let patterns = return_if_err!(1, patterns::get_patterns(&patterns[..]));
let options = CsplitOptions::new(&matches);
if file_name == "-" {
let stdin = io::stdin();
crash_if_err!(1, csplit(&options, patterns, stdin.lock()));
} else {
let file = return_if_err!(1, File::open(file_name));
let file_metadata = return_if_err!(1, file.metadata());
if !file_metadata.is_file() {
crash!(1, "'{}' is not a regular file", file_name);
}
crash_if_err!(1, csplit(&options, patterns, BufReader::new(file)));
};
0
}
pub fn uu_app() -> App<'static, 'static> {
App::new(executable!())
.version(crate_version!())
.about(SUMMARY)
.usage(&usage[..])
.arg(
Arg::with_name(options::SUFFIX_FORMAT)
.short("b")
@@ -768,29 +795,4 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
.required(true),
)
.after_help(LONG_HELP)
.get_matches_from(args);
// get the file to split
let file_name = matches.value_of(options::FILE).unwrap();
// get the patterns to split on
let patterns: Vec<String> = matches
.values_of(options::PATTERN)
.unwrap()
.map(str::to_string)
.collect();
let patterns = return_if_err!(1, patterns::get_patterns(&patterns[..]));
let options = CsplitOptions::new(&matches);
if file_name == "-" {
let stdin = io::stdin();
crash_if_err!(1, csplit(&options, patterns, stdin.lock()));
} else {
let file = return_if_err!(1, File::open(file_name));
let file_metadata = return_if_err!(1, file.metadata());
if !file_metadata.is_file() {
crash!(1, "'{}' is not a regular file", file_name);
}
crash_if_err!(1, csplit(&options, patterns, BufReader::new(file)));
};
0
}

Some files were not shown because too many files have changed in this diff Show More