Merge branch 'uutils:master' into master

This commit is contained in:
backwaterred
2021-07-06 15:59:03 -07:00
committed by GitHub
25 changed files with 954 additions and 197 deletions
+26 -18
View File
@@ -6,6 +6,7 @@
// file that was distributed with this source code.
use clap::App;
use clap::Arg;
use clap::Shell;
use std::cmp;
use std::collections::hash_map::HashMap;
@@ -122,31 +123,38 @@ fn main() {
/// 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>,
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 all_utilities: Vec<_> = std::iter::once("coreutils")
.chain(util_map.keys().copied())
.collect();
let matches = App::new("completion")
.about("Prints completions to stdout")
.arg(
Arg::with_name("utility")
.possible_values(&all_utilities)
.required(true),
)
.arg(
Arg::with_name("shell")
.possible_values(&Shell::variants())
.required(true),
)
.get_matches_from(std::iter::once(OsString::from("completion")).chain(args));
let utility = matches.value_of("utility").unwrap();
let shell = matches.value_of("shell").unwrap();
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)
util_map.get(utility).unwrap().1()
};
let shell: Shell = shell.parse().unwrap();
let bin_name = std::env::var("PROG_PREFIX").unwrap_or_default() + &utility;
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);
+3
View File
@@ -6,6 +6,9 @@
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
// clippy bug https://github.com/rust-lang/rust-clippy/issues/7422
#![allow(clippy::nonstandard_macro_braces)]
#[macro_use]
extern crate uucore;
+3
View File
@@ -10,6 +10,9 @@
// spell-checker:ignore (ToDO) nonprint nonblank nonprinting
// clippy bug https://github.com/rust-lang/rust-clippy/issues/7422
#![allow(clippy::nonstandard_macro_braces)]
#[cfg(unix)]
extern crate unix_socket;
#[macro_use]
+3
View File
@@ -1,3 +1,6 @@
// clippy bug https://github.com/rust-lang/rust-clippy/issues/7422
#![allow(clippy::nonstandard_macro_braces)]
use std::io;
use thiserror::Error;
+8 -4
View File
@@ -5,11 +5,15 @@
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
// Clippy bug: https://github.com/rust-lang/rust-clippy/issues/7422
#![allow(clippy::nonstandard_macro_braces)]
#[macro_use]
extern crate uucore;
use clap::{crate_version, App, Arg};
use std::path::Path;
use uucore::error::{UResult, UUsageError};
use uucore::InvalidEncodingHandling;
static ABOUT: &str = "strip last component from file name";
@@ -30,7 +34,8 @@ fn get_long_usage() -> String {
)
}
pub fn uumain(args: impl uucore::Args) -> i32 {
#[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let args = args
.collect_str(InvalidEncodingHandling::ConvertLossy)
.accept_any();
@@ -77,11 +82,10 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
print!("{}", separator);
}
} else {
show_usage_error!("missing operand");
return 1;
return Err(UUsageError::new(1, "missing operand".to_string()));
}
0
Ok(())
}
pub fn uu_app() -> App<'static, 'static> {
+37 -12
View File
@@ -10,6 +10,7 @@ extern crate uucore;
use chrono::prelude::DateTime;
use chrono::Local;
use clap::ArgMatches;
use clap::{crate_version, App, Arg};
use std::collections::HashSet;
use std::convert::TryFrom;
@@ -63,6 +64,7 @@ mod options {
pub const TIME_STYLE: &str = "time-style";
pub const ONE_FILE_SYSTEM: &str = "one-file-system";
pub const DEREFERENCE: &str = "dereference";
pub const INODES: &str = "inodes";
pub const FILE: &str = "FILE";
}
@@ -89,6 +91,7 @@ struct Options {
separate_dirs: bool,
one_file_system: bool,
dereference: bool,
inodes: bool,
}
#[derive(PartialEq, Eq, Hash, Clone, Copy)]
@@ -102,6 +105,7 @@ struct Stat {
is_dir: bool,
size: u64,
blocks: u64,
inodes: u64,
inode: Option<FileInfo>,
created: Option<u64>,
accessed: u64,
@@ -127,6 +131,7 @@ impl Stat {
is_dir: metadata.is_dir(),
size: metadata.len(),
blocks: metadata.blocks() as u64,
inodes: 1,
inode: Some(file_info),
created: birth_u64(&metadata),
accessed: metadata.atime() as u64,
@@ -144,6 +149,7 @@ impl Stat {
size: metadata.len(),
blocks: size_on_disk / 1024 * 2,
inode: file_info,
inodes: 1,
created: windows_creation_time_to_unix_time(metadata.creation_time()),
accessed: windows_time_to_unix_time(metadata.last_access_time()),
modified: windows_time_to_unix_time(metadata.last_write_time()),
@@ -257,6 +263,18 @@ fn read_block_size(s: Option<&str>) -> usize {
}
}
fn choose_size(matches: &ArgMatches, stat: &Stat) -> u64 {
if matches.is_present(options::INODES) {
stat.inodes
} else if matches.is_present(options::APPARENT_SIZE) || matches.is_present(options::BYTES) {
stat.size
} else {
// The st_blocks field indicates the number of blocks allocated to the file, 512-byte units.
// See: http://linux.die.net/man/2/stat
stat.blocks * 512
}
}
// this takes `my_stat` to avoid having to stat files multiple times.
// XXX: this should use the impl Trait return type when it is stabilized
fn du(
@@ -307,6 +325,7 @@ fn du(
} else {
my_stat.size += this_stat.size;
my_stat.blocks += this_stat.blocks;
my_stat.inodes += 1;
if options.all {
stats.push(this_stat);
}
@@ -330,6 +349,7 @@ fn du(
if !options.separate_dirs && stat.path.parent().unwrap() == my_stat.path {
my_stat.size += stat.size;
my_stat.blocks += stat.blocks;
my_stat.inodes += stat.inodes;
}
options
.max_depth
@@ -413,6 +433,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
separate_dirs: matches.is_present(options::SEPARATE_DIRS),
one_file_system: matches.is_present(options::ONE_FILE_SYSTEM),
dereference: matches.is_present(options::DEREFERENCE),
inodes: matches.is_present(options::INODES),
};
let files = match matches.value_of(options::FILE) {
@@ -420,6 +441,12 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
None => vec!["."],
};
if options.inodes
&& (matches.is_present(options::APPARENT_SIZE) || matches.is_present(options::BYTES))
{
show_warning!("options --apparent-size and -b are ineffective with --inodes")
}
let block_size = u64::try_from(read_block_size(matches.value_of(options::BLOCK_SIZE))).unwrap();
let threshold = matches.value_of(options::THRESHOLD).map(|s| {
@@ -445,7 +472,13 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
convert_size_other
}
};
let convert_size = |size| convert_size_fn(size, multiplier, block_size);
let convert_size = |size: u64| {
if options.inodes {
size.to_string()
} else {
convert_size_fn(size, multiplier, block_size)
}
};
let time_format_str = match matches.value_of("time-style") {
Some(s) => match s {
@@ -488,15 +521,7 @@ Try '{} --help' for more information.",
let (_, len) = iter.size_hint();
let len = len.unwrap();
for (index, stat) in iter.enumerate() {
let size = if matches.is_present(options::APPARENT_SIZE)
|| matches.is_present(options::BYTES)
{
stat.size
} else {
// C's stat is such that each block is assume to be 512 bytes
// See: http://linux.die.net/man/2/stat
stat.blocks * 512
};
let size = choose_size(&matches, &stat);
if threshold.map_or(false, |threshold| threshold.should_exclude(size)) {
continue;
@@ -629,8 +654,8 @@ pub fn uu_app() -> App<'static, 'static> {
.help("print sizes in human readable format (e.g., 1K 234M 2G)")
)
.arg(
Arg::with_name("inodes")
.long("inodes")
Arg::with_name(options::INODES)
.long(options::INODES)
.help(
"list inode usage information instead of block usage like --block-size=1K"
)
+7 -2
View File
@@ -7,11 +7,15 @@
// spell-checker:ignore (ToDO) gethostid
// Clippy bug: https://github.com/rust-lang/rust-clippy/issues/7422
#![allow(clippy::nonstandard_macro_braces)]
#[macro_use]
extern crate uucore;
use clap::{crate_version, App};
use libc::c_long;
use uucore::error::UResult;
static SYNTAX: &str = "[options]";
@@ -20,10 +24,11 @@ extern "C" {
pub fn gethostid() -> c_long;
}
pub fn uumain(args: impl uucore::Args) -> i32 {
#[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
uu_app().get_matches_from(args);
hostid();
0
Ok(())
}
pub fn uu_app() -> App<'static, 'static> {
+20 -14
View File
@@ -7,6 +7,9 @@
// spell-checker:ignore (ToDO) MAKEWORD addrs hashset
// Clippy bug: https://github.com/rust-lang/rust-clippy/issues/7422
#![allow(clippy::nonstandard_macro_braces)]
#[macro_use]
extern crate uucore;
@@ -14,6 +17,9 @@ use clap::{crate_version, App, Arg, ArgMatches};
use std::collections::hash_set::HashSet;
use std::net::ToSocketAddrs;
use std::str;
#[cfg(windows)]
use uucore::error::UUsageError;
use uucore::error::{UResult, USimpleError};
#[cfg(windows)]
use winapi::shared::minwindef::MAKEWORD;
@@ -28,15 +34,18 @@ static OPT_FQDN: &str = "fqdn";
static OPT_SHORT: &str = "short";
static OPT_HOST: &str = "host";
pub fn uumain(args: impl uucore::Args) -> i32 {
#[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
#![allow(clippy::let_and_return)]
#[cfg(windows)]
unsafe {
#[allow(deprecated)]
let mut data = std::mem::uninitialized();
if WSAStartup(MAKEWORD(2, 2), &mut data as *mut _) != 0 {
eprintln!("Failed to start Winsock 2.2");
return 1;
return Err(UUsageError::new(
1,
"Failed to start Winsock 2.2".to_string(),
));
}
}
let result = execute(args);
@@ -50,7 +59,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
fn get_usage() -> String {
format!("{0} [OPTION]... [HOSTNAME]", executable!())
}
fn execute(args: impl uucore::Args) -> i32 {
fn execute(args: impl uucore::Args) -> UResult<()> {
let usage = get_usage();
let matches = uu_app().usage(&usage[..]).get_matches_from(args);
@@ -58,10 +67,9 @@ fn execute(args: impl uucore::Args) -> i32 {
None => display_hostname(&matches),
Some(host) => {
if let Err(err) = hostname::set(host) {
show_error!("{}", err);
1
return Err(USimpleError::new(1, format!("{}", err)));
} else {
0
Ok(())
}
}
}
@@ -97,7 +105,7 @@ pub fn uu_app() -> App<'static, 'static> {
.arg(Arg::with_name(OPT_HOST))
}
fn display_hostname(matches: &ArgMatches) -> i32 {
fn display_hostname(matches: &ArgMatches) -> UResult<()> {
let hostname = hostname::get().unwrap().into_string().unwrap();
if matches.is_present(OPT_IP_ADDRESS) {
@@ -127,12 +135,10 @@ fn display_hostname(matches: &ArgMatches) -> i32 {
println!("{}", &output[0..len - 1]);
}
0
Ok(())
}
Err(f) => {
show_error!("{}", f);
1
return Err(USimpleError::new(1, format!("{}", f)));
}
}
} else {
@@ -144,12 +150,12 @@ fn display_hostname(matches: &ArgMatches) -> i32 {
} else {
println!("{}", &hostname[ci.0 + 1..]);
}
return 0;
return Ok(());
}
}
println!("{}", hostname);
0
Ok(())
}
}
+43 -20
View File
@@ -15,6 +15,7 @@ extern crate uucore;
use clap::{crate_version, App, Arg, ArgMatches};
use file_diff::diff;
use filetime::{set_file_times, FileTime};
use uucore::backup_control::{self, BackupMode};
use uucore::entries::{grp2gid, usr2uid};
use uucore::perms::{wrap_chgrp, wrap_chown, Verbosity};
@@ -33,6 +34,7 @@ const DEFAULT_STRIP_PROGRAM: &str = "strip";
pub struct Behavior {
main_function: MainFunction,
specified_mode: Option<u32>,
backup_mode: BackupMode,
suffix: String,
owner: String,
group: String,
@@ -68,7 +70,7 @@ static ABOUT: &str = "Copy SOURCE to DEST or multiple SOURCE(s) to the existing
static OPT_COMPARE: &str = "compare";
static OPT_BACKUP: &str = "backup";
static OPT_BACKUP_2: &str = "backup2";
static OPT_BACKUP_NO_ARG: &str = "backup2";
static OPT_DIRECTORY: &str = "directory";
static OPT_IGNORED: &str = "ignored";
static OPT_CREATE_LEADING: &str = "create-leading";
@@ -130,14 +132,17 @@ pub fn uu_app() -> App<'static, 'static> {
.arg(
Arg::with_name(OPT_BACKUP)
.long(OPT_BACKUP)
.help("(unimplemented) make a backup of each existing destination file")
.help("make a backup of each existing destination file")
.takes_value(true)
.require_equals(true)
.min_values(0)
.value_name("CONTROL")
)
.arg(
// TODO implement flag
Arg::with_name(OPT_BACKUP_2)
Arg::with_name(OPT_BACKUP_NO_ARG)
.short("b")
.help("(unimplemented) like --backup but does not accept an argument")
.help("like --backup but does not accept an argument")
)
.arg(
Arg::with_name(OPT_IGNORED)
@@ -210,7 +215,7 @@ pub fn uu_app() -> App<'static, 'static> {
Arg::with_name(OPT_SUFFIX)
.short("S")
.long(OPT_SUFFIX)
.help("(unimplemented) override the usual backup suffix")
.help("override the usual backup suffix")
.value_name("SUFFIX")
.takes_value(true)
.min_values(1)
@@ -265,13 +270,7 @@ pub fn uu_app() -> App<'static, 'static> {
///
///
fn check_unimplemented<'a>(matches: &ArgMatches) -> Result<(), &'a str> {
if matches.is_present(OPT_BACKUP) {
Err("--backup")
} else if matches.is_present(OPT_BACKUP_2) {
Err("-b")
} else if matches.is_present(OPT_SUFFIX) {
Err("--suffix, -S")
} else if matches.is_present(OPT_NO_TARGET_DIRECTORY) {
if matches.is_present(OPT_NO_TARGET_DIRECTORY) {
Err("--no-target-directory, -T")
} else if matches.is_present(OPT_PRESERVE_CONTEXT) {
Err("--preserve-context, -P")
@@ -309,18 +308,16 @@ fn behavior(matches: &ArgMatches) -> Result<Behavior, i32> {
None
};
let backup_suffix = if matches.is_present(OPT_SUFFIX) {
matches.value_of(OPT_SUFFIX).ok_or(1)?
} else {
"~"
};
let target_dir = matches.value_of(OPT_TARGET_DIRECTORY).map(|d| d.to_owned());
Ok(Behavior {
main_function,
specified_mode,
suffix: backup_suffix.to_string(),
backup_mode: backup_control::determine_backup_mode(
matches.is_present(OPT_BACKUP_NO_ARG) || matches.is_present(OPT_BACKUP),
matches.value_of(OPT_BACKUP),
),
suffix: backup_control::determine_backup_suffix(matches.value_of(OPT_SUFFIX)),
owner: matches.value_of(OPT_OWNER).unwrap_or("").to_string(),
group: matches.value_of(OPT_GROUP).unwrap_or("").to_string(),
verbose: matches.is_present(OPT_VERBOSE),
@@ -517,6 +514,28 @@ fn copy(from: &Path, to: &Path, b: &Behavior) -> Result<(), ()> {
if b.compare && !need_copy(from, to, b) {
return Ok(());
}
// Declare the path here as we may need it for the verbose output below.
let mut backup_path = None;
// Perform backup, if any, before overwriting 'to'
//
// The codes actually making use of the backup process don't seem to agree
// on how best to approach the issue. (mv and ln, for example)
if to.exists() {
backup_path = backup_control::get_backup_path(b.backup_mode, to, &b.suffix);
if let Some(ref backup_path) = backup_path {
// TODO!!
if let Err(err) = fs::rename(to, backup_path) {
show_error!(
"install: cannot backup file '{}' to '{}': {}",
to.display(),
backup_path.display(),
err
);
return Err(());
}
}
}
if from.to_string_lossy() == "/dev/null" {
/* workaround a limitation of fs::copy
@@ -624,7 +643,11 @@ fn copy(from: &Path, to: &Path, b: &Behavior) -> Result<(), ()> {
}
if b.verbose {
show_error!("'{}' -> '{}'", from.display(), to.display());
print!("'{}' -> '{}'", from.display(), to.display());
match backup_path {
Some(path) => println!(" (backup: '{}')", path.display()),
None => println!(),
}
}
Ok(())
+137 -121
View File
File diff suppressed because it is too large Load Diff
+3
View File
@@ -5,6 +5,9 @@
// * For the full copyright and license information, please view the LICENSE
// * file that was distributed with this source code.
// clippy bug https://github.com/rust-lang/rust-clippy/issues/7422
#![allow(clippy::nonstandard_macro_braces)]
#[macro_use]
extern crate uucore;
+3
View File
@@ -8,6 +8,9 @@
// spell-checker:ignore (paths) GPGHome
// clippy bug https://github.com/rust-lang/rust-clippy/issues/7422
#![allow(clippy::nonstandard_macro_braces)]
#[macro_use]
extern crate uucore;
+4 -6
View File
@@ -349,12 +349,10 @@ impl<'a> Pager<'a> {
let status = format!("--More--({})", status_inner);
let banner = match (self.silent, wrong_key) {
(true, Some(key)) => {
format!(
"{} [Unknown key: '{}'. Press 'h' for instructions. (unimplemented)]",
status, key
)
}
(true, Some(key)) => format!(
"{} [Unknown key: '{}'. Press 'h' for instructions. (unimplemented)]",
status, key
),
(true, None) => format!("{}[Press space to continue, 'q' to quit.]", status),
(false, Some(_)) => format!("{}{}", status, BELL),
(false, None) => status,
+1
View File
@@ -1267,6 +1267,7 @@ pub fn uu_app() -> App<'static, 'static> {
.help("sort by a key")
.long_help(LONG_HELP_KEYS)
.multiple(true)
.number_of_values(1)
.takes_value(true),
)
.arg(
+3
View File
@@ -8,6 +8,9 @@
// spell-checker:ignore (ToDO) filetime strptime utcoff strs datetime MMDDhhmm
// clippy bug https://github.com/rust-lang/rust-clippy/issues/7422
#![allow(clippy::nonstandard_macro_braces)]
pub extern crate filetime;
#[macro_use]
+2
View File
@@ -4,6 +4,8 @@
// *
// * For the full copyright and license information, please view the LICENSE
// * file that was distributed with this source code.
// clippy bug https://github.com/rust-lang/rust-clippy/issues/7422
#![allow(clippy::nonstandard_macro_braces)]
#[macro_use]
extern crate uucore;
+3
View File
@@ -7,6 +7,9 @@
// spell-checker:ignore (strings) ABCDEFGHIJKLMNOPQRSTUVWXYZ
// clippy bug https://github.com/rust-lang/rust-clippy/issues/7422
#![allow(clippy::nonstandard_macro_braces)]
extern crate data_encoding;
use self::data_encoding::{DecodeError, BASE32, BASE64};
+3
View File
@@ -27,6 +27,9 @@ macro_rules! show(
let e = $err;
uucore::error::set_exit_code(e.code());
eprintln!("{}: {}", executable!(), e);
if e.usage() {
eprintln!("Try '{} --help' for more information.", executable!());
}
})
);
+13
View File
@@ -37,6 +37,19 @@ pub fn determine_backup_suffix(supplied_suffix: Option<&str>) -> String {
}
}
/// # TODO
///
/// This function currently deviates slightly from how the [manual][1] describes
/// that it should work. In particular, the current implementation:
///
/// 1. Doesn't strictly respect the order in which to determine the backup type,
/// which is (in order of precedence)
/// 1. Take a valid value to the '--backup' option
/// 2. Take the value of the `VERSION_CONTROL` env var
/// 3. default to 'existing'
/// 2. Doesn't accept abbreviations to the 'backup_option' parameter
///
/// [1]: https://www.gnu.org/software/coreutils/manual/html_node/Backup-options.html
pub fn determine_backup_mode(backup_opt_exists: bool, backup_opt: Option<&str>) -> BackupMode {
if backup_opt_exists {
match backup_opt.map(String::from) {
+50
View File
@@ -144,6 +144,13 @@ impl UError {
UError::Custom(e) => e.code(),
}
}
pub fn usage(&self) -> bool {
match self {
UError::Common(e) => e.usage(),
UError::Custom(e) => e.usage(),
}
}
}
impl From<UCommonError> for UError {
@@ -220,6 +227,10 @@ pub trait UCustomError: Error {
fn code(&self) -> i32 {
1
}
fn usage(&self) -> bool {
false
}
}
impl From<Box<dyn UCustomError>> for i32 {
@@ -291,6 +302,37 @@ impl UCustomError for USimpleError {
}
}
#[derive(Debug)]
pub struct UUsageError {
pub code: i32,
pub message: String,
}
impl UUsageError {
#[allow(clippy::new_ret_no_self)]
pub fn new(code: i32, message: String) -> UError {
UError::Custom(Box::new(Self { code, message }))
}
}
impl Error for UUsageError {}
impl Display for UUsageError {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
self.message.fmt(f)
}
}
impl UCustomError for UUsageError {
fn code(&self) -> i32 {
self.code
}
fn usage(&self) -> bool {
true
}
}
/// Wrapper type around [`std::io::Error`].
///
/// The messages displayed by [`UIoError`] should match the error messages displayed by GNU
@@ -331,6 +373,10 @@ impl UIoError {
pub fn code(&self) -> i32 {
1
}
pub fn usage(&self) -> bool {
false
}
}
impl Error for UIoError {}
@@ -427,6 +473,10 @@ impl UCommonError {
pub fn code(&self) -> i32 {
1
}
pub fn usage(&self) -> bool {
false
}
}
impl From<UCommonError> for i32 {

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