1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 11:37:44 +00:00

Merge branch 'main' into tail_notify

This commit is contained in:
Jan Scheer 2022-04-19 22:25:35 +02:00
commit 7228902e55
No known key found for this signature in database
GPG key ID: C62AD4C29E2B9828
196 changed files with 3582 additions and 1752 deletions

View file

@ -67,7 +67,7 @@ jobs:
- name: Install `rust` toolchain - name: Install `rust` toolchain
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
with: with:
toolchain: nightly-2022-03-21 toolchain: nightly
default: true default: true
profile: minimal profile: minimal
- name: Install `cargo-udeps` - name: Install `cargo-udeps`
@ -86,7 +86,7 @@ jobs:
fault_type="${{ steps.vars.outputs.FAULT_TYPE }}" fault_type="${{ steps.vars.outputs.FAULT_TYPE }}"
fault_prefix=$(echo "$fault_type" | tr '[:lower:]' '[:upper:]') fault_prefix=$(echo "$fault_type" | tr '[:lower:]' '[:upper:]')
# #
cargo +nightly-2022-03-21 udeps ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} --all-targets &> udeps.log || cat udeps.log cargo +nightly udeps ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} --all-targets &> udeps.log || cat udeps.log
grep --ignore-case "all deps seem to have been used" udeps.log || { printf "%s\n" "::${fault_type} ::${fault_prefix}: \`cargo udeps\`: style violation (unused dependency found)" ; fault=true ; } grep --ignore-case "all deps seem to have been used" udeps.log || { printf "%s\n" "::${fault_type} ::${fault_prefix}: \`cargo udeps\`: style violation (unused dependency found)" ; fault=true ; }
if [ -n "${{ steps.vars.outputs.FAIL_ON_FAULT }}" ] && [ -n "$fault" ]; then exit 1 ; fi if [ -n "${{ steps.vars.outputs.FAIL_ON_FAULT }}" ] && [ -n "$fault" ]; then exit 1 ; fi
@ -483,7 +483,7 @@ jobs:
- name: Install `rust` toolchain - name: Install `rust` toolchain
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
with: with:
toolchain: nightly-2022-03-21 toolchain: nightly
default: true default: true
profile: minimal # minimal component installation (ie, no documentation) profile: minimal # minimal component installation (ie, no documentation)
- name: Test - name: Test
@ -759,7 +759,7 @@ jobs:
with: with:
use-cross: ${{ steps.vars.outputs.CARGO_USE_CROSS }} use-cross: ${{ steps.vars.outputs.CARGO_USE_CROSS }}
command: test command: test
args: --target=${{ matrix.job.target }} ${{ steps.vars.outputs.CARGO_TEST_OPTIONS}} ${{ matrix.job.cargo-options }} ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} ${{ steps.dep_vars.outputs.CARGO_UTILITY_LIST_OPTIONS }} args: --target=${{ matrix.job.target }} ${{ steps.vars.outputs.CARGO_TEST_OPTIONS}} ${{ matrix.job.cargo-options }} ${{ steps.dep_vars.outputs.CARGO_UTILITY_LIST_OPTIONS }}
toolchain: ${{ env.RUST_MIN_SRV }} toolchain: ${{ env.RUST_MIN_SRV }}
- name: Archive executable artifacts - name: Archive executable artifacts
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v2
@ -935,7 +935,7 @@ jobs:
## VARs setup ## VARs setup
outputs() { step_id="vars"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; } outputs() { step_id="vars"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; }
# toolchain # toolchain
TOOLCHAIN="nightly-2022-03-21" ## default to "nightly" toolchain (required for certain required unstable compiler flags) ## !maint: refactor when stable channel has needed support TOOLCHAIN="nightly" ## default to "nightly" toolchain (required for certain required unstable compiler flags) ## !maint: refactor when stable channel has needed support
# * specify gnu-type TOOLCHAIN for windows; `grcov` requires gnu-style code coverage data files # * specify gnu-type TOOLCHAIN for windows; `grcov` requires gnu-style code coverage data files
case ${{ matrix.job.os }} in windows-*) TOOLCHAIN="$TOOLCHAIN-x86_64-pc-windows-gnu" ;; esac; case ${{ matrix.job.os }} in windows-*) TOOLCHAIN="$TOOLCHAIN-x86_64-pc-windows-gnu" ;; esac;
# * use requested TOOLCHAIN if specified # * use requested TOOLCHAIN if specified
@ -994,7 +994,7 @@ jobs:
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: test command: test
args: ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} --no-fail-fast -p uucore args: --no-fail-fast -p uucore
env: env:
CARGO_INCREMENTAL: "0" CARGO_INCREMENTAL: "0"
RUSTC_WRAPPER: "" RUSTC_WRAPPER: ""
@ -1016,7 +1016,7 @@ jobs:
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: test command: test
args: ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} --no-fail-fast ${{ steps.dep_vars.outputs.CARGO_UTILITY_LIST_OPTIONS }} args: --no-fail-fast ${{ steps.dep_vars.outputs.CARGO_UTILITY_LIST_OPTIONS }}
env: env:
CARGO_INCREMENTAL: "0" CARGO_INCREMENTAL: "0"
RUSTC_WRAPPER: "" RUSTC_WRAPPER: ""

View file

@ -25,7 +25,7 @@ jobs:
outputs path_GNU path_GNU_tests path_reference path_UUTILS outputs path_GNU path_GNU_tests path_reference path_UUTILS
# #
repo_default_branch="${{ github.event.repository.default_branch }}" repo_default_branch="${{ github.event.repository.default_branch }}"
repo_GNU_ref="v9.0" repo_GNU_ref="v9.1"
repo_reference_branch="${{ github.event.repository.default_branch }}" repo_reference_branch="${{ github.event.repository.default_branch }}"
outputs repo_default_branch repo_GNU_ref repo_reference_branch outputs repo_default_branch repo_GNU_ref repo_reference_branch
# #
@ -216,12 +216,12 @@ jobs:
with: with:
repository: 'coreutils/coreutils' repository: 'coreutils/coreutils'
path: 'gnu' path: 'gnu'
ref: 'v9.0' ref: 'v9.1'
submodules: recursive submodules: recursive
- name: Install `rust` toolchain - name: Install `rust` toolchain
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
with: with:
toolchain: nightly-2022-03-21 toolchain: nightly
default: true default: true
profile: minimal # minimal component installation (ie, no documentation) profile: minimal # minimal component installation (ie, no documentation)
components: rustfmt components: rustfmt

View file

@ -179,3 +179,4 @@ Smigle00
anonymousknight anonymousknight
kwantam kwantam
nicoo nicoo
gmnsii

View file

@ -93,6 +93,7 @@ rollup
sed sed
selinuxenabled selinuxenabled
sestatus sestatus
vdir
wslpath wslpath
xargs xargs

531
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -16,7 +16,7 @@ repository = "https://github.com/uutils/coreutils"
readme = "README.md" readme = "README.md"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
build = "build.rs" build = "build.rs"
@ -45,6 +45,7 @@ feat_common_core = [
"cut", "cut",
"date", "date",
"df", "df",
"dir",
"dircolors", "dircolors",
"dirname", "dirname",
"dd", "dd",
@ -100,6 +101,7 @@ feat_common_core = [
"unexpand", "unexpand",
"uniq", "uniq",
"unlink", "unlink",
"vdir",
"wc", "wc",
"yes", "yes",
] ]
@ -252,8 +254,7 @@ lazy_static = { version="1.3" }
textwrap = { version="0.15", features=["terminal_size"] } textwrap = { version="0.15", features=["terminal_size"] }
uucore = { version=">=0.0.11", package="uucore", path="src/uucore" } uucore = { version=">=0.0.11", package="uucore", path="src/uucore" }
selinux = { version="0.2", optional = true } selinux = { version="0.2", optional = true }
ureq = "2.4.0" zip = { version = "0.6.0", default_features=false, features=["deflate"] }
zip = { version = "0.5.13", default_features=false, features=["deflate"] }
# * uutils # * uutils
uu_test = { optional=true, version="0.0.13", package="uu_test", path="src/uu/test" } uu_test = { optional=true, version="0.0.13", package="uu_test", path="src/uu/test" }
# #
@ -276,6 +277,7 @@ cut = { optional=true, version="0.0.13", package="uu_cut", path="src/uu/cut
date = { optional=true, version="0.0.13", package="uu_date", path="src/uu/date" } date = { optional=true, version="0.0.13", package="uu_date", path="src/uu/date" }
dd = { optional=true, version="0.0.13", package="uu_dd", path="src/uu/dd" } dd = { optional=true, version="0.0.13", package="uu_dd", path="src/uu/dd" }
df = { optional=true, version="0.0.13", package="uu_df", path="src/uu/df" } df = { optional=true, version="0.0.13", package="uu_df", path="src/uu/df" }
dir = { optional=true, version="0.0.13", package="uu_dir", path="src/uu/dir" }
dircolors= { optional=true, version="0.0.13", package="uu_dircolors", path="src/uu/dircolors" } dircolors= { optional=true, version="0.0.13", package="uu_dircolors", path="src/uu/dircolors" }
dirname = { optional=true, version="0.0.13", package="uu_dirname", path="src/uu/dirname" } dirname = { optional=true, version="0.0.13", package="uu_dirname", path="src/uu/dirname" }
du = { optional=true, version="0.0.13", package="uu_du", path="src/uu/du" } du = { optional=true, version="0.0.13", package="uu_du", path="src/uu/du" }
@ -352,6 +354,7 @@ uniq = { optional=true, version="0.0.13", package="uu_uniq", path="src/uu/un
unlink = { optional=true, version="0.0.13", package="uu_unlink", path="src/uu/unlink" } unlink = { optional=true, version="0.0.13", package="uu_unlink", path="src/uu/unlink" }
uptime = { optional=true, version="0.0.13", package="uu_uptime", path="src/uu/uptime" } uptime = { optional=true, version="0.0.13", package="uu_uptime", path="src/uu/uptime" }
users = { optional=true, version="0.0.13", package="uu_users", path="src/uu/users" } users = { optional=true, version="0.0.13", package="uu_users", path="src/uu/users" }
vdir = { optional=true, version="0.0.13", package="uu_vdir", path="src/uu/vdir" }
wc = { optional=true, version="0.0.13", package="uu_wc", path="src/uu/wc" } wc = { optional=true, version="0.0.13", package="uu_wc", path="src/uu/wc" }
who = { optional=true, version="0.0.13", package="uu_who", path="src/uu/who" } who = { optional=true, version="0.0.13", package="uu_who", path="src/uu/who" }
whoami = { optional=true, version="0.0.13", package="uu_whoami", path="src/uu/whoami" } whoami = { optional=true, version="0.0.13", package="uu_whoami", path="src/uu/whoami" }
@ -373,7 +376,7 @@ glob = "0.3.0"
libc = "0.2" libc = "0.2"
pretty_assertions = "1" pretty_assertions = "1"
rand = "0.8" rand = "0.8"
regex = "1.0" regex = "1.5"
sha1 = { version="0.10", features=["std"] } sha1 = { version="0.10", features=["std"] }
tempfile = "3" tempfile = "3"
time = "0.1" time = "0.1"
@ -384,7 +387,7 @@ atty = "0.2"
hex-literal = "0.3.1" hex-literal = "0.3.1"
[target.'cfg(target_os = "linux")'.dev-dependencies] [target.'cfg(target_os = "linux")'.dev-dependencies]
rlimit = "0.4.0" rlimit = "0.8.3"
[target.'cfg(unix)'.dev-dependencies] [target.'cfg(unix)'.dev-dependencies]
nix = "0.23.1" nix = "0.23.1"

View file

@ -1,4 +1,4 @@
# spell-checker:ignore (misc) testsuite runtest findstring (targets) busytest distclean manpages pkgs ; (vars/env) BINDIR BUILDDIR CARGOFLAGS DESTDIR DOCSDIR INSTALLDIR INSTALLEES MANDIR MULTICALL # spell-checker:ignore (misc) testsuite runtest findstring (targets) busytest distclean manpages pkgs ; (vars/env) BINDIR BUILDDIR CARGOFLAGS DESTDIR DOCSDIR INSTALLDIR INSTALLEES MULTICALL DATAROOTDIR
# Config options # Config options
PROFILE ?= debug PROFILE ?= debug
@ -22,10 +22,10 @@ CARGOFLAGS ?=
# Install directories # Install directories
PREFIX ?= /usr/local PREFIX ?= /usr/local
DESTDIR ?= DESTDIR ?=
BINDIR ?= /bin BINDIR ?= $(PREFIX)/bin
MANDIR ?= /man/man1 DATAROOTDIR ?= $(PREFIX)/share
INSTALLDIR_BIN=$(DESTDIR)$(PREFIX)$(BINDIR) INSTALLDIR_BIN=$(DESTDIR)$(BINDIR)
#prefix to apply to coreutils binary and all tool binaries #prefix to apply to coreutils binary and all tool binaries
PROG_PREFIX ?= PROG_PREFIX ?=
@ -65,6 +65,7 @@ PROGS := \
date \ date \
dd \ dd \
df \ df \
dir \
dircolors \ dircolors \
dirname \ dirname \
echo \ echo \
@ -118,6 +119,7 @@ PROGS := \
tsort \ tsort \
unexpand \ unexpand \
uniq \ uniq \
vdir \
wc \ wc \
whoami \ whoami \
yes yes
@ -328,13 +330,13 @@ else
$(INSTALL) $(BUILDDIR)/$(prog) $(INSTALLDIR_BIN)/$(PROG_PREFIX)$(prog);) $(INSTALL) $(BUILDDIR)/$(prog) $(INSTALLDIR_BIN)/$(PROG_PREFIX)$(prog);)
$(if $(findstring test,$(INSTALLEES)), $(INSTALL) $(BUILDDIR)/test $(INSTALLDIR_BIN)/$(PROG_PREFIX)[) $(if $(findstring test,$(INSTALLEES)), $(INSTALL) $(BUILDDIR)/test $(INSTALLDIR_BIN)/$(PROG_PREFIX)[)
endif endif
mkdir -p $(DESTDIR)$(PREFIX)/share/zsh/site-functions mkdir -p $(DESTDIR)$(DATAROOTDIR)/zsh/site-functions
mkdir -p $(DESTDIR)$(PREFIX)/share/bash-completion/completions mkdir -p $(DESTDIR)$(DATAROOTDIR)/bash-completion/completions
mkdir -p $(DESTDIR)$(PREFIX)/share/fish/vendor_completions.d mkdir -p $(DESTDIR)$(DATAROOTDIR)/fish/vendor_completions.d
$(foreach prog, $(INSTALLEES), \ $(foreach prog, $(INSTALLEES), \
$(BUILDDIR)/coreutils completion $(prog) zsh > $(DESTDIR)$(PREFIX)/share/zsh/site-functions/_$(PROG_PREFIX)$(prog); \ $(BUILDDIR)/coreutils completion $(prog) zsh > $(DESTDIR)$(DATAROOTDIR)/zsh/site-functions/_$(PROG_PREFIX)$(prog); \
$(BUILDDIR)/coreutils completion $(prog) bash > $(DESTDIR)$(PREFIX)/share/bash-completion/completions/$(PROG_PREFIX)$(prog); \ $(BUILDDIR)/coreutils completion $(prog) bash > $(DESTDIR)$(DATAROOTDIR)/bash-completion/completions/$(PROG_PREFIX)$(prog); \
$(BUILDDIR)/coreutils completion $(prog) fish > $(DESTDIR)$(PREFIX)/share/fish/vendor_completions.d/$(PROG_PREFIX)$(prog).fish; \ $(BUILDDIR)/coreutils completion $(prog) fish > $(DESTDIR)$(DATAROOTDIR)/fish/vendor_completions.d/$(PROG_PREFIX)$(prog).fish; \
) )
uninstall: uninstall:
@ -343,8 +345,8 @@ ifeq (${MULTICALL}, y)
endif endif
rm -f $(addprefix $(INSTALLDIR_BIN)/$(PROG_PREFIX),$(PROGS)) rm -f $(addprefix $(INSTALLDIR_BIN)/$(PROG_PREFIX),$(PROGS))
rm -f $(INSTALLDIR_BIN)/$(PROG_PREFIX)[ rm -f $(INSTALLDIR_BIN)/$(PROG_PREFIX)[
rm -f $(addprefix $(DESTDIR)$(PREFIX)/share/zsh/site-functions/_$(PROG_PREFIX),$(PROGS)) rm -f $(addprefix $(DESTDIR)$(DATAROOTDIR)/zsh/site-functions/_$(PROG_PREFIX),$(PROGS))
rm -f $(addprefix $(DESTDIR)$(PREFIX)/share/bash-completion/completions/$(PROG_PREFIX),$(PROGS)) rm -f $(addprefix $(DESTDIR)$(DATAROOTDIR)/bash-completion/completions/$(PROG_PREFIX),$(PROGS))
rm -f $(addprefix $(DESTDIR)$(PREFIX)/share/fish/vendor_completions.d/$(PROG_PREFIX),$(addsuffix .fish,$(PROGS))) rm -f $(addprefix $(DESTDIR)$(DATAROOTDIR)/fish/vendor_completions.d/$(PROG_PREFIX),$(addsuffix .fish,$(PROGS)))
.PHONY: all build build-coreutils build-pkgs test distclean clean busytest install uninstall .PHONY: all build build-coreutils build-pkgs test distclean clean busytest install uninstall

View file

@ -385,6 +385,7 @@ To improve the GNU compatibility, the following process is recommended:
1. Start to modify `<your test>` to understand what is wrong. Examples: 1. Start to modify `<your test>` to understand what is wrong. Examples:
1. Add `set -v` to have the bash verbose mode 1. Add `set -v` to have the bash verbose mode
1. Add `echo $?` where needed 1. Add `echo $?` where needed
1. When the variable `fail` is used in the test, `echo $fail` to see when the test started to fail
1. Bump the content of the output (ex: `cat err`) 1. Bump the content of the output (ex: `cat err`)
1. ... 1. ...
1. Or, if the test is simple, extract the relevant information to create a new test case running both GNU & Rust implementation 1. Or, if the test is simple, extract the relevant information to create a new test case running both GNU & Rust implementation
@ -398,7 +399,15 @@ To contribute to uutils, please see [CONTRIBUTING](CONTRIBUTING.md).
## Utilities ## Utilities
| Done | Semi-Done | To Do | Please note that this is not fully accurate:
* Some new options can be added / removed in the GNU implementation;
* Some error management might be missing;
* Some behaviors might be different.
See https://github.com/uutils/coreutils/issues/3336 for the main meta bugs
(many are missing).
| Done | WIP | To Do |
|-----------|-----------|--------| |-----------|-----------|--------|
| arch | cp | stty | | arch | cp | stty |
| base32 | date | | | base32 | date | |
@ -417,8 +426,8 @@ To contribute to uutils, please see [CONTRIBUTING](CONTRIBUTING.md).
| cut | tac | | | cut | tac | |
| dircolors | tail | | | dircolors | tail | |
| dirname | test | | | dirname | test | |
| du | | | | du | dir | |
| echo | | | | echo | vdir | |
| env | | | | env | | |
| expand | | | | expand | | |
| factor | | | | factor | | |

1
docs/.gitignore vendored
View file

@ -1,3 +1,4 @@
book book
src/utils src/utils
src/SUMMARY.md src/SUMMARY.md
tldr.zip

11
docs/theme/head.hbs vendored
View file

@ -5,10 +5,17 @@
main { main {
position: relative; position: relative;
} }
.version { .additional {
position: absolute; position: absolute;
top: 1em; top: 0.5em;
right: 0; right: 0;
display: flex;
gap: 5px;
align-items: center;
font-size: 1.3em;
}
.platforms {
font-size: 2rem;
} }
dd > p { dd > p {
margin-top: 0.2em; margin-top: 0.2em;

View file

@ -5,23 +5,26 @@
// spell-checker:ignore tldr // spell-checker:ignore tldr
use clap::Command; use clap::Command;
use std::collections::HashMap;
use std::ffi::OsString; use std::ffi::OsString;
use std::fs::File; use std::fs::File;
use std::io::Cursor;
use std::io::{self, Read, Seek, Write}; use std::io::{self, Read, Seek, Write};
use zip::ZipArchive; use zip::ZipArchive;
include!(concat!(env!("OUT_DIR"), "/uutils_map.rs")); include!(concat!(env!("OUT_DIR"), "/uutils_map.rs"));
fn main() -> io::Result<()> { fn main() -> io::Result<()> {
println!("Downloading tldr archive"); let mut tldr_zip = File::open("docs/tldr.zip")
let mut zip_reader = ureq::get("https://tldr.sh/assets/tldr.zip") .ok()
.call() .and_then(|f| ZipArchive::new(f).ok());
.unwrap()
.into_reader(); if tldr_zip.is_none() {
let mut buffer = Vec::new(); println!("Warning: No tldr archive found, so the documentation will not include examples.");
zip_reader.read_to_end(&mut buffer).unwrap(); println!("To include examples in the documentation, download the tldr archive and put it in the docs/ folder.");
let mut tldr_zip = ZipArchive::new(Cursor::new(buffer)).unwrap(); println!();
println!(" curl https://tldr.sh/assets/tldr.zip -o docs/tldr.zip");
println!();
}
let utils = util_map::<Box<dyn Iterator<Item = OsString>>>(); let utils = util_map::<Box<dyn Iterator<Item = OsString>>>();
match std::fs::create_dir("docs/src/utils/") { match std::fs::create_dir("docs/src/utils/") {
@ -29,6 +32,7 @@ fn main() -> io::Result<()> {
x => x, x => x,
}?; }?;
println!("Writing initial info to SUMMARY.md");
let mut summary = File::create("docs/src/SUMMARY.md")?; let mut summary = File::create("docs/src/SUMMARY.md")?;
let _ = write!( let _ = write!(
@ -44,6 +48,40 @@ fn main() -> io::Result<()> {
* [Multi-call binary](multicall.md)\n", * [Multi-call binary](multicall.md)\n",
); );
println!("Gathering utils per platform");
let utils_per_platform = {
let mut map = HashMap::new();
for platform in ["unix", "macos", "windows"] {
let platform_utils: Vec<String> = String::from_utf8(
std::process::Command::new("./util/show-utils.sh")
.arg(format!("--features=feat_os_{}", platform))
.output()?
.stdout,
)
.unwrap()
.split(' ')
.map(ToString::to_string)
.collect();
map.insert(platform, platform_utils);
}
// Linux is a special case because it can support selinux
let platform_utils: Vec<String> = String::from_utf8(
std::process::Command::new("./util/show-utils.sh")
.arg("--features=feat_os_unix feat_selinux")
.output()?
.stdout,
)
.unwrap()
.split(' ')
.map(ToString::to_string)
.collect();
map.insert("linux", platform_utils);
map
};
println!("Writing to utils");
let mut utils = utils.entries().collect::<Vec<_>>(); let mut utils = utils.entries().collect::<Vec<_>>();
utils.sort(); utils.sort();
for (&name, (_, command)) in utils { for (&name, (_, command)) in utils {
@ -52,7 +90,14 @@ fn main() -> io::Result<()> {
} }
let p = format!("docs/src/utils/{}.md", name); let p = format!("docs/src/utils/{}.md", name);
if let Ok(f) = File::create(&p) { if let Ok(f) = File::create(&p) {
write_markdown(f, &mut command(), name, &mut tldr_zip)?; MDWriter {
w: Box::new(f),
command: command(),
name,
tldr_zip: &mut tldr_zip,
utils_per_platform: &utils_per_platform,
}
.markdown()?;
println!("Wrote to '{}'", p); println!("Wrote to '{}'", p);
} else { } else {
println!("Error writing to {}", p); println!("Error writing to {}", p);
@ -62,88 +107,186 @@ fn main() -> io::Result<()> {
Ok(()) Ok(())
} }
fn write_markdown( struct MDWriter<'a, 'b> {
mut w: impl Write, w: Box<dyn Write>,
command: &mut Command, command: Command<'a>,
name: &str, name: &'a str,
tldr_zip: &mut zip::ZipArchive<impl Read + Seek>, tldr_zip: &'b mut Option<ZipArchive<File>>,
) -> io::Result<()> { utils_per_platform: &'b HashMap<&'b str, Vec<String>>,
write!(w, "# {}\n\n", name)?;
write_version(&mut w, command)?;
write_usage(&mut w, command, name)?;
write_description(&mut w, command)?;
write_options(&mut w, command)?;
write_examples(&mut w, name, tldr_zip)
} }
fn write_version(w: &mut impl Write, command: &Command) -> io::Result<()> { impl<'a, 'b> MDWriter<'a, 'b> {
writeln!( fn markdown(&mut self) -> io::Result<()> {
w, write!(self.w, "# {}\n\n", self.name)?;
"<div class=\"version\">version: {}</div>", self.additional()?;
command.render_version().split_once(' ').unwrap().1 self.usage()?;
) self.description()?;
} self.options()?;
self.examples()
}
fn write_usage(w: &mut impl Write, command: &mut Command, name: &str) -> io::Result<()> { fn additional(&mut self) -> io::Result<()> {
writeln!(w, "\n```")?; writeln!(self.w, "<div class=\"additional\">")?;
let mut usage: String = command self.platforms()?;
.render_usage() self.version()?;
.lines() writeln!(self.w, "</div>")
.skip(1) }
.map(|l| l.trim())
.filter(|l| !l.is_empty()) fn platforms(&mut self) -> io::Result<()> {
.collect::<Vec<_>>() writeln!(self.w, "<div class=\"platforms\">")?;
.join("\n"); for (feature, icon) in [
usage = usage.replace(uucore::execution_phrase(), name); ("linux", "linux"),
writeln!(w, "{}", usage)?; // freebsd is disabled for now because mdbook does not use font-awesome 5 yet.
writeln!(w, "```") // ("unix", "freebsd"),
} ("macos", "apple"),
("windows", "windows"),
] {
if self.name.contains("sum")
|| self.utils_per_platform[feature]
.iter()
.any(|u| u == self.name)
{
writeln!(self.w, "<i class=\"fa fa-brands fa-{}\"></i>", icon)?;
}
}
writeln!(self.w, "</div>")?;
fn write_description(w: &mut impl Write, command: &Command) -> io::Result<()> {
if let Some(about) = command.get_long_about().or_else(|| command.get_about()) {
writeln!(w, "{}", about)
} else {
Ok(()) Ok(())
} }
}
fn write_examples( fn version(&mut self) -> io::Result<()> {
w: &mut impl Write, writeln!(
name: &str, self.w,
tldr_zip: &mut zip::ZipArchive<impl Read + Seek>, "<div class=\"version\">v{}</div>",
) -> io::Result<()> { self.command.render_version().split_once(' ').unwrap().1
let content = if let Some(f) = get_zip_content(tldr_zip, &format!("pages/common/{}.md", name)) { )
f }
} else if let Some(f) = get_zip_content(tldr_zip, &format!("pages/linux/{}.md", name)) {
f
} else {
return Ok(());
};
writeln!(w, "## Examples")?; fn usage(&mut self) -> io::Result<()> {
writeln!(w)?; writeln!(self.w, "\n```")?;
for line in content.lines().skip_while(|l| !l.starts_with('-')) { let mut usage: String = self
if let Some(l) = line.strip_prefix("- ") { .command
writeln!(w, "{}", l)?; .render_usage()
} else if line.starts_with('`') { .lines()
writeln!(w, "```shell\n{}\n```", line.trim_matches('`'))?; .skip(1)
} else if line.is_empty() { .map(|l| l.trim())
writeln!(w)?; .filter(|l| !l.is_empty())
.collect::<Vec<_>>()
.join("\n");
usage = usage.replace(uucore::execution_phrase(), self.name);
writeln!(self.w, "{}", usage)?;
writeln!(self.w, "```")
}
fn description(&mut self) -> io::Result<()> {
if let Some(about) = self
.command
.get_long_about()
.or_else(|| self.command.get_about())
{
writeln!(self.w, "{}", about)
} else { } else {
println!("Not sure what to do with this line:"); Ok(())
println!("{}", line);
} }
} }
writeln!(w)?;
writeln!( fn examples(&mut self) -> io::Result<()> {
w, if let Some(zip) = self.tldr_zip {
"> The examples are provided by the [tldr-pages project](https://tldr.sh) under the [CC BY 4.0 License](https://github.com/tldr-pages/tldr/blob/main/LICENSE.md)." let content = if let Some(f) =
)?; get_zip_content(zip, &format!("pages/common/{}.md", self.name))
writeln!(w, ">")?; {
writeln!( f
w, } else if let Some(f) = get_zip_content(zip, &format!("pages/linux/{}.md", self.name)) {
"> Please note that, as uutils is a work in progress, some examples might fail." f
) } else {
return Ok(());
};
writeln!(self.w, "## Examples")?;
writeln!(self.w)?;
for line in content.lines().skip_while(|l| !l.starts_with('-')) {
if let Some(l) = line.strip_prefix("- ") {
writeln!(self.w, "{}", l)?;
} else if line.starts_with('`') {
writeln!(self.w, "```shell\n{}\n```", line.trim_matches('`'))?;
} else if line.is_empty() {
writeln!(self.w)?;
} else {
println!("Not sure what to do with this line:");
println!("{}", line);
}
}
writeln!(self.w)?;
writeln!(
self.w,
"> The examples are provided by the [tldr-pages project](https://tldr.sh) under the [CC BY 4.0 License](https://github.com/tldr-pages/tldr/blob/main/LICENSE.md)."
)?;
writeln!(self.w, ">")?;
writeln!(
self.w,
"> Please note that, as uutils is a work in progress, some examples might fail."
)?;
}
Ok(())
}
fn options(&mut self) -> io::Result<()> {
writeln!(self.w, "<h2>Options</h2>")?;
write!(self.w, "<dl>")?;
for arg in self.command.get_arguments() {
write!(self.w, "<dt>")?;
let mut first = true;
for l in arg.get_long_and_visible_aliases().unwrap_or_default() {
if !first {
write!(self.w, ", ")?;
} else {
first = false;
}
write!(self.w, "<code>")?;
write!(self.w, "--{}", l)?;
if let Some(names) = arg.get_value_names() {
write!(
self.w,
"={}",
names
.iter()
.map(|x| format!("&lt;{}&gt;", x))
.collect::<Vec<_>>()
.join(" ")
)?;
}
write!(self.w, "</code>")?;
}
for s in arg.get_short_and_visible_aliases().unwrap_or_default() {
if !first {
write!(self.w, ", ")?;
} else {
first = false;
}
write!(self.w, "<code>")?;
write!(self.w, "-{}", s)?;
if let Some(names) = arg.get_value_names() {
write!(
self.w,
" {}",
names
.iter()
.map(|x| format!("&lt;{}&gt;", x))
.collect::<Vec<_>>()
.join(" ")
)?;
}
write!(self.w, "</code>")?;
}
writeln!(self.w, "</dt>")?;
writeln!(
self.w,
"<dd>\n\n{}\n\n</dd>",
arg.get_help().unwrap_or_default().replace('\n', "<br />")
)?;
}
writeln!(self.w, "</dl>\n")
}
} }
fn get_zip_content(archive: &mut ZipArchive<impl Read + Seek>, name: &str) -> Option<String> { fn get_zip_content(archive: &mut ZipArchive<impl Read + Seek>, name: &str) -> Option<String> {
@ -151,61 +294,3 @@ fn get_zip_content(archive: &mut ZipArchive<impl Read + Seek>, name: &str) -> Op
archive.by_name(name).ok()?.read_to_string(&mut s).unwrap(); archive.by_name(name).ok()?.read_to_string(&mut s).unwrap();
Some(s) Some(s)
} }
fn write_options(w: &mut impl Write, command: &Command) -> io::Result<()> {
writeln!(w, "<h2>Options</h2>")?;
write!(w, "<dl>")?;
for arg in command.get_arguments() {
write!(w, "<dt>")?;
let mut first = true;
for l in arg.get_long_and_visible_aliases().unwrap_or_default() {
if !first {
write!(w, ", ")?;
} else {
first = false;
}
write!(w, "<code>")?;
write!(w, "--{}", l)?;
if let Some(names) = arg.get_value_names() {
write!(
w,
"={}",
names
.iter()
.map(|x| format!("&lt;{}&gt;", x))
.collect::<Vec<_>>()
.join(" ")
)?;
}
write!(w, "</code>")?;
}
for s in arg.get_short_and_visible_aliases().unwrap_or_default() {
if !first {
write!(w, ", ")?;
} else {
first = false;
}
write!(w, "<code>")?;
write!(w, "-{}", s)?;
if let Some(names) = arg.get_value_names() {
write!(
w,
" {}",
names
.iter()
.map(|x| format!("&lt;{}&gt;", x))
.collect::<Vec<_>>()
.join(" ")
)?;
}
write!(w, "</code>")?;
}
writeln!(w, "</dt>")?;
writeln!(
w,
"<dd>\n\n{}\n\n</dd>",
arg.get_help().unwrap_or_default().replace('\n', "<br />")
)?;
}
writeln!(w, "</dl>\n")
}

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/arch" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/arch"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/arch.rs" path = "src/arch.rs"

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/base32" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/base32"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/base32.rs" path = "src/base32.rs"

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/base64" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/base64"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/base64.rs" path = "src/base64.rs"

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/basename" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/basename"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/basename.rs" path = "src/basename.rs"

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/basenc" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/basenc"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/basenc.rs" path = "src/basenc.rs"

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/cat" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/cat"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/cat.rs" path = "src/cat.rs"

View file

@ -8,7 +8,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/chcon" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/chcon"
keywords = ["coreutils", "uutils", "cli", "utility"] keywords = ["coreutils", "uutils", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/chcon.rs" path = "src/chcon.rs"

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/chgrp" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/chgrp"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/chgrp.rs" path = "src/chgrp.rs"

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/chmod" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/chmod"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/chmod.rs" path = "src/chmod.rs"

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/chown" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/chown"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/chown.rs" path = "src/chown.rs"

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/chroot" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/chroot"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/chroot.rs" path = "src/chroot.rs"

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/cksum" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/cksum"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/cksum.rs" path = "src/cksum.rs"

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/comm" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/comm"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/comm.rs" path = "src/comm.rs"

View file

@ -13,7 +13,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/cp" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/cp"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/cp.rs" path = "src/cp.rs"

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/ls" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/ls"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/csplit.rs" path = "src/csplit.rs"
@ -17,7 +17,7 @@ path = "src/csplit.rs"
[dependencies] [dependencies]
clap = { version = "3.1", features = ["wrap_help", "cargo"] } clap = { version = "3.1", features = ["wrap_help", "cargo"] }
thiserror = "1.0" thiserror = "1.0"
regex = "1.0.0" regex = "1.5.5"
uucore = { version=">=0.0.11", package="uucore", path="../../uucore", features=["entries", "fs"] } uucore = { version=">=0.0.11", package="uucore", path="../../uucore", features=["entries", "fs"] }
[[bin]] [[bin]]

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/cut" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/cut"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/cut.rs" path = "src/cut.rs"

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/date" repository = "https://github.com/uutils/coreutils/tree/main/src/date"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/date.rs" path = "src/date.rs"

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/dd" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/dd"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/dd.rs" path = "src/dd.rs"

View file

@ -23,7 +23,6 @@ mod blocks;
use blocks::conv_block_unblock_helper; use blocks::conv_block_unblock_helper;
use std::cmp; use std::cmp;
use std::convert::TryInto;
use std::env; use std::env;
use std::fs::{File, OpenOptions}; use std::fs::{File, OpenOptions};
use std::io::{self, Read, Seek, Write}; use std::io::{self, Read, Seek, Write};

View file

@ -10,7 +10,7 @@ fn unimplemented_flags_should_error_non_linux() {
let mut succeeded = Vec::new(); let mut succeeded = Vec::new();
// The following flags are only implemented in linux // The following flags are only implemented in linux
for &flag in &[ for flag in [
"direct", "direct",
"directory", "directory",
"dsync", "dsync",
@ -47,7 +47,7 @@ fn unimplemented_flags_should_error() {
let mut succeeded = Vec::new(); let mut succeeded = Vec::new();
// The following flags are not implemented // The following flags are not implemented
for &flag in &["cio", "nocache", "nolinks", "text", "binary"] { for flag in ["cio", "nocache", "nolinks", "text", "binary"] {
let args = vec![ let args = vec![
String::from("dd"), String::from("dd"),
format!("--iflag={}", flag), format!("--iflag={}", flag),

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/df" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/df"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/df.rs" path = "src/df.rs"

View file

@ -183,4 +183,31 @@ impl Column {
_ => Err(()), _ => Err(()),
} }
} }
/// Return the alignment of the specified column.
pub(crate) fn alignment(column: &Self) -> Alignment {
match column {
Self::Source | Self::Target | Self::File | Self::Fstype => Alignment::Left,
_ => Alignment::Right,
}
}
/// Return the minimum width of the specified column.
pub(crate) fn min_width(column: &Self) -> usize {
match column {
// 14 = length of "Filesystem" plus 4 spaces
Self::Source => 14,
// the shortest headers have a length of 4 chars so we use that as the minimum width
_ => 4,
}
}
}
/// A column's alignment.
///
/// We define our own `Alignment` enum instead of using `std::fmt::Alignment` because df doesn't
/// have centered columns and hence a `Center` variant is not needed.
pub(crate) enum Alignment {
Left,
Right,
} }

View file

@ -12,9 +12,9 @@ mod filesystem;
mod table; mod table;
use uucore::display::Quotable; use uucore::display::Quotable;
use uucore::error::{UError, UResult}; use uucore::error::{UError, UResult, USimpleError};
use uucore::format_usage;
use uucore::fsext::{read_fs_list, MountInfo}; use uucore::fsext::{read_fs_list, MountInfo};
use uucore::{format_usage, show};
use clap::{crate_version, Arg, ArgMatches, Command}; use clap::{crate_version, Arg, ArgMatches, Command};
@ -25,7 +25,7 @@ use std::path::Path;
use crate::blocks::{block_size_from_matches, BlockSize}; use crate::blocks::{block_size_from_matches, BlockSize};
use crate::columns::{Column, ColumnError}; use crate::columns::{Column, ColumnError};
use crate::filesystem::Filesystem; use crate::filesystem::Filesystem;
use crate::table::{DisplayRow, Header, Row}; use crate::table::Table;
static ABOUT: &str = "Show information about the file system on which each FILE resides,\n\ static ABOUT: &str = "Show information about the file system on which each FILE resides,\n\
or all file systems by default."; or all file systems by default.";
@ -61,7 +61,6 @@ static OUTPUT_FIELD_LIST: [&str; 12] = [
struct Options { struct Options {
show_local_fs: bool, show_local_fs: bool,
show_all_fs: bool, show_all_fs: bool,
show_listed_fs: bool,
block_size: BlockSize, block_size: BlockSize,
/// Optional list of filesystem types to include in the output table. /// Optional list of filesystem types to include in the output table.
@ -88,7 +87,6 @@ impl Default for Options {
Self { Self {
show_local_fs: Default::default(), show_local_fs: Default::default(),
show_all_fs: Default::default(), show_all_fs: Default::default(),
show_listed_fs: Default::default(),
block_size: Default::default(), block_size: Default::default(),
include: Default::default(), include: Default::default(),
exclude: Default::default(), exclude: Default::default(),
@ -111,6 +109,8 @@ enum OptionsError {
/// An error getting the columns to display in the output table. /// An error getting the columns to display in the output table.
ColumnError(ColumnError), ColumnError(ColumnError),
FilesystemTypeBothSelectedAndExcluded(Vec<String>),
} }
impl fmt::Display for OptionsError { impl fmt::Display for OptionsError {
@ -126,6 +126,16 @@ impl fmt::Display for OptionsError {
"option --output: field {} used more than once", "option --output: field {} used more than once",
s.quote() s.quote()
), ),
Self::FilesystemTypeBothSelectedAndExcluded(types) => {
for t in types {
eprintln!(
"{}: file system type {} both selected and excluded",
uucore::util_name(),
t.quote()
);
}
Ok(())
}
} }
} }
} }
@ -133,18 +143,38 @@ impl fmt::Display for OptionsError {
impl Options { impl Options {
/// Convert command-line arguments into [`Options`]. /// Convert command-line arguments into [`Options`].
fn from(matches: &ArgMatches) -> Result<Self, OptionsError> { fn from(matches: &ArgMatches) -> Result<Self, OptionsError> {
let include = matches.values_of_lossy(OPT_TYPE);
let exclude = matches.values_of_lossy(OPT_EXCLUDE_TYPE);
if let (Some(include), Some(exclude)) = (&include, &exclude) {
if let Some(types) = Self::get_intersected_types(include, exclude) {
return Err(OptionsError::FilesystemTypeBothSelectedAndExcluded(types));
}
}
Ok(Self { Ok(Self {
show_local_fs: matches.is_present(OPT_LOCAL), show_local_fs: matches.is_present(OPT_LOCAL),
show_all_fs: matches.is_present(OPT_ALL), show_all_fs: matches.is_present(OPT_ALL),
show_listed_fs: false,
block_size: block_size_from_matches(matches) block_size: block_size_from_matches(matches)
.map_err(|_| OptionsError::InvalidBlockSize)?, .map_err(|_| OptionsError::InvalidBlockSize)?,
include: matches.values_of_lossy(OPT_TYPE), include,
exclude: matches.values_of_lossy(OPT_EXCLUDE_TYPE), exclude,
show_total: matches.is_present(OPT_TOTAL), show_total: matches.is_present(OPT_TOTAL),
columns: Column::from_matches(matches).map_err(OptionsError::ColumnError)?, columns: Column::from_matches(matches).map_err(OptionsError::ColumnError)?,
}) })
} }
fn get_intersected_types(include: &[String], exclude: &[String]) -> Option<Vec<String>> {
let mut intersected_types = Vec::new();
for t in include {
if exclude.contains(t) {
intersected_types.push(t.clone());
}
}
(!intersected_types.is_empty()).then(|| intersected_types)
}
} }
/// Whether to display the mount info given the inclusion settings. /// Whether to display the mount info given the inclusion settings.
@ -155,7 +185,7 @@ fn is_included(mi: &MountInfo, opt: &Options) -> bool {
} }
// Don't show pseudo filesystems unless `--all` has been given. // Don't show pseudo filesystems unless `--all` has been given.
if mi.dummy && !opt.show_all_fs && !opt.show_listed_fs { if mi.dummy && !opt.show_all_fs {
return false; return false;
} }
@ -278,10 +308,17 @@ where
// Convert each path into a `Filesystem`, which contains // Convert each path into a `Filesystem`, which contains
// both the mount information and usage information. // both the mount information and usage information.
paths let mut result = vec![];
.iter() for path in paths {
.filter_map(|p| Filesystem::from_path(&mounts, p)) match Filesystem::from_path(&mounts, path) {
.collect() Some(fs) => result.push(fs),
None => show!(USimpleError::new(
1,
format!("{}: No such file or directory", path.as_ref().display())
)),
}
}
result
} }
#[derive(Debug)] #[derive(Debug)]
@ -309,6 +346,7 @@ impl fmt::Display for DfError {
#[uucore::main] #[uucore::main]
pub fn uumain(args: impl uucore::Args) -> UResult<()> { pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let matches = uu_app().get_matches_from(args); let matches = uu_app().get_matches_from(args);
#[cfg(windows)] #[cfg(windows)]
{ {
if matches.is_present(OPT_INODES) { if matches.is_present(OPT_INODES) {
@ -318,36 +356,38 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
} }
let opt = Options::from(&matches).map_err(DfError::OptionsError)?; let opt = Options::from(&matches).map_err(DfError::OptionsError)?;
// Get the list of filesystems to display in the output table. // Get the list of filesystems to display in the output table.
let filesystems: Vec<Filesystem> = match matches.values_of(OPT_PATHS) { let filesystems: Vec<Filesystem> = match matches.values_of(OPT_PATHS) {
None => get_all_filesystems(&opt), None => {
let filesystems = get_all_filesystems(&opt);
if filesystems.is_empty() {
return Err(USimpleError::new(1, "No file systems processed"));
}
filesystems
}
Some(paths) => { Some(paths) => {
let paths: Vec<&str> = paths.collect(); let paths: Vec<&str> = paths.collect();
get_named_filesystems(&paths) let filesystems = get_named_filesystems(&paths);
// This can happen if paths are given as command-line arguments
// but none of the paths exist.
if filesystems.is_empty() {
return Ok(());
}
filesystems
} }
}; };
// The running total of filesystem sizes and usage. // This can happen if paths are given as command-line arguments
// // but none of the paths exist.
// This accumulator is computed in case we need to display the if filesystems.is_empty() {
// total counts in the last row of the table. return Ok(());
let mut total = Row::new("total"); }
println!("{}", Header::new(&opt)); println!("{}", Table::new(&opt, filesystems));
for filesystem in filesystems {
// If the filesystem is not empty, or if the options require
// showing all filesystems, then print the data as a row in
// the output table.
if opt.show_all_fs || opt.show_listed_fs || filesystem.usage.blocks > 0 {
let row = Row::from(filesystem);
println!("{}", DisplayRow::new(&row, &opt));
total += row;
}
}
if opt.show_total {
println!("{}", DisplayRow::new(&total, &opt));
}
Ok(()) Ok(())
} }
@ -367,6 +407,7 @@ pub fn uu_app<'a>() -> Command<'a> {
Arg::new(OPT_ALL) Arg::new(OPT_ALL)
.short('a') .short('a')
.long("all") .long("all")
.overrides_with(OPT_ALL)
.help("include dummy file systems"), .help("include dummy file systems"),
) )
.arg( .arg(
@ -374,47 +415,56 @@ pub fn uu_app<'a>() -> Command<'a> {
.short('B') .short('B')
.long("block-size") .long("block-size")
.takes_value(true) .takes_value(true)
.overrides_with_all(&[OPT_KILO, OPT_BLOCKSIZE])
.help( .help(
"scale sizes by SIZE before printing them; e.g.\ "scale sizes by SIZE before printing them; e.g.\
'-BM' prints sizes in units of 1,048,576 bytes", '-BM' prints sizes in units of 1,048,576 bytes",
), ),
) )
.arg( .arg(
Arg::new(OPT_TOTAL) Arg::new(OPT_TOTAL)
.long("total") .long("total")
.overrides_with(OPT_TOTAL)
.help("produce a grand total"), .help("produce a grand total"),
) )
.arg( .arg(
Arg::new(OPT_HUMAN_READABLE_BINARY) Arg::new(OPT_HUMAN_READABLE_BINARY)
.short('h') .short('h')
.long("human-readable") .long("human-readable")
.conflicts_with(OPT_HUMAN_READABLE_DECIMAL) .overrides_with_all(&[OPT_HUMAN_READABLE_DECIMAL, OPT_HUMAN_READABLE_BINARY])
.help("print sizes in human readable format (e.g., 1K 234M 2G)"), .help("print sizes in human readable format (e.g., 1K 234M 2G)"),
) )
.arg( .arg(
Arg::new(OPT_HUMAN_READABLE_DECIMAL) Arg::new(OPT_HUMAN_READABLE_DECIMAL)
.short('H') .short('H')
.long("si") .long("si")
.conflicts_with(OPT_HUMAN_READABLE_BINARY) .overrides_with_all(&[OPT_HUMAN_READABLE_BINARY, OPT_HUMAN_READABLE_DECIMAL])
.help("likewise, but use powers of 1000 not 1024"), .help("likewise, but use powers of 1000 not 1024"),
) )
.arg( .arg(
Arg::new(OPT_INODES) Arg::new(OPT_INODES)
.short('i') .short('i')
.long("inodes") .long("inodes")
.overrides_with(OPT_INODES)
.help("list inode information instead of block usage"), .help("list inode information instead of block usage"),
) )
.arg(Arg::new(OPT_KILO).short('k').help("like --block-size=1K")) .arg(
Arg::new(OPT_KILO)
.short('k')
.help("like --block-size=1K")
.overrides_with_all(&[OPT_BLOCKSIZE, OPT_KILO]),
)
.arg( .arg(
Arg::new(OPT_LOCAL) Arg::new(OPT_LOCAL)
.short('l') .short('l')
.long("local") .long("local")
.overrides_with(OPT_LOCAL)
.help("limit listing to local file systems"), .help("limit listing to local file systems"),
) )
.arg( .arg(
Arg::new(OPT_NO_SYNC) Arg::new(OPT_NO_SYNC)
.long("no-sync") .long("no-sync")
.conflicts_with(OPT_SYNC) .overrides_with_all(&[OPT_SYNC, OPT_NO_SYNC])
.help("do not invoke sync before getting usage info (default)"), .help("do not invoke sync before getting usage info (default)"),
) )
.arg( .arg(
@ -438,12 +488,13 @@ pub fn uu_app<'a>() -> Command<'a> {
Arg::new(OPT_PORTABILITY) Arg::new(OPT_PORTABILITY)
.short('P') .short('P')
.long("portability") .long("portability")
.overrides_with(OPT_PORTABILITY)
.help("use the POSIX output format"), .help("use the POSIX output format"),
) )
.arg( .arg(
Arg::new(OPT_SYNC) Arg::new(OPT_SYNC)
.long("sync") .long("sync")
.conflicts_with(OPT_NO_SYNC) .overrides_with_all(&[OPT_NO_SYNC, OPT_SYNC])
.help("invoke sync before getting usage info"), .help("invoke sync before getting usage info"),
) )
.arg( .arg(
@ -459,6 +510,7 @@ pub fn uu_app<'a>() -> Command<'a> {
Arg::new(OPT_PRINT_TYPE) Arg::new(OPT_PRINT_TYPE)
.short('T') .short('T')
.long("print-type") .long("print-type")
.overrides_with(OPT_PRINT_TYPE)
.help("print file system type"), .help("print file system type"),
) )
.arg( .arg(
@ -612,7 +664,6 @@ mod tests {
fn test_dummy_included() { fn test_dummy_included() {
let opt = Options { let opt = Options {
show_all_fs: true, show_all_fs: true,
show_listed_fs: true,
..Default::default() ..Default::default()
}; };
let m = mount_info("ext4", "/mnt/foo", false, true); let m = mount_info("ext4", "/mnt/foo", false, true);
@ -708,22 +759,6 @@ mod tests {
let m = mount_info("ext4", "/mnt/foo", false, false); let m = mount_info("ext4", "/mnt/foo", false, false);
assert!(is_included(&m, &opt)); assert!(is_included(&m, &opt));
} }
#[test]
fn test_include_and_exclude_match_both() {
// TODO The same filesystem type in both `include` and
// `exclude` should cause an error, but currently does
// not.
let include = Some(vec![String::from("ext4")]);
let exclude = Some(vec![String::from("ext4")]);
let opt = Options {
include,
exclude,
..Default::default()
};
let m = mount_info("ext4", "/mnt/foo", false, false);
assert!(!is_included(&m, &opt));
}
} }
mod filter_mount_list { mod filter_mount_list {

View file

@ -5,13 +5,11 @@
// spell-checker:ignore tmpfs Pcent Itotal Iused Iavail Ipcent // spell-checker:ignore tmpfs Pcent Itotal Iused Iavail Ipcent
//! The filesystem usage data table. //! The filesystem usage data table.
//! //!
//! A table comprises a header row ([`Header`]) and a collection of //! A table ([`Table`]) comprises a header row ([`Header`]) and a
//! data rows ([`Row`]), one per filesystem. To display a [`Row`], //! collection of data rows ([`Row`]), one per filesystem.
//! combine it with [`Options`] in the [`DisplayRow`] struct; the
//! [`DisplayRow`] implements [`std::fmt::Display`].
use number_prefix::NumberPrefix; use number_prefix::NumberPrefix;
use crate::columns::Column; use crate::columns::{Alignment, Column};
use crate::filesystem::Filesystem; use crate::filesystem::Filesystem;
use crate::{BlockSize, Options}; use crate::{BlockSize, Options};
use uucore::fsext::{FsUsage, MountInfo}; use uucore::fsext::{FsUsage, MountInfo};
@ -154,6 +152,7 @@ impl From<Filesystem> for Row {
.. ..
} = fs.usage; } = fs.usage;
let bused = blocks - bfree; let bused = blocks - bfree;
let fused = files - ffree;
Self { Self {
file: fs.file, file: fs.file,
fs_device: dev_name, fs_device: dev_name,
@ -177,25 +176,25 @@ impl From<Filesystem> for Row {
Some(bavail as f64 / ((bused + bavail) as f64)) Some(bavail as f64 / ((bused + bavail) as f64))
}, },
inodes: files, inodes: files,
inodes_used: files - ffree, inodes_used: fused,
inodes_free: ffree, inodes_free: ffree,
inodes_usage: if files == 0 { inodes_usage: if files == 0 {
None None
} else { } else {
Some(ffree as f64 / files as f64) Some(fused as f64 / files as f64)
}, },
} }
} }
} }
/// A displayable wrapper around a [`Row`]. /// A formatter for [`Row`].
/// ///
/// The `options` control how the information in the row gets displayed. /// The `options` control how the information in the row gets formatted.
pub(crate) struct DisplayRow<'a> { pub(crate) struct RowFormatter<'a> {
/// The data in this row. /// The data in this row.
row: &'a Row, row: &'a Row,
/// Options that control how to display the data. /// Options that control how to format the data.
options: &'a Options, options: &'a Options,
// TODO We don't need all of the command-line options here. Some // TODO We don't need all of the command-line options here. Some
// of the command-line options indicate which rows to include or // of the command-line options indicate which rows to include or
@ -206,28 +205,48 @@ pub(crate) struct DisplayRow<'a> {
// `df.rs` module. // `df.rs` module.
} }
impl<'a> DisplayRow<'a> { impl<'a> RowFormatter<'a> {
/// Instantiate this struct. /// Instantiate this struct.
pub(crate) fn new(row: &'a Row, options: &'a Options) -> Self { pub(crate) fn new(row: &'a Row, options: &'a Options) -> Self {
Self { row, options } Self { row, options }
} }
/// Get a string giving the scaled version of the input number. /// Get a human readable string giving the scaled version of the input number.
/// ///
/// The scaling factor is defined in the `options` field. /// The scaling factor is defined in the `options` field.
/// ///
/// # Errors /// This function is supposed to be used by `scaled_bytes()` and `scaled_inodes()` only.
/// fn scaled_human_readable(&self, size: u64) -> String {
/// If the scaling factor is not 1000, 1024, or a negative number.
fn scaled(&self, size: u64) -> Result<String, fmt::Error> {
let number_prefix = match self.options.block_size { let number_prefix = match self.options.block_size {
BlockSize::HumanReadableDecimal => NumberPrefix::decimal(size as f64), BlockSize::HumanReadableDecimal => NumberPrefix::decimal(size as f64),
BlockSize::HumanReadableBinary => NumberPrefix::binary(size as f64), BlockSize::HumanReadableBinary => NumberPrefix::binary(size as f64),
BlockSize::Bytes(d) => return Ok((size / d).to_string()), _ => unreachable!(),
}; };
match number_prefix { match number_prefix {
NumberPrefix::Standalone(bytes) => Ok(bytes.to_string()), NumberPrefix::Standalone(bytes) => bytes.to_string(),
NumberPrefix::Prefixed(prefix, bytes) => Ok(format!("{:.1}{}", bytes, prefix.symbol())), NumberPrefix::Prefixed(prefix, bytes) => format!("{:.1}{}", bytes, prefix.symbol()),
}
}
/// Get a string giving the scaled version of the input number.
///
/// The scaling factor is defined in the `options` field.
fn scaled_bytes(&self, size: u64) -> String {
if let BlockSize::Bytes(d) = self.options.block_size {
(size / d).to_string()
} else {
self.scaled_human_readable(size)
}
}
/// Get a string giving the scaled version of the input number.
///
/// The scaling factor is defined in the `options` field.
fn scaled_inodes(&self, size: u64) -> String {
if let BlockSize::Bytes(_) = self.options.block_size {
size.to_string()
} else {
self.scaled_human_readable(size)
} }
} }
@ -240,82 +259,163 @@ impl<'a> DisplayRow<'a> {
Some(x) => format!("{:.0}%", (100.0 * x).ceil()), Some(x) => format!("{:.0}%", (100.0 * x).ceil()),
} }
} }
}
impl fmt::Display for DisplayRow<'_> { /// Returns formatted row data.
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn get_values(&self) -> Vec<String> {
let mut strings = Vec::new();
for column in &self.options.columns { for column in &self.options.columns {
match column { let string = match column {
Column::Source => write!(f, "{0: <16} ", self.row.fs_device)?, Column::Source => self.row.fs_device.to_string(),
Column::Size => write!(f, "{0: >12} ", self.scaled(self.row.bytes)?)?, Column::Size => self.scaled_bytes(self.row.bytes),
Column::Used => write!(f, "{0: >12} ", self.scaled(self.row.bytes_used)?)?, Column::Used => self.scaled_bytes(self.row.bytes_used),
Column::Avail => write!(f, "{0: >12} ", self.scaled(self.row.bytes_avail)?)?, Column::Avail => self.scaled_bytes(self.row.bytes_avail),
Column::Pcent => { Column::Pcent => Self::percentage(self.row.bytes_usage),
write!(f, "{0: >5} ", DisplayRow::percentage(self.row.bytes_usage))?;
} Column::Target => self.row.fs_mount.to_string(),
Column::Target => write!(f, "{0: <16}", self.row.fs_mount)?, Column::Itotal => self.scaled_inodes(self.row.inodes),
Column::Itotal => write!(f, "{0: >12} ", self.scaled(self.row.inodes)?)?, Column::Iused => self.scaled_inodes(self.row.inodes_used),
Column::Iused => write!(f, "{0: >12} ", self.scaled(self.row.inodes_used)?)?, Column::Iavail => self.scaled_inodes(self.row.inodes_free),
Column::Iavail => write!(f, "{0: >12} ", self.scaled(self.row.inodes_free)?)?, Column::Ipcent => Self::percentage(self.row.inodes_usage),
Column::Ipcent => { Column::File => self.row.file.as_ref().unwrap_or(&"-".into()).to_string(),
write!(f, "{0: >5} ", DisplayRow::percentage(self.row.inodes_usage))?;
} Column::Fstype => self.row.fs_type.to_string(),
Column::File => {
write!(f, "{0: <16}", self.row.file.as_ref().unwrap_or(&"-".into()))?;
}
Column::Fstype => write!(f, "{0: <5} ", self.row.fs_type)?,
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
Column::Capacity => write!( Column::Capacity => Self::percentage(self.row.bytes_capacity),
f, };
"{0: >12} ",
DisplayRow::percentage(self.row.bytes_capacity) strings.push(string);
)?,
}
} }
Ok(())
strings
} }
} }
/// The header row. /// The data of the header row.
/// struct Header {}
/// The `options` control which columns are displayed.
pub(crate) struct Header<'a> {
/// Options that control which columns are displayed.
options: &'a Options,
}
impl<'a> Header<'a> { impl Header {
/// Instantiate this struct. /// Return the headers for the specified columns.
pub(crate) fn new(options: &'a Options) -> Self { ///
Self { options } /// The `options` control which column headers are returned.
fn get_headers(options: &Options) -> Vec<String> {
let mut headers = Vec::new();
for column in &options.columns {
let header = match column {
Column::Source => String::from("Filesystem"),
Column::Size => options.block_size.to_string(),
Column::Used => String::from("Used"),
Column::Avail => String::from("Available"),
Column::Pcent => String::from("Use%"),
Column::Target => String::from("Mounted on"),
Column::Itotal => String::from("Inodes"),
Column::Iused => String::from("IUsed"),
Column::Iavail => String::from("IFree"),
Column::Ipcent => String::from("IUse%"),
Column::File => String::from("File"),
Column::Fstype => String::from("Type"),
#[cfg(target_os = "macos")]
Column::Capacity => String::from("Capacity"),
};
headers.push(header);
}
headers
} }
} }
impl fmt::Display for Header<'_> { /// The output table.
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { pub(crate) struct Table {
for column in &self.options.columns { alignments: Vec<Alignment>,
match column { rows: Vec<Vec<String>>,
Column::Source => write!(f, "{0: <16} ", "Filesystem")?, widths: Vec<usize>,
// `Display` is implemented for `BlockSize`, but }
// `Display` only works when formatting an object into
// an empty format, `{}`. So we use `format!()` first impl Table {
// to create the string, then use `write!()` to align pub(crate) fn new(options: &Options, filesystems: Vec<Filesystem>) -> Self {
// the string and pad with spaces. let headers = Header::get_headers(options);
Column::Size => write!(f, "{0: >12} ", format!("{}", self.options.block_size))?, let mut widths: Vec<_> = options
Column::Used => write!(f, "{0: >12} ", "Used")?, .columns
Column::Avail => write!(f, "{0: >12} ", "Available")?, .iter()
Column::Pcent => write!(f, "{0: >5} ", "Use%")?, .enumerate()
Column::Target => write!(f, "{0: <16} ", "Mounted on")?, .map(|(i, col)| Column::min_width(col).max(headers[i].len()))
Column::Itotal => write!(f, "{0: >12} ", "Inodes")?, .collect();
Column::Iused => write!(f, "{0: >12} ", "IUsed")?,
Column::Iavail => write!(f, "{0: >12} ", "IFree")?, let mut rows = vec![headers];
Column::Ipcent => write!(f, "{0: >5} ", "IUse%")?,
Column::File => write!(f, "{0: <16}", "File")?, // The running total of filesystem sizes and usage.
Column::Fstype => write!(f, "{0: <5} ", "Type")?, //
#[cfg(target_os = "macos")] // This accumulator is computed in case we need to display the
Column::Capacity => write!(f, "{0: >12} ", "Capacity")?, // total counts in the last row of the table.
let mut total = Row::new("total");
for filesystem in filesystems {
// If the filesystem is not empty, or if the options require
// showing all filesystems, then print the data as a row in
// the output table.
if options.show_all_fs || filesystem.usage.blocks > 0 {
let row = Row::from(filesystem);
let fmt = RowFormatter::new(&row, options);
let values = fmt.get_values();
total += row;
for (i, value) in values.iter().enumerate() {
if value.len() > widths[i] {
widths[i] = value.len();
}
}
rows.push(values);
} }
} }
if options.show_total {
let total_row = RowFormatter::new(&total, options);
rows.push(total_row.get_values());
}
Self {
rows,
widths,
alignments: Self::get_alignments(&options.columns),
}
}
fn get_alignments(columns: &Vec<Column>) -> Vec<Alignment> {
let mut alignments = Vec::new();
for column in columns {
alignments.push(Column::alignment(column));
}
alignments
}
}
impl fmt::Display for Table {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut row_iter = self.rows.iter().peekable();
while let Some(row) = row_iter.next() {
let mut col_iter = row.iter().enumerate().peekable();
while let Some((i, elem)) = col_iter.next() {
match self.alignments[i] {
Alignment::Left => write!(f, "{:<width$}", elem, width = self.widths[i])?,
Alignment::Right => write!(f, "{:>width$}", elem, width = self.widths[i])?,
}
if col_iter.peek().is_some() {
// column separator
write!(f, " ")?;
}
}
if row_iter.peek().is_some() {
writeln!(f)?;
}
}
Ok(()) Ok(())
} }
} }
@ -324,7 +424,7 @@ impl fmt::Display for Header<'_> {
mod tests { mod tests {
use crate::columns::Column; use crate::columns::Column;
use crate::table::{DisplayRow, Header, Row}; use crate::table::{Header, Row, RowFormatter};
use crate::{BlockSize, Options}; use crate::{BlockSize, Options};
const COLUMNS_WITH_FS_TYPE: [Column; 7] = [ const COLUMNS_WITH_FS_TYPE: [Column; 7] = [
@ -346,76 +446,119 @@ mod tests {
]; ];
#[test] #[test]
fn test_header_display() { fn test_default_header() {
let options = Default::default(); let options = Default::default();
assert_eq!( assert_eq!(
Header::new(&options).to_string(), Header::get_headers(&options),
"Filesystem 1K-blocks Used Available Use% Mounted on " vec!(
"Filesystem",
"1K-blocks",
"Used",
"Available",
"Use%",
"Mounted on"
)
); );
} }
#[test] #[test]
fn test_header_display_fs_type() { fn test_header_with_fs_type() {
let options = Options { let options = Options {
columns: COLUMNS_WITH_FS_TYPE.to_vec(), columns: COLUMNS_WITH_FS_TYPE.to_vec(),
..Default::default() ..Default::default()
}; };
assert_eq!( assert_eq!(
Header::new(&options).to_string(), Header::get_headers(&options),
"Filesystem Type 1K-blocks Used Available Use% Mounted on " vec!(
"Filesystem",
"Type",
"1K-blocks",
"Used",
"Available",
"Use%",
"Mounted on"
)
); );
} }
#[test] #[test]
fn test_header_display_inode() { fn test_header_with_inodes() {
let options = Options { let options = Options {
columns: COLUMNS_WITH_INODES.to_vec(), columns: COLUMNS_WITH_INODES.to_vec(),
..Default::default() ..Default::default()
}; };
assert_eq!( assert_eq!(
Header::new(&options).to_string(), Header::get_headers(&options),
"Filesystem Inodes IUsed IFree IUse% Mounted on " vec!(
"Filesystem",
"Inodes",
"IUsed",
"IFree",
"IUse%",
"Mounted on"
)
); );
} }
#[test] #[test]
fn test_header_display_block_size_1024() { fn test_header_with_block_size_1024() {
let options = Options { let options = Options {
block_size: BlockSize::Bytes(3 * 1024), block_size: BlockSize::Bytes(3 * 1024),
..Default::default() ..Default::default()
}; };
assert_eq!( assert_eq!(
Header::new(&options).to_string(), Header::get_headers(&options),
"Filesystem 3K-blocks Used Available Use% Mounted on " vec!(
"Filesystem",
"3K-blocks",
"Used",
"Available",
"Use%",
"Mounted on"
)
); );
} }
#[test] #[test]
fn test_header_display_human_readable_binary() { fn test_header_with_human_readable_binary() {
let options = Options { let options = Options {
block_size: BlockSize::HumanReadableBinary, block_size: BlockSize::HumanReadableBinary,
..Default::default() ..Default::default()
}; };
assert_eq!( assert_eq!(
Header::new(&options).to_string(), Header::get_headers(&options),
"Filesystem Size Used Available Use% Mounted on " vec!(
"Filesystem",
"Size",
"Used",
"Available",
"Use%",
"Mounted on"
)
); );
} }
#[test] #[test]
fn test_header_display_human_readable_si() { fn test_header_with_human_readable_si() {
let options = Options { let options = Options {
block_size: BlockSize::HumanReadableDecimal, block_size: BlockSize::HumanReadableDecimal,
..Default::default() ..Default::default()
}; };
assert_eq!( assert_eq!(
Header::new(&options).to_string(), Header::get_headers(&options),
"Filesystem Size Used Available Use% Mounted on " vec!(
"Filesystem",
"Size",
"Used",
"Available",
"Use%",
"Mounted on"
)
); );
} }
#[test] #[test]
fn test_row_display() { fn test_row_formatter() {
let options = Options { let options = Options {
block_size: BlockSize::Bytes(1), block_size: BlockSize::Bytes(1),
..Default::default() ..Default::default()
@ -439,14 +582,15 @@ mod tests {
inodes_free: 8, inodes_free: 8,
inodes_usage: Some(0.2), inodes_usage: Some(0.2),
}; };
let fmt = RowFormatter::new(&row, &options);
assert_eq!( assert_eq!(
DisplayRow::new(&row, &options).to_string(), fmt.get_values(),
"my_device 100 25 75 25% my_mount " vec!("my_device", "100", "25", "75", "25%", "my_mount")
); );
} }
#[test] #[test]
fn test_row_display_fs_type() { fn test_row_formatter_with_fs_type() {
let options = Options { let options = Options {
columns: COLUMNS_WITH_FS_TYPE.to_vec(), columns: COLUMNS_WITH_FS_TYPE.to_vec(),
block_size: BlockSize::Bytes(1), block_size: BlockSize::Bytes(1),
@ -471,14 +615,15 @@ mod tests {
inodes_free: 8, inodes_free: 8,
inodes_usage: Some(0.2), inodes_usage: Some(0.2),
}; };
let fmt = RowFormatter::new(&row, &options);
assert_eq!( assert_eq!(
DisplayRow::new(&row, &options).to_string(), fmt.get_values(),
"my_device my_type 100 25 75 25% my_mount " vec!("my_device", "my_type", "100", "25", "75", "25%", "my_mount")
); );
} }
#[test] #[test]
fn test_row_display_inodes() { fn test_row_formatter_with_inodes() {
let options = Options { let options = Options {
columns: COLUMNS_WITH_INODES.to_vec(), columns: COLUMNS_WITH_INODES.to_vec(),
block_size: BlockSize::Bytes(1), block_size: BlockSize::Bytes(1),
@ -503,14 +648,45 @@ mod tests {
inodes_free: 8, inodes_free: 8,
inodes_usage: Some(0.2), inodes_usage: Some(0.2),
}; };
let fmt = RowFormatter::new(&row, &options);
assert_eq!( assert_eq!(
DisplayRow::new(&row, &options).to_string(), fmt.get_values(),
"my_device 10 2 8 20% my_mount " vec!("my_device", "10", "2", "8", "20%", "my_mount")
); );
} }
#[test] #[test]
fn test_row_display_human_readable_si() { fn test_row_formatter_with_bytes_and_inodes() {
let options = Options {
columns: vec![Column::Size, Column::Itotal],
block_size: BlockSize::Bytes(100),
..Default::default()
};
let row = Row {
file: Some("/path/to/file".to_string()),
fs_device: "my_device".to_string(),
fs_type: "my_type".to_string(),
fs_mount: "my_mount".to_string(),
bytes: 100,
bytes_used: 25,
bytes_avail: 75,
bytes_usage: Some(0.25),
#[cfg(target_os = "macos")]
bytes_capacity: Some(0.5),
inodes: 10,
inodes_used: 2,
inodes_free: 8,
inodes_usage: Some(0.2),
};
let fmt = RowFormatter::new(&row, &options);
assert_eq!(fmt.get_values(), vec!("1", "10"));
}
#[test]
fn test_row_formatter_with_human_readable_si() {
let options = Options { let options = Options {
block_size: BlockSize::HumanReadableDecimal, block_size: BlockSize::HumanReadableDecimal,
columns: COLUMNS_WITH_FS_TYPE.to_vec(), columns: COLUMNS_WITH_FS_TYPE.to_vec(),
@ -535,14 +711,23 @@ mod tests {
inodes_free: 8, inodes_free: 8,
inodes_usage: Some(0.2), inodes_usage: Some(0.2),
}; };
let fmt = RowFormatter::new(&row, &options);
assert_eq!( assert_eq!(
DisplayRow::new(&row, &options).to_string(), fmt.get_values(),
"my_device my_type 4.0k 1.0k 3.0k 25% my_mount " vec!(
"my_device",
"my_type",
"4.0k",
"1.0k",
"3.0k",
"25%",
"my_mount"
)
); );
} }
#[test] #[test]
fn test_row_display_human_readable_binary() { fn test_row_formatter_with_human_readable_binary() {
let options = Options { let options = Options {
block_size: BlockSize::HumanReadableBinary, block_size: BlockSize::HumanReadableBinary,
columns: COLUMNS_WITH_FS_TYPE.to_vec(), columns: COLUMNS_WITH_FS_TYPE.to_vec(),
@ -567,14 +752,23 @@ mod tests {
inodes_free: 8, inodes_free: 8,
inodes_usage: Some(0.2), inodes_usage: Some(0.2),
}; };
let fmt = RowFormatter::new(&row, &options);
assert_eq!( assert_eq!(
DisplayRow::new(&row, &options).to_string(), fmt.get_values(),
"my_device my_type 4.0Ki 1.0Ki 3.0Ki 25% my_mount " vec!(
"my_device",
"my_type",
"4.0Ki",
"1.0Ki",
"3.0Ki",
"25%",
"my_mount"
)
); );
} }
#[test] #[test]
fn test_row_display_round_up_usage() { fn test_row_formatter_with_round_up_usage() {
let options = Options { let options = Options {
block_size: BlockSize::Bytes(1), block_size: BlockSize::Bytes(1),
..Default::default() ..Default::default()
@ -598,9 +792,10 @@ mod tests {
inodes_free: 8, inodes_free: 8,
inodes_usage: Some(0.2), inodes_usage: Some(0.2),
}; };
let fmt = RowFormatter::new(&row, &options);
assert_eq!( assert_eq!(
DisplayRow::new(&row, &options).to_string(), fmt.get_values(),
"my_device 100 25 75 26% my_mount " vec!("my_device", "100", "25", "75", "26%", "my_mount")
); );
} }
} }

25
src/uu/dir/Cargo.toml Normal file
View file

@ -0,0 +1,25 @@
[package]
name = "uu_dir"
version = "0.0.13"
authors = ["uutils developers"]
license = "MIT"
description = "shortcut to ls -C -b"
homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/ls"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"]
edition = "2021"
[lib]
path = "src/dir.rs"
[dependencies]
clap = { version = "3.1", features = ["wrap_help", "cargo", "env"] }
uucore = { version = ">=0.0.8", package = "uucore", path = "../../uucore", features = ["entries", "fs"] }
selinux = { version="0.2", optional = true }
uu_ls = {path="../ls"}
[[bin]]
name = "dir"
path = "src/main.rs"

1
src/uu/dir/LICENSE Symbolic link
View file

@ -0,0 +1 @@
../../../LICENSE

70
src/uu/dir/src/dir.rs Normal file
View file

@ -0,0 +1,70 @@
// * This file is part of the uutils coreutils package.
// *
// * (c) gmnsii <gmnsii@protonmail.com>
// *
// * For the full copyright and license information, please view the LICENSE file
// * that was distributed with this source code.
use clap::Command;
use std::path::Path;
use uu_ls::quoting_style::{Quotes, QuotingStyle};
use uu_ls::{options, Config, Format};
use uucore::error::UResult;
#[uucore::main]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let command = uu_ls::uu_app();
let matches = command.get_matches_from(args);
let mut default_quoting_style = false;
let mut default_format_style = false;
// We check if any options on formatting or quoting style have been given.
// If not, we will use dir default formatting and quoting style options
if !matches.is_present(options::QUOTING_STYLE)
&& !matches.is_present(options::quoting::C)
&& !matches.is_present(options::quoting::ESCAPE)
&& !matches.is_present(options::quoting::LITERAL)
{
default_quoting_style = true;
}
if !matches.is_present(options::FORMAT)
&& !matches.is_present(options::format::ACROSS)
&& !matches.is_present(options::format::COLUMNS)
&& !matches.is_present(options::format::COMMAS)
&& !matches.is_present(options::format::LONG)
&& !matches.is_present(options::format::LONG_NO_GROUP)
&& !matches.is_present(options::format::LONG_NO_OWNER)
&& !matches.is_present(options::format::LONG_NUMERIC_UID_GID)
&& !matches.is_present(options::format::ONE_LINE)
{
default_format_style = true;
}
let mut config = Config::from(&matches)?;
if default_quoting_style {
config.quoting_style = QuotingStyle::C {
quotes: Quotes::None,
};
}
if default_format_style {
config.format = Format::Columns;
}
let locs = matches
.values_of_os(options::PATHS)
.map(|v| v.map(Path::new).collect())
.unwrap_or_else(|| vec![Path::new(".")]);
uu_ls::list(locs, &config)
}
// To avoid code duplication, we reuse ls uu_app function which has the same
// arguments. However, coreutils won't compile if one of the utils is missing
// an uu_app function, so we need this dummy one.
pub fn uu_app<'a>() -> Command<'a> {
Command::new(uucore::util_name())
}

1
src/uu/dir/src/main.rs Normal file
View file

@ -0,0 +1 @@
uucore::bin!(uu_dir);

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/dircolors" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/dircolors"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/dircolors.rs" path = "src/dircolors.rs"

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/dirname" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/dirname"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/dirname.rs" path = "src/dirname.rs"

View file

@ -9,13 +9,15 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/du" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/du"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/du.rs" path = "src/du.rs"
[dependencies] [dependencies]
chrono = "^0.4.11" chrono = "^0.4.11"
# For the --exclude & --exclude-from options
glob = "0.3.0"
clap = { version = "3.1", features = ["wrap_help", "cargo"] } clap = { version = "3.1", features = ["wrap_help", "cargo"] }
uucore = { version=">=0.0.11", package="uucore", path="../../uucore" } uucore = { version=">=0.0.11", package="uucore", path="../../uucore" }

View file

@ -11,11 +11,15 @@ extern crate uucore;
use chrono::prelude::DateTime; use chrono::prelude::DateTime;
use chrono::Local; use chrono::Local;
use clap::{crate_version, Arg, ArgMatches, Command}; use clap::{crate_version, Arg, ArgMatches, Command};
use glob::Pattern;
use std::collections::HashSet; use std::collections::HashSet;
use std::env; use std::env;
use std::fs; use std::fs;
use std::fs::File;
#[cfg(not(windows))] #[cfg(not(windows))]
use std::fs::Metadata; use std::fs::Metadata;
use std::io::BufRead;
use std::io::BufReader;
use std::io::{ErrorKind, Result}; use std::io::{ErrorKind, Result};
use std::iter; use std::iter;
#[cfg(not(windows))] #[cfg(not(windows))]
@ -24,14 +28,13 @@ use std::os::unix::fs::MetadataExt;
use std::os::windows::fs::MetadataExt; use std::os::windows::fs::MetadataExt;
#[cfg(windows)] #[cfg(windows)]
use std::os::windows::io::AsRawHandle; use std::os::windows::io::AsRawHandle;
#[cfg(windows)]
use std::path::Path; use std::path::Path;
use std::path::PathBuf; use std::path::PathBuf;
use std::str::FromStr; use std::str::FromStr;
use std::time::{Duration, UNIX_EPOCH}; use std::time::{Duration, UNIX_EPOCH};
use std::{error::Error, fmt::Display}; use std::{error::Error, fmt::Display};
use uucore::display::{print_verbatim, Quotable}; use uucore::display::{print_verbatim, Quotable};
use uucore::error::{UError, UResult}; use uucore::error::{set_exit_code, UError, UResult};
use uucore::format_usage; use uucore::format_usage;
use uucore::parse_size::{parse_size, ParseSizeError}; use uucore::parse_size::{parse_size, ParseSizeError};
use uucore::InvalidEncodingHandling; use uucore::InvalidEncodingHandling;
@ -68,6 +71,9 @@ mod options {
pub const ONE_FILE_SYSTEM: &str = "one-file-system"; pub const ONE_FILE_SYSTEM: &str = "one-file-system";
pub const DEREFERENCE: &str = "dereference"; pub const DEREFERENCE: &str = "dereference";
pub const INODES: &str = "inodes"; pub const INODES: &str = "inodes";
pub const EXCLUDE: &str = "exclude";
pub const EXCLUDE_FROM: &str = "exclude-from";
pub const VERBOSE: &str = "verbose";
pub const FILE: &str = "FILE"; pub const FILE: &str = "FILE";
} }
@ -80,6 +86,12 @@ Otherwise, units default to 1024 bytes (or 512 if POSIXLY_CORRECT is set).
SIZE is an integer and optional unit (example: 10M is 10*1024*1024). SIZE is an integer and optional unit (example: 10M is 10*1024*1024).
Units are K, M, G, T, P, E, Z, Y (powers of 1024) or KB, MB,... (powers Units are K, M, G, T, P, E, Z, Y (powers of 1024) or KB, MB,... (powers
of 1000). of 1000).
PATTERN allows some advanced exclusions. For example, the following syntaxes
are supported:
? will match only one character
* will match zero or more characters
{a,b} will match a or b
"; ";
const USAGE: &str = "\ const USAGE: &str = "\
{} [OPTION]... [FILE]... {} [OPTION]... [FILE]...
@ -97,6 +109,7 @@ struct Options {
one_file_system: bool, one_file_system: bool,
dereference: bool, dereference: bool,
inodes: bool, inodes: bool,
verbose: bool,
} }
#[derive(PartialEq, Eq, Hash, Clone, Copy)] #[derive(PartialEq, Eq, Hash, Clone, Copy)]
@ -253,7 +266,7 @@ fn read_block_size(s: Option<&str>) -> u64 {
parse_size(s) parse_size(s)
.unwrap_or_else(|e| crash!(1, "{}", format_error_message(&e, s, options::BLOCK_SIZE))) .unwrap_or_else(|e| crash!(1, "{}", format_error_message(&e, s, options::BLOCK_SIZE)))
} else { } else {
for env_var in &["DU_BLOCK_SIZE", "BLOCK_SIZE", "BLOCKSIZE"] { for env_var in ["DU_BLOCK_SIZE", "BLOCK_SIZE", "BLOCKSIZE"] {
if let Ok(env_size) = env::var(env_var) { if let Ok(env_size) = env::var(env_var) {
if let Ok(v) = parse_size(&env_size) { if let Ok(v) = parse_size(&env_size) {
return v; return v;
@ -287,6 +300,7 @@ fn du(
options: &Options, options: &Options,
depth: usize, depth: usize,
inodes: &mut HashSet<FileInfo>, inodes: &mut HashSet<FileInfo>,
exclude: &[Pattern],
) -> Box<dyn DoubleEndedIterator<Item = Stat>> { ) -> Box<dyn DoubleEndedIterator<Item = Stat>> {
let mut stats = vec![]; let mut stats = vec![];
let mut futures = vec![]; let mut futures = vec![];
@ -301,49 +315,73 @@ fn du(
my_stat.path.quote(), my_stat.path.quote(),
e e
); );
set_exit_code(1);
return Box::new(iter::once(my_stat)); return Box::new(iter::once(my_stat));
} }
}; };
for f in read { 'file_loop: for f in read {
match f { match f {
Ok(entry) => match Stat::new(entry.path(), options) { Ok(entry) => {
Ok(this_stat) => { match Stat::new(entry.path(), options) {
if let Some(inode) = this_stat.inode { Ok(this_stat) => {
if inodes.contains(&inode) { // We have an exclude list
continue; for pattern in exclude {
} // Look at all patterns with both short and long paths
inodes.insert(inode); // if we have 'du foo' but search to exclude 'foo/bar'
} // we need the full path
if this_stat.is_dir { if pattern.matches(&this_stat.path.to_string_lossy())
if options.one_file_system { || pattern.matches(&entry.file_name().into_string().unwrap())
if let (Some(this_inode), Some(my_inode)) =
(this_stat.inode, my_stat.inode)
{ {
if this_inode.dev_id != my_inode.dev_id { // if the directory is ignored, leave early
continue; if options.verbose {
println!("{} ignored", &this_stat.path.quote());
} }
// Go to the next file
continue 'file_loop;
} }
} }
futures.push(du(this_stat, options, depth + 1, inodes));
} else { if let Some(inode) = this_stat.inode {
my_stat.size += this_stat.size; if inodes.contains(&inode) {
my_stat.blocks += this_stat.blocks; continue;
my_stat.inodes += 1; }
if options.all { inodes.insert(inode);
stats.push(this_stat); }
if this_stat.is_dir {
if options.one_file_system {
if let (Some(this_inode), Some(my_inode)) =
(this_stat.inode, my_stat.inode)
{
if this_inode.dev_id != my_inode.dev_id {
continue;
}
}
}
futures.push(du(this_stat, options, depth + 1, inodes, exclude));
} else {
my_stat.size += this_stat.size;
my_stat.blocks += this_stat.blocks;
my_stat.inodes += 1;
if options.all {
stats.push(this_stat);
}
} }
} }
Err(error) => match error.kind() {
ErrorKind::PermissionDenied => {
let description = format!("cannot access {}", entry.path().quote());
let error_message = "Permission denied";
show_error_custom_description!(description, "{}", error_message);
set_exit_code(1);
}
_ => {
set_exit_code(1);
show_error!("cannot access {}: {}", entry.path().quote(), error);
}
},
} }
Err(error) => match error.kind() { }
ErrorKind::PermissionDenied => {
let description = format!("cannot access {}", entry.path().quote());
let error_message = "Permission denied";
show_error_custom_description!(description, "{}", error_message);
}
_ => show_error!("cannot access {}: {}", entry.path().quote(), error),
},
},
Err(error) => show_error!("{}", error), Err(error) => show_error!("{}", error),
} }
} }
@ -401,6 +439,7 @@ enum DuError {
SummarizeDepthConflict(String), SummarizeDepthConflict(String),
InvalidTimeStyleArg(String), InvalidTimeStyleArg(String),
InvalidTimeArg(String), InvalidTimeArg(String),
InvalidGlob(String),
} }
impl Display for DuError { impl Display for DuError {
@ -431,6 +470,7 @@ Try '{} --help' for more information.",
'birth' and 'creation' arguments are not supported on this platform.", 'birth' and 'creation' arguments are not supported on this platform.",
s.quote() s.quote()
), ),
DuError::InvalidGlob(s) => write!(f, "Invalid exclude syntax: {}", s),
} }
} }
} }
@ -443,11 +483,75 @@ impl UError for DuError {
Self::InvalidMaxDepthArg(_) Self::InvalidMaxDepthArg(_)
| Self::SummarizeDepthConflict(_) | Self::SummarizeDepthConflict(_)
| Self::InvalidTimeStyleArg(_) | Self::InvalidTimeStyleArg(_)
| Self::InvalidTimeArg(_) => 1, | Self::InvalidTimeArg(_)
| Self::InvalidGlob(_) => 1,
} }
} }
} }
// Read a file and return each line in a vector of String
fn file_as_vec(filename: impl AsRef<Path>) -> Vec<String> {
let file = File::open(filename).expect("no such file");
let buf = BufReader::new(file);
buf.lines()
.map(|l| l.expect("Could not parse line"))
.collect()
}
// Given the --exclude-from and/or --exclude arguments, returns the globset lists
// to ignore the files
fn get_glob_ignore(matches: &ArgMatches) -> UResult<Vec<Pattern>> {
let mut excludes_from = if matches.is_present(options::EXCLUDE_FROM) {
match matches.values_of(options::EXCLUDE_FROM) {
Some(all_files) => {
let mut exclusion = Vec::<String>::new();
// Read the exclude lists from all the files
// and add them into a vector of string
let files: Vec<String> = all_files.clone().map(|v| v.to_owned()).collect();
for f in files {
exclusion.extend(file_as_vec(&f));
}
exclusion
}
None => Vec::<String>::new(),
}
} else {
Vec::<String>::new()
};
let mut excludes = if matches.is_present(options::EXCLUDE) {
match matches.values_of(options::EXCLUDE) {
Some(v) => {
// Read the various arguments
v.clone().map(|v| v.to_owned()).collect()
}
None => Vec::<String>::new(),
}
} else {
Vec::<String>::new()
};
// Merge the two lines
excludes.append(&mut excludes_from);
if !&excludes.is_empty() {
let mut builder = Vec::new();
// Create the `Vec` of excludes
for f in excludes {
if matches.is_present(options::VERBOSE) {
println!("adding {:?} to the exclude list ", &f);
}
match Pattern::new(&f) {
Ok(glob) => builder.push(glob),
Err(err) => return Err(DuError::InvalidGlob(err.to_string()).into()),
};
}
Ok(builder)
} else {
Ok(Vec::new())
}
}
#[uucore::main] #[uucore::main]
#[allow(clippy::cognitive_complexity)] #[allow(clippy::cognitive_complexity)]
pub fn uumain(args: impl uucore::Args) -> UResult<()> { pub fn uumain(args: impl uucore::Args) -> UResult<()> {
@ -470,6 +574,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
one_file_system: matches.is_present(options::ONE_FILE_SYSTEM), one_file_system: matches.is_present(options::ONE_FILE_SYSTEM),
dereference: matches.is_present(options::DEREFERENCE), dereference: matches.is_present(options::DEREFERENCE),
inodes: matches.is_present(options::INODES), inodes: matches.is_present(options::INODES),
verbose: matches.is_present(options::VERBOSE),
}; };
let files = match matches.value_of(options::FILE) { let files = match matches.value_of(options::FILE) {
@ -524,8 +629,25 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
"\n" "\n"
}; };
let excludes = get_glob_ignore(&matches)?;
let mut grand_total = 0; let mut grand_total = 0;
for path_string in files { 'loop_file: for path_string in files {
// Skip if we don't want to ignore anything
if !&excludes.is_empty() {
for pattern in &excludes {
{
if pattern.matches(path_string) {
// if the directory is ignored, leave early
if options.verbose {
println!("{} ignored", path_string.quote());
}
continue 'loop_file;
}
}
}
}
let path = PathBuf::from(&path_string); let path = PathBuf::from(&path_string);
match Stat::new(path, &options) { match Stat::new(path, &options) {
Ok(stat) => { Ok(stat) => {
@ -533,7 +655,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
if let Some(inode) = stat.inode { if let Some(inode) = stat.inode {
inodes.insert(inode); inodes.insert(inode);
} }
let iter = du(stat, &options, 0, &mut inodes); let iter = du(stat, &options, 0, &mut inodes, &excludes);
let (_, len) = iter.size_hint(); let (_, len) = iter.size_hint();
let len = len.unwrap(); let len = len.unwrap();
for (index, stat) in iter.enumerate() { for (index, stat) in iter.enumerate() {
@ -758,19 +880,28 @@ pub fn uu_app<'a>() -> Command<'a> {
.help("exclude entries smaller than SIZE if positive, \ .help("exclude entries smaller than SIZE if positive, \
or entries greater than SIZE if negative") or entries greater than SIZE if negative")
) )
// .arg( .arg(
// Arg::new("") Arg::new(options::VERBOSE)
// .short('x') .short('v')
// .long("exclude-from") .long("verbose")
// .value_name("FILE") .help("verbose mode (option not present in GNU/Coreutils)")
// .help("exclude files that match any pattern in FILE") )
// ) .arg(
// .arg( Arg::new(options::EXCLUDE)
// Arg::new("exclude") .long(options::EXCLUDE)
// .long("exclude") .value_name("PATTERN")
// .value_name("PATTERN") .help("exclude files that match PATTERN")
// .help("exclude files that match PATTERN") .multiple_occurrences(true)
// ) )
.arg(
Arg::new(options::EXCLUDE_FROM)
.short('X')
.long("exclude-from")
.value_name("FILE")
.help("exclude files that match any pattern in FILE")
.multiple_occurrences(true)
)
.arg( .arg(
Arg::new(options::TIME) Arg::new(options::TIME)
.long(options::TIME) .long(options::TIME)

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/echo" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/echo"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/echo.rs" path = "src/echo.rs"

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/env" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/env"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/env.rs" path = "src/env.rs"

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/expand" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/expand"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/expand.rs" path = "src/expand.rs"

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/expr" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/expr"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/expr.rs" path = "src/expr.rs"

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[build-dependencies] [build-dependencies]
num-traits = "0.2.13" # used in src/numerics.rs, which is included by build.rs num-traits = "0.2.13" # used in src/numerics.rs, which is included by build.rs

View file

@ -69,7 +69,7 @@ pub(crate) fn test<A: Arithmetic + Basis>(m: A) -> Result {
continue; continue;
} }
let a = m.from_u64(_a); let a = m.to_mod(_a);
// x = a^r mod n // x = a^r mod n
let mut x = m.pow(a, r); let mut x = m.pow(a, r);

View file

@ -16,7 +16,7 @@ pub(crate) trait Arithmetic: Copy + Sized {
fn new(m: u64) -> Self; fn new(m: u64) -> Self;
fn modulus(&self) -> u64; fn modulus(&self) -> u64;
fn from_u64(&self, n: u64) -> Self::ModInt; fn to_mod(&self, n: u64) -> Self::ModInt;
fn to_u64(&self, n: Self::ModInt) -> u64; fn to_u64(&self, n: Self::ModInt) -> u64;
fn add(&self, a: Self::ModInt, b: Self::ModInt) -> Self::ModInt; fn add(&self, a: Self::ModInt, b: Self::ModInt) -> Self::ModInt;
fn mul(&self, a: Self::ModInt, b: Self::ModInt) -> Self::ModInt; fn mul(&self, a: Self::ModInt, b: Self::ModInt) -> Self::ModInt;
@ -47,13 +47,13 @@ pub(crate) trait Arithmetic: Copy + Sized {
} }
fn one(&self) -> Self::ModInt { fn one(&self) -> Self::ModInt {
self.from_u64(1) self.to_mod(1)
} }
fn minus_one(&self) -> Self::ModInt { fn minus_one(&self) -> Self::ModInt {
self.from_u64(self.modulus() - 1) self.to_mod(self.modulus() - 1)
} }
fn zero(&self) -> Self::ModInt { fn zero(&self) -> Self::ModInt {
self.from_u64(0) self.to_mod(0)
} }
} }
@ -113,7 +113,7 @@ impl<T: DoubleInt> Arithmetic for Montgomery<T> {
self.n.as_u64() self.n.as_u64()
} }
fn from_u64(&self, x: u64) -> Self::ModInt { fn to_mod(&self, x: u64) -> Self::ModInt {
// TODO: optimise! // TODO: optimise!
debug_assert!(x < self.n.as_u64()); debug_assert!(x < self.n.as_u64());
let r = T::from_double_width( let r = T::from_double_width(
@ -189,9 +189,9 @@ mod tests {
let n = 2 * n + 1; let n = 2 * n + 1;
let m = Montgomery::<A>::new(n); let m = Montgomery::<A>::new(n);
for x in 0..n { for x in 0..n {
let m_x = m.from_u64(x); let m_x = m.to_mod(x);
for y in 0..=x { for y in 0..=x {
let m_y = m.from_u64(y); let m_y = m.to_mod(y);
println!("{n:?}, {x:?}, {y:?}", n = n, x = x, y = y); println!("{n:?}, {x:?}, {y:?}", n = n, x = x, y = y);
assert_eq!((x + y) % n, m.to_u64(m.add(m_x, m_y))); assert_eq!((x + y) % n, m.to_u64(m.add(m_x, m_y)));
} }
@ -205,9 +205,9 @@ mod tests {
let n = 2 * n + 1; let n = 2 * n + 1;
let m = Montgomery::<A>::new(n); let m = Montgomery::<A>::new(n);
for x in 0..n { for x in 0..n {
let m_x = m.from_u64(x); let m_x = m.to_mod(x);
for y in 0..=x { for y in 0..=x {
let m_y = m.from_u64(y); let m_y = m.to_mod(y);
assert_eq!((x * y) % n, m.to_u64(m.mul(m_x, m_y))); assert_eq!((x * y) % n, m.to_u64(m.mul(m_x, m_y)));
} }
} }
@ -220,7 +220,7 @@ mod tests {
let n = 2 * n + 1; let n = 2 * n + 1;
let m = Montgomery::<A>::new(n); let m = Montgomery::<A>::new(n);
for x in 0..n { for x in 0..n {
let x_ = m.from_u64(x); let x_ = m.to_mod(x);
assert_eq!(x, m.to_u64(x_)); assert_eq!(x, m.to_u64(x_));
} }
} }

View file

@ -18,7 +18,7 @@ pub(crate) fn find_divisor<A: Arithmetic>(n: A) -> u64 {
let mut rand = { let mut rand = {
let range = Uniform::new(1, n.modulus()); let range = Uniform::new(1, n.modulus());
let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap(); let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap();
move || n.from_u64(range.sample(&mut rng)) move || n.to_mod(range.sample(&mut rng))
}; };
let quadratic = |a, b| move |x| n.add(n.mul(a, n.mul(x, x)), b); let quadratic = |a, b| move |x| n.add(n.mul(a, n.mul(x, x)), b);

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/false" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/false"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/false.rs" path = "src/false.rs"

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/fmt" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/fmt"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/fmt.rs" path = "src/fmt.rs"

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/fold" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/fold"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/fold.rs" path = "src/fold.rs"

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/groups" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/groups"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/groups.rs" path = "src/groups.rs"

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/hashsum" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/hashsum"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/hashsum.rs" path = "src/hashsum.rs"
@ -20,7 +20,7 @@ clap = { version = "3.1", features = ["wrap_help", "cargo"] }
hex = "0.4.3" hex = "0.4.3"
memchr = "2" memchr = "2"
md-5 = "0.10.1" md-5 = "0.10.1"
regex = "1.0.1" regex = "1.5.5"
sha1 = "0.10.1" sha1 = "0.10.1"
sha2 = "0.10.2" sha2 = "0.10.2"
sha3 = "0.10.1" sha3 = "0.10.1"

View file

@ -7,7 +7,7 @@
// * For the full copyright and license information, please view the LICENSE // * For the full copyright and license information, please view the LICENSE
// * file that was distributed with this source code. // * file that was distributed with this source code.
// spell-checker:ignore (ToDO) algo, algoname, regexes, nread // spell-checker:ignore (ToDO) algo, algoname, regexes, nread, nonames
#[macro_use] #[macro_use]
extern crate clap; extern crate clap;
@ -46,6 +46,7 @@ struct Options {
binary: bool, binary: bool,
check: bool, check: bool,
tag: bool, tag: bool,
nonames: bool,
status: bool, status: bool,
quiet: bool, quiet: bool,
strict: bool, strict: bool,
@ -316,6 +317,7 @@ pub fn uumain(mut args: impl uucore::Args) -> UResult<()> {
}; };
let check = matches.is_present("check"); let check = matches.is_present("check");
let tag = matches.is_present("tag"); let tag = matches.is_present("tag");
let nonames = matches.is_present("no-names");
let status = matches.is_present("status"); let status = matches.is_present("status");
let quiet = matches.is_present("quiet") || status; let quiet = matches.is_present("quiet") || status;
let strict = matches.is_present("strict"); let strict = matches.is_present("strict");
@ -328,6 +330,7 @@ pub fn uumain(mut args: impl uucore::Args) -> UResult<()> {
binary, binary,
check, check,
tag, tag,
nonames,
status, status,
quiet, quiet,
strict, strict,
@ -370,6 +373,11 @@ pub fn uu_app_common<'a>() -> Command<'a> {
.long("tag") .long("tag")
.help("create a BSD-style checksum"), .help("create a BSD-style checksum"),
) )
.arg(
Arg::new("no-names")
.long("no-names")
.help("Omits filenames in the output (option not present in GNU/Coreutils)"),
)
.arg( .arg(
Arg::new("text") Arg::new("text")
.short('t') .short('t')
@ -602,6 +610,8 @@ where
.map_err_context(|| "failed to read input".to_string())?; .map_err_context(|| "failed to read input".to_string())?;
if options.tag { if options.tag {
println!("{} ({}) = {}", options.algoname, filename.display(), sum); println!("{} ({}) = {}", options.algoname, filename.display(), sum);
} else if options.nonames {
println!("{}", sum);
} else { } else {
println!("{} {}{}", sum, binary_marker, filename.display()); println!("{} {}{}", sum, binary_marker, filename.display());
} }

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/head" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/head"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/head.rs" path = "src/head.rs"

View file

@ -6,7 +6,6 @@
// spell-checker:ignore (vars) zlines BUFWRITER seekable // spell-checker:ignore (vars) zlines BUFWRITER seekable
use clap::{crate_version, Arg, ArgMatches, Command}; use clap::{crate_version, Arg, ArgMatches, Command};
use std::convert::{TryFrom, TryInto};
use std::ffi::OsString; use std::ffi::OsString;
use std::io::{self, BufWriter, ErrorKind, Read, Seek, SeekFrom, Write}; use std::io::{self, BufWriter, ErrorKind, Read, Seek, SeekFrom, Write};
use uucore::display::Quotable; use uucore::display::Quotable;

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/hostid" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/hostid"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/hostid.rs" path = "src/hostid.rs"

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/hostname" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/hostname"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/hostname.rs" path = "src/hostname.rs"

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/id" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/id"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/id.rs" path = "src/id.rs"

View file

@ -12,7 +12,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/install" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/install"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/install.rs" path = "src/install.rs"

View file

@ -464,6 +464,19 @@ fn standard(mut paths: Vec<String>, b: &Behavior) -> UResult<()> {
} else { } else {
if let Some(parent) = target.parent() { if let Some(parent) = target.parent() {
if !parent.exists() && b.create_leading { if !parent.exists() && b.create_leading {
if b.verbose {
let mut result = PathBuf::new();
// When creating directories with -Dv, show directory creations step
// by step
for part in parent.components() {
result.push(part.as_os_str());
if !Path::new(part.as_os_str()).is_dir() {
// Don't display when the directory already exists
println!("install: creating directory {}", result.quote());
}
}
}
if let Err(e) = fs::create_dir_all(parent) { if let Err(e) = fs::create_dir_all(parent) {
return Err(InstallError::CreateDirFailed(parent.to_path_buf(), e).into()); return Err(InstallError::CreateDirFailed(parent.to_path_buf(), e).into());
} }
@ -594,13 +607,19 @@ fn copy(from: &Path, to: &Path, b: &Behavior) -> UResult<()> {
match process::Command::new(&b.strip_program).arg(to).output() { match process::Command::new(&b.strip_program).arg(to).output() {
Ok(o) => { Ok(o) => {
if !o.status.success() { if !o.status.success() {
// Follow GNU's behavior: if strip fails, removes the target
let _ = fs::remove_file(to);
return Err(InstallError::StripProgramFailed( return Err(InstallError::StripProgramFailed(
String::from_utf8(o.stderr).unwrap_or_default(), String::from_utf8(o.stderr).unwrap_or_default(),
) )
.into()); .into());
} }
} }
Err(e) => return Err(InstallError::StripProgramFailed(e.to_string()).into()), Err(e) => {
// Follow GNU's behavior: if strip fails, removes the target
let _ = fs::remove_file(to);
return Err(InstallError::StripProgramFailed(e.to_string()).into());
}
} }
} }

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/join" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/join"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/join.rs" path = "src/join.rs"

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/kill" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/kill"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/kill.rs" path = "src/kill.rs"

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/link" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/link"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/link.rs" path = "src/link.rs"

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/ln" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/ln"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/ln.rs" path = "src/ln.rs"

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/logname" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/logname"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/logname.rs" path = "src/logname.rs"

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/ls" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/ls"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/ls.rs" path = "src/ls.rs"
@ -22,7 +22,7 @@ number_prefix = "0.4"
term_grid = "0.1.5" term_grid = "0.1.5"
termsize = "0.1.6" termsize = "0.1.6"
glob = "0.3.0" glob = "0.3.0"
lscolors = { version = "0.7.1", features = ["ansi_term"] } lscolors = { version = "0.9.0", features = ["ansi_term"] }
uucore = { version = ">=0.0.8", package = "uucore", path = "../../uucore", features = ["entries", "fs"] } uucore = { version = ">=0.0.8", package = "uucore", path = "../../uucore", features = ["entries", "fs"] }
once_cell = "1.10.0" once_cell = "1.10.0"
atty = "0.2" atty = "0.2"

File diff suppressed because it is too large Load diff

View file

@ -6,7 +6,7 @@ use std::ffi::OsStr;
const SPECIAL_SHELL_CHARS_START: &[char] = &['~', '#']; const SPECIAL_SHELL_CHARS_START: &[char] = &['~', '#'];
const SPECIAL_SHELL_CHARS: &str = "`$&*()|[]{};\\'\"<>?! "; const SPECIAL_SHELL_CHARS: &str = "`$&*()|[]{};\\'\"<>?! ";
pub(super) enum QuotingStyle { pub enum QuotingStyle {
Shell { Shell {
escape: bool, escape: bool,
always_quote: bool, always_quote: bool,
@ -21,7 +21,7 @@ pub(super) enum QuotingStyle {
} }
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub(super) enum Quotes { pub enum Quotes {
None, None,
Single, Single,
Double, Double,

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/mkdir" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/mkdir"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/mkdir.rs" path = "src/mkdir.rs"

View file

@ -64,12 +64,15 @@ fn get_mode(matches: &ArgMatches, mode_had_minus_prefix: bool) -> Result<u32, St
} }
Ok(new_mode) Ok(new_mode)
} }
None => Ok(DEFAULT_PERM), None => {
// If no mode argument is specified return the mode derived from umask
Ok(!mode::get_umask() & 0o0777)
}
} }
} }
#[cfg(windows)] #[cfg(windows)]
fn strip_minus_from_mode(_args: &mut Vec<String>) -> bool { fn strip_minus_from_mode(_args: &mut [String]) -> bool {
false false
} }
@ -115,7 +118,7 @@ pub fn uu_app<'a>() -> Command<'a> {
.short('m') .short('m')
.long(options::MODE) .long(options::MODE)
.help("set file mode (not implemented on windows)") .help("set file mode (not implemented on windows)")
.default_value("755"), .takes_value(true),
) )
.arg( .arg(
Arg::new(options::PARENTS) Arg::new(options::PARENTS)

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/mkfifo" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/mkfifo"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/mkfifo.rs" path = "src/mkfifo.rs"

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/mknod" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/mknod"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
name = "uu_mknod" name = "uu_mknod"

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/mktemp" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/mktemp"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/mktemp.rs" path = "src/mktemp.rs"

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/more" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/more"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/more.rs" path = "src/more.rs"

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/mv" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/mv"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/mv.rs" path = "src/mv.rs"

View file

@ -258,6 +258,16 @@ fn exec(files: &[OsString], b: &Behavior) -> UResult<()> {
move_files_into_dir(&[source.clone()], target, b) move_files_into_dir(&[source.clone()], target, b)
} }
} else if target.exists() && source.is_dir() { } else if target.exists() && source.is_dir() {
match b.overwrite {
OverwriteMode::NoClobber => return Ok(()),
OverwriteMode::Interactive => {
println!("{}: overwrite {}? ", uucore::util_name(), target.quote());
if !read_yes() {
return Ok(());
}
}
OverwriteMode::Force => {}
};
Err(MvError::NonDirectoryToDirectory( Err(MvError::NonDirectoryToDirectory(
source.quote().to_string(), source.quote().to_string(),
target.quote().to_string(), target.quote().to_string(),
@ -275,7 +285,23 @@ fn exec(files: &[OsString], b: &Behavior) -> UResult<()> {
)); ));
} }
let target_dir = paths.last().unwrap(); let target_dir = paths.last().unwrap();
move_files_into_dir(&paths[..paths.len() - 1], target_dir, b) let sources = &paths[..paths.len() - 1];
// Check if we have mv dir1 dir2 dir2
// And generate an error if this is the case
if sources.contains(target_dir) {
return Err(USimpleError::new(
1,
format!(
"cannot move {} to a subdirectory of itself, '{}/{}'",
target_dir.quote(),
target_dir.display(),
target_dir.display()
),
));
}
move_files_into_dir(sources, target_dir, b)
} }
} }
} }

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/nice" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/nice"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/nice.rs" path = "src/nice.rs"

View file

@ -9,14 +9,14 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/nl" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/nl"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/nl.rs" path = "src/nl.rs"
[dependencies] [dependencies]
clap = { version = "3.1", features = ["wrap_help", "cargo"] } clap = { version = "3.1", features = ["wrap_help", "cargo"] }
regex = "1.0.1" regex = "1.5.5"
uucore = { version=">=0.0.11", package="uucore", path="../../uucore" } uucore = { version=">=0.0.11", package="uucore", path="../../uucore" }
[[bin]] [[bin]]

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/nohup" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/nohup"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/nohup.rs" path = "src/nohup.rs"

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/nproc" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/nproc"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/nproc.rs" path = "src/nproc.rs"

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/numfmt" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/numfmt"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/numfmt.rs" path = "src/numfmt.rs"

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/od" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/od"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/od.rs" path = "src/od.rs"

View file

@ -29,7 +29,6 @@ mod prn_float;
mod prn_int; mod prn_int;
use std::cmp; use std::cmp;
use std::convert::TryFrom;
use crate::byteorder_io::*; use crate::byteorder_io::*;
use crate::formatteriteminfo::*; use crate::formatteriteminfo::*;

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/paste" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/paste"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/paste.rs" path = "src/paste.rs"

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/pathchk" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/pathchk"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/pathchk.rs" path = "src/pathchk.rs"

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/pinky" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/pinky"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/pinky.rs" path = "src/pinky.rs"

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/pr" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/pr"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/pr.rs" path = "src/pr.rs"
@ -20,7 +20,7 @@ uucore = { version=">=0.0.7", package="uucore", path="../../uucore", features=["
chrono = "0.4.19" chrono = "0.4.19"
quick-error = "2.0.1" quick-error = "2.0.1"
itertools = "0.10.0" itertools = "0.10.0"
regex = "1.0" regex = "1.5"
[[bin]] [[bin]]
name = "pr" name = "pr"

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/printenv" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/printenv"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/printenv.rs" path = "src/printenv.rs"

View file

@ -12,7 +12,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/printf" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/printf"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/printf.rs" path = "src/printf.rs"

View file

@ -9,14 +9,14 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/ptx" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/ptx"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/ptx.rs" path = "src/ptx.rs"
[dependencies] [dependencies]
clap = { version = "3.1", features = ["wrap_help", "cargo"] } clap = { version = "3.1", features = ["wrap_help", "cargo"] }
regex = "1.0.1" regex = "1.5.5"
uucore = { version=">=0.0.11", package="uucore", path="../../uucore" } uucore = { version=">=0.0.11", package="uucore", path="../../uucore" }
[[bin]] [[bin]]

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/pwd" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/pwd"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/pwd.rs" path = "src/pwd.rs"

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/readlink" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/readlink"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/readlink.rs" path = "src/readlink.rs"

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/realpath" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/realpath"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/realpath.rs" path = "src/realpath.rs"

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/relpath" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/relpath"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/relpath.rs" path = "src/relpath.rs"

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/rm" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/rm"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/rm.rs" path = "src/rm.rs"

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/rmdir" repository = "https://github.com/uutils/coreutils/tree/main/src/uu/rmdir"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2018" edition = "2021"
[lib] [lib]
path = "src/rmdir.rs" path = "src/rmdir.rs"

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