1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-29 03:57: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_type="${{ steps.vars.outputs.FAULT_TYPE }}"
fault_prefix=$(echo "$fault_type" | tr '[:lower:]' '[:upper:]') 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> # * 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 if [ -n "${{ steps.vars.outputs.FAIL_ON_FAULT }}" ] && [ -n "$fault" ]; then exit 1 ; fi
style_spellcheck: style_spellcheck:
@ -319,7 +319,7 @@ jobs:
shell: bash shell: bash
run: | run: |
RUSTDOCFLAGS="-Dwarnings" cargo doc ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} --no-deps --workspace --document-private-items 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: with:
command: fix command: fix
globs: | globs: |
@ -452,8 +452,20 @@ jobs:
test -f /tmp/usr/local/share/man/man1/whoami.1 test -f /tmp/usr/local/share/man/man1/whoami.1
# Check that the completion is present # Check that the completion is present
test -f /tmp/usr/local/share/zsh/site-functions/_install 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: env:
RUST_BACKTRACE: "1" 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: build_rust_stable:
name: Build/stable name: Build/stable
@ -1048,6 +1060,15 @@ jobs:
name: toybox-result.json name: toybox-result.json
path: ${{ steps.vars.outputs.TEST_SUMMARY_FILE }} 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: coverage:
name: Code Coverage name: Code Coverage
runs-on: ${{ matrix.job.os }} 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 id: vars
shell: bash shell: bash
run: | 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 # 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)" ) RUST_MIN_SRV=$(grep -P "^\s+RUST_MIN_SRV:" .github/workflows/CICD.yml | grep -Po "(?<=\x22)\d+[.]\d+(?:[.]\d+)?(?=\x22)" )
outputs RUST_MIN_SRV echo "RUST_MIN_SRV=${RUST_MIN_SRV}" >> $GITHUB_OUTPUT
- uses: dtolnay/rust-toolchain@${{ steps.vars.outputs.RUST_MIN_SRV }} - uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ steps.vars.outputs.RUST_MIN_SRV }}
- uses: Swatinem/rust-cache@v2 - uses: Swatinem/rust-cache@v2
- name: Ensure updated 'Cargo.lock' - name: Ensure updated 'Cargo.lock'
shell: bash shell: bash
@ -67,7 +67,7 @@ jobs:
- name: Commit any changes (to '${{ env.BRANCH_TARGET }}') - name: Commit any changes (to '${{ env.BRANCH_TARGET }}')
uses: EndBug/add-and-commit@v9 uses: EndBug/add-and-commit@v9
with: with:
branch: ${{ env.BRANCH_TARGET }} new_branch: ${{ env.BRANCH_TARGET }}
default_author: github_actions default_author: github_actions
message: "maint ~ refresh 'Cargo.lock'" message: "maint ~ refresh 'Cargo.lock'"
add: Cargo.lock add: Cargo.lock
@ -90,13 +90,11 @@ jobs:
id: vars id: vars
shell: bash shell: bash
run: | 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 # target-specific options
# * CARGO_FEATURES_OPTION # * CARGO_FEATURES_OPTION
CARGO_FEATURES_OPTION='' ; CARGO_FEATURES_OPTION='' ;
if [ -n "${{ matrix.job.features }}" ]; then CARGO_FEATURES_OPTION='--features "${{ matrix.job.features }}"' ; fi 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 - uses: dtolnay/rust-toolchain@master
with: with:
toolchain: stable toolchain: stable
@ -114,7 +112,7 @@ jobs:
- name: Commit any changes (to '${{ env.BRANCH_TARGET }}') - name: Commit any changes (to '${{ env.BRANCH_TARGET }}')
uses: EndBug/add-and-commit@v9 uses: EndBug/add-and-commit@v9
with: with:
branch: ${{ env.BRANCH_TARGET }} new_branch: ${{ env.BRANCH_TARGET }}
default_author: github_actions default_author: github_actions
message: "maint ~ rustfmt (`cargo fmt`)" message: "maint ~ rustfmt (`cargo fmt`)"
env: env:

View file

@ -205,7 +205,7 @@ jobs:
path_UUTILS='${{ steps.vars.outputs.path_UUTILS }}' path_UUTILS='${{ steps.vars.outputs.path_UUTILS }}'
# https://github.com/uutils/coreutils/issues/4294 # https://github.com/uutils/coreutils/issues/4294
# https://github.com/uutils/coreutils/issues/4295 # 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 }} mkdir -p ${{ steps.vars.outputs.path_reference }}

View file

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

1
.gitignore vendored
View file

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

1
.vscode/cSpell.json vendored
View file

@ -19,6 +19,7 @@
// files to ignore (globs supported) // files to ignore (globs supported)
"ignorePaths": [ "ignorePaths": [
"Cargo.lock", "Cargo.lock",
"oranda.json",
"target/**", "target/**",
"tests/**/fixtures/**", "tests/**/fixtures/**",
"src/uu/dd/test-resources/**", "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) * [V lang](https://github.com/vlang/coreutils)
* [SerenityOS](https://github.com/SerenityOS/serenity/tree/master/Userland/Utilities) * [SerenityOS](https://github.com/SerenityOS/serenity/tree/master/Userland/Utilities)
* [Initial Unix](https://github.com/dspinellis/unix-history-repo) * [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 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). 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) # coreutils (uutils)
# * see the repository LICENSE, README, and CONTRIBUTING files for more information # * 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] [package]
name = "coreutils" name = "coreutils"
version = "0.0.18" version = "0.0.20"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "coreutils ~ GNU coreutils (updated); implemented as universal (cross-platform) utils, written in Rust" description = "coreutils ~ GNU coreutils (updated); implemented as universal (cross-platform) utils, written in Rust"
@ -22,16 +22,16 @@ edition = "2021"
build = "build.rs" build = "build.rs"
[features] [features]
default = [ "feat_common_core" ] default = ["feat_common_core"]
## OS feature shortcodes ## OS feature shortcodes
macos = [ "feat_os_macos" ] macos = ["feat_os_macos"]
unix = [ "feat_os_unix" ] unix = ["feat_os_unix"]
windows = [ "feat_os_windows" ] windows = ["feat_os_windows"]
## project-specific feature shortcodes ## project-specific feature shortcodes
nightly = [] nightly = []
test_unimplemented = [] test_unimplemented = []
# * only build `uudoc` when `--feature uudoc` is activated # * only build `uudoc` when `--feature uudoc` is activated
uudoc = ["zip", "dep:help_parser"] uudoc = ["zip", "dep:uuhelp_parser"]
## features ## features
# "feat_acl" == enable support for ACLs (access control lists; by using`--features feat_acl`) # "feat_acl" == enable support for ACLs (access control lists; by using`--features feat_acl`)
# NOTE: # NOTE:
@ -42,239 +42,237 @@ feat_acl = ["cp/feat_acl"]
# NOTE: # NOTE:
# * The selinux(-sys) crate requires `libselinux` headers and shared library to be accessible in the C toolchain at compile time. # * 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. # * 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 ## feature sets
## (common/core and Tier1) 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" == baseline core set of utilities which can be built/run on most targets
feat_common_core = [ feat_common_core = [
"base32", "base32",
"base64", "base64",
"basename", "basename",
"basenc", "basenc",
"cat", "cat",
"cksum", "cksum",
"comm", "comm",
"cp", "cp",
"csplit", "csplit",
"cut", "cut",
"date", "date",
"df", "df",
"dir", "dir",
"dircolors", "dircolors",
"dirname", "dirname",
"dd", "dd",
"du", "du",
"echo", "echo",
"env", "env",
"expand", "expand",
"expr", "expr",
"factor", "factor",
"false", "false",
"fmt", "fmt",
"fold", "fold",
"hashsum", "hashsum",
"head", "head",
"join", "join",
"link", "link",
"ln", "ln",
"ls", "ls",
"mkdir", "mkdir",
"mktemp", "mktemp",
"more", "more",
"mv", "mv",
"nl", "nl",
"numfmt", "numfmt",
"od", "od",
"paste", "paste",
"pr", "pr",
"printenv", "printenv",
"printf", "printf",
"ptx", "ptx",
"pwd", "pwd",
"readlink", "readlink",
"realpath", "realpath",
"relpath", "relpath",
"rm", "rm",
"rmdir", "rmdir",
"seq", "seq",
"shred", "shred",
"shuf", "shuf",
"sleep", "sleep",
"sort", "sort",
"split", "split",
"sum", "sum",
"tac", "tac",
"tail", "tail",
"tee", "tee",
"test", "test",
"tr", "tr",
"true", "true",
"truncate", "truncate",
"tsort", "tsort",
"touch", "touch",
"unexpand", "unexpand",
"uniq", "uniq",
"unlink", "unlink",
"vdir", "vdir",
"wc", "wc",
"yes", "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" == 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_Tier1 = [
"feat_common_core", "feat_common_core",
# #
"arch", "arch",
"hostname", "hostname",
"nproc", "nproc",
"sync", "sync",
"touch", "touch",
"uname", "uname",
"whoami", "whoami",
] ]
## (primary platforms) feature sets ## (primary platforms) feature sets
# "feat_os_macos" == set of utilities which can be built/run on the MacOS platform # "feat_os_macos" == set of utilities which can be built/run on the MacOS platform
feat_os_macos = [ feat_os_macos = [
"feat_os_unix", ## == a modern/usual *nix platform "feat_os_unix", ## == a modern/usual *nix platform
# #
"feat_require_unix_hostid", "feat_require_unix_hostid",
] ]
# "feat_os_unix" == set of utilities which can be built/run on modern/usual *nix platforms # "feat_os_unix" == set of utilities which can be built/run on modern/usual *nix platforms
feat_os_unix = [ feat_os_unix = [
"feat_Tier1", "feat_Tier1",
# #
"feat_require_crate_cpp", "feat_require_crate_cpp",
"feat_require_unix", "feat_require_unix",
"feat_require_unix_utmpx", "feat_require_unix_utmpx",
] ]
# "feat_os_windows" == set of utilities which can be built/run on modern/usual windows platforms # "feat_os_windows" == set of utilities which can be built/run on modern/usual windows platforms
feat_os_windows = [ feat_os_windows = [
"feat_Tier1", ## == "feat_os_windows_legacy" + "hostname" "feat_Tier1", ## == "feat_os_windows_legacy" + "hostname"
] ]
## (secondary platforms) feature sets ## (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" == set of utilities which can be built/run on the "arm-unknown-linux-gnueabihf" target (ARMv6 Linux [hardfloat])
feat_os_unix_gnueabihf = [ feat_os_unix_gnueabihf = [
"feat_Tier1", "feat_Tier1",
# #
"feat_require_unix", "feat_require_unix",
"feat_require_unix_hostid", "feat_require_unix_hostid",
"feat_require_unix_utmpx", "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" == 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_os_unix_musl = [
"feat_Tier1", "feat_Tier1",
# #
"feat_require_unix", "feat_require_unix",
"feat_require_unix_hostid", "feat_require_unix_hostid",
] ]
feat_os_unix_android = [ feat_os_unix_android = [
"feat_Tier1", "feat_Tier1",
# #
"feat_require_unix", "feat_require_unix",
] ]
## feature sets with requirements (restricting cross-platform availability) ## 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 # ** 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" == set of utilities requiring the `cpp` crate (which fail to compile on several platforms; as of 2020-04-23)
feat_require_crate_cpp = [ feat_require_crate_cpp = ["stdbuf"]
"stdbuf",
]
# "feat_require_unix" == set of utilities requiring support which is only available on unix platforms (as of 2020-04-23) # "feat_require_unix" == set of utilities requiring support which is only available on unix platforms (as of 2020-04-23)
feat_require_unix = [ feat_require_unix = [
"chgrp", "chgrp",
"chmod", "chmod",
"chown", "chown",
"chroot", "chroot",
"groups", "groups",
"id", "id",
"install", "install",
"kill", "kill",
"logname", "logname",
"mkfifo", "mkfifo",
"mknod", "mknod",
"nice", "nice",
"nohup", "nohup",
"pathchk", "pathchk",
"stat", "stat",
"stty", "stty",
"timeout", "timeout",
"tty", "tty",
] ]
# "feat_require_unix_utmpx" == set of utilities requiring unix utmp/utmpx support # "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?> # * ref: <https://wiki.musl-libc.org/faq.html#Q:-Why-is-the-utmp/wtmp-functionality-only-implemented-as-stubs?>
feat_require_unix_utmpx = [ feat_require_unix_utmpx = ["pinky", "uptime", "users", "who"]
"pinky",
"uptime",
"users",
"who",
]
# "feat_require_unix_hostid" == set of utilities requiring gethostid in libc (only some unixes provide) # "feat_require_unix_hostid" == set of utilities requiring gethostid in libc (only some unixes provide)
feat_require_unix_hostid = [ feat_require_unix_hostid = ["hostid"]
"hostid",
]
# "feat_require_selinux" == set of utilities depending on SELinux. # "feat_require_selinux" == set of utilities depending on SELinux.
feat_require_selinux = [ feat_require_selinux = ["chcon", "runcon"]
"chcon",
"runcon",
]
## (alternate/newer/smaller platforms) feature sets ## (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" == 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_os_unix_fuchsia = [
"feat_common_core", "feat_common_core",
# #
"feat_require_crate_cpp", "feat_require_crate_cpp",
# #
"chgrp", "chgrp",
"chmod", "chmod",
"chown", "chown",
"du", "du",
"groups", "groups",
"hostid", "hostid",
"install", "install",
"logname", "logname",
"mkfifo", "mkfifo",
"mknod", "mknod",
"nice", "nice",
"pathchk", "pathchk",
"tty", "tty",
"uname", "uname",
"unlink", "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" == 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_os_unix_redox = [
"feat_common_core", "feat_common_core",
# #
"chmod", "chmod",
"uname", "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" == slightly restricted set of utilities which can be built/run on early windows platforms (eg, "WinXP")
feat_os_windows_legacy = [ feat_os_windows_legacy = [
"feat_common_core", "feat_common_core",
# #
"arch", "arch",
"nproc", "nproc",
"sync", "sync",
"touch", "touch",
"whoami", "whoami",
] ]
## ##
# * bypass/override ~ translate 'test' feature name to avoid dependency collision with rust core 'test' crate (o/w surfaces as compiler errors during testing) # * 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] [workspace.dependencies]
bigdecimal = "0.3" bigdecimal = "0.4"
binary-heap-plus = "0.5.0" binary-heap-plus = "0.5.0"
bstr = "1.5" bstr = "1.6"
bytecount = "0.6.3" bytecount = "0.6.3"
byteorder = "1.4.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 = { version = "4.3", features = ["wrap_help", "cargo"] }
clap_complete = "4.3" clap_complete = "4.3"
clap_mangen = "0.2" clap_mangen = "0.2"
compare = "0.1.0" compare = "0.1.0"
coz = { version = "0.1.3" } coz = { version = "0.1.3" }
crossterm = ">=0.26.1" crossterm = ">=0.27.0"
ctrlc = { version = "3.4", features = ["termination"] } ctrlc = { version = "3.4", features = ["termination"] }
exacl = "0.10.0" exacl = "0.10.0"
file_diff = "1.0.0" file_diff = "1.0.0"
@ -282,189 +280,192 @@ filetime = "0.2"
fnv = "1.0.7" fnv = "1.0.7"
fs_extra = "1.3.0" fs_extra = "1.3.0"
fts-sys = "0.2" fts-sys = "0.2"
fundu = "0.5.1" fundu = "2.0.0"
gcd = "2.3" gcd = "2.3"
glob = "0.3.1" glob = "0.3.1"
half = "2.2" half = "2.2"
humantime_to_duration = "0.2.1"
indicatif = "0.17" indicatif = "0.17"
is-terminal = "0.4.7" is-terminal = "0.4.7"
itertools = "0.10.5" itertools = "0.11.0"
libc = "0.2.144" libc = "0.2.147"
lscolors = { version = "0.14.0", default-features=false, features = ["nu-ansi-term"] } lscolors = { version = "0.15.0", default-features = false, features = [
"nu-ansi-term",
] }
memchr = "2" memchr = "2"
nix = { version="0.26", default-features=false } memmap2 = "0.7"
nix = { version = "0.26", default-features = false }
nom = "7.1.3" 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-bigint = "0.4.3"
num-traits = "0.2.15" num-traits = "0.2.16"
number_prefix = "0.4" number_prefix = "0.4"
once_cell = "1.17.2" once_cell = "1.18.0"
onig = { version = "~6.4", default-features = false } onig = { version = "~6.4", default-features = false }
ouroboros = "0.15.6" parse_datetime = "0.4.0"
phf = "0.11.1" phf = "0.11.2"
phf_codegen = "0.11.1" phf_codegen = "0.11.2"
platform-info = "2.0.1" platform-info = "2.0.2"
quick-error = "2.0.1" quick-error = "2.0.1"
rand = { version = "0.8", features = ["small_rng"] } rand = { version = "0.8", features = ["small_rng"] }
rand_core = "0.6" rand_core = "0.6"
rayon = "1.7" rayon = "1.7"
redox_syscall = "0.3" redox_syscall = "0.3"
regex = "1.8.3" regex = "1.9.3"
rstest = "0.17.0" rstest = "0.18.1"
rust-ini = "0.18.0" rust-ini = "0.19.0"
same-file = "1.0.6" same-file = "1.0.6"
self_cell = "1.0.1"
selinux = "0.4" selinux = "0.4"
signal-hook = "0.3.15" signal-hook = "0.3.17"
smallvec = { version = "1.10", features = ["union"] } smallvec = { version = "1.11", features = ["union"] }
tempfile = "3.5.0" tempfile = "3.6.0"
term_grid = "0.1.5" term_grid = "0.1.5"
terminal_size = "0.2.6" terminal_size = "0.2.6"
textwrap = { version="0.16.0", features=["terminal_size"] } textwrap = { version = "0.16.0", features = ["terminal_size"] }
thiserror = "1.0" thiserror = "1.0"
time = { version="0.3" } time = { version = "0.3" }
unicode-segmentation = "1.10.1" unicode-segmentation = "1.10.1"
unicode-width = "0.1.10" unicode-width = "0.1.10"
utf-8 = "0.7.6" utf-8 = "0.7.6"
walkdir = "2.3" walkdir = "2.3"
winapi-util = "0.1.5" winapi-util = "0.1.5"
windows-sys = { version="0.48.0", default-features=false } windows-sys = { version = "0.48.0", default-features = false }
xattr = "1.0.0" xattr = "1.0.1"
zip = { version = "0.6.6", default_features=false, features=["deflate"] } zip = { version = "0.6.6", default_features = false, features = ["deflate"] }
hex = "0.4.3" hex = "0.4.3"
md-5 = "0.10.5" md-5 = "0.10.5"
sha1 = "0.10.5" sha1 = "0.10.5"
sha2 = "0.10.6" sha2 = "0.10.7"
sha3 = "0.10.8" sha3 = "0.10.8"
blake2b_simd = "1.0.1" blake2b_simd = "1.0.1"
blake3 = "1.3.3" blake3 = "1.4.0"
sm3 = "0.4.2" sm3 = "0.4.2"
digest = "0.10.7" digest = "0.10.7"
uucore = { version=">=0.0.18", package="uucore", path="src/uucore" } uucore = { version = ">=0.0.19", package = "uucore", path = "src/uucore" }
uucore_procs = { version=">=0.0.18", package="uucore_procs", path="src/uucore_procs" } uucore_procs = { version = ">=0.0.19", package = "uucore_procs", path = "src/uucore_procs" }
uu_ls = { version=">=0.0.18", path="src/uu/ls" } uu_ls = { version = ">=0.0.18", path = "src/uu/ls" }
uu_base32 = { version=">=0.0.18", path="src/uu/base32"} uu_base32 = { version = ">=0.0.18", path = "src/uu/base32" }
[dependencies] [dependencies]
clap = { workspace=true } clap = { workspace = true }
once_cell = { workspace=true } once_cell = { workspace = true }
uucore = { workspace=true } uucore = { workspace = true }
clap_complete = { workspace=true } clap_complete = { workspace = true }
clap_mangen = { workspace=true } clap_mangen = { workspace = true }
phf = { workspace=true } phf = { workspace = true }
selinux = { workspace=true, optional = true } selinux = { workspace = true, optional = true }
textwrap = { workspace=true } textwrap = { workspace = true }
zip = { workspace=true, optional = 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 # * 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" } arch = { optional = true, version = "0.0.20", package = "uu_arch", path = "src/uu/arch" }
base32 = { optional=true, version="0.0.18", package="uu_base32", path="src/uu/base32" } base32 = { optional = true, version = "0.0.20", package = "uu_base32", path = "src/uu/base32" }
base64 = { optional=true, version="0.0.18", package="uu_base64", path="src/uu/base64" } base64 = { optional = true, version = "0.0.20", package = "uu_base64", path = "src/uu/base64" }
basename = { optional=true, version="0.0.18", package="uu_basename", path="src/uu/basename" } basename = { optional = true, version = "0.0.20", package = "uu_basename", path = "src/uu/basename" }
basenc = { optional=true, version="0.0.18", package="uu_basenc", path="src/uu/basenc" } basenc = { optional = true, version = "0.0.20", package = "uu_basenc", path = "src/uu/basenc" }
cat = { optional=true, version="0.0.18", package="uu_cat", path="src/uu/cat" } cat = { optional = true, version = "0.0.20", package = "uu_cat", path = "src/uu/cat" }
chcon = { optional=true, version="0.0.18", package="uu_chcon", path="src/uu/chcon" } chcon = { optional = true, version = "0.0.20", package = "uu_chcon", path = "src/uu/chcon" }
chgrp = { optional=true, version="0.0.18", package="uu_chgrp", path="src/uu/chgrp" } chgrp = { optional = true, version = "0.0.20", package = "uu_chgrp", path = "src/uu/chgrp" }
chmod = { optional=true, version="0.0.18", package="uu_chmod", path="src/uu/chmod" } chmod = { optional = true, version = "0.0.20", package = "uu_chmod", path = "src/uu/chmod" }
chown = { optional=true, version="0.0.18", package="uu_chown", path="src/uu/chown" } chown = { optional = true, version = "0.0.20", package = "uu_chown", path = "src/uu/chown" }
chroot = { optional=true, version="0.0.18", package="uu_chroot", path="src/uu/chroot" } chroot = { optional = true, version = "0.0.20", package = "uu_chroot", path = "src/uu/chroot" }
cksum = { optional=true, version="0.0.18", package="uu_cksum", path="src/uu/cksum" } cksum = { optional = true, version = "0.0.20", package = "uu_cksum", path = "src/uu/cksum" }
comm = { optional=true, version="0.0.18", package="uu_comm", path="src/uu/comm" } comm = { optional = true, version = "0.0.20", package = "uu_comm", path = "src/uu/comm" }
cp = { optional=true, version="0.0.18", package="uu_cp", path="src/uu/cp" } cp = { optional = true, version = "0.0.20", package = "uu_cp", path = "src/uu/cp" }
csplit = { optional=true, version="0.0.18", package="uu_csplit", path="src/uu/csplit" } csplit = { optional = true, version = "0.0.20", package = "uu_csplit", path = "src/uu/csplit" }
cut = { optional=true, version="0.0.18", package="uu_cut", path="src/uu/cut" } cut = { optional = true, version = "0.0.20", package = "uu_cut", path = "src/uu/cut" }
date = { optional=true, version="0.0.18", package="uu_date", path="src/uu/date" } date = { optional = true, version = "0.0.20", package = "uu_date", path = "src/uu/date" }
dd = { optional=true, version="0.0.18", package="uu_dd", path="src/uu/dd" } dd = { optional = true, version = "0.0.20", package = "uu_dd", path = "src/uu/dd" }
df = { optional=true, version="0.0.18", package="uu_df", path="src/uu/df" } df = { optional = true, version = "0.0.20", package = "uu_df", path = "src/uu/df" }
dir = { optional=true, version="0.0.18", package="uu_dir", path="src/uu/dir" } dir = { optional = true, version = "0.0.20", package = "uu_dir", path = "src/uu/dir" }
dircolors= { optional=true, version="0.0.18", package="uu_dircolors", path="src/uu/dircolors" } dircolors = { optional = true, version = "0.0.20", package = "uu_dircolors", path = "src/uu/dircolors" }
dirname = { optional=true, version="0.0.18", package="uu_dirname", path="src/uu/dirname" } dirname = { optional = true, version = "0.0.20", package = "uu_dirname", path = "src/uu/dirname" }
du = { optional=true, version="0.0.18", package="uu_du", path="src/uu/du" } du = { optional = true, version = "0.0.20", package = "uu_du", path = "src/uu/du" }
echo = { optional=true, version="0.0.18", package="uu_echo", path="src/uu/echo" } echo = { optional = true, version = "0.0.20", package = "uu_echo", path = "src/uu/echo" }
env = { optional=true, version="0.0.18", package="uu_env", path="src/uu/env" } env = { optional = true, version = "0.0.20", package = "uu_env", path = "src/uu/env" }
expand = { optional=true, version="0.0.18", package="uu_expand", path="src/uu/expand" } expand = { optional = true, version = "0.0.20", package = "uu_expand", path = "src/uu/expand" }
expr = { optional=true, version="0.0.18", package="uu_expr", path="src/uu/expr" } expr = { optional = true, version = "0.0.20", package = "uu_expr", path = "src/uu/expr" }
factor = { optional=true, version="0.0.18", package="uu_factor", path="src/uu/factor" } factor = { optional = true, version = "0.0.20", package = "uu_factor", path = "src/uu/factor" }
false = { optional=true, version="0.0.18", package="uu_false", path="src/uu/false" } false = { optional = true, version = "0.0.20", package = "uu_false", path = "src/uu/false" }
fmt = { optional=true, version="0.0.18", package="uu_fmt", path="src/uu/fmt" } fmt = { optional = true, version = "0.0.20", package = "uu_fmt", path = "src/uu/fmt" }
fold = { optional=true, version="0.0.18", package="uu_fold", path="src/uu/fold" } fold = { optional = true, version = "0.0.20", package = "uu_fold", path = "src/uu/fold" }
groups = { optional=true, version="0.0.18", package="uu_groups", path="src/uu/groups" } groups = { optional = true, version = "0.0.20", package = "uu_groups", path = "src/uu/groups" }
hashsum = { optional=true, version="0.0.18", package="uu_hashsum", path="src/uu/hashsum" } hashsum = { optional = true, version = "0.0.20", package = "uu_hashsum", path = "src/uu/hashsum" }
head = { optional=true, version="0.0.18", package="uu_head", path="src/uu/head" } head = { optional = true, version = "0.0.20", package = "uu_head", path = "src/uu/head" }
hostid = { optional=true, version="0.0.18", package="uu_hostid", path="src/uu/hostid" } hostid = { optional = true, version = "0.0.20", package = "uu_hostid", path = "src/uu/hostid" }
hostname = { optional=true, version="0.0.18", package="uu_hostname", path="src/uu/hostname" } hostname = { optional = true, version = "0.0.20", package = "uu_hostname", path = "src/uu/hostname" }
id = { optional=true, version="0.0.18", package="uu_id", path="src/uu/id" } id = { optional = true, version = "0.0.20", package = "uu_id", path = "src/uu/id" }
install = { optional=true, version="0.0.18", package="uu_install", path="src/uu/install" } install = { optional = true, version = "0.0.20", package = "uu_install", path = "src/uu/install" }
join = { optional=true, version="0.0.18", package="uu_join", path="src/uu/join" } join = { optional = true, version = "0.0.20", package = "uu_join", path = "src/uu/join" }
kill = { optional=true, version="0.0.18", package="uu_kill", path="src/uu/kill" } kill = { optional = true, version = "0.0.20", package = "uu_kill", path = "src/uu/kill" }
link = { optional=true, version="0.0.18", package="uu_link", path="src/uu/link" } link = { optional = true, version = "0.0.20", package = "uu_link", path = "src/uu/link" }
ln = { optional=true, version="0.0.18", package="uu_ln", path="src/uu/ln" } ln = { optional = true, version = "0.0.20", package = "uu_ln", path = "src/uu/ln" }
ls = { optional=true, version="0.0.18", package="uu_ls", path="src/uu/ls" } ls = { optional = true, version = "0.0.20", package = "uu_ls", path = "src/uu/ls" }
logname = { optional=true, version="0.0.18", package="uu_logname", path="src/uu/logname" } logname = { optional = true, version = "0.0.20", package = "uu_logname", path = "src/uu/logname" }
mkdir = { optional=true, version="0.0.18", package="uu_mkdir", path="src/uu/mkdir" } mkdir = { optional = true, version = "0.0.20", package = "uu_mkdir", path = "src/uu/mkdir" }
mkfifo = { optional=true, version="0.0.18", package="uu_mkfifo", path="src/uu/mkfifo" } mkfifo = { optional = true, version = "0.0.20", package = "uu_mkfifo", path = "src/uu/mkfifo" }
mknod = { optional=true, version="0.0.18", package="uu_mknod", path="src/uu/mknod" } mknod = { optional = true, version = "0.0.20", package = "uu_mknod", path = "src/uu/mknod" }
mktemp = { optional=true, version="0.0.18", package="uu_mktemp", path="src/uu/mktemp" } mktemp = { optional = true, version = "0.0.20", package = "uu_mktemp", path = "src/uu/mktemp" }
more = { optional=true, version="0.0.18", package="uu_more", path="src/uu/more" } more = { optional = true, version = "0.0.20", package = "uu_more", path = "src/uu/more" }
mv = { optional=true, version="0.0.18", package="uu_mv", path="src/uu/mv" } mv = { optional = true, version = "0.0.20", package = "uu_mv", path = "src/uu/mv" }
nice = { optional=true, version="0.0.18", package="uu_nice", path="src/uu/nice" } nice = { optional = true, version = "0.0.20", package = "uu_nice", path = "src/uu/nice" }
nl = { optional=true, version="0.0.18", package="uu_nl", path="src/uu/nl" } nl = { optional = true, version = "0.0.20", package = "uu_nl", path = "src/uu/nl" }
nohup = { optional=true, version="0.0.18", package="uu_nohup", path="src/uu/nohup" } nohup = { optional = true, version = "0.0.20", package = "uu_nohup", path = "src/uu/nohup" }
nproc = { optional=true, version="0.0.18", package="uu_nproc", path="src/uu/nproc" } nproc = { optional = true, version = "0.0.20", package = "uu_nproc", path = "src/uu/nproc" }
numfmt = { optional=true, version="0.0.18", package="uu_numfmt", path="src/uu/numfmt" } numfmt = { optional = true, version = "0.0.20", package = "uu_numfmt", path = "src/uu/numfmt" }
od = { optional=true, version="0.0.18", package="uu_od", path="src/uu/od" } od = { optional = true, version = "0.0.20", package = "uu_od", path = "src/uu/od" }
paste = { optional=true, version="0.0.18", package="uu_paste", path="src/uu/paste" } paste = { optional = true, version = "0.0.20", package = "uu_paste", path = "src/uu/paste" }
pathchk = { optional=true, version="0.0.18", package="uu_pathchk", path="src/uu/pathchk" } pathchk = { optional = true, version = "0.0.20", package = "uu_pathchk", path = "src/uu/pathchk" }
pinky = { optional=true, version="0.0.18", package="uu_pinky", path="src/uu/pinky" } pinky = { optional = true, version = "0.0.20", package = "uu_pinky", path = "src/uu/pinky" }
pr = { optional=true, version="0.0.18", package="uu_pr", path="src/uu/pr" } pr = { optional = true, version = "0.0.20", package = "uu_pr", path = "src/uu/pr" }
printenv = { optional=true, version="0.0.18", package="uu_printenv", path="src/uu/printenv" } printenv = { optional = true, version = "0.0.20", package = "uu_printenv", path = "src/uu/printenv" }
printf = { optional=true, version="0.0.18", package="uu_printf", path="src/uu/printf" } printf = { optional = true, version = "0.0.20", package = "uu_printf", path = "src/uu/printf" }
ptx = { optional=true, version="0.0.18", package="uu_ptx", path="src/uu/ptx" } ptx = { optional = true, version = "0.0.20", package = "uu_ptx", path = "src/uu/ptx" }
pwd = { optional=true, version="0.0.18", package="uu_pwd", path="src/uu/pwd" } pwd = { optional = true, version = "0.0.20", package = "uu_pwd", path = "src/uu/pwd" }
readlink = { optional=true, version="0.0.18", package="uu_readlink", path="src/uu/readlink" } readlink = { optional = true, version = "0.0.20", package = "uu_readlink", path = "src/uu/readlink" }
realpath = { optional=true, version="0.0.18", package="uu_realpath", path="src/uu/realpath" } realpath = { optional = true, version = "0.0.20", package = "uu_realpath", path = "src/uu/realpath" }
relpath = { optional=true, version="0.0.18", package="uu_relpath", path="src/uu/relpath" } relpath = { optional = true, version = "0.0.20", package = "uu_relpath", path = "src/uu/relpath" }
rm = { optional=true, version="0.0.18", package="uu_rm", path="src/uu/rm" } rm = { optional = true, version = "0.0.20", package = "uu_rm", path = "src/uu/rm" }
rmdir = { optional=true, version="0.0.18", package="uu_rmdir", path="src/uu/rmdir" } rmdir = { optional = true, version = "0.0.20", package = "uu_rmdir", path = "src/uu/rmdir" }
runcon = { optional=true, version="0.0.18", package="uu_runcon", path="src/uu/runcon" } runcon = { optional = true, version = "0.0.20", package = "uu_runcon", path = "src/uu/runcon" }
seq = { optional=true, version="0.0.18", package="uu_seq", path="src/uu/seq" } seq = { optional = true, version = "0.0.20", package = "uu_seq", path = "src/uu/seq" }
shred = { optional=true, version="0.0.18", package="uu_shred", path="src/uu/shred" } shred = { optional = true, version = "0.0.20", package = "uu_shred", path = "src/uu/shred" }
shuf = { optional=true, version="0.0.18", package="uu_shuf", path="src/uu/shuf" } shuf = { optional = true, version = "0.0.20", package = "uu_shuf", path = "src/uu/shuf" }
sleep = { optional=true, version="0.0.18", package="uu_sleep", path="src/uu/sleep" } sleep = { optional = true, version = "0.0.20", package = "uu_sleep", path = "src/uu/sleep" }
sort = { optional=true, version="0.0.18", package="uu_sort", path="src/uu/sort" } sort = { optional = true, version = "0.0.20", package = "uu_sort", path = "src/uu/sort" }
split = { optional=true, version="0.0.18", package="uu_split", path="src/uu/split" } split = { optional = true, version = "0.0.20", package = "uu_split", path = "src/uu/split" }
stat = { optional=true, version="0.0.18", package="uu_stat", path="src/uu/stat" } stat = { optional = true, version = "0.0.20", package = "uu_stat", path = "src/uu/stat" }
stdbuf = { optional=true, version="0.0.18", package="uu_stdbuf", path="src/uu/stdbuf" } stdbuf = { optional = true, version = "0.0.20", package = "uu_stdbuf", path = "src/uu/stdbuf" }
stty = { optional=true, version="0.0.18", package="uu_stty", path="src/uu/stty" } stty = { optional = true, version = "0.0.20", package = "uu_stty", path = "src/uu/stty" }
sum = { optional=true, version="0.0.18", package="uu_sum", path="src/uu/sum" } sum = { optional = true, version = "0.0.20", package = "uu_sum", path = "src/uu/sum" }
sync = { optional=true, version="0.0.18", package="uu_sync", path="src/uu/sync" } sync = { optional = true, version = "0.0.20", package = "uu_sync", path = "src/uu/sync" }
tac = { optional=true, version="0.0.18", package="uu_tac", path="src/uu/tac" } tac = { optional = true, version = "0.0.20", package = "uu_tac", path = "src/uu/tac" }
tail = { optional=true, version="0.0.18", package="uu_tail", path="src/uu/tail" } tail = { optional = true, version = "0.0.20", package = "uu_tail", path = "src/uu/tail" }
tee = { optional=true, version="0.0.18", package="uu_tee", path="src/uu/tee" } tee = { optional = true, version = "0.0.20", package = "uu_tee", path = "src/uu/tee" }
timeout = { optional=true, version="0.0.18", package="uu_timeout", path="src/uu/timeout" } timeout = { optional = true, version = "0.0.20", package = "uu_timeout", path = "src/uu/timeout" }
touch = { optional=true, version="0.0.18", package="uu_touch", path="src/uu/touch" } touch = { optional = true, version = "0.0.20", package = "uu_touch", path = "src/uu/touch" }
tr = { optional=true, version="0.0.18", package="uu_tr", path="src/uu/tr" } tr = { optional = true, version = "0.0.20", package = "uu_tr", path = "src/uu/tr" }
true = { optional=true, version="0.0.18", package="uu_true", path="src/uu/true" } true = { optional = true, version = "0.0.20", package = "uu_true", path = "src/uu/true" }
truncate = { optional=true, version="0.0.18", package="uu_truncate", path="src/uu/truncate" } truncate = { optional = true, version = "0.0.20", package = "uu_truncate", path = "src/uu/truncate" }
tsort = { optional=true, version="0.0.18", package="uu_tsort", path="src/uu/tsort" } tsort = { optional = true, version = "0.0.20", package = "uu_tsort", path = "src/uu/tsort" }
tty = { optional=true, version="0.0.18", package="uu_tty", path="src/uu/tty" } tty = { optional = true, version = "0.0.20", package = "uu_tty", path = "src/uu/tty" }
uname = { optional=true, version="0.0.18", package="uu_uname", path="src/uu/uname" } uname = { optional = true, version = "0.0.20", package = "uu_uname", path = "src/uu/uname" }
unexpand = { optional=true, version="0.0.18", package="uu_unexpand", path="src/uu/unexpand" } unexpand = { optional = true, version = "0.0.20", package = "uu_unexpand", path = "src/uu/unexpand" }
uniq = { optional=true, version="0.0.18", package="uu_uniq", path="src/uu/uniq" } uniq = { optional = true, version = "0.0.20", package = "uu_uniq", path = "src/uu/uniq" }
unlink = { optional=true, version="0.0.18", package="uu_unlink", path="src/uu/unlink" } unlink = { optional = true, version = "0.0.20", package = "uu_unlink", path = "src/uu/unlink" }
uptime = { optional=true, version="0.0.18", package="uu_uptime", path="src/uu/uptime" } uptime = { optional = true, version = "0.0.20", package = "uu_uptime", path = "src/uu/uptime" }
users = { optional=true, version="0.0.18", package="uu_users", path="src/uu/users" } users = { optional = true, version = "0.0.20", package = "uu_users", path = "src/uu/users" }
vdir = { optional=true, version="0.0.18", package="uu_vdir", path="src/uu/vdir" } vdir = { optional = true, version = "0.0.20", package = "uu_vdir", path = "src/uu/vdir" }
wc = { optional=true, version="0.0.18", package="uu_wc", path="src/uu/wc" } wc = { optional = true, version = "0.0.20", package = "uu_wc", path = "src/uu/wc" }
who = { optional=true, version="0.0.18", package="uu_who", path="src/uu/who" } who = { optional = true, version = "0.0.20", package = "uu_who", path = "src/uu/who" }
whoami = { optional=true, version="0.0.18", package="uu_whoami", path="src/uu/whoami" } whoami = { optional = true, version = "0.0.20", package = "uu_whoami", path = "src/uu/whoami" }
yes = { optional=true, version="0.0.18", package="uu_yes", path="src/uu/yes" } 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)" # 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" } # 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()`) #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] [dev-dependencies]
chrono = { workspace=true } chrono = { workspace = true }
conv = "0.3" conv = "0.3"
filetime = { workspace=true } filetime = { workspace = true }
glob = { workspace=true } glob = { workspace = true }
libc = { workspace=true } libc = { workspace = true }
pretty_assertions = "1" pretty_assertions = "1"
rand = { workspace=true } rand = { workspace = true }
regex = { workspace=true } regex = { workspace = true }
sha1 = { version="0.10", features=["std"] } sha1 = { version = "0.10", features = ["std"] }
tempfile = { workspace=true } tempfile = { workspace = true }
time = { workspace=true, features=["local-offset"] } time = { workspace = true, features = ["local-offset"] }
unindent = "0.2" unindent = "0.2"
uucore = { workspace=true, features=["entries", "process", "signals"] } uucore = { workspace = true, features = ["entries", "process", "signals"] }
walkdir = { workspace=true } walkdir = { workspace = true }
is-terminal = { workspace=true } is-terminal = { workspace = true }
hex-literal = "0.4.1" hex-literal = "0.4.1"
rstest = "0.17.0" rstest = { workspace = true }
[target.'cfg(any(target_os = "linux", target_os = "android"))'.dev-dependencies] [target.'cfg(any(target_os = "linux", target_os = "android"))'.dev-dependencies]
procfs = { version = "0.15", default-features = false } procfs = { version = "0.15", default-features = false }
rlimit = "0.9.1" rlimit = "0.10.1"
[target.'cfg(unix)'.dev-dependencies] [target.'cfg(unix)'.dev-dependencies]
nix = { workspace=true, features=["process", "signal", "user"] } nix = { workspace = true, features = ["process", "signal", "user"] }
rust-users = { version="0.11", package="users" }
rand_pcg = "0.3" rand_pcg = "0.3"
[build-dependencies] [build-dependencies]
phf_codegen = { workspace=true } phf_codegen = { workspace = true }
[[bin]] [[bin]]
name = "coreutils" 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 # Config options
PROFILE ?= debug PROFILE ?= debug
@ -337,7 +337,21 @@ clean:
distclean: clean distclean: clean
$(CARGO) clean $(CARGOFLAGS) && $(CARGO) update $(CARGOFLAGS) $(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) mkdir -p $(INSTALLDIR_BIN)
ifeq (${MULTICALL}, y) ifeq (${MULTICALL}, y)
$(INSTALL) $(BUILDDIR)/coreutils $(INSTALLDIR_BIN)/$(PROG_PREFIX)coreutils $(INSTALL) $(BUILDDIR)/coreutils $(INSTALLDIR_BIN)/$(PROG_PREFIX)coreutils
@ -349,15 +363,18 @@ else
$(INSTALL) $(BUILDDIR)/$(prog) $(INSTALLDIR_BIN)/$(PROG_PREFIX)$(prog);) $(INSTALL) $(BUILDDIR)/$(prog) $(INSTALLDIR_BIN)/$(PROG_PREFIX)$(prog);)
$(if $(findstring test,$(INSTALLEES)), $(INSTALL) $(BUILDDIR)/test $(INSTALLDIR_BIN)/$(PROG_PREFIX)[) $(if $(findstring test,$(INSTALLEES)), $(INSTALL) $(BUILDDIR)/test $(INSTALLDIR_BIN)/$(PROG_PREFIX)[)
endif endif
mkdir -p $(DESTDIR)$(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)/zsh/site-functions
mkdir -p $(DESTDIR)$(DATAROOTDIR)/bash-completion/completions mkdir -p $(DESTDIR)$(DATAROOTDIR)/bash-completion/completions
mkdir -p $(DESTDIR)$(DATAROOTDIR)/fish/vendor_completions.d mkdir -p $(DESTDIR)$(DATAROOTDIR)/fish/vendor_completions.d
mkdir -p $(DESTDIR)$(DATAROOTDIR)/man/man1
$(foreach prog, $(INSTALLEES), \ $(foreach prog, $(INSTALLEES), \
$(BUILDDIR)/coreutils completion $(prog) zsh > $(DESTDIR)$(DATAROOTDIR)/zsh/site-functions/_$(PROG_PREFIX)$(prog); \ $(INSTALL) $(BUILDDIR)/completions/zsh/_$(PROG_PREFIX)$(prog) $(DESTDIR)$(DATAROOTDIR)/zsh/site-functions/; \
$(BUILDDIR)/coreutils completion $(prog) bash > $(DESTDIR)$(DATAROOTDIR)/bash-completion/completions/$(PROG_PREFIX)$(prog); \ $(INSTALL) $(BUILDDIR)/completions/bash/$(PROG_PREFIX)$(prog) $(DESTDIR)$(DATAROOTDIR)/bash-completion/completions/; \
$(BUILDDIR)/coreutils completion $(prog) fish > $(DESTDIR)$(DATAROOTDIR)/fish/vendor_completions.d/$(PROG_PREFIX)$(prog).fish; \ $(INSTALL) $(BUILDDIR)/completions/fish/$(PROG_PREFIX)$(prog).fish $(DESTDIR)$(DATAROOTDIR)/fish/vendor_completions.d/; \
$(BUILDDIR)/coreutils manpage $(prog) > $(DESTDIR)$(DATAROOTDIR)/man/man1/$(PROG_PREFIX)$(prog).1; \
) )
uninstall: uninstall:
@ -369,5 +386,6 @@ endif
rm -f $(addprefix $(DESTDIR)$(DATAROOTDIR)/zsh/site-functions/_$(PROG_PREFIX),$(PROGS)) rm -f $(addprefix $(DESTDIR)$(DATAROOTDIR)/zsh/site-functions/_$(PROG_PREFIX),$(PROGS))
rm -f $(addprefix $(DESTDIR)$(DATAROOTDIR)/bash-completion/completions/$(PROG_PREFIX),$(PROGS)) rm -f $(addprefix $(DESTDIR)$(DATAROOTDIR)/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)/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 .PHONY: all build build-coreutils build-pkgs test distclean clean busytest install uninstall

View file

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

View file

@ -1,6 +1,7 @@
<!-- markdownlint-disable MD033 MD041 MD002 --> <!-- markdownlint-disable MD033 MD041 MD002 -->
<!-- markdownlint-disable commands-show-output no-duplicate-heading --> <!-- 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"> <div align="center">
![uutils logo](docs/src/logo.svg) ![uutils logo](docs/src/logo.svg)
@ -19,11 +20,14 @@
--- ---
</div>
uutils coreutils is a cross-platform reimplementation of the GNU coreutils in 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 [Rust](http://www.rust-lang.org). While all programs have been implemented, some
options might be missing or different behavior might be experienced. options might be missing or different behavior might be experienced.
<div class="oranda-hide">
To install it: To install it:
```shell ```shell
@ -31,6 +35,8 @@ cargo install coreutils
~/.cargo/bin/coreutils ~/.cargo/bin/coreutils
``` ```
</div>
<!-- markdownlint-disable-next-line MD026 --> <!-- markdownlint-disable-next-line MD026 -->
## Goals ## 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, utils on Linux, Mac, Windows and other platforms. This ensures, for example,
that scripts can be easily transferred between platforms. that scripts can be easily transferred between platforms.
<div class="oranda-hide">
## Documentation ## Documentation
uutils has both user and developer documentation available: 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 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. the [coreutils docs](https://github.com/uutils/uutils.github.io) repository.
<!-- ANCHOR: build (this mark is needed for mdbook) --> <!-- ANCHOR: build (this mark is needed for mdbook) -->
## Requirements ## 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) ![Evolution over time](https://github.com/uutils/coreutils-tracking/blob/main/gnu-results.png?raw=true)
</div> <!-- close oranda-hide div -->
## Contributing ## Contributing
To contribute to uutils, please see [CONTRIBUTING](CONTRIBUTING.md). 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(); let mut mf = File::create(Path::new(&out_dir).join("uutils_map.rs")).unwrap();
mf.write_all( 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\ \n\
fn util_map<T: uucore::Args>() -> UtilityMap<T> {\n" fn util_map<T: uucore::Args>() -> UtilityMap<T> {\n"
.as_bytes(), .as_bytes(),
) )
.unwrap(); .unwrap();
let mut phf_map = phf_codegen::Map::<&str>::new(); let mut phf_map = phf_codegen::OrderedMap::<&str>::new();
for krate in &crates { for krate in &crates {
let map_value = format!("({krate}::uumain, {krate}::uu_app)"); let map_value = format!("({krate}::uumain, {krate}::uu_app)");
match krate.as_ref() { match krate.as_ref() {

View file

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

View file

@ -6,7 +6,7 @@ src = "src"
title = "uutils Documentation" title = "uutils Documentation"
[output.html] [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] [preprocessor.toc]
command = "mdbook-toc" command = "mdbook-toc"

View file

@ -61,7 +61,20 @@ feature is adopted from [FreeBSD](https://www.freebsd.org/cgi/man.cgi?cut).
## `fmt` ## `fmt`
`fmt` has additional flags for prefixes: `-P/--skip-prefix`, `-x/--exact-prefix`, and `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 `-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 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. 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
[![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 ```shell
pacman -S uutils-coreutils 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 { fn gen_coreutils_app<T: uucore::Args>(util_map: &UtilityMap<T>) -> Command {
let mut command = Command::new("coreutils"); let mut command = Command::new("coreutils");
for (_, (_, sub_app)) in util_map { for (name, (_, sub_app)) in util_map {
command = command.subcommand(sub_app()); // 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 command
} }

View file

@ -2,7 +2,7 @@
// //
// For the full copyright and license information, please view the LICENSE // For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code. // file that was distributed with this source code.
// spell-checker:ignore tldr // spell-checker:ignore tldr uuhelp
use clap::Command; use clap::Command;
use std::collections::HashMap; use std::collections::HashMap;
@ -178,7 +178,7 @@ impl<'a, 'b> MDWriter<'a, 'b> {
fn usage(&mut self) -> io::Result<()> { fn usage(&mut self) -> io::Result<()> {
if let Some(markdown) = &self.markdown { 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); let usage = usage.replace("{}", self.name);
writeln!(self.w, "\n```")?; writeln!(self.w, "\n```")?;
@ -191,7 +191,7 @@ impl<'a, 'b> MDWriter<'a, 'b> {
fn about(&mut self) -> io::Result<()> { fn about(&mut self) -> io::Result<()> {
if let Some(markdown) = &self.markdown { if let Some(markdown) = &self.markdown {
writeln!(self.w, "{}", help_parser::parse_about(markdown)) writeln!(self.w, "{}", uuhelp_parser::parse_about(markdown))
} else { } else {
Ok(()) Ok(())
} }
@ -199,7 +199,7 @@ impl<'a, 'b> MDWriter<'a, 'b> {
fn after_help(&mut self) -> io::Result<()> { fn after_help(&mut self) -> io::Result<()> {
if let Some(markdown) = &self.markdown { 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}"); 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] [package]
name = "uu_arch" name = "uu_arch"
version = "0.0.18" version = "0.0.20"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "arch ~ (uutils) display machine architecture" description = "arch ~ (uutils) display machine architecture"
@ -15,9 +15,9 @@ edition = "2021"
path = "src/arch.rs" path = "src/arch.rs"
[dependencies] [dependencies]
platform-info = { workspace=true } platform-info = { workspace = true }
clap = { workspace=true } clap = { workspace = true }
uucore = { workspace=true } uucore = { workspace = true }
[[bin]] [[bin]]
name = "arch" name = "arch"

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -43,7 +43,8 @@ use uucore::fs::{
}; };
use uucore::update_control::{self, UpdateMode}; use uucore::update_control::{self, UpdateMode};
use uucore::{ 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; use crate::copydir::copy_directory;
@ -229,10 +230,79 @@ pub struct Options {
backup_suffix: String, backup_suffix: String,
target_dir: Option<PathBuf>, target_dir: Option<PathBuf>,
update: UpdateMode, update: UpdateMode,
debug: bool,
verbose: bool, verbose: bool,
progress_bar: 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 ABOUT: &str = help_about!("cp.md");
const USAGE: &str = help_usage!("cp.md"); const USAGE: &str = help_usage!("cp.md");
const AFTER_HELP: &str = help_section!("after help", "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 STRIP_TRAILING_SLASHES: &str = "strip-trailing-slashes";
pub const SYMBOLIC_LINK: &str = "symbolic-link"; pub const SYMBOLIC_LINK: &str = "symbolic-link";
pub const TARGET_DIRECTORY: &str = "target-directory"; pub const TARGET_DIRECTORY: &str = "target-directory";
pub const DEBUG: &str = "debug";
pub const VERBOSE: &str = "verbose"; pub const VERBOSE: &str = "verbose";
} }
@ -312,6 +383,7 @@ pub fn uu_app() -> Command {
backup_control::BACKUP_CONTROL_LONG_HELP backup_control::BACKUP_CONTROL_LONG_HELP
)) ))
.infer_long_args(true) .infer_long_args(true)
.args_override_self(true)
.arg( .arg(
Arg::new(options::TARGET_DIRECTORY) Arg::new(options::TARGET_DIRECTORY)
.short('t') .short('t')
@ -369,6 +441,12 @@ pub fn uu_app() -> Command {
.help("remove any trailing slashes from each SOURCE argument") .help("remove any trailing slashes from each SOURCE argument")
.action(ArgAction::SetTrue), .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(
Arg::new(options::VERBOSE) Arg::new(options::VERBOSE)
.short('v') .short('v')
@ -438,6 +516,7 @@ pub fn uu_app() -> Command {
PRESERVABLE_ATTRIBUTES, PRESERVABLE_ATTRIBUTES,
)) ))
.num_args(0..) .num_args(0..)
.require_equals(true)
.value_name("ATTR_LIST") .value_name("ATTR_LIST")
.overrides_with_all([ .overrides_with_all([
options::ARCHIVE, options::ARCHIVE,
@ -839,7 +918,8 @@ impl Options {
one_file_system: matches.get_flag(options::ONE_FILE_SYSTEM), one_file_system: matches.get_flag(options::ONE_FILE_SYSTEM),
parents: matches.get_flag(options::PARENTS), parents: matches.get_flag(options::PARENTS),
update: update_mode, 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), strip_trailing_slashes: matches.get_flag(options::STRIP_TRAILING_SLASHES),
reflink_mode: { reflink_mode: {
if let Some(reflink) = matches.get_one::<String>(options::REFLINK) { 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. /// 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) {
fn show_error_if_needed(error: &Error) -> bool {
match error { match error {
// When using --no-clobber, we don't want to show // When using --no-clobber, we don't want to show
// an error message // an error message
Error::NotAllFilesCopied => (), Error::NotAllFilesCopied => {
// Need to return an error code
}
Error::Skipped => { Error::Skipped => {
// touch a b && echo "n"|cp -i a b && echo $? // touch a b && echo "n"|cp -i a b && echo $?
// should return an error from GNU 9.2 // should return an error from GNU 9.2
return true;
} }
_ => { _ => {
show_error!("{}", error); show_error!("{}", error);
return true;
} }
} }
false
} }
/// Copy all `sources` to `target`. Returns an /// Copy all `sources` to `target`. Returns an
@ -1098,9 +1176,8 @@ fn copy(sources: &[Source], target: &TargetSlice, options: &Options) -> CopyResu
options, options,
&mut symlinked_files, &mut symlinked_files,
) { ) {
if show_error_if_needed(&error) { show_error_if_needed(&error);
non_fatal_errors = true; non_fatal_errors = true;
}
} }
} }
seen_sources.insert(source); seen_sources.insert(source);
@ -1177,13 +1254,23 @@ fn copy_source(
} }
impl OverwriteMode { impl OverwriteMode {
fn verify(&self, path: &Path) -> CopyResult<()> { fn verify(&self, path: &Path, verbose: bool) -> CopyResult<()> {
match *self { 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(_) => { Self::Interactive(_) => {
if prompt_yes!("overwrite {}?", path.quote()) { if prompt_yes!("overwrite {}?", path.quote()) {
Ok(()) Ok(())
} else { } else {
if verbose {
println!("skipped {}", path.quote());
}
Err(Error::Skipped) Err(Error::Skipped)
} }
} }
@ -1391,7 +1478,7 @@ fn handle_existing_dest(
return Err(format!("{} and {} are the same file", source.quote(), dest.quote()).into()); 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); let backup_path = backup_control::get_backup_path(options.backup, dest, &options.backup_suffix);
if let Some(backup_path) = backup_path { if let Some(backup_path) = backup_path {
@ -1749,11 +1836,11 @@ fn copy_helper(
File::create(dest).context(dest.display().to_string())?; File::create(dest).context(dest.display().to_string())?;
} else if source_is_fifo && options.recursive && !options.copy_contents { } else if source_is_fifo && options.recursive && !options.copy_contents {
#[cfg(unix)] #[cfg(unix)]
copy_fifo(dest, options.overwrite)?; copy_fifo(dest, options.overwrite, options.verbose)?;
} else if source_is_symlink { } else if source_is_symlink {
copy_link(source, dest, symlinked_files)?; copy_link(source, dest, symlinked_files)?;
} else { } else {
copy_on_write( let copy_debug = copy_on_write(
source, source,
dest, dest,
options.reflink_mode, options.reflink_mode,
@ -1762,6 +1849,10 @@ fn copy_helper(
#[cfg(any(target_os = "linux", target_os = "android", target_os = "macos"))] #[cfg(any(target_os = "linux", target_os = "android", target_os = "macos"))]
source_is_fifo, source_is_fifo,
)?; )?;
if !options.attributes_only && options.debug {
show_debug(&copy_debug);
}
} }
Ok(()) Ok(())
@ -1770,9 +1861,9 @@ fn copy_helper(
// "Copies" a FIFO by creating a new one. This workaround is because Rust's // "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). // built-in fs::copy does not handle FIFOs (see rust-lang/rust/issues/79390).
#[cfg(unix)] #[cfg(unix)]
fn copy_fifo(dest: &Path, overwrite: OverwriteMode) -> CopyResult<()> { fn copy_fifo(dest: &Path, overwrite: OverwriteMode, verbose: bool) -> CopyResult<()> {
if dest.exists() { if dest.exists() {
overwrite.verify(dest)?; overwrite.verify(dest, verbose)?;
fs::remove_file(dest)?; fs::remove_file(dest)?;
} }

View file

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

View file

@ -11,7 +11,7 @@ use std::path::Path;
use quick_error::ResultExt; 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. /// Copies `source` to `dest` using copy-on-write if possible.
/// ///
@ -24,10 +24,15 @@ pub(crate) fn copy_on_write(
sparse_mode: SparseMode, sparse_mode: SparseMode,
context: &str, context: &str,
source_is_fifo: bool, source_is_fifo: bool,
) -> CopyResult<()> { ) -> CopyResult<CopyDebug> {
if sparse_mode != SparseMode::Auto { if sparse_mode != SparseMode::Auto {
return Err("--sparse is only supported on linux".to_string().into()); 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. // 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 // 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()) return Err(format!("failed to clone {source:?} from {dest:?}: {error}").into())
} }
_ => { _ => {
copy_debug.reflink = OffloadReflinkDebug::Yes;
if source_is_fifo { if source_is_fifo {
let mut src_file = File::open(source)?; let mut src_file = File::open(source)?;
let mut dst_file = File::create(dest)?; 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 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 /// Copies `source` to `dest` for systems without copy-on-write
pub(crate) fn copy_on_write( pub(crate) fn copy_on_write(
@ -17,7 +17,7 @@ pub(crate) fn copy_on_write(
reflink_mode: ReflinkMode, reflink_mode: ReflinkMode,
sparse_mode: SparseMode, sparse_mode: SparseMode,
context: &str, context: &str,
) -> CopyResult<()> { ) -> CopyResult<CopyDebug> {
if reflink_mode != ReflinkMode::Never { if reflink_mode != ReflinkMode::Never {
return Err("--reflink is only supported on linux and macOS" return Err("--reflink is only supported on linux and macOS"
.to_string() .to_string()
@ -26,8 +26,12 @@ pub(crate) fn copy_on_write(
if sparse_mode != SparseMode::Auto { if sparse_mode != SparseMode::Auto {
return Err("--sparse is only supported on linux".to_string().into()); 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)?; fs::copy(source, dest).context(context)?;
Ok(()) Ok(copy_debug)
} }

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_csplit" name = "uu_csplit"
version = "0.0.18" version = "0.0.20"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" 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" 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" path = "src/csplit.rs"
[dependencies] [dependencies]
clap = { workspace=true } clap = { workspace = true }
thiserror = { workspace = true } thiserror = { workspace = true }
regex = { workspace=true } regex = { workspace = true }
uucore = { workspace=true, features=["entries", "fs"] } uucore = { workspace = true, features = ["entries", "fs"] }
[[bin]] [[bin]]
name = "csplit" name = "csplit"

View file

@ -127,7 +127,7 @@ where
I: Iterator<Item = (usize, io::Result<String>)>, I: Iterator<Item = (usize, io::Result<String>)>,
{ {
// split the file based on patterns // split the file based on patterns
for pattern in patterns.into_iter() { for pattern in patterns {
let pattern_as_str = pattern.to_string(); let pattern_as_str = pattern.to_string();
let is_skip = matches!(pattern, patterns::Pattern::SkipToMatch(_, _, _)); let is_skip = matches!(pattern, patterns::Pattern::SkipToMatch(_, _, _));
match pattern { 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)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@ -714,106 +817,3 @@ mod tests {
assert!(input_splitter.next().is_none()); 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] [package]
name = "uu_cut" name = "uu_cut"
version = "0.0.18" version = "0.0.20"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "cut ~ (uutils) display byte/field columns of input lines" description = "cut ~ (uutils) display byte/field columns of input lines"
@ -15,11 +15,11 @@ edition = "2021"
path = "src/cut.rs" path = "src/cut.rs"
[dependencies] [dependencies]
clap = { workspace=true } clap = { workspace = true }
uucore = { workspace=true } uucore = { workspace = true }
memchr = { workspace=true } memchr = { workspace = true }
bstr = { workspace=true } bstr = { workspace = true }
is-terminal = { workspace=true } is-terminal = { workspace = true }
[[bin]] [[bin]]
name = "cut" name = "cut"

View file

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

View file

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

View file

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

View file

@ -13,7 +13,7 @@ Copy, and optionally convert, a file system resource
### Operands ### 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`. overwrites `ibs` and `obs`.
- `cbs=BYTES` : the 'conversion block size' in bytes. Applies to the - `cbs=BYTES` : the 'conversion block size' in bytes. Applies to the
`conv=block`, and `conv=unblock` operations. `conv=block`, and `conv=unblock` operations.
@ -114,7 +114,7 @@ Copy, and optionally convert, a file system resource
### General Flags ### 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 - `directory` : fail unless the given input (if used as an iflag) or
output (if used as an oflag) is a directory. output (if used as an oflag) is a directory.
- `dsync` : use synchronized I/O for data. - `dsync` : use synchronized I/O for data.

View file

@ -36,9 +36,12 @@ use std::os::unix::{
io::{AsRawFd, FromRawFd}, io::{AsRawFd, FromRawFd},
}; };
use std::path::Path; use std::path::Path;
use std::sync::mpsc; use std::sync::{
atomic::{AtomicBool, Ordering::Relaxed},
mpsc, Arc,
};
use std::thread; use std::thread;
use std::time; use std::time::{Duration, Instant};
use clap::{crate_version, Arg, Command}; use clap::{crate_version, Arg, Command};
use gcd::Gcd; use gcd::Gcd;
@ -75,6 +78,45 @@ struct Settings {
status: Option<StatusLevel>, 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 /// A number in blocks or bytes
/// ///
/// Some values (seek, skip, iseek, oseek) can have values either in blocks or in 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, // of its report includes the throughput in bytes per second,
// which requires knowing how long the process has been // which requires knowing how long the process has been
// running. // running.
let start = time::Instant::now(); let start = Instant::now();
// A good buffer size for reading. // A good buffer size for reading.
// //
@ -780,7 +822,6 @@ fn dd_copy(mut i: Input, mut o: Output) -> std::io::Result<()> {
// information. // information.
let (prog_tx, rx) = mpsc::channel(); let (prog_tx, rx) = mpsc::channel();
let output_thread = thread::spawn(gen_prog_updater(rx, i.settings.status)); 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 // Optimization: if no blocks are to be written, then don't
// bother allocating any buffers. // 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. // This is the max size needed.
let mut buf = vec![BUF_INIT_BYTE; bsize]; 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 // Index in the input file where we are reading bytes and in
// the output file where we are writing bytes. // 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. // error.
rstat += rstat_update; rstat += rstat_update;
wstat += wstat_update; wstat += wstat_update;
let prog_update = ProgUpdate::new(rstat, wstat, start.elapsed(), false); if alarm.is_triggered() {
if prog_update.duration.as_secs() >= progress_as_secs { let prog_update = ProgUpdate::new(rstat, wstat, start.elapsed(), false);
progress_as_secs = prog_update.duration.as_secs() + 1;
prog_tx.send(prog_update).unwrap_or(()); prog_tx.send(prog_update).unwrap_or(());
} }
} }
@ -885,7 +931,7 @@ fn finalize<T>(
output: &mut Output, output: &mut Output,
rstat: ReadStat, rstat: ReadStat,
wstat: WriteStat, wstat: WriteStat,
start: time::Instant, start: Instant,
prog_tx: &mpsc::Sender<ProgUpdate>, prog_tx: &mpsc::Sender<ProgUpdate>,
output_thread: thread::JoinHandle<T>, output_thread: thread::JoinHandle<T>,
) -> std::io::Result<()> { ) -> std::io::Result<()> {

View file

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

View file

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

View file

@ -746,7 +746,7 @@ mod tests {
#[test] #[test]
fn test_remote_included() { fn test_remote_included() {
let opt = Default::default(); let opt = Options::default();
let m = mount_info("ext4", "/mnt/foo", true, false); let m = mount_info("ext4", "/mnt/foo", true, false);
assert!(is_included(&m, &opt)); assert!(is_included(&m, &opt));
} }
@ -773,7 +773,7 @@ mod tests {
#[test] #[test]
fn test_dummy_excluded() { fn test_dummy_excluded() {
let opt = Default::default(); let opt = Options::default();
let m = mount_info("ext4", "/mnt/foo", false, true); let m = mount_info("ext4", "/mnt/foo", false, true);
assert!(!is_included(&m, &opt)); assert!(!is_included(&m, &opt));
} }
@ -864,11 +864,11 @@ mod tests {
mod filter_mount_list { mod filter_mount_list {
use crate::filter_mount_list; use crate::{filter_mount_list, Options};
#[test] #[test]
fn test_empty() { fn test_empty() {
let opt = Default::default(); let opt = Options::default();
let mount_infos = vec![]; let mount_infos = vec![];
assert!(filter_mount_list(mount_infos, &opt).is_empty()); 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 /// This function returns the element of `mounts` on which `path` is
/// mounted. If there are no matches, this function returns /// mounted. If there are no matches, this function returns
/// [`None`]. If there are two or more matches, then the single /// [`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 /// If `canonicalize` is `true`, then the `path` is canonicalized
/// before checking whether it matches any mount directories. /// before checking whether it matches any mount directories.
@ -68,9 +68,19 @@ where
path.as_ref().to_path_buf() path.as_ref().to_path_buf()
}; };
// Find the potential mount point that matches entered path
let maybe_mount_point = mounts let maybe_mount_point = mounts
.iter() .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(|| { maybe_mount_point.or_else(|| {
mounts mounts
@ -148,12 +158,12 @@ mod tests {
// Create a fake `MountInfo` with the given directory name. // Create a fake `MountInfo` with the given directory name.
fn mount_info(mount_dir: &str) -> MountInfo { fn mount_info(mount_dir: &str) -> MountInfo {
MountInfo { MountInfo {
dev_id: Default::default(), dev_id: String::default(),
dev_name: Default::default(), dev_name: String::default(),
fs_type: Default::default(), fs_type: String::default(),
mount_dir: String::from(mount_dir), mount_dir: String::from(mount_dir),
mount_option: Default::default(), mount_option: String::default(),
mount_root: Default::default(), mount_root: String::default(),
remote: Default::default(), remote: Default::default(),
dummy: Default::default(), dummy: Default::default(),
} }
@ -211,10 +221,16 @@ mod tests {
#[test] #[test]
fn test_dev_name_match() { 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"); 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 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])); assert!(mount_info_eq(actual, &mounts[0]));
} }
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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

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

View file

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

View file

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

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_factor" name = "uu_factor"
version = "0.0.18" version = "0.0.20"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "factor ~ (uutils) display the prime factors of each NUMBER" description = "factor ~ (uutils) display the prime factors of each NUMBER"
@ -12,15 +12,15 @@ categories = ["command-line-utilities"]
edition = "2021" edition = "2021"
[build-dependencies] [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] [dependencies]
clap = { workspace=true } clap = { workspace = true }
coz = { workspace=true, optional = true } coz = { workspace = true, optional = true }
num-traits = { workspace=true } num-traits = { workspace = true }
rand = { workspace=true } rand = { workspace = true }
smallvec = { workspace=true } smallvec = { workspace = true }
uucore = { workspace=true } uucore = { workspace = true }
[dev-dependencies] [dev-dependencies]
quickcheck = "1.0.3" quickcheck = "1.0.3"

View file

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

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_fmt" name = "uu_fmt"
version = "0.0.18" version = "0.0.20"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "fmt ~ (uutils) reformat each paragraph of input" description = "fmt ~ (uutils) reformat each paragraph of input"
@ -15,9 +15,9 @@ edition = "2021"
path = "src/fmt.rs" path = "src/fmt.rs"
[dependencies] [dependencies]
clap = { workspace=true } clap = { workspace = true }
unicode-width = { workspace=true } unicode-width = { workspace = true }
uucore = { workspace=true } uucore = { workspace = true }
[[bin]] [[bin]]
name = "fmt" 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); self.prev_punct && (before_tab.is_some() || word_start_relative > 1);
// now record whether this word ends in punctuation // 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), Some(ch) => WordSplit::is_punctuation(ch),
_ => panic!("fatal: expected word not to be empty"), _ => panic!("fatal: expected word not to be empty"),
}; };

View file

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

View file

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

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_hashsum" name = "uu_hashsum"
version = "0.0.18" version = "0.0.20"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "hashsum ~ (uutils) display or check input digests" description = "hashsum ~ (uutils) display or check input digests"
@ -15,11 +15,11 @@ edition = "2021"
path = "src/hashsum.rs" path = "src/hashsum.rs"
[dependencies] [dependencies]
clap = { workspace=true } clap = { workspace = true }
uucore = { workspace=true, features=["sum"] } uucore = { workspace = true, features = ["sum"] }
memchr = { workspace=true } memchr = { workspace = true }
regex = { workspace=true } regex = { workspace = true }
hex = { workspace=true } hex = { workspace = true }
[[bin]] [[bin]]
name = "hashsum" 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" // if there is no program name for some reason, default to "hashsum"
let program = args.next().unwrap_or_else(|| OsString::from(NAME)); let program = args.next().unwrap_or_else(|| OsString::from(NAME));
let binary_name = Path::new(&program) let binary_name = Path::new(&program)
.file_name() .file_stem()
.unwrap_or_else(|| OsStr::new(NAME)) .unwrap_or_else(|| OsStr::new(NAME))
.to_string_lossy(); .to_string_lossy();

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -17,6 +17,8 @@ use lscolors::LsColors;
use number_prefix::NumberPrefix; use number_prefix::NumberPrefix;
use once_cell::unsync::OnceCell; use once_cell::unsync::OnceCell;
use std::collections::HashSet; use std::collections::HashSet;
use std::num::IntErrorKind;
#[cfg(windows)] #[cfg(windows)]
use std::os::windows::fs::MetadataExt; use std::os::windows::fs::MetadataExt;
use std::{ use std::{
@ -294,6 +296,7 @@ enum Sort {
Time, Time,
Version, Version,
Extension, Extension,
Width,
} }
#[derive(PartialEq)] #[derive(PartialEq)]
@ -496,6 +499,7 @@ fn extract_sort(options: &clap::ArgMatches) -> Sort {
"size" => Sort::Size, "size" => Sort::Size,
"version" => Sort::Version, "version" => Sort::Version,
"extension" => Sort::Extension, "extension" => Sort::Extension,
"width" => Sort::Width,
// below should never happen as clap already restricts the values. // below should never happen as clap already restricts the values.
_ => unreachable!("Invalid field for --sort"), _ => 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 { impl Config {
#[allow(clippy::cognitive_complexity)] #[allow(clippy::cognitive_complexity)]
pub fn from(options: &clap::ArgMatches) -> UResult<Self> { pub fn from(options: &clap::ArgMatches) -> UResult<Self> {
@ -793,20 +810,7 @@ impl Config {
}; };
let width = match options.get_one::<String>(options::WIDTH) { let width = match options.get_one::<String>(options::WIDTH) {
Some(x) => { Some(x) => parse_width(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()),
}
}
}
None => match terminal_size::terminal_size() { None => match terminal_size::terminal_size() {
Some((width, _)) => width.0, Some((width, _)) => width.0,
None => match std::env::var_os("COLUMNS") { None => match std::env::var_os("COLUMNS") {
@ -1200,6 +1204,7 @@ pub fn uu_app() -> Command {
Arg::new(options::quoting::LITERAL) Arg::new(options::quoting::LITERAL)
.short('N') .short('N')
.long(options::quoting::LITERAL) .long(options::quoting::LITERAL)
.alias("l")
.help("Use literal quoting style. Equivalent to `--quoting-style=literal`") .help("Use literal quoting style. Equivalent to `--quoting-style=literal`")
.overrides_with_all([ .overrides_with_all([
options::QUOTING_STYLE, options::QUOTING_STYLE,
@ -1322,9 +1327,9 @@ pub fn uu_app() -> Command {
.arg( .arg(
Arg::new(options::SORT) Arg::new(options::SORT)
.long(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_name("field")
.value_parser(["name", "none", "time", "size", "version", "extension"]) .value_parser(["name", "none", "time", "size", "version", "extension", "width"])
.require_equals(true) .require_equals(true)
.overrides_with_all([ .overrides_with_all([
options::SORT, 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))), 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 // The default sort in GNU ls is case insensitive
Sort::Name => entries.sort_by(|a, b| a.display_name.cmp(&b.display_name)), Sort::Name => entries.sort_by(|a, b| a.display_name.cmp(&b.display_name)),
Sort::Version => entries Sort::Version => entries.sort_by(|a, b| {
.sort_by(|a, b| version_cmp(&a.p_buf.to_string_lossy(), &b.p_buf.to_string_lossy())), 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| { Sort::Extension => entries.sort_by(|a, b| {
a.p_buf a.p_buf
.extension() .extension()
.cmp(&b.p_buf.extension()) .cmp(&b.p_buf.extension())
.then(a.p_buf.file_stem().cmp(&b.p_buf.file_stem())) .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 => {} Sort::None => {}
} }
@ -3000,6 +3013,20 @@ fn display_inode(metadata: &Metadata) -> String {
#[allow(unused_variables)] #[allow(unused_variables)]
fn get_security_context(config: &Config, p_buf: &Path, must_dereference: bool) -> String { fn get_security_context(config: &Config, p_buf: &Path, must_dereference: bool) -> String {
let substitute_string = "?".to_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 { if config.selinux_supported {
#[cfg(feature = "selinux")] #[cfg(feature = "selinux")]
{ {

View file

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

View file

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

View file

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

View file

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

View file

@ -8,7 +8,7 @@
// spell-checker:ignore (paths) GPGHome findxs // 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::display::{println_verbatim, Quotable};
use uucore::error::{FromIo, UError, UResult, UUsageError}; use uucore::error::{FromIo, UError, UResult, UUsageError};
use uucore::{format_usage, help_about, help_usage}; 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_QUIET: &str = "quiet";
static OPT_SUFFIX: &str = "suffix"; static OPT_SUFFIX: &str = "suffix";
static OPT_TMPDIR: &str = "tmpdir"; static OPT_TMPDIR: &str = "tmpdir";
static OPT_P: &str = "p";
static OPT_T: &str = "t"; static OPT_T: &str = "t";
static ARG_TEMPLATE: &str = "template"; static ARG_TEMPLATE: &str = "template";
@ -130,7 +131,7 @@ struct Options {
/// The directory in which to create the temporary file. /// The directory in which to create the temporary file.
/// ///
/// If `None`, the file will be created in the current directory. /// 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. /// The suffix to append to the temporary file, if any.
suffix: Option<String>, suffix: Option<String>,
@ -142,72 +143,32 @@ struct Options {
template: String, 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 { impl Options {
fn from(matches: &ArgMatches) -> Self { fn from(matches: &ArgMatches) -> Self {
// Special case to work around a limitation of `clap`; see let tmpdir = matches
// `is_tmpdir_argument_actually_the_template()` for more .get_one::<PathBuf>(OPT_TMPDIR)
// information. .or_else(|| matches.get_one::<PathBuf>(OPT_P))
// .cloned();
// Fixed in clap 3 let (tmpdir, template) = match matches.get_one::<String>(ARG_TEMPLATE) {
// 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 {
// If no template argument is given, `--tmpdir` is implied. // If no template argument is given, `--tmpdir` is implied.
match matches.get_one::<String>(ARG_TEMPLATE) { None => {
None => { let tmpdir = Some(tmpdir.unwrap_or_else(env::temp_dir));
let tmpdir = match matches.get_one::<String>(OPT_TMPDIR) { let template = DEFAULT_TEMPLATE;
None => Some(env::temp_dir().display().to_string()), (tmpdir, template.to_string())
Some(tmpdir) => Some(tmpdir.to_string()), }
}; Some(template) => {
let template = DEFAULT_TEMPLATE; let tmpdir = if env::var(TMPDIR_ENV_VAR).is_ok() && matches.get_flag(OPT_T) {
(tmpdir, template.to_string()) env::var_os(TMPDIR_ENV_VAR).map(|t| t.into())
} } else if tmpdir.is_some() {
Some(template) => { tmpdir
let tmpdir = if env::var(TMPDIR_ENV_VAR).is_ok() && matches.get_flag(OPT_T) { } else if matches.get_flag(OPT_T) || matches.contains_id(OPT_TMPDIR) {
env::var(TMPDIR_ENV_VAR).ok() // If --tmpdir is given without an argument, or -t is given
} else if matches.contains_id(OPT_TMPDIR) { // export in TMPDIR
matches.get_one::<String>(OPT_TMPDIR).map(String::from) Some(env::temp_dir())
} else if matches.get_flag(OPT_T) { } else {
// mktemp -t foo.xxx should export in TMPDIR None
Some(env::temp_dir().display().to_string()) };
} else { (tmpdir, template.to_string())
matches.get_one::<String>(OPT_TMPDIR).map(String::from)
};
(tmpdir, template.to_string())
}
} }
}; };
Self { Self {
@ -372,7 +333,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
if env::var("POSIXLY_CORRECT").is_ok() { if env::var("POSIXLY_CORRECT").is_ok() {
// If POSIXLY_CORRECT was set, template MUST be the last argument. // 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. // Template argument was provided, check if was the last one.
if args.last().unwrap() != &options.template { if args.last().unwrap() != &options.template {
return Err(Box::new(MkTempError::TooManyTemplates)); return Err(Box::new(MkTempError::TooManyTemplates));
@ -444,8 +405,16 @@ pub fn uu_app() -> Command {
.value_name("SUFFIX"), .value_name("SUFFIX"),
) )
.arg( .arg(
Arg::new(OPT_TMPDIR) Arg::new(OPT_P)
.short('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) .long(OPT_TMPDIR)
.help( .help(
"interpret TEMPLATE relative to DIR; if DIR is not specified, use \ "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, // Allows use of default argument just by setting --tmpdir. Else,
// use provided input to generate tmpdir // use provided input to generate tmpdir
.num_args(0..=1) .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), .value_hint(clap::ValueHint::DirPath),
) )
.arg( .arg(

View file

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

View file

@ -14,10 +14,10 @@ use std::{
time::Duration, 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::event::KeyEventKind;
use crossterm::{ use crossterm::{
cursor::MoveTo, cursor::{MoveTo, MoveUp},
event::{self, Event, KeyCode, KeyEvent, KeyModifiers}, event::{self, Event, KeyCode, KeyEvent, KeyModifiers},
execute, queue, execute, queue,
style::Attribute, style::Attribute,
@ -50,19 +50,37 @@ pub mod options {
pub const FILES: &str = "files"; 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 { struct Options {
silent: bool,
clean_print: bool, clean_print: bool,
from_line: usize,
lines: Option<u16>,
print_over: bool, print_over: bool,
silent: bool,
squeeze: bool, squeeze: bool,
} }
impl Options { impl Options {
fn from(matches: &ArgMatches) -> Self { 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 { Self {
clean_print: matches.get_flag(options::CLEAN_PRINT), clean_print: matches.get_flag(options::CLEAN_PRINT),
from_line,
lines,
print_over: matches.get_flag(options::PRINT_OVER), print_over: matches.get_flag(options::PRINT_OVER),
silent: matches.get_flag(options::SILENT), silent: matches.get_flag(options::SILENT),
squeeze: matches.get_flag(options::SQUEEZE), squeeze: matches.get_flag(options::SQUEEZE),
@ -78,7 +96,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
Err(e) => return Err(e.into()), Err(e) => return Err(e.into()),
}; };
let options = Options::from(&matches); let mut options = Options::from(&matches);
let mut buff = String::new(); 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()), 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) { let opened_file = match File::open(file) {
Err(why) => { Err(why) => {
terminal::disable_raw_mode().unwrap(); terminal::disable_raw_mode().unwrap();
@ -118,14 +133,21 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
}; };
let mut reader = BufReader::new(opened_file); let mut reader = BufReader::new(opened_file);
reader.read_to_string(&mut buff).unwrap(); 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(); buff.clear();
} }
reset_term(&mut stdout); reset_term(&mut stdout);
} else if !std::io::stdin().is_terminal() { } else if !std::io::stdin().is_terminal() {
stdin().read_to_string(&mut buff).unwrap(); stdin().read_to_string(&mut buff).unwrap();
let mut stdout = setup_term(); let mut stdout = setup_term();
more(&buff, &mut stdout, None, &options)?; more(&buff, &mut stdout, false, None, None, &mut options)?;
reset_term(&mut stdout); reset_term(&mut stdout);
} else { } else {
return Err(UUsageError::new(1, "bad usage")); return Err(UUsageError::new(1, "bad usage"));
@ -167,6 +189,38 @@ pub fn uu_app() -> Command {
.help("Squeeze multiple blank lines into one") .help("Squeeze multiple blank lines into one")
.action(ArgAction::SetTrue), .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: // The commented arguments below are unimplemented:
/* /*
.arg( .arg(
@ -181,37 +235,6 @@ pub fn uu_app() -> Command {
.long(options::NO_PAUSE) .long(options::NO_PAUSE)
.help("Suppress pause after form feed"), .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(
Arg::new(options::PATTERN) Arg::new(options::PATTERN)
.short('P') .short('P')
@ -260,15 +283,36 @@ fn reset_term(_: &mut usize) {}
fn more( fn more(
buff: &str, buff: &str,
stdout: &mut Stdout, stdout: &mut Stdout,
multiple_file: bool,
file: Option<&str>,
next_file: Option<&str>, next_file: Option<&str>,
options: &Options, options: &mut Options,
) -> UResult<()> { ) -> 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 lines = break_buff(buff, usize::from(cols));
let mut pager = Pager::new(rows, lines, next_file, options); 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); 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(()); return Ok(());
} }
@ -327,6 +371,7 @@ fn more(
.. ..
}) => { }) => {
pager.page_up(); pager.page_up();
paging_add_back_message(options, stdout)?;
} }
Event::Key(KeyEvent { Event::Key(KeyEvent {
code: KeyCode::Char('j'), code: KeyCode::Char('j'),
@ -347,7 +392,7 @@ fn more(
pager.prev_line(); pager.prev_line();
} }
Event::Resize(col, row) => { Event::Resize(col, row) => {
pager.page_resize(col, row); pager.page_resize(col, row, options.lines);
} }
Event::Key(KeyEvent { Event::Key(KeyEvent {
code: KeyCode::Char(k), 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 { fn new(rows: u16, lines: Vec<String>, next_file: Option<&'a str>, options: &Options) -> Self {
let line_count = lines.len(); let line_count = lines.len();
Self { Self {
upper_mark: 0, upper_mark: options.from_line,
content_rows: rows.saturating_sub(1), content_rows: rows.saturating_sub(1),
lines, lines,
next_file, next_file,
@ -447,15 +492,17 @@ impl<'a> Pager<'a> {
} }
// TODO: Deal with column size changes. // TODO: Deal with column size changes.
fn page_resize(&mut self, _: u16, row: u16) { fn page_resize(&mut self, _: u16, row: u16, option_line: Option<u16>) {
self.content_rows = row.saturating_sub(1); 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>) { fn draw(&mut self, stdout: &mut std::io::Stdout, wrong_key: Option<char>) {
self.draw_lines(stdout);
let lower_mark = self let lower_mark = self
.line_count .line_count
.min(self.upper_mark.saturating_add(self.content_rows.into())); .min(self.upper_mark.saturating_add(self.content_rows.into()));
self.draw_lines(stdout);
self.draw_prompt(stdout, lower_mark, wrong_key); self.draw_prompt(stdout, lower_mark, wrong_key);
stdout.flush().unwrap(); stdout.flush().unwrap();
} }
@ -515,7 +562,6 @@ impl<'a> Pager<'a> {
}; };
let status = format!("--More--({status_inner})"); let status = format!("--More--({status_inner})");
let banner = match (self.silent, wrong_key) { let banner = match (self.silent, wrong_key) {
(true, Some(key)) => format!( (true, Some(key)) => format!(
"{status} [Unknown key: '{key}'. Press 'h' for instructions. (unimplemented)]" "{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 // Break the lines on the cols of the terminal
fn break_buff(buff: &str, cols: usize) -> Vec<String> { fn break_buff(buff: &str, cols: usize) -> Vec<String> {
let mut lines = Vec::with_capacity(buff.lines().count()); let mut lines = Vec::with_capacity(buff.lines().count());

View file

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

View file

@ -22,10 +22,10 @@ use std::os::unix;
#[cfg(windows)] #[cfg(windows)]
use std::os::windows; use std::os::windows;
use std::path::{Path, PathBuf}; 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::display::Quotable;
use uucore::error::{set_exit_code, FromIo, UError, UResult, USimpleError, UUsageError}; 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::update_control::{self, UpdateMode};
use uucore::{format_usage, help_about, help_section, help_usage, prompt_yes, show}; 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<()> { 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() { if source.symlink_metadata().is_err() {
return Err(MvError::NoSuchFile(source.quote().to_string()).into()); return Err(MvError::NoSuchFile(source.quote().to_string()).into());
} }
if (source.eq(target) || are_hardlinks_to_same_file(source, target)) if (source.eq(target)
&& b.backup != BackupMode::SimpleBackup || 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() { if source.eq(Path::new(".")) || source.ends_with("/.") || source.is_file() {
return Err( 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()) { 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) => { Err(e) => {
let e = e.map_err_context(|| { let e = e.map_err_context(|| {
format!( format!(
@ -420,9 +433,7 @@ fn rename(
let mut backup_path = None; let mut backup_path = None;
if to.exists() { if to.exists() {
if (b.update == UpdateMode::ReplaceIfOlder || b.update == UpdateMode::ReplaceNone) if b.update == UpdateMode::ReplaceIfOlder && b.overwrite == OverwriteMode::Interactive {
&& b.overwrite == OverwriteMode::Interactive
{
// `mv -i --update old new` when `new` exists doesn't move anything // `mv -i --update old new` when `new` exists doesn't move anything
// and exit with 0 // and exit with 0
return Ok(()); return Ok(());

View file

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

View file

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

View file

@ -1,7 +1,23 @@
#nl # nl
``` ```
nl [OPTION]... [FILE]... nl [OPTION]... [FILE]...
``` ```
Number lines of files 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. // parse_style parses a style string into a NumberingStyle.
fn parse_style(chars: &[char]) -> Result<crate::NumberingStyle, String> { fn parse_style(chars: &[char]) -> Result<crate::NumberingStyle, String> {
if chars.len() == 1 && chars[0] == 'a' { if chars.len() == 1 && chars[0] == 'a' {
Ok(crate::NumberingStyle::NumberForAll) Ok(crate::NumberingStyle::All)
} else if chars.len() == 1 && chars[0] == 't' { } else if chars.len() == 1 && chars[0] == 't' {
Ok(crate::NumberingStyle::NumberForNonEmpty) Ok(crate::NumberingStyle::NonEmpty)
} else if chars.len() == 1 && chars[0] == 'n' { } else if chars.len() == 1 && chars[0] == 'n' {
Ok(crate::NumberingStyle::NumberForNone) Ok(crate::NumberingStyle::None)
} else if chars.len() > 1 && chars[0] == 'p' { } else if chars.len() > 1 && chars[0] == 'p' {
let s: String = chars[1..].iter().cloned().collect(); let s: String = chars[1..].iter().cloned().collect();
match regex::Regex::new(&s) { match regex::Regex::new(&s) {
Ok(re) => Ok(crate::NumberingStyle::NumberForRegularExpression(Box::new( Ok(re) => Ok(crate::NumberingStyle::Regex(Box::new(re))),
re,
))),
Err(_) => Err(String::from("Illegal regular expression")), Err(_) => Err(String::from("Illegal regular expression")),
} }
} else { } 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> { pub fn parse_options(settings: &mut crate::Settings, opts: &clap::ArgMatches) -> Vec<String> {
// This vector holds error messages encountered. // This vector holds error messages encountered.
let mut errs: Vec<String> = vec![]; let mut errs: Vec<String> = vec![];
settings.renumber = !opts.contains_id(options::NO_RENUMBER); settings.renumber = opts.get_flag(options::NO_RENUMBER);
match opts.get_one::<String>(options::NUMBER_SEPARATOR) { if let Some(val) = opts.get_one::<String>(options::NUMBER_SEPARATOR) {
None => {} settings.number_separator = val.to_owned();
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.number_format = opts
.get_one::<String>(options::NUMBER_FORMAT)
.map(Into::into)
.unwrap_or_default();
match opts.get_one::<String>(options::BODY_NUMBERING) { match opts.get_one::<String>(options::BODY_NUMBERING) {
None => {} None => {}
Some(val) => { 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 => {} None => {}
Some(val) => { Some(num) if *num > 0 => settings.number_width = *num,
let conv: Option<u64> = val.parse().ok(); Some(_) => errs.push(String::from(
match conv { "Invalid line number field width: 0: Numerical result out of range",
None => { )),
errs.push(String::from("Illegal value for -i"));
}
Some(num) => settings.line_increment = num,
}
}
} }
match opts.get_one::<String>(options::NUMBER_WIDTH) { match opts.get_one::<u64>(options::JOIN_BLANK_LINES) {
None => {} None => {}
Some(val) => { Some(num) if *num > 0 => settings.join_blank_lines = *num,
let conv: Option<usize> = val.parse().ok(); Some(_) => errs.push(String::from(
match conv { "Invalid line number of blank lines: 0: Numerical result out of range",
None => { )),
errs.push(String::from("Illegal value for -w"));
}
Some(num) => settings.number_width = num,
}
}
} }
match opts.get_one::<String>(options::STARTING_LINE_NUMBER) { if let Some(num) = opts.get_one::<i64>(options::LINE_INCREMENT) {
None => {} settings.line_increment = *num;
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,
}
}
} }
match opts.get_one::<String>(options::JOIN_BLANK_LINES) { if let Some(num) = opts.get_one::<i64>(options::STARTING_LINE_NUMBER) {
None => {} settings.starting_line_number = *num;
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,
}
}
} }
errs errs
} }

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