mirror of
https://github.com/uutils/coreutils.git
synced 2026-06-10 15:48:22 -07:00
Merge branch 'main' into dd-seconds-precision-3
This commit is contained in:
+100
-29
@@ -37,8 +37,6 @@ jobs:
|
||||
## ToDO: [2021-11-10; rivy] 'Style/deps' needs more informative output and better integration of results into the GHA dashboard
|
||||
name: Style/deps
|
||||
runs-on: ${{ matrix.job.os }}
|
||||
# env:
|
||||
# STYLE_FAIL_ON_FAULT: false # overrides workflow default
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -73,7 +71,6 @@ jobs:
|
||||
run: |
|
||||
rustup toolchain install nightly --no-self-update --profile minimal
|
||||
rustup default nightly
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Install `cargo-udeps`
|
||||
run: cargo install cargo-udeps
|
||||
env:
|
||||
@@ -93,8 +90,6 @@ jobs:
|
||||
style_format:
|
||||
name: Style/format
|
||||
runs-on: ${{ matrix.job.os }}
|
||||
# env:
|
||||
# STYLE_FAIL_ON_FAULT: false # overrides workflow default
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -124,7 +119,6 @@ jobs:
|
||||
## Install `rust` toolchain
|
||||
rustup toolchain install stable --no-self-update -c rustfmt --profile minimal
|
||||
rustup default stable
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: "`cargo fmt` testing"
|
||||
shell: bash
|
||||
run: |
|
||||
@@ -151,7 +145,7 @@ jobs:
|
||||
- name: Install `cargo-fuzz`
|
||||
run: cargo install cargo-fuzz
|
||||
- name: Run fuzz_date for XX seconds
|
||||
# TODO: fix the issues
|
||||
# TODO: fix https://github.com/uutils/coreutils/issues/4494
|
||||
continue-on-error: true
|
||||
shell: bash
|
||||
run: |
|
||||
@@ -159,8 +153,6 @@ jobs:
|
||||
cd fuzz
|
||||
cargo +nightly fuzz run fuzz_date -- -max_total_time=${{ env.RUN_FOR }} -detect_leaks=0
|
||||
- name: Run fuzz_parse_glob for XX seconds
|
||||
# TODO: fix the issues
|
||||
continue-on-error: true
|
||||
shell: bash
|
||||
run: |
|
||||
## Run it
|
||||
@@ -173,8 +165,6 @@ jobs:
|
||||
cd fuzz
|
||||
cargo +nightly fuzz run fuzz_parse_size -- -max_total_time=${{ env.RUN_FOR }} -detect_leaks=0
|
||||
- name: Run fuzz_parse_time for XX seconds
|
||||
# TODO: fix the issues
|
||||
continue-on-error: true
|
||||
shell: bash
|
||||
run: |
|
||||
## Run it
|
||||
@@ -184,8 +174,9 @@ jobs:
|
||||
style_lint:
|
||||
name: Style/lint
|
||||
runs-on: ${{ matrix.job.os }}
|
||||
# env:
|
||||
# STYLE_FAIL_ON_FAULT: false # overrides workflow default
|
||||
env:
|
||||
SCCACHE_GHA_ENABLED: "true"
|
||||
RUSTC_WRAPPER: "sccache"
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -195,6 +186,11 @@ jobs:
|
||||
- { os: windows-latest , features: feat_os_windows }
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Run sccache-cache
|
||||
uses: mozilla-actions/sccache-action@v0.0.2
|
||||
with:
|
||||
version: "v0.4.0-pre.11"
|
||||
- name: Initialize workflow variables
|
||||
id: vars
|
||||
shell: bash
|
||||
@@ -229,7 +225,6 @@ jobs:
|
||||
## Install `rust` toolchain
|
||||
rustup toolchain install stable --no-self-update -c clippy --profile minimal
|
||||
rustup default stable
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: "`cargo clippy` lint testing"
|
||||
shell: bash
|
||||
run: |
|
||||
@@ -244,15 +239,12 @@ jobs:
|
||||
style_spellcheck:
|
||||
name: Style/spelling
|
||||
runs-on: ${{ matrix.job.os }}
|
||||
# env:
|
||||
# STYLE_FAIL_ON_FAULT: false # overrides workflow default
|
||||
strategy:
|
||||
matrix:
|
||||
job:
|
||||
- { os: ubuntu-latest , features: feat_os_unix }
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Initialize workflow variables
|
||||
id: vars
|
||||
shell: bash
|
||||
@@ -292,6 +284,9 @@ jobs:
|
||||
doc_warnings:
|
||||
name: Documentation/warnings
|
||||
runs-on: ${{ matrix.job.os }}
|
||||
env:
|
||||
SCCACHE_GHA_ENABLED: "true"
|
||||
RUSTC_WRAPPER: "sccache"
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -304,6 +299,11 @@ jobs:
|
||||
# - { os: windows-latest , features: feat_os_windows }
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Run sccache-cache
|
||||
uses: mozilla-actions/sccache-action@v0.0.2
|
||||
with:
|
||||
version: "v0.4.0-pre.11"
|
||||
- name: Initialize workflow variables
|
||||
id: vars
|
||||
shell: bash
|
||||
@@ -331,7 +331,6 @@ jobs:
|
||||
## Install `rust` toolchain
|
||||
rustup toolchain install stable --no-self-update -c clippy --profile minimal
|
||||
rustup default stable
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: "`cargo doc` with warnings"
|
||||
shell: bash
|
||||
run: |
|
||||
@@ -347,12 +346,20 @@ jobs:
|
||||
min_version:
|
||||
name: MinRustV # Minimum supported rust version (aka, MinSRV or MSRV)
|
||||
runs-on: ${{ matrix.job.os }}
|
||||
env:
|
||||
SCCACHE_GHA_ENABLED: "true"
|
||||
RUSTC_WRAPPER: "sccache"
|
||||
strategy:
|
||||
matrix:
|
||||
job:
|
||||
- { os: ubuntu-latest , features: feat_os_unix }
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Run sccache-cache
|
||||
uses: mozilla-actions/sccache-action@v0.0.2
|
||||
with:
|
||||
version: "v0.4.0-pre.11"
|
||||
- name: Initialize workflow variables
|
||||
id: vars
|
||||
shell: bash
|
||||
@@ -369,7 +376,6 @@ jobs:
|
||||
## Install `rust` toolchain (v${{ env.RUST_MIN_SRV }})
|
||||
rustup toolchain install --no-self-update ${{ env.RUST_MIN_SRV }} --profile minimal
|
||||
rustup default ${{ env.RUST_MIN_SRV }}
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Confirm MinSRV compatible 'Cargo.lock'
|
||||
shell: bash
|
||||
run: |
|
||||
@@ -424,7 +430,6 @@ jobs:
|
||||
## Install `rust` toolchain
|
||||
rustup toolchain install stable --no-self-update --profile minimal
|
||||
rustup default stable
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: "`cargo update` testing"
|
||||
shell: bash
|
||||
run: |
|
||||
@@ -436,6 +441,9 @@ jobs:
|
||||
name: Build/Makefile
|
||||
needs: [ min_version, deps ]
|
||||
runs-on: ${{ matrix.job.os }}
|
||||
env:
|
||||
SCCACHE_GHA_ENABLED: "true"
|
||||
RUSTC_WRAPPER: "sccache"
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -443,12 +451,16 @@ jobs:
|
||||
- { os: ubuntu-latest , features: feat_os_unix }
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Run sccache-cache
|
||||
uses: mozilla-actions/sccache-action@v0.0.2
|
||||
with:
|
||||
version: "v0.4.0-pre.11"
|
||||
- name: Install `rust` toolchain
|
||||
run: |
|
||||
## Install `rust` toolchain
|
||||
rustup toolchain install stable --no-self-update --profile minimal
|
||||
rustup default stable
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: "`make build`"
|
||||
shell: bash
|
||||
run: |
|
||||
@@ -470,12 +482,14 @@ jobs:
|
||||
env:
|
||||
RUST_BACKTRACE: "1"
|
||||
|
||||
|
||||
build_rust_stable:
|
||||
name: Build/stable
|
||||
needs: [ min_version, deps ]
|
||||
runs-on: ${{ matrix.job.os }}
|
||||
timeout-minutes: 90
|
||||
env:
|
||||
SCCACHE_GHA_ENABLED: "true"
|
||||
RUSTC_WRAPPER: "sccache"
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -485,12 +499,16 @@ jobs:
|
||||
- { os: windows-latest , features: feat_os_windows }
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Run sccache-cache
|
||||
uses: mozilla-actions/sccache-action@v0.0.2
|
||||
with:
|
||||
version: "v0.4.0-pre.11"
|
||||
- name: Install `rust` toolchain
|
||||
run: |
|
||||
## Install `rust` toolchain
|
||||
rustup toolchain install stable --no-self-update --profile minimal
|
||||
rustup default stable
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Test
|
||||
run: cargo test ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }}
|
||||
env:
|
||||
@@ -501,6 +519,9 @@ jobs:
|
||||
needs: [ min_version, deps ]
|
||||
runs-on: ${{ matrix.job.os }}
|
||||
timeout-minutes: 90
|
||||
env:
|
||||
SCCACHE_GHA_ENABLED: "true"
|
||||
RUSTC_WRAPPER: "sccache"
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -510,12 +531,16 @@ jobs:
|
||||
- { os: windows-latest , features: feat_os_windows }
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Run sccache-cache
|
||||
uses: mozilla-actions/sccache-action@v0.0.2
|
||||
with:
|
||||
version: "v0.4.0-pre.11"
|
||||
- name: Install `rust` toolchain
|
||||
run: |
|
||||
## Install `rust` toolchain
|
||||
rustup toolchain install nightly --no-self-update --profile minimal
|
||||
rustup default nightly
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Test
|
||||
run: cargo test ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }}
|
||||
env:
|
||||
@@ -525,6 +550,9 @@ jobs:
|
||||
name: Binary sizes
|
||||
needs: [ min_version, deps ]
|
||||
runs-on: ${{ matrix.job.os }}
|
||||
env:
|
||||
SCCACHE_GHA_ENABLED: "true"
|
||||
RUSTC_WRAPPER: "sccache"
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -532,6 +560,11 @@ jobs:
|
||||
- { os: ubuntu-latest , features: feat_os_unix }
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Run sccache-cache
|
||||
uses: mozilla-actions/sccache-action@v0.0.2
|
||||
with:
|
||||
version: "v0.4.0-pre.11"
|
||||
- name: Install dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
@@ -543,7 +576,6 @@ jobs:
|
||||
## Install `rust` toolchain
|
||||
rustup toolchain install stable --no-self-update --profile minimal
|
||||
rustup default stable
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: "`make install`"
|
||||
shell: bash
|
||||
run: |
|
||||
@@ -579,6 +611,8 @@ jobs:
|
||||
timeout-minutes: 90
|
||||
env:
|
||||
DOCKER_OPTS: '--volume /etc/passwd:/etc/passwd --volume /etc/group:/etc/group'
|
||||
SCCACHE_GHA_ENABLED: "true"
|
||||
RUSTC_WRAPPER: "sccache"
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -605,6 +639,11 @@ jobs:
|
||||
- { os: windows-latest , target: x86_64-pc-windows-msvc , features: feat_os_windows }
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Run sccache-cache
|
||||
uses: mozilla-actions/sccache-action@v0.0.2
|
||||
with:
|
||||
version: "v0.4.0-pre.11"
|
||||
- name: Initialize workflow variables
|
||||
id: vars
|
||||
shell: bash
|
||||
@@ -732,9 +771,6 @@ jobs:
|
||||
## rust toolchain ~ install
|
||||
rustup toolchain install --no-self-update ${{ env.RUST_MIN_SRV }} -t ${{ matrix.job.target }} --profile minimal
|
||||
rustup default ${{ env.RUST_MIN_SRV }}
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
key: ${{ matrix.job.os }}-${{ matrix.job.target }}
|
||||
- name: Initialize toolchain-dependent workflow variables
|
||||
id: dep_vars
|
||||
shell: bash
|
||||
@@ -844,6 +880,9 @@ jobs:
|
||||
name: Tests/BusyBox test suite
|
||||
needs: [ min_version, deps ]
|
||||
runs-on: ${{ matrix.job.os }}
|
||||
env:
|
||||
SCCACHE_GHA_ENABLED: "true"
|
||||
RUSTC_WRAPPER: "sccache"
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -858,6 +897,10 @@ jobs:
|
||||
echo "TEST_SUMMARY_FILE=busybox-result.json" >> $GITHUB_OUTPUT
|
||||
- uses: actions/checkout@v3
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Run sccache-cache
|
||||
uses: mozilla-actions/sccache-action@v0.0.2
|
||||
with:
|
||||
version: "v0.4.0-pre.11"
|
||||
- name: Install/setup prerequisites
|
||||
shell: bash
|
||||
run: |
|
||||
@@ -917,6 +960,9 @@ jobs:
|
||||
name: Tests/Toybox test suite
|
||||
needs: [ min_version, deps ]
|
||||
runs-on: ${{ matrix.job.os }}
|
||||
env:
|
||||
SCCACHE_GHA_ENABLED: "true"
|
||||
RUSTC_WRAPPER: "sccache"
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -933,6 +979,10 @@ jobs:
|
||||
outputs TEST_SUMMARY_FILE
|
||||
- uses: actions/checkout@v3
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Run sccache-cache
|
||||
uses: mozilla-actions/sccache-action@v0.0.2
|
||||
with:
|
||||
version: "v0.4.0-pre.11"
|
||||
- name: rust toolchain ~ install
|
||||
run: |
|
||||
## rust toolchain ~ install
|
||||
@@ -1010,8 +1060,15 @@ jobs:
|
||||
arch: [x86] # , arm64-v8a
|
||||
env:
|
||||
TERMUX: v0.118.0
|
||||
SCCACHE_GHA_ENABLED: "true"
|
||||
RUSTC_WRAPPER: "sccache"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Run sccache-cache
|
||||
uses: mozilla-actions/sccache-action@v0.0.2
|
||||
with:
|
||||
version: "v0.4.0-pre.11"
|
||||
- name: AVD cache
|
||||
uses: actions/cache@v3
|
||||
id: avd-cache
|
||||
@@ -1065,9 +1122,15 @@ jobs:
|
||||
- { os: macos-12 , features: unix } ## GHA MacOS-11.0 VM won't have VirtualBox; refs: <https://github.com/actions/virtual-environments/issues/4060> , <https://github.com/actions/virtual-environments/pull/4010>
|
||||
env:
|
||||
mem: 4096
|
||||
SCCACHE_GHA_ENABLED: "true"
|
||||
RUSTC_WRAPPER: "sccache"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Run sccache-cache
|
||||
uses: mozilla-actions/sccache-action@v0.0.2
|
||||
with:
|
||||
version: "v0.4.0-pre.11"
|
||||
- name: Prepare, build and test
|
||||
## spell-checker:ignore (ToDO) sshfs usesh vmactions
|
||||
uses: vmactions/freebsd-vm@v0.3.0
|
||||
@@ -1132,6 +1195,9 @@ jobs:
|
||||
name: Code Coverage
|
||||
runs-on: ${{ matrix.job.os }}
|
||||
timeout-minutes: 90
|
||||
env:
|
||||
SCCACHE_GHA_ENABLED: "true"
|
||||
RUSTC_WRAPPER: "sccache"
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -1141,6 +1207,11 @@ jobs:
|
||||
- { os: windows-latest , features: windows }
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Run sccache-cache
|
||||
uses: mozilla-actions/sccache-action@v0.0.2
|
||||
with:
|
||||
version: "v0.4.0-pre.11"
|
||||
# - name: Reattach HEAD ## may be needed for accurate code coverage info
|
||||
# run: git checkout ${{ github.head_ref }}
|
||||
- name: Initialize workflow variables
|
||||
@@ -1194,7 +1265,6 @@ jobs:
|
||||
## rust toolchain ~ install
|
||||
rustup toolchain install ${{ steps.vars.outputs.TOOLCHAIN }} --no-self-update --profile minimal
|
||||
rustup default ${{ steps.vars.outputs.TOOLCHAIN }}
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Initialize toolchain-dependent workflow variables
|
||||
id: dep_vars
|
||||
shell: bash
|
||||
@@ -1260,3 +1330,4 @@ jobs:
|
||||
flags: ${{ steps.vars.outputs.CODECOV_FLAGS }}
|
||||
name: codecov-umbrella
|
||||
fail_ci_if_error: false
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ exacl
|
||||
filetime
|
||||
formatteriteminfo
|
||||
fsext
|
||||
fundu
|
||||
getopts
|
||||
getrandom
|
||||
globset
|
||||
|
||||
+259
-29
@@ -1,20 +1,12 @@
|
||||
<!-- spell-checker:ignore reimplementing toybox RUNTEST -->
|
||||
|
||||
# Contributing to coreutils
|
||||
|
||||
Contributions are very welcome, and should target Rust's main branch until the
|
||||
standard libraries are stabilized. You may *claim* an item on the to-do list by
|
||||
following these steps:
|
||||
|
||||
1. Open an issue named "Implement [the utility of your choice]", e.g. "Implement
|
||||
ls".
|
||||
1. State that you are working on this utility.
|
||||
1. Develop the utility.
|
||||
1. Add integration tests.
|
||||
1. Add the reference to your utility into Cargo.toml and Makefile.
|
||||
1. Remove utility from the to-do list in the README.
|
||||
1. Submit a pull request and close the issue.
|
||||
|
||||
The steps above imply that, before starting to work on a utility, you should
|
||||
search the issues to make sure no one else is working on it.
|
||||
Contributions are very welcome via Pull Requests. If you don't know where to
|
||||
start, take a look at the
|
||||
[`good-first-issues`](https://github.com/uutils/coreutils/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22).
|
||||
If you have any questions, feel free to ask them in the issues or on
|
||||
[Discord](https://discord.gg/wQVJbvJ).
|
||||
|
||||
## Best practices
|
||||
|
||||
@@ -38,36 +30,240 @@ search the issues to make sure no one else is working on it.
|
||||
|
||||
## Platforms
|
||||
|
||||
We take pride in supporting many operating systems and architectures.
|
||||
We take pride in supporting many operating systems and architectures. Any code
|
||||
you contribute must at least compile without warnings for all platforms in the
|
||||
CI. However, you can use `#[cfg(...)]` attributes to create platform dependent features.
|
||||
|
||||
**Tip:**
|
||||
For Windows, Microsoft provides some images (VMWare, Hyper-V, VirtualBox and Parallels)
|
||||
for development:
|
||||
**Tip:** For Windows, Microsoft provides some images (VMWare, Hyper-V,
|
||||
VirtualBox and Parallels) for development:
|
||||
<https://developer.microsoft.com/windows/downloads/virtual-machines/>
|
||||
|
||||
## Tools
|
||||
|
||||
We have an extensive CI that will check your code before it can be merged. This
|
||||
section explains how to run those checks locally to avoid waiting for the CI.
|
||||
|
||||
### pre-commit hooks
|
||||
|
||||
A configuration for `pre-commit` is provided in the repository. It allows
|
||||
automatically checking every git commit you make to ensure it compiles, and
|
||||
passes `clippy` and `rustfmt` without warnings.
|
||||
|
||||
To use the provided hook:
|
||||
|
||||
1. [Install `pre-commit`](https://pre-commit.com/#install)
|
||||
1. Run `pre-commit install` while in the repository directory
|
||||
|
||||
Your git commits will then automatically be checked. If a check fails, an error
|
||||
message will explain why, and your commit will be canceled. You can then make
|
||||
the suggested changes, and run `git commit ...` again.
|
||||
|
||||
### clippy
|
||||
|
||||
```shell
|
||||
cargo clippy --all-targets --all-features
|
||||
```
|
||||
|
||||
The `msrv` key in the clippy configuration file `clippy.toml` is used to disable
|
||||
lints pertaining to newer features by specifying the minimum supported Rust
|
||||
version (MSRV).
|
||||
|
||||
### rustfmt
|
||||
|
||||
```shell
|
||||
cargo fmt --all
|
||||
```
|
||||
|
||||
### cargo-deny
|
||||
|
||||
This project uses [cargo-deny](https://github.com/EmbarkStudios/cargo-deny/) to
|
||||
detect duplicate dependencies, checks licenses, etc. To run it locally, first
|
||||
install it and then run with:
|
||||
|
||||
```
|
||||
cargo deny --all-features check all
|
||||
```
|
||||
|
||||
### Markdown linter
|
||||
|
||||
We use [markdownlint](https://github.com/DavidAnson/markdownlint) to lint the
|
||||
Markdown files in the repository.
|
||||
|
||||
### Spell checker
|
||||
|
||||
We use `cspell` as spell checker for all files in the project. If you are using
|
||||
VS Code, you can install the
|
||||
[code spell checker](https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker)
|
||||
extension to enable spell checking within your editor. Otherwise, you can
|
||||
install [cspell](https://cspell.org/) separately.
|
||||
|
||||
If you want to make the spell checker ignore a word, you can add
|
||||
|
||||
```rust
|
||||
// spell-checker:ignore word_to_ignore
|
||||
```
|
||||
|
||||
at the top of the file.
|
||||
|
||||
## Testing
|
||||
|
||||
Testing can be done using either Cargo or `make`.
|
||||
|
||||
### Testing with Cargo
|
||||
|
||||
Just like with building, we follow the standard procedure for testing using
|
||||
Cargo:
|
||||
|
||||
```shell
|
||||
cargo test
|
||||
```
|
||||
|
||||
By default, `cargo test` only runs the common programs. To run also platform
|
||||
specific tests, run:
|
||||
|
||||
```shell
|
||||
cargo test --features unix
|
||||
```
|
||||
|
||||
If you would prefer to test a select few utilities:
|
||||
|
||||
```shell
|
||||
cargo test --features "chmod mv tail" --no-default-features
|
||||
```
|
||||
|
||||
If you also want to test the core utilities:
|
||||
|
||||
```shell
|
||||
cargo test -p uucore -p coreutils
|
||||
```
|
||||
|
||||
To debug:
|
||||
|
||||
```shell
|
||||
gdb --args target/debug/coreutils ls
|
||||
(gdb) b ls.rs:79
|
||||
(gdb) run
|
||||
```
|
||||
|
||||
### Testing with GNU Make
|
||||
|
||||
To simply test all available utilities:
|
||||
|
||||
```shell
|
||||
make test
|
||||
```
|
||||
|
||||
To test all but a few of the available utilities:
|
||||
|
||||
```shell
|
||||
make SKIP_UTILS='UTILITY_1 UTILITY_2' test
|
||||
```
|
||||
|
||||
To test only a few of the available utilities:
|
||||
|
||||
```shell
|
||||
make UTILS='UTILITY_1 UTILITY_2' test
|
||||
```
|
||||
|
||||
To include tests for unimplemented behavior:
|
||||
|
||||
```shell
|
||||
make UTILS='UTILITY_1 UTILITY_2' SPEC=y test
|
||||
```
|
||||
|
||||
### Run Busybox Tests
|
||||
|
||||
This testing functionality is only available on *nix operating systems and
|
||||
requires `make`.
|
||||
|
||||
To run busybox tests for all utilities for which busybox has tests
|
||||
|
||||
```shell
|
||||
make busytest
|
||||
```
|
||||
|
||||
To run busybox tests for a few of the available utilities
|
||||
|
||||
```shell
|
||||
make UTILS='UTILITY_1 UTILITY_2' busytest
|
||||
```
|
||||
|
||||
To pass an argument like "-v" to the busybox test runtime
|
||||
|
||||
```shell
|
||||
make UTILS='UTILITY_1 UTILITY_2' RUNTEST_ARGS='-v' busytest
|
||||
```
|
||||
|
||||
### Comparing with GNU
|
||||
|
||||
To run uutils against the GNU test suite locally, run the following commands:
|
||||
|
||||
```shell
|
||||
bash util/build-gnu.sh
|
||||
bash util/run-gnu-test.sh
|
||||
# To run a single test:
|
||||
bash util/run-gnu-test.sh tests/touch/not-owner.sh # for example
|
||||
# To run several tests:
|
||||
bash util/run-gnu-test.sh tests/touch/not-owner.sh tests/rm/no-give-up.sh # for example
|
||||
# If this is a perl (.pl) test, to run in debug:
|
||||
DEBUG=1 bash util/run-gnu-test.sh tests/misc/sm3sum.pl
|
||||
```
|
||||
|
||||
Note that it relies on individual utilities (not the multicall binary).
|
||||
|
||||
### Improving the GNU compatibility
|
||||
|
||||
The Python script `./util/remaining-gnu-error.py` shows the list of failing
|
||||
tests in the CI.
|
||||
|
||||
To improve the GNU compatibility, the following process is recommended:
|
||||
|
||||
1. Identify a test (the smaller, the better) on a program that you understand or
|
||||
is easy to understand. You can use the `./util/remaining-gnu-error.py` script
|
||||
to help with this decision.
|
||||
1. Build both the GNU and Rust coreutils using: `bash util/build-gnu.sh`
|
||||
1. Run the test with `bash util/run-gnu-test.sh <your test>`
|
||||
1. Start to modify `<your test>` to understand what is wrong. Examples:
|
||||
1. Add `set -v` to have the bash verbose mode
|
||||
1. Add `echo $?` where needed
|
||||
1. When the variable `fail` is used in the test, `echo $fail` to see when the
|
||||
test started to fail
|
||||
1. Bump the content of the output (ex: `cat err`)
|
||||
1. ...
|
||||
1. Or, if the test is simple, extract the relevant information to create a new
|
||||
test case running both GNU & Rust implementation
|
||||
1. Start to modify the Rust implementation to match the expected behavior
|
||||
1. Add a test to make sure that we don't regress (our test suite is super quick)
|
||||
|
||||
## Commit messages
|
||||
|
||||
To help the project maintainers review pull requests from contributors across
|
||||
numerous utilities, the team has settled on conventions for commit messages.
|
||||
|
||||
From <http://git-scm.com/book/ch5-2.html>:
|
||||
From <https://git-scm.com/book/ch5-2.html>:
|
||||
|
||||
```
|
||||
Short (50 chars or less) summary of changes
|
||||
Capitalized, short (50 chars or less) summary
|
||||
|
||||
More detailed explanatory text, if necessary. Wrap it to about 72
|
||||
characters or so. In some contexts, the first line is treated as the
|
||||
subject of an email and the rest of the text as the body. The blank
|
||||
line separating the summary from the body is critical (unless you omit
|
||||
the body entirely); tools like rebase can get confused if you run the
|
||||
the body entirely); tools like rebase will confuse you if you run the
|
||||
two together.
|
||||
|
||||
Write your commit message in the imperative: "Fix bug" and not "Fixed bug"
|
||||
or "Fixes bug." This convention matches up with commit messages generated
|
||||
by commands like git merge and git revert.
|
||||
|
||||
Further paragraphs come after blank lines.
|
||||
|
||||
- Bullet points are okay, too
|
||||
|
||||
- Typically a hyphen or asterisk is used for the bullet, preceded by a
|
||||
- Typically a hyphen or asterisk is used for the bullet, followed by a
|
||||
single space, with blank lines in between, but conventions vary here
|
||||
|
||||
- Use a hanging indent
|
||||
```
|
||||
|
||||
Furthermore, here are a few examples for a summary line:
|
||||
@@ -103,15 +299,49 @@ uutils: add new utility
|
||||
gitignore: add temporary files
|
||||
```
|
||||
|
||||
## cargo-deny
|
||||
## Code coverage
|
||||
|
||||
This project uses [cargo-deny](https://github.com/EmbarkStudios/cargo-deny/) to
|
||||
detect duplicate dependencies, checks licenses, etc. To run it locally, first
|
||||
install it and then run with:
|
||||
<!-- spell-checker:ignore (flags) Ccodegen Coverflow Cpanic Zinstrument Zpanic -->
|
||||
|
||||
Code coverage report can be generated using [grcov](https://github.com/mozilla/grcov).
|
||||
|
||||
### Using Nightly Rust
|
||||
|
||||
To generate [gcov-based](https://github.com/mozilla/grcov#example-how-to-generate-gcda-files-for-a-rust-project) coverage report
|
||||
|
||||
```shell
|
||||
export CARGO_INCREMENTAL=0
|
||||
export RUSTFLAGS="-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort"
|
||||
export RUSTDOCFLAGS="-Cpanic=abort"
|
||||
cargo build <options...> # e.g., --features feat_os_unix
|
||||
cargo test <options...> # e.g., --features feat_os_unix test_pathchk
|
||||
grcov . -s . --binary-path ./target/debug/ -t html --branch --ignore-not-existing --ignore build.rs --excl-br-line "^\s*((debug_)?assert(_eq|_ne)?\#\[derive\()" -o ./target/debug/coverage/
|
||||
# open target/debug/coverage/index.html in browser
|
||||
```
|
||||
cargo deny --all-features check all
|
||||
```
|
||||
|
||||
if changes are not reflected in the report then run `cargo clean` and run the above commands.
|
||||
|
||||
### Using Stable Rust
|
||||
|
||||
If you are using stable version of Rust that doesn't enable code coverage instrumentation by default
|
||||
then add `-Z-Zinstrument-coverage` flag to `RUSTFLAGS` env variable specified above.
|
||||
|
||||
|
||||
## Other implementations
|
||||
|
||||
The Coreutils have different implementations, with different levels of completions:
|
||||
|
||||
* [GNU's](https://git.savannah.gnu.org/gitweb/?p=coreutils.git)
|
||||
* [OpenBSD](https://github.com/openbsd/src/tree/master/bin)
|
||||
* [Busybox](https://github.com/mirror/busybox/tree/master/coreutils)
|
||||
* [Toybox (Android)](https://github.com/landley/toybox/tree/master/toys/posix)
|
||||
* [V lang](https://github.com/vlang/coreutils)
|
||||
* [SerenityOS](https://github.com/SerenityOS/serenity/tree/master/Userland/Utilities)
|
||||
* [Initial Unix](https://github.com/dspinellis/unix-history-repo)
|
||||
|
||||
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).
|
||||
|
||||
|
||||
## Licensing
|
||||
|
||||
|
||||
Generated
+21
-46
@@ -89,9 +89,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bindgen"
|
||||
version = "0.62.0"
|
||||
version = "0.63.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c6720a8b7b2d39dd533285ed438d458f65b31b5c257e6ac7bb3d7e82844dd722"
|
||||
checksum = "36d860121800b2a9a94f9b5604b332d5cffb234ce17609ea479d723dbc9d3885"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cexpr",
|
||||
@@ -870,9 +870,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "fts-sys"
|
||||
version = "0.2.3"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32bd98333d10742c0b048272ebf4cb05336d415423b853961c92ccb398966a03"
|
||||
checksum = "9a66c0a21e344f20c87b4ca12643cf4f40a7018f132c98d344e989b959f49dd1"
|
||||
dependencies = [
|
||||
"bindgen",
|
||||
"libc",
|
||||
@@ -880,9 +880,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "fundu"
|
||||
version = "0.3.0"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "925250bc259498d4008ee072bf16586083ab2c491aa4b06b3c4d0a6556cebd74"
|
||||
checksum = "da58c38fe7b706cead98429d8a8535261addbe55fd531c7d7c7d770346464010"
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
@@ -1139,12 +1139,6 @@ dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "io-lifetimes"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59ce5ef949d49ee85593fc4d3f3f95ad61657076395cbbce23e2121fc5542074"
|
||||
|
||||
[[package]]
|
||||
name = "io-lifetimes"
|
||||
version = "1.0.5"
|
||||
@@ -1162,8 +1156,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22e18b0a45d56fe973d6db23972bf5bc46f988a4a2385deac9cc29572f09daef"
|
||||
dependencies = [
|
||||
"hermit-abi 0.3.1",
|
||||
"io-lifetimes 1.0.5",
|
||||
"rustix 0.36.8",
|
||||
"io-lifetimes",
|
||||
"rustix",
|
||||
"windows-sys 0.45.0",
|
||||
]
|
||||
|
||||
@@ -1257,12 +1251,6 @@ dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.0.46"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4d2456c373231a208ad294c33dc5bff30051eafd954cd4caae83a712b12854d"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.1.4"
|
||||
@@ -1719,15 +1707,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "procfs"
|
||||
version = "0.14.1"
|
||||
version = "0.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dfb6451c91904606a1abe93e83a8ec851f45827fa84273f256ade45dc095818"
|
||||
checksum = "943ca7f9f29bab5844ecd8fdb3992c5969b6622bb9609b9502fef9b4310e3f1f"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"byteorder",
|
||||
"hex",
|
||||
"lazy_static",
|
||||
"rustix 0.35.13",
|
||||
"rustix",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1921,20 +1909,6 @@ dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.35.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "727a1a6d65f786ec22df8a81ca3121107f235970dc1705ed681d3e6e8b9cd5f9"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"io-lifetimes 0.7.5",
|
||||
"libc",
|
||||
"linux-raw-sys 0.0.46",
|
||||
"windows-sys 0.42.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.36.8"
|
||||
@@ -1943,9 +1917,9 @@ checksum = "f43abb88211988493c1abb44a70efa56ff0ce98f233b7b276146f1f3f7ba9644"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"io-lifetimes 1.0.5",
|
||||
"io-lifetimes",
|
||||
"libc",
|
||||
"linux-raw-sys 0.1.4",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.45.0",
|
||||
]
|
||||
|
||||
@@ -1992,9 +1966,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "selinux-sys"
|
||||
version = "0.6.1"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e4c02c5c6e2db8a78b3ffffc666f75fcda5bbd7068ba3c0f560e5504f4d88443"
|
||||
checksum = "806d381649bb85347189d2350728817418138d11d738e2482cb644ec7f3c755d"
|
||||
dependencies = [
|
||||
"bindgen",
|
||||
"cc",
|
||||
@@ -2185,7 +2159,7 @@ dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand",
|
||||
"redox_syscall",
|
||||
"rustix 0.36.8",
|
||||
"rustix",
|
||||
"windows-sys 0.42.0",
|
||||
]
|
||||
|
||||
@@ -2209,12 +2183,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "terminal_size"
|
||||
version = "0.2.2"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40ca90c434fd12083d1a6bdcbe9f92a14f96c8a1ba600ba451734ac334521f7a"
|
||||
checksum = "4c9afddd2cec1c0909f06b00ef33f94ab2cc0578c4a610aa208ddfec8aa2b43a"
|
||||
dependencies = [
|
||||
"rustix 0.35.13",
|
||||
"windows-sys 0.42.0",
|
||||
"rustix",
|
||||
"windows-sys 0.45.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3041,6 +3015,7 @@ name = "uu_sleep"
|
||||
version = "0.0.17"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"fundu",
|
||||
"uucore",
|
||||
]
|
||||
|
||||
|
||||
+3
-3
@@ -282,7 +282,7 @@ filetime = "0.2"
|
||||
fnv = "1.0.7"
|
||||
fs_extra = "1.1.0"
|
||||
fts-sys = "0.2"
|
||||
fundu = "0.3.0"
|
||||
fundu = "0.4.2"
|
||||
gcd = "2.2"
|
||||
glob = "0.3.0"
|
||||
half = "2.1"
|
||||
@@ -320,7 +320,7 @@ strum = "0.24.1"
|
||||
strum_macros = "0.24.2"
|
||||
tempfile = "3.4.0"
|
||||
term_grid = "0.1.5"
|
||||
terminal_size = "0.2.2"
|
||||
terminal_size = "0.2.5"
|
||||
textwrap = { version="0.16.0", features=["terminal_size"] }
|
||||
thiserror = "1.0"
|
||||
time = { version="0.3" }
|
||||
@@ -493,7 +493,7 @@ hex-literal = "0.3.1"
|
||||
rstest = "0.16.0"
|
||||
|
||||
[target.'cfg(any(target_os = "linux", target_os = "android"))'.dev-dependencies]
|
||||
procfs = { version = "0.14", default-features = false }
|
||||
procfs = { version = "0.15", default-features = false }
|
||||
rlimit = "0.9.1"
|
||||
|
||||
[target.'cfg(unix)'.dev-dependencies]
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
# Documentation
|
||||
|
||||
The source of the documentation is available on:
|
||||
|
||||
<https://uutils.github.io/dev/coreutils/>
|
||||
|
||||
The documentation is updated everyday on this repository:
|
||||
|
||||
<https://github.com/uutils/uutils.github.io/>
|
||||
|
||||
## Running GNU tests
|
||||
|
||||
<!-- spell-checker:ignore gnulib -->
|
||||
|
||||
- Check out <https://github.com/coreutils/coreutils> next to your fork as gnu
|
||||
- Check out <https://github.com/coreutils/gnulib> next to your fork as gnulib
|
||||
- Rename the checkout of your fork to uutils
|
||||
|
||||
At the end you should have uutils, gnu and gnulib checked out next to each other.
|
||||
|
||||
- Run `cd uutils && ./util/build-gnu.sh && cd ..` to get everything ready (this may take a while)
|
||||
- Finally, you can run tests with `bash uutils/util/run-gnu-test.sh <tests>`. Instead of `<tests>` insert the tests you want to run, e.g. `tests/misc/wc-proc.sh`.
|
||||
|
||||
## Code Coverage Report Generation
|
||||
|
||||
<!-- spell-checker:ignore (flags) Ccodegen Coverflow Cpanic Zinstrument Zpanic -->
|
||||
|
||||
Code coverage report can be generated using [grcov](https://github.com/mozilla/grcov).
|
||||
|
||||
### Using Nightly Rust
|
||||
|
||||
To generate [gcov-based](https://github.com/mozilla/grcov#example-how-to-generate-gcda-files-for-a-rust-project) coverage report
|
||||
|
||||
```shell
|
||||
export CARGO_INCREMENTAL=0
|
||||
export RUSTFLAGS="-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort"
|
||||
export RUSTDOCFLAGS="-Cpanic=abort"
|
||||
cargo build <options...> # e.g., --features feat_os_unix
|
||||
cargo test <options...> # e.g., --features feat_os_unix test_pathchk
|
||||
grcov . -s . --binary-path ./target/debug/ -t html --branch --ignore-not-existing --ignore build.rs --excl-br-line "^\s*((debug_)?assert(_eq|_ne)?\#\[derive\()" -o ./target/debug/coverage/
|
||||
# open target/debug/coverage/index.html in browser
|
||||
```
|
||||
|
||||
if changes are not reflected in the report then run `cargo clean` and run the above commands.
|
||||
|
||||
### Using Stable Rust
|
||||
|
||||
If you are using stable version of Rust that doesn't enable code coverage instrumentation by default
|
||||
then add `-Z-Zinstrument-coverage` flag to `RUSTFLAGS` env variable specified above.
|
||||
|
||||
## pre-commit hooks
|
||||
|
||||
A configuration for `pre-commit` is provided in the repository. It allows automatically checking every git commit you make to ensure it compiles, and passes `clippy` and `rustfmt` without warnings.
|
||||
|
||||
To use the provided hook:
|
||||
|
||||
1. [Install `pre-commit`](https://pre-commit.com/#install)
|
||||
1. Run `pre-commit install` while in the repository directory
|
||||
|
||||
Your git commits will then automatically be checked. If a check fails, an error message will explain why, and your commit will be canceled. You can then make the suggested changes, and run `git commit ...` again.
|
||||
|
||||
## Using Clippy
|
||||
|
||||
The `msrv` key in the clippy configuration file `clippy.toml` is used to disable lints pertaining to newer features by specifying the minimum supported Rust version (MSRV). However, this key is only supported on `nightly`. To invoke clippy without errors, use `cargo +nightly clippy`. In order to also check tests and non-default crate features, use `cargo +nightly clippy --all-targets --all-features`.
|
||||
|
||||
## Markdown linter
|
||||
|
||||
We use <https://github.com/DavidAnson/markdownlint> to lint the Markdown files.
|
||||
@@ -1,3 +1,10 @@
|
||||
<!-- markdownlint-disable MD033 MD041 MD002 -->
|
||||
<!-- markdownlint-disable commands-show-output no-duplicate-heading -->
|
||||
<!-- spell-checker:ignore markdownlint ; (options) DESTDIR UTILNAME manpages reimplementation -->
|
||||
<div align="center">
|
||||
|
||||

|
||||
|
||||
# uutils coreutils
|
||||
|
||||
[](https://crates.io/crates/coreutils)
|
||||
@@ -9,15 +16,14 @@
|
||||
[](https://codecov.io/gh/uutils/coreutils)
|
||||

|
||||
|
||||
-----------------------------------------------
|
||||
</div>
|
||||
|
||||
<!-- markdownlint-disable commands-show-output no-duplicate-heading -->
|
||||
<!-- spell-checker:ignore markdownlint ; (options) DESTDIR RUNTEST UTILNAME manpages -->
|
||||
---
|
||||
|
||||
uutils is an attempt at writing universal (as in cross-platform) CLI
|
||||
utilities in [Rust](http://www.rust-lang.org).
|
||||
While all programs have been implemented, some options might be missing
|
||||
or different behavior might be experienced.
|
||||
|
||||
uutils coreutils is a cross-platform reimplementation of the GNU coreutils in
|
||||
[Rust](http://www.rust-lang.org). While all programs have been implemented, some
|
||||
options might be missing or different behavior might be experienced.
|
||||
|
||||
To install it:
|
||||
|
||||
@@ -27,13 +33,15 @@ cargo install coreutils
|
||||
```
|
||||
|
||||
<!-- markdownlint-disable-next-line MD026 -->
|
||||
## Why?
|
||||
|
||||
uutils aims to work on as many platforms as possible, to be able to use the
|
||||
same utils on Linux, Mac, Windows and other platforms. This ensures, for
|
||||
example, that scripts can be easily transferred between platforms. Rust was
|
||||
chosen not only because it is fast and safe, but is also excellent for
|
||||
writing cross-platform code.
|
||||
## Goals
|
||||
|
||||
uutils aims to be a drop-in replacement for the GNU utils. Differences with GNU
|
||||
are treated as bugs.
|
||||
|
||||
uutils aims to work on as many platforms as possible, to be able to use the same
|
||||
utils on Linux, Mac, Windows and other platforms. This ensures, for example,
|
||||
that scripts can be easily transferred between platforms.
|
||||
|
||||
## Documentation
|
||||
|
||||
@@ -42,10 +50,11 @@ uutils has both user and developer documentation available:
|
||||
- [User Manual](https://uutils.github.io/user/)
|
||||
- [Developer Documentation](https://uutils.github.io/dev/coreutils/)
|
||||
|
||||
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.
|
||||
Both can also be generated locally, the instructions for that can be found in
|
||||
the [coreutils docs](https://github.com/uutils/uutils.github.io) repository.
|
||||
|
||||
<!-- ANCHOR: build (this mark is needed for mdbook) -->
|
||||
|
||||
## Requirements
|
||||
|
||||
- Rust (`cargo`, `rustc`)
|
||||
@@ -53,13 +62,13 @@ Both can also be generated locally, the instructions for that can be found in th
|
||||
|
||||
### Rust Version
|
||||
|
||||
uutils follows Rust's release channels and is tested against stable, beta and nightly.
|
||||
The current Minimum Supported Rust Version (MSRV) is `1.64.0`.
|
||||
uutils follows Rust's release channels and is tested against stable, beta and
|
||||
nightly. The current Minimum Supported Rust Version (MSRV) is `1.64.0`.
|
||||
|
||||
## Building
|
||||
|
||||
There are currently two methods to build the uutils binaries: either Cargo
|
||||
or GNU Make.
|
||||
There are currently two methods to build the uutils binaries: either Cargo or
|
||||
GNU Make.
|
||||
|
||||
> Building the full package, including all documentation, requires both Cargo
|
||||
> and Gnu Make on a Unix platform.
|
||||
@@ -73,8 +82,8 @@ cd coreutils
|
||||
|
||||
### Cargo
|
||||
|
||||
Building uutils using Cargo is easy because the process is the same as for
|
||||
every other Rust program:
|
||||
Building uutils using Cargo is easy because the process is the same as for every
|
||||
other Rust program:
|
||||
|
||||
```shell
|
||||
cargo build --release
|
||||
@@ -83,9 +92,9 @@ cargo build --release
|
||||
This command builds the most portable common core set of uutils into a multicall
|
||||
(BusyBox-type) binary, named 'coreutils', on most Rust-supported platforms.
|
||||
|
||||
Additional platform-specific uutils are often available. Building these
|
||||
expanded sets of uutils for a platform (on that platform) is as simple as
|
||||
specifying it as a feature:
|
||||
Additional platform-specific uutils are often available. Building these expanded
|
||||
sets of uutils for a platform (on that platform) is as simple as specifying it
|
||||
as a feature:
|
||||
|
||||
```shell
|
||||
cargo build --release --features macos
|
||||
@@ -96,18 +105,18 @@ cargo build --release --features unix
|
||||
```
|
||||
|
||||
If you don't want to build every utility available on your platform into the
|
||||
final binary, you can also specify which ones you want to build manually.
|
||||
For example:
|
||||
final binary, you can also specify which ones you want to build manually. For
|
||||
example:
|
||||
|
||||
```shell
|
||||
cargo build --features "base32 cat echo rm" --no-default-features
|
||||
```
|
||||
|
||||
If you don't want to build the multicall binary and would prefer to build
|
||||
the utilities as individual binaries, that is also possible. Each utility
|
||||
is contained in its own package within the main repository, named
|
||||
"uu_UTILNAME". To build individual utilities, use cargo to build just the
|
||||
specific packages (using the `--package` [aka `-p`] option). For example:
|
||||
If you don't want to build the multicall binary and would prefer to build the
|
||||
utilities as individual binaries, that is also possible. Each utility is
|
||||
contained in its own package within the main repository, named "uu_UTILNAME". To
|
||||
build individual utilities, use cargo to build just the specific packages (using
|
||||
the `--package` [aka `-p`] option). For example:
|
||||
|
||||
```shell
|
||||
cargo build -p uu_base32 -p uu_cat -p uu_echo -p uu_rm
|
||||
@@ -148,13 +157,15 @@ make UTILS='UTILITY_1 UTILITY_2'
|
||||
Likewise, installing can simply be done using:
|
||||
|
||||
```shell
|
||||
cargo install --path .
|
||||
cargo install --path . --locked
|
||||
```
|
||||
|
||||
This command will install uutils into Cargo's *bin* folder (*e.g.* `$HOME/.cargo/bin`).
|
||||
This command will install uutils into Cargo's _bin_ folder (_e.g._
|
||||
`$HOME/.cargo/bin`).
|
||||
|
||||
This does not install files necessary for shell completion or manpages.
|
||||
For manpages or shell completion to work, use `GNU Make` or see `Manually install shell completions`/`Manually install manpages`.
|
||||
This does not install files necessary for shell completion or manpages. For
|
||||
manpages or shell completion to work, use `GNU Make` or see
|
||||
`Manually install shell completions`/`Manually install manpages`.
|
||||
|
||||
### Install with GNU Make
|
||||
|
||||
@@ -207,8 +218,8 @@ be generated; See `Manually install shell completions`.
|
||||
|
||||
### Manually install shell completions
|
||||
|
||||
The `coreutils` binary can generate completions for the `bash`, `elvish`, `fish`, `powershell`
|
||||
and `zsh` shells. It prints the result to stdout.
|
||||
The `coreutils` binary can generate completions for the `bash`, `elvish`,
|
||||
`fish`, `powershell` and `zsh` shells. It prints the result to stdout.
|
||||
|
||||
The syntax is:
|
||||
|
||||
@@ -216,8 +227,8 @@ The syntax is:
|
||||
cargo run completion <utility> <shell>
|
||||
```
|
||||
|
||||
So, to install completions for `ls` on `bash` to `/usr/local/share/bash-completion/completions/ls`,
|
||||
run:
|
||||
So, to install completions for `ls` on `bash` to
|
||||
`/usr/local/share/bash-completion/completions/ls`, run:
|
||||
|
||||
```shell
|
||||
cargo run completion ls bash > /usr/local/share/bash-completion/completions/ls
|
||||
@@ -226,12 +237,12 @@ cargo run completion ls bash > /usr/local/share/bash-completion/completions/ls
|
||||
### Manually install manpages
|
||||
|
||||
To generate manpages, the syntax is:
|
||||
|
||||
```bash
|
||||
cargo run manpage <utility>
|
||||
```
|
||||
|
||||
So, to install the manpage for `ls` to `/usr/local/share/man/man1/ls.1`
|
||||
run:
|
||||
So, to install the manpage for `ls` to `/usr/local/share/man/man1/ls.1` run:
|
||||
|
||||
```bash
|
||||
cargo run manpage ls > /usr/local/share/man/man1/ls.1
|
||||
@@ -239,8 +250,8 @@ cargo run manpage ls > /usr/local/share/man/man1/ls.1
|
||||
|
||||
## Un-installation
|
||||
|
||||
Un-installation differs depending on how you have installed uutils. If you used
|
||||
Cargo to install, use Cargo to uninstall. If you used GNU Make to install, use
|
||||
Un-installation differs depending on how you have installed uutils. If you used
|
||||
Cargo to install, use Cargo to uninstall. If you used GNU Make to install, use
|
||||
Make to uninstall.
|
||||
|
||||
### Uninstall with Cargo
|
||||
@@ -280,245 +291,21 @@ make PREFIX=/my/path uninstall
|
||||
|
||||
<!-- ANCHOR_END: build (this mark is needed for mdbook) -->
|
||||
|
||||
## Testing
|
||||
|
||||
Testing can be done using either Cargo or `make`.
|
||||
|
||||
### Testing with Cargo
|
||||
|
||||
Just like with building, we follow the standard procedure for testing using
|
||||
Cargo:
|
||||
|
||||
```shell
|
||||
cargo test
|
||||
```
|
||||
|
||||
By default, `cargo test` only runs the common programs. To run also platform
|
||||
specific tests, run:
|
||||
|
||||
```shell
|
||||
cargo test --features unix
|
||||
```
|
||||
|
||||
If you would prefer to test a select few utilities:
|
||||
|
||||
```shell
|
||||
cargo test --features "chmod mv tail" --no-default-features
|
||||
```
|
||||
|
||||
If you also want to test the core utilities:
|
||||
|
||||
```shell
|
||||
cargo test -p uucore -p coreutils
|
||||
```
|
||||
|
||||
To debug:
|
||||
|
||||
```shell
|
||||
gdb --args target/debug/coreutils ls
|
||||
(gdb) b ls.rs:79
|
||||
(gdb) run
|
||||
```
|
||||
|
||||
### Testing with GNU Make
|
||||
|
||||
To simply test all available utilities:
|
||||
|
||||
```shell
|
||||
make test
|
||||
```
|
||||
|
||||
To test all but a few of the available utilities:
|
||||
|
||||
```shell
|
||||
make SKIP_UTILS='UTILITY_1 UTILITY_2' test
|
||||
```
|
||||
|
||||
To test only a few of the available utilities:
|
||||
|
||||
```shell
|
||||
make UTILS='UTILITY_1 UTILITY_2' test
|
||||
```
|
||||
|
||||
To include tests for unimplemented behavior:
|
||||
|
||||
```shell
|
||||
make UTILS='UTILITY_1 UTILITY_2' SPEC=y test
|
||||
```
|
||||
|
||||
### Run Busybox Tests
|
||||
|
||||
This testing functionality is only available on *nix operating systems and
|
||||
requires `make`.
|
||||
|
||||
To run busybox tests for all utilities for which busybox has tests
|
||||
|
||||
```shell
|
||||
make busytest
|
||||
```
|
||||
|
||||
To run busybox tests for a few of the available utilities
|
||||
|
||||
```shell
|
||||
make UTILS='UTILITY_1 UTILITY_2' busytest
|
||||
```
|
||||
|
||||
To pass an argument like "-v" to the busybox test runtime
|
||||
|
||||
```shell
|
||||
make UTILS='UTILITY_1 UTILITY_2' RUNTEST_ARGS='-v' busytest
|
||||
```
|
||||
|
||||
### Comparing with GNU
|
||||
## GNU test suite compatibility
|
||||
|
||||
Below is the evolution of how many GNU tests uutils passes. A more detailed
|
||||
breakdown of the GNU test results of the main branch can be found
|
||||
[in the user manual](https://uutils.github.io/user/test_coverage.html).
|
||||
|
||||
See <https://github.com/uutils/coreutils/issues/3336> for the main meta bugs
|
||||
(many are missing).
|
||||
|
||||

|
||||
|
||||
To run locally:
|
||||
|
||||
```shell
|
||||
bash util/build-gnu.sh
|
||||
bash util/run-gnu-test.sh
|
||||
# To run a single test:
|
||||
bash util/run-gnu-test.sh tests/touch/not-owner.sh # for example
|
||||
# To run several tests:
|
||||
bash util/run-gnu-test.sh tests/touch/not-owner.sh tests/rm/no-give-up.sh # for example
|
||||
# If this is a perl (.pl) test, to run in debug:
|
||||
DEBUG=1 bash util/run-gnu-test.sh tests/misc/sm3sum.pl
|
||||
```
|
||||
|
||||
Note that it relies on individual utilities (not the multicall binary).
|
||||
|
||||
### Improving the GNU compatibility
|
||||
|
||||
The Python script `./util/remaining-gnu-error.py` shows the list of failing tests in the CI.
|
||||
|
||||
To improve the GNU compatibility, the following process is recommended:
|
||||
|
||||
1. Identify a test (the smaller, the better) on a program that you understand or is easy to understand. You can use the `./util/remaining-gnu-error.py` script to help with this decision.
|
||||
1. Build both the GNU and Rust coreutils using: `bash util/build-gnu.sh`
|
||||
1. Run the test with `bash util/run-gnu-test.sh <your test>`
|
||||
1. Start to modify `<your test>` to understand what is wrong. Examples:
|
||||
1. Add `set -v` to have the bash verbose mode
|
||||
1. Add `echo $?` where needed
|
||||
1. When the variable `fail` is used in the test, `echo $fail` to see when the test started to fail
|
||||
1. Bump the content of the output (ex: `cat err`)
|
||||
1. ...
|
||||
1. Or, if the test is simple, extract the relevant information to create a new test case running both GNU & Rust implementation
|
||||
1. Start to modify the Rust implementation to match the expected behavior
|
||||
1. Add a test to make sure that we don't regress (our test suite is super quick)
|
||||
|
||||
## Contributing
|
||||
|
||||
To contribute to uutils, please see [CONTRIBUTING](CONTRIBUTING.md).
|
||||
|
||||
## Utilities
|
||||
|
||||
Please note that this is not fully accurate:
|
||||
|
||||
- Some new options can be added / removed in the GNU implementation;
|
||||
- Some error management might be missing;
|
||||
- Some behaviors might be different.
|
||||
|
||||
See <https://github.com/uutils/coreutils/issues/3336> for the main meta bugs
|
||||
(many are missing).
|
||||
|
||||
| Done | WIP |
|
||||
|-----------|-----------|
|
||||
| arch | cp |
|
||||
| base32 | date |
|
||||
| base64 | dd |
|
||||
| basename | df |
|
||||
| basenc | expr |
|
||||
| cat | install |
|
||||
| chcon | ls |
|
||||
| chgrp | more |
|
||||
| chmod | numfmt |
|
||||
| chown | od (`--strings` and 128-bit data types missing) |
|
||||
| chroot | pr |
|
||||
| cksum | printf |
|
||||
| comm | sort |
|
||||
| csplit | split |
|
||||
| cut | tac |
|
||||
| dircolors | test |
|
||||
| dirname | dir |
|
||||
| du | vdir |
|
||||
| echo | stty |
|
||||
| env | |
|
||||
| expand | |
|
||||
| factor | |
|
||||
| false | |
|
||||
| fmt | |
|
||||
| fold | |
|
||||
| groups | |
|
||||
| hashsum | |
|
||||
| head | |
|
||||
| hostid | |
|
||||
| hostname | |
|
||||
| id | |
|
||||
| join | |
|
||||
| kill | |
|
||||
| link | |
|
||||
| ln | |
|
||||
| logname | |
|
||||
| ~~md5sum~~ (replaced by [hashsum](https://github.com/uutils/coreutils/blob/main/src/uu/hashsum/src/hashsum.rs)) | |
|
||||
| ~~sha1sum~~ (replaced by [hashsum](https://github.com/uutils/coreutils/blob/main/src/uu/hashsum/src/hashsum.rs)) | |
|
||||
| ~~sha224sum~~ (replaced by [hashsum](https://github.com/uutils/coreutils/blob/main/src/uu/hashsum/src/hashsum.rs)) | |
|
||||
| ~~sha256sum~~ (replaced by [hashsum](https://github.com/uutils/coreutils/blob/main/src/uu/hashsum/src/hashsum.rs)) | |
|
||||
| ~~sha384sum~~ (replaced by [hashsum](https://github.com/uutils/coreutils/blob/main/src/uu/hashsum/src/hashsum.rs)) | |
|
||||
| ~~sha512sum~~ (replaced by [hashsum](https://github.com/uutils/coreutils/blob/main/src/uu/hashsum/src/hashsum.rs)) | |
|
||||
| mkdir | |
|
||||
| mkfifo | |
|
||||
| mknod | |
|
||||
| mktemp | |
|
||||
| mv | |
|
||||
| nice | |
|
||||
| nl | |
|
||||
| nohup | |
|
||||
| nproc | |
|
||||
| paste | |
|
||||
| pathchk | |
|
||||
| pinky | |
|
||||
| printenv | |
|
||||
| ptx | |
|
||||
| pwd | |
|
||||
| readlink | |
|
||||
| realpath | |
|
||||
| relpath | |
|
||||
| rm | |
|
||||
| rmdir | |
|
||||
| runcon | |
|
||||
| seq | |
|
||||
| shred | |
|
||||
| shuf | |
|
||||
| sleep | |
|
||||
| stat | |
|
||||
| stdbuf | |
|
||||
| sum | |
|
||||
| sync | |
|
||||
| tail | |
|
||||
| tee | |
|
||||
| timeout | |
|
||||
| touch | |
|
||||
| tr | |
|
||||
| true | |
|
||||
| truncate | |
|
||||
| tsort | |
|
||||
| tty | |
|
||||
| uname | |
|
||||
| unexpand | |
|
||||
| uniq | |
|
||||
| unlink | |
|
||||
| uptime | |
|
||||
| users | |
|
||||
| wc | |
|
||||
| who | |
|
||||
| whoami | |
|
||||
| yes | |
|
||||
|
||||
## License
|
||||
|
||||
uutils is licensed under the MIT License - see the `LICENSE` file for details
|
||||
|
||||
@@ -20,6 +20,8 @@ pub fn main() {
|
||||
for (key, val) in env::vars() {
|
||||
if val == "1" && key.starts_with(ENV_FEATURE_PREFIX) {
|
||||
let krate = key[ENV_FEATURE_PREFIX.len()..].to_lowercase();
|
||||
// Allow this as we have a bunch of info in the comments
|
||||
#[allow(clippy::match_same_arms)]
|
||||
match krate.as_ref() {
|
||||
"default" | "macos" | "unix" | "windows" | "selinux" | "zip" => continue, // common/standard feature names
|
||||
"nightly" | "test_unimplemented" => continue, // crate-local custom features
|
||||
|
||||
+95
-11
@@ -8,6 +8,7 @@
|
||||
// spell-checker:ignore (ToDO) Chmoder cmode fmode fperm fref ugoa RFILE RFILE's
|
||||
|
||||
use clap::{crate_version, Arg, ArgAction, Command};
|
||||
use std::ffi::OsString;
|
||||
use std::fs;
|
||||
use std::os::unix::fs::{MetadataExt, PermissionsExt};
|
||||
use std::path::Path;
|
||||
@@ -35,14 +36,64 @@ mod options {
|
||||
pub const FILE: &str = "FILE";
|
||||
}
|
||||
|
||||
/// Extract negative modes (starting with '-') from the rest of the arguments.
|
||||
///
|
||||
/// This is mainly required for GNU compatibility, where "non-positional negative" modes are used
|
||||
/// as the actual positional MODE. Some examples of these cases are:
|
||||
/// * "chmod -w -r file", which is the same as "chmod -w,-r file"
|
||||
/// * "chmod -w file -r", which is the same as "chmod -w,-r file"
|
||||
///
|
||||
/// These can currently not be handled by clap.
|
||||
/// Therefore it might be possible that a pseudo MODE is inserted to pass clap parsing.
|
||||
/// The pseudo MODE is later replaced by the extracted (and joined) negative modes.
|
||||
fn extract_negative_modes(mut args: impl uucore::Args) -> (Option<String>, Vec<OsString>) {
|
||||
// we look up the args until "--" is found
|
||||
// "-mode" will be extracted into parsed_cmode_vec
|
||||
let (parsed_cmode_vec, pre_double_hyphen_args): (Vec<OsString>, Vec<OsString>) =
|
||||
args.by_ref().take_while(|a| a != "--").partition(|arg| {
|
||||
let arg = if let Some(arg) = arg.to_str() {
|
||||
arg.to_string()
|
||||
} else {
|
||||
return false;
|
||||
};
|
||||
arg.len() >= 2
|
||||
&& arg.starts_with('-')
|
||||
&& matches!(
|
||||
arg.chars().nth(1).unwrap(),
|
||||
'r' | 'w' | 'x' | 'X' | 's' | 't' | 'u' | 'g' | 'o' | '0'..='7'
|
||||
)
|
||||
});
|
||||
|
||||
let mut clean_args = Vec::new();
|
||||
if !parsed_cmode_vec.is_empty() {
|
||||
// we need a pseudo cmode for clap, which won't be used later.
|
||||
// this is required because clap needs the default "chmod MODE FILE" scheme.
|
||||
clean_args.push("w".into());
|
||||
}
|
||||
clean_args.extend(pre_double_hyphen_args);
|
||||
|
||||
if let Some(arg) = args.next() {
|
||||
// as there is still something left in the iterator, we previously consumed the "--"
|
||||
// -> add it to the args again
|
||||
clean_args.push("--".into());
|
||||
clean_args.push(arg);
|
||||
}
|
||||
clean_args.extend(args);
|
||||
|
||||
let parsed_cmode = Some(
|
||||
parsed_cmode_vec
|
||||
.iter()
|
||||
.map(|s| s.to_str().unwrap())
|
||||
.collect::<Vec<&str>>()
|
||||
.join(","),
|
||||
)
|
||||
.filter(|s| !s.is_empty());
|
||||
(parsed_cmode, clean_args)
|
||||
}
|
||||
|
||||
#[uucore::main]
|
||||
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
let mut args = args.collect_lossy();
|
||||
|
||||
// Before we can parse 'args' with clap (and previously getopts),
|
||||
// a possible MODE prefix '-' needs to be removed (e.g. "chmod -x FILE").
|
||||
let mode_had_minus_prefix = mode::strip_minus_from_mode(&mut args);
|
||||
|
||||
let (parsed_cmode, args) = extract_negative_modes(args.skip(1)); // skip binary name
|
||||
let matches = uu_app().after_help(LONG_USAGE).try_get_matches_from(args)?;
|
||||
|
||||
let changes = matches.get_flag(options::CHANGES);
|
||||
@@ -62,13 +113,14 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
let modes = matches.get_one::<String>(options::MODE).unwrap(); // should always be Some because required
|
||||
let cmode = if mode_had_minus_prefix {
|
||||
// clap parsing is finished, now put prefix back
|
||||
format!("-{modes}")
|
||||
|
||||
let modes = matches.get_one::<String>(options::MODE);
|
||||
let cmode = if let Some(parsed_cmode) = parsed_cmode {
|
||||
parsed_cmode
|
||||
} else {
|
||||
modes.to_string()
|
||||
modes.unwrap().to_string() // modes is required
|
||||
};
|
||||
// FIXME: enable non-utf8 paths
|
||||
let mut files: Vec<String> = matches
|
||||
.get_many::<String>(options::FILE)
|
||||
.map(|v| v.map(ToString::to_string).collect())
|
||||
@@ -107,6 +159,7 @@ pub fn uu_app() -> Command {
|
||||
.override_usage(format_usage(USAGE))
|
||||
.args_override_self(true)
|
||||
.infer_long_args(true)
|
||||
.no_binary_name(true)
|
||||
.arg(
|
||||
Arg::new(options::CHANGES)
|
||||
.long(options::CHANGES)
|
||||
@@ -376,3 +429,34 @@ impl Chmoder {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_extract_negative_modes() {
|
||||
// "chmod -w -r file" becomes "chmod -w,-r file". clap does not accept "-w,-r" as MODE.
|
||||
// Therefore, "w" is added as pseudo mode to pass clap.
|
||||
let (c, a) = extract_negative_modes(vec!["-w", "-r", "file"].iter().map(OsString::from));
|
||||
assert_eq!(c, Some("-w,-r".to_string()));
|
||||
assert_eq!(a, vec!["w", "file"]);
|
||||
|
||||
// "chmod -w file -r" becomes "chmod -w,-r file". clap does not accept "-w,-r" as MODE.
|
||||
// Therefore, "w" is added as pseudo mode to pass clap.
|
||||
let (c, a) = extract_negative_modes(vec!["-w", "file", "-r"].iter().map(OsString::from));
|
||||
assert_eq!(c, Some("-w,-r".to_string()));
|
||||
assert_eq!(a, vec!["w", "file"]);
|
||||
|
||||
// "chmod -w -- -r file" becomes "chmod -w -r file", where "-r" is interpreted as file.
|
||||
// Again, "w" is needed as pseudo mode.
|
||||
let (c, a) = extract_negative_modes(vec!["-w", "--", "-r", "f"].iter().map(OsString::from));
|
||||
assert_eq!(c, Some("-w".to_string()));
|
||||
assert_eq!(a, vec!["w", "--", "-r", "f"]);
|
||||
|
||||
// "chmod -- -r file" becomes "chmod -r file".
|
||||
let (c, a) = extract_negative_modes(vec!["--", "-r", "file"].iter().map(OsString::from));
|
||||
assert_eq!(c, None);
|
||||
assert_eq!(a, vec!["--", "-r", "file"]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
# cksum
|
||||
|
||||
```
|
||||
cksum [OPTIONS] [FILE]...
|
||||
```
|
||||
|
||||
Print CRC and size for each file
|
||||
|
||||
## After Help
|
||||
|
||||
DIGEST determines the digest algorithm and default output format:
|
||||
|
||||
- `-a=sysv`: (equivalent to sum -s)
|
||||
- `-a=bsd`: (equivalent to sum -r)
|
||||
- `-a=crc`: (equivalent to cksum)
|
||||
- `-a=md5`: (equivalent to md5sum)
|
||||
- `-a=sha1`: (equivalent to sha1sum)
|
||||
- `-a=sha224`: (equivalent to sha224sum)
|
||||
- `-a=sha256`: (equivalent to sha256sum)
|
||||
- `-a=sha384`: (equivalent to sha384sum)
|
||||
- `-a=sha512`: (equivalent to sha512sum)
|
||||
- `-a=blake2b`: (equivalent to b2sum)
|
||||
- `-a=sm3`: (only available through cksum)
|
||||
@@ -15,15 +15,16 @@ use std::iter;
|
||||
use std::path::Path;
|
||||
use uucore::{
|
||||
error::{FromIo, UResult},
|
||||
format_usage,
|
||||
format_usage, help_about, help_section, help_usage,
|
||||
sum::{
|
||||
div_ceil, Blake2b, Digest, DigestWriter, Md5, Sha1, Sha224, Sha256, Sha384, Sha512, Sm3,
|
||||
BSD, CRC, SYSV,
|
||||
},
|
||||
};
|
||||
|
||||
const USAGE: &str = "{} [OPTIONS] [FILE]...";
|
||||
const ABOUT: &str = "Print CRC and size for each file";
|
||||
const USAGE: &str = help_usage!("cksum.md");
|
||||
const ABOUT: &str = help_about!("cksum.md");
|
||||
const AFTER_HELP: &str = help_section!("after help", "cksum.md");
|
||||
|
||||
const ALGORITHM_OPTIONS_SYSV: &str = "sysv";
|
||||
const ALGORITHM_OPTIONS_BSD: &str = "bsd";
|
||||
@@ -205,21 +206,6 @@ mod options {
|
||||
pub static ALGORITHM: &str = "algorithm";
|
||||
}
|
||||
|
||||
const ALGORITHM_HELP_DESC: &str =
|
||||
"DIGEST determines the digest algorithm and default output format:\n\
|
||||
\n\
|
||||
-a=sysv: (equivalent to sum -s)\n\
|
||||
-a=bsd: (equivalent to sum -r)\n\
|
||||
-a=crc: (equivalent to cksum)\n\
|
||||
-a=md5: (equivalent to md5sum)\n\
|
||||
-a=sha1: (equivalent to sha1sum)\n\
|
||||
-a=sha224: (equivalent to sha224sum)\n\
|
||||
-a=sha256: (equivalent to sha256sum)\n\
|
||||
-a=sha384: (equivalent to sha384sum)\n\
|
||||
-a=sha512: (equivalent to sha512sum)\n\
|
||||
-a=blake2b: (equivalent to b2sum)\n\
|
||||
-a=sm3: (only available through cksum)\n";
|
||||
|
||||
#[uucore::main]
|
||||
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
let args = args.collect_ignore();
|
||||
@@ -278,5 +264,5 @@ pub fn uu_app() -> Command {
|
||||
ALGORITHM_OPTIONS_SM3,
|
||||
]),
|
||||
)
|
||||
.after_help(ALGORITHM_HELP_DESC)
|
||||
.after_help(AFTER_HELP)
|
||||
}
|
||||
|
||||
+24
-13
@@ -22,7 +22,7 @@ use uucore::display::Quotable;
|
||||
#[cfg(not(any(target_os = "macos", target_os = "redox")))]
|
||||
use uucore::error::FromIo;
|
||||
use uucore::error::{UResult, USimpleError};
|
||||
use uucore::{format_usage, help_about, help_usage, show_error};
|
||||
use uucore::{format_usage, help_about, help_usage, show};
|
||||
#[cfg(windows)]
|
||||
use windows_sys::Win32::{Foundation::SYSTEMTIME, System::SystemInformation::SetSystemTime};
|
||||
|
||||
@@ -114,8 +114,8 @@ impl<'a> From<&'a str> for Iso8601Format {
|
||||
SECONDS | SECOND => Self::Seconds,
|
||||
NS => Self::Ns,
|
||||
DATE => Self::Date,
|
||||
// Should be caught by clap
|
||||
_ => panic!("Invalid format: {s}"),
|
||||
// Note: This is caught by clap via `possible_values`
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -203,9 +203,6 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
|
||||
return set_system_datetime(date);
|
||||
} else {
|
||||
// Declare a file here because it needs to outlive the `dates` iterator.
|
||||
let file: File;
|
||||
|
||||
// Get the current time, either in the local time zone or UTC.
|
||||
let now: DateTime<FixedOffset> = if settings.utc {
|
||||
let now = Utc::now();
|
||||
@@ -222,12 +219,19 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
let iter = std::iter::once(date);
|
||||
Box::new(iter)
|
||||
}
|
||||
DateSource::File(ref path) => {
|
||||
file = File::open(path).unwrap();
|
||||
let lines = BufReader::new(file).lines();
|
||||
let iter = lines.filter_map(Result::ok).map(parse_date);
|
||||
Box::new(iter)
|
||||
}
|
||||
DateSource::File(ref path) => match File::open(path) {
|
||||
Ok(file) => {
|
||||
let lines = BufReader::new(file).lines();
|
||||
let iter = lines.filter_map(Result::ok).map(parse_date);
|
||||
Box::new(iter)
|
||||
}
|
||||
Err(_err) => {
|
||||
return Err(USimpleError::new(
|
||||
2,
|
||||
format!("{}: No such file or directory", path.display()),
|
||||
));
|
||||
}
|
||||
},
|
||||
DateSource::Now => {
|
||||
let iter = std::iter::once(Ok(now));
|
||||
Box::new(iter)
|
||||
@@ -257,7 +261,10 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
.replace("%f", "%N");
|
||||
println!("{formatted}");
|
||||
}
|
||||
Err((input, _err)) => show_error!("invalid date {}", input.quote()),
|
||||
Err((input, _err)) => show!(USimpleError::new(
|
||||
1,
|
||||
format!("invalid date {}", input.quote())
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -291,6 +298,9 @@ pub fn uu_app() -> Command {
|
||||
.short('I')
|
||||
.long(OPT_ISO_8601)
|
||||
.value_name("FMT")
|
||||
.value_parser([DATE, HOUR, HOURS, MINUTE, MINUTES, SECOND, SECONDS, NS])
|
||||
.num_args(0..=1)
|
||||
.default_missing_value(OPT_DATE)
|
||||
.help(ISO_8601_HELP_STRING),
|
||||
)
|
||||
.arg(
|
||||
@@ -304,6 +314,7 @@ pub fn uu_app() -> Command {
|
||||
Arg::new(OPT_RFC_3339)
|
||||
.long(OPT_RFC_3339)
|
||||
.value_name("FMT")
|
||||
.value_parser([DATE, SECOND, SECONDS, NS])
|
||||
.help(RFC_3339_HELP_STRING),
|
||||
)
|
||||
.arg(
|
||||
|
||||
+45
-4
@@ -27,11 +27,14 @@ use std::cmp;
|
||||
use std::env;
|
||||
use std::ffi::OsString;
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::io::{self, Read, Seek, SeekFrom, Stdin, Stdout, Write};
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::fs::FileTypeExt;
|
||||
use std::io::{self, Read, Seek, SeekFrom, Stdout, Write};
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
use std::os::unix::fs::OpenOptionsExt;
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::{
|
||||
fs::FileTypeExt,
|
||||
io::{AsRawFd, FromRawFd},
|
||||
};
|
||||
use std::path::Path;
|
||||
use std::sync::mpsc;
|
||||
use std::thread;
|
||||
@@ -93,21 +96,44 @@ impl Num {
|
||||
}
|
||||
|
||||
/// Data sources.
|
||||
///
|
||||
/// Use [`Source::stdin_as_file`] if available to enable more
|
||||
/// fine-grained access to reading from stdin.
|
||||
enum Source {
|
||||
/// Input from stdin.
|
||||
Stdin(Stdin),
|
||||
#[cfg(not(unix))]
|
||||
Stdin(io::Stdin),
|
||||
|
||||
/// Input from a file.
|
||||
File(File),
|
||||
|
||||
/// Input from stdin, opened from its file descriptor.
|
||||
#[cfg(unix)]
|
||||
StdinFile(File),
|
||||
|
||||
/// Input from a named pipe, also known as a FIFO.
|
||||
#[cfg(unix)]
|
||||
Fifo(File),
|
||||
}
|
||||
|
||||
impl Source {
|
||||
/// Create a source from stdin using its raw file descriptor.
|
||||
///
|
||||
/// This returns an instance of the `Source::StdinFile` variant,
|
||||
/// using the raw file descriptor of [`std::io::Stdin`] to create
|
||||
/// the [`std::fs::File`] parameter. You can use this instead of
|
||||
/// `Source::Stdin` to allow reading from stdin without consuming
|
||||
/// the entire contents of stdin when this process terminates.
|
||||
#[cfg(unix)]
|
||||
fn stdin_as_file() -> Self {
|
||||
let fd = io::stdin().as_raw_fd();
|
||||
let f = unsafe { File::from_raw_fd(fd) };
|
||||
Self::StdinFile(f)
|
||||
}
|
||||
|
||||
fn skip(&mut self, n: u64) -> io::Result<u64> {
|
||||
match self {
|
||||
#[cfg(not(unix))]
|
||||
Self::Stdin(stdin) => match io::copy(&mut stdin.take(n), &mut io::sink()) {
|
||||
Ok(m) if m < n => {
|
||||
show_error!("'standard input': cannot skip to specified offset");
|
||||
@@ -116,6 +142,15 @@ impl Source {
|
||||
Ok(m) => Ok(m),
|
||||
Err(e) => Err(e),
|
||||
},
|
||||
#[cfg(unix)]
|
||||
Self::StdinFile(f) => match io::copy(&mut f.take(n), &mut io::sink()) {
|
||||
Ok(m) if m < n => {
|
||||
show_error!("'standard input': cannot skip to specified offset");
|
||||
Ok(m)
|
||||
}
|
||||
Ok(m) => Ok(m),
|
||||
Err(e) => Err(e),
|
||||
},
|
||||
Self::File(f) => f.seek(io::SeekFrom::Start(n)),
|
||||
#[cfg(unix)]
|
||||
Self::Fifo(f) => io::copy(&mut f.take(n), &mut io::sink()),
|
||||
@@ -126,9 +161,12 @@ impl Source {
|
||||
impl Read for Source {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
match self {
|
||||
#[cfg(not(unix))]
|
||||
Self::Stdin(stdin) => stdin.read(buf),
|
||||
Self::File(f) => f.read(buf),
|
||||
#[cfg(unix)]
|
||||
Self::StdinFile(f) => f.read(buf),
|
||||
#[cfg(unix)]
|
||||
Self::Fifo(f) => f.read(buf),
|
||||
}
|
||||
}
|
||||
@@ -151,7 +189,10 @@ struct Input<'a> {
|
||||
impl<'a> Input<'a> {
|
||||
/// Instantiate this struct with stdin as a source.
|
||||
fn new_stdin(settings: &'a Settings) -> UResult<Self> {
|
||||
#[cfg(not(unix))]
|
||||
let mut src = Source::Stdin(io::stdin());
|
||||
#[cfg(unix)]
|
||||
let mut src = Source::stdin_as_file();
|
||||
if settings.skip > 0 {
|
||||
src.skip(settings.skip)?;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
# fold
|
||||
|
||||
```
|
||||
fold [OPTION]... [FILE]...
|
||||
```
|
||||
|
||||
Writes each file (or standard input if no files are given)
|
||||
to standard output whilst breaking long lines
|
||||
@@ -13,13 +13,12 @@ use std::io::{stdin, BufRead, BufReader, Read};
|
||||
use std::path::Path;
|
||||
use uucore::display::Quotable;
|
||||
use uucore::error::{FromIo, UResult, USimpleError};
|
||||
use uucore::format_usage;
|
||||
use uucore::{format_usage, help_about, help_usage};
|
||||
|
||||
const TAB_WIDTH: usize = 8;
|
||||
|
||||
static USAGE: &str = "{} [OPTION]... [FILE]...";
|
||||
static ABOUT: &str = "Writes each file (or standard input if no files are given)
|
||||
to standard output whilst breaking long lines";
|
||||
const USAGE: &str = help_usage!("fold.md");
|
||||
const ABOUT: &str = help_about!("fold.md");
|
||||
|
||||
mod options {
|
||||
pub const BYTES: &str = "bytes";
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
# mkfifo
|
||||
|
||||
```
|
||||
mkfifo [OPTION]... NAME...
|
||||
```
|
||||
|
||||
Create a FIFO with the given name.
|
||||
@@ -10,10 +10,10 @@ use libc::mkfifo;
|
||||
use std::ffi::CString;
|
||||
use uucore::display::Quotable;
|
||||
use uucore::error::{UResult, USimpleError};
|
||||
use uucore::{format_usage, show};
|
||||
use uucore::{format_usage, help_about, help_usage, show};
|
||||
|
||||
static USAGE: &str = "{} [OPTION]... NAME...";
|
||||
static ABOUT: &str = "Create a FIFO with the given name.";
|
||||
static USAGE: &str = help_usage!("mkfifo.md");
|
||||
static ABOUT: &str = help_about!("mkfifo.md");
|
||||
|
||||
mod options {
|
||||
pub static MODE: &str = "mode";
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
# mknod
|
||||
|
||||
```
|
||||
mknod [OPTION]... NAME TYPE [MAJOR MINOR]
|
||||
```
|
||||
|
||||
Create the special file NAME of the given TYPE.
|
||||
|
||||
## After Help
|
||||
|
||||
Mandatory arguments to long options are mandatory for short options too.
|
||||
`-m`, `--mode=MODE` set file permission bits to `MODE`, not `a=rw - umask`
|
||||
|
||||
Both `MAJOR` and `MINOR` must be specified when `TYPE` is `b`, `c`, or `u`, and they
|
||||
must be omitted when `TYPE` is `p`. If `MAJOR` or `MINOR` begins with `0x` or `0X`,
|
||||
it is interpreted as hexadecimal; otherwise, if it begins with 0, as octal;
|
||||
otherwise, as decimal. `TYPE` may be:
|
||||
|
||||
* `b` create a block (buffered) special file
|
||||
* `c`, `u` create a character (unbuffered) special file
|
||||
* `p` create a FIFO
|
||||
|
||||
NOTE: your shell may have its own version of mknod, which usually supersedes
|
||||
the version described here. Please refer to your shell's documentation
|
||||
for details about the options it supports.
|
||||
@@ -14,28 +14,11 @@ use std::ffi::CString;
|
||||
|
||||
use uucore::display::Quotable;
|
||||
use uucore::error::{set_exit_code, UResult, USimpleError, UUsageError};
|
||||
use uucore::format_usage;
|
||||
use uucore::{format_usage, help_about, help_section, help_usage};
|
||||
|
||||
static ABOUT: &str = "Create the special file NAME of the given TYPE.";
|
||||
static USAGE: &str = "{} [OPTION]... NAME TYPE [MAJOR MINOR]";
|
||||
static LONG_HELP: &str = "Mandatory arguments to long options are mandatory for short options too.
|
||||
-m, --mode=MODE set file permission bits to MODE, not a=rw - umask
|
||||
--help display this help and exit
|
||||
--version output version information and exit
|
||||
|
||||
Both MAJOR and MINOR must be specified when TYPE is b, c, or u, and they
|
||||
must be omitted when TYPE is p. If MAJOR or MINOR begins with 0x or 0X,
|
||||
it is interpreted as hexadecimal; otherwise, if it begins with 0, as octal;
|
||||
otherwise, as decimal. TYPE may be:
|
||||
|
||||
b create a block (buffered) special file
|
||||
c, u create a character (unbuffered) special file
|
||||
p create a FIFO
|
||||
|
||||
NOTE: your shell may have its own version of mknod, which usually supersedes
|
||||
the version described here. Please refer to your shell's documentation
|
||||
for details about the options it supports.
|
||||
";
|
||||
const ABOUT: &str = help_about!("mknod.md");
|
||||
const USAGE: &str = help_usage!("mknod.md");
|
||||
const AFTER_HELP: &str = help_section!("after help", "mknod.md");
|
||||
|
||||
const MODE_RW_UGO: mode_t = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
|
||||
|
||||
@@ -142,7 +125,7 @@ pub fn uu_app() -> Command {
|
||||
Command::new(uucore::util_name())
|
||||
.version(crate_version!())
|
||||
.override_usage(format_usage(USAGE))
|
||||
.after_help(LONG_HELP)
|
||||
.after_help(AFTER_HELP)
|
||||
.about(ABOUT)
|
||||
.infer_long_args(true)
|
||||
.arg(
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
# mktemp
|
||||
|
||||
```
|
||||
mktemp [OPTION]... [TEMPLATE]
|
||||
```
|
||||
|
||||
Create a temporary file or directory.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user