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
uses: actions-rs/toolchain@v1
with:
toolchain: nightly-2022-03-21
toolchain: nightly
default: true
profile: minimal
- name: Install `cargo-udeps`
@ -86,7 +86,7 @@ jobs:
fault_type="${{ steps.vars.outputs.FAULT_TYPE }}"
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 ; }
if [ -n "${{ steps.vars.outputs.FAIL_ON_FAULT }}" ] && [ -n "$fault" ]; then exit 1 ; fi
@ -483,7 +483,7 @@ jobs:
- name: Install `rust` toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: nightly-2022-03-21
toolchain: nightly
default: true
profile: minimal # minimal component installation (ie, no documentation)
- name: Test
@ -759,7 +759,7 @@ jobs:
with:
use-cross: ${{ steps.vars.outputs.CARGO_USE_CROSS }}
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 }}
- name: Archive executable artifacts
uses: actions/upload-artifact@v2
@ -935,7 +935,7 @@ jobs:
## VARs setup
outputs() { step_id="vars"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; }
# 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
case ${{ matrix.job.os }} in windows-*) TOOLCHAIN="$TOOLCHAIN-x86_64-pc-windows-gnu" ;; esac;
# * use requested TOOLCHAIN if specified
@ -994,7 +994,7 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: test
args: ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} --no-fail-fast -p uucore
args: --no-fail-fast -p uucore
env:
CARGO_INCREMENTAL: "0"
RUSTC_WRAPPER: ""
@ -1016,7 +1016,7 @@ jobs:
uses: actions-rs/cargo@v1
with:
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:
CARGO_INCREMENTAL: "0"
RUSTC_WRAPPER: ""

View file

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

View file

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

View file

@ -93,6 +93,7 @@ rollup
sed
selinuxenabled
sestatus
vdir
wslpath
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"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"]
edition = "2018"
edition = "2021"
build = "build.rs"
@ -45,6 +45,7 @@ feat_common_core = [
"cut",
"date",
"df",
"dir",
"dircolors",
"dirname",
"dd",
@ -100,6 +101,7 @@ feat_common_core = [
"unexpand",
"uniq",
"unlink",
"vdir",
"wc",
"yes",
]
@ -252,8 +254,7 @@ lazy_static = { version="1.3" }
textwrap = { version="0.15", features=["terminal_size"] }
uucore = { version=">=0.0.11", package="uucore", path="src/uucore" }
selinux = { version="0.2", optional = true }
ureq = "2.4.0"
zip = { version = "0.5.13", default_features=false, features=["deflate"] }
zip = { version = "0.6.0", default_features=false, features=["deflate"] }
# * uutils
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" }
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" }
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" }
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" }
@ -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" }
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" }
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" }
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" }
@ -373,7 +376,7 @@ glob = "0.3.0"
libc = "0.2"
pretty_assertions = "1"
rand = "0.8"
regex = "1.0"
regex = "1.5"
sha1 = { version="0.10", features=["std"] }
tempfile = "3"
time = "0.1"
@ -384,7 +387,7 @@ atty = "0.2"
hex-literal = "0.3.1"
[target.'cfg(target_os = "linux")'.dev-dependencies]
rlimit = "0.4.0"
rlimit = "0.8.3"
[target.'cfg(unix)'.dev-dependencies]
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
PROFILE ?= debug
@ -22,10 +22,10 @@ CARGOFLAGS ?=
# Install directories
PREFIX ?= /usr/local
DESTDIR ?=
BINDIR ?= /bin
MANDIR ?= /man/man1
BINDIR ?= $(PREFIX)/bin
DATAROOTDIR ?= $(PREFIX)/share
INSTALLDIR_BIN=$(DESTDIR)$(PREFIX)$(BINDIR)
INSTALLDIR_BIN=$(DESTDIR)$(BINDIR)
#prefix to apply to coreutils binary and all tool binaries
PROG_PREFIX ?=
@ -65,6 +65,7 @@ PROGS := \
date \
dd \
df \
dir \
dircolors \
dirname \
echo \
@ -118,6 +119,7 @@ PROGS := \
tsort \
unexpand \
uniq \
vdir \
wc \
whoami \
yes
@ -328,13 +330,13 @@ else
$(INSTALL) $(BUILDDIR)/$(prog) $(INSTALLDIR_BIN)/$(PROG_PREFIX)$(prog);)
$(if $(findstring test,$(INSTALLEES)), $(INSTALL) $(BUILDDIR)/test $(INSTALLDIR_BIN)/$(PROG_PREFIX)[)
endif
mkdir -p $(DESTDIR)$(PREFIX)/share/zsh/site-functions
mkdir -p $(DESTDIR)$(PREFIX)/share/bash-completion/completions
mkdir -p $(DESTDIR)$(PREFIX)/share/fish/vendor_completions.d
mkdir -p $(DESTDIR)$(DATAROOTDIR)/zsh/site-functions
mkdir -p $(DESTDIR)$(DATAROOTDIR)/bash-completion/completions
mkdir -p $(DESTDIR)$(DATAROOTDIR)/fish/vendor_completions.d
$(foreach prog, $(INSTALLEES), \
$(BUILDDIR)/coreutils completion $(prog) zsh > $(DESTDIR)$(PREFIX)/share/zsh/site-functions/_$(PROG_PREFIX)$(prog); \
$(BUILDDIR)/coreutils completion $(prog) bash > $(DESTDIR)$(PREFIX)/share/bash-completion/completions/$(PROG_PREFIX)$(prog); \
$(BUILDDIR)/coreutils completion $(prog) fish > $(DESTDIR)$(PREFIX)/share/fish/vendor_completions.d/$(PROG_PREFIX)$(prog).fish; \
$(BUILDDIR)/coreutils completion $(prog) zsh > $(DESTDIR)$(DATAROOTDIR)/zsh/site-functions/_$(PROG_PREFIX)$(prog); \
$(BUILDDIR)/coreutils completion $(prog) bash > $(DESTDIR)$(DATAROOTDIR)/bash-completion/completions/$(PROG_PREFIX)$(prog); \
$(BUILDDIR)/coreutils completion $(prog) fish > $(DESTDIR)$(DATAROOTDIR)/fish/vendor_completions.d/$(PROG_PREFIX)$(prog).fish; \
)
uninstall:
@ -343,8 +345,8 @@ ifeq (${MULTICALL}, y)
endif
rm -f $(addprefix $(INSTALLDIR_BIN)/$(PROG_PREFIX),$(PROGS))
rm -f $(INSTALLDIR_BIN)/$(PROG_PREFIX)[
rm -f $(addprefix $(DESTDIR)$(PREFIX)/share/zsh/site-functions/_$(PROG_PREFIX),$(PROGS))
rm -f $(addprefix $(DESTDIR)$(PREFIX)/share/bash-completion/completions/$(PROG_PREFIX),$(PROGS))
rm -f $(addprefix $(DESTDIR)$(PREFIX)/share/fish/vendor_completions.d/$(PROG_PREFIX),$(addsuffix .fish,$(PROGS)))
rm -f $(addprefix $(DESTDIR)$(DATAROOTDIR)/zsh/site-functions/_$(PROG_PREFIX),$(PROGS))
rm -f $(addprefix $(DESTDIR)$(DATAROOTDIR)/bash-completion/completions/$(PROG_PREFIX),$(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

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. Add `set -v` to have the bash verbose mode
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. ...
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
| 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 |
| base32 | date | |
@ -417,8 +426,8 @@ To contribute to uutils, please see [CONTRIBUTING](CONTRIBUTING.md).
| cut | tac | |
| dircolors | tail | |
| dirname | test | |
| du | | |
| echo | | |
| du | dir | |
| echo | vdir | |
| env | | |
| expand | | |
| factor | | |

1
docs/.gitignore vendored
View file

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

11
docs/theme/head.hbs vendored
View file

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

View file

@ -5,23 +5,26 @@
// spell-checker:ignore tldr
use clap::Command;
use std::collections::HashMap;
use std::ffi::OsString;
use std::fs::File;
use std::io::Cursor;
use std::io::{self, Read, Seek, Write};
use zip::ZipArchive;
include!(concat!(env!("OUT_DIR"), "/uutils_map.rs"));
fn main() -> io::Result<()> {
println!("Downloading tldr archive");
let mut zip_reader = ureq::get("https://tldr.sh/assets/tldr.zip")
.call()
.unwrap()
.into_reader();
let mut buffer = Vec::new();
zip_reader.read_to_end(&mut buffer).unwrap();
let mut tldr_zip = ZipArchive::new(Cursor::new(buffer)).unwrap();
let mut tldr_zip = File::open("docs/tldr.zip")
.ok()
.and_then(|f| ZipArchive::new(f).ok());
if tldr_zip.is_none() {
println!("Warning: No tldr archive found, so the documentation will not include examples.");
println!("To include examples in the documentation, download the tldr archive and put it in the docs/ folder.");
println!();
println!(" curl https://tldr.sh/assets/tldr.zip -o docs/tldr.zip");
println!();
}
let utils = util_map::<Box<dyn Iterator<Item = OsString>>>();
match std::fs::create_dir("docs/src/utils/") {
@ -29,6 +32,7 @@ fn main() -> io::Result<()> {
x => x,
}?;
println!("Writing initial info to SUMMARY.md");
let mut summary = File::create("docs/src/SUMMARY.md")?;
let _ = write!(
@ -44,6 +48,40 @@ fn main() -> io::Result<()> {
* [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<_>>();
utils.sort();
for (&name, (_, command)) in utils {
@ -52,7 +90,14 @@ fn main() -> io::Result<()> {
}
let p = format!("docs/src/utils/{}.md", name);
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);
} else {
println!("Error writing to {}", p);
@ -62,31 +107,65 @@ fn main() -> io::Result<()> {
Ok(())
}
fn write_markdown(
mut w: impl Write,
command: &mut Command,
name: &str,
tldr_zip: &mut zip::ZipArchive<impl Read + Seek>,
) -> io::Result<()> {
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)
struct MDWriter<'a, 'b> {
w: Box<dyn Write>,
command: Command<'a>,
name: &'a str,
tldr_zip: &'b mut Option<ZipArchive<File>>,
utils_per_platform: &'b HashMap<&'b str, Vec<String>>,
}
fn write_version(w: &mut impl Write, command: &Command) -> io::Result<()> {
impl<'a, 'b> MDWriter<'a, 'b> {
fn markdown(&mut self) -> io::Result<()> {
write!(self.w, "# {}\n\n", self.name)?;
self.additional()?;
self.usage()?;
self.description()?;
self.options()?;
self.examples()
}
fn additional(&mut self) -> io::Result<()> {
writeln!(self.w, "<div class=\"additional\">")?;
self.platforms()?;
self.version()?;
writeln!(self.w, "</div>")
}
fn platforms(&mut self) -> io::Result<()> {
writeln!(self.w, "<div class=\"platforms\">")?;
for (feature, icon) in [
("linux", "linux"),
// freebsd is disabled for now because mdbook does not use font-awesome 5 yet.
// ("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>")?;
Ok(())
}
fn version(&mut self) -> io::Result<()> {
writeln!(
w,
"<div class=\"version\">version: {}</div>",
command.render_version().split_once(' ').unwrap().1
self.w,
"<div class=\"version\">v{}</div>",
self.command.render_version().split_once(' ').unwrap().1
)
}
}
fn write_usage(w: &mut impl Write, command: &mut Command, name: &str) -> io::Result<()> {
writeln!(w, "\n```")?;
let mut usage: String = command
fn usage(&mut self) -> io::Result<()> {
writeln!(self.w, "\n```")?;
let mut usage: String = self
.command
.render_usage()
.lines()
.skip(1)
@ -94,81 +173,80 @@ fn write_usage(w: &mut impl Write, command: &mut Command, name: &str) -> io::Res
.filter(|l| !l.is_empty())
.collect::<Vec<_>>()
.join("\n");
usage = usage.replace(uucore::execution_phrase(), name);
writeln!(w, "{}", usage)?;
writeln!(w, "```")
}
usage = usage.replace(uucore::execution_phrase(), self.name);
writeln!(self.w, "{}", usage)?;
writeln!(self.w, "```")
}
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)
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 {
Ok(())
}
}
}
fn write_examples(
w: &mut impl Write,
name: &str,
tldr_zip: &mut zip::ZipArchive<impl Read + Seek>,
) -> io::Result<()> {
let content = if let Some(f) = get_zip_content(tldr_zip, &format!("pages/common/{}.md", name)) {
fn examples(&mut self) -> io::Result<()> {
if let Some(zip) = self.tldr_zip {
let content = if let Some(f) =
get_zip_content(zip, &format!("pages/common/{}.md", self.name))
{
f
} else if let Some(f) = get_zip_content(tldr_zip, &format!("pages/linux/{}.md", name)) {
} else if let Some(f) = get_zip_content(zip, &format!("pages/linux/{}.md", self.name)) {
f
} else {
return Ok(());
};
writeln!(w, "## Examples")?;
writeln!(w)?;
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!(w, "{}", l)?;
writeln!(self.w, "{}", l)?;
} else if line.starts_with('`') {
writeln!(w, "```shell\n{}\n```", line.trim_matches('`'))?;
writeln!(self.w, "```shell\n{}\n```", line.trim_matches('`'))?;
} else if line.is_empty() {
writeln!(w)?;
writeln!(self.w)?;
} else {
println!("Not sure what to do with this line:");
println!("{}", line);
}
}
writeln!(w)?;
writeln!(self.w)?;
writeln!(
w,
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!(w, ">")?;
writeln!(self.w, ">")?;
writeln!(
w,
self.w,
"> Please note that, as uutils is a work in progress, some examples might fail."
)
}
)?;
}
Ok(())
}
fn get_zip_content(archive: &mut ZipArchive<impl Read + Seek>, name: &str) -> Option<String> {
let mut s = String::new();
archive.by_name(name).ok()?.read_to_string(&mut s).unwrap();
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>")?;
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!(w, ", ")?;
write!(self.w, ", ")?;
} else {
first = false;
}
write!(w, "<code>")?;
write!(w, "--{}", l)?;
write!(self.w, "<code>")?;
write!(self.w, "--{}", l)?;
if let Some(names) = arg.get_value_names() {
write!(
w,
self.w,
"={}",
names
.iter()
@ -177,19 +255,19 @@ fn write_options(w: &mut impl Write, command: &Command) -> io::Result<()> {
.join(" ")
)?;
}
write!(w, "</code>")?;
write!(self.w, "</code>")?;
}
for s in arg.get_short_and_visible_aliases().unwrap_or_default() {
if !first {
write!(w, ", ")?;
write!(self.w, ", ")?;
} else {
first = false;
}
write!(w, "<code>")?;
write!(w, "-{}", s)?;
write!(self.w, "<code>")?;
write!(self.w, "-{}", s)?;
if let Some(names) = arg.get_value_names() {
write!(
w,
self.w,
" {}",
names
.iter()
@ -198,14 +276,21 @@ fn write_options(w: &mut impl Write, command: &Command) -> io::Result<()> {
.join(" ")
)?;
}
write!(w, "</code>")?;
write!(self.w, "</code>")?;
}
writeln!(w, "</dt>")?;
writeln!(self.w, "</dt>")?;
writeln!(
w,
self.w,
"<dd>\n\n{}\n\n</dd>",
arg.get_help().unwrap_or_default().replace('\n', "<br />")
)?;
}
writeln!(w, "</dl>\n")
writeln!(self.w, "</dl>\n")
}
}
fn get_zip_content(archive: &mut ZipArchive<impl Read + Seek>, name: &str) -> Option<String> {
let mut s = String::new();
archive.by_name(name).ok()?.read_to_string(&mut s).unwrap();
Some(s)
}

View file

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

View file

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

View file

@ -23,7 +23,6 @@ mod blocks;
use blocks::conv_block_unblock_helper;
use std::cmp;
use std::convert::TryInto;
use std::env;
use std::fs::{File, OpenOptions};
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();
// The following flags are only implemented in linux
for &flag in &[
for flag in [
"direct",
"directory",
"dsync",
@ -47,7 +47,7 @@ fn unimplemented_flags_should_error() {
let mut succeeded = Vec::new();
// 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![
String::from("dd"),
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"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"]
edition = "2018"
edition = "2021"
[lib]
path = "src/df.rs"

View file

@ -183,4 +183,31 @@ impl Column {
_ => 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;
use uucore::display::Quotable;
use uucore::error::{UError, UResult};
use uucore::format_usage;
use uucore::error::{UError, UResult, USimpleError};
use uucore::fsext::{read_fs_list, MountInfo};
use uucore::{format_usage, show};
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::columns::{Column, ColumnError};
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\
or all file systems by default.";
@ -61,7 +61,6 @@ static OUTPUT_FIELD_LIST: [&str; 12] = [
struct Options {
show_local_fs: bool,
show_all_fs: bool,
show_listed_fs: bool,
block_size: BlockSize,
/// Optional list of filesystem types to include in the output table.
@ -88,7 +87,6 @@ impl Default for Options {
Self {
show_local_fs: Default::default(),
show_all_fs: Default::default(),
show_listed_fs: Default::default(),
block_size: Default::default(),
include: Default::default(),
exclude: Default::default(),
@ -111,6 +109,8 @@ enum OptionsError {
/// An error getting the columns to display in the output table.
ColumnError(ColumnError),
FilesystemTypeBothSelectedAndExcluded(Vec<String>),
}
impl fmt::Display for OptionsError {
@ -126,6 +126,16 @@ impl fmt::Display for OptionsError {
"option --output: field {} used more than once",
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 {
/// Convert command-line arguments into [`Options`].
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 {
show_local_fs: matches.is_present(OPT_LOCAL),
show_all_fs: matches.is_present(OPT_ALL),
show_listed_fs: false,
block_size: block_size_from_matches(matches)
.map_err(|_| OptionsError::InvalidBlockSize)?,
include: matches.values_of_lossy(OPT_TYPE),
exclude: matches.values_of_lossy(OPT_EXCLUDE_TYPE),
include,
exclude,
show_total: matches.is_present(OPT_TOTAL),
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.
@ -155,7 +185,7 @@ fn is_included(mi: &MountInfo, opt: &Options) -> bool {
}
// 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;
}
@ -278,10 +308,17 @@ where
// Convert each path into a `Filesystem`, which contains
// both the mount information and usage information.
paths
.iter()
.filter_map(|p| Filesystem::from_path(&mounts, p))
.collect()
let mut result = vec![];
for path in paths {
match Filesystem::from_path(&mounts, path) {
Some(fs) => result.push(fs),
None => show!(USimpleError::new(
1,
format!("{}: No such file or directory", path.as_ref().display())
)),
}
}
result
}
#[derive(Debug)]
@ -309,6 +346,7 @@ impl fmt::Display for DfError {
#[uucore::main]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let matches = uu_app().get_matches_from(args);
#[cfg(windows)]
{
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)?;
// Get the list of filesystems to display in the output table.
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) => {
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 accumulator is computed in case we need to display the
// total counts in the last row of the table.
let mut total = Row::new("total");
// This can happen if paths are given as command-line arguments
// but none of the paths exist.
if filesystems.is_empty() {
return Ok(());
}
println!("{}", Header::new(&opt));
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));
}
println!("{}", Table::new(&opt, filesystems));
Ok(())
}
@ -367,6 +407,7 @@ pub fn uu_app<'a>() -> Command<'a> {
Arg::new(OPT_ALL)
.short('a')
.long("all")
.overrides_with(OPT_ALL)
.help("include dummy file systems"),
)
.arg(
@ -374,6 +415,7 @@ pub fn uu_app<'a>() -> Command<'a> {
.short('B')
.long("block-size")
.takes_value(true)
.overrides_with_all(&[OPT_KILO, OPT_BLOCKSIZE])
.help(
"scale sizes by SIZE before printing them; e.g.\
'-BM' prints sizes in units of 1,048,576 bytes",
@ -382,39 +424,47 @@ pub fn uu_app<'a>() -> Command<'a> {
.arg(
Arg::new(OPT_TOTAL)
.long("total")
.overrides_with(OPT_TOTAL)
.help("produce a grand total"),
)
.arg(
Arg::new(OPT_HUMAN_READABLE_BINARY)
.short('h')
.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)"),
)
.arg(
Arg::new(OPT_HUMAN_READABLE_DECIMAL)
.short('H')
.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"),
)
.arg(
Arg::new(OPT_INODES)
.short('i')
.long("inodes")
.overrides_with(OPT_INODES)
.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::new(OPT_LOCAL)
.short('l')
.long("local")
.overrides_with(OPT_LOCAL)
.help("limit listing to local file systems"),
)
.arg(
Arg::new(OPT_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)"),
)
.arg(
@ -438,12 +488,13 @@ pub fn uu_app<'a>() -> Command<'a> {
Arg::new(OPT_PORTABILITY)
.short('P')
.long("portability")
.overrides_with(OPT_PORTABILITY)
.help("use the POSIX output format"),
)
.arg(
Arg::new(OPT_SYNC)
.long("sync")
.conflicts_with(OPT_NO_SYNC)
.overrides_with_all(&[OPT_NO_SYNC, OPT_SYNC])
.help("invoke sync before getting usage info"),
)
.arg(
@ -459,6 +510,7 @@ pub fn uu_app<'a>() -> Command<'a> {
Arg::new(OPT_PRINT_TYPE)
.short('T')
.long("print-type")
.overrides_with(OPT_PRINT_TYPE)
.help("print file system type"),
)
.arg(
@ -612,7 +664,6 @@ mod tests {
fn test_dummy_included() {
let opt = Options {
show_all_fs: true,
show_listed_fs: true,
..Default::default()
};
let m = mount_info("ext4", "/mnt/foo", false, true);
@ -708,22 +759,6 @@ mod tests {
let m = mount_info("ext4", "/mnt/foo", false, false);
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 {

View file

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

View file

@ -11,11 +11,15 @@ extern crate uucore;
use chrono::prelude::DateTime;
use chrono::Local;
use clap::{crate_version, Arg, ArgMatches, Command};
use glob::Pattern;
use std::collections::HashSet;
use std::env;
use std::fs;
use std::fs::File;
#[cfg(not(windows))]
use std::fs::Metadata;
use std::io::BufRead;
use std::io::BufReader;
use std::io::{ErrorKind, Result};
use std::iter;
#[cfg(not(windows))]
@ -24,14 +28,13 @@ use std::os::unix::fs::MetadataExt;
use std::os::windows::fs::MetadataExt;
#[cfg(windows)]
use std::os::windows::io::AsRawHandle;
#[cfg(windows)]
use std::path::Path;
use std::path::PathBuf;
use std::str::FromStr;
use std::time::{Duration, UNIX_EPOCH};
use std::{error::Error, fmt::Display};
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::parse_size::{parse_size, ParseSizeError};
use uucore::InvalidEncodingHandling;
@ -68,6 +71,9 @@ mod options {
pub const ONE_FILE_SYSTEM: &str = "one-file-system";
pub const DEREFERENCE: &str = "dereference";
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";
}
@ -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).
Units are K, M, G, T, P, E, Z, Y (powers of 1024) or KB, MB,... (powers
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 = "\
{} [OPTION]... [FILE]...
@ -97,6 +109,7 @@ struct Options {
one_file_system: bool,
dereference: bool,
inodes: bool,
verbose: bool,
}
#[derive(PartialEq, Eq, Hash, Clone, Copy)]
@ -253,7 +266,7 @@ fn read_block_size(s: Option<&str>) -> u64 {
parse_size(s)
.unwrap_or_else(|e| crash!(1, "{}", format_error_message(&e, s, options::BLOCK_SIZE)))
} 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(v) = parse_size(&env_size) {
return v;
@ -287,6 +300,7 @@ fn du(
options: &Options,
depth: usize,
inodes: &mut HashSet<FileInfo>,
exclude: &[Pattern],
) -> Box<dyn DoubleEndedIterator<Item = Stat>> {
let mut stats = vec![];
let mut futures = vec![];
@ -301,14 +315,33 @@ fn du(
my_stat.path.quote(),
e
);
set_exit_code(1);
return Box::new(iter::once(my_stat));
}
};
for f in read {
'file_loop: for f in read {
match f {
Ok(entry) => match Stat::new(entry.path(), options) {
Ok(entry) => {
match Stat::new(entry.path(), options) {
Ok(this_stat) => {
// We have an exclude list
for pattern in exclude {
// Look at all patterns with both short and long paths
// if we have 'du foo' but search to exclude 'foo/bar'
// we need the full path
if pattern.matches(&this_stat.path.to_string_lossy())
|| pattern.matches(&entry.file_name().into_string().unwrap())
{
// if the directory is ignored, leave early
if options.verbose {
println!("{} ignored", &this_stat.path.quote());
}
// Go to the next file
continue 'file_loop;
}
}
if let Some(inode) = this_stat.inode {
if inodes.contains(&inode) {
continue;
@ -325,7 +358,7 @@ fn du(
}
}
}
futures.push(du(this_stat, options, depth + 1, inodes));
futures.push(du(this_stat, options, depth + 1, inodes, exclude));
} else {
my_stat.size += this_stat.size;
my_stat.blocks += this_stat.blocks;
@ -340,10 +373,15 @@ fn du(
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);
}
_ => show_error!("cannot access {}: {}", entry.path().quote(), error),
},
},
}
}
Err(error) => show_error!("{}", error),
}
}
@ -401,6 +439,7 @@ enum DuError {
SummarizeDepthConflict(String),
InvalidTimeStyleArg(String),
InvalidTimeArg(String),
InvalidGlob(String),
}
impl Display for DuError {
@ -431,6 +470,7 @@ Try '{} --help' for more information.",
'birth' and 'creation' arguments are not supported on this platform.",
s.quote()
),
DuError::InvalidGlob(s) => write!(f, "Invalid exclude syntax: {}", s),
}
}
}
@ -443,11 +483,75 @@ impl UError for DuError {
Self::InvalidMaxDepthArg(_)
| Self::SummarizeDepthConflict(_)
| 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]
#[allow(clippy::cognitive_complexity)]
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),
dereference: matches.is_present(options::DEREFERENCE),
inodes: matches.is_present(options::INODES),
verbose: matches.is_present(options::VERBOSE),
};
let files = match matches.value_of(options::FILE) {
@ -524,8 +629,25 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
"\n"
};
let excludes = get_glob_ignore(&matches)?;
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);
match Stat::new(path, &options) {
Ok(stat) => {
@ -533,7 +655,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
if let Some(inode) = stat.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 = len.unwrap();
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, \
or entries greater than SIZE if negative")
)
// .arg(
// Arg::new("")
// .short('x')
// .long("exclude-from")
// .value_name("FILE")
// .help("exclude files that match any pattern in FILE")
// )
// .arg(
// Arg::new("exclude")
// .long("exclude")
// .value_name("PATTERN")
// .help("exclude files that match PATTERN")
// )
.arg(
Arg::new(options::VERBOSE)
.short('v')
.long("verbose")
.help("verbose mode (option not present in GNU/Coreutils)")
)
.arg(
Arg::new(options::EXCLUDE)
.long(options::EXCLUDE)
.value_name("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::new(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"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"]
edition = "2018"
edition = "2021"
[lib]
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"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"]
edition = "2018"
edition = "2021"
[lib]
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"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"]
edition = "2018"
edition = "2021"
[lib]
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"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"]
edition = "2018"
edition = "2021"
[lib]
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/"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"]
edition = "2018"
edition = "2021"
[build-dependencies]
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;
}
let a = m.from_u64(_a);
let a = m.to_mod(_a);
// x = a^r mod n
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 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 add(&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 {
self.from_u64(1)
self.to_mod(1)
}
fn minus_one(&self) -> Self::ModInt {
self.from_u64(self.modulus() - 1)
self.to_mod(self.modulus() - 1)
}
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()
}
fn from_u64(&self, x: u64) -> Self::ModInt {
fn to_mod(&self, x: u64) -> Self::ModInt {
// TODO: optimise!
debug_assert!(x < self.n.as_u64());
let r = T::from_double_width(
@ -189,9 +189,9 @@ mod tests {
let n = 2 * n + 1;
let m = Montgomery::<A>::new(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 {
let m_y = m.from_u64(y);
let m_y = m.to_mod(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)));
}
@ -205,9 +205,9 @@ mod tests {
let n = 2 * n + 1;
let m = Montgomery::<A>::new(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 {
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)));
}
}
@ -220,7 +220,7 @@ mod tests {
let n = 2 * n + 1;
let m = Montgomery::<A>::new(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_));
}
}

View file

@ -18,7 +18,7 @@ pub(crate) fn find_divisor<A: Arithmetic>(n: A) -> u64 {
let mut rand = {
let range = Uniform::new(1, n.modulus());
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);

View file

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

View file

@ -7,7 +7,7 @@
// * For the full copyright and license information, please view the LICENSE
// * 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]
extern crate clap;
@ -46,6 +46,7 @@ struct Options {
binary: bool,
check: bool,
tag: bool,
nonames: bool,
status: bool,
quiet: bool,
strict: bool,
@ -316,6 +317,7 @@ pub fn uumain(mut args: impl uucore::Args) -> UResult<()> {
};
let check = matches.is_present("check");
let tag = matches.is_present("tag");
let nonames = matches.is_present("no-names");
let status = matches.is_present("status");
let quiet = matches.is_present("quiet") || status;
let strict = matches.is_present("strict");
@ -328,6 +330,7 @@ pub fn uumain(mut args: impl uucore::Args) -> UResult<()> {
binary,
check,
tag,
nonames,
status,
quiet,
strict,
@ -370,6 +373,11 @@ pub fn uu_app_common<'a>() -> Command<'a> {
.long("tag")
.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::new("text")
.short('t')
@ -602,6 +610,8 @@ where
.map_err_context(|| "failed to read input".to_string())?;
if options.tag {
println!("{} ({}) = {}", options.algoname, filename.display(), sum);
} else if options.nonames {
println!("{}", sum);
} else {
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"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"]
edition = "2018"
edition = "2021"
[lib]
path = "src/head.rs"

View file

@ -6,7 +6,6 @@
// spell-checker:ignore (vars) zlines BUFWRITER seekable
use clap::{crate_version, Arg, ArgMatches, Command};
use std::convert::{TryFrom, TryInto};
use std::ffi::OsString;
use std::io::{self, BufWriter, ErrorKind, Read, Seek, SeekFrom, Write};
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"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"]
edition = "2018"
edition = "2021"
[lib]
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"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"]
edition = "2018"
edition = "2021"
[lib]
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"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"]
edition = "2018"
edition = "2021"
[lib]
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"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"]
edition = "2018"
edition = "2021"
[lib]
path = "src/install.rs"

View file

@ -464,6 +464,19 @@ fn standard(mut paths: Vec<String>, b: &Behavior) -> UResult<()> {
} else {
if let Some(parent) = target.parent() {
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) {
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() {
Ok(o) => {
if !o.status.success() {
// Follow GNU's behavior: if strip fails, removes the target
let _ = fs::remove_file(to);
return Err(InstallError::StripProgramFailed(
String::from_utf8(o.stderr).unwrap_or_default(),
)
.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"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"]
edition = "2018"
edition = "2021"
[lib]
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"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"]
edition = "2018"
edition = "2021"
[lib]
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"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"]
edition = "2018"
edition = "2021"
[lib]
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"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"]
edition = "2018"
edition = "2021"
[lib]
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"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"]
edition = "2018"
edition = "2021"
[lib]
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"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"]
edition = "2018"
edition = "2021"
[lib]
path = "src/ls.rs"
@ -22,7 +22,7 @@ number_prefix = "0.4"
term_grid = "0.1.5"
termsize = "0.1.6"
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"] }
once_cell = "1.10.0"
atty = "0.2"

View file

@ -13,7 +13,8 @@ extern crate uucore;
#[macro_use]
extern crate lazy_static;
mod quoting_style;
// dir and vdir also need access to the quoting_style module
pub mod quoting_style;
use clap::{crate_version, Arg, Command};
use glob::Pattern;
@ -242,7 +243,7 @@ impl Display for LsError {
}
#[derive(PartialEq, Eq)]
enum Format {
pub enum Format {
Columns,
Long,
OneLine,
@ -304,8 +305,9 @@ enum IndicatorStyle {
Classify,
}
struct Config {
format: Format,
pub struct Config {
// Dir and vdir needs access to this field
pub format: Format,
files: Files,
sort: Sort,
recursive: bool,
@ -322,7 +324,8 @@ struct Config {
alloc_size: bool,
block_size: Option<u64>,
width: u16,
quoting_style: QuotingStyle,
// Dir and vdir needs access to this field
pub quoting_style: QuotingStyle,
indicator_style: IndicatorStyle,
time_style: TimeStyle,
context: bool,
@ -355,7 +358,7 @@ struct PaddingCollection {
impl Config {
#[allow(clippy::cognitive_complexity)]
fn from(options: &clap::ArgMatches) -> UResult<Self> {
pub fn from(options: &clap::ArgMatches) -> UResult<Self> {
let context = options.is_present(options::CONTEXT);
let (mut format, opt) = if let Some(format_) = options.value_of(options::FORMAT) {
(
@ -801,11 +804,7 @@ pub fn uu_app<'a>() -> Command<'a> {
Command::new(uucore::util_name())
.version(crate_version!())
.override_usage(format_usage(USAGE))
.about(
"By default, ls will list the files and contents of any directories on \
the command line, expect that it will ignore files and directories \
whose names start with '.'.",
)
.about("List directory contents. Ignore files and directories starting with a '.' by default")
.infer_long_args(true)
.arg(
Arg::new(options::HELP)
@ -1216,7 +1215,7 @@ pub fn uu_app<'a>() -> Command<'a> {
.multiple_occurrences(true)
.help(
"In a directory, do not ignore all file names that start with '.', \
only ignore '.' and '..'.",
only ignore '.' and '..'.",
),
)
.arg(
@ -1543,7 +1542,7 @@ impl PathData {
}
}
fn list(locs: Vec<&Path>, config: &Config) -> UResult<()> {
pub fn list(locs: Vec<&Path>, config: &Config) -> UResult<()> {
let mut files = Vec::<PathData>::new();
let mut dirs = Vec::<PathData>::new();
let mut out = BufWriter::new(stdout());

View file

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

View file

@ -9,7 +9,7 @@ homepage = "https://github.com/uutils/coreutils"
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/mkdir"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"]
edition = "2018"
edition = "2021"
[lib]
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)
}
None => Ok(DEFAULT_PERM),
None => {
// If no mode argument is specified return the mode derived from umask
Ok(!mode::get_umask() & 0o0777)
}
}
}
#[cfg(windows)]
fn strip_minus_from_mode(_args: &mut Vec<String>) -> bool {
fn strip_minus_from_mode(_args: &mut [String]) -> bool {
false
}
@ -115,7 +118,7 @@ pub fn uu_app<'a>() -> Command<'a> {
.short('m')
.long(options::MODE)
.help("set file mode (not implemented on windows)")
.default_value("755"),
.takes_value(true),
)
.arg(
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"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"]
edition = "2018"
edition = "2021"
[lib]
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"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"]
edition = "2018"
edition = "2021"
[lib]
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"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"]
edition = "2018"
edition = "2021"
[lib]
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"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"]
edition = "2018"
edition = "2021"
[lib]
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"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"]
edition = "2018"
edition = "2021"
[lib]
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)
}
} 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(
source.quote().to_string(),
target.quote().to_string(),
@ -275,7 +285,23 @@ fn exec(files: &[OsString], b: &Behavior) -> UResult<()> {
));
}
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"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"]
edition = "2018"
edition = "2021"
[lib]
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"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"]
edition = "2018"
edition = "2021"
[lib]
path = "src/nl.rs"
[dependencies]
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" }
[[bin]]

View file

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

View file

@ -29,7 +29,6 @@ mod prn_float;
mod prn_int;
use std::cmp;
use std::convert::TryFrom;
use crate::byteorder_io::*;
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"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"]
edition = "2018"
edition = "2021"
[lib]
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"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"]
edition = "2018"
edition = "2021"
[lib]
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"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"]
edition = "2018"
edition = "2021"
[lib]
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"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"]
edition = "2018"
edition = "2021"
[lib]
path = "src/pr.rs"
@ -20,7 +20,7 @@ uucore = { version=">=0.0.7", package="uucore", path="../../uucore", features=["
chrono = "0.4.19"
quick-error = "2.0.1"
itertools = "0.10.0"
regex = "1.0"
regex = "1.5"
[[bin]]
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"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"]
edition = "2018"
edition = "2021"
[lib]
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"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"]
edition = "2018"
edition = "2021"
[lib]
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"
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"]
edition = "2018"
edition = "2021"
[lib]
path = "src/ptx.rs"
[dependencies]
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" }
[[bin]]

View file

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

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