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

Merge branch 'main' into fmt_implement_default_for_fmtoptions

This commit is contained in:
Terts Diepraam 2023-08-11 12:38:42 +02:00 committed by GitHub
commit a3b80e1bef
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
234 changed files with 4854 additions and 2994 deletions

1
.github/FUNDING.yml vendored Normal file
View file

@ -0,0 +1 @@
github: uutils

View file

@ -220,7 +220,7 @@ jobs:
fault_type="${{ steps.vars.outputs.FAULT_TYPE }}"
fault_prefix=$(echo "$fault_type" | tr '[:lower:]' '[:upper:]')
# * convert any warnings to GHA UI annotations; ref: <https://help.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-a-warning-message>
S=$(cargo clippy --all-targets ${{ matrix.job.cargo-options }} ${{ steps.vars.outputs.CARGO_UTILITY_LIST_OPTIONS }} -- -W clippy::manual_string_new -D warnings 2>&1) && printf "%s\n" "$S" || { printf "%s\n" "$S" ; printf "%s" "$S" | sed -E -n -e '/^error:/{' -e "N; s/^error:[[:space:]]+(.*)\\n[[:space:]]+-->[[:space:]]+(.*):([0-9]+):([0-9]+).*$/::${fault_type} file=\2,line=\3,col=\4::${fault_prefix}: \`cargo clippy\`: \1 (file:'\2', line:\3)/p;" -e '}' ; fault=true ; }
S=$(cargo clippy --all-targets ${{ matrix.job.cargo-options }} ${{ steps.vars.outputs.CARGO_UTILITY_LIST_OPTIONS }} -- -W clippy::default_trait_access -W clippy::manual_string_new -D warnings 2>&1) && printf "%s\n" "$S" || { printf "%s\n" "$S" ; printf "%s" "$S" | sed -E -n -e '/^error:/{' -e "N; s/^error:[[:space:]]+(.*)\\n[[:space:]]+-->[[:space:]]+(.*):([0-9]+):([0-9]+).*$/::${fault_type} file=\2,line=\3,col=\4::${fault_prefix}: \`cargo clippy\`: \1 (file:'\2', line:\3)/p;" -e '}' ; fault=true ; }
if [ -n "${{ steps.vars.outputs.FAIL_ON_FAULT }}" ] && [ -n "$fault" ]; then exit 1 ; fi
style_spellcheck:
@ -319,7 +319,7 @@ jobs:
shell: bash
run: |
RUSTDOCFLAGS="-Dwarnings" cargo doc ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} --no-deps --workspace --document-private-items
- uses: DavidAnson/markdownlint-cli2-action@v10
- uses: DavidAnson/markdownlint-cli2-action@v11
with:
command: fix
globs: |
@ -452,8 +452,20 @@ jobs:
test -f /tmp/usr/local/share/man/man1/whoami.1
# Check that the completion is present
test -f /tmp/usr/local/share/zsh/site-functions/_install
test -f /tmp/usr/local/share/bash-completion/completions/head
test -f /tmp/usr/local/share/fish/vendor_completions.d/cat.fish
env:
RUST_BACKTRACE: "1"
- name: "`make uninstall`"
shell: bash
run: |
DESTDIR=/tmp/ make uninstall
# Check that the manpage is not present
! test -f /tmp/usr/local/share/man/man1/whoami.1
# Check that the completion is not present
! test -f /tmp/usr/local/share/zsh/site-functions/_install
! test -f /tmp/usr/local/share/bash-completion/completions/head
! test -f /tmp/usr/local/share/fish/vendor_completions.d/cat.fish
build_rust_stable:
name: Build/stable
@ -1048,6 +1060,15 @@ jobs:
name: toybox-result.json
path: ${{ steps.vars.outputs.TEST_SUMMARY_FILE }}
toml_format:
runs-on: ubuntu-latest
steps:
- name: Clone repository
uses: actions/checkout@v3
- name: Check
run: npx --yes @taplo/cli fmt --check
coverage:
name: Code Coverage
runs-on: ${{ matrix.job.os }}

74
.github/workflows/CheckScripts.yml vendored Normal file
View file

@ -0,0 +1,74 @@
name: CheckScripts
# spell-checker:ignore ludeeus mfinelli
env:
SCRIPT_DIR: 'util'
on:
push:
branches:
- main
paths:
- 'util/**/*.sh'
pull_request:
branches:
- main
paths:
- 'util/**/*.sh'
# End the current execution if there is a new changeset in the PR.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
jobs:
shell_check:
name: ShellScript/Check
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v3
- name: Run ShellCheck
uses: ludeeus/action-shellcheck@master
env:
SHELLCHECK_OPTS: -s bash
with:
severity: warning
scandir: ${{ env.SCRIPT_DIR }}
format: tty
shell_fmt:
name: ShellScript/Format
# no need to run in pr events
# shfmt will be performed on main branch when the PR is merged
if: github.event_name != 'pull_request'
runs-on: ubuntu-latest
needs: [ shell_check ]
permissions:
contents: write
pull-requests: write
steps:
- uses: actions/checkout@v3
- name: Setup shfmt
uses: mfinelli/setup-shfmt@v2
- name: Run shfmt
shell: bash
run: |
# show differs first for every files that need to be formatted
# fmt options: bash syntax, 4 spaces indent, indent for switch-case
echo "## show the differences between formatted and original scripts..."
find ${{ env.SCRIPT_DIR }} -name "*.sh" -print0 | xargs -0 shfmt -ln=bash -i 4 -ci -d || true
# perform a shell format
echo "## perform a shell format..."
# ignore the error code because `-d` will always return false when the file has difference
find ${{ env.SCRIPT_DIR }} -name "*.sh" -print0 | xargs -0 shfmt -ln=bash -i 4 -ci -w
- name: Commit any changes
uses: EndBug/add-and-commit@v9
with:
default_author: github_actions
message: "style: auto format by CI (shfmt)"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View file

@ -31,12 +31,12 @@ jobs:
id: vars
shell: bash
run: |
## VARs setup
outputs() { step_id="${{ github.action }}"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo "${var}=${!var}" >> $GITHUB_OUTPUT; done; }
# surface MSRV from CICD workflow
RUST_MIN_SRV=$(grep -P "^\s+RUST_MIN_SRV:" .github/workflows/CICD.yml | grep -Po "(?<=\x22)\d+[.]\d+(?:[.]\d+)?(?=\x22)" )
outputs RUST_MIN_SRV
- uses: dtolnay/rust-toolchain@${{ steps.vars.outputs.RUST_MIN_SRV }}
echo "RUST_MIN_SRV=${RUST_MIN_SRV}" >> $GITHUB_OUTPUT
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ steps.vars.outputs.RUST_MIN_SRV }}
- uses: Swatinem/rust-cache@v2
- name: Ensure updated 'Cargo.lock'
shell: bash
@ -67,7 +67,7 @@ jobs:
- name: Commit any changes (to '${{ env.BRANCH_TARGET }}')
uses: EndBug/add-and-commit@v9
with:
branch: ${{ env.BRANCH_TARGET }}
new_branch: ${{ env.BRANCH_TARGET }}
default_author: github_actions
message: "maint ~ refresh 'Cargo.lock'"
add: Cargo.lock
@ -90,13 +90,11 @@ jobs:
id: vars
shell: bash
run: |
## VARs setup
outputs() { step_id="${{ github.action }}"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo "${var}=${!var}" >> $GITHUB_OUTPUT; done; }
# target-specific options
# * CARGO_FEATURES_OPTION
CARGO_FEATURES_OPTION='' ;
if [ -n "${{ matrix.job.features }}" ]; then CARGO_FEATURES_OPTION='--features "${{ matrix.job.features }}"' ; fi
outputs CARGO_FEATURES_OPTION
echo "CARGO_FEATURES_OPTION=${CARGO_FEATURES_OPTION}" >> $GITHUB_OUTPUT
- uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
@ -114,7 +112,7 @@ jobs:
- name: Commit any changes (to '${{ env.BRANCH_TARGET }}')
uses: EndBug/add-and-commit@v9
with:
branch: ${{ env.BRANCH_TARGET }}
new_branch: ${{ env.BRANCH_TARGET }}
default_author: github_actions
message: "maint ~ rustfmt (`cargo fmt`)"
env:

View file

@ -205,7 +205,7 @@ jobs:
path_UUTILS='${{ steps.vars.outputs.path_UUTILS }}'
# https://github.com/uutils/coreutils/issues/4294
# https://github.com/uutils/coreutils/issues/4295
IGNORE_INTERMITTENT='${path_UUTILS}/.github/workflows/ignore-intermittent.txt'
IGNORE_INTERMITTENT="${path_UUTILS}/.github/workflows/ignore-intermittent.txt"
mkdir -p ${{ steps.vars.outputs.path_reference }}

View file

@ -35,7 +35,7 @@ jobs:
- name: Run sccache-cache
uses: mozilla-actions/sccache-action@v0.0.3
- name: Prepare, build and test
uses: vmactions/freebsd-vm@v0.3.0
uses: vmactions/freebsd-vm@v0.3.1
with:
usesh: true
# We need jq to run show-utils.sh and bash to use inline shell string replacement
@ -125,7 +125,7 @@ jobs:
- name: Run sccache-cache
uses: mozilla-actions/sccache-action@v0.0.3
- name: Prepare, build and test
uses: vmactions/freebsd-vm@v0.3.0
uses: vmactions/freebsd-vm@v0.3.1
with:
usesh: true
# sync: sshfs

1
.gitignore vendored
View file

@ -5,6 +5,7 @@ target/
/busybox/
/.vscode/
/.vs/
/public/
*~
.*.swp
.*.swo

1
.vscode/cSpell.json vendored
View file

@ -19,6 +19,7 @@
// files to ignore (globs supported)
"ignorePaths": [
"Cargo.lock",
"oranda.json",
"target/**",
"tests/**/fixtures/**",
"src/uu/dd/test-resources/**",

View file

@ -356,6 +356,7 @@ The Coreutils have different implementations, with different levels of completio
* [V lang](https://github.com/vlang/coreutils)
* [SerenityOS](https://github.com/SerenityOS/serenity/tree/master/Userland/Utilities)
* [Initial Unix](https://github.com/dspinellis/unix-history-repo)
* [Perl Power Tools](https://metacpan.org/pod/PerlPowerTools)
However, when reimplementing the tools/options in Rust, don't read their source codes
when they are using reciprocal licenses (ex: GNU GPL, GNU LGPL, etc).

940
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,11 +1,11 @@
# coreutils (uutils)
# * see the repository LICENSE, README, and CONTRIBUTING files for more information
# spell-checker:ignore (libs) libselinux gethostid procfs bigdecimal kqueue fundu mangen humantime
# spell-checker:ignore (libs) bigdecimal datetime fundu gethostid kqueue libselinux mangen memmap procfs uuhelp
[package]
name = "coreutils"
version = "0.0.18"
version = "0.0.20"
authors = ["uutils developers"]
license = "MIT"
description = "coreutils ~ GNU coreutils (updated); implemented as universal (cross-platform) utils, written in Rust"
@ -22,16 +22,16 @@ edition = "2021"
build = "build.rs"
[features]
default = [ "feat_common_core" ]
default = ["feat_common_core"]
## OS feature shortcodes
macos = [ "feat_os_macos" ]
unix = [ "feat_os_unix" ]
windows = [ "feat_os_windows" ]
macos = ["feat_os_macos"]
unix = ["feat_os_unix"]
windows = ["feat_os_windows"]
## project-specific feature shortcodes
nightly = []
test_unimplemented = []
# * only build `uudoc` when `--feature uudoc` is activated
uudoc = ["zip", "dep:help_parser"]
uudoc = ["zip", "dep:uuhelp_parser"]
## features
# "feat_acl" == enable support for ACLs (access control lists; by using`--features feat_acl`)
# NOTE:
@ -42,239 +42,237 @@ feat_acl = ["cp/feat_acl"]
# NOTE:
# * The selinux(-sys) crate requires `libselinux` headers and shared library to be accessible in the C toolchain at compile time.
# * Running a uutils compiled with `feat_selinux` requires an SELinux enabled Kernel at run time.
feat_selinux = ["cp/selinux", "id/selinux", "ls/selinux", "selinux", "feat_require_selinux"]
feat_selinux = [
"cp/selinux",
"id/selinux",
"ls/selinux",
"selinux",
"feat_require_selinux",
]
##
## feature sets
## (common/core and Tier1) feature sets
# "feat_common_core" == baseline core set of utilities which can be built/run on most targets
feat_common_core = [
"base32",
"base64",
"basename",
"basenc",
"cat",
"cksum",
"comm",
"cp",
"csplit",
"cut",
"date",
"df",
"dir",
"dircolors",
"dirname",
"dd",
"du",
"echo",
"env",
"expand",
"expr",
"factor",
"false",
"fmt",
"fold",
"hashsum",
"head",
"join",
"link",
"ln",
"ls",
"mkdir",
"mktemp",
"more",
"mv",
"nl",
"numfmt",
"od",
"paste",
"pr",
"printenv",
"printf",
"ptx",
"pwd",
"readlink",
"realpath",
"relpath",
"rm",
"rmdir",
"seq",
"shred",
"shuf",
"sleep",
"sort",
"split",
"sum",
"tac",
"tail",
"tee",
"test",
"tr",
"true",
"truncate",
"tsort",
"touch",
"unexpand",
"uniq",
"unlink",
"vdir",
"wc",
"yes",
"base32",
"base64",
"basename",
"basenc",
"cat",
"cksum",
"comm",
"cp",
"csplit",
"cut",
"date",
"df",
"dir",
"dircolors",
"dirname",
"dd",
"du",
"echo",
"env",
"expand",
"expr",
"factor",
"false",
"fmt",
"fold",
"hashsum",
"head",
"join",
"link",
"ln",
"ls",
"mkdir",
"mktemp",
"more",
"mv",
"nl",
"numfmt",
"od",
"paste",
"pr",
"printenv",
"printf",
"ptx",
"pwd",
"readlink",
"realpath",
"relpath",
"rm",
"rmdir",
"seq",
"shred",
"shuf",
"sleep",
"sort",
"split",
"sum",
"tac",
"tail",
"tee",
"test",
"tr",
"true",
"truncate",
"tsort",
"touch",
"unexpand",
"uniq",
"unlink",
"vdir",
"wc",
"yes",
]
# "feat_Tier1" == expanded set of utilities which can be built/run on the usual rust "Tier 1" target platforms (ref: <https://forge.rust-lang.org/release/platform-support.html>)
feat_Tier1 = [
"feat_common_core",
#
"arch",
"hostname",
"nproc",
"sync",
"touch",
"uname",
"whoami",
"feat_common_core",
#
"arch",
"hostname",
"nproc",
"sync",
"touch",
"uname",
"whoami",
]
## (primary platforms) feature sets
# "feat_os_macos" == set of utilities which can be built/run on the MacOS platform
feat_os_macos = [
"feat_os_unix", ## == a modern/usual *nix platform
#
"feat_require_unix_hostid",
"feat_os_unix", ## == a modern/usual *nix platform
#
"feat_require_unix_hostid",
]
# "feat_os_unix" == set of utilities which can be built/run on modern/usual *nix platforms
feat_os_unix = [
"feat_Tier1",
#
"feat_require_crate_cpp",
"feat_require_unix",
"feat_require_unix_utmpx",
"feat_Tier1",
#
"feat_require_crate_cpp",
"feat_require_unix",
"feat_require_unix_utmpx",
]
# "feat_os_windows" == set of utilities which can be built/run on modern/usual windows platforms
feat_os_windows = [
"feat_Tier1", ## == "feat_os_windows_legacy" + "hostname"
"feat_Tier1", ## == "feat_os_windows_legacy" + "hostname"
]
## (secondary platforms) feature sets
# "feat_os_unix_gnueabihf" == set of utilities which can be built/run on the "arm-unknown-linux-gnueabihf" target (ARMv6 Linux [hardfloat])
feat_os_unix_gnueabihf = [
"feat_Tier1",
#
"feat_require_unix",
"feat_require_unix_hostid",
"feat_require_unix_utmpx",
"feat_Tier1",
#
"feat_require_unix",
"feat_require_unix_hostid",
"feat_require_unix_utmpx",
]
# "feat_os_unix_musl" == set of utilities which can be built/run on targets binding to the "musl" library (ref: <https://musl.libc.org/about.html>)
feat_os_unix_musl = [
"feat_Tier1",
#
"feat_require_unix",
"feat_require_unix_hostid",
"feat_Tier1",
#
"feat_require_unix",
"feat_require_unix_hostid",
]
feat_os_unix_android = [
"feat_Tier1",
#
"feat_require_unix",
"feat_Tier1",
#
"feat_require_unix",
]
## feature sets with requirements (restricting cross-platform availability)
#
# ** NOTE: these `feat_require_...` sets should be minimized as much as possible to encourage cross-platform availability of utilities
#
# "feat_require_crate_cpp" == set of utilities requiring the `cpp` crate (which fail to compile on several platforms; as of 2020-04-23)
feat_require_crate_cpp = [
"stdbuf",
]
feat_require_crate_cpp = ["stdbuf"]
# "feat_require_unix" == set of utilities requiring support which is only available on unix platforms (as of 2020-04-23)
feat_require_unix = [
"chgrp",
"chmod",
"chown",
"chroot",
"groups",
"id",
"install",
"kill",
"logname",
"mkfifo",
"mknod",
"nice",
"nohup",
"pathchk",
"stat",
"stty",
"timeout",
"tty",
"chgrp",
"chmod",
"chown",
"chroot",
"groups",
"id",
"install",
"kill",
"logname",
"mkfifo",
"mknod",
"nice",
"nohup",
"pathchk",
"stat",
"stty",
"timeout",
"tty",
]
# "feat_require_unix_utmpx" == set of utilities requiring unix utmp/utmpx support
# * ref: <https://wiki.musl-libc.org/faq.html#Q:-Why-is-the-utmp/wtmp-functionality-only-implemented-as-stubs?>
feat_require_unix_utmpx = [
"pinky",
"uptime",
"users",
"who",
]
feat_require_unix_utmpx = ["pinky", "uptime", "users", "who"]
# "feat_require_unix_hostid" == set of utilities requiring gethostid in libc (only some unixes provide)
feat_require_unix_hostid = [
"hostid",
]
feat_require_unix_hostid = ["hostid"]
# "feat_require_selinux" == set of utilities depending on SELinux.
feat_require_selinux = [
"chcon",
"runcon",
]
feat_require_selinux = ["chcon", "runcon"]
## (alternate/newer/smaller platforms) feature sets
# "feat_os_unix_fuchsia" == set of utilities which can be built/run on the "Fuchsia" OS (refs: <https://fuchsia.dev>; <https://en.wikipedia.org/wiki/Google_Fuchsia>)
feat_os_unix_fuchsia = [
"feat_common_core",
#
"feat_require_crate_cpp",
#
"chgrp",
"chmod",
"chown",
"du",
"groups",
"hostid",
"install",
"logname",
"mkfifo",
"mknod",
"nice",
"pathchk",
"tty",
"uname",
"unlink",
"feat_common_core",
#
"feat_require_crate_cpp",
#
"chgrp",
"chmod",
"chown",
"du",
"groups",
"hostid",
"install",
"logname",
"mkfifo",
"mknod",
"nice",
"pathchk",
"tty",
"uname",
"unlink",
]
# "feat_os_unix_redox" == set of utilities which can be built/run on "Redox OS" (refs: <https://www.redox-os.org>; <https://en.wikipedia.org/wiki/Redox_(operating_system)>)
feat_os_unix_redox = [
"feat_common_core",
#
"chmod",
"uname",
"feat_common_core",
#
"chmod",
"uname",
]
# "feat_os_windows_legacy" == slightly restricted set of utilities which can be built/run on early windows platforms (eg, "WinXP")
feat_os_windows_legacy = [
"feat_common_core",
#
"arch",
"nproc",
"sync",
"touch",
"whoami",
"feat_common_core",
#
"arch",
"nproc",
"sync",
"touch",
"whoami",
]
##
# * bypass/override ~ translate 'test' feature name to avoid dependency collision with rust core 'test' crate (o/w surfaces as compiler errors during testing)
test = [ "uu_test" ]
test = ["uu_test"]
[workspace.dependencies]
bigdecimal = "0.3"
bigdecimal = "0.4"
binary-heap-plus = "0.5.0"
bstr = "1.5"
bstr = "1.6"
bytecount = "0.6.3"
byteorder = "1.4.3"
chrono = { version="^0.4.26", default-features=false, features=["std", "alloc", "clock"]}
chrono = { version = "^0.4.26", default-features = false, features = [
"std",
"alloc",
"clock",
] }
clap = { version = "4.3", features = ["wrap_help", "cargo"] }
clap_complete = "4.3"
clap_mangen = "0.2"
compare = "0.1.0"
coz = { version = "0.1.3" }
crossterm = ">=0.26.1"
crossterm = ">=0.27.0"
ctrlc = { version = "3.4", features = ["termination"] }
exacl = "0.10.0"
file_diff = "1.0.0"
@ -282,189 +280,192 @@ filetime = "0.2"
fnv = "1.0.7"
fs_extra = "1.3.0"
fts-sys = "0.2"
fundu = "0.5.1"
fundu = "2.0.0"
gcd = "2.3"
glob = "0.3.1"
half = "2.2"
humantime_to_duration = "0.2.1"
indicatif = "0.17"
is-terminal = "0.4.7"
itertools = "0.10.5"
libc = "0.2.144"
lscolors = { version = "0.14.0", default-features=false, features = ["nu-ansi-term"] }
itertools = "0.11.0"
libc = "0.2.147"
lscolors = { version = "0.15.0", default-features = false, features = [
"nu-ansi-term",
] }
memchr = "2"
nix = { version="0.26", default-features=false }
memmap2 = "0.7"
nix = { version = "0.26", default-features = false }
nom = "7.1.3"
notify = { version = "=6.0.0", features=["macos_kqueue"]}
notify = { version = "=6.0.1", features = ["macos_kqueue"] }
num-bigint = "0.4.3"
num-traits = "0.2.15"
num-traits = "0.2.16"
number_prefix = "0.4"
once_cell = "1.17.2"
once_cell = "1.18.0"
onig = { version = "~6.4", default-features = false }
ouroboros = "0.15.6"
phf = "0.11.1"
phf_codegen = "0.11.1"
platform-info = "2.0.1"
parse_datetime = "0.4.0"
phf = "0.11.2"
phf_codegen = "0.11.2"
platform-info = "2.0.2"
quick-error = "2.0.1"
rand = { version = "0.8", features = ["small_rng"] }
rand_core = "0.6"
rayon = "1.7"
redox_syscall = "0.3"
regex = "1.8.3"
rstest = "0.17.0"
rust-ini = "0.18.0"
regex = "1.9.3"
rstest = "0.18.1"
rust-ini = "0.19.0"
same-file = "1.0.6"
self_cell = "1.0.1"
selinux = "0.4"
signal-hook = "0.3.15"
smallvec = { version = "1.10", features = ["union"] }
tempfile = "3.5.0"
signal-hook = "0.3.17"
smallvec = { version = "1.11", features = ["union"] }
tempfile = "3.6.0"
term_grid = "0.1.5"
terminal_size = "0.2.6"
textwrap = { version="0.16.0", features=["terminal_size"] }
textwrap = { version = "0.16.0", features = ["terminal_size"] }
thiserror = "1.0"
time = { version="0.3" }
time = { version = "0.3" }
unicode-segmentation = "1.10.1"
unicode-width = "0.1.10"
utf-8 = "0.7.6"
walkdir = "2.3"
winapi-util = "0.1.5"
windows-sys = { version="0.48.0", default-features=false }
xattr = "1.0.0"
zip = { version = "0.6.6", default_features=false, features=["deflate"] }
windows-sys = { version = "0.48.0", default-features = false }
xattr = "1.0.1"
zip = { version = "0.6.6", default_features = false, features = ["deflate"] }
hex = "0.4.3"
md-5 = "0.10.5"
sha1 = "0.10.5"
sha2 = "0.10.6"
sha2 = "0.10.7"
sha3 = "0.10.8"
blake2b_simd = "1.0.1"
blake3 = "1.3.3"
blake3 = "1.4.0"
sm3 = "0.4.2"
digest = "0.10.7"
uucore = { version=">=0.0.18", package="uucore", path="src/uucore" }
uucore_procs = { version=">=0.0.18", package="uucore_procs", path="src/uucore_procs" }
uu_ls = { version=">=0.0.18", path="src/uu/ls" }
uu_base32 = { version=">=0.0.18", path="src/uu/base32"}
uucore = { version = ">=0.0.19", package = "uucore", path = "src/uucore" }
uucore_procs = { version = ">=0.0.19", package = "uucore_procs", path = "src/uucore_procs" }
uu_ls = { version = ">=0.0.18", path = "src/uu/ls" }
uu_base32 = { version = ">=0.0.18", path = "src/uu/base32" }
[dependencies]
clap = { workspace=true }
once_cell = { workspace=true }
uucore = { workspace=true }
clap_complete = { workspace=true }
clap_mangen = { workspace=true }
phf = { workspace=true }
selinux = { workspace=true, optional = true }
textwrap = { workspace=true }
zip = { workspace=true, optional = true }
clap = { workspace = true }
once_cell = { workspace = true }
uucore = { workspace = true }
clap_complete = { workspace = true }
clap_mangen = { workspace = true }
phf = { workspace = true }
selinux = { workspace = true, optional = true }
textwrap = { workspace = true }
zip = { workspace = true, optional = true }
help_parser = { path="src/help_parser", optional = true }
uuhelp_parser = { optional = true, version = ">=0.0.19", path = "src/uuhelp_parser" }
# * uutils
uu_test = { optional=true, version="0.0.18", package="uu_test", path="src/uu/test" }
uu_test = { optional = true, version = "0.0.20", package = "uu_test", path = "src/uu/test" }
#
arch = { optional=true, version="0.0.18", package="uu_arch", path="src/uu/arch" }
base32 = { optional=true, version="0.0.18", package="uu_base32", path="src/uu/base32" }
base64 = { optional=true, version="0.0.18", package="uu_base64", path="src/uu/base64" }
basename = { optional=true, version="0.0.18", package="uu_basename", path="src/uu/basename" }
basenc = { optional=true, version="0.0.18", package="uu_basenc", path="src/uu/basenc" }
cat = { optional=true, version="0.0.18", package="uu_cat", path="src/uu/cat" }
chcon = { optional=true, version="0.0.18", package="uu_chcon", path="src/uu/chcon" }
chgrp = { optional=true, version="0.0.18", package="uu_chgrp", path="src/uu/chgrp" }
chmod = { optional=true, version="0.0.18", package="uu_chmod", path="src/uu/chmod" }
chown = { optional=true, version="0.0.18", package="uu_chown", path="src/uu/chown" }
chroot = { optional=true, version="0.0.18", package="uu_chroot", path="src/uu/chroot" }
cksum = { optional=true, version="0.0.18", package="uu_cksum", path="src/uu/cksum" }
comm = { optional=true, version="0.0.18", package="uu_comm", path="src/uu/comm" }
cp = { optional=true, version="0.0.18", package="uu_cp", path="src/uu/cp" }
csplit = { optional=true, version="0.0.18", package="uu_csplit", path="src/uu/csplit" }
cut = { optional=true, version="0.0.18", package="uu_cut", path="src/uu/cut" }
date = { optional=true, version="0.0.18", package="uu_date", path="src/uu/date" }
dd = { optional=true, version="0.0.18", package="uu_dd", path="src/uu/dd" }
df = { optional=true, version="0.0.18", package="uu_df", path="src/uu/df" }
dir = { optional=true, version="0.0.18", package="uu_dir", path="src/uu/dir" }
dircolors= { optional=true, version="0.0.18", package="uu_dircolors", path="src/uu/dircolors" }
dirname = { optional=true, version="0.0.18", package="uu_dirname", path="src/uu/dirname" }
du = { optional=true, version="0.0.18", package="uu_du", path="src/uu/du" }
echo = { optional=true, version="0.0.18", package="uu_echo", path="src/uu/echo" }
env = { optional=true, version="0.0.18", package="uu_env", path="src/uu/env" }
expand = { optional=true, version="0.0.18", package="uu_expand", path="src/uu/expand" }
expr = { optional=true, version="0.0.18", package="uu_expr", path="src/uu/expr" }
factor = { optional=true, version="0.0.18", package="uu_factor", path="src/uu/factor" }
false = { optional=true, version="0.0.18", package="uu_false", path="src/uu/false" }
fmt = { optional=true, version="0.0.18", package="uu_fmt", path="src/uu/fmt" }
fold = { optional=true, version="0.0.18", package="uu_fold", path="src/uu/fold" }
groups = { optional=true, version="0.0.18", package="uu_groups", path="src/uu/groups" }
hashsum = { optional=true, version="0.0.18", package="uu_hashsum", path="src/uu/hashsum" }
head = { optional=true, version="0.0.18", package="uu_head", path="src/uu/head" }
hostid = { optional=true, version="0.0.18", package="uu_hostid", path="src/uu/hostid" }
hostname = { optional=true, version="0.0.18", package="uu_hostname", path="src/uu/hostname" }
id = { optional=true, version="0.0.18", package="uu_id", path="src/uu/id" }
install = { optional=true, version="0.0.18", package="uu_install", path="src/uu/install" }
join = { optional=true, version="0.0.18", package="uu_join", path="src/uu/join" }
kill = { optional=true, version="0.0.18", package="uu_kill", path="src/uu/kill" }
link = { optional=true, version="0.0.18", package="uu_link", path="src/uu/link" }
ln = { optional=true, version="0.0.18", package="uu_ln", path="src/uu/ln" }
ls = { optional=true, version="0.0.18", package="uu_ls", path="src/uu/ls" }
logname = { optional=true, version="0.0.18", package="uu_logname", path="src/uu/logname" }
mkdir = { optional=true, version="0.0.18", package="uu_mkdir", path="src/uu/mkdir" }
mkfifo = { optional=true, version="0.0.18", package="uu_mkfifo", path="src/uu/mkfifo" }
mknod = { optional=true, version="0.0.18", package="uu_mknod", path="src/uu/mknod" }
mktemp = { optional=true, version="0.0.18", package="uu_mktemp", path="src/uu/mktemp" }
more = { optional=true, version="0.0.18", package="uu_more", path="src/uu/more" }
mv = { optional=true, version="0.0.18", package="uu_mv", path="src/uu/mv" }
nice = { optional=true, version="0.0.18", package="uu_nice", path="src/uu/nice" }
nl = { optional=true, version="0.0.18", package="uu_nl", path="src/uu/nl" }
nohup = { optional=true, version="0.0.18", package="uu_nohup", path="src/uu/nohup" }
nproc = { optional=true, version="0.0.18", package="uu_nproc", path="src/uu/nproc" }
numfmt = { optional=true, version="0.0.18", package="uu_numfmt", path="src/uu/numfmt" }
od = { optional=true, version="0.0.18", package="uu_od", path="src/uu/od" }
paste = { optional=true, version="0.0.18", package="uu_paste", path="src/uu/paste" }
pathchk = { optional=true, version="0.0.18", package="uu_pathchk", path="src/uu/pathchk" }
pinky = { optional=true, version="0.0.18", package="uu_pinky", path="src/uu/pinky" }
pr = { optional=true, version="0.0.18", package="uu_pr", path="src/uu/pr" }
printenv = { optional=true, version="0.0.18", package="uu_printenv", path="src/uu/printenv" }
printf = { optional=true, version="0.0.18", package="uu_printf", path="src/uu/printf" }
ptx = { optional=true, version="0.0.18", package="uu_ptx", path="src/uu/ptx" }
pwd = { optional=true, version="0.0.18", package="uu_pwd", path="src/uu/pwd" }
readlink = { optional=true, version="0.0.18", package="uu_readlink", path="src/uu/readlink" }
realpath = { optional=true, version="0.0.18", package="uu_realpath", path="src/uu/realpath" }
relpath = { optional=true, version="0.0.18", package="uu_relpath", path="src/uu/relpath" }
rm = { optional=true, version="0.0.18", package="uu_rm", path="src/uu/rm" }
rmdir = { optional=true, version="0.0.18", package="uu_rmdir", path="src/uu/rmdir" }
runcon = { optional=true, version="0.0.18", package="uu_runcon", path="src/uu/runcon" }
seq = { optional=true, version="0.0.18", package="uu_seq", path="src/uu/seq" }
shred = { optional=true, version="0.0.18", package="uu_shred", path="src/uu/shred" }
shuf = { optional=true, version="0.0.18", package="uu_shuf", path="src/uu/shuf" }
sleep = { optional=true, version="0.0.18", package="uu_sleep", path="src/uu/sleep" }
sort = { optional=true, version="0.0.18", package="uu_sort", path="src/uu/sort" }
split = { optional=true, version="0.0.18", package="uu_split", path="src/uu/split" }
stat = { optional=true, version="0.0.18", package="uu_stat", path="src/uu/stat" }
stdbuf = { optional=true, version="0.0.18", package="uu_stdbuf", path="src/uu/stdbuf" }
stty = { optional=true, version="0.0.18", package="uu_stty", path="src/uu/stty" }
sum = { optional=true, version="0.0.18", package="uu_sum", path="src/uu/sum" }
sync = { optional=true, version="0.0.18", package="uu_sync", path="src/uu/sync" }
tac = { optional=true, version="0.0.18", package="uu_tac", path="src/uu/tac" }
tail = { optional=true, version="0.0.18", package="uu_tail", path="src/uu/tail" }
tee = { optional=true, version="0.0.18", package="uu_tee", path="src/uu/tee" }
timeout = { optional=true, version="0.0.18", package="uu_timeout", path="src/uu/timeout" }
touch = { optional=true, version="0.0.18", package="uu_touch", path="src/uu/touch" }
tr = { optional=true, version="0.0.18", package="uu_tr", path="src/uu/tr" }
true = { optional=true, version="0.0.18", package="uu_true", path="src/uu/true" }
truncate = { optional=true, version="0.0.18", package="uu_truncate", path="src/uu/truncate" }
tsort = { optional=true, version="0.0.18", package="uu_tsort", path="src/uu/tsort" }
tty = { optional=true, version="0.0.18", package="uu_tty", path="src/uu/tty" }
uname = { optional=true, version="0.0.18", package="uu_uname", path="src/uu/uname" }
unexpand = { optional=true, version="0.0.18", package="uu_unexpand", path="src/uu/unexpand" }
uniq = { optional=true, version="0.0.18", package="uu_uniq", path="src/uu/uniq" }
unlink = { optional=true, version="0.0.18", package="uu_unlink", path="src/uu/unlink" }
uptime = { optional=true, version="0.0.18", package="uu_uptime", path="src/uu/uptime" }
users = { optional=true, version="0.0.18", package="uu_users", path="src/uu/users" }
vdir = { optional=true, version="0.0.18", package="uu_vdir", path="src/uu/vdir" }
wc = { optional=true, version="0.0.18", package="uu_wc", path="src/uu/wc" }
who = { optional=true, version="0.0.18", package="uu_who", path="src/uu/who" }
whoami = { optional=true, version="0.0.18", package="uu_whoami", path="src/uu/whoami" }
yes = { optional=true, version="0.0.18", package="uu_yes", path="src/uu/yes" }
arch = { optional = true, version = "0.0.20", package = "uu_arch", path = "src/uu/arch" }
base32 = { optional = true, version = "0.0.20", package = "uu_base32", path = "src/uu/base32" }
base64 = { optional = true, version = "0.0.20", package = "uu_base64", path = "src/uu/base64" }
basename = { optional = true, version = "0.0.20", package = "uu_basename", path = "src/uu/basename" }
basenc = { optional = true, version = "0.0.20", package = "uu_basenc", path = "src/uu/basenc" }
cat = { optional = true, version = "0.0.20", package = "uu_cat", path = "src/uu/cat" }
chcon = { optional = true, version = "0.0.20", package = "uu_chcon", path = "src/uu/chcon" }
chgrp = { optional = true, version = "0.0.20", package = "uu_chgrp", path = "src/uu/chgrp" }
chmod = { optional = true, version = "0.0.20", package = "uu_chmod", path = "src/uu/chmod" }
chown = { optional = true, version = "0.0.20", package = "uu_chown", path = "src/uu/chown" }
chroot = { optional = true, version = "0.0.20", package = "uu_chroot", path = "src/uu/chroot" }
cksum = { optional = true, version = "0.0.20", package = "uu_cksum", path = "src/uu/cksum" }
comm = { optional = true, version = "0.0.20", package = "uu_comm", path = "src/uu/comm" }
cp = { optional = true, version = "0.0.20", package = "uu_cp", path = "src/uu/cp" }
csplit = { optional = true, version = "0.0.20", package = "uu_csplit", path = "src/uu/csplit" }
cut = { optional = true, version = "0.0.20", package = "uu_cut", path = "src/uu/cut" }
date = { optional = true, version = "0.0.20", package = "uu_date", path = "src/uu/date" }
dd = { optional = true, version = "0.0.20", package = "uu_dd", path = "src/uu/dd" }
df = { optional = true, version = "0.0.20", package = "uu_df", path = "src/uu/df" }
dir = { optional = true, version = "0.0.20", package = "uu_dir", path = "src/uu/dir" }
dircolors = { optional = true, version = "0.0.20", package = "uu_dircolors", path = "src/uu/dircolors" }
dirname = { optional = true, version = "0.0.20", package = "uu_dirname", path = "src/uu/dirname" }
du = { optional = true, version = "0.0.20", package = "uu_du", path = "src/uu/du" }
echo = { optional = true, version = "0.0.20", package = "uu_echo", path = "src/uu/echo" }
env = { optional = true, version = "0.0.20", package = "uu_env", path = "src/uu/env" }
expand = { optional = true, version = "0.0.20", package = "uu_expand", path = "src/uu/expand" }
expr = { optional = true, version = "0.0.20", package = "uu_expr", path = "src/uu/expr" }
factor = { optional = true, version = "0.0.20", package = "uu_factor", path = "src/uu/factor" }
false = { optional = true, version = "0.0.20", package = "uu_false", path = "src/uu/false" }
fmt = { optional = true, version = "0.0.20", package = "uu_fmt", path = "src/uu/fmt" }
fold = { optional = true, version = "0.0.20", package = "uu_fold", path = "src/uu/fold" }
groups = { optional = true, version = "0.0.20", package = "uu_groups", path = "src/uu/groups" }
hashsum = { optional = true, version = "0.0.20", package = "uu_hashsum", path = "src/uu/hashsum" }
head = { optional = true, version = "0.0.20", package = "uu_head", path = "src/uu/head" }
hostid = { optional = true, version = "0.0.20", package = "uu_hostid", path = "src/uu/hostid" }
hostname = { optional = true, version = "0.0.20", package = "uu_hostname", path = "src/uu/hostname" }
id = { optional = true, version = "0.0.20", package = "uu_id", path = "src/uu/id" }
install = { optional = true, version = "0.0.20", package = "uu_install", path = "src/uu/install" }
join = { optional = true, version = "0.0.20", package = "uu_join", path = "src/uu/join" }
kill = { optional = true, version = "0.0.20", package = "uu_kill", path = "src/uu/kill" }
link = { optional = true, version = "0.0.20", package = "uu_link", path = "src/uu/link" }
ln = { optional = true, version = "0.0.20", package = "uu_ln", path = "src/uu/ln" }
ls = { optional = true, version = "0.0.20", package = "uu_ls", path = "src/uu/ls" }
logname = { optional = true, version = "0.0.20", package = "uu_logname", path = "src/uu/logname" }
mkdir = { optional = true, version = "0.0.20", package = "uu_mkdir", path = "src/uu/mkdir" }
mkfifo = { optional = true, version = "0.0.20", package = "uu_mkfifo", path = "src/uu/mkfifo" }
mknod = { optional = true, version = "0.0.20", package = "uu_mknod", path = "src/uu/mknod" }
mktemp = { optional = true, version = "0.0.20", package = "uu_mktemp", path = "src/uu/mktemp" }
more = { optional = true, version = "0.0.20", package = "uu_more", path = "src/uu/more" }
mv = { optional = true, version = "0.0.20", package = "uu_mv", path = "src/uu/mv" }
nice = { optional = true, version = "0.0.20", package = "uu_nice", path = "src/uu/nice" }
nl = { optional = true, version = "0.0.20", package = "uu_nl", path = "src/uu/nl" }
nohup = { optional = true, version = "0.0.20", package = "uu_nohup", path = "src/uu/nohup" }
nproc = { optional = true, version = "0.0.20", package = "uu_nproc", path = "src/uu/nproc" }
numfmt = { optional = true, version = "0.0.20", package = "uu_numfmt", path = "src/uu/numfmt" }
od = { optional = true, version = "0.0.20", package = "uu_od", path = "src/uu/od" }
paste = { optional = true, version = "0.0.20", package = "uu_paste", path = "src/uu/paste" }
pathchk = { optional = true, version = "0.0.20", package = "uu_pathchk", path = "src/uu/pathchk" }
pinky = { optional = true, version = "0.0.20", package = "uu_pinky", path = "src/uu/pinky" }
pr = { optional = true, version = "0.0.20", package = "uu_pr", path = "src/uu/pr" }
printenv = { optional = true, version = "0.0.20", package = "uu_printenv", path = "src/uu/printenv" }
printf = { optional = true, version = "0.0.20", package = "uu_printf", path = "src/uu/printf" }
ptx = { optional = true, version = "0.0.20", package = "uu_ptx", path = "src/uu/ptx" }
pwd = { optional = true, version = "0.0.20", package = "uu_pwd", path = "src/uu/pwd" }
readlink = { optional = true, version = "0.0.20", package = "uu_readlink", path = "src/uu/readlink" }
realpath = { optional = true, version = "0.0.20", package = "uu_realpath", path = "src/uu/realpath" }
relpath = { optional = true, version = "0.0.20", package = "uu_relpath", path = "src/uu/relpath" }
rm = { optional = true, version = "0.0.20", package = "uu_rm", path = "src/uu/rm" }
rmdir = { optional = true, version = "0.0.20", package = "uu_rmdir", path = "src/uu/rmdir" }
runcon = { optional = true, version = "0.0.20", package = "uu_runcon", path = "src/uu/runcon" }
seq = { optional = true, version = "0.0.20", package = "uu_seq", path = "src/uu/seq" }
shred = { optional = true, version = "0.0.20", package = "uu_shred", path = "src/uu/shred" }
shuf = { optional = true, version = "0.0.20", package = "uu_shuf", path = "src/uu/shuf" }
sleep = { optional = true, version = "0.0.20", package = "uu_sleep", path = "src/uu/sleep" }
sort = { optional = true, version = "0.0.20", package = "uu_sort", path = "src/uu/sort" }
split = { optional = true, version = "0.0.20", package = "uu_split", path = "src/uu/split" }
stat = { optional = true, version = "0.0.20", package = "uu_stat", path = "src/uu/stat" }
stdbuf = { optional = true, version = "0.0.20", package = "uu_stdbuf", path = "src/uu/stdbuf" }
stty = { optional = true, version = "0.0.20", package = "uu_stty", path = "src/uu/stty" }
sum = { optional = true, version = "0.0.20", package = "uu_sum", path = "src/uu/sum" }
sync = { optional = true, version = "0.0.20", package = "uu_sync", path = "src/uu/sync" }
tac = { optional = true, version = "0.0.20", package = "uu_tac", path = "src/uu/tac" }
tail = { optional = true, version = "0.0.20", package = "uu_tail", path = "src/uu/tail" }
tee = { optional = true, version = "0.0.20", package = "uu_tee", path = "src/uu/tee" }
timeout = { optional = true, version = "0.0.20", package = "uu_timeout", path = "src/uu/timeout" }
touch = { optional = true, version = "0.0.20", package = "uu_touch", path = "src/uu/touch" }
tr = { optional = true, version = "0.0.20", package = "uu_tr", path = "src/uu/tr" }
true = { optional = true, version = "0.0.20", package = "uu_true", path = "src/uu/true" }
truncate = { optional = true, version = "0.0.20", package = "uu_truncate", path = "src/uu/truncate" }
tsort = { optional = true, version = "0.0.20", package = "uu_tsort", path = "src/uu/tsort" }
tty = { optional = true, version = "0.0.20", package = "uu_tty", path = "src/uu/tty" }
uname = { optional = true, version = "0.0.20", package = "uu_uname", path = "src/uu/uname" }
unexpand = { optional = true, version = "0.0.20", package = "uu_unexpand", path = "src/uu/unexpand" }
uniq = { optional = true, version = "0.0.20", package = "uu_uniq", path = "src/uu/uniq" }
unlink = { optional = true, version = "0.0.20", package = "uu_unlink", path = "src/uu/unlink" }
uptime = { optional = true, version = "0.0.20", package = "uu_uptime", path = "src/uu/uptime" }
users = { optional = true, version = "0.0.20", package = "uu_users", path = "src/uu/users" }
vdir = { optional = true, version = "0.0.20", package = "uu_vdir", path = "src/uu/vdir" }
wc = { optional = true, version = "0.0.20", package = "uu_wc", path = "src/uu/wc" }
who = { optional = true, version = "0.0.20", package = "uu_who", path = "src/uu/who" }
whoami = { optional = true, version = "0.0.20", package = "uu_whoami", path = "src/uu/whoami" }
yes = { optional = true, version = "0.0.20", package = "uu_yes", path = "src/uu/yes" }
# this breaks clippy linting with: "tests/by-util/test_factor_benches.rs: No such file or directory (os error 2)"
# factor_benches = { optional = true, version = "0.0.0", package = "uu_factor_benches", path = "tests/benches/factor" }
@ -475,35 +476,34 @@ yes = { optional=true, version="0.0.18", package="uu_yes", path="src/uu/yes
#pin_cc = { version="1.0.61, < 1.0.62", package="cc" } ## cc v1.0.62 has compiler errors for MinRustV v1.32.0, requires 1.34 (for `std::str::split_ascii_whitespace()`)
[dev-dependencies]
chrono = { workspace=true }
chrono = { workspace = true }
conv = "0.3"
filetime = { workspace=true }
glob = { workspace=true }
libc = { workspace=true }
filetime = { workspace = true }
glob = { workspace = true }
libc = { workspace = true }
pretty_assertions = "1"
rand = { workspace=true }
regex = { workspace=true }
sha1 = { version="0.10", features=["std"] }
tempfile = { workspace=true }
time = { workspace=true, features=["local-offset"] }
rand = { workspace = true }
regex = { workspace = true }
sha1 = { version = "0.10", features = ["std"] }
tempfile = { workspace = true }
time = { workspace = true, features = ["local-offset"] }
unindent = "0.2"
uucore = { workspace=true, features=["entries", "process", "signals"] }
walkdir = { workspace=true }
is-terminal = { workspace=true }
uucore = { workspace = true, features = ["entries", "process", "signals"] }
walkdir = { workspace = true }
is-terminal = { workspace = true }
hex-literal = "0.4.1"
rstest = "0.17.0"
rstest = { workspace = true }
[target.'cfg(any(target_os = "linux", target_os = "android"))'.dev-dependencies]
procfs = { version = "0.15", default-features = false }
rlimit = "0.9.1"
rlimit = "0.10.1"
[target.'cfg(unix)'.dev-dependencies]
nix = { workspace=true, features=["process", "signal", "user"] }
rust-users = { version="0.11", package="users" }
nix = { workspace = true, features = ["process", "signal", "user"] }
rand_pcg = "0.3"
[build-dependencies]
phf_codegen = { workspace=true }
phf_codegen = { workspace = true }
[[bin]]
name = "coreutils"

View file

@ -1,4 +1,4 @@
# spell-checker:ignore (misc) testsuite runtest findstring (targets) busytest toybox distclean pkgs nextest ; (vars/env) BINDIR BUILDDIR CARGOFLAGS DESTDIR DOCSDIR INSTALLDIR INSTALLEES MULTICALL DATAROOTDIR TESTDIR
# spell-checker:ignore (misc) testsuite runtest findstring (targets) busytest toybox distclean pkgs nextest ; (vars/env) BINDIR BUILDDIR CARGOFLAGS DESTDIR DOCSDIR INSTALLDIR INSTALLEES MULTICALL DATAROOTDIR TESTDIR manpages
# Config options
PROFILE ?= debug
@ -337,7 +337,21 @@ clean:
distclean: clean
$(CARGO) clean $(CARGOFLAGS) && $(CARGO) update $(CARGOFLAGS)
install: build
manpages: build-coreutils
mkdir -p $(BUILDDIR)/man/
$(foreach prog, $(INSTALLEES), \
$(BUILDDIR)/coreutils manpage $(prog) > $(BUILDDIR)/man/$(PROG_PREFIX)$(prog).1; \
)
completions: build-coreutils
mkdir -p $(BUILDDIR)/completions/zsh $(BUILDDIR)/completions/bash $(BUILDDIR)/completions/fish
$(foreach prog, $(INSTALLEES), \
$(BUILDDIR)/coreutils completion $(prog) zsh > $(BUILDDIR)/completions/zsh/_$(PROG_PREFIX)$(prog); \
$(BUILDDIR)/coreutils completion $(prog) bash > $(BUILDDIR)/completions/bash/$(PROG_PREFIX)$(prog); \
$(BUILDDIR)/coreutils completion $(prog) fish > $(BUILDDIR)/completions/fish/$(PROG_PREFIX)$(prog).fish; \
)
install: build manpages completions
mkdir -p $(INSTALLDIR_BIN)
ifeq (${MULTICALL}, y)
$(INSTALL) $(BUILDDIR)/coreutils $(INSTALLDIR_BIN)/$(PROG_PREFIX)coreutils
@ -349,15 +363,18 @@ else
$(INSTALL) $(BUILDDIR)/$(prog) $(INSTALLDIR_BIN)/$(PROG_PREFIX)$(prog);)
$(if $(findstring test,$(INSTALLEES)), $(INSTALL) $(BUILDDIR)/test $(INSTALLDIR_BIN)/$(PROG_PREFIX)[)
endif
mkdir -p $(DESTDIR)$(DATAROOTDIR)/man/man1
$(foreach prog, $(INSTALLEES), \
$(INSTALL) $(BUILDDIR)/man/$(PROG_PREFIX)$(prog).1 $(DESTDIR)$(DATAROOTDIR)/man/man1/; \
)
mkdir -p $(DESTDIR)$(DATAROOTDIR)/zsh/site-functions
mkdir -p $(DESTDIR)$(DATAROOTDIR)/bash-completion/completions
mkdir -p $(DESTDIR)$(DATAROOTDIR)/fish/vendor_completions.d
mkdir -p $(DESTDIR)$(DATAROOTDIR)/man/man1
$(foreach prog, $(INSTALLEES), \
$(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; \
$(BUILDDIR)/coreutils manpage $(prog) > $(DESTDIR)$(DATAROOTDIR)/man/man1/$(PROG_PREFIX)$(prog).1; \
$(INSTALL) $(BUILDDIR)/completions/zsh/_$(PROG_PREFIX)$(prog) $(DESTDIR)$(DATAROOTDIR)/zsh/site-functions/; \
$(INSTALL) $(BUILDDIR)/completions/bash/$(PROG_PREFIX)$(prog) $(DESTDIR)$(DATAROOTDIR)/bash-completion/completions/; \
$(INSTALL) $(BUILDDIR)/completions/fish/$(PROG_PREFIX)$(prog).fish $(DESTDIR)$(DATAROOTDIR)/fish/vendor_completions.d/; \
)
uninstall:
@ -369,5 +386,6 @@ endif
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)))
rm -f $(addprefix $(DESTDIR)$(DATAROOTDIR)/man/man1/$(PROG_PREFIX),$(addsuffix .1,$(PROGS)))
.PHONY: all build build-coreutils build-pkgs test distclean clean busytest install uninstall

View file

@ -20,15 +20,12 @@ run_task = "_init"
[tasks._init]
private = true
dependencies = [
"_init-vars",
]
dependencies = ["_init-vars"]
[tasks._init-vars]
private = true
script_runner = "@duckscript"
script = [
'''
script = ['''
# reset build/test flags
set_env CARGO_MAKE_CARGO_BUILD_TEST_FLAGS ""
# determine features
@ -90,54 +87,36 @@ for arg in "${args_utils_list}"
end
args_utils = trim "${args_utils}"
set_env CARGO_MAKE_TASK_BUILD_UTILS_ARGS "${args_utils}"
'''
]
''']
### tasks
[tasks.default]
description = "## *DEFAULT* Build (debug-mode) and test project"
category = "[project]"
dependencies = [
"action-build-debug",
"test-terse",
]
dependencies = ["action-build-debug", "test-terse"]
##
[tasks.build]
description = "## Build (release-mode) project"
category = "[project]"
dependencies = [
"core::pre-build",
"action-build-release",
"core::post-build",
]
dependencies = ["core::pre-build", "action-build-release", "core::post-build"]
[tasks.build-debug]
description = "## Build (debug-mode) project"
category = "[project]"
dependencies = [
"action-build-debug",
]
dependencies = ["action-build-debug"]
[tasks.build-examples]
description = "## Build (release-mode) project example(s); usage: `cargo make (build-examples | examples) [EXAMPLE]...`"
category = "[project]"
dependencies = [
"core::pre-build",
"action-build-examples",
"core::post-build",
]
dependencies = ["core::pre-build", "action-build-examples", "core::post-build"]
[tasks.build-features]
description = "## Build (with features; release-mode) project; usage: `cargo make (build-features | features) FEATURE...`"
category = "[project]"
dependencies = [
"core::pre-build",
"action-build-features",
"core::post-build",
]
dependencies = ["core::pre-build", "action-build-features", "core::post-build"]
[tasks.build-release]
alias = "build"
@ -148,9 +127,7 @@ alias = "build-debug"
[tasks.example]
description = "hidden singular-form alias for 'examples'"
category = "[project]"
dependencies = [
"examples",
]
dependencies = ["examples"]
[tasks.examples]
alias = "build-examples"
@ -161,17 +138,12 @@ alias = "build-features"
[tasks.format]
description = "## Format code files (with `cargo fmt`; includes tests)"
category = "[project]"
dependencies = [
"action-format",
"action-format-tests",
]
dependencies = ["action-format", "action-format-tests"]
[tasks.help]
description = "## Display help"
category = "[project]"
dependencies = [
"action-display-help",
]
dependencies = ["action-display-help"]
[tasks.install]
description = "## Install project binary (to $HOME/.cargo/bin)"
@ -182,10 +154,7 @@ args = ["install", "--path", "."]
[tasks.lint]
description = "## Display lint report"
category = "[project]"
dependencies = [
"action-clippy",
"action-fmt_report",
]
dependencies = ["action-clippy", "action-fmt_report"]
[tasks.release]
alias = "build"
@ -193,48 +162,32 @@ alias = "build"
[tasks.test]
description = "## Run project tests"
category = "[project]"
dependencies = [
"core::pre-test",
"core::test",
"core::post-test",
]
dependencies = ["core::pre-test", "core::test", "core::post-test"]
[tasks.test-terse]
description = "## Run project tests (with terse/summary output)"
category = "[project]"
dependencies = [
"core::pre-test",
"action-test_quiet",
"core::post-test",
]
dependencies = ["core::pre-test", "action-test_quiet", "core::post-test"]
[tasks.test-util]
description = "## Test (individual) utilities; usage: `cargo make (test-util | test-uutil) [UTIL_NAME...]`"
category = "[project]"
dependencies = [
"action-test-utils",
]
dependencies = ["action-test-utils"]
[tasks.test-utils]
description = "hidden plural-form alias for 'test-util'"
category = "[project]"
dependencies = [
"test-util",
]
dependencies = ["test-util"]
[tasks.test-uutil]
description = "hidden alias for 'test-util'"
category = "[project]"
dependencies = [
"test-util",
]
dependencies = ["test-util"]
[tasks.test-uutils]
description = "hidden alias for 'test-util'"
category = "[project]"
dependencies = [
"test-util",
]
dependencies = ["test-util"]
[tasks.uninstall]
description = "## Remove project binary (from $HOME/.cargo/bin)"
@ -246,63 +199,66 @@ args = ["uninstall"]
description = "## Build (individual; release-mode) utilities; usage: `cargo make (util | uutil) [UTIL_NAME...]`"
category = "[project]"
dependencies = [
"core::pre-build",
"action-determine-utils",
"action-build-utils",
"core::post-build",
"core::pre-build",
"action-determine-utils",
"action-build-utils",
"core::post-build",
]
[tasks.utils]
description = "hidden plural-form alias for 'util'"
category = "[project]"
dependencies = [
"util",
]
dependencies = ["util"]
[tasks.uutil]
description = "hidden alias for 'util'"
category = "[project]"
dependencies = [
"util",
]
dependencies = ["util"]
[tasks.uutils]
description = "hidden plural-form alias for 'util'"
category = "[project]"
dependencies = [
"util",
]
dependencies = ["util"]
### actions
[tasks.action-build-release]
description = "`cargo build --release`"
command = "cargo"
args = ["build", "--release", "@@split(CARGO_MAKE_CARGO_BUILD_TEST_FLAGS, )" ]
args = ["build", "--release", "@@split(CARGO_MAKE_CARGO_BUILD_TEST_FLAGS, )"]
[tasks.action-build-debug]
description = "`cargo build`"
command = "cargo"
args = ["build", "@@split(CARGO_MAKE_CARGO_BUILD_TEST_FLAGS, )" ]
args = ["build", "@@split(CARGO_MAKE_CARGO_BUILD_TEST_FLAGS, )"]
[tasks.action-build-examples]
description = "`cargo build (--examples|(--example EXAMPLE)...)`"
command = "cargo"
args = ["build", "--release", "@@split(CARGO_MAKE_CARGO_BUILD_TEST_FLAGS, )", "${CARGO_MAKE_TASK_BUILD_EXAMPLES_ARGS}" ]
args = [
"build",
"--release",
"@@split(CARGO_MAKE_CARGO_BUILD_TEST_FLAGS, )",
"${CARGO_MAKE_TASK_BUILD_EXAMPLES_ARGS}",
]
[tasks.action-build-features]
description = "`cargo build --release --features FEATURES`"
command = "cargo"
args = ["build", "--release", "--no-default-features", "--features", "${CARGO_MAKE_TASK_BUILD_FEATURES_ARGS}" ]
args = [
"build",
"--release",
"--no-default-features",
"--features",
"${CARGO_MAKE_TASK_BUILD_FEATURES_ARGS}",
]
[tasks.action-build-utils]
description = "Build individual utilities"
dependencies = [
"action-determine-utils",
]
dependencies = ["action-determine-utils"]
command = "cargo"
# args = ["build", "@@remove-empty(CARGO_MAKE_TASK_BUILD_UTILS_ARGS)" ]
args = ["build", "--release", "@@split(CARGO_MAKE_TASK_BUILD_UTILS_ARGS, )" ]
args = ["build", "--release", "@@split(CARGO_MAKE_TASK_BUILD_UTILS_ARGS, )"]
[tasks.action-clippy]
description = "`cargo clippy` lint report"
@ -311,8 +267,7 @@ args = ["clippy", "@@split(CARGO_MAKE_CARGO_BUILD_TEST_FLAGS, )"]
[tasks.action-determine-utils]
script_runner = "@duckscript"
script = [
'''
script = ['''
package_options = get_env CARGO_MAKE_TASK_BUILD_UTILS_ARGS
if is_empty "${package_options}"
show_utils = get_env CARGO_MAKE_VAR_SHOW_UTILS
@ -335,13 +290,11 @@ if is_empty "${package_options}"
package_options = trim "${package_options}"
end_if
set_env CARGO_MAKE_TASK_BUILD_UTILS_ARGS "${package_options}"
'''
]
''']
[tasks.action-determine-tests]
script_runner = "@duckscript"
script = [
'''
script = ['''
test_files = glob_array tests/**/*.rs
for file in ${test_files}
file = replace "${file}" "\\" "/"
@ -354,8 +307,7 @@ for file in ${test_files}
end_if
end
set_env CARGO_MAKE_VAR_TESTS "${tests}"
'''
]
''']
[tasks.action-format]
description = "`cargo fmt`"
@ -364,9 +316,7 @@ args = ["fmt"]
[tasks.action-format-tests]
description = "`cargo fmt` tests"
dependencies = [
"action-determine-tests",
]
dependencies = ["action-determine-tests"]
command = "cargo"
args = ["fmt", "--", "@@split(CARGO_MAKE_VAR_TESTS, )"]
@ -381,16 +331,18 @@ args = ["fmt", "--", "--check"]
[tasks.action-spellcheck-codespell]
description = "`codespell` spellcheck repository"
command = "codespell" # (from `pip install codespell`)
args = [".", "--skip=*/.git,./target,./tests/fixtures", "--ignore-words-list=mut,od"]
args = [
".",
"--skip=*/.git,./target,./tests/fixtures",
"--ignore-words-list=mut,od",
]
[tasks.action-test-utils]
description = "Build individual utilities"
dependencies = [
"action-determine-utils",
]
dependencies = ["action-determine-utils"]
command = "cargo"
# args = ["build", "@@remove-empty(CARGO_MAKE_TASK_BUILD_UTILS_ARGS)" ]
args = ["test", "@@split(CARGO_MAKE_TASK_BUILD_UTILS_ARGS, )" ]
args = ["test", "@@split(CARGO_MAKE_TASK_BUILD_UTILS_ARGS, )"]
[tasks.action-test_quiet]
description = "Test (in `--quiet` mode)"
@ -399,8 +351,7 @@ args = ["test", "--quiet", "@@split(CARGO_MAKE_CARGO_BUILD_TEST_FLAGS, )"]
[tasks.action-display-help]
script_runner = "@duckscript"
script = [
'''
script = ['''
echo ""
echo "usage: `cargo make TARGET [ARGS...]`"
echo ""
@ -432,5 +383,4 @@ script = [
end_if
end
echo ""
'''
]
''']

View file

@ -1,6 +1,7 @@
<!-- markdownlint-disable MD033 MD041 MD002 -->
<!-- markdownlint-disable commands-show-output no-duplicate-heading -->
<!-- spell-checker:ignore markdownlint ; (options) DESTDIR UTILNAME manpages reimplementation -->
<!-- spell-checker:ignore markdownlint ; (options) DESTDIR UTILNAME manpages reimplementation oranda -->
<div class="oranda-hide">
<div align="center">
![uutils logo](docs/src/logo.svg)
@ -19,11 +20,14 @@
---
</div>
uutils coreutils is a cross-platform reimplementation of the GNU coreutils in
[Rust](http://www.rust-lang.org). While all programs have been implemented, some
options might be missing or different behavior might be experienced.
<div class="oranda-hide">
To install it:
```shell
@ -31,6 +35,8 @@ cargo install coreutils
~/.cargo/bin/coreutils
```
</div>
<!-- markdownlint-disable-next-line MD026 -->
## Goals
@ -42,6 +48,8 @@ uutils aims to work on as many platforms as possible, to be able to use the same
utils on Linux, Mac, Windows and other platforms. This ensures, for example,
that scripts can be easily transferred between platforms.
<div class="oranda-hide">
## Documentation
uutils has both user and developer documentation available:
@ -52,6 +60,7 @@ uutils has both user and developer documentation available:
Both can also be generated locally, the instructions for that can be found in
the [coreutils docs](https://github.com/uutils/uutils.github.io) repository.
<!-- ANCHOR: build (this mark is needed for mdbook) -->
## Requirements
@ -301,6 +310,8 @@ See <https://github.com/uutils/coreutils/issues/3336> for the main meta bugs
![Evolution over time](https://github.com/uutils/coreutils-tracking/blob/main/gnu-results.png?raw=true)
</div> <!-- close oranda-hide div -->
## Contributing
To contribute to uutils, please see [CONTRIBUTING](CONTRIBUTING.md).

View file

@ -38,14 +38,14 @@ pub fn main() {
let mut mf = File::create(Path::new(&out_dir).join("uutils_map.rs")).unwrap();
mf.write_all(
"type UtilityMap<T> = phf::Map<&'static str, (fn(T) -> i32, fn() -> Command)>;\n\
"type UtilityMap<T> = phf::OrderedMap<&'static str, (fn(T) -> i32, fn() -> Command)>;\n\
\n\
fn util_map<T: uucore::Args>() -> UtilityMap<T> {\n"
.as_bytes(),
)
.unwrap();
let mut phf_map = phf_codegen::Map::<&str>::new();
let mut phf_map = phf_codegen::OrderedMap::<&str>::new();
for krate in &crates {
let map_value = format!("({krate}::uumain, {krate}::uu_app)");
match krate.as_ref() {

View file

@ -11,7 +11,7 @@ unmaintained = "warn"
yanked = "warn"
notice = "warn"
ignore = [
#"RUSTSEC-0000-0000",
#"RUSTSEC-0000-0000",
]
# This section is considered when running `cargo deny check licenses`
@ -20,15 +20,14 @@ ignore = [
[licenses]
unlicensed = "deny"
allow = [
"MIT",
"Apache-2.0",
"ISC",
"BSD-2-Clause",
"BSD-2-Clause-FreeBSD",
"BSD-3-Clause",
"CC0-1.0",
"MPL-2.0", # XXX considered copyleft?
"Unicode-DFS-2016",
"MIT",
"Apache-2.0",
"ISC",
"BSD-2-Clause",
"BSD-2-Clause-FreeBSD",
"BSD-3-Clause",
"CC0-1.0",
"Unicode-DFS-2016",
]
copyleft = "deny"
allow-osi-fsf-free = "neither"
@ -59,29 +58,37 @@ highlight = "all"
# introduces it.
# spell-checker: disable
skip = [
# is-terminal
{ name = "hermit-abi", version = "0.3.1" },
# is-terminal
{ name = "rustix", version = "0.36.14" },
# is-terminal (via rustix)
{ name = "io-lifetimes", version = "1.0.5" },
# is-terminal
{ name = "linux-raw-sys", version = "0.1.4" },
# is-terminal
{ name = "windows-sys", version = "0.45.0" },
{ name = "windows-targets", version = "0.42.2" },
{ name = "windows_aarch64_gnullvm", version = "0.42.2" },
{ name = "windows_aarch64_msvc", version = "0.42.2" },
{ name = "windows_i686_gnu", version = "0.42.2" },
{ name = "windows_i686_msvc", version = "0.42.2" },
{ name = "windows_x86_64_gnu", version = "0.42.2" },
{ name = "windows_x86_64_gnullvm", version = "0.42.2" },
{ name = "windows_x86_64_msvc", version = "0.42.2" },
# tempfile
{ name = "redox_syscall", version = "0.3.5" },
# cpp_macros
{ name = "aho-corasick", version = "0.7.19" },
# procfs
{ name = "rustix", version = "0.36.15" },
# rustix
{ name = "linux-raw-sys", version = "0.1.4" },
{ name = "linux-raw-sys", version = "0.3.8" },
# tempfile
{ name = "rustix", version = "0.37.23" },
# various crates
{ name = "windows-sys", version = "0.45.0" },
# windows-sys
{ name = "windows-targets", version = "0.42.2" },
# windows-targets
{ name = "windows_aarch64_gnullvm", version = "0.42.2" },
# windows-targets
{ name = "windows_aarch64_msvc", version = "0.42.2" },
# windows-targets
{ name = "windows_i686_gnu", version = "0.42.2" },
# windows-targets
{ name = "windows_i686_msvc", version = "0.42.2" },
# windows-targets
{ name = "windows_x86_64_gnu", version = "0.42.2" },
# windows-targets
{ name = "windows_x86_64_gnullvm", version = "0.42.2" },
# windows-targets
{ name = "windows_x86_64_msvc", version = "0.42.2" },
# cpp_macros
{ name = "aho-corasick", version = "0.7.20" },
# various crates
{ name = "syn", version = "1.0.109" },
# various crates
{ name = "bitflags", version = "1.3.2" },
]
# spell-checker: enable

View file

@ -6,8 +6,8 @@ src = "src"
title = "uutils Documentation"
[output.html]
git-repository-url = "https://github.com/rust-lang/cargo/tree/master/src/doc/src"
git-repository-url = "https://github.com/uutils/coreutils/tree/main/docs/src"
[preprocessor.toc]
command = "mdbook-toc"
renderer = ["html"]
renderer = ["html"]

View file

@ -61,7 +61,20 @@ feature is adopted from [FreeBSD](https://www.freebsd.org/cgi/man.cgi?cut).
## `fmt`
`fmt` has additional flags for prefixes: `-P/--skip-prefix`, `-x/--exact-prefix`, and
`-X/--exact-skip-prefix`. With `-m/--preserve-headers`, an attempt is made to detect and preserve
mail headers in the input. `-q/--quick` breaks lines more quickly. And `-T/--tab-width` defines the
`fmt` has additional flags for prefixes: `-P`/`--skip-prefix`, `-x`/`--exact-prefix`, and
`-X`/`--exact-skip-prefix`. With `-m`/`--preserve-headers`, an attempt is made to detect and preserve
mail headers in the input. `-q`/`--quick` breaks lines more quickly. And `-T`/`--tab-width` defines the
number of spaces representing a tab when determining the line length.
## `seq`
`seq` provides `-t`/`--terminator` to set the terminator character.
## `ls`
GNU `ls` provides two ways to use a long listing format: `-l` and `--format=long`. We support a
third way: `--long`.
## `du`
`du` allows `birth` and `creation` as values for the `--time` argument to show the creation time.

View file

@ -37,7 +37,7 @@ apk update uutils-coreutils
### Arch
[![Arch package](https://repology.org/badge/version-for-repo/arch/uutils-coreutils.svg)](https://archlinux.org/packages/community/x86_64/uutils-coreutils/)
[![Arch package](https://repology.org/badge/version-for-repo/arch/uutils-coreutils.svg)](https://archlinux.org/packages/extra/x86_64/uutils-coreutils/)
```shell
pacman -S uutils-coreutils

4
docs/src/oranda.css Normal file
View file

@ -0,0 +1,4 @@
.logo {
display: block;
height: 170px;
}

13
oranda.json Normal file
View file

@ -0,0 +1,13 @@
{
"project": {
"name": "uutils coreutils"
},
"components": {
"changelog": true
},
"styles": {
"theme": "light",
"logo": "docs/src/logo.svg",
"additional_css": ["docs/src/oranda.css"]
}
}

View file

@ -212,8 +212,15 @@ fn gen_manpage<T: uucore::Args>(
fn gen_coreutils_app<T: uucore::Args>(util_map: &UtilityMap<T>) -> Command {
let mut command = Command::new("coreutils");
for (_, (_, sub_app)) in util_map {
command = command.subcommand(sub_app());
for (name, (_, sub_app)) in util_map {
// Recreate a small subcommand with only the relevant info
// (name & short description)
let about = sub_app()
.get_about()
.expect("Could not get the 'about'")
.to_string();
let sub_app = Command::new(name).about(about);
command = command.subcommand(sub_app);
}
command
}

View file

@ -2,7 +2,7 @@
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
// spell-checker:ignore tldr
// spell-checker:ignore tldr uuhelp
use clap::Command;
use std::collections::HashMap;
@ -178,7 +178,7 @@ impl<'a, 'b> MDWriter<'a, 'b> {
fn usage(&mut self) -> io::Result<()> {
if let Some(markdown) = &self.markdown {
let usage = help_parser::parse_usage(markdown);
let usage = uuhelp_parser::parse_usage(markdown);
let usage = usage.replace("{}", self.name);
writeln!(self.w, "\n```")?;
@ -191,7 +191,7 @@ impl<'a, 'b> MDWriter<'a, 'b> {
fn about(&mut self) -> io::Result<()> {
if let Some(markdown) = &self.markdown {
writeln!(self.w, "{}", help_parser::parse_about(markdown))
writeln!(self.w, "{}", uuhelp_parser::parse_about(markdown))
} else {
Ok(())
}
@ -199,7 +199,7 @@ impl<'a, 'b> MDWriter<'a, 'b> {
fn after_help(&mut self) -> io::Result<()> {
if let Some(markdown) = &self.markdown {
if let Some(after_help) = help_parser::parse_section("after help", markdown) {
if let Some(after_help) = uuhelp_parser::parse_section("after help", markdown) {
return writeln!(self.w, "\n\n{after_help}");
}
}

View file

@ -1,5 +0,0 @@
[package]
name = "help_parser"
version = "0.0.18"
edition = "2021"
license = "MIT"

View file

@ -1,6 +1,6 @@
[package]
name = "uu_arch"
version = "0.0.18"
version = "0.0.20"
authors = ["uutils developers"]
license = "MIT"
description = "arch ~ (uutils) display machine architecture"
@ -15,9 +15,9 @@ edition = "2021"
path = "src/arch.rs"
[dependencies]
platform-info = { workspace=true }
clap = { workspace=true }
uucore = { workspace=true }
platform-info = { workspace = true }
clap = { workspace = true }
uucore = { workspace = true }
[[bin]]
name = "arch"

View file

@ -1,6 +1,6 @@
[package]
name = "uu_base32"
version = "0.0.18"
version = "0.0.20"
authors = ["uutils developers"]
license = "MIT"
description = "base32 ~ (uutils) decode/encode input (base32-encoding)"
@ -15,8 +15,8 @@ edition = "2021"
path = "src/base32.rs"
[dependencies]
clap = { workspace=true }
uucore = { workspace=true, features = ["encoding"] }
clap = { workspace = true }
uucore = { workspace = true, features = ["encoding"] }
[[bin]]
name = "base32"

View file

@ -1,6 +1,6 @@
[package]
name = "uu_base64"
version = "0.0.18"
version = "0.0.20"
authors = ["uutils developers"]
license = "MIT"
description = "base64 ~ (uutils) decode/encode input (base64-encoding)"
@ -15,8 +15,8 @@ edition = "2021"
path = "src/base64.rs"
[dependencies]
uucore = { workspace=true, features = ["encoding"] }
uu_base32 = { workspace=true }
uucore = { workspace = true, features = ["encoding"] }
uu_base32 = { workspace = true }
[[bin]]
name = "base64"

View file

@ -1,6 +1,6 @@
[package]
name = "uu_basename"
version = "0.0.18"
version = "0.0.20"
authors = ["uutils developers"]
license = "MIT"
description = "basename ~ (uutils) display PATHNAME with leading directory components removed"
@ -15,8 +15,8 @@ edition = "2021"
path = "src/basename.rs"
[dependencies]
clap = { workspace=true }
uucore = { workspace=true }
clap = { workspace = true }
uucore = { workspace = true }
[[bin]]
name = "basename"

View file

@ -1,6 +1,6 @@
[package]
name = "uu_basenc"
version = "0.0.18"
version = "0.0.20"
authors = ["uutils developers"]
license = "MIT"
description = "basenc ~ (uutils) decode/encode input"
@ -15,9 +15,9 @@ edition = "2021"
path = "src/basenc.rs"
[dependencies]
clap = { workspace=true }
uucore = { workspace=true, features = ["encoding"] }
uu_base32 = { workspace=true }
clap = { workspace = true }
uucore = { workspace = true, features = ["encoding"] }
uu_base32 = { workspace = true }
[[bin]]
name = "basenc"

View file

@ -1,6 +1,6 @@
[package]
name = "uu_cat"
version = "0.0.18"
version = "0.0.20"
authors = ["uutils developers"]
license = "MIT"
description = "cat ~ (uutils) concatenate and display input"
@ -15,13 +15,13 @@ edition = "2021"
path = "src/cat.rs"
[dependencies]
clap = { workspace=true }
clap = { workspace = true }
thiserror = { workspace = true }
is-terminal = { workspace = true }
uucore = { workspace=true, features=["fs", "pipes"] }
uucore = { workspace = true, features = ["fs", "pipes"] }
[target.'cfg(unix)'.dependencies]
nix = { workspace=true }
nix = { workspace = true }
[[bin]]
name = "cat"

View file

@ -290,7 +290,6 @@ pub fn uu_app() -> Command {
.arg(
Arg::new(options::SHOW_NONPRINTING_TABS)
.short('t')
.long(options::SHOW_NONPRINTING_TABS)
.help("equivalent to -vT")
.action(ArgAction::SetTrue),
)

View file

@ -1,6 +1,6 @@
[package]
name = "uu_chcon"
version = "0.0.18"
version = "0.0.20"
authors = ["uutils developers"]
license = "MIT"
description = "chcon ~ (uutils) change file security context"
@ -14,12 +14,12 @@ edition = "2021"
path = "src/chcon.rs"
[dependencies]
clap = { workspace=true }
uucore = { workspace=true, features=["entries", "fs", "perms"] }
selinux = { workspace=true }
clap = { workspace = true }
uucore = { workspace = true, features = ["entries", "fs", "perms"] }
selinux = { workspace = true }
thiserror = { workspace = true }
libc = { workspace=true }
fts-sys = { workspace=true }
libc = { workspace = true }
fts-sys = { workspace = true }
[[bin]]
name = "chcon"

View file

@ -1,6 +1,6 @@
[package]
name = "uu_chgrp"
version = "0.0.18"
version = "0.0.20"
authors = ["uutils developers"]
license = "MIT"
description = "chgrp ~ (uutils) change the group ownership of FILE"
@ -15,8 +15,8 @@ edition = "2021"
path = "src/chgrp.rs"
[dependencies]
clap = { workspace=true }
uucore = { workspace=true, features=["entries", "fs", "perms"] }
clap = { workspace = true }
uucore = { workspace = true, features = ["entries", "fs", "perms"] }
[[bin]]
name = "chgrp"

View file

@ -1,6 +1,6 @@
[package]
name = "uu_chmod"
version = "0.0.18"
version = "0.0.20"
authors = ["uutils developers"]
license = "MIT"
description = "chmod ~ (uutils) change mode of FILE"
@ -15,9 +15,9 @@ edition = "2021"
path = "src/chmod.rs"
[dependencies]
clap = { workspace=true }
libc = { workspace=true }
uucore = { workspace=true, features=["fs", "mode"] }
clap = { workspace = true }
libc = { workspace = true }
uucore = { workspace = true, features = ["fs", "mode"] }
[[bin]]
name = "chmod"

View file

@ -1,6 +1,6 @@
[package]
name = "uu_chown"
version = "0.0.18"
version = "0.0.20"
authors = ["uutils developers"]
license = "MIT"
description = "chown ~ (uutils) change the ownership of FILE"
@ -15,8 +15,8 @@ edition = "2021"
path = "src/chown.rs"
[dependencies]
clap = { workspace=true }
uucore = { workspace=true, features=["entries", "fs", "perms"] }
clap = { workspace = true }
uucore = { workspace = true, features = ["entries", "fs", "perms"] }
[[bin]]
name = "chown"

View file

@ -1,6 +1,6 @@
[package]
name = "uu_chroot"
version = "0.0.18"
version = "0.0.20"
authors = ["uutils developers"]
license = "MIT"
description = "chroot ~ (uutils) run COMMAND under a new root directory"
@ -15,8 +15,8 @@ edition = "2021"
path = "src/chroot.rs"
[dependencies]
clap = { workspace=true }
uucore = { workspace=true, features=["entries", "fs"] }
clap = { workspace = true }
uucore = { workspace = true, features = ["entries", "fs"] }
[[bin]]
name = "chroot"

View file

@ -1,6 +1,6 @@
[package]
name = "uu_cksum"
version = "0.0.18"
version = "0.0.20"
authors = ["uutils developers"]
license = "MIT"
description = "cksum ~ (uutils) display CRC and size of input"
@ -15,9 +15,9 @@ edition = "2021"
path = "src/cksum.rs"
[dependencies]
clap = { workspace=true }
uucore = { workspace=true, features=["sum"] }
hex = { workspace=true }
clap = { workspace = true }
uucore = { workspace = true, features = ["sum"] }
hex = { workspace = true }
[[bin]]
name = "cksum"

View file

@ -1,6 +1,6 @@
[package]
name = "uu_comm"
version = "0.0.18"
version = "0.0.20"
authors = ["uutils developers"]
license = "MIT"
description = "comm ~ (uutils) compare sorted inputs"
@ -15,8 +15,8 @@ edition = "2021"
path = "src/comm.rs"
[dependencies]
clap = { workspace=true }
uucore = { workspace=true }
clap = { workspace = true }
uucore = { workspace = true }
[[bin]]
name = "comm"

View file

@ -1,10 +1,10 @@
[package]
name = "uu_cp"
version = "0.0.18"
version = "0.0.20"
authors = [
"Jordy Dickinson <jordy.dickinson@gmail.com>",
"Joshua S. Miller <jsmiller@uchicago.edu>",
"uutils developers",
"Jordy Dickinson <jordy.dickinson@gmail.com>",
"Joshua S. Miller <jsmiller@uchicago.edu>",
"uutils developers",
]
license = "MIT"
description = "cp ~ (uutils) copy SOURCE to DESTINATION"
@ -19,18 +19,18 @@ edition = "2021"
path = "src/cp.rs"
[dependencies]
clap = { workspace=true }
filetime = { workspace=true }
libc = { workspace=true }
quick-error = { workspace=true }
selinux = { workspace=true, optional=true }
uucore = { workspace=true, features=["entries", "fs", "perms", "mode"] }
walkdir = { workspace=true }
indicatif = { workspace=true }
clap = { workspace = true }
filetime = { workspace = true }
libc = { workspace = true }
quick-error = { workspace = true }
selinux = { workspace = true, optional = true }
uucore = { workspace = true, features = ["entries", "fs", "perms", "mode"] }
walkdir = { workspace = true }
indicatif = { workspace = true }
[target.'cfg(unix)'.dependencies]
xattr = { workspace=true }
exacl = { workspace=true, optional=true }
xattr = { workspace = true }
exacl = { workspace = true, optional = true }
[[bin]]
name = "cp"

View file

@ -43,7 +43,8 @@ use uucore::fs::{
};
use uucore::update_control::{self, UpdateMode};
use uucore::{
crash, format_usage, help_about, help_section, help_usage, prompt_yes, show_error, show_warning,
crash, format_usage, help_about, help_section, help_usage, prompt_yes, show_error,
show_warning, util_name,
};
use crate::copydir::copy_directory;
@ -229,10 +230,79 @@ pub struct Options {
backup_suffix: String,
target_dir: Option<PathBuf>,
update: UpdateMode,
debug: bool,
verbose: bool,
progress_bar: bool,
}
/// Enum representing various debug states of the offload and reflink actions.
#[derive(Debug)]
#[allow(dead_code)] // All of them are used on Linux
enum OffloadReflinkDebug {
Unknown,
No,
Yes,
Avoided,
Unsupported,
}
/// Enum representing various debug states of the sparse detection.
#[derive(Debug)]
#[allow(dead_code)] // silent for now until we use them
enum SparseDebug {
Unknown,
No,
Zeros,
SeekHole,
SeekHoleZeros,
Unsupported,
}
/// Struct that contains the debug state for each action in a file copy operation.
#[derive(Debug)]
struct CopyDebug {
offload: OffloadReflinkDebug,
reflink: OffloadReflinkDebug,
sparse_detection: SparseDebug,
}
impl OffloadReflinkDebug {
fn to_string(&self) -> &'static str {
match self {
Self::No => "no",
Self::Yes => "yes",
Self::Avoided => "avoided",
Self::Unsupported => "unsupported",
Self::Unknown => "unknown",
}
}
}
impl SparseDebug {
fn to_string(&self) -> &'static str {
match self {
Self::No => "no",
Self::Zeros => "zeros",
Self::SeekHole => "SEEK_HOLE",
Self::SeekHoleZeros => "SEEK_HOLE + zeros",
Self::Unsupported => "unsupported",
Self::Unknown => "unknown",
}
}
}
/// This function prints the debug information of a file copy operation if
/// no hard link or symbolic link is required, and data copy is required.
/// It prints the debug information of the offload, reflink, and sparse detection actions.
fn show_debug(copy_debug: &CopyDebug) {
println!(
"copy offload: {}, reflink: {}, sparse detection: {}",
copy_debug.offload.to_string(),
copy_debug.reflink.to_string(),
copy_debug.sparse_detection.to_string(),
);
}
const ABOUT: &str = help_about!("cp.md");
const USAGE: &str = help_usage!("cp.md");
const AFTER_HELP: &str = help_section!("after help", "cp.md");
@ -269,6 +339,7 @@ mod options {
pub const STRIP_TRAILING_SLASHES: &str = "strip-trailing-slashes";
pub const SYMBOLIC_LINK: &str = "symbolic-link";
pub const TARGET_DIRECTORY: &str = "target-directory";
pub const DEBUG: &str = "debug";
pub const VERBOSE: &str = "verbose";
}
@ -312,6 +383,7 @@ pub fn uu_app() -> Command {
backup_control::BACKUP_CONTROL_LONG_HELP
))
.infer_long_args(true)
.args_override_self(true)
.arg(
Arg::new(options::TARGET_DIRECTORY)
.short('t')
@ -369,6 +441,12 @@ pub fn uu_app() -> Command {
.help("remove any trailing slashes from each SOURCE argument")
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(options::DEBUG)
.long(options::DEBUG)
.help("explain how a file is copied. Implies -v")
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(options::VERBOSE)
.short('v')
@ -438,6 +516,7 @@ pub fn uu_app() -> Command {
PRESERVABLE_ATTRIBUTES,
))
.num_args(0..)
.require_equals(true)
.value_name("ATTR_LIST")
.overrides_with_all([
options::ARCHIVE,
@ -839,7 +918,8 @@ impl Options {
one_file_system: matches.get_flag(options::ONE_FILE_SYSTEM),
parents: matches.get_flag(options::PARENTS),
update: update_mode,
verbose: matches.get_flag(options::VERBOSE),
debug: matches.get_flag(options::DEBUG),
verbose: matches.get_flag(options::VERBOSE) || matches.get_flag(options::DEBUG),
strip_trailing_slashes: matches.get_flag(options::STRIP_TRAILING_SLASHES),
reflink_mode: {
if let Some(reflink) = matches.get_one::<String>(options::REFLINK) {
@ -1025,23 +1105,21 @@ fn preserve_hardlinks(
}
/// When handling errors, we don't always want to show them to the user. This function handles that.
/// If the error is printed, returns true, false otherwise.
fn show_error_if_needed(error: &Error) -> bool {
fn show_error_if_needed(error: &Error) {
match error {
// When using --no-clobber, we don't want to show
// an error message
Error::NotAllFilesCopied => (),
Error::NotAllFilesCopied => {
// Need to return an error code
}
Error::Skipped => {
// touch a b && echo "n"|cp -i a b && echo $?
// should return an error from GNU 9.2
return true;
}
_ => {
show_error!("{}", error);
return true;
}
}
false
}
/// Copy all `sources` to `target`. Returns an
@ -1098,9 +1176,8 @@ fn copy(sources: &[Source], target: &TargetSlice, options: &Options) -> CopyResu
options,
&mut symlinked_files,
) {
if show_error_if_needed(&error) {
non_fatal_errors = true;
}
show_error_if_needed(&error);
non_fatal_errors = true;
}
}
seen_sources.insert(source);
@ -1177,13 +1254,23 @@ fn copy_source(
}
impl OverwriteMode {
fn verify(&self, path: &Path) -> CopyResult<()> {
fn verify(&self, path: &Path, verbose: bool) -> CopyResult<()> {
match *self {
Self::NoClobber => Err(Error::NotAllFilesCopied),
Self::NoClobber => {
if verbose {
println!("skipped {}", path.quote());
} else {
eprintln!("{}: not replacing {}", util_name(), path.quote());
}
Err(Error::NotAllFilesCopied)
}
Self::Interactive(_) => {
if prompt_yes!("overwrite {}?", path.quote()) {
Ok(())
} else {
if verbose {
println!("skipped {}", path.quote());
}
Err(Error::Skipped)
}
}
@ -1391,7 +1478,7 @@ fn handle_existing_dest(
return Err(format!("{} and {} are the same file", source.quote(), dest.quote()).into());
}
options.overwrite.verify(dest)?;
options.overwrite.verify(dest, options.verbose)?;
let backup_path = backup_control::get_backup_path(options.backup, dest, &options.backup_suffix);
if let Some(backup_path) = backup_path {
@ -1749,11 +1836,11 @@ fn copy_helper(
File::create(dest).context(dest.display().to_string())?;
} else if source_is_fifo && options.recursive && !options.copy_contents {
#[cfg(unix)]
copy_fifo(dest, options.overwrite)?;
copy_fifo(dest, options.overwrite, options.verbose)?;
} else if source_is_symlink {
copy_link(source, dest, symlinked_files)?;
} else {
copy_on_write(
let copy_debug = copy_on_write(
source,
dest,
options.reflink_mode,
@ -1762,6 +1849,10 @@ fn copy_helper(
#[cfg(any(target_os = "linux", target_os = "android", target_os = "macos"))]
source_is_fifo,
)?;
if !options.attributes_only && options.debug {
show_debug(&copy_debug);
}
}
Ok(())
@ -1770,9 +1861,9 @@ fn copy_helper(
// "Copies" a FIFO by creating a new one. This workaround is because Rust's
// built-in fs::copy does not handle FIFOs (see rust-lang/rust/issues/79390).
#[cfg(unix)]
fn copy_fifo(dest: &Path, overwrite: OverwriteMode) -> CopyResult<()> {
fn copy_fifo(dest: &Path, overwrite: OverwriteMode, verbose: bool) -> CopyResult<()> {
if dest.exists() {
overwrite.verify(dest)?;
overwrite.verify(dest, verbose)?;
fs::remove_file(dest)?;
}

View file

@ -13,7 +13,7 @@ use quick_error::ResultExt;
use uucore::mode::get_umask;
use crate::{CopyResult, ReflinkMode, SparseMode};
use crate::{CopyDebug, CopyResult, OffloadReflinkDebug, ReflinkMode, SparseDebug, SparseMode};
// From /usr/include/linux/fs.h:
// #define FICLONE _IOW(0x94, 9, int)
@ -145,24 +145,51 @@ pub(crate) fn copy_on_write(
sparse_mode: SparseMode,
context: &str,
source_is_fifo: bool,
) -> CopyResult<()> {
) -> CopyResult<CopyDebug> {
let mut copy_debug = CopyDebug {
offload: OffloadReflinkDebug::Unknown,
reflink: OffloadReflinkDebug::Unsupported,
sparse_detection: SparseDebug::No,
};
let result = match (reflink_mode, sparse_mode) {
(ReflinkMode::Never, SparseMode::Always) => sparse_copy(source, dest),
(ReflinkMode::Never, _) => std::fs::copy(source, dest).map(|_| ()),
(ReflinkMode::Auto, SparseMode::Always) => sparse_copy(source, dest),
(ReflinkMode::Never, SparseMode::Always) => {
copy_debug.sparse_detection = SparseDebug::Zeros;
copy_debug.offload = OffloadReflinkDebug::Avoided;
copy_debug.reflink = OffloadReflinkDebug::No;
sparse_copy(source, dest)
}
(ReflinkMode::Never, _) => {
copy_debug.sparse_detection = SparseDebug::No;
copy_debug.reflink = OffloadReflinkDebug::No;
std::fs::copy(source, dest).map(|_| ())
}
(ReflinkMode::Auto, SparseMode::Always) => {
copy_debug.offload = OffloadReflinkDebug::Avoided;
copy_debug.sparse_detection = SparseDebug::Zeros;
copy_debug.reflink = OffloadReflinkDebug::Unsupported;
sparse_copy(source, dest)
}
(ReflinkMode::Auto, _) => {
copy_debug.sparse_detection = SparseDebug::No;
copy_debug.reflink = OffloadReflinkDebug::Unsupported;
if source_is_fifo {
copy_fifo_contents(source, dest).map(|_| ())
} else {
clone(source, dest, CloneFallback::FSCopy)
}
}
(ReflinkMode::Always, SparseMode::Auto) => clone(source, dest, CloneFallback::Error),
(ReflinkMode::Always, SparseMode::Auto) => {
copy_debug.sparse_detection = SparseDebug::No;
copy_debug.reflink = OffloadReflinkDebug::Yes;
clone(source, dest, CloneFallback::Error)
}
(ReflinkMode::Always, _) => {
return Err("`--reflink=always` can be used only with --sparse=auto".into())
}
};
result.context(context)?;
Ok(())
Ok(copy_debug)
}

View file

@ -11,7 +11,7 @@ use std::path::Path;
use quick_error::ResultExt;
use crate::{CopyResult, ReflinkMode, SparseMode};
use crate::{CopyDebug, CopyResult, OffloadReflinkDebug, ReflinkMode, SparseDebug, SparseMode};
/// Copies `source` to `dest` using copy-on-write if possible.
///
@ -24,10 +24,15 @@ pub(crate) fn copy_on_write(
sparse_mode: SparseMode,
context: &str,
source_is_fifo: bool,
) -> CopyResult<()> {
) -> CopyResult<CopyDebug> {
if sparse_mode != SparseMode::Auto {
return Err("--sparse is only supported on linux".to_string().into());
}
let mut copy_debug = CopyDebug {
offload: OffloadReflinkDebug::Unknown,
reflink: OffloadReflinkDebug::Unsupported,
sparse_detection: SparseDebug::Unsupported,
};
// Extract paths in a form suitable to be passed to a syscall.
// The unwrap() is safe because they come from the command-line and so contain non nul
@ -72,6 +77,7 @@ pub(crate) fn copy_on_write(
return Err(format!("failed to clone {source:?} from {dest:?}: {error}").into())
}
_ => {
copy_debug.reflink = OffloadReflinkDebug::Yes;
if source_is_fifo {
let mut src_file = File::open(source)?;
let mut dst_file = File::create(dest)?;
@ -83,5 +89,5 @@ pub(crate) fn copy_on_write(
};
}
Ok(())
Ok(copy_debug)
}

View file

@ -8,7 +8,7 @@ use std::path::Path;
use quick_error::ResultExt;
use crate::{CopyResult, ReflinkMode, SparseMode};
use crate::{CopyDebug, CopyResult, OffloadReflinkDebug, ReflinkMode, SparseDebug, SparseMode};
/// Copies `source` to `dest` for systems without copy-on-write
pub(crate) fn copy_on_write(
@ -17,7 +17,7 @@ pub(crate) fn copy_on_write(
reflink_mode: ReflinkMode,
sparse_mode: SparseMode,
context: &str,
) -> CopyResult<()> {
) -> CopyResult<CopyDebug> {
if reflink_mode != ReflinkMode::Never {
return Err("--reflink is only supported on linux and macOS"
.to_string()
@ -26,8 +26,12 @@ pub(crate) fn copy_on_write(
if sparse_mode != SparseMode::Auto {
return Err("--sparse is only supported on linux".to_string().into());
}
let copy_debug = CopyDebug {
offload: OffloadReflinkDebug::Unsupported,
reflink: OffloadReflinkDebug::Unsupported,
sparse_detection: SparseDebug::Unsupported,
};
fs::copy(source, dest).context(context)?;
Ok(())
Ok(copy_debug)
}

View file

@ -1,6 +1,6 @@
[package]
name = "uu_csplit"
version = "0.0.18"
version = "0.0.20"
authors = ["uutils developers"]
license = "MIT"
description = "csplit ~ (uutils) Output pieces of FILE separated by PATTERN(s) to files 'xx00', 'xx01', ..., and output byte counts of each piece to standard output"
@ -15,10 +15,10 @@ edition = "2021"
path = "src/csplit.rs"
[dependencies]
clap = { workspace=true }
clap = { workspace = true }
thiserror = { workspace = true }
regex = { workspace=true }
uucore = { workspace=true, features=["entries", "fs"] }
regex = { workspace = true }
uucore = { workspace = true, features = ["entries", "fs"] }
[[bin]]
name = "csplit"

View file

@ -127,7 +127,7 @@ where
I: Iterator<Item = (usize, io::Result<String>)>,
{
// split the file based on patterns
for pattern in patterns.into_iter() {
for pattern in patterns {
let pattern_as_str = pattern.to_string();
let is_skip = matches!(pattern, patterns::Pattern::SkipToMatch(_, _, _));
match pattern {
@ -552,6 +552,109 @@ where
}
}
#[uucore::main]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let args = args.collect_ignore();
let matches = uu_app().try_get_matches_from(args)?;
// get the file to split
let file_name = matches.get_one::<String>(options::FILE).unwrap();
// get the patterns to split on
let patterns: Vec<String> = matches
.get_many::<String>(options::PATTERN)
.unwrap()
.map(|s| s.to_string())
.collect();
let patterns = patterns::get_patterns(&patterns[..])?;
let options = CsplitOptions::new(&matches);
if file_name == "-" {
let stdin = io::stdin();
Ok(csplit(&options, patterns, stdin.lock())?)
} else {
let file = File::open(file_name)
.map_err_context(|| format!("cannot access {}", file_name.quote()))?;
let file_metadata = file
.metadata()
.map_err_context(|| format!("cannot access {}", file_name.quote()))?;
if !file_metadata.is_file() {
return Err(CsplitError::NotRegularFile(file_name.to_string()).into());
}
Ok(csplit(&options, patterns, BufReader::new(file))?)
}
}
pub fn uu_app() -> Command {
Command::new(uucore::util_name())
.version(crate_version!())
.about(ABOUT)
.override_usage(format_usage(USAGE))
.infer_long_args(true)
.arg(
Arg::new(options::SUFFIX_FORMAT)
.short('b')
.long(options::SUFFIX_FORMAT)
.value_name("FORMAT")
.help("use sprintf FORMAT instead of %02d"),
)
.arg(
Arg::new(options::PREFIX)
.short('f')
.long(options::PREFIX)
.value_name("PREFIX")
.help("use PREFIX instead of 'xx'"),
)
.arg(
Arg::new(options::KEEP_FILES)
.short('k')
.long(options::KEEP_FILES)
.help("do not remove output files on errors")
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(options::SUPPRESS_MATCHED)
.long(options::SUPPRESS_MATCHED)
.help("suppress the lines matching PATTERN")
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(options::DIGITS)
.short('n')
.long(options::DIGITS)
.value_name("DIGITS")
.help("use specified number of digits instead of 2"),
)
.arg(
Arg::new(options::QUIET)
.short('s')
.long(options::QUIET)
.visible_alias("silent")
.help("do not print counts of output file sizes")
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(options::ELIDE_EMPTY_FILES)
.short('z')
.long(options::ELIDE_EMPTY_FILES)
.help("remove empty output files")
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(options::FILE)
.hide(true)
.required(true)
.value_hint(clap::ValueHint::FilePath),
)
.arg(
Arg::new(options::PATTERN)
.hide(true)
.action(clap::ArgAction::Append)
.required(true),
)
.after_help(AFTER_HELP)
}
#[cfg(test)]
mod tests {
use super::*;
@ -714,106 +817,3 @@ mod tests {
assert!(input_splitter.next().is_none());
}
}
#[uucore::main]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let args = args.collect_ignore();
let matches = uu_app().try_get_matches_from(args)?;
// get the file to split
let file_name = matches.get_one::<String>(options::FILE).unwrap();
// get the patterns to split on
let patterns: Vec<String> = matches
.get_many::<String>(options::PATTERN)
.unwrap()
.map(|s| s.to_string())
.collect();
let patterns = patterns::get_patterns(&patterns[..])?;
let options = CsplitOptions::new(&matches);
if file_name == "-" {
let stdin = io::stdin();
Ok(csplit(&options, patterns, stdin.lock())?)
} else {
let file = File::open(file_name)
.map_err_context(|| format!("cannot access {}", file_name.quote()))?;
let file_metadata = file
.metadata()
.map_err_context(|| format!("cannot access {}", file_name.quote()))?;
if !file_metadata.is_file() {
return Err(CsplitError::NotRegularFile(file_name.to_string()).into());
}
Ok(csplit(&options, patterns, BufReader::new(file))?)
}
}
pub fn uu_app() -> Command {
Command::new(uucore::util_name())
.version(crate_version!())
.about(ABOUT)
.override_usage(format_usage(USAGE))
.infer_long_args(true)
.arg(
Arg::new(options::SUFFIX_FORMAT)
.short('b')
.long(options::SUFFIX_FORMAT)
.value_name("FORMAT")
.help("use sprintf FORMAT instead of %02d"),
)
.arg(
Arg::new(options::PREFIX)
.short('f')
.long(options::PREFIX)
.value_name("PREFIX")
.help("use PREFIX instead of 'xx'"),
)
.arg(
Arg::new(options::KEEP_FILES)
.short('k')
.long(options::KEEP_FILES)
.help("do not remove output files on errors")
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(options::SUPPRESS_MATCHED)
.long(options::SUPPRESS_MATCHED)
.help("suppress the lines matching PATTERN")
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(options::DIGITS)
.short('n')
.long(options::DIGITS)
.value_name("DIGITS")
.help("use specified number of digits instead of 2"),
)
.arg(
Arg::new(options::QUIET)
.short('s')
.long(options::QUIET)
.visible_alias("silent")
.help("do not print counts of output file sizes")
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(options::ELIDE_EMPTY_FILES)
.short('z')
.long(options::ELIDE_EMPTY_FILES)
.help("remove empty output files")
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(options::FILE)
.hide(true)
.required(true)
.value_hint(clap::ValueHint::FilePath),
)
.arg(
Arg::new(options::PATTERN)
.hide(true)
.action(clap::ArgAction::Append)
.required(true),
)
.after_help(AFTER_HELP)
}

View file

@ -1,6 +1,6 @@
[package]
name = "uu_cut"
version = "0.0.18"
version = "0.0.20"
authors = ["uutils developers"]
license = "MIT"
description = "cut ~ (uutils) display byte/field columns of input lines"
@ -15,11 +15,11 @@ edition = "2021"
path = "src/cut.rs"
[dependencies]
clap = { workspace=true }
uucore = { workspace=true }
memchr = { workspace=true }
bstr = { workspace=true }
is-terminal = { workspace=true }
clap = { workspace = true }
uucore = { workspace = true }
memchr = { workspace = true }
bstr = { workspace = true }
is-terminal = { workspace = true }
[[bin]]
name = "cut"

View file

@ -1,7 +1,7 @@
# spell-checker:ignore humantime
# spell-checker:ignore datetime
[package]
name = "uu_date"
version = "0.0.18"
version = "0.0.20"
authors = ["uutils developers"]
license = "MIT"
description = "date ~ (uutils) display or set the current time"
@ -16,18 +16,19 @@ edition = "2021"
path = "src/date.rs"
[dependencies]
chrono = { workspace=true }
#/ TODO: check if we can avoid chrono+time
time = { workspace=true }
clap = { workspace=true }
uucore = { workspace=true }
humantime_to_duration = { workspace=true }
chrono = { workspace = true }
clap = { workspace = true }
uucore = { workspace = true }
parse_datetime = { workspace = true }
[target.'cfg(unix)'.dependencies]
libc = { workspace=true }
libc = { workspace = true }
[target.'cfg(windows)'.dependencies]
windows-sys = { workspace=true, features = ["Win32_Foundation", "Win32_System_SystemInformation"] }
windows-sys = { workspace = true, features = [
"Win32_Foundation",
"Win32_System_SystemInformation",
] }
[[bin]]
name = "date"

View file

@ -6,10 +6,10 @@
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
// spell-checker:ignore (chrono) Datelike Timelike ; (format) DATEFILE MMDDhhmm ; (vars) datetime datetimes humantime
// spell-checker:ignore (chrono) Datelike Timelike ; (format) DATEFILE MMDDhhmm ; (vars) datetime datetimes
use chrono::format::{Item, StrftimeItems};
use chrono::{DateTime, Duration as ChronoDuration, FixedOffset, Local, Offset, Utc};
use chrono::{DateTime, Duration, FixedOffset, Local, Offset, Utc};
#[cfg(windows)]
use chrono::{Datelike, Timelike};
use clap::{crate_version, Arg, ArgAction, Command};
@ -18,7 +18,6 @@ use libc::{clock_settime, timespec, CLOCK_REALTIME};
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::PathBuf;
use time::Duration;
use uucore::display::Quotable;
#[cfg(not(any(target_os = "redox")))]
use uucore::error::FromIo;
@ -27,14 +26,13 @@ use uucore::{format_usage, help_about, help_usage, show};
#[cfg(windows)]
use windows_sys::Win32::{Foundation::SYSTEMTIME, System::SystemInformation::SetSystemTime};
use uucore::shortcut_value_parser::ShortcutValueParser;
// Options
const DATE: &str = "date";
const HOURS: &str = "hours";
const MINUTES: &str = "minutes";
const SECONDS: &str = "seconds";
const HOUR: &str = "hour";
const MINUTE: &str = "minute";
const SECOND: &str = "second";
const NS: &str = "ns";
const ABOUT: &str = help_about!("date.md");
@ -111,9 +109,9 @@ enum Iso8601Format {
impl<'a> From<&'a str> for Iso8601Format {
fn from(s: &str) -> Self {
match s {
HOURS | HOUR => Self::Hours,
MINUTES | MINUTE => Self::Minutes,
SECONDS | SECOND => Self::Seconds,
HOURS => Self::Hours,
MINUTES => Self::Minutes,
SECONDS => Self::Seconds,
NS => Self::Ns,
DATE => Self::Date,
// Note: This is caught by clap via `possible_values`
@ -132,7 +130,7 @@ impl<'a> From<&'a str> for Rfc3339Format {
fn from(s: &str) -> Self {
match s {
DATE => Self::Date,
SECONDS | SECOND => Self::Seconds,
SECONDS => Self::Seconds,
NS => Self::Ns,
// Should be caught by clap
_ => panic!("Invalid format: {s}"),
@ -171,7 +169,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
};
let date_source = if let Some(date) = matches.get_one::<String>(OPT_DATE) {
if let Ok(duration) = humantime_to_duration::from_str(date.as_str()) {
if let Ok(duration) = parse_datetime::from_str(date.as_str()) {
DateSource::Human(duration)
} else {
DateSource::Custom(date.into())
@ -226,13 +224,10 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let iter = std::iter::once(date);
Box::new(iter)
}
DateSource::Human(ref input) => {
// Get the current DateTime<FixedOffset> and convert the input time::Duration to chrono::Duration
// for things like "1 year ago"
DateSource::Human(relative_time) => {
// Get the current DateTime<FixedOffset> for things like "1 year ago"
let current_time = DateTime::<FixedOffset>::from(Local::now());
let input_chrono = ChronoDuration::seconds(input.as_seconds_f32() as i64)
+ ChronoDuration::nanoseconds(input.subsec_nanoseconds() as i64);
let iter = std::iter::once(Ok(current_time + input_chrono));
let iter = std::iter::once(Ok(current_time + relative_time));
Box::new(iter)
}
DateSource::File(ref path) => {
@ -321,7 +316,9 @@ pub fn uu_app() -> Command {
.short('I')
.long(OPT_ISO_8601)
.value_name("FMT")
.value_parser([DATE, HOUR, HOURS, MINUTE, MINUTES, SECOND, SECONDS, NS])
.value_parser(ShortcutValueParser::new([
DATE, HOURS, MINUTES, SECONDS, NS,
]))
.num_args(0..=1)
.default_missing_value(OPT_DATE)
.help(ISO_8601_HELP_STRING),
@ -337,7 +334,7 @@ pub fn uu_app() -> Command {
Arg::new(OPT_RFC_3339)
.long(OPT_RFC_3339)
.value_name("FMT")
.value_parser([DATE, SECOND, SECONDS, NS])
.value_parser(ShortcutValueParser::new([DATE, SECONDS, NS]))
.help(RFC_3339_HELP_STRING),
)
.arg(

View file

@ -1,6 +1,6 @@
[package]
name = "uu_dd"
version = "0.0.18"
version = "0.0.20"
authors = ["uutils developers"]
license = "MIT"
description = "dd ~ (uutils) copy and convert files"
@ -15,16 +15,16 @@ edition = "2021"
path = "src/dd.rs"
[dependencies]
clap = { workspace=true }
gcd = { workspace=true }
libc = { workspace=true }
uucore = { workspace=true, features=["memo"] }
clap = { workspace = true }
gcd = { workspace = true }
libc = { workspace = true }
uucore = { workspace = true, features = ["memo"] }
[target.'cfg(any(target_os = "linux"))'.dependencies]
nix = { workspace=true, features = ["fs"] }
nix = { workspace = true, features = ["fs"] }
[target.'cfg(any(target_os = "linux", target_os = "android"))'.dependencies]
signal-hook = { workspace=true }
signal-hook = { workspace = true }
[[bin]]
name = "dd"

View file

@ -13,7 +13,7 @@ Copy, and optionally convert, a file system resource
### Operands
- `Bs=BYTES` : read and write up to BYTES bytes at a time (default: 512);
- `bs=BYTES` : read and write up to BYTES bytes at a time (default: 512);
overwrites `ibs` and `obs`.
- `cbs=BYTES` : the 'conversion block size' in bytes. Applies to the
`conv=block`, and `conv=unblock` operations.
@ -114,7 +114,7 @@ Copy, and optionally convert, a file system resource
### General Flags
- `Direct` : use direct I/O for data.
- `direct` : use direct I/O for data.
- `directory` : fail unless the given input (if used as an iflag) or
output (if used as an oflag) is a directory.
- `dsync` : use synchronized I/O for data.

View file

@ -36,9 +36,12 @@ use std::os::unix::{
io::{AsRawFd, FromRawFd},
};
use std::path::Path;
use std::sync::mpsc;
use std::sync::{
atomic::{AtomicBool, Ordering::Relaxed},
mpsc, Arc,
};
use std::thread;
use std::time;
use std::time::{Duration, Instant};
use clap::{crate_version, Arg, Command};
use gcd::Gcd;
@ -75,6 +78,45 @@ struct Settings {
status: Option<StatusLevel>,
}
/// A timer which triggers on a given interval
///
/// After being constructed with [`Alarm::with_interval`], [`Alarm::is_triggered`]
/// will return true once per the given [`Duration`].
///
/// Can be cloned, but the trigger status is shared across all instances so only
/// the first caller each interval will yield true.
///
/// When all instances are dropped the background thread will exit on the next interval.
#[derive(Debug, Clone)]
pub struct Alarm {
interval: Duration,
trigger: Arc<AtomicBool>,
}
impl Alarm {
pub fn with_interval(interval: Duration) -> Self {
let trigger = Arc::new(AtomicBool::default());
let weak_trigger = Arc::downgrade(&trigger);
thread::spawn(move || {
while let Some(trigger) = weak_trigger.upgrade() {
thread::sleep(interval);
trigger.store(true, Relaxed);
}
});
Self { interval, trigger }
}
pub fn is_triggered(&self) -> bool {
self.trigger.swap(false, Relaxed)
}
pub fn get_interval(&self) -> Duration {
self.interval
}
}
/// A number in blocks or bytes
///
/// Some values (seek, skip, iseek, oseek) can have values either in blocks or in bytes.
@ -760,7 +802,7 @@ fn dd_copy(mut i: Input, mut o: Output) -> std::io::Result<()> {
// of its report includes the throughput in bytes per second,
// which requires knowing how long the process has been
// running.
let start = time::Instant::now();
let start = Instant::now();
// A good buffer size for reading.
//
@ -780,7 +822,6 @@ fn dd_copy(mut i: Input, mut o: Output) -> std::io::Result<()> {
// information.
let (prog_tx, rx) = mpsc::channel();
let output_thread = thread::spawn(gen_prog_updater(rx, i.settings.status));
let mut progress_as_secs = 0;
// Optimization: if no blocks are to be written, then don't
// bother allocating any buffers.
@ -813,6 +854,12 @@ fn dd_copy(mut i: Input, mut o: Output) -> std::io::Result<()> {
// This is the max size needed.
let mut buf = vec![BUF_INIT_BYTE; bsize];
// Spawn a timer thread to provide a scheduled signal indicating when we
// should send an update of our progress to the reporting thread.
//
// This avoids the need to query the OS monotonic clock for every block.
let alarm = Alarm::with_interval(Duration::from_secs(1));
// Index in the input file where we are reading bytes and in
// the output file where we are writing bytes.
//
@ -871,9 +918,8 @@ fn dd_copy(mut i: Input, mut o: Output) -> std::io::Result<()> {
// error.
rstat += rstat_update;
wstat += wstat_update;
let prog_update = ProgUpdate::new(rstat, wstat, start.elapsed(), false);
if prog_update.duration.as_secs() >= progress_as_secs {
progress_as_secs = prog_update.duration.as_secs() + 1;
if alarm.is_triggered() {
let prog_update = ProgUpdate::new(rstat, wstat, start.elapsed(), false);
prog_tx.send(prog_update).unwrap_or(());
}
}
@ -885,7 +931,7 @@ fn finalize<T>(
output: &mut Output,
rstat: ReadStat,
wstat: WriteStat,
start: time::Instant,
start: Instant,
prog_tx: &mpsc::Sender<ProgUpdate>,
output_thread: thread::JoinHandle<T>,
) -> std::io::Result<()> {

View file

@ -498,7 +498,7 @@ mod tests {
fn prog_update_write(n: u128) -> ProgUpdate {
ProgUpdate {
read_stat: Default::default(),
read_stat: ReadStat::default(),
write_stat: WriteStat {
bytes_total: n,
..Default::default()
@ -510,8 +510,8 @@ mod tests {
fn prog_update_duration(duration: Duration) -> ProgUpdate {
ProgUpdate {
read_stat: Default::default(),
write_stat: Default::default(),
read_stat: ReadStat::default(),
write_stat: WriteStat::default(),
duration,
complete: false,
}
@ -557,8 +557,8 @@ mod tests {
#[test]
fn test_prog_update_write_prog_line() {
let prog_update = ProgUpdate {
read_stat: Default::default(),
write_stat: Default::default(),
read_stat: ReadStat::default(),
write_stat: WriteStat::default(),
duration: Duration::new(1, 0), // one second
complete: false,
};
@ -613,8 +613,8 @@ mod tests {
#[test]
fn write_transfer_stats() {
let prog_update = ProgUpdate {
read_stat: Default::default(),
write_stat: Default::default(),
read_stat: ReadStat::default(),
write_stat: WriteStat::default(),
duration: Duration::new(1, 0), // one second
complete: false,
};
@ -634,8 +634,8 @@ mod tests {
fn write_final_transfer_stats() {
// Tests the formatting of the final statistics written after a progress line.
let prog_update = ProgUpdate {
read_stat: Default::default(),
write_stat: Default::default(),
read_stat: ReadStat::default(),
write_stat: WriteStat::default(),
duration: Duration::new(1, 0), // one second
complete: false,
};

View file

@ -1,6 +1,6 @@
[package]
name = "uu_df"
version = "0.0.18"
version = "0.0.20"
authors = ["uutils developers"]
license = "MIT"
description = "df ~ (uutils) display file system information"
@ -15,9 +15,12 @@ edition = "2021"
path = "src/df.rs"
[dependencies]
clap = { workspace=true }
uucore = { workspace=true, features=["libc", "fsext"] }
unicode-width = { workspace=true }
clap = { workspace = true }
uucore = { workspace = true, features = ["libc", "fsext"] }
unicode-width = { workspace = true }
[dev-dependencies]
tempfile = "3"
[[bin]]
name = "df"

View file

@ -746,7 +746,7 @@ mod tests {
#[test]
fn test_remote_included() {
let opt = Default::default();
let opt = Options::default();
let m = mount_info("ext4", "/mnt/foo", true, false);
assert!(is_included(&m, &opt));
}
@ -773,7 +773,7 @@ mod tests {
#[test]
fn test_dummy_excluded() {
let opt = Default::default();
let opt = Options::default();
let m = mount_info("ext4", "/mnt/foo", false, true);
assert!(!is_included(&m, &opt));
}
@ -864,11 +864,11 @@ mod tests {
mod filter_mount_list {
use crate::filter_mount_list;
use crate::{filter_mount_list, Options};
#[test]
fn test_empty() {
let opt = Default::default();
let opt = Options::default();
let mount_infos = vec![];
assert!(filter_mount_list(mount_infos, &opt).is_empty());
}

View file

@ -42,7 +42,7 @@ pub(crate) struct Filesystem {
/// This function returns the element of `mounts` on which `path` is
/// mounted. If there are no matches, this function returns
/// [`None`]. If there are two or more matches, then the single
/// [`MountInfo`] with the longest mount directory is returned.
/// [`MountInfo`] with the device name corresponding to the entered path.
///
/// If `canonicalize` is `true`, then the `path` is canonicalized
/// before checking whether it matches any mount directories.
@ -68,9 +68,19 @@ where
path.as_ref().to_path_buf()
};
// Find the potential mount point that matches entered path
let maybe_mount_point = mounts
.iter()
.find(|mi| mi.dev_name.eq(&path.to_string_lossy()));
// Create pair MountInfo, canonicalized device name
// TODO Abstract from accessing real filesystem to
// make code more testable
.map(|m| (m, std::fs::canonicalize(&m.dev_name)))
// Ignore non existing paths
.filter(|m| m.1.is_ok())
.map(|m| (m.0, m.1.ok().unwrap()))
// Try to find canonicalized device name corresponding to entered path
.find(|m| m.1.eq(&path))
.map(|m| m.0);
maybe_mount_point.or_else(|| {
mounts
@ -148,12 +158,12 @@ mod tests {
// Create a fake `MountInfo` with the given directory name.
fn mount_info(mount_dir: &str) -> MountInfo {
MountInfo {
dev_id: Default::default(),
dev_name: Default::default(),
fs_type: Default::default(),
dev_id: String::default(),
dev_name: String::default(),
fs_type: String::default(),
mount_dir: String::from(mount_dir),
mount_option: Default::default(),
mount_root: Default::default(),
mount_option: String::default(),
mount_root: String::default(),
remote: Default::default(),
dummy: Default::default(),
}
@ -211,10 +221,16 @@ mod tests {
#[test]
fn test_dev_name_match() {
let tmp = tempfile::TempDir::new().expect("Failed to create temp dir");
let dev_name = std::fs::canonicalize(tmp.path())
.expect("Failed to canonicalize tmp path")
.to_string_lossy()
.to_string();
let mut mount_info = mount_info("/foo");
mount_info.dev_name = "/dev/sda2".to_string();
mount_info.dev_name = dev_name.clone();
let mounts = [mount_info];
let actual = mount_info_from_path(&mounts, "/dev/sda2", false).unwrap();
let actual = mount_info_from_path(&mounts, dev_name, false).unwrap();
assert!(mount_info_eq(actual, &mounts[0]));
}
}

View file

@ -513,7 +513,7 @@ mod tests {
#[test]
fn test_default_header() {
let options = Default::default();
let options = Options::default();
assert_eq!(
Header::get_headers(&options),
vec!(

View file

@ -1,6 +1,6 @@
[package]
name = "uu_dir"
version = "0.0.18"
version = "0.0.20"
authors = ["uutils developers"]
license = "MIT"
description = "shortcut to ls -C -b"
@ -15,9 +15,9 @@ edition = "2021"
path = "src/dir.rs"
[dependencies]
clap = { workspace=true, features = ["env"] }
uucore = { workspace=true, features=["entries", "fs"] }
uu_ls = { workspace=true }
clap = { workspace = true, features = ["env"] }
uucore = { workspace = true, features = ["entries", "fs"] }
uu_ls = { workspace = true }
[[bin]]
name = "dir"

View file

@ -1,6 +1,6 @@
[package]
name = "uu_dircolors"
version = "0.0.18"
version = "0.0.20"
authors = ["uutils developers"]
license = "MIT"
description = "dircolors ~ (uutils) display commands to set LS_COLORS"
@ -15,8 +15,8 @@ edition = "2021"
path = "src/dircolors.rs"
[dependencies]
clap = { workspace=true }
uucore = { workspace=true }
clap = { workspace = true }
uucore = { workspace = true }
[[bin]]
name = "dircolors"

View file

@ -1,6 +1,6 @@
[package]
name = "uu_dirname"
version = "0.0.18"
version = "0.0.20"
authors = ["uutils developers"]
license = "MIT"
description = "dirname ~ (uutils) display parent directory of PATHNAME"
@ -15,8 +15,8 @@ edition = "2021"
path = "src/dirname.rs"
[dependencies]
clap = { workspace=true }
uucore = { workspace=true }
clap = { workspace = true }
uucore = { workspace = true }
[[bin]]
name = "dirname"

View file

@ -1,6 +1,6 @@
[package]
name = "uu_du"
version = "0.0.18"
version = "0.0.20"
authors = ["uutils developers"]
license = "MIT"
description = "du ~ (uutils) display disk usage"
@ -15,14 +15,17 @@ edition = "2021"
path = "src/du.rs"
[dependencies]
chrono = { workspace=true }
chrono = { workspace = true }
# For the --exclude & --exclude-from options
glob = { workspace=true }
clap = { workspace=true }
uucore = { workspace=true }
glob = { workspace = true }
clap = { workspace = true }
uucore = { workspace = true }
[target.'cfg(target_os = "windows")'.dependencies]
windows-sys = { workspace=true, features = ["Win32_Storage_FileSystem", "Win32_Foundation"] }
windows-sys = { workspace = true, features = [
"Win32_Storage_FileSystem",
"Win32_Foundation",
] }
[[bin]]
name = "du"

View file

@ -145,7 +145,7 @@ impl Stat {
return Ok(Self {
path: path.to_path_buf(),
is_dir: metadata.is_dir(),
size: metadata.len(),
size: if path.is_dir() { 0 } else { metadata.len() },
blocks: metadata.blocks(),
inodes: 1,
inode: Some(file_info),
@ -162,7 +162,7 @@ impl Stat {
Ok(Self {
path: path.to_path_buf(),
is_dir: metadata.is_dir(),
size: metadata.len(),
size: if path.is_dir() { 0 } else { metadata.len() },
blocks: size_on_disk / 1024 * 2,
inode: file_info,
inodes: 1,

View file

@ -1,6 +1,6 @@
[package]
name = "uu_echo"
version = "0.0.18"
version = "0.0.20"
authors = ["uutils developers"]
license = "MIT"
description = "echo ~ (uutils) display TEXT"
@ -15,8 +15,8 @@ edition = "2021"
path = "src/echo.rs"
[dependencies]
clap = { workspace=true }
uucore = { workspace=true }
clap = { workspace = true }
uucore = { workspace = true }
[[bin]]
name = "echo"

10
src/uu/env/Cargo.toml vendored
View file

@ -1,6 +1,6 @@
[package]
name = "uu_env"
version = "0.0.18"
version = "0.0.20"
authors = ["uutils developers"]
license = "MIT"
description = "env ~ (uutils) set each NAME to VALUE in the environment and run COMMAND"
@ -15,12 +15,12 @@ edition = "2021"
path = "src/env.rs"
[dependencies]
clap = { workspace=true }
rust-ini = { workspace=true }
uucore = { workspace=true, features=["signals"]}
clap = { workspace = true }
rust-ini = { workspace = true }
uucore = { workspace = true, features = ["signals"] }
[target.'cfg(unix)'.dependencies]
nix = { workspace=true, features = ["signal"] }
nix = { workspace = true, features = ["signal"] }
[[bin]]

View file

@ -1,6 +1,6 @@
[package]
name = "uu_expand"
version = "0.0.18"
version = "0.0.20"
authors = ["uutils developers"]
license = "MIT"
description = "expand ~ (uutils) convert input tabs to spaces"
@ -15,9 +15,9 @@ edition = "2021"
path = "src/expand.rs"
[dependencies]
clap = { workspace=true }
unicode-width = { workspace=true }
uucore = { workspace=true }
clap = { workspace = true }
unicode-width = { workspace = true }
uucore = { workspace = true }
[[bin]]
name = "expand"

View file

@ -1,6 +1,6 @@
[package]
name = "uu_expr"
version = "0.0.18"
version = "0.0.20"
authors = ["uutils developers"]
license = "MIT"
description = "expr ~ (uutils) display the value of EXPRESSION"
@ -15,11 +15,11 @@ edition = "2021"
path = "src/expr.rs"
[dependencies]
clap = { workspace=true }
num-bigint = { workspace=true }
num-traits = { workspace=true }
onig = { workspace=true }
uucore = { workspace=true }
clap = { workspace = true }
num-bigint = { workspace = true }
num-traits = { workspace = true }
onig = { workspace = true }
uucore = { workspace = true }
[[bin]]
name = "expr"

View file

@ -1,6 +1,6 @@
[package]
name = "uu_factor"
version = "0.0.18"
version = "0.0.20"
authors = ["uutils developers"]
license = "MIT"
description = "factor ~ (uutils) display the prime factors of each NUMBER"
@ -12,15 +12,15 @@ categories = ["command-line-utilities"]
edition = "2021"
[build-dependencies]
num-traits = { workspace=true } # used in src/numerics.rs, which is included by build.rs
num-traits = { workspace = true } # used in src/numerics.rs, which is included by build.rs
[dependencies]
clap = { workspace=true }
coz = { workspace=true, optional = true }
num-traits = { workspace=true }
rand = { workspace=true }
smallvec = { workspace=true }
uucore = { workspace=true }
clap = { workspace = true }
coz = { workspace = true, optional = true }
num-traits = { workspace = true }
rand = { workspace = true }
smallvec = { workspace = true }
uucore = { workspace = true }
[dev-dependencies]
quickcheck = "1.0.3"

View file

@ -1,6 +1,6 @@
[package]
name = "uu_false"
version = "0.0.18"
version = "0.0.20"
authors = ["uutils developers"]
license = "MIT"
description = "false ~ (uutils) do nothing and fail"
@ -15,8 +15,8 @@ edition = "2021"
path = "src/false.rs"
[dependencies]
clap = { workspace=true }
uucore = { workspace=true }
clap = { workspace = true }
uucore = { workspace = true }
[[bin]]
name = "false"

View file

@ -1,6 +1,6 @@
[package]
name = "uu_fmt"
version = "0.0.18"
version = "0.0.20"
authors = ["uutils developers"]
license = "MIT"
description = "fmt ~ (uutils) reformat each paragraph of input"
@ -15,9 +15,9 @@ edition = "2021"
path = "src/fmt.rs"
[dependencies]
clap = { workspace=true }
unicode-width = { workspace=true }
uucore = { workspace=true }
clap = { workspace = true }
unicode-width = { workspace = true }
uucore = { workspace = true }
[[bin]]
name = "fmt"

View file

@ -598,7 +598,7 @@ impl<'a> Iterator for WordSplit<'a> {
self.prev_punct && (before_tab.is_some() || word_start_relative > 1);
// now record whether this word ends in punctuation
self.prev_punct = match self.string[..self.position].chars().rev().next() {
self.prev_punct = match self.string[..self.position].chars().next_back() {
Some(ch) => WordSplit::is_punctuation(ch),
_ => panic!("fatal: expected word not to be empty"),
};

View file

@ -1,6 +1,6 @@
[package]
name = "uu_fold"
version = "0.0.18"
version = "0.0.20"
authors = ["uutils developers"]
license = "MIT"
description = "fold ~ (uutils) wrap each line of input"
@ -15,8 +15,8 @@ edition = "2021"
path = "src/fold.rs"
[dependencies]
clap = { workspace=true }
uucore = { workspace=true }
clap = { workspace = true }
uucore = { workspace = true }
[[bin]]
name = "fold"

View file

@ -1,6 +1,6 @@
[package]
name = "uu_groups"
version = "0.0.18"
version = "0.0.20"
authors = ["uutils developers"]
license = "MIT"
description = "groups ~ (uutils) display group memberships for USERNAME"
@ -15,8 +15,8 @@ edition = "2021"
path = "src/groups.rs"
[dependencies]
clap = { workspace=true }
uucore = { workspace=true, features=["entries", "process"] }
clap = { workspace = true }
uucore = { workspace = true, features = ["entries", "process"] }
[[bin]]
name = "groups"

View file

@ -1,6 +1,6 @@
[package]
name = "uu_hashsum"
version = "0.0.18"
version = "0.0.20"
authors = ["uutils developers"]
license = "MIT"
description = "hashsum ~ (uutils) display or check input digests"
@ -15,11 +15,11 @@ edition = "2021"
path = "src/hashsum.rs"
[dependencies]
clap = { workspace=true }
uucore = { workspace=true, features=["sum"] }
memchr = { workspace=true }
regex = { workspace=true }
hex = { workspace=true }
clap = { workspace = true }
uucore = { workspace = true, features = ["sum"] }
memchr = { workspace = true }
regex = { workspace = true }
hex = { workspace = true }
[[bin]]
name = "hashsum"

View file

@ -306,7 +306,7 @@ pub fn uumain(mut args: impl uucore::Args) -> UResult<()> {
// if there is no program name for some reason, default to "hashsum"
let program = args.next().unwrap_or_else(|| OsString::from(NAME));
let binary_name = Path::new(&program)
.file_name()
.file_stem()
.unwrap_or_else(|| OsStr::new(NAME))
.to_string_lossy();

View file

@ -1,6 +1,6 @@
[package]
name = "uu_head"
version = "0.0.18"
version = "0.0.20"
authors = ["uutils developers"]
license = "MIT"
description = "head ~ (uutils) display the first lines of input"
@ -15,9 +15,9 @@ edition = "2021"
path = "src/head.rs"
[dependencies]
clap = { workspace=true }
memchr = { workspace=true }
uucore = { workspace=true, features=["ringbuffer", "lines"] }
clap = { workspace = true }
memchr = { workspace = true }
uucore = { workspace = true, features = ["ringbuffer", "lines"] }
[[bin]]
name = "head"

View file

@ -575,7 +575,7 @@ mod tests {
}
#[test]
fn test_options_correct_defaults() {
let opts: HeadOptions = Default::default();
let opts = HeadOptions::default();
assert!(!opts.verbose);
assert!(!opts.quiet);
@ -622,12 +622,10 @@ mod tests {
#[test]
#[cfg(target_os = "linux")]
fn test_arg_iterate_bad_encoding() {
#[allow(clippy::invalid_utf8_in_unchecked)]
let invalid = unsafe { std::str::from_utf8_unchecked(b"\x80\x81") };
use std::os::unix::ffi::OsStringExt;
let invalid = OsString::from_vec(vec![b'\x80', b'\x81']);
// this arises from a conversion from OsString to &str
assert!(
arg_iterate(vec![OsString::from("head"), OsString::from(invalid)].into_iter()).is_err()
);
assert!(arg_iterate(vec![OsString::from("head"), invalid].into_iter()).is_err());
}
#[test]
fn read_early_exit() {

View file

@ -114,7 +114,13 @@ pub fn parse_num(src: &str) -> Result<(u64, bool), ParseSizeError> {
return Err(ParseSizeError::ParseFailure(src.to_string()));
}
parse_size(size_string).map(|n| (n, all_but_last))
// remove leading zeros so that size is interpreted as decimal, not octal
let trimmed_string = size_string.trim_start_matches('0');
if trimmed_string.is_empty() {
Ok((0, all_but_last))
} else {
parse_size(trimmed_string).map(|n| (n, all_but_last))
}
}
#[cfg(test)]

View file

@ -1,6 +1,6 @@
[package]
name = "uu_hostid"
version = "0.0.18"
version = "0.0.20"
authors = ["uutils developers"]
license = "MIT"
description = "hostid ~ (uutils) display the numeric identifier of the current host"
@ -15,9 +15,9 @@ edition = "2021"
path = "src/hostid.rs"
[dependencies]
clap = { workspace=true }
libc = { workspace=true }
uucore = { workspace=true }
clap = { workspace = true }
libc = { workspace = true }
uucore = { workspace = true }
[[bin]]
name = "hostid"

View file

@ -1,6 +1,6 @@
[package]
name = "uu_hostname"
version = "0.0.18"
version = "0.0.20"
authors = ["uutils developers"]
license = "MIT"
description = "hostname ~ (uutils) display or set the host name of the current host"
@ -15,12 +15,15 @@ edition = "2021"
path = "src/hostname.rs"
[dependencies]
clap = { workspace=true }
clap = { workspace = true }
hostname = { version = "0.3", features = ["set"] }
uucore = { workspace=true, features=["wide"] }
uucore = { workspace = true, features = ["wide"] }
[target.'cfg(target_os = "windows")'.dependencies]
windows-sys = { workspace=true, features = ["Win32_Networking_WinSock", "Win32_Foundation"] }
windows-sys = { workspace = true, features = [
"Win32_Networking_WinSock",
"Win32_Foundation",
] }
[[bin]]
name = "hostname"

View file

@ -1,6 +1,6 @@
[package]
name = "uu_id"
version = "0.0.18"
version = "0.0.20"
authors = ["uutils developers"]
license = "MIT"
description = "id ~ (uutils) display user and group information for USER"
@ -15,9 +15,9 @@ edition = "2021"
path = "src/id.rs"
[dependencies]
clap = { workspace=true }
uucore = { workspace=true, features=["entries", "process"] }
selinux = { workspace=true, optional=true }
clap = { workspace = true }
uucore = { workspace = true, features = ["entries", "process"] }
selinux = { workspace = true, optional = true }
[[bin]]
name = "id"

View file

@ -1,10 +1,7 @@
[package]
name = "uu_install"
version = "0.0.18"
authors = [
"Ben Eills <ben@beneills.com>",
"uutils developers",
]
version = "0.0.20"
authors = ["Ben Eills <ben@beneills.com>", "uutils developers"]
license = "MIT"
description = "install ~ (uutils) copy files from SOURCE to DESTINATION (with specified attributes)"
@ -18,14 +15,11 @@ edition = "2021"
path = "src/install.rs"
[dependencies]
clap = { workspace=true }
filetime = { workspace=true }
file_diff = { workspace=true }
libc = { workspace=true }
uucore = { workspace=true, features=["fs", "mode", "perms", "entries"] }
[dev-dependencies]
time = { workspace=true }
clap = { workspace = true }
filetime = { workspace = true }
file_diff = { workspace = true }
libc = { workspace = true }
uucore = { workspace = true, features = ["fs", "mode", "perms", "entries"] }
[[bin]]
name = "install"

View file

@ -1,6 +1,6 @@
[package]
name = "uu_join"
version = "0.0.18"
version = "0.0.20"
authors = ["uutils developers"]
license = "MIT"
description = "join ~ (uutils) merge lines from inputs with matching join fields"
@ -15,9 +15,9 @@ edition = "2021"
path = "src/join.rs"
[dependencies]
clap = { workspace=true }
uucore = { workspace=true }
memchr = { workspace=true }
clap = { workspace = true }
uucore = { workspace = true }
memchr = { workspace = true }
[[bin]]
name = "join"

View file

@ -1,6 +1,6 @@
[package]
name = "uu_kill"
version = "0.0.18"
version = "0.0.20"
authors = ["uutils developers"]
license = "MIT"
description = "kill ~ (uutils) send a signal to a process"
@ -15,9 +15,9 @@ edition = "2021"
path = "src/kill.rs"
[dependencies]
clap = { workspace=true }
nix = { workspace=true, features = ["signal"] }
uucore = { workspace=true, features=["signals"] }
clap = { workspace = true }
nix = { workspace = true, features = ["signal"] }
uucore = { workspace = true, features = ["signals"] }
[[bin]]
name = "kill"

View file

@ -1,6 +1,6 @@
[package]
name = "uu_link"
version = "0.0.18"
version = "0.0.20"
authors = ["uutils developers"]
license = "MIT"
description = "link ~ (uutils) create a hard (file system) link to FILE"
@ -15,8 +15,8 @@ edition = "2021"
path = "src/link.rs"
[dependencies]
clap = { workspace=true }
uucore = { workspace=true }
clap = { workspace = true }
uucore = { workspace = true }
[[bin]]
name = "link"

View file

@ -1,6 +1,6 @@
[package]
name = "uu_ln"
version = "0.0.18"
version = "0.0.20"
authors = ["uutils developers"]
license = "MIT"
description = "ln ~ (uutils) create a (file system) link to TARGET"
@ -15,8 +15,8 @@ edition = "2021"
path = "src/ln.rs"
[dependencies]
clap = { workspace=true }
uucore = { workspace=true, features=["fs"] }
clap = { workspace = true }
uucore = { workspace = true, features = ["fs"] }
[[bin]]
name = "ln"

View file

@ -1,6 +1,6 @@
[package]
name = "uu_logname"
version = "0.0.18"
version = "0.0.20"
authors = ["uutils developers"]
license = "MIT"
description = "logname ~ (uutils) display the login name of the current user"
@ -15,9 +15,9 @@ edition = "2021"
path = "src/logname.rs"
[dependencies]
libc = { workspace=true }
clap = { workspace=true }
uucore = { workspace=true }
libc = { workspace = true }
clap = { workspace = true }
uucore = { workspace = true }
[[bin]]
name = "logname"

View file

@ -1,6 +1,6 @@
[package]
name = "uu_ls"
version = "0.0.18"
version = "0.0.20"
authors = ["uutils developers"]
license = "MIT"
description = "ls ~ (uutils) display directory contents"
@ -15,18 +15,18 @@ edition = "2021"
path = "src/ls.rs"
[dependencies]
clap = { workspace=true, features = ["env"] }
chrono = { workspace=true }
unicode-width = { workspace=true }
number_prefix = { workspace=true }
term_grid = { workspace=true }
terminal_size = { workspace=true }
glob = { workspace=true }
lscolors = { workspace=true }
uucore = { workspace=true, features = ["entries", "fs"] }
once_cell = { workspace=true }
is-terminal = { workspace=true }
selinux = { workspace=true, optional = true }
clap = { workspace = true, features = ["env"] }
chrono = { workspace = true }
unicode-width = { workspace = true }
number_prefix = { workspace = true }
term_grid = { workspace = true }
terminal_size = { workspace = true }
glob = { workspace = true }
lscolors = { workspace = true }
uucore = { workspace = true, features = ["entries", "fs"] }
once_cell = { workspace = true }
is-terminal = { workspace = true }
selinux = { workspace = true, optional = true }
[[bin]]
name = "ls"

View file

@ -17,6 +17,8 @@ use lscolors::LsColors;
use number_prefix::NumberPrefix;
use once_cell::unsync::OnceCell;
use std::collections::HashSet;
use std::num::IntErrorKind;
#[cfg(windows)]
use std::os::windows::fs::MetadataExt;
use std::{
@ -294,6 +296,7 @@ enum Sort {
Time,
Version,
Extension,
Width,
}
#[derive(PartialEq)]
@ -496,6 +499,7 @@ fn extract_sort(options: &clap::ArgMatches) -> Sort {
"size" => Sort::Size,
"version" => Sort::Version,
"extension" => Sort::Extension,
"width" => Sort::Width,
// below should never happen as clap already restricts the values.
_ => unreachable!("Invalid field for --sort"),
}
@ -655,6 +659,19 @@ fn extract_indicator_style(options: &clap::ArgMatches) -> IndicatorStyle {
}
}
fn parse_width(s: &str) -> Result<u16, LsError> {
let radix = match s.starts_with('0') && s.len() > 1 {
true => 8,
false => 10,
};
match u16::from_str_radix(s, radix) {
Ok(x) => Ok(x),
Err(e) => match e.kind() {
IntErrorKind::PosOverflow => Ok(u16::MAX),
_ => Err(LsError::InvalidLineWidth(s.into())),
},
}
}
impl Config {
#[allow(clippy::cognitive_complexity)]
pub fn from(options: &clap::ArgMatches) -> UResult<Self> {
@ -793,20 +810,7 @@ impl Config {
};
let width = match options.get_one::<String>(options::WIDTH) {
Some(x) => {
if x.starts_with('0') && x.len() > 1 {
// Read number as octal
match u16::from_str_radix(x, 8) {
Ok(v) => v,
Err(_) => return Err(LsError::InvalidLineWidth(x.into()).into()),
}
} else {
match x.parse::<u16>() {
Ok(u) => u,
Err(_) => return Err(LsError::InvalidLineWidth(x.into()).into()),
}
}
}
Some(x) => parse_width(x)?,
None => match terminal_size::terminal_size() {
Some((width, _)) => width.0,
None => match std::env::var_os("COLUMNS") {
@ -1200,6 +1204,7 @@ pub fn uu_app() -> Command {
Arg::new(options::quoting::LITERAL)
.short('N')
.long(options::quoting::LITERAL)
.alias("l")
.help("Use literal quoting style. Equivalent to `--quoting-style=literal`")
.overrides_with_all([
options::QUOTING_STYLE,
@ -1322,9 +1327,9 @@ pub fn uu_app() -> Command {
.arg(
Arg::new(options::SORT)
.long(options::SORT)
.help("Sort by <field>: name, none (-U), time (-t), size (-S) or extension (-X)")
.help("Sort by <field>: name, none (-U), time (-t), size (-S), extension (-X) or width")
.value_name("field")
.value_parser(["name", "none", "time", "size", "version", "extension"])
.value_parser(["name", "none", "time", "size", "version", "extension", "width"])
.require_equals(true)
.overrides_with_all([
options::SORT,
@ -1929,14 +1934,22 @@ fn sort_entries(entries: &mut [PathData], config: &Config, out: &mut BufWriter<S
Sort::Size => entries.sort_by_key(|k| Reverse(k.md(out).map(|md| md.len()).unwrap_or(0))),
// The default sort in GNU ls is case insensitive
Sort::Name => entries.sort_by(|a, b| a.display_name.cmp(&b.display_name)),
Sort::Version => entries
.sort_by(|a, b| version_cmp(&a.p_buf.to_string_lossy(), &b.p_buf.to_string_lossy())),
Sort::Version => entries.sort_by(|a, b| {
version_cmp(&a.p_buf.to_string_lossy(), &b.p_buf.to_string_lossy())
.then(a.p_buf.to_string_lossy().cmp(&b.p_buf.to_string_lossy()))
}),
Sort::Extension => entries.sort_by(|a, b| {
a.p_buf
.extension()
.cmp(&b.p_buf.extension())
.then(a.p_buf.file_stem().cmp(&b.p_buf.file_stem()))
}),
Sort::Width => entries.sort_by(|a, b| {
a.display_name
.len()
.cmp(&b.display_name.len())
.then(a.display_name.cmp(&b.display_name))
}),
Sort::None => {}
}
@ -3000,6 +3013,20 @@ fn display_inode(metadata: &Metadata) -> String {
#[allow(unused_variables)]
fn get_security_context(config: &Config, p_buf: &Path, must_dereference: bool) -> String {
let substitute_string = "?".to_string();
// If we must dereference, ensure that the symlink is actually valid even if the system
// does not support SELinux.
// Conforms to the GNU coreutils where a dangling symlink results in exit code 1.
if must_dereference {
match get_metadata(p_buf, must_dereference) {
Err(err) => {
// The Path couldn't be dereferenced, so return early and set exit code 1
// to indicate a minor error
show!(LsError::IOErrorContext(err, p_buf.to_path_buf(), false));
return substitute_string;
}
Ok(md) => (),
}
}
if config.selinux_supported {
#[cfg(feature = "selinux")]
{

View file

@ -1,6 +1,6 @@
[package]
name = "uu_mkdir"
version = "0.0.18"
version = "0.0.20"
authors = ["uutils developers"]
license = "MIT"
description = "mkdir ~ (uutils) create DIRECTORY"
@ -15,8 +15,8 @@ edition = "2021"
path = "src/mkdir.rs"
[dependencies]
clap = { workspace=true }
uucore = { workspace=true, features=["fs", "mode"] }
clap = { workspace = true }
uucore = { workspace = true, features = ["fs", "mode"] }
[[bin]]
name = "mkdir"

View file

@ -1,6 +1,6 @@
[package]
name = "uu_mkfifo"
version = "0.0.18"
version = "0.0.20"
authors = ["uutils developers"]
license = "MIT"
description = "mkfifo ~ (uutils) create FIFOs (named pipes)"
@ -15,9 +15,9 @@ edition = "2021"
path = "src/mkfifo.rs"
[dependencies]
clap = { workspace=true }
libc = { workspace=true }
uucore = { workspace=true }
clap = { workspace = true }
libc = { workspace = true }
uucore = { workspace = true }
[[bin]]
name = "mkfifo"

View file

@ -1,6 +1,6 @@
[package]
name = "uu_mknod"
version = "0.0.18"
version = "0.0.20"
authors = ["uutils developers"]
license = "MIT"
description = "mknod ~ (uutils) create special file NAME of TYPE"
@ -16,9 +16,9 @@ name = "uu_mknod"
path = "src/mknod.rs"
[dependencies]
clap = { workspace=true }
libc = { workspace=true }
uucore = { workspace=true, features=["mode"] }
clap = { workspace = true }
libc = { workspace = true }
uucore = { workspace = true, features = ["mode"] }
[[bin]]
name = "mknod"

View file

@ -1,6 +1,6 @@
[package]
name = "uu_mktemp"
version = "0.0.18"
version = "0.0.20"
authors = ["uutils developers"]
license = "MIT"
description = "mktemp ~ (uutils) create and display a temporary file or directory from TEMPLATE"
@ -15,10 +15,10 @@ edition = "2021"
path = "src/mktemp.rs"
[dependencies]
clap = { workspace=true }
rand = { workspace=true }
tempfile = { workspace=true }
uucore = { workspace=true }
clap = { workspace = true }
rand = { workspace = true }
tempfile = { workspace = true }
uucore = { workspace = true }
[[bin]]
name = "mktemp"

View file

@ -8,7 +8,7 @@
// spell-checker:ignore (paths) GPGHome findxs
use clap::{crate_version, Arg, ArgAction, ArgMatches, Command};
use clap::{builder::ValueParser, crate_version, Arg, ArgAction, ArgMatches, Command};
use uucore::display::{println_verbatim, Quotable};
use uucore::error::{FromIo, UError, UResult, UUsageError};
use uucore::{format_usage, help_about, help_usage};
@ -38,6 +38,7 @@ static OPT_DRY_RUN: &str = "dry-run";
static OPT_QUIET: &str = "quiet";
static OPT_SUFFIX: &str = "suffix";
static OPT_TMPDIR: &str = "tmpdir";
static OPT_P: &str = "p";
static OPT_T: &str = "t";
static ARG_TEMPLATE: &str = "template";
@ -130,7 +131,7 @@ struct Options {
/// The directory in which to create the temporary file.
///
/// If `None`, the file will be created in the current directory.
tmpdir: Option<String>,
tmpdir: Option<PathBuf>,
/// The suffix to append to the temporary file, if any.
suffix: Option<String>,
@ -142,72 +143,32 @@ struct Options {
template: String,
}
/// Decide whether the argument to `--tmpdir` should actually be the template.
///
/// This function is required to work around a limitation of `clap`,
/// the command-line argument parsing library. In case the command
/// line is
///
/// ```sh
/// mktemp --tmpdir XXX
/// ```
///
/// the program should behave like
///
/// ```sh
/// mktemp --tmpdir=${TMPDIR:-/tmp} XXX
/// ```
///
/// However, `clap` thinks that `XXX` is the value of the `--tmpdir`
/// option. This function returns `true` in this case and `false`
/// in all other cases.
fn is_tmpdir_argument_actually_the_template(matches: &ArgMatches) -> bool {
if !matches.contains_id(ARG_TEMPLATE) {
if let Some(tmpdir) = matches.get_one::<String>(OPT_TMPDIR) {
if !Path::new(tmpdir).is_dir() && tmpdir.contains("XXX") {
return true;
}
}
}
false
}
impl Options {
fn from(matches: &ArgMatches) -> Self {
// Special case to work around a limitation of `clap`; see
// `is_tmpdir_argument_actually_the_template()` for more
// information.
//
// Fixed in clap 3
// See https://github.com/clap-rs/clap/pull/1587
let (tmpdir, template) = if is_tmpdir_argument_actually_the_template(matches) {
let tmpdir = Some(env::temp_dir().display().to_string());
let template = matches.get_one::<String>(OPT_TMPDIR).unwrap().to_string();
(tmpdir, template)
} else {
let tmpdir = matches
.get_one::<PathBuf>(OPT_TMPDIR)
.or_else(|| matches.get_one::<PathBuf>(OPT_P))
.cloned();
let (tmpdir, template) = match matches.get_one::<String>(ARG_TEMPLATE) {
// If no template argument is given, `--tmpdir` is implied.
match matches.get_one::<String>(ARG_TEMPLATE) {
None => {
let tmpdir = match matches.get_one::<String>(OPT_TMPDIR) {
None => Some(env::temp_dir().display().to_string()),
Some(tmpdir) => Some(tmpdir.to_string()),
};
let template = DEFAULT_TEMPLATE;
(tmpdir, template.to_string())
}
Some(template) => {
let tmpdir = if env::var(TMPDIR_ENV_VAR).is_ok() && matches.get_flag(OPT_T) {
env::var(TMPDIR_ENV_VAR).ok()
} else if matches.contains_id(OPT_TMPDIR) {
matches.get_one::<String>(OPT_TMPDIR).map(String::from)
} else if matches.get_flag(OPT_T) {
// mktemp -t foo.xxx should export in TMPDIR
Some(env::temp_dir().display().to_string())
} else {
matches.get_one::<String>(OPT_TMPDIR).map(String::from)
};
(tmpdir, template.to_string())
}
None => {
let tmpdir = Some(tmpdir.unwrap_or_else(env::temp_dir));
let template = DEFAULT_TEMPLATE;
(tmpdir, template.to_string())
}
Some(template) => {
let tmpdir = if env::var(TMPDIR_ENV_VAR).is_ok() && matches.get_flag(OPT_T) {
env::var_os(TMPDIR_ENV_VAR).map(|t| t.into())
} else if tmpdir.is_some() {
tmpdir
} else if matches.get_flag(OPT_T) || matches.contains_id(OPT_TMPDIR) {
// If --tmpdir is given without an argument, or -t is given
// export in TMPDIR
Some(env::temp_dir())
} else {
None
};
(tmpdir, template.to_string())
}
};
Self {
@ -372,7 +333,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
if env::var("POSIXLY_CORRECT").is_ok() {
// If POSIXLY_CORRECT was set, template MUST be the last argument.
if is_tmpdir_argument_actually_the_template(&matches) || matches.contains_id(ARG_TEMPLATE) {
if matches.contains_id(ARG_TEMPLATE) {
// Template argument was provided, check if was the last one.
if args.last().unwrap() != &options.template {
return Err(Box::new(MkTempError::TooManyTemplates));
@ -444,8 +405,16 @@ pub fn uu_app() -> Command {
.value_name("SUFFIX"),
)
.arg(
Arg::new(OPT_TMPDIR)
Arg::new(OPT_P)
.short('p')
.help("short form of --tmpdir")
.value_name("DIR")
.num_args(1)
.value_parser(ValueParser::path_buf())
.value_hint(clap::ValueHint::DirPath),
)
.arg(
Arg::new(OPT_TMPDIR)
.long(OPT_TMPDIR)
.help(
"interpret TEMPLATE relative to DIR; if DIR is not specified, use \
@ -457,6 +426,10 @@ pub fn uu_app() -> Command {
// Allows use of default argument just by setting --tmpdir. Else,
// use provided input to generate tmpdir
.num_args(0..=1)
// Require an equals to avoid ambiguity if no tmpdir is supplied
.require_equals(true)
.overrides_with(OPT_P)
.value_parser(ValueParser::path_buf())
.value_hint(clap::ValueHint::DirPath),
)
.arg(

View file

@ -1,6 +1,6 @@
[package]
name = "uu_more"
version = "0.0.18"
version = "0.0.20"
authors = ["uutils developers"]
license = "MIT"
description = "more ~ (uutils) input perusal filter"
@ -15,15 +15,15 @@ edition = "2021"
path = "src/more.rs"
[dependencies]
clap = { workspace=true }
uucore = { workspace=true }
crossterm = { workspace=true }
is-terminal = { workspace=true }
unicode-width = { workspace=true }
unicode-segmentation = { workspace=true }
clap = { workspace = true }
uucore = { workspace = true }
crossterm = { workspace = true }
is-terminal = { workspace = true }
unicode-width = { workspace = true }
unicode-segmentation = { workspace = true }
[target.'cfg(all(unix, not(target_os = "fuchsia")))'.dependencies]
nix = { workspace=true }
nix = { workspace = true }
[[bin]]
name = "more"

View file

@ -14,10 +14,10 @@ use std::{
time::Duration,
};
use clap::{crate_version, Arg, ArgAction, ArgMatches, Command};
use clap::{crate_version, value_parser, Arg, ArgAction, ArgMatches, Command};
use crossterm::event::KeyEventKind;
use crossterm::{
cursor::MoveTo,
cursor::{MoveTo, MoveUp},
event::{self, Event, KeyCode, KeyEvent, KeyModifiers},
execute, queue,
style::Attribute,
@ -50,19 +50,37 @@ pub mod options {
pub const FILES: &str = "files";
}
const MULTI_FILE_TOP_PROMPT: &str = "::::::::::::::\n{}\n::::::::::::::\n";
const MULTI_FILE_TOP_PROMPT: &str = "\r::::::::::::::\n\r{}\n\r::::::::::::::\n";
struct Options {
silent: bool,
clean_print: bool,
from_line: usize,
lines: Option<u16>,
print_over: bool,
silent: bool,
squeeze: bool,
}
impl Options {
fn from(matches: &ArgMatches) -> Self {
let lines = match (
matches.get_one::<u16>(options::LINES).copied(),
matches.get_one::<u16>(options::NUMBER).copied(),
) {
// We add 1 to the number of lines to display because the last line
// is used for the banner
(Some(number), _) if number > 0 => Some(number + 1),
(None, Some(number)) if number > 0 => Some(number + 1),
(_, _) => None,
};
let from_line = match matches.get_one::<usize>(options::FROM_LINE).copied() {
Some(number) if number > 1 => number - 1,
_ => 0,
};
Self {
clean_print: matches.get_flag(options::CLEAN_PRINT),
from_line,
lines,
print_over: matches.get_flag(options::PRINT_OVER),
silent: matches.get_flag(options::SILENT),
squeeze: matches.get_flag(options::SQUEEZE),
@ -78,7 +96,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
Err(e) => return Err(e.into()),
};
let options = Options::from(&matches);
let mut options = Options::from(&matches);
let mut buff = String::new();
@ -103,9 +121,6 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
format!("cannot open {}: No such file or directory", file.quote()),
));
}
if length > 1 {
buff.push_str(&MULTI_FILE_TOP_PROMPT.replace("{}", file.to_str().unwrap()));
}
let opened_file = match File::open(file) {
Err(why) => {
terminal::disable_raw_mode().unwrap();
@ -118,14 +133,21 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
};
let mut reader = BufReader::new(opened_file);
reader.read_to_string(&mut buff).unwrap();
more(&buff, &mut stdout, next_file.copied(), &options)?;
more(
&buff,
&mut stdout,
length > 1,
file.to_str(),
next_file.copied(),
&mut options,
)?;
buff.clear();
}
reset_term(&mut stdout);
} else if !std::io::stdin().is_terminal() {
stdin().read_to_string(&mut buff).unwrap();
let mut stdout = setup_term();
more(&buff, &mut stdout, None, &options)?;
more(&buff, &mut stdout, false, None, None, &mut options)?;
reset_term(&mut stdout);
} else {
return Err(UUsageError::new(1, "bad usage"));
@ -167,6 +189,38 @@ pub fn uu_app() -> Command {
.help("Squeeze multiple blank lines into one")
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(options::PLAIN)
.short('u')
.long(options::PLAIN)
.action(ArgAction::SetTrue)
.hide(true),
)
.arg(
Arg::new(options::FROM_LINE)
.short('F')
.long(options::FROM_LINE)
.num_args(1)
.value_name("number")
.value_parser(value_parser!(usize))
.help("Display file beginning from line number"),
)
.arg(
Arg::new(options::LINES)
.short('n')
.long(options::LINES)
.value_name("number")
.num_args(1)
.value_parser(value_parser!(u16).range(0..))
.help("The number of lines per screen full"),
)
.arg(
Arg::new(options::NUMBER)
.long(options::NUMBER)
.num_args(1)
.value_parser(value_parser!(u16).range(0..))
.help("Same as --lines"),
)
// The commented arguments below are unimplemented:
/*
.arg(
@ -181,37 +235,6 @@ pub fn uu_app() -> Command {
.long(options::NO_PAUSE)
.help("Suppress pause after form feed"),
)
.arg(
Arg::new(options::PLAIN)
.short('u')
.long(options::PLAIN)
.help("Suppress underlining and bold"),
)
.arg(
Arg::new(options::LINES)
.short('n')
.long(options::LINES)
.value_name("number")
.takes_value(true)
.help("The number of lines per screen full"),
)
.arg(
Arg::new(options::NUMBER)
.allow_hyphen_values(true)
.long(options::NUMBER)
.required(false)
.takes_value(true)
.help("Same as --lines"),
)
.arg(
Arg::new(options::FROM_LINE)
.short('F')
.allow_hyphen_values(true)
.required(false)
.takes_value(true)
.value_name("number")
.help("Display file beginning from line number"),
)
.arg(
Arg::new(options::PATTERN)
.short('P')
@ -260,15 +283,36 @@ fn reset_term(_: &mut usize) {}
fn more(
buff: &str,
stdout: &mut Stdout,
multiple_file: bool,
file: Option<&str>,
next_file: Option<&str>,
options: &Options,
options: &mut Options,
) -> UResult<()> {
let (cols, rows) = terminal::size().unwrap();
let (cols, mut rows) = terminal::size().unwrap();
if let Some(number) = options.lines {
rows = number;
}
let lines = break_buff(buff, usize::from(cols));
let mut pager = Pager::new(rows, lines, next_file, options);
if multiple_file {
execute!(stdout, terminal::Clear(terminal::ClearType::CurrentLine)).unwrap();
stdout.write_all(
MULTI_FILE_TOP_PROMPT
.replace("{}", file.unwrap_or_default())
.as_bytes(),
)?;
pager.content_rows -= 3;
}
pager.draw(stdout, None);
if pager.should_close() {
if multiple_file {
options.from_line = 0;
pager.content_rows += 3;
}
if pager.should_close() && next_file.is_none() {
return Ok(());
}
@ -327,6 +371,7 @@ fn more(
..
}) => {
pager.page_up();
paging_add_back_message(options, stdout)?;
}
Event::Key(KeyEvent {
code: KeyCode::Char('j'),
@ -347,7 +392,7 @@ fn more(
pager.prev_line();
}
Event::Resize(col, row) => {
pager.page_resize(col, row);
pager.page_resize(col, row, options.lines);
}
Event::Key(KeyEvent {
code: KeyCode::Char(k),
@ -388,7 +433,7 @@ impl<'a> Pager<'a> {
fn new(rows: u16, lines: Vec<String>, next_file: Option<&'a str>, options: &Options) -> Self {
let line_count = lines.len();
Self {
upper_mark: 0,
upper_mark: options.from_line,
content_rows: rows.saturating_sub(1),
lines,
next_file,
@ -447,15 +492,17 @@ impl<'a> Pager<'a> {
}
// TODO: Deal with column size changes.
fn page_resize(&mut self, _: u16, row: u16) {
self.content_rows = row.saturating_sub(1);
fn page_resize(&mut self, _: u16, row: u16, option_line: Option<u16>) {
if option_line.is_none() {
self.content_rows = row.saturating_sub(1);
};
}
fn draw(&mut self, stdout: &mut std::io::Stdout, wrong_key: Option<char>) {
self.draw_lines(stdout);
let lower_mark = self
.line_count
.min(self.upper_mark.saturating_add(self.content_rows.into()));
self.draw_lines(stdout);
self.draw_prompt(stdout, lower_mark, wrong_key);
stdout.flush().unwrap();
}
@ -515,7 +562,6 @@ impl<'a> Pager<'a> {
};
let status = format!("--More--({status_inner})");
let banner = match (self.silent, wrong_key) {
(true, Some(key)) => format!(
"{status} [Unknown key: '{key}'. Press 'h' for instructions. (unimplemented)]"
@ -536,6 +582,14 @@ impl<'a> Pager<'a> {
}
}
fn paging_add_back_message(options: &Options, stdout: &mut std::io::Stdout) -> UResult<()> {
if options.lines.is_some() {
execute!(stdout, MoveUp(1))?;
stdout.write_all("\n\r...back 1 page\n".as_bytes())?;
}
Ok(())
}
// Break the lines on the cols of the terminal
fn break_buff(buff: &str, cols: usize) -> Vec<String> {
let mut lines = Vec::with_capacity(buff.lines().count());

View file

@ -1,6 +1,6 @@
[package]
name = "uu_mv"
version = "0.0.18"
version = "0.0.20"
authors = ["uutils developers"]
license = "MIT"
description = "mv ~ (uutils) move (rename) SOURCE to DESTINATION"
@ -15,10 +15,10 @@ edition = "2021"
path = "src/mv.rs"
[dependencies]
clap = { workspace=true }
fs_extra = { workspace=true }
indicatif = { workspace=true }
uucore = { workspace=true, features=["fs"] }
clap = { workspace = true }
fs_extra = { workspace = true }
indicatif = { workspace = true }
uucore = { workspace = true, features = ["fs"] }
[[bin]]
name = "mv"

View file

@ -22,10 +22,10 @@ use std::os::unix;
#[cfg(windows)]
use std::os::windows;
use std::path::{Path, PathBuf};
use uucore::backup_control::{self, BackupMode};
use uucore::backup_control::{self, source_is_target_backup, BackupMode};
use uucore::display::Quotable;
use uucore::error::{set_exit_code, FromIo, UError, UResult, USimpleError, UUsageError};
use uucore::fs::are_hardlinks_to_same_file;
use uucore::fs::{are_hardlinks_or_one_way_symlink_to_same_file, are_hardlinks_to_same_file};
use uucore::update_control::{self, UpdateMode};
use uucore::{format_usage, help_about, help_section, help_usage, prompt_yes, show};
@ -251,12 +251,25 @@ fn parse_paths(files: &[OsString], b: &Behavior) -> Vec<PathBuf> {
}
fn handle_two_paths(source: &Path, target: &Path, b: &Behavior) -> UResult<()> {
if b.backup == BackupMode::SimpleBackup && source_is_target_backup(source, target, &b.suffix) {
return Err(io::Error::new(
io::ErrorKind::NotFound,
format!(
"backing up {} might destroy source; {} not moved",
target.quote(),
source.quote()
),
)
.into());
}
if source.symlink_metadata().is_err() {
return Err(MvError::NoSuchFile(source.quote().to_string()).into());
}
if (source.eq(target) || are_hardlinks_to_same_file(source, target))
&& b.backup != BackupMode::SimpleBackup
if (source.eq(target)
|| are_hardlinks_to_same_file(source, target)
|| are_hardlinks_or_one_way_symlink_to_same_file(source, target))
&& b.backup == BackupMode::NoBackup
{
if source.eq(Path::new(".")) || source.ends_with("/.") || source.is_file() {
return Err(
@ -387,7 +400,7 @@ fn move_files_into_dir(files: &[PathBuf], target_dir: &Path, b: &Behavior) -> UR
}
match rename(sourcepath, &targetpath, b, multi_progress.as_ref()) {
Err(e) if e.to_string() == "" => set_exit_code(1),
Err(e) if e.to_string().is_empty() => set_exit_code(1),
Err(e) => {
let e = e.map_err_context(|| {
format!(
@ -420,9 +433,7 @@ fn rename(
let mut backup_path = None;
if to.exists() {
if (b.update == UpdateMode::ReplaceIfOlder || b.update == UpdateMode::ReplaceNone)
&& b.overwrite == OverwriteMode::Interactive
{
if b.update == UpdateMode::ReplaceIfOlder && b.overwrite == OverwriteMode::Interactive {
// `mv -i --update old new` when `new` exists doesn't move anything
// and exit with 0
return Ok(());

View file

@ -1,6 +1,6 @@
[package]
name = "uu_nice"
version = "0.0.18"
version = "0.0.20"
authors = ["uutils developers"]
license = "MIT"
description = "nice ~ (uutils) run PROGRAM with modified scheduling priority"
@ -15,10 +15,10 @@ edition = "2021"
path = "src/nice.rs"
[dependencies]
clap = { workspace=true }
libc = { workspace=true }
nix = { workspace=true }
uucore = { workspace=true }
clap = { workspace = true }
libc = { workspace = true }
nix = { workspace = true }
uucore = { workspace = true }
[[bin]]
name = "nice"

View file

@ -1,6 +1,6 @@
[package]
name = "uu_nl"
version = "0.0.18"
version = "0.0.20"
authors = ["uutils developers"]
license = "MIT"
description = "nl ~ (uutils) display input with added line numbers"
@ -15,9 +15,9 @@ edition = "2021"
path = "src/nl.rs"
[dependencies]
clap = { workspace=true }
regex = { workspace=true }
uucore = { workspace=true }
clap = { workspace = true }
regex = { workspace = true }
uucore = { workspace = true }
[[bin]]
name = "nl"

View file

@ -1,7 +1,23 @@
#nl
# nl
```
nl [OPTION]... [FILE]...
```
Number lines of files
## After Help
`STYLE` is one of:
* `a` number all lines
* `t` number only nonempty lines
* `n` number no lines
* `pBRE` number only lines that contain a match for the basic regular
expression, `BRE`
`FORMAT` is one of:
* `ln` left justified, no leading zeros
* `rn` right justified, no leading zeros
* `rz` right justified, leading zeros

View file

@ -5,17 +5,15 @@ use crate::options;
// parse_style parses a style string into a NumberingStyle.
fn parse_style(chars: &[char]) -> Result<crate::NumberingStyle, String> {
if chars.len() == 1 && chars[0] == 'a' {
Ok(crate::NumberingStyle::NumberForAll)
Ok(crate::NumberingStyle::All)
} else if chars.len() == 1 && chars[0] == 't' {
Ok(crate::NumberingStyle::NumberForNonEmpty)
Ok(crate::NumberingStyle::NonEmpty)
} else if chars.len() == 1 && chars[0] == 'n' {
Ok(crate::NumberingStyle::NumberForNone)
Ok(crate::NumberingStyle::None)
} else if chars.len() > 1 && chars[0] == 'p' {
let s: String = chars[1..].iter().cloned().collect();
match regex::Regex::new(&s) {
Ok(re) => Ok(crate::NumberingStyle::NumberForRegularExpression(Box::new(
re,
))),
Ok(re) => Ok(crate::NumberingStyle::Regex(Box::new(re))),
Err(_) => Err(String::from("Illegal regular expression")),
}
} else {
@ -29,30 +27,14 @@ fn parse_style(chars: &[char]) -> Result<crate::NumberingStyle, String> {
pub fn parse_options(settings: &mut crate::Settings, opts: &clap::ArgMatches) -> Vec<String> {
// This vector holds error messages encountered.
let mut errs: Vec<String> = vec![];
settings.renumber = !opts.contains_id(options::NO_RENUMBER);
match opts.get_one::<String>(options::NUMBER_SEPARATOR) {
None => {}
Some(val) => {
settings.number_separator = val.to_owned();
}
}
match opts.get_one::<String>(options::NUMBER_FORMAT) {
None => {}
Some(val) => match val.as_str() {
"ln" => {
settings.number_format = crate::NumberFormat::Left;
}
"rn" => {
settings.number_format = crate::NumberFormat::Right;
}
"rz" => {
settings.number_format = crate::NumberFormat::RightZero;
}
_ => {
errs.push(String::from("Illegal value for -n"));
}
},
settings.renumber = opts.get_flag(options::NO_RENUMBER);
if let Some(val) = opts.get_one::<String>(options::NUMBER_SEPARATOR) {
settings.number_separator = val.to_owned();
}
settings.number_format = opts
.get_one::<String>(options::NUMBER_FORMAT)
.map(Into::into)
.unwrap_or_default();
match opts.get_one::<String>(options::BODY_NUMBERING) {
None => {}
Some(val) => {
@ -95,53 +77,25 @@ pub fn parse_options(settings: &mut crate::Settings, opts: &clap::ArgMatches) ->
}
}
}
match opts.get_one::<String>(options::LINE_INCREMENT) {
match opts.get_one::<usize>(options::NUMBER_WIDTH) {
None => {}
Some(val) => {
let conv: Option<u64> = val.parse().ok();
match conv {
None => {
errs.push(String::from("Illegal value for -i"));
}
Some(num) => settings.line_increment = num,
}
}
Some(num) if *num > 0 => settings.number_width = *num,
Some(_) => errs.push(String::from(
"Invalid line number field width: 0: Numerical result out of range",
)),
}
match opts.get_one::<String>(options::NUMBER_WIDTH) {
match opts.get_one::<u64>(options::JOIN_BLANK_LINES) {
None => {}
Some(val) => {
let conv: Option<usize> = val.parse().ok();
match conv {
None => {
errs.push(String::from("Illegal value for -w"));
}
Some(num) => settings.number_width = num,
}
}
Some(num) if *num > 0 => settings.join_blank_lines = *num,
Some(_) => errs.push(String::from(
"Invalid line number of blank lines: 0: Numerical result out of range",
)),
}
match opts.get_one::<String>(options::STARTING_LINE_NUMBER) {
None => {}
Some(val) => {
let conv: Option<u64> = val.parse().ok();
match conv {
None => {
errs.push(String::from("Illegal value for -v"));
}
Some(num) => settings.starting_line_number = num,
}
}
if let Some(num) = opts.get_one::<i64>(options::LINE_INCREMENT) {
settings.line_increment = *num;
}
match opts.get_one::<String>(options::JOIN_BLANK_LINES) {
None => {}
Some(val) => {
let conv: Option<u64> = val.parse().ok();
match conv {
None => {
errs.push(String::from("Illegal value for -l"));
}
Some(num) => settings.join_blank_lines = num,
}
}
if let Some(num) = opts.get_one::<i64>(options::STARTING_LINE_NUMBER) {
settings.starting_line_number = *num;
}
errs
}

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