mirror of
https://github.com/uutils/coreutils.git
synced 2026-06-10 15:48:22 -07:00
Merge branch 'main' into fs_extra
This commit is contained in:
@@ -20,6 +20,11 @@ on: [push, pull_request]
|
||||
permissions:
|
||||
contents: read # to fetch code (actions/checkout)
|
||||
|
||||
# End the current execution if there is a new changeset in the PR.
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
|
||||
|
||||
jobs:
|
||||
cargo-deny:
|
||||
name: Style/cargo-deny
|
||||
|
||||
@@ -14,6 +14,11 @@ on: [push, pull_request]
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
# End the current execution if there is a new changeset in the PR.
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
|
||||
|
||||
jobs:
|
||||
gnu:
|
||||
permissions:
|
||||
@@ -196,6 +201,9 @@ jobs:
|
||||
REF_LOG_FILE='${{ steps.vars.outputs.path_reference }}/test-logs/test-suite.log'
|
||||
REF_SUMMARY_FILE='${{ steps.vars.outputs.path_reference }}/test-summary/gnu-result.json'
|
||||
REPO_DEFAULT_BRANCH='${{ steps.vars.outputs.repo_default_branch }}'
|
||||
# https://github.com/uutils/coreutils/issues/4294
|
||||
# https://github.com/uutils/coreutils/issues/4295
|
||||
IGNORE_INTERMITTENT='tests/tail-2/inotify-dir-recreate tests/misc/timeout tests/rm/rm1'
|
||||
|
||||
mkdir -p ${{ steps.vars.outputs.path_reference }}
|
||||
|
||||
@@ -227,10 +235,18 @@ jobs:
|
||||
do
|
||||
if ! grep -Fxq ${LINE}<<<"${REF_FAILING}"
|
||||
then
|
||||
MSG="GNU test failed: ${LINE}. ${LINE} is passing on '${{ steps.vars.outputs.repo_default_branch }}'. Maybe you have to rebase?"
|
||||
echo "::error ::$MSG"
|
||||
echo $MSG >> ${COMMENT_LOG}
|
||||
have_new_failures="true"
|
||||
if ! grep ${LINE} ${IGNORE_INTERMITTENT}
|
||||
then
|
||||
MSG="GNU test failed: ${LINE}. ${LINE} is passing on '${{ steps.vars.outputs.repo_default_branch }}'. Maybe you have to rebase?"
|
||||
echo "::error ::$MSG"
|
||||
echo $MSG >> ${COMMENT_LOG}
|
||||
have_new_failures="true"
|
||||
else
|
||||
MSG="Skip an intermittent issue ${LINE}"
|
||||
echo "::warning ::$MSG"
|
||||
echo $MSG >> ${COMMENT_LOG}
|
||||
echo ""
|
||||
fi
|
||||
fi
|
||||
done
|
||||
for LINE in ${REF_ERROR}
|
||||
|
||||
Generated
+7
-2
@@ -878,6 +878,12 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fundu"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "925250bc259498d4008ee072bf16586083ab2c491aa4b06b3c4d0a6556cebd74"
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.25"
|
||||
@@ -2807,7 +2813,6 @@ version = "0.0.17"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"libc",
|
||||
"num_cpus",
|
||||
"uucore",
|
||||
]
|
||||
|
||||
@@ -3094,9 +3099,9 @@ version = "0.0.17"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"clap",
|
||||
"fundu",
|
||||
"libc",
|
||||
"memchr",
|
||||
"nix",
|
||||
"notify",
|
||||
"same-file",
|
||||
"uucore",
|
||||
|
||||
+2
-1
@@ -1,7 +1,7 @@
|
||||
# coreutils (uutils)
|
||||
# * see the repository LICENSE, README, and CONTRIBUTING files for more information
|
||||
|
||||
# spell-checker:ignore (libs) libselinux gethostid procfs bigdecimal kqueue
|
||||
# spell-checker:ignore (libs) libselinux gethostid procfs bigdecimal kqueue fundu
|
||||
|
||||
[package]
|
||||
name = "coreutils"
|
||||
@@ -282,6 +282,7 @@ filetime = "0.2"
|
||||
fnv = "1.0.7"
|
||||
fs_extra = "1.1.0"
|
||||
fts-sys = "0.2"
|
||||
fundu = "0.3.0"
|
||||
gcd = "2.2"
|
||||
glob = "0.3.0"
|
||||
half = "2.1"
|
||||
|
||||
@@ -43,13 +43,23 @@ pacman -S uutils-coreutils
|
||||
|
||||
### Debian
|
||||
|
||||
[](https://packages.debian.org/sid/source/rust-coreutils)
|
||||
[](https://packages.debian.org/sid/source/rust-coreutils)
|
||||
|
||||
```bash
|
||||
apt install rust-coreutils
|
||||
# To use it:
|
||||
export PATH=/usr/lib/cargo/bin/coreutils:$PATH
|
||||
```
|
||||
|
||||
> **Note**: Requires the `unstable` repository.
|
||||
> **Note**: Only available from Bookworm (Debian 12)
|
||||
|
||||
### Gentoo
|
||||
|
||||
[](https://packages.gentoo.org/packages/sys-apps/uutils)
|
||||
|
||||
```bash
|
||||
emerge -pv sys-apps/uutils
|
||||
```
|
||||
|
||||
### Manjaro
|
||||

|
||||
@@ -69,6 +79,18 @@ pamac install uutils-coreutils
|
||||
nix-env -iA nixos.uutils-coreutils
|
||||
```
|
||||
|
||||
### Ubuntu
|
||||
|
||||
[](https://packages.ubuntu.com/source/lunar/rust-coreutils)
|
||||
|
||||
```bash
|
||||
apt install rust-coreutils
|
||||
# To use it:
|
||||
export PATH=/usr/lib/cargo/bin/coreutils:$PATH
|
||||
```
|
||||
|
||||
> **Note**: Only available from Kinetic (Ubuntu 22.10)
|
||||
|
||||
## MacOS
|
||||
|
||||
### Homebrew
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
# cat
|
||||
|
||||
## Usage
|
||||
```
|
||||
cat [OPTION]... [FILE]...
|
||||
```
|
||||
|
||||
## About
|
||||
|
||||
Concatenate FILE(s), or standard input, to standard output
|
||||
With no FILE, or when FILE is -, read standard input.
|
||||
@@ -33,11 +33,10 @@ use std::net::Shutdown;
|
||||
use std::os::unix::fs::FileTypeExt;
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::net::UnixStream;
|
||||
use uucore::format_usage;
|
||||
use uucore::{format_usage, help_section, help_usage};
|
||||
|
||||
static USAGE: &str = "{} [OPTION]... [FILE]...";
|
||||
static ABOUT: &str = "Concatenate FILE(s), or standard input, to standard output
|
||||
With no FILE, or when FILE is -, read standard input.";
|
||||
const USAGE: &str = help_usage!("cat.md");
|
||||
const ABOUT: &str = help_section!("about", "cat.md");
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
enum CatError {
|
||||
|
||||
+18
-20
@@ -12,15 +12,21 @@ use std::fs;
|
||||
use std::os::unix::fs::{MetadataExt, PermissionsExt};
|
||||
use std::path::Path;
|
||||
use uucore::display::Quotable;
|
||||
use uucore::error::{ExitCode, UResult, USimpleError, UUsageError};
|
||||
use uucore::error::{set_exit_code, ExitCode, UResult, USimpleError, UUsageError};
|
||||
use uucore::fs::display_permissions_unix;
|
||||
use uucore::libc::mode_t;
|
||||
#[cfg(not(windows))]
|
||||
use uucore::mode;
|
||||
use uucore::{format_usage, show_error};
|
||||
use uucore::{format_usage, show, show_error};
|
||||
|
||||
static ABOUT: &str = "Change the mode of each FILE to MODE.
|
||||
With --reference, change the mode of each FILE to that of RFILE.";
|
||||
const ABOUT: &str = "Change the mode of each FILE to MODE.\n\
|
||||
With --reference, change the mode of each FILE to that of RFILE.";
|
||||
const USAGE: &str = "\
|
||||
{} [OPTION]... MODE[,MODE]... FILE...
|
||||
{} [OPTION]... OCTAL-MODE FILE...
|
||||
{} [OPTION]... --reference=RFILE FILE...";
|
||||
const LONG_USAGE: &str =
|
||||
"Each MODE is of the form '[ugoa]*([-+=]([rwxXst]*|[ugo]))+|[-+=]?[0-7]+'.";
|
||||
|
||||
mod options {
|
||||
pub const CHANGES: &str = "changes";
|
||||
@@ -34,15 +40,6 @@ mod options {
|
||||
pub const FILE: &str = "FILE";
|
||||
}
|
||||
|
||||
const USAGE: &str = "\
|
||||
{} [OPTION]... MODE[,MODE]... FILE...
|
||||
{} [OPTION]... OCTAL-MODE FILE...
|
||||
{} [OPTION]... --reference=RFILE FILE...";
|
||||
|
||||
fn get_long_usage() -> &'static str {
|
||||
"Each MODE is of the form '[ugoa]*([-+=]([rwxXst]*|[ugo]))+|[-+=]?[0-7]+'."
|
||||
}
|
||||
|
||||
#[uucore::main]
|
||||
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
let mut args = args.collect_lossy();
|
||||
@@ -51,9 +48,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
// a possible MODE prefix '-' needs to be removed (e.g. "chmod -x FILE").
|
||||
let mode_had_minus_prefix = mode::strip_minus_from_mode(&mut args);
|
||||
|
||||
let after_help = get_long_usage();
|
||||
|
||||
let matches = uu_app().after_help(after_help).try_get_matches_from(args)?;
|
||||
let matches = uu_app().after_help(LONG_USAGE).try_get_matches_from(args)?;
|
||||
|
||||
let changes = matches.get_flag(options::CHANGES);
|
||||
let quiet = matches.get_flag(options::QUIET);
|
||||
@@ -200,21 +195,24 @@ impl Chmoder {
|
||||
filename.quote()
|
||||
);
|
||||
if !self.quiet {
|
||||
return Err(USimpleError::new(
|
||||
show!(USimpleError::new(
|
||||
1,
|
||||
format!("cannot operate on dangling symlink {}", filename.quote()),
|
||||
));
|
||||
}
|
||||
} else if !self.quiet {
|
||||
return Err(USimpleError::new(
|
||||
show!(USimpleError::new(
|
||||
1,
|
||||
format!(
|
||||
"cannot access {}: No such file or directory",
|
||||
filename.quote()
|
||||
),
|
||||
)
|
||||
));
|
||||
}
|
||||
return Err(ExitCode::new(1));
|
||||
// GNU exits with exit code 1 even if -q or --quiet are passed
|
||||
// So we set the exit code, because it hasn't been set yet if `self.quiet` is true.
|
||||
set_exit_code(1);
|
||||
continue;
|
||||
}
|
||||
if self.recursive && self.preserve_root && filename == "/" {
|
||||
return Err(USimpleError::new(
|
||||
|
||||
+19
-19
@@ -32,21 +32,12 @@ mod options {
|
||||
pub const TOTAL: &str = "total";
|
||||
}
|
||||
|
||||
fn mkdelim(col: usize, opts: &ArgMatches) -> String {
|
||||
let mut s = String::new();
|
||||
let delim = match opts.get_one::<String>(options::DELIMITER).unwrap().as_str() {
|
||||
"" => "\0",
|
||||
delim => delim,
|
||||
};
|
||||
|
||||
if col > 1 && !opts.get_flag(options::COLUMN_1) {
|
||||
s.push_str(delim.as_ref());
|
||||
fn column_width(col: &str, opts: &ArgMatches) -> usize {
|
||||
if opts.get_flag(col) {
|
||||
0
|
||||
} else {
|
||||
1
|
||||
}
|
||||
if col > 2 && !opts.get_flag(options::COLUMN_2) {
|
||||
s.push_str(delim.as_ref());
|
||||
}
|
||||
|
||||
s
|
||||
}
|
||||
|
||||
fn ensure_nl(line: &mut String) {
|
||||
@@ -70,7 +61,16 @@ impl LineReader {
|
||||
}
|
||||
|
||||
fn comm(a: &mut LineReader, b: &mut LineReader, opts: &ArgMatches) {
|
||||
let delim: Vec<String> = (0..4).map(|col| mkdelim(col, opts)).collect();
|
||||
let delim = match opts.get_one::<String>(options::DELIMITER).unwrap().as_str() {
|
||||
"" => "\0",
|
||||
delim => delim,
|
||||
};
|
||||
|
||||
let width_col_1 = column_width(options::COLUMN_1, opts);
|
||||
let width_col_2 = column_width(options::COLUMN_2, opts);
|
||||
|
||||
let delim_col_2 = delim.repeat(width_col_1);
|
||||
let delim_col_3 = delim.repeat(width_col_1 + width_col_2);
|
||||
|
||||
let ra = &mut String::new();
|
||||
let mut na = a.read_line(ra);
|
||||
@@ -98,7 +98,7 @@ fn comm(a: &mut LineReader, b: &mut LineReader, opts: &ArgMatches) {
|
||||
Ordering::Less => {
|
||||
if !opts.get_flag(options::COLUMN_1) {
|
||||
ensure_nl(ra);
|
||||
print!("{}{}", delim[1], ra);
|
||||
print!("{ra}");
|
||||
}
|
||||
ra.clear();
|
||||
na = a.read_line(ra);
|
||||
@@ -107,7 +107,7 @@ fn comm(a: &mut LineReader, b: &mut LineReader, opts: &ArgMatches) {
|
||||
Ordering::Greater => {
|
||||
if !opts.get_flag(options::COLUMN_2) {
|
||||
ensure_nl(rb);
|
||||
print!("{}{}", delim[2], rb);
|
||||
print!("{delim_col_2}{rb}");
|
||||
}
|
||||
rb.clear();
|
||||
nb = b.read_line(rb);
|
||||
@@ -116,7 +116,7 @@ fn comm(a: &mut LineReader, b: &mut LineReader, opts: &ArgMatches) {
|
||||
Ordering::Equal => {
|
||||
if !opts.get_flag(options::COLUMN_3) {
|
||||
ensure_nl(ra);
|
||||
print!("{}{}", delim[3], ra);
|
||||
print!("{delim_col_3}{ra}");
|
||||
}
|
||||
ra.clear();
|
||||
rb.clear();
|
||||
@@ -128,7 +128,7 @@ fn comm(a: &mut LineReader, b: &mut LineReader, opts: &ArgMatches) {
|
||||
}
|
||||
|
||||
if opts.get_flag(options::TOTAL) {
|
||||
println!("{total_col_1}\t{total_col_2}\t{total_col_3}\ttotal");
|
||||
println!("{total_col_1}{delim}{total_col_2}{delim}{total_col_3}{delim}total");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
# cp
|
||||
|
||||
## Usage
|
||||
```
|
||||
cp [OPTION]... [-T] SOURCE DEST
|
||||
cp [OPTION]... SOURCE... DIRECTORY
|
||||
cp [OPTION]... -t DIRECTORY SOURCE...
|
||||
```
|
||||
|
||||
## About
|
||||
|
||||
Copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY.
|
||||
+3
-6
@@ -40,7 +40,7 @@ use uucore::error::{set_exit_code, UClapError, UError, UResult, UUsageError};
|
||||
use uucore::fs::{
|
||||
canonicalize, paths_refer_to_same_file, FileInformation, MissingHandling, ResolveMode,
|
||||
};
|
||||
use uucore::{crash, format_usage, prompt_yes, show_error, show_warning};
|
||||
use uucore::{crash, format_usage, help_section, help_usage, prompt_yes, show_error, show_warning};
|
||||
|
||||
use crate::copydir::copy_directory;
|
||||
|
||||
@@ -228,13 +228,10 @@ pub struct Options {
|
||||
progress_bar: bool,
|
||||
}
|
||||
|
||||
static ABOUT: &str = "Copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY.";
|
||||
const ABOUT: &str = help_section!("about", "cp.md");
|
||||
static EXIT_ERR: i32 = 1;
|
||||
|
||||
const USAGE: &str = "\
|
||||
{} [OPTION]... [-T] SOURCE DEST
|
||||
{} [OPTION]... SOURCE... DIRECTORY
|
||||
{} [OPTION]... -t DIRECTORY SOURCE...";
|
||||
const USAGE: &str = help_usage!("cp.md");
|
||||
|
||||
// Argument constants
|
||||
mod options {
|
||||
|
||||
@@ -11,26 +11,22 @@ use uucore::display::print_verbatim;
|
||||
use uucore::error::{UResult, UUsageError};
|
||||
use uucore::format_usage;
|
||||
|
||||
static ABOUT: &str = "Strip last component from file name";
|
||||
const ABOUT: &str = "Strip last component from file name";
|
||||
const USAGE: &str = "{} [OPTION] NAME...";
|
||||
const LONG_USAGE: &str = "\
|
||||
Output each NAME with its last non-slash component and trailing slashes \n\
|
||||
removed; if NAME contains no /'s, output '.' (meaning the current directory).";
|
||||
|
||||
mod options {
|
||||
pub const ZERO: &str = "zero";
|
||||
pub const DIR: &str = "dir";
|
||||
}
|
||||
|
||||
fn get_long_usage() -> &'static str {
|
||||
"Output each NAME with its last non-slash component and trailing slashes \n\
|
||||
removed; if NAME contains no /'s, output '.' (meaning the current directory)."
|
||||
}
|
||||
|
||||
#[uucore::main]
|
||||
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
let args = args.collect_lossy();
|
||||
|
||||
let matches = uu_app()
|
||||
.after_help(get_long_usage())
|
||||
.try_get_matches_from(args)?;
|
||||
let matches = uu_app().after_help(LONG_USAGE).try_get_matches_from(args)?;
|
||||
|
||||
let separator = if matches.get_flag(options::ZERO) {
|
||||
"\0"
|
||||
|
||||
@@ -22,8 +22,10 @@ use uucore::{format_usage, show, show_if_err};
|
||||
|
||||
static DEFAULT_PERM: u32 = 0o755;
|
||||
|
||||
static ABOUT: &str = "Create the given DIRECTORY(ies) if they do not exist";
|
||||
const ABOUT: &str = "Create the given DIRECTORY(ies) if they do not exist";
|
||||
const USAGE: &str = "{} [OPTION]... [USER]";
|
||||
const LONG_USAGE: &str =
|
||||
"Each MODE is of the form '[ugoa]*([-+=]([rwxXst]*|[ugo]))+|[-+=]?[0-7]+'.";
|
||||
|
||||
mod options {
|
||||
pub const MODE: &str = "mode";
|
||||
@@ -32,10 +34,6 @@ mod options {
|
||||
pub const DIRS: &str = "dirs";
|
||||
}
|
||||
|
||||
fn get_long_usage() -> &'static str {
|
||||
"Each MODE is of the form '[ugoa]*([-+=]([rwxXst]*|[ugo]))+|[-+=]?[0-7]+'."
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn get_mode(_matches: &ArgMatches, _mode_had_minus_prefix: bool) -> Result<u32, String> {
|
||||
Ok(DEFAULT_PERM)
|
||||
@@ -92,9 +90,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
// Linux-specific options, not implemented
|
||||
// opts.optflag("Z", "context", "set SELinux security context" +
|
||||
// " of each created directory to CTX"),
|
||||
let matches = uu_app()
|
||||
.after_help(get_long_usage())
|
||||
.try_get_matches_from(args)?;
|
||||
let matches = uu_app().after_help(LONG_USAGE).try_get_matches_from(args)?;
|
||||
|
||||
let dirs = matches
|
||||
.get_many::<OsString>(options::DIRS)
|
||||
|
||||
@@ -16,7 +16,6 @@ path = "src/nproc.rs"
|
||||
|
||||
[dependencies]
|
||||
libc = { workspace=true }
|
||||
num_cpus = { workspace=true }
|
||||
clap = { workspace=true }
|
||||
uucore = { workspace=true, features=["fs"] }
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
// spell-checker:ignore (ToDO) NPROCESSORS nprocs numstr threadstr sysconf
|
||||
|
||||
use clap::{crate_version, Arg, ArgAction, Command};
|
||||
use std::env;
|
||||
use std::{env, thread};
|
||||
use uucore::display::Quotable;
|
||||
use uucore::error::{UResult, USimpleError};
|
||||
use uucore::format_usage;
|
||||
@@ -73,16 +73,16 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
// If OMP_NUM_THREADS=0, rejects the value
|
||||
let thread: Vec<&str> = threadstr.split_terminator(',').collect();
|
||||
match &thread[..] {
|
||||
[] => num_cpus::get(),
|
||||
[] => available_parallelism(),
|
||||
[s, ..] => match s.parse() {
|
||||
Ok(0) | Err(_) => num_cpus::get(),
|
||||
Ok(0) | Err(_) => available_parallelism(),
|
||||
Ok(n) => n,
|
||||
},
|
||||
}
|
||||
}
|
||||
// the variable 'OMP_NUM_THREADS' doesn't exist
|
||||
// fallback to the regular CPU detection
|
||||
Err(_) => num_cpus::get(),
|
||||
Err(_) => available_parallelism(),
|
||||
}
|
||||
};
|
||||
|
||||
@@ -127,7 +127,7 @@ fn num_cpus_all() -> usize {
|
||||
if nprocs == 1 {
|
||||
// In some situation, /proc and /sys are not mounted, and sysconf returns 1.
|
||||
// However, we want to guarantee that `nproc --all` >= `nproc`.
|
||||
num_cpus::get()
|
||||
available_parallelism()
|
||||
} else if nprocs > 0 {
|
||||
nprocs as usize
|
||||
} else {
|
||||
@@ -135,7 +135,7 @@ fn num_cpus_all() -> usize {
|
||||
}
|
||||
}
|
||||
|
||||
// Other platforms (e.g., windows), num_cpus::get() directly.
|
||||
// Other platforms (e.g., windows), available_parallelism() directly.
|
||||
#[cfg(not(any(
|
||||
target_os = "linux",
|
||||
target_vendor = "apple",
|
||||
@@ -143,5 +143,14 @@ fn num_cpus_all() -> usize {
|
||||
target_os = "netbsd"
|
||||
)))]
|
||||
fn num_cpus_all() -> usize {
|
||||
num_cpus::get()
|
||||
available_parallelism()
|
||||
}
|
||||
|
||||
// In some cases, thread::available_parallelism() may return an Err
|
||||
// In this case, we will return 1 (like GNU)
|
||||
fn available_parallelism() -> usize {
|
||||
match thread::available_parallelism() {
|
||||
Ok(n) => n.get(),
|
||||
Err(_) => 1,
|
||||
}
|
||||
}
|
||||
|
||||
+16
-21
@@ -37,8 +37,22 @@ struct Options {
|
||||
verbose: bool,
|
||||
}
|
||||
|
||||
static ABOUT: &str = "Remove (unlink) the FILE(s)";
|
||||
const ABOUT: &str = "Remove (unlink) the FILE(s)";
|
||||
const USAGE: &str = "{} [OPTION]... FILE...";
|
||||
const LONG_USAGE: &str = "\
|
||||
By default, rm does not remove directories. Use the --recursive (-r or -R)
|
||||
option to remove each listed directory, too, along with all of its contents
|
||||
|
||||
To remove a file whose name starts with a '-', for example '-foo',
|
||||
use one of these commands:
|
||||
rm -- -foo
|
||||
|
||||
rm ./-foo
|
||||
|
||||
Note that if you use rm to remove a file, it might be possible to recover
|
||||
some of its contents, given sufficient expertise and/or time. For greater
|
||||
assurance that the contents are truly unrecoverable, consider using shred.";
|
||||
|
||||
static OPT_DIR: &str = "dir";
|
||||
static OPT_INTERACTIVE: &str = "interactive";
|
||||
static OPT_FORCE: &str = "force";
|
||||
@@ -53,28 +67,9 @@ static PRESUME_INPUT_TTY: &str = "-presume-input-tty";
|
||||
|
||||
static ARG_FILES: &str = "files";
|
||||
|
||||
fn get_long_usage() -> String {
|
||||
String::from(
|
||||
"By default, rm does not remove directories. Use the --recursive (-r or -R)
|
||||
option to remove each listed directory, too, along with all of its contents
|
||||
|
||||
To remove a file whose name starts with a '-', for example '-foo',
|
||||
use one of these commands:
|
||||
rm -- -foo
|
||||
|
||||
rm ./-foo
|
||||
|
||||
Note that if you use rm to remove a file, it might be possible to recover
|
||||
some of its contents, given sufficient expertise and/or time. For greater
|
||||
assurance that the contents are truly unrecoverable, consider using shred.",
|
||||
)
|
||||
}
|
||||
|
||||
#[uucore::main]
|
||||
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
let matches = uu_app()
|
||||
.after_help(get_long_usage())
|
||||
.try_get_matches_from(args)?;
|
||||
let matches = uu_app().after_help(LONG_USAGE).try_get_matches_from(args)?;
|
||||
|
||||
let files: Vec<String> = matches
|
||||
.get_many::<String>(ARG_FILES)
|
||||
|
||||
+5
-62
@@ -13,7 +13,7 @@ use uucore::fsext::{
|
||||
pretty_filetype, pretty_fstype, pretty_time, read_fs_list, statfs, BirthTime, FsMeta,
|
||||
};
|
||||
use uucore::libc::mode_t;
|
||||
use uucore::{entries, format_usage, show_error, show_warning};
|
||||
use uucore::{entries, format_usage, help_section, help_usage, show_error, show_warning};
|
||||
|
||||
use clap::{crate_version, Arg, ArgAction, ArgMatches, Command};
|
||||
use std::borrow::Cow;
|
||||
@@ -24,8 +24,9 @@ use std::os::unix::fs::{FileTypeExt, MetadataExt};
|
||||
use std::os::unix::prelude::OsStrExt;
|
||||
use std::path::Path;
|
||||
|
||||
const ABOUT: &str = "Display file or file system status.";
|
||||
const USAGE: &str = "{} [OPTION]... FILE...";
|
||||
const ABOUT: &str = help_section!("about", "stat.md");
|
||||
const USAGE: &str = help_usage!("stat.md");
|
||||
const LONG_USAGE: &str = help_section!("long usage", "stat.md");
|
||||
|
||||
mod options {
|
||||
pub const DEREFERENCE: &str = "dereference";
|
||||
@@ -751,67 +752,9 @@ impl Stater {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_long_usage() -> &'static str {
|
||||
"
|
||||
The valid format sequences for files (without --file-system):
|
||||
|
||||
%a access rights in octal (note '#' and '0' printf flags)
|
||||
%A access rights in human readable form
|
||||
%b number of blocks allocated (see %B)
|
||||
%B the size in bytes of each block reported by %b
|
||||
%C SELinux security context string
|
||||
%d device number in decimal
|
||||
%D device number in hex
|
||||
%f raw mode in hex
|
||||
%F file type
|
||||
%g group ID of owner
|
||||
%G group name of owner
|
||||
%h number of hard links
|
||||
%i inode number
|
||||
%m mount point
|
||||
%n file name
|
||||
%N quoted file name with dereference if symbolic link
|
||||
%o optimal I/O transfer size hint
|
||||
%s total size, in bytes
|
||||
%t major device type in hex, for character/block device special files
|
||||
%T minor device type in hex, for character/block device special files
|
||||
%u user ID of owner
|
||||
%U user name of owner
|
||||
%w time of file birth, human-readable; - if unknown
|
||||
%W time of file birth, seconds since Epoch; 0 if unknown
|
||||
%x time of last access, human-readable
|
||||
%X time of last access, seconds since Epoch
|
||||
%y time of last data modification, human-readable
|
||||
%Y time of last data modification, seconds since Epoch
|
||||
%z time of last status change, human-readable
|
||||
%Z time of last status change, seconds since Epoch
|
||||
|
||||
Valid format sequences for file systems:
|
||||
|
||||
%a free blocks available to non-superuser
|
||||
%b total data blocks in file system
|
||||
%c total file nodes in file system
|
||||
%d free file nodes in file system
|
||||
%f free blocks in file system
|
||||
%i file system ID in hex
|
||||
%l maximum length of filenames
|
||||
%n file name
|
||||
%s block size (for faster transfers)
|
||||
%S fundamental block size (for block counts)
|
||||
%t file system type in hex
|
||||
%T file system type in human readable form
|
||||
|
||||
NOTE: your shell may have its own version of stat, which usually supersedes
|
||||
the version described here. Please refer to your shell's documentation
|
||||
for details about the options it supports.
|
||||
"
|
||||
}
|
||||
|
||||
#[uucore::main]
|
||||
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
let matches = uu_app()
|
||||
.after_help(get_long_usage())
|
||||
.try_get_matches_from(args)?;
|
||||
let matches = uu_app().after_help(LONG_USAGE).try_get_matches_from(args)?;
|
||||
|
||||
let stater = Stater::new(&matches)?;
|
||||
let exit_status = stater.exec();
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
# stat
|
||||
|
||||
## About
|
||||
|
||||
Display file or file system status.
|
||||
|
||||
## Usage
|
||||
```
|
||||
stat [OPTION]... FILE...
|
||||
```
|
||||
|
||||
## Long Usage
|
||||
|
||||
The valid format sequences for files (without `--file-system`):
|
||||
|
||||
%a access rights in octal (note '#' and '0' printf flags)
|
||||
%A access rights in human readable form
|
||||
%b number of blocks allocated (see %B)
|
||||
%B the size in bytes of each block reported by %b
|
||||
%C SELinux security context string
|
||||
%d device number in decimal
|
||||
%D device number in hex
|
||||
%f raw mode in hex
|
||||
%F file type
|
||||
%g group ID of owner
|
||||
%G group name of owner
|
||||
%h number of hard links
|
||||
%i inode number
|
||||
%m mount point
|
||||
%n file name
|
||||
%N quoted file name with dereference if symbolic link
|
||||
%o optimal I/O transfer size hint
|
||||
%s total size, in bytes
|
||||
%t major device type in hex, for character/block device special files
|
||||
%T minor device type in hex, for character/block device special files
|
||||
%u user ID of owner
|
||||
%U user name of owner
|
||||
%w time of file birth, human-readable; - if unknown
|
||||
%W time of file birth, seconds since Epoch; 0 if unknown
|
||||
%x time of last access, human-readable
|
||||
%X time of last access, seconds since Epoch
|
||||
%y time of last data modification, human-readable
|
||||
%Y time of last data modification, seconds since Epoch
|
||||
%z time of last status change, human-readable
|
||||
%Z time of last status change, seconds since Epoch
|
||||
|
||||
Valid format sequences for file systems:
|
||||
|
||||
%a free blocks available to non-superuser
|
||||
%b total data blocks in file system
|
||||
%c total file nodes in file system
|
||||
%d free file nodes in file system
|
||||
%f free blocks in file system
|
||||
%i file system ID in hex
|
||||
%l maximum length of filenames
|
||||
%n file name
|
||||
%s block size (for faster transfers)
|
||||
%S fundamental block size (for block counts)
|
||||
%t file system type in hex
|
||||
%T file system type in human readable form
|
||||
|
||||
NOTE: your shell may have its own version of stat, which usually supersedes
|
||||
the version described here. Please refer to your shell's documentation
|
||||
for details about the options it supports.
|
||||
@@ -1,3 +1,4 @@
|
||||
# spell-checker:ignore (libs) kqueue fundu
|
||||
[package]
|
||||
name = "uu_tail"
|
||||
version = "0.0.17"
|
||||
@@ -19,17 +20,15 @@ clap = { workspace=true }
|
||||
libc = { workspace=true }
|
||||
memchr = { workspace=true }
|
||||
notify = { workspace=true }
|
||||
uucore = { workspace=true, features=["ringbuffer", "lines"] }
|
||||
uucore = { workspace=true }
|
||||
same-file = { workspace=true }
|
||||
atty = { workspace=true }
|
||||
fundu = { workspace=true }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
windows-sys = { workspace=true, features = ["Win32_System_Threading", "Win32_Foundation"] }
|
||||
winapi-util = { workspace=true }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
nix = { workspace=true, features = ["fs"] }
|
||||
|
||||
[[bin]]
|
||||
name = "tail"
|
||||
path = "src/main.rs"
|
||||
|
||||
+16
-11
@@ -3,13 +3,14 @@
|
||||
// * For the full copyright and license information, please view the LICENSE
|
||||
// * file that was distributed with this source code.
|
||||
|
||||
// spell-checker:ignore (ToDO) kqueue Signum
|
||||
// spell-checker:ignore (ToDO) kqueue Signum fundu
|
||||
|
||||
use crate::paths::Input;
|
||||
use crate::{parse, platform, Quotable};
|
||||
use atty::Stream;
|
||||
use clap::crate_version;
|
||||
use clap::{parser::ValueSource, Arg, ArgAction, ArgMatches, Command};
|
||||
use fundu::DurationParser;
|
||||
use same_file::Handle;
|
||||
use std::collections::VecDeque;
|
||||
use std::ffi::OsString;
|
||||
@@ -148,16 +149,20 @@ impl Settings {
|
||||
settings.retry =
|
||||
matches.get_flag(options::RETRY) || matches.get_flag(options::FOLLOW_RETRY);
|
||||
|
||||
if let Some(s) = matches.get_one::<String>(options::SLEEP_INT) {
|
||||
settings.sleep_sec = match s.parse::<f32>() {
|
||||
Ok(s) => Duration::from_secs_f32(s),
|
||||
Err(_) => {
|
||||
return Err(UUsageError::new(
|
||||
1,
|
||||
format!("invalid number of seconds: {}", s.quote()),
|
||||
))
|
||||
}
|
||||
}
|
||||
if let Some(source) = matches.get_one::<String>(options::SLEEP_INT) {
|
||||
// Advantage of `fundu` over `Duration::(try_)from_secs_f64(source.parse().unwrap())`:
|
||||
// * doesn't panic on errors like `Duration::from_secs_f64` would.
|
||||
// * no precision loss, rounding errors or other floating point problems.
|
||||
// * evaluates to `Duration::MAX` if the parsed number would have exceeded
|
||||
// `DURATION::MAX` or `infinity` was given
|
||||
// * not applied here but it supports customizable time units and provides better error
|
||||
// messages
|
||||
settings.sleep_sec =
|
||||
DurationParser::without_time_units()
|
||||
.parse(source)
|
||||
.map_err(|_| {
|
||||
UUsageError::new(1, format!("invalid number of seconds: '{source}'"))
|
||||
})?;
|
||||
}
|
||||
|
||||
settings.use_polling = matches.get_flag(options::USE_POLLING);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user