mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 19:47:45 +00:00
Fix merge conflict in tests/by-util/test_cp.rs
This commit is contained in:
commit
0379fca260
63 changed files with 1178 additions and 554 deletions
|
@ -1,2 +1,11 @@
|
||||||
[target.x86_64-unknown-redox]
|
[target.x86_64-unknown-redox]
|
||||||
linker = "x86_64-unknown-redox-gcc"
|
linker = "x86_64-unknown-redox-gcc"
|
||||||
|
|
||||||
|
[target.'cfg(feature = "cargo-clippy")']
|
||||||
|
rustflags = [
|
||||||
|
"-Wclippy::use_self",
|
||||||
|
"-Wclippy::needless_pass_by_value",
|
||||||
|
"-Wclippy::semicolon_if_nothing_returned",
|
||||||
|
"-Wclippy::single_char_pattern",
|
||||||
|
"-Wclippy::explicit_iter_loop",
|
||||||
|
]
|
||||||
|
|
9
.github/workflows/CICD.yml
vendored
9
.github/workflows/CICD.yml
vendored
|
@ -1,7 +1,7 @@
|
||||||
name: CICD
|
name: CICD
|
||||||
|
|
||||||
# spell-checker:ignore (acronyms) CICD MSVC musl
|
# spell-checker:ignore (acronyms) CICD MSVC musl
|
||||||
# spell-checker:ignore (env/flags) Awarnings Ccodegen Coverflow Cpanic RUSTDOCFLAGS RUSTFLAGS Zpanic
|
# spell-checker:ignore (env/flags) Awarnings Ccodegen Coverflow Cpanic Dwarnings RUSTDOCFLAGS RUSTFLAGS Zpanic
|
||||||
# spell-checker:ignore (jargon) SHAs deps dequote softprops subshell toolchain
|
# spell-checker:ignore (jargon) SHAs deps dequote softprops subshell toolchain
|
||||||
# spell-checker:ignore (names) CodeCOV MacOS MinGW Peltoche rivy
|
# spell-checker:ignore (names) CodeCOV MacOS MinGW Peltoche rivy
|
||||||
# spell-checker:ignore (shell/tools) choco clippy dmake dpkg esac fakeroot gmake grcov halium lcov libssl mkdir popd printf pushd rustc rustfmt rustup shopt xargs
|
# spell-checker:ignore (shell/tools) choco clippy dmake dpkg esac fakeroot gmake grcov halium lcov libssl mkdir popd printf pushd rustc rustfmt rustup shopt xargs
|
||||||
|
@ -340,6 +340,13 @@ jobs:
|
||||||
## Confirm MinSRV compatible 'Cargo.lock'
|
## Confirm MinSRV compatible 'Cargo.lock'
|
||||||
# * 'Cargo.lock' is required to be in a format that `cargo` of MinSRV can interpret (eg, v1-format for MinSRV < v1.38)
|
# * 'Cargo.lock' is required to be in a format that `cargo` of MinSRV can interpret (eg, v1-format for MinSRV < v1.38)
|
||||||
cargo fetch --locked --quiet || { echo "::error file=Cargo.lock::Incompatible (or out-of-date) 'Cargo.lock' file; update using \`cargo +${{ env.RUST_MIN_SRV }} update\`" ; exit 1 ; }
|
cargo fetch --locked --quiet || { echo "::error file=Cargo.lock::Incompatible (or out-of-date) 'Cargo.lock' file; update using \`cargo +${{ env.RUST_MIN_SRV }} update\`" ; exit 1 ; }
|
||||||
|
- name: Confirm MinSRV equivalence for '.clippy.toml'
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
## Confirm MinSRV equivalence for '.clippy.toml'
|
||||||
|
# * ensure '.clippy.toml' MSRV configuration setting is equal to ${{ env.RUST_MIN_SRV }}
|
||||||
|
CLIPPY_MSRV=$(grep -P "(?i)^\s*msrv\s*=\s*" .clippy.toml | grep -oP "\d+([.]\d+)+")
|
||||||
|
if [ "${CLIPPY_MSRV}" != "${{ env.RUST_MIN_SRV }}" ]; then { echo "::error file=.clippy.toml::Incorrect MSRV configuration for clippy (found '${CLIPPY_MSRV}'; should be '${{ env.RUST_MIN_SRV }}'); update '.clippy.toml' with 'msrv = \"${{ env.RUST_MIN_SRV }}\"'" ; exit 1 ; } ; fi
|
||||||
- name: Info
|
- name: Info
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
|
|
145
.github/workflows/GnuTests.yml
vendored
145
.github/workflows/GnuTests.yml
vendored
|
@ -1,6 +1,6 @@
|
||||||
name: GnuTests
|
name: GnuTests
|
||||||
|
|
||||||
# spell-checker:ignore (names) gnulib ; (utils) autopoint gperf pyinotify texinfo ; (vars) XPASS
|
# spell-checker:ignore (names) gnulib ; (people) Dawid Dziurla * dawidd6 ; (utils) autopoint chksum gperf pyinotify shopt texinfo ; (vars) FILESET XPASS
|
||||||
|
|
||||||
on: [push, pull_request]
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
@ -9,23 +9,55 @@ jobs:
|
||||||
name: Run GNU tests
|
name: Run GNU tests
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
- name: Initialize workflow variables
|
||||||
|
id: vars
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
## VARs setup
|
||||||
|
outputs() { step_id="vars"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; }
|
||||||
|
# * config
|
||||||
|
path_GNU="gnu"
|
||||||
|
path_GNULIB="gnulib"
|
||||||
|
path_GNU_tests="gnu/tests"
|
||||||
|
path_UUTILS="uutils"
|
||||||
|
path_reference="reference"
|
||||||
|
outputs path_GNU path_GNU_tests path_GNULIB path_reference path_UUTILS
|
||||||
|
#
|
||||||
|
repo_GNU_ref="v9.0"
|
||||||
|
repo_GNULIB_ref="8e99f24c0931a38880c6ee9b8287c7da80b0036b"
|
||||||
|
repo_reference_branch="${{ github.event.repository.default_branch }}"
|
||||||
|
outputs repo_GNU_ref repo_GNULIB_ref repo_reference_branch
|
||||||
|
#
|
||||||
|
SUITE_LOG_FILE="${path_GNU_tests}/test-suite.log"
|
||||||
|
TEST_LOGS_GLOB="${path_GNU_tests}/**/*.log" ## note: not usable at bash CLI; [why] double globstar not enabled by default b/c MacOS includes only bash v3 which doesn't have double globstar support
|
||||||
|
TEST_FILESET_PREFIX='test-fileset-IDs.sha1#'
|
||||||
|
TEST_FILESET_SUFFIX='.txt'
|
||||||
|
TEST_SUMMARY_FILE='gnu-result.json'
|
||||||
|
outputs SUITE_LOG_FILE TEST_FILESET_PREFIX TEST_FILESET_SUFFIX TEST_LOGS_GLOB TEST_SUMMARY_FILE
|
||||||
- name: Checkout code uutil
|
- name: Checkout code uutil
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
path: 'uutils'
|
path: '${{ steps.vars.outputs.path_UUTILS }}'
|
||||||
- name: Checkout GNU coreutils
|
- name: Checkout GNU coreutils
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
repository: 'coreutils/coreutils'
|
repository: 'coreutils/coreutils'
|
||||||
path: 'gnu'
|
path: '${{ steps.vars.outputs.path_GNU }}'
|
||||||
ref: v9.0
|
ref: ${{ steps.vars.outputs.repo_GNU_ref }}
|
||||||
- name: Checkout GNU coreutils library (gnulib)
|
- name: Checkout GNU coreutils library (gnulib)
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
repository: 'coreutils/gnulib'
|
repository: 'coreutils/gnulib'
|
||||||
path: 'gnulib'
|
path: '${{ steps.vars.outputs.path_GNULIB }}'
|
||||||
ref: 8e99f24c0931a38880c6ee9b8287c7da80b0036b
|
ref: ${{ steps.vars.outputs.repo_GNULIB_ref }}
|
||||||
fetch-depth: 0 # gnu gets upset if gnulib is a shallow checkout
|
fetch-depth: 0 # full depth checkout (o/w gnu gets upset if gnulib is a shallow checkout)
|
||||||
|
- name: Retrieve reference artifacts
|
||||||
|
uses: dawidd6/action-download-artifact@v2
|
||||||
|
continue-on-error: true ## don't break the build for missing reference artifacts (may be expired or just not generated yet)
|
||||||
|
with:
|
||||||
|
workflow: GnuTests.yml
|
||||||
|
branch: "${{ steps.vars.outputs.repo_reference_branch }}"
|
||||||
|
path: "${{ steps.vars.outputs.path_reference }}"
|
||||||
- name: Install `rust` toolchain
|
- name: Install `rust` toolchain
|
||||||
uses: actions-rs/toolchain@v1
|
uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
|
@ -43,27 +75,33 @@ jobs:
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
## Build binaries
|
## Build binaries
|
||||||
cd uutils
|
cd '${{ steps.vars.outputs.path_UUTILS }}'
|
||||||
bash util/build-gnu.sh
|
bash util/build-gnu.sh
|
||||||
- name: Run GNU tests
|
- name: Run GNU tests
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
bash uutils/util/run-gnu-test.sh
|
path_GNU='${{ steps.vars.outputs.path_GNU }}'
|
||||||
- name: Extract testing info
|
path_GNULIB='${{ steps.vars.outputs.path_GNULIB }}'
|
||||||
|
path_UUTILS='${{ steps.vars.outputs.path_UUTILS }}'
|
||||||
|
bash "${path_UUTILS}/util/run-gnu-test.sh"
|
||||||
|
- name: Extract/summarize testing info
|
||||||
|
id: summary
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
## Extract testing info
|
## Extract/summarize testing info
|
||||||
LOG_FILE=gnu/tests/test-suite.log
|
outputs() { step_id="summary"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; }
|
||||||
if test -f "$LOG_FILE"
|
#
|
||||||
|
SUITE_LOG_FILE='${{ steps.vars.outputs.SUITE_LOG_FILE }}'
|
||||||
|
if test -f "${SUITE_LOG_FILE}"
|
||||||
then
|
then
|
||||||
TOTAL=$(sed -n "s/.*# TOTAL: \(.*\)/\1/p" "$LOG_FILE"|tr -d '\r'|head -n1)
|
TOTAL=$(sed -n "s/.*# TOTAL: \(.*\)/\1/p" "${SUITE_LOG_FILE}" | tr -d '\r' | head -n1)
|
||||||
PASS=$(sed -n "s/.*# PASS: \(.*\)/\1/p" "$LOG_FILE"|tr -d '\r'|head -n1)
|
PASS=$(sed -n "s/.*# PASS: \(.*\)/\1/p" "${SUITE_LOG_FILE}" | tr -d '\r' | head -n1)
|
||||||
SKIP=$(sed -n "s/.*# SKIP: \(.*\)/\1/p" "$LOG_FILE"|tr -d '\r'|head -n1)
|
SKIP=$(sed -n "s/.*# SKIP: \(.*\)/\1/p" "${SUITE_LOG_FILE}" | tr -d '\r' | head -n1)
|
||||||
FAIL=$(sed -n "s/.*# FAIL: \(.*\)/\1/p" "$LOG_FILE"|tr -d '\r'|head -n1)
|
FAIL=$(sed -n "s/.*# FAIL: \(.*\)/\1/p" "${SUITE_LOG_FILE}" | tr -d '\r' | head -n1)
|
||||||
XPASS=$(sed -n "s/.*# XPASS: \(.*\)/\1/p" "$LOG_FILE"|tr -d '\r'|head -n1)
|
XPASS=$(sed -n "s/.*# XPASS: \(.*\)/\1/p" "${SUITE_LOG_FILE}" | tr -d '\r' | head -n1)
|
||||||
ERROR=$(sed -n "s/.*# ERROR: \(.*\)/\1/p" "$LOG_FILE"|tr -d '\r'|head -n1)
|
ERROR=$(sed -n "s/.*# ERROR: \(.*\)/\1/p" "${SUITE_LOG_FILE}" | tr -d '\r' | head -n1)
|
||||||
if [[ "$TOTAL" -eq 0 || "$TOTAL" -eq 1 ]]; then
|
if [[ "$TOTAL" -eq 0 || "$TOTAL" -eq 1 ]]; then
|
||||||
echo "Error in the execution, failing early"
|
echo "::error ::Failed to parse test results from '${SUITE_LOG_FILE}'; failing early"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
output="GNU tests summary = TOTAL: $TOTAL / PASS: $PASS / FAIL: $FAIL / ERROR: $ERROR"
|
output="GNU tests summary = TOTAL: $TOTAL / PASS: $PASS / FAIL: $FAIL / ERROR: $ERROR"
|
||||||
|
@ -78,40 +116,38 @@ jobs:
|
||||||
--arg fail "$FAIL" \
|
--arg fail "$FAIL" \
|
||||||
--arg xpass "$XPASS" \
|
--arg xpass "$XPASS" \
|
||||||
--arg error "$ERROR" \
|
--arg error "$ERROR" \
|
||||||
'{($date): { sha: $sha, total: $total, pass: $pass, skip: $skip, fail: $fail, xpass: $xpass, error: $error, }}' > gnu-result.json
|
'{($date): { sha: $sha, total: $total, pass: $pass, skip: $skip, fail: $fail, xpass: $xpass, error: $error, }}' > '${{ steps.vars.outputs.TEST_SUMMARY_FILE }}'
|
||||||
|
HASH=$(sha1sum '${{ steps.vars.outputs.TEST_SUMMARY_FILE }}' | cut --delim=" " -f 1)
|
||||||
|
outputs HASH
|
||||||
else
|
else
|
||||||
echo "::error ::Failed to get summary of test results"
|
echo "::error ::Failed to find summary of test results (missing '${SUITE_LOG_FILE}'); failing early"
|
||||||
|
exit 1
|
||||||
fi
|
fi
|
||||||
- uses: actions/upload-artifact@v2
|
- name: Reserve SHA1/ID of 'test-summary'
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: test-report
|
name: "${{ steps.summary.outputs.HASH }}"
|
||||||
path: gnu/tests/**/*.log
|
path: "${{ steps.vars.outputs.TEST_SUMMARY_FILE }}"
|
||||||
- uses: actions/upload-artifact@v2
|
- name: Reserve test results summary
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: gnu-result
|
name: test-summary
|
||||||
path: gnu-result.json
|
path: "${{ steps.vars.outputs.TEST_SUMMARY_FILE }}"
|
||||||
- name: Download the result
|
- name: Reserve test logs
|
||||||
uses: dawidd6/action-download-artifact@v2
|
uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
workflow: GnuTests.yml
|
name: test-logs
|
||||||
name: gnu-result
|
path: "${{ steps.vars.outputs.TEST_LOGS_GLOB }}"
|
||||||
repo: uutils/coreutils
|
- name: Compare test failures VS reference
|
||||||
branch: main
|
|
||||||
path: dl
|
|
||||||
- name: Download the log
|
|
||||||
uses: dawidd6/action-download-artifact@v2
|
|
||||||
with:
|
|
||||||
workflow: GnuTests.yml
|
|
||||||
name: test-report
|
|
||||||
repo: uutils/coreutils
|
|
||||||
branch: main
|
|
||||||
path: dl
|
|
||||||
- name: Compare failing tests against main
|
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
OLD_FAILING=$(sed -n "s/^FAIL: \([[:print:]]\+\).*/\1/p" dl/test-suite.log | sort)
|
REF_LOG_FILE='${{ steps.vars.outputs.path_reference }}/test-logs/test-suite.log'
|
||||||
NEW_FAILING=$(sed -n "s/^FAIL: \([[:print:]]\+\).*/\1/p" gnu/tests/test-suite.log | sort)
|
REF_SUMMARY_FILE='${{ steps.vars.outputs.path_reference }}/test-summary/gnu-result.json'
|
||||||
for LINE in $OLD_FAILING
|
if test -f "${REF_LOG_FILE}"; then
|
||||||
|
echo "Reference SHA1/ID (of '${REF_SUMMARY_FILE}'): $(sha1sum -- "${REF_SUMMARY_FILE}")"
|
||||||
|
REF_FAILING=$(sed -n "s/^FAIL: \([[:print:]]\+\).*/\1/p" "${REF_LOG_FILE}" | sort)
|
||||||
|
NEW_FAILING=$(sed -n "s/^FAIL: \([[:print:]]\+\).*/\1/p" '${{ steps.vars.outputs.path_GNU_tests }}/test-suite.log' | sort)
|
||||||
|
for LINE in $REF_FAILING
|
||||||
do
|
do
|
||||||
if ! grep -Fxq $LINE<<<"$NEW_FAILING"; then
|
if ! grep -Fxq $LINE<<<"$NEW_FAILING"; then
|
||||||
echo "::warning ::Congrats! The gnu test $LINE is now passing!"
|
echo "::warning ::Congrats! The gnu test $LINE is now passing!"
|
||||||
|
@ -119,13 +155,22 @@ jobs:
|
||||||
done
|
done
|
||||||
for LINE in $NEW_FAILING
|
for LINE in $NEW_FAILING
|
||||||
do
|
do
|
||||||
if ! grep -Fxq $LINE<<<"$OLD_FAILING"
|
if ! grep -Fxq $LINE<<<"$REF_FAILING"
|
||||||
then
|
then
|
||||||
echo "::error ::GNU test failed: $LINE. $LINE is passing on 'main'. Maybe you have to rebase?"
|
echo "::error ::GNU test failed: $LINE. $LINE is passing on 'main'. Maybe you have to rebase?"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
- name: Compare against main results
|
else
|
||||||
|
echo "::warning ::Skipping test failure comparison; no prior reference test logs are available."
|
||||||
|
fi
|
||||||
|
- name: Compare test summary VS reference
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
mv dl/gnu-result.json main-gnu-result.json
|
REF_SUMMARY_FILE='${{ steps.vars.outputs.path_reference }}/test-summary/gnu-result.json'
|
||||||
|
if test -f "${REF_SUMMARY_FILE}"; then
|
||||||
|
echo "Reference SHA1/ID (of '${REF_SUMMARY_FILE}'): $(sha1sum -- "${REF_SUMMARY_FILE}")"
|
||||||
|
mv "${REF_SUMMARY_FILE}" main-gnu-result.json
|
||||||
python uutils/util/compare_gnu_result.py
|
python uutils/util/compare_gnu_result.py
|
||||||
|
else
|
||||||
|
echo "::warning ::Skipping test summary comparison; no prior reference summary is available."
|
||||||
|
fi
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#!/bin/bash
|
#!/bin/sh
|
||||||
|
|
||||||
rustup target add x86_64-unknown-redox
|
rustup target add x86_64-unknown-redox
|
||||||
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys AA12E97F0881517F
|
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys AA12E97F0881517F
|
||||||
|
|
2
.vscode/.gitattributes
vendored
Normal file
2
.vscode/.gitattributes
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
# Configure GitHub to not mark comments in configuration files as errors
|
||||||
|
*.json linguist-language=jsonc
|
26
.vscode/cSpell.json
vendored
26
.vscode/cSpell.json
vendored
|
@ -1,7 +1,12 @@
|
||||||
// `cspell` settings
|
// `cspell` settings
|
||||||
{
|
{
|
||||||
"version": "0.1", // Version of the setting file. Always 0.1
|
// version of the setting file (always 0.1)
|
||||||
"language": "en", // language - current active spelling language
|
"version": "0.1",
|
||||||
|
|
||||||
|
// spelling language
|
||||||
|
"language": "en",
|
||||||
|
|
||||||
|
// custom dictionaries
|
||||||
"dictionaries": ["acronyms+names", "jargon", "people", "shell", "workspace"],
|
"dictionaries": ["acronyms+names", "jargon", "people", "shell", "workspace"],
|
||||||
"dictionaryDefinitions": [
|
"dictionaryDefinitions": [
|
||||||
{ "name": "acronyms+names", "path": "./cspell.dictionaries/acronyms+names.wordlist.txt" },
|
{ "name": "acronyms+names", "path": "./cspell.dictionaries/acronyms+names.wordlist.txt" },
|
||||||
|
@ -10,10 +15,19 @@
|
||||||
{ "name": "shell", "path": "./cspell.dictionaries/shell.wordlist.txt" },
|
{ "name": "shell", "path": "./cspell.dictionaries/shell.wordlist.txt" },
|
||||||
{ "name": "workspace", "path": "./cspell.dictionaries/workspace.wordlist.txt" }
|
{ "name": "workspace", "path": "./cspell.dictionaries/workspace.wordlist.txt" }
|
||||||
],
|
],
|
||||||
// ignorePaths - a list of globs to specify which files are to be ignored
|
|
||||||
"ignorePaths": ["Cargo.lock", "target/**", "tests/**/fixtures/**", "src/uu/dd/test-resources/**", "vendor/**"],
|
// files to ignore (globs supported)
|
||||||
// ignoreWords - a list of words to be ignored (even if they are in the flagWords)
|
"ignorePaths": [
|
||||||
|
"Cargo.lock",
|
||||||
|
"target/**",
|
||||||
|
"tests/**/fixtures/**",
|
||||||
|
"src/uu/dd/test-resources/**",
|
||||||
|
"vendor/**"
|
||||||
|
],
|
||||||
|
|
||||||
|
// words to ignore (even if they are in the flagWords)
|
||||||
"ignoreWords": [],
|
"ignoreWords": [],
|
||||||
// words - list of words to be always considered correct
|
|
||||||
|
// words to always consider correct
|
||||||
"words": []
|
"words": []
|
||||||
}
|
}
|
||||||
|
|
6
.vscode/extensions.json
vendored
6
.vscode/extensions.json
vendored
|
@ -1,10 +1,8 @@
|
||||||
{
|
|
||||||
// spell-checker:ignore (misc) matklad
|
// spell-checker:ignore (misc) matklad
|
||||||
// see <http://go.microsoft.com/fwlink/?LinkId=827846> for the documentation about the extensions.json format
|
// see <http://go.microsoft.com/fwlink/?LinkId=827846> for the documentation about the extensions.json format
|
||||||
|
{
|
||||||
"recommendations": [
|
"recommendations": [
|
||||||
// Rust language support.
|
// Rust language support
|
||||||
"rust-lang.rust",
|
|
||||||
// Provides support for rust-analyzer: novel LSP server for the Rust programming language.
|
|
||||||
"matklad.rust-analyzer",
|
"matklad.rust-analyzer",
|
||||||
// `cspell` spell-checker support
|
// `cspell` spell-checker support
|
||||||
"streetsidesoftware.code-spell-checker"
|
"streetsidesoftware.code-spell-checker"
|
||||||
|
|
|
@ -87,7 +87,7 @@ fn main() {
|
||||||
};
|
};
|
||||||
|
|
||||||
if util == "completion" {
|
if util == "completion" {
|
||||||
gen_completions(args, utils);
|
gen_completions(args, &utils);
|
||||||
}
|
}
|
||||||
|
|
||||||
match utils.get(util) {
|
match utils.get(util) {
|
||||||
|
@ -132,7 +132,7 @@ fn main() {
|
||||||
/// Prints completions for the utility in the first parameter for the shell in the second parameter to stdout
|
/// Prints completions for the utility in the first parameter for the shell in the second parameter to stdout
|
||||||
fn gen_completions<T: uucore::Args>(
|
fn gen_completions<T: uucore::Args>(
|
||||||
args: impl Iterator<Item = OsString>,
|
args: impl Iterator<Item = OsString>,
|
||||||
util_map: UtilityMap<T>,
|
util_map: &UtilityMap<T>,
|
||||||
) -> ! {
|
) -> ! {
|
||||||
let all_utilities: Vec<_> = std::iter::once("coreutils")
|
let all_utilities: Vec<_> = std::iter::once("coreutils")
|
||||||
.chain(util_map.keys().copied())
|
.chain(util_map.keys().copied())
|
||||||
|
@ -168,9 +168,9 @@ fn gen_completions<T: uucore::Args>(
|
||||||
process::exit(0);
|
process::exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gen_coreutils_app<T: uucore::Args>(util_map: UtilityMap<T>) -> App<'static> {
|
fn gen_coreutils_app<T: uucore::Args>(util_map: &UtilityMap<T>) -> App<'static> {
|
||||||
let mut app = App::new("coreutils");
|
let mut app = App::new("coreutils");
|
||||||
for (_, (_, sub_app)) in &util_map {
|
for (_, (_, sub_app)) in util_map {
|
||||||
app = app.subcommand(sub_app());
|
app = app.subcommand(sub_app());
|
||||||
}
|
}
|
||||||
app
|
app
|
||||||
|
|
|
@ -12,7 +12,7 @@ use uucore::{encoding::Format, error::UResult};
|
||||||
|
|
||||||
pub mod base_common;
|
pub mod base_common;
|
||||||
|
|
||||||
static ABOUT: &str = "
|
static ABOUT: &str = "\
|
||||||
With no FILE, or when FILE is -, read standard input.
|
With no FILE, or when FILE is -, read standard input.
|
||||||
|
|
||||||
The data are encoded as described for the base32 alphabet in RFC
|
The data are encoded as described for the base32 alphabet in RFC
|
||||||
|
|
|
@ -13,7 +13,7 @@ use uucore::{encoding::Format, error::UResult};
|
||||||
|
|
||||||
use std::io::{stdin, Read};
|
use std::io::{stdin, Read};
|
||||||
|
|
||||||
static ABOUT: &str = "
|
static ABOUT: &str = "\
|
||||||
With no FILE, or when FILE is -, read standard input.
|
With no FILE, or when FILE is -, read standard input.
|
||||||
|
|
||||||
The data are encoded as described for the base64 alphabet in RFC
|
The data are encoded as described for the base64 alphabet in RFC
|
||||||
|
|
|
@ -19,7 +19,7 @@ use uucore::{
|
||||||
|
|
||||||
use std::io::{stdin, Read};
|
use std::io::{stdin, Read};
|
||||||
|
|
||||||
static ABOUT: &str = "
|
static ABOUT: &str = "\
|
||||||
With no FILE, or when FILE is -, read standard input.
|
With no FILE, or when FILE is -, read standard input.
|
||||||
|
|
||||||
When decoding, the input may contain newlines in addition to the bytes of
|
When decoding, the input may contain newlines in addition to the bytes of
|
||||||
|
|
|
@ -201,12 +201,12 @@ fn set_main_group(group: &str) -> UResult<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(target_vendor = "apple", target_os = "freebsd"))]
|
#[cfg(any(target_vendor = "apple", target_os = "freebsd"))]
|
||||||
fn set_groups(groups: Vec<libc::gid_t>) -> libc::c_int {
|
fn set_groups(groups: &[libc::gid_t]) -> libc::c_int {
|
||||||
unsafe { setgroups(groups.len() as libc::c_int, groups.as_ptr()) }
|
unsafe { setgroups(groups.len() as libc::c_int, groups.as_ptr()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
fn set_groups(groups: Vec<libc::gid_t>) -> libc::c_int {
|
fn set_groups(groups: &[libc::gid_t]) -> libc::c_int {
|
||||||
unsafe { setgroups(groups.len() as libc::size_t, groups.as_ptr()) }
|
unsafe { setgroups(groups.len() as libc::size_t, groups.as_ptr()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,7 +220,7 @@ fn set_groups_from_str(groups: &str) -> UResult<()> {
|
||||||
};
|
};
|
||||||
groups_vec.push(gid);
|
groups_vec.push(gid);
|
||||||
}
|
}
|
||||||
let err = set_groups(groups_vec);
|
let err = set_groups(&groups_vec);
|
||||||
if err != 0 {
|
if err != 0 {
|
||||||
return Err(ChrootError::SetGroupsFailed(Error::last_os_error()).into());
|
return Err(ChrootError::SetGroupsFailed(Error::last_os_error()).into());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1035,7 +1035,10 @@ fn copy_directory(
|
||||||
if is_symlink && !options.dereference {
|
if is_symlink && !options.dereference {
|
||||||
copy_link(&path, &local_to_target, symlinked_files)?;
|
copy_link(&path, &local_to_target, symlinked_files)?;
|
||||||
} else if path.is_dir() && !local_to_target.exists() {
|
} else if path.is_dir() && !local_to_target.exists() {
|
||||||
or_continue!(fs::create_dir_all(local_to_target));
|
if target.is_file() {
|
||||||
|
return Err("cannot overwrite non-directory with directory".into());
|
||||||
|
}
|
||||||
|
fs::create_dir_all(local_to_target)?;
|
||||||
} else if !path.is_dir() {
|
} else if !path.is_dir() {
|
||||||
if preserve_hard_links {
|
if preserve_hard_links {
|
||||||
let mut found_hard_link = false;
|
let mut found_hard_link = false;
|
||||||
|
|
|
@ -4,7 +4,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 ctty, ctable, iconvflags, oconvflags
|
// spell-checker:ignore ctty, ctable, iconvflags, oconvflags parseargs
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod unit_tests;
|
mod unit_tests;
|
||||||
|
@ -12,6 +12,8 @@ mod unit_tests;
|
||||||
use super::*;
|
use super::*;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use uucore::error::UError;
|
use uucore::error::UError;
|
||||||
|
use uucore::parse_size::ParseSizeError;
|
||||||
|
use uucore::show_warning;
|
||||||
|
|
||||||
pub type Matches = ArgMatches;
|
pub type Matches = ArgMatches;
|
||||||
|
|
||||||
|
@ -31,6 +33,25 @@ pub enum ParseError {
|
||||||
Unimplemented(String),
|
Unimplemented(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ParseError {
|
||||||
|
/// Replace the argument, if any, with the given string, consuming self.
|
||||||
|
fn with_arg(self, s: String) -> Self {
|
||||||
|
match self {
|
||||||
|
Self::MultipleFmtTable => Self::MultipleFmtTable,
|
||||||
|
Self::MultipleUCaseLCase => Self::MultipleUCaseLCase,
|
||||||
|
Self::MultipleBlockUnblock => Self::MultipleBlockUnblock,
|
||||||
|
Self::MultipleExclNoCreate => Self::MultipleExclNoCreate,
|
||||||
|
Self::FlagNoMatch(_) => Self::FlagNoMatch(s),
|
||||||
|
Self::ConvFlagNoMatch(_) => Self::ConvFlagNoMatch(s),
|
||||||
|
Self::MultiplierStringParseFailure(_) => Self::MultiplierStringParseFailure(s),
|
||||||
|
Self::MultiplierStringOverflow(_) => Self::MultiplierStringOverflow(s),
|
||||||
|
Self::BlockUnblockWithoutCBS => Self::BlockUnblockWithoutCBS,
|
||||||
|
Self::StatusLevelNotRecognized(_) => Self::StatusLevelNotRecognized(s),
|
||||||
|
Self::Unimplemented(_) => Self::Unimplemented(s),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for ParseError {
|
impl std::fmt::Display for ParseError {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
|
@ -310,28 +331,83 @@ fn parse_bytes_only(s: &str) -> Result<usize, ParseError> {
|
||||||
.map_err(|_| ParseError::MultiplierStringParseFailure(s.to_string()))
|
.map_err(|_| ParseError::MultiplierStringParseFailure(s.to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parse a number of bytes from the given string, assuming no `'x'` characters.
|
||||||
|
///
|
||||||
|
/// The `'x'` character means "multiply the number before the `'x'` by
|
||||||
|
/// the number after the `'x'`". In order to compute the numbers
|
||||||
|
/// before and after the `'x'`, use this function, which assumes there
|
||||||
|
/// are no `'x'` characters in the string.
|
||||||
|
///
|
||||||
|
/// A suffix `'c'` means multiply by 1, `'w'` by 2, and `'b'` by
|
||||||
|
/// 512. You can also use standard block size suffixes like `'k'` for
|
||||||
|
/// 1024.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// If a number cannot be parsed or if the multiplication would cause
|
||||||
|
/// an overflow.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// assert_eq!(parse_bytes_no_x("123").unwrap(), 123);
|
||||||
|
/// assert_eq!(parse_bytes_no_x("2c").unwrap(), 2 * 1);
|
||||||
|
/// assert_eq!(parse_bytes_no_x("3w").unwrap(), 3 * 2);
|
||||||
|
/// assert_eq!(parse_bytes_no_x("2b").unwrap(), 2 * 512);
|
||||||
|
/// assert_eq!(parse_bytes_no_x("2k").unwrap(), 2 * 1024);
|
||||||
|
/// ```
|
||||||
|
fn parse_bytes_no_x(s: &str) -> Result<usize, ParseError> {
|
||||||
|
if s == "0" {
|
||||||
|
show_warning!(
|
||||||
|
"{} is a zero multiplier; use {} if that is intended",
|
||||||
|
"0x".quote(),
|
||||||
|
"00x".quote()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let (num, multiplier) = match (s.find('c'), s.rfind('w'), s.rfind('b')) {
|
||||||
|
(None, None, None) => match uucore::parse_size::parse_size(s) {
|
||||||
|
Ok(n) => (n, 1),
|
||||||
|
Err(ParseSizeError::ParseFailure(s)) => {
|
||||||
|
return Err(ParseError::MultiplierStringParseFailure(s))
|
||||||
|
}
|
||||||
|
Err(ParseSizeError::SizeTooBig(s)) => {
|
||||||
|
return Err(ParseError::MultiplierStringOverflow(s))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(Some(i), None, None) => (parse_bytes_only(&s[..i])?, 1),
|
||||||
|
(None, Some(i), None) => (parse_bytes_only(&s[..i])?, 2),
|
||||||
|
(None, None, Some(i)) => (parse_bytes_only(&s[..i])?, 512),
|
||||||
|
_ => return Err(ParseError::MultiplierStringParseFailure(s.to_string())),
|
||||||
|
};
|
||||||
|
num.checked_mul(multiplier)
|
||||||
|
.ok_or_else(|| ParseError::MultiplierStringOverflow(s.to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
/// Parse byte and multiplier like 512, 5KiB, or 1G.
|
/// Parse byte and multiplier like 512, 5KiB, or 1G.
|
||||||
/// Uses uucore::parse_size, and adds the 'w' and 'c' suffixes which are mentioned
|
/// Uses uucore::parse_size, and adds the 'w' and 'c' suffixes which are mentioned
|
||||||
/// in dd's info page.
|
/// in dd's info page.
|
||||||
fn parse_bytes_with_opt_multiplier(s: &str) -> Result<usize, ParseError> {
|
fn parse_bytes_with_opt_multiplier(s: &str) -> Result<usize, ParseError> {
|
||||||
if let Some(idx) = s.rfind('c') {
|
// TODO On my Linux system, there seems to be a maximum block size of 4096 bytes:
|
||||||
parse_bytes_only(&s[..idx])
|
//
|
||||||
} else if let Some(idx) = s.rfind('w') {
|
// $ printf "%0.sa" {1..10000} | dd bs=4095 count=1 status=none | wc -c
|
||||||
let partial = parse_bytes_only(&s[..idx])?;
|
// 4095
|
||||||
|
// $ printf "%0.sa" {1..10000} | dd bs=4k count=1 status=none | wc -c
|
||||||
|
// 4096
|
||||||
|
// $ printf "%0.sa" {1..10000} | dd bs=4097 count=1 status=none | wc -c
|
||||||
|
// 4096
|
||||||
|
// $ printf "%0.sa" {1..10000} | dd bs=5k count=1 status=none | wc -c
|
||||||
|
// 4096
|
||||||
|
//
|
||||||
|
|
||||||
partial
|
// Split on the 'x' characters. Each component will be parsed
|
||||||
.checked_mul(2)
|
// individually, then multiplied together.
|
||||||
.ok_or_else(|| ParseError::MultiplierStringOverflow(s.to_string()))
|
let mut total = 1;
|
||||||
} else {
|
for part in s.split('x') {
|
||||||
uucore::parse_size::parse_size(s).map_err(|e| match e {
|
let num = parse_bytes_no_x(part).map_err(|e| e.with_arg(s.to_string()))?;
|
||||||
uucore::parse_size::ParseSizeError::ParseFailure(s) => {
|
total *= num;
|
||||||
ParseError::MultiplierStringParseFailure(s)
|
|
||||||
}
|
|
||||||
uucore::parse_size::ParseSizeError::SizeTooBig(s) => {
|
|
||||||
ParseError::MultiplierStringOverflow(s)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(total)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_ibs(matches: &Matches) -> Result<usize, ParseError> {
|
pub fn parse_ibs(matches: &Matches) -> Result<usize, ParseError> {
|
||||||
|
@ -689,3 +765,25 @@ pub fn parse_input_non_ascii(matches: &Matches) -> Result<bool, ParseError> {
|
||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
|
||||||
|
use crate::parseargs::parse_bytes_with_opt_multiplier;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_bytes_with_opt_multiplier() {
|
||||||
|
assert_eq!(parse_bytes_with_opt_multiplier("123").unwrap(), 123);
|
||||||
|
assert_eq!(parse_bytes_with_opt_multiplier("123c").unwrap(), 123 * 1);
|
||||||
|
assert_eq!(parse_bytes_with_opt_multiplier("123w").unwrap(), 123 * 2);
|
||||||
|
assert_eq!(parse_bytes_with_opt_multiplier("123b").unwrap(), 123 * 512);
|
||||||
|
assert_eq!(parse_bytes_with_opt_multiplier("123x3").unwrap(), 123 * 3);
|
||||||
|
assert_eq!(parse_bytes_with_opt_multiplier("123k").unwrap(), 123 * 1024);
|
||||||
|
assert_eq!(parse_bytes_with_opt_multiplier("1x2x3").unwrap(), 1 * 2 * 3);
|
||||||
|
assert_eq!(
|
||||||
|
parse_bytes_with_opt_multiplier("1wx2cx3w").unwrap(),
|
||||||
|
(1 * 2) * (2 * 1) * (3 * 2)
|
||||||
|
);
|
||||||
|
assert!(parse_bytes_with_opt_multiplier("123asdf").is_err());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -12,17 +12,17 @@ use uucore::error::UResult;
|
||||||
use uucore::fsext::statfs_fn;
|
use uucore::fsext::statfs_fn;
|
||||||
use uucore::fsext::{read_fs_list, FsUsage, MountInfo};
|
use uucore::fsext::{read_fs_list, FsUsage, MountInfo};
|
||||||
|
|
||||||
use clap::{crate_version, App, AppSettings, Arg};
|
use clap::{crate_version, App, AppSettings, Arg, ArgMatches};
|
||||||
|
|
||||||
use number_prefix::NumberPrefix;
|
use number_prefix::NumberPrefix;
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
use std::iter::FromIterator;
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
|
@ -69,6 +69,27 @@ struct Options {
|
||||||
fs_selector: FsSelector,
|
fs_selector: FsSelector,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Options {
|
||||||
|
/// Convert command-line arguments into [`Options`].
|
||||||
|
fn from(matches: &ArgMatches) -> Self {
|
||||||
|
Self {
|
||||||
|
show_local_fs: matches.is_present(OPT_LOCAL),
|
||||||
|
show_all_fs: matches.is_present(OPT_ALL),
|
||||||
|
show_listed_fs: false,
|
||||||
|
show_fs_type: matches.is_present(OPT_PRINT_TYPE),
|
||||||
|
show_inode_instead: matches.is_present(OPT_INODES),
|
||||||
|
human_readable_base: if matches.is_present(OPT_HUMAN_READABLE) {
|
||||||
|
1024
|
||||||
|
} else if matches.is_present(OPT_HUMAN_READABLE_2) {
|
||||||
|
1000
|
||||||
|
} else {
|
||||||
|
-1
|
||||||
|
},
|
||||||
|
fs_selector: FsSelector::from(matches),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct Filesystem {
|
struct Filesystem {
|
||||||
mount_info: MountInfo,
|
mount_info: MountInfo,
|
||||||
|
@ -80,18 +101,19 @@ fn usage() -> String {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FsSelector {
|
impl FsSelector {
|
||||||
fn new() -> Self {
|
/// Convert command-line arguments into a [`FsSelector`].
|
||||||
Self::default()
|
///
|
||||||
}
|
/// This function reads the include and exclude sets from
|
||||||
|
/// [`ArgMatches`] and returns the corresponding [`FsSelector`]
|
||||||
#[inline(always)]
|
/// instance.
|
||||||
fn include(&mut self, fs_type: String) {
|
fn from(matches: &ArgMatches) -> Self {
|
||||||
self.include.insert(fs_type);
|
let include = HashSet::from_iter(matches.values_of_lossy(OPT_TYPE).unwrap_or_default());
|
||||||
}
|
let exclude = HashSet::from_iter(
|
||||||
|
matches
|
||||||
#[inline(always)]
|
.values_of_lossy(OPT_EXCLUDE_TYPE)
|
||||||
fn exclude(&mut self, fs_type: String) {
|
.unwrap_or_default(),
|
||||||
self.exclude.insert(fs_type);
|
);
|
||||||
|
Self { include, exclude }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn should_select(&self, fs_type: &str) -> bool {
|
fn should_select(&self, fs_type: &str) -> bool {
|
||||||
|
@ -102,24 +124,6 @@ impl FsSelector {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Options {
|
|
||||||
fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
show_local_fs: false,
|
|
||||||
show_all_fs: false,
|
|
||||||
show_listed_fs: false,
|
|
||||||
show_fs_type: false,
|
|
||||||
show_inode_instead: false,
|
|
||||||
// block_size: match env::var("BLOCKSIZE") {
|
|
||||||
// Ok(size) => size.parse().unwrap(),
|
|
||||||
// Err(_) => 512,
|
|
||||||
// },
|
|
||||||
human_readable_base: -1,
|
|
||||||
fs_selector: FsSelector::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Filesystem {
|
impl Filesystem {
|
||||||
// TODO: resolve uuid in `mount_info.dev_name` if exists
|
// TODO: resolve uuid in `mount_info.dev_name` if exists
|
||||||
fn new(mount_info: MountInfo) -> Option<Self> {
|
fn new(mount_info: MountInfo) -> Option<Self> {
|
||||||
|
@ -157,35 +161,52 @@ impl Filesystem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Keep only the specified subset of [`MountInfo`] instances.
|
||||||
|
///
|
||||||
|
/// If `paths` is non-empty, this function excludes any [`MountInfo`]
|
||||||
|
/// that is not mounted at the specified path.
|
||||||
|
///
|
||||||
|
/// The `opt` argument specifies a variety of ways of excluding
|
||||||
|
/// [`MountInfo`] instances; see [`Options`] for more information.
|
||||||
|
///
|
||||||
|
/// Finally, if there are duplicate entries, the one with the shorter
|
||||||
|
/// path is kept.
|
||||||
fn filter_mount_list(vmi: Vec<MountInfo>, paths: &[String], opt: &Options) -> Vec<MountInfo> {
|
fn filter_mount_list(vmi: Vec<MountInfo>, paths: &[String], opt: &Options) -> Vec<MountInfo> {
|
||||||
vmi.into_iter()
|
let mut mount_info_by_id = HashMap::<String, Cell<MountInfo>>::new();
|
||||||
.filter_map(|mi| {
|
for mi in vmi {
|
||||||
if (mi.remote && opt.show_local_fs)
|
// Don't show remote filesystems if `--local` has been given.
|
||||||
|| (mi.dummy && !opt.show_all_fs && !opt.show_listed_fs)
|
if mi.remote && opt.show_local_fs {
|
||||||
|| !opt.fs_selector.should_select(&mi.fs_type)
|
continue;
|
||||||
{
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
if paths.is_empty() {
|
|
||||||
// No path specified
|
|
||||||
return Some((mi.dev_id.clone(), mi));
|
|
||||||
}
|
}
|
||||||
if paths.contains(&mi.mount_dir) {
|
|
||||||
// One or more paths have been provided
|
// Don't show pseudo filesystems unless `--all` has been given.
|
||||||
Some((mi.dev_id.clone(), mi))
|
if mi.dummy && !opt.show_all_fs && !opt.show_listed_fs {
|
||||||
} else {
|
continue;
|
||||||
// Not a path we want to see
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Don't show filesystems if they have been explicitly excluded.
|
||||||
|
if !opt.fs_selector.should_select(&mi.fs_type) {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
})
|
|
||||||
.fold(
|
// Don't show filesystems other than the ones specified on the
|
||||||
HashMap::<String, Cell<MountInfo>>::new(),
|
// command line, if any.
|
||||||
|mut acc, (id, mi)| {
|
if !paths.is_empty() && !paths.contains(&mi.mount_dir) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the device ID has not been encountered yet, just store it.
|
||||||
|
let id = mi.dev_id.clone();
|
||||||
#[allow(clippy::map_entry)]
|
#[allow(clippy::map_entry)]
|
||||||
{
|
if !mount_info_by_id.contains_key(&id) {
|
||||||
if acc.contains_key(&id) {
|
mount_info_by_id.insert(id, Cell::new(mi));
|
||||||
let seen = acc[&id].replace(mi.clone());
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, if we have seen the current device ID before,
|
||||||
|
// then check if we need to update it or keep the previously
|
||||||
|
// seen one.
|
||||||
|
let seen = mount_info_by_id[&id].replace(mi.clone());
|
||||||
let target_nearer_root = seen.mount_dir.len() > mi.mount_dir.len();
|
let target_nearer_root = seen.mount_dir.len() > mi.mount_dir.len();
|
||||||
// With bind mounts, prefer items nearer the root of the source
|
// With bind mounts, prefer items nearer the root of the source
|
||||||
let source_below_root = !seen.mount_root.is_empty()
|
let source_below_root = !seen.mount_root.is_empty()
|
||||||
|
@ -203,18 +224,16 @@ fn filter_mount_list(vmi: Vec<MountInfo>, paths: &[String], opt: &Options) -> Ve
|
||||||
environments for example. */
|
environments for example. */
|
||||||
|| seen.mount_dir != mi.mount_dir)
|
|| seen.mount_dir != mi.mount_dir)
|
||||||
{
|
{
|
||||||
acc[&id].replace(seen);
|
mount_info_by_id[&id].replace(seen);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
acc.insert(id, Cell::new(mi));
|
|
||||||
}
|
}
|
||||||
acc
|
|
||||||
}
|
// Take ownership of the `MountInfo` instances and collect them
|
||||||
},
|
// into a `Vec`.
|
||||||
)
|
mount_info_by_id
|
||||||
.into_iter()
|
.into_values()
|
||||||
.map(|ent| ent.1.into_inner())
|
.map(|m| m.into_inner())
|
||||||
.collect::<Vec<_>>()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert `value` to a human readable string based on `base`.
|
/// Convert `value` to a human readable string based on `base`.
|
||||||
|
@ -293,34 +312,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut opt = Options::new();
|
let opt = Options::from(&matches);
|
||||||
if matches.is_present(OPT_LOCAL) {
|
|
||||||
opt.show_local_fs = true;
|
|
||||||
}
|
|
||||||
if matches.is_present(OPT_ALL) {
|
|
||||||
opt.show_all_fs = true;
|
|
||||||
}
|
|
||||||
if matches.is_present(OPT_INODES) {
|
|
||||||
opt.show_inode_instead = true;
|
|
||||||
}
|
|
||||||
if matches.is_present(OPT_PRINT_TYPE) {
|
|
||||||
opt.show_fs_type = true;
|
|
||||||
}
|
|
||||||
if matches.is_present(OPT_HUMAN_READABLE) {
|
|
||||||
opt.human_readable_base = 1024;
|
|
||||||
}
|
|
||||||
if matches.is_present(OPT_HUMAN_READABLE_2) {
|
|
||||||
opt.human_readable_base = 1000;
|
|
||||||
}
|
|
||||||
for fs_type in matches.values_of_lossy(OPT_TYPE).unwrap_or_default() {
|
|
||||||
opt.fs_selector.include(fs_type.to_owned());
|
|
||||||
}
|
|
||||||
for fs_type in matches
|
|
||||||
.values_of_lossy(OPT_EXCLUDE_TYPE)
|
|
||||||
.unwrap_or_default()
|
|
||||||
{
|
|
||||||
opt.fs_selector.exclude(fs_type.to_owned());
|
|
||||||
}
|
|
||||||
|
|
||||||
let fs_list = filter_mount_list(read_fs_list(), &paths, &opt)
|
let fs_list = filter_mount_list(read_fs_list(), &paths, &opt)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
|
|
@ -144,7 +144,7 @@ impl Stat {
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
let file_info = get_file_info(&path);
|
let file_info = get_file_info(&path);
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
Ok(Stat {
|
Ok(Self {
|
||||||
path,
|
path,
|
||||||
is_dir: metadata.is_dir(),
|
is_dir: metadata.is_dir(),
|
||||||
size: metadata.len(),
|
size: metadata.len(),
|
||||||
|
|
|
@ -88,10 +88,7 @@ fn print_escaped(input: &str, mut output: impl Write) -> io::Result<bool> {
|
||||||
start = 0;
|
start = 0;
|
||||||
next
|
next
|
||||||
}),
|
}),
|
||||||
'0' => parse_code(&mut iter, 8, 3, 3).unwrap_or_else(|| {
|
'0' => parse_code(&mut iter, 8, 3, 3).unwrap_or('\0'),
|
||||||
start = 0;
|
|
||||||
next
|
|
||||||
}),
|
|
||||||
_ => {
|
_ => {
|
||||||
start = 0;
|
start = 0;
|
||||||
next
|
next
|
||||||
|
|
1
src/uu/env/src/env.rs
vendored
1
src/uu/env/src/env.rs
vendored
|
@ -104,6 +104,7 @@ fn load_config_file(opts: &mut Options) -> UResult<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
|
#[allow(clippy::ptr_arg)]
|
||||||
fn build_command<'a, 'b>(args: &'a mut Vec<&'b str>) -> (Cow<'b, str>, &'a [&'b str]) {
|
fn build_command<'a, 'b>(args: &'a mut Vec<&'b str>) -> (Cow<'b, str>, &'a [&'b str]) {
|
||||||
let progname = Cow::from(args[0]);
|
let progname = Cow::from(args[0]);
|
||||||
(progname, &args[1..])
|
(progname, &args[1..])
|
||||||
|
|
|
@ -4,16 +4,62 @@
|
||||||
// *
|
// *
|
||||||
// * 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.
|
||||||
|
use clap::{App, AppSettings, Arg};
|
||||||
|
use std::io::Write;
|
||||||
|
use uucore::error::{set_exit_code, UResult};
|
||||||
|
|
||||||
use clap::App;
|
static ABOUT: &str = "\
|
||||||
use uucore::error::UResult;
|
Returns false, an unsuccessful exit status.
|
||||||
|
|
||||||
|
Immediately returns with the exit status `1`. When invoked with one of the recognized options it
|
||||||
|
will try to write the help or version text. Any IO error during this operation is diagnosed, yet
|
||||||
|
the program will also return `1`.
|
||||||
|
";
|
||||||
|
|
||||||
#[uucore::main]
|
#[uucore::main]
|
||||||
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
uu_app().get_matches_from(args);
|
let mut app = uu_app();
|
||||||
Err(1.into())
|
|
||||||
|
// Mirror GNU options, always return `1`. In particular even the 'successful' cases of no-op,
|
||||||
|
// and the interrupted display of help and version should return `1`. Also, we return Ok in all
|
||||||
|
// paths to avoid the allocation of an error object, an operation that could, in theory, fail
|
||||||
|
// and unwind through the standard library allocation handling machinery.
|
||||||
|
set_exit_code(1);
|
||||||
|
|
||||||
|
if let Ok(matches) = app.try_get_matches_from_mut(args) {
|
||||||
|
let error = if matches.index_of("help").is_some() {
|
||||||
|
app.print_long_help()
|
||||||
|
} else if matches.index_of("version").is_some() {
|
||||||
|
writeln!(std::io::stdout(), "{}", app.render_version())
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
};
|
||||||
|
|
||||||
|
// Try to display this error.
|
||||||
|
if let Err(print_fail) = error {
|
||||||
|
// Completely ignore any error here, no more failover and we will fail in any case.
|
||||||
|
let _ = writeln!(std::io::stderr(), "{}: {}", uucore::util_name(), print_fail);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn uu_app<'a>() -> App<'a> {
|
pub fn uu_app<'a>() -> App<'a> {
|
||||||
App::new(uucore::util_name())
|
App::new(uucore::util_name())
|
||||||
|
.version(clap::crate_version!())
|
||||||
|
.about(ABOUT)
|
||||||
|
// We provide our own help and version options, to ensure maximum compatibility with GNU.
|
||||||
|
.setting(AppSettings::DisableHelpFlag | AppSettings::DisableVersionFlag)
|
||||||
|
.arg(
|
||||||
|
Arg::new("help")
|
||||||
|
.long("help")
|
||||||
|
.help("Print help information")
|
||||||
|
.exclusive(true),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new("version")
|
||||||
|
.long("version")
|
||||||
|
.help("Print version information"),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ pub fn parse_obsolete(src: &str) -> Option<Result<impl Iterator<Item = OsString>
|
||||||
let mut has_num = false;
|
let mut has_num = false;
|
||||||
let mut last_char = 0 as char;
|
let mut last_char = 0 as char;
|
||||||
for (n, c) in &mut chars {
|
for (n, c) in &mut chars {
|
||||||
if c.is_numeric() {
|
if c.is_digit(10) {
|
||||||
has_num = true;
|
has_num = true;
|
||||||
num_end = n;
|
num_end = n;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -12,6 +12,7 @@ use libc::c_long;
|
||||||
use uucore::error::UResult;
|
use uucore::error::UResult;
|
||||||
|
|
||||||
static SYNTAX: &str = "[options]";
|
static SYNTAX: &str = "[options]";
|
||||||
|
const SUMMARY: &str = "Print the numeric identifier (in hexadecimal) for the current host";
|
||||||
|
|
||||||
// currently rust libc interface doesn't include gethostid
|
// currently rust libc interface doesn't include gethostid
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
@ -28,6 +29,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
pub fn uu_app<'a>() -> App<'a> {
|
pub fn uu_app<'a>() -> App<'a> {
|
||||||
App::new(uucore::util_name())
|
App::new(uucore::util_name())
|
||||||
.version(crate_version!())
|
.version(crate_version!())
|
||||||
|
.about(SUMMARY)
|
||||||
.override_usage(SYNTAX)
|
.override_usage(SYNTAX)
|
||||||
.setting(AppSettings::InferLongArgs)
|
.setting(AppSettings::InferLongArgs)
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,7 +74,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
table();
|
table();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Mode::List => list(pids_or_signals.get(0).cloned()),
|
Mode::List => list(pids_or_signals.get(0)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,9 +168,9 @@ fn print_signals() {
|
||||||
println!();
|
println!();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn list(arg: Option<String>) -> UResult<()> {
|
fn list(arg: Option<&String>) -> UResult<()> {
|
||||||
match arg {
|
match arg {
|
||||||
Some(ref x) => print_signal(x),
|
Some(x) => print_signal(x),
|
||||||
None => {
|
None => {
|
||||||
print_signals();
|
print_signals();
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -9,22 +9,15 @@ use uucore::InvalidEncodingHandling;
|
||||||
|
|
||||||
const VERSION: &str = "version";
|
const VERSION: &str = "version";
|
||||||
const HELP: &str = "help";
|
const HELP: &str = "help";
|
||||||
static LONGHELP_LEAD: &str = "printf
|
const USAGE: &str = "printf FORMATSTRING [ARGUMENT]...";
|
||||||
|
const ABOUT: &str = "Print output based off of the format string and proceeding arguments.";
|
||||||
USAGE: printf FORMATSTRING [ARGUMENT]...
|
const AFTER_HELP: &str = "
|
||||||
|
|
||||||
basic anonymous string templating:
|
basic anonymous string templating:
|
||||||
|
|
||||||
prints format string at least once, repeating as long as there are remaining arguments
|
prints format string at least once, repeating as long as there are remaining arguments
|
||||||
output prints escaped literals in the format string as character literals
|
output prints escaped literals in the format string as character literals
|
||||||
output replaces anonymous fields with the next unused argument, formatted according to the field.
|
output replaces anonymous fields with the next unused argument, formatted according to the field.
|
||||||
|
|
||||||
Options:
|
|
||||||
--help display this help and exit
|
|
||||||
--version output version information and exit
|
|
||||||
|
|
||||||
";
|
|
||||||
static LONGHELP_BODY: &str = "
|
|
||||||
Prints the , replacing escaped character sequences with character literals
|
Prints the , replacing escaped character sequences with character literals
|
||||||
and substitution field sequences with passed arguments
|
and substitution field sequences with passed arguments
|
||||||
|
|
||||||
|
@ -271,31 +264,43 @@ COPYRIGHT :
|
||||||
|
|
||||||
";
|
";
|
||||||
|
|
||||||
|
mod options {
|
||||||
|
pub const FORMATSTRING: &str = "FORMATSTRING";
|
||||||
|
pub const ARGUMENT: &str = "ARGUMENT";
|
||||||
|
}
|
||||||
|
|
||||||
#[uucore::main]
|
#[uucore::main]
|
||||||
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
let args = args
|
let args = args
|
||||||
.collect_str(InvalidEncodingHandling::Ignore)
|
.collect_str(InvalidEncodingHandling::Ignore)
|
||||||
.accept_any();
|
.accept_any();
|
||||||
|
let matches = uu_app().get_matches_from(args);
|
||||||
|
|
||||||
if args.len() <= 1 {
|
let format_string = matches
|
||||||
return Err(UUsageError::new(1, "missing operand"));
|
.value_of(options::FORMATSTRING)
|
||||||
}
|
.ok_or_else(|| UUsageError::new(1, "missing operand"))?;
|
||||||
let formatstr = &args[1];
|
let values: Vec<String> = match matches.values_of(options::ARGUMENT) {
|
||||||
|
Some(s) => s.map(|s| s.to_string()).collect(),
|
||||||
|
None => vec![],
|
||||||
|
};
|
||||||
|
|
||||||
if formatstr == "--help" {
|
memo::Memo::run_all(format_string, &values[..]);
|
||||||
print!("{} {}", LONGHELP_LEAD, LONGHELP_BODY);
|
|
||||||
} else if formatstr == "--version" {
|
|
||||||
println!("{} {}", uucore::util_name(), crate_version!());
|
|
||||||
} else {
|
|
||||||
let printf_args = &args[2..];
|
|
||||||
memo::Memo::run_all(formatstr, printf_args);
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn uu_app<'a>() -> App<'a> {
|
pub fn uu_app<'a>() -> App<'a> {
|
||||||
App::new(uucore::util_name())
|
App::new(uucore::util_name())
|
||||||
.arg(Arg::new(VERSION).long(VERSION))
|
.setting(AppSettings::AllowHyphenValues)
|
||||||
.arg(Arg::new(HELP).long(HELP))
|
.version(crate_version!())
|
||||||
.setting(AppSettings::InferLongArgs)
|
.about(ABOUT)
|
||||||
|
.after_help(AFTER_HELP)
|
||||||
|
.override_usage(USAGE)
|
||||||
|
.arg(Arg::new(HELP).long(HELP).help("Print help information"))
|
||||||
|
.arg(
|
||||||
|
Arg::new(VERSION)
|
||||||
|
.long(VERSION)
|
||||||
|
.help("Print version information"),
|
||||||
|
)
|
||||||
|
.arg(Arg::new(options::FORMATSTRING))
|
||||||
|
.arg(Arg::new(options::ARGUMENT).multiple_occurrences(true))
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ Write a random permutation of the input lines to standard output.
|
||||||
|
|
||||||
With no FILE, or when FILE is -, read standard input.
|
With no FILE, or when FILE is -, read standard input.
|
||||||
"#;
|
"#;
|
||||||
|
static ABOUT: &str = "Shuffle the input by outputting a random permutation of input lines. Each output permutation is equally likely.";
|
||||||
static TEMPLATE: &str = "Usage: {usage}\nMandatory arguments to long options are mandatory for short options too.\n{options}";
|
static TEMPLATE: &str = "Usage: {usage}\nMandatory arguments to long options are mandatory for short options too.\n{options}";
|
||||||
|
|
||||||
struct Options {
|
struct Options {
|
||||||
|
@ -121,6 +122,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
pub fn uu_app<'a>() -> App<'a> {
|
pub fn uu_app<'a>() -> App<'a> {
|
||||||
App::new(uucore::util_name())
|
App::new(uucore::util_name())
|
||||||
.name(NAME)
|
.name(NAME)
|
||||||
|
.about(ABOUT)
|
||||||
.version(crate_version!())
|
.version(crate_version!())
|
||||||
.help_template(TEMPLATE)
|
.help_template(TEMPLATE)
|
||||||
.override_usage(USAGE)
|
.override_usage(USAGE)
|
||||||
|
|
|
@ -96,8 +96,8 @@ impl Number {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
fn digits(&self) -> &Vec<u8> {
|
fn digits(&self) -> &Vec<u8> {
|
||||||
match self {
|
match self {
|
||||||
Number::FixedWidth(number) => &number.digits,
|
Self::FixedWidth(number) => &number.digits,
|
||||||
Number::DynamicWidth(number) => &number.digits,
|
Self::DynamicWidth(number) => &number.digits,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,8 +136,8 @@ impl Number {
|
||||||
/// ```
|
/// ```
|
||||||
pub fn increment(&mut self) -> Result<(), Overflow> {
|
pub fn increment(&mut self) -> Result<(), Overflow> {
|
||||||
match self {
|
match self {
|
||||||
Number::FixedWidth(number) => number.increment(),
|
Self::FixedWidth(number) => number.increment(),
|
||||||
Number::DynamicWidth(number) => number.increment(),
|
Self::DynamicWidth(number) => number.increment(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -145,8 +145,8 @@ impl Number {
|
||||||
impl Display for Number {
|
impl Display for Number {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Number::FixedWidth(number) => number.fmt(f),
|
Self::FixedWidth(number) => number.fmt(f),
|
||||||
Number::DynamicWidth(number) => number.fmt(f),
|
Self::DynamicWidth(number) => number.fmt(f),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -183,8 +183,8 @@ pub struct FixedWidthNumber {
|
||||||
|
|
||||||
impl FixedWidthNumber {
|
impl FixedWidthNumber {
|
||||||
/// Instantiate a number of the given radix and width.
|
/// Instantiate a number of the given radix and width.
|
||||||
pub fn new(radix: u8, width: usize) -> FixedWidthNumber {
|
pub fn new(radix: u8, width: usize) -> Self {
|
||||||
FixedWidthNumber {
|
Self {
|
||||||
radix,
|
radix,
|
||||||
digits: vec![0; width],
|
digits: vec![0; width],
|
||||||
}
|
}
|
||||||
|
@ -286,8 +286,8 @@ impl DynamicWidthNumber {
|
||||||
///
|
///
|
||||||
/// This associated function returns a new instance of the struct
|
/// This associated function returns a new instance of the struct
|
||||||
/// with the given radix and a width of two digits, both 0.
|
/// with the given radix and a width of two digits, both 0.
|
||||||
pub fn new(radix: u8) -> DynamicWidthNumber {
|
pub fn new(radix: u8) -> Self {
|
||||||
DynamicWidthNumber {
|
Self {
|
||||||
radix,
|
radix,
|
||||||
digits: vec![0, 0],
|
digits: vec![0, 0],
|
||||||
}
|
}
|
||||||
|
@ -404,7 +404,7 @@ mod tests {
|
||||||
fn num(n: usize) -> Number {
|
fn num(n: usize) -> Number {
|
||||||
let mut number = Number::DynamicWidth(DynamicWidthNumber::new(26));
|
let mut number = Number::DynamicWidth(DynamicWidthNumber::new(26));
|
||||||
for _ in 0..n {
|
for _ in 0..n {
|
||||||
number.increment().unwrap()
|
number.increment().unwrap();
|
||||||
}
|
}
|
||||||
number
|
number
|
||||||
}
|
}
|
||||||
|
@ -428,7 +428,7 @@ mod tests {
|
||||||
fn num(n: usize) -> Number {
|
fn num(n: usize) -> Number {
|
||||||
let mut number = Number::DynamicWidth(DynamicWidthNumber::new(10));
|
let mut number = Number::DynamicWidth(DynamicWidthNumber::new(10));
|
||||||
for _ in 0..n {
|
for _ in 0..n {
|
||||||
number.increment().unwrap()
|
number.increment().unwrap();
|
||||||
}
|
}
|
||||||
number
|
number
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,7 +62,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
.override_usage(&usage[..])
|
.override_usage(&usage[..])
|
||||||
.after_help(&long_usage[..])
|
.after_help(&long_usage[..])
|
||||||
.get_matches_from(args);
|
.get_matches_from(args);
|
||||||
let settings = Settings::from(matches)?;
|
let settings = Settings::from(&matches)?;
|
||||||
split(&settings)
|
split(&settings)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,7 +232,7 @@ struct Settings {
|
||||||
|
|
||||||
impl Settings {
|
impl Settings {
|
||||||
/// Parse a strategy from the command-line arguments.
|
/// Parse a strategy from the command-line arguments.
|
||||||
fn from(matches: ArgMatches) -> UResult<Self> {
|
fn from(matches: &ArgMatches) -> UResult<Self> {
|
||||||
let result = Self {
|
let result = Self {
|
||||||
suffix_length: matches
|
suffix_length: matches
|
||||||
.value_of(OPT_SUFFIX_LENGTH)
|
.value_of(OPT_SUFFIX_LENGTH)
|
||||||
|
@ -242,7 +242,7 @@ impl Settings {
|
||||||
numeric_suffix: matches.occurrences_of(OPT_NUMERIC_SUFFIXES) > 0,
|
numeric_suffix: matches.occurrences_of(OPT_NUMERIC_SUFFIXES) > 0,
|
||||||
additional_suffix: matches.value_of(OPT_ADDITIONAL_SUFFIX).unwrap().to_owned(),
|
additional_suffix: matches.value_of(OPT_ADDITIONAL_SUFFIX).unwrap().to_owned(),
|
||||||
verbose: matches.occurrences_of("verbose") > 0,
|
verbose: matches.occurrences_of("verbose") > 0,
|
||||||
strategy: Strategy::from(&matches)?,
|
strategy: Strategy::from(matches)?,
|
||||||
input: matches.value_of(ARG_INPUT).unwrap().to_owned(),
|
input: matches.value_of(ARG_INPUT).unwrap().to_owned(),
|
||||||
prefix: matches.value_of(ARG_PREFIX).unwrap().to_owned(),
|
prefix: matches.value_of(ARG_PREFIX).unwrap().to_owned(),
|
||||||
filter: matches.value_of(OPT_FILTER).map(|s| s.to_owned()),
|
filter: matches.value_of(OPT_FILTER).map(|s| s.to_owned()),
|
||||||
|
|
|
@ -19,7 +19,7 @@ pub fn parse_obsolete(src: &str) -> Option<Result<impl Iterator<Item = OsString>
|
||||||
let mut has_num = false;
|
let mut has_num = false;
|
||||||
let mut last_char = 0 as char;
|
let mut last_char = 0 as char;
|
||||||
for (n, c) in &mut chars {
|
for (n, c) in &mut chars {
|
||||||
if c.is_numeric() {
|
if c.is_digit(10) {
|
||||||
has_num = true;
|
has_num = true;
|
||||||
num_end = n;
|
num_end = n;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -30,6 +30,7 @@ impl ProcessChecker {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Borrowing mutably to be aligned with Windows implementation
|
// Borrowing mutably to be aligned with Windows implementation
|
||||||
|
#[allow(clippy::wrong_self_convention)]
|
||||||
pub fn is_dead(&mut self) -> bool {
|
pub fn is_dead(&mut self) -> bool {
|
||||||
unsafe { libc::kill(self.pid, 0) != 0 && get_errno() != libc::EPERM }
|
unsafe { libc::kill(self.pid, 0) != 0 && get_errno() != libc::EPERM }
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,11 +24,11 @@ pub struct ProcessChecker {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ProcessChecker {
|
impl ProcessChecker {
|
||||||
pub fn new(process_id: self::Pid) -> ProcessChecker {
|
pub fn new(process_id: self::Pid) -> Self {
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
let FALSE = 0i32;
|
let FALSE = 0i32;
|
||||||
let h = unsafe { OpenProcess(SYNCHRONIZE, FALSE, process_id as DWORD) };
|
let h = unsafe { OpenProcess(SYNCHRONIZE, FALSE, process_id as DWORD) };
|
||||||
ProcessChecker {
|
Self {
|
||||||
dead: h.is_null(),
|
dead: h.is_null(),
|
||||||
handle: h,
|
handle: h,
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
mod parser;
|
mod parser;
|
||||||
|
|
||||||
use clap::{crate_version, App, AppSettings};
|
use clap::{crate_version, App};
|
||||||
use parser::{parse, Operator, Symbol, UnaryOperator};
|
use parser::{parse, Operator, Symbol, UnaryOperator};
|
||||||
use std::ffi::{OsStr, OsString};
|
use std::ffi::{OsStr, OsString};
|
||||||
use uucore::display::Quotable;
|
use uucore::display::Quotable;
|
||||||
|
@ -86,10 +86,14 @@ NOTE: your shell may have its own version of test and/or [, which usually supers
|
||||||
the version described here. Please refer to your shell's documentation
|
the version described here. Please refer to your shell's documentation
|
||||||
for details about the options it supports.";
|
for details about the options it supports.";
|
||||||
|
|
||||||
|
const ABOUT: &str = "Check file types and compare values.";
|
||||||
|
|
||||||
pub fn uu_app<'a>() -> App<'a> {
|
pub fn uu_app<'a>() -> App<'a> {
|
||||||
App::new(uucore::util_name())
|
App::new(uucore::util_name())
|
||||||
.setting(AppSettings::DisableHelpFlag)
|
.version(crate_version!())
|
||||||
.setting(AppSettings::DisableVersionFlag)
|
.about(ABOUT)
|
||||||
|
.override_usage(USAGE)
|
||||||
|
.after_help(AFTER_HELP)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[uucore::main]
|
#[uucore::main]
|
||||||
|
@ -104,6 +108,7 @@ pub fn uumain(mut args: impl uucore::Args) -> UResult<()> {
|
||||||
// Let clap pretty-print help and version
|
// Let clap pretty-print help and version
|
||||||
App::new(binary_name)
|
App::new(binary_name)
|
||||||
.version(crate_version!())
|
.version(crate_version!())
|
||||||
|
.about(ABOUT)
|
||||||
.override_usage(USAGE)
|
.override_usage(USAGE)
|
||||||
.after_help(AFTER_HELP)
|
.after_help(AFTER_HELP)
|
||||||
// Disable printing of -h and -v as valid alternatives for --help and --version,
|
// Disable printing of -h and -v as valid alternatives for --help and --version,
|
||||||
|
|
|
@ -58,7 +58,13 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
|
|
||||||
let matches = uu_app().override_usage(&usage[..]).get_matches_from(args);
|
let matches = uu_app().override_usage(&usage[..]).get_matches_from(args);
|
||||||
|
|
||||||
let files = matches.values_of_os(ARG_FILES).unwrap();
|
let files = matches.values_of_os(ARG_FILES).ok_or_else(|| {
|
||||||
|
USimpleError::new(
|
||||||
|
1,
|
||||||
|
r##"missing file operand
|
||||||
|
Try 'touch --help' for more information."##,
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
let (mut atime, mut mtime) =
|
let (mut atime, mut mtime) =
|
||||||
if let Some(reference) = matches.value_of_os(options::sources::REFERENCE) {
|
if let Some(reference) = matches.value_of_os(options::sources::REFERENCE) {
|
||||||
|
|
|
@ -4,16 +4,59 @@
|
||||||
// *
|
// *
|
||||||
// * 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.
|
||||||
|
use clap::{App, AppSettings, Arg};
|
||||||
|
use std::io::Write;
|
||||||
|
use uucore::error::{set_exit_code, UResult};
|
||||||
|
|
||||||
use clap::{App, AppSettings};
|
static ABOUT: &str = "\
|
||||||
use uucore::error::UResult;
|
Returns true, a successful exit status.
|
||||||
|
|
||||||
|
Immediately returns with the exit status `0`, except when invoked with one of the recognized
|
||||||
|
options. In those cases it will try to write the help or version text. Any IO error during this
|
||||||
|
operation causes the program to return `1` instead.
|
||||||
|
";
|
||||||
|
|
||||||
#[uucore::main]
|
#[uucore::main]
|
||||||
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
uu_app().get_matches_from(args);
|
let mut app = uu_app();
|
||||||
|
|
||||||
|
if let Ok(matches) = app.try_get_matches_from_mut(args) {
|
||||||
|
let error = if matches.index_of("help").is_some() {
|
||||||
|
app.print_long_help()
|
||||||
|
} else if matches.index_of("version").is_some() {
|
||||||
|
writeln!(std::io::stdout(), "{}", app.render_version())
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(print_fail) = error {
|
||||||
|
// Try to display this error.
|
||||||
|
let _ = writeln!(std::io::stderr(), "{}: {}", uucore::util_name(), print_fail);
|
||||||
|
// Mirror GNU options. When failing to print warnings or version flags, then we exit
|
||||||
|
// with FAIL. This avoids allocation some error information which may result in yet
|
||||||
|
// other types of failure.
|
||||||
|
set_exit_code(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn uu_app<'a>() -> App<'a> {
|
pub fn uu_app<'a>() -> App<'a> {
|
||||||
App::new(uucore::util_name()).setting(AppSettings::InferLongArgs)
|
App::new(uucore::util_name())
|
||||||
|
.version(clap::crate_version!())
|
||||||
|
.about(ABOUT)
|
||||||
|
// We provide our own help and version options, to ensure maximum compatibility with GNU.
|
||||||
|
.setting(AppSettings::DisableHelpFlag | AppSettings::DisableVersionFlag)
|
||||||
|
.arg(
|
||||||
|
Arg::new("help")
|
||||||
|
.long("help")
|
||||||
|
.help("Print help information")
|
||||||
|
.exclusive(true),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new("version")
|
||||||
|
.long("version")
|
||||||
|
.help("Print version information"),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -115,7 +115,14 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
let matches = uu_app()
|
let matches = uu_app()
|
||||||
.override_usage(&usage[..])
|
.override_usage(&usage[..])
|
||||||
.after_help(&long_usage[..])
|
.after_help(&long_usage[..])
|
||||||
.get_matches_from(args);
|
.try_get_matches_from(args)
|
||||||
|
.map_err(|e| {
|
||||||
|
e.print().expect("Error writing clap::Error");
|
||||||
|
match e.kind {
|
||||||
|
clap::ErrorKind::DisplayHelp | clap::ErrorKind::DisplayVersion => 0,
|
||||||
|
_ => 1,
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
let files: Vec<String> = matches
|
let files: Vec<String> = matches
|
||||||
.values_of(options::ARG_FILES)
|
.values_of(options::ARG_FILES)
|
||||||
|
|
|
@ -238,7 +238,7 @@ impl MountInfo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
fn new(mut volume_name: String) -> Option<MountInfo> {
|
fn new(mut volume_name: String) -> Option<Self> {
|
||||||
let mut dev_name_buf = [0u16; MAX_PATH];
|
let mut dev_name_buf = [0u16; MAX_PATH];
|
||||||
volume_name.pop();
|
volume_name.pop();
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -289,7 +289,7 @@ impl MountInfo {
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
let mut mn_info = MountInfo {
|
let mut mn_info = Self {
|
||||||
dev_id: volume_name,
|
dev_id: volume_name,
|
||||||
dev_name,
|
dev_name,
|
||||||
fs_type: fs_type.unwrap_or_else(|| "".to_string()),
|
fs_type: fs_type.unwrap_or_else(|| "".to_string()),
|
||||||
|
@ -319,7 +319,7 @@ use std::ffi::CStr;
|
||||||
))]
|
))]
|
||||||
impl From<StatFs> for MountInfo {
|
impl From<StatFs> for MountInfo {
|
||||||
fn from(statfs: StatFs) -> Self {
|
fn from(statfs: StatFs) -> Self {
|
||||||
let mut info = MountInfo {
|
let mut info = Self {
|
||||||
dev_id: "".to_string(),
|
dev_id: "".to_string(),
|
||||||
dev_name: unsafe {
|
dev_name: unsafe {
|
||||||
// spell-checker:disable-next-line
|
// spell-checker:disable-next-line
|
||||||
|
@ -553,7 +553,7 @@ impl FsUsage {
|
||||||
}
|
}
|
||||||
|
|
||||||
let bytes_per_cluster = sectors_per_cluster as u64 * bytes_per_sector as u64;
|
let bytes_per_cluster = sectors_per_cluster as u64 * bytes_per_sector as u64;
|
||||||
FsUsage {
|
Self {
|
||||||
// f_bsize File system block size.
|
// f_bsize File system block size.
|
||||||
blocksize: bytes_per_cluster as u64,
|
blocksize: bytes_per_cluster as u64,
|
||||||
// f_blocks - Total number of blocks on the file system, in units of f_frsize.
|
// f_blocks - Total number of blocks on the file system, in units of f_frsize.
|
||||||
|
|
|
@ -60,6 +60,7 @@ pub struct Sub {
|
||||||
field_char: char,
|
field_char: char,
|
||||||
field_type: FieldType,
|
field_type: FieldType,
|
||||||
orig: String,
|
orig: String,
|
||||||
|
prefix_char: char,
|
||||||
}
|
}
|
||||||
impl Sub {
|
impl Sub {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
|
@ -67,6 +68,7 @@ impl Sub {
|
||||||
second_field: CanAsterisk<Option<u32>>,
|
second_field: CanAsterisk<Option<u32>>,
|
||||||
field_char: char,
|
field_char: char,
|
||||||
orig: String,
|
orig: String,
|
||||||
|
prefix_char: char,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
// for more dry printing, field characters are grouped
|
// for more dry printing, field characters are grouped
|
||||||
// in initialization of token.
|
// in initialization of token.
|
||||||
|
@ -90,6 +92,7 @@ impl Sub {
|
||||||
field_char,
|
field_char,
|
||||||
field_type,
|
field_type,
|
||||||
orig,
|
orig,
|
||||||
|
prefix_char,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -126,6 +129,11 @@ impl SubParser {
|
||||||
fn build_token(parser: Self) -> Box<dyn token::Token> {
|
fn build_token(parser: Self) -> Box<dyn token::Token> {
|
||||||
// not a self method so as to allow move of sub-parser vals.
|
// not a self method so as to allow move of sub-parser vals.
|
||||||
// return new Sub struct as token
|
// return new Sub struct as token
|
||||||
|
let prefix_char = match &parser.min_width_tmp {
|
||||||
|
Some(width) if width.starts_with('0') => '0',
|
||||||
|
_ => ' ',
|
||||||
|
};
|
||||||
|
|
||||||
let t: Box<dyn token::Token> = Box::new(Sub::new(
|
let t: Box<dyn token::Token> = Box::new(Sub::new(
|
||||||
if parser.min_width_is_asterisk {
|
if parser.min_width_is_asterisk {
|
||||||
CanAsterisk::Asterisk
|
CanAsterisk::Asterisk
|
||||||
|
@ -139,6 +147,7 @@ impl SubParser {
|
||||||
},
|
},
|
||||||
parser.field_char.unwrap(),
|
parser.field_char.unwrap(),
|
||||||
parser.text_so_far,
|
parser.text_so_far,
|
||||||
|
prefix_char,
|
||||||
));
|
));
|
||||||
t
|
t
|
||||||
}
|
}
|
||||||
|
@ -394,7 +403,7 @@ impl token::Token for Sub {
|
||||||
final_str.push_str(&pre_min_width);
|
final_str.push_str(&pre_min_width);
|
||||||
}
|
}
|
||||||
for _ in 0..diff {
|
for _ in 0..diff {
|
||||||
final_str.push(' ');
|
final_str.push(self.prefix_char);
|
||||||
}
|
}
|
||||||
if pad_before {
|
if pad_before {
|
||||||
final_str.push_str(&pre_min_width);
|
final_str.push_str(&pre_min_width);
|
||||||
|
|
|
@ -27,10 +27,10 @@ pub trait FromWide {
|
||||||
fn from_wide_null(wide: &[u16]) -> Self;
|
fn from_wide_null(wide: &[u16]) -> Self;
|
||||||
}
|
}
|
||||||
impl FromWide for String {
|
impl FromWide for String {
|
||||||
fn from_wide(wide: &[u16]) -> String {
|
fn from_wide(wide: &[u16]) -> Self {
|
||||||
OsString::from_wide(wide).to_string_lossy().into_owned()
|
OsString::from_wide(wide).to_string_lossy().into_owned()
|
||||||
}
|
}
|
||||||
fn from_wide_null(wide: &[u16]) -> String {
|
fn from_wide_null(wide: &[u16]) -> Self {
|
||||||
let len = wide.iter().take_while(|&&c| c != 0).count();
|
let len = wide.iter().take_while(|&&c| c != 0).count();
|
||||||
OsString::from_wide(&wide[..len])
|
OsString::from_wide(&wide[..len])
|
||||||
.to_string_lossy()
|
.to_string_lossy()
|
||||||
|
|
|
@ -231,6 +231,7 @@ pub mod arguments {
|
||||||
.help("override the usual backup suffix")
|
.help("override the usual backup suffix")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.value_name("SUFFIX")
|
.value_name("SUFFIX")
|
||||||
|
.allow_hyphen_values(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -618,4 +619,13 @@ mod tests {
|
||||||
assert_eq!(result, BackupMode::SimpleBackup);
|
assert_eq!(result, BackupMode::SimpleBackup);
|
||||||
env::remove_var(ENV_VERSION_CONTROL);
|
env::remove_var(ENV_VERSION_CONTROL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_suffix_takes_hyphen_value() {
|
||||||
|
let _dummy = TEST_MUTEX.lock().unwrap();
|
||||||
|
let matches = make_app().get_matches_from(vec!["app", "-b", "--suffix", "-v"]);
|
||||||
|
|
||||||
|
let result = determine_backup_suffix(&matches);
|
||||||
|
assert_eq!(result, "-v");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -385,6 +385,24 @@ fn test_cp_arg_suffix() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_cp_arg_suffix_hyphen_value() {
|
||||||
|
let (at, mut ucmd) = at_and_ucmd!();
|
||||||
|
|
||||||
|
ucmd.arg(TEST_HELLO_WORLD_SOURCE)
|
||||||
|
.arg("-b")
|
||||||
|
.arg("--suffix")
|
||||||
|
.arg("-v")
|
||||||
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
||||||
|
.succeeds();
|
||||||
|
|
||||||
|
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n");
|
||||||
|
assert_eq!(
|
||||||
|
at.read(&*format!("{}-v", TEST_HOW_ARE_YOU_SOURCE)),
|
||||||
|
"How are you?\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_cp_custom_backup_suffix_via_env() {
|
fn test_cp_custom_backup_suffix_via_env() {
|
||||||
let (at, mut ucmd) = at_and_ucmd!();
|
let (at, mut ucmd) = at_and_ucmd!();
|
||||||
|
@ -1489,3 +1507,13 @@ fn test_dir_recursive_copy() {
|
||||||
.fails()
|
.fails()
|
||||||
.stderr_contains("cannot copy a directory");
|
.stderr_contains("cannot copy a directory");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_cp_dir_vs_file() {
|
||||||
|
new_ucmd!()
|
||||||
|
.arg("-R")
|
||||||
|
.arg(TEST_COPY_FROM_FOLDER)
|
||||||
|
.arg(TEST_EXISTING_FILE)
|
||||||
|
.fails()
|
||||||
|
.stderr_only("cp: cannot overwrite non-directory with directory");
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// spell-checker:ignore fname, tname, fpath, specfile, testfile, unspec, ifile, ofile, outfile, fullblock, urand, fileio, atoe, atoibm, availible, behaviour, bmax, bremain, btotal, cflags, creat, ctable, ctty, datastructures, doesnt, etoa, fileout, fname, gnudd, iconvflags, nocache, noctty, noerror, nofollow, nolinks, nonblock, oconvflags, outfile, parseargs, rlen, rmax, rposition, rremain, rsofar, rstat, sigusr, sigval, wlen, wstat abcdefghijklm
|
// spell-checker:ignore fname, tname, fpath, specfile, testfile, unspec, ifile, ofile, outfile, fullblock, urand, fileio, atoe, atoibm, availible, behaviour, bmax, bremain, btotal, cflags, creat, ctable, ctty, datastructures, doesnt, etoa, fileout, fname, gnudd, iconvflags, nocache, noctty, noerror, nofollow, nolinks, nonblock, oconvflags, outfile, parseargs, rlen, rmax, rposition, rremain, rsofar, rstat, sigusr, sigval, wlen, wstat abcdefghijklm abcdefghi
|
||||||
|
|
||||||
use crate::common::util::*;
|
use crate::common::util::*;
|
||||||
|
|
||||||
|
@ -178,6 +178,59 @@ fn test_stdin_stdout_count_w_multiplier() {
|
||||||
.success();
|
.success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_b_multiplier() {
|
||||||
|
// "2b" means 2 * 512, which is 1024.
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&["bs=2b", "count=1"])
|
||||||
|
.pipe_in("a".repeat(1025))
|
||||||
|
.succeeds()
|
||||||
|
.stdout_is("a".repeat(1024));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_x_multiplier() {
|
||||||
|
// "2x3" means 2 * 3, which is 6.
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&["bs=2x3", "count=1"])
|
||||||
|
.pipe_in("abcdefghi")
|
||||||
|
.succeeds()
|
||||||
|
.stdout_is("abcdef");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_zero_multiplier_warning() {
|
||||||
|
for arg in ["count", "seek", "skip"] {
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&[format!("{}=00x1", arg).as_str(), "status=none"])
|
||||||
|
.pipe_in("")
|
||||||
|
.succeeds()
|
||||||
|
.no_stdout()
|
||||||
|
.no_stderr();
|
||||||
|
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&[format!("{}=0x1", arg).as_str(), "status=none"])
|
||||||
|
.pipe_in("")
|
||||||
|
.succeeds()
|
||||||
|
.no_stdout()
|
||||||
|
.stderr_contains("warning: '0x' is a zero multiplier; use '00x' if that is intended");
|
||||||
|
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&[format!("{}=0x0x1", arg).as_str(), "status=none"])
|
||||||
|
.pipe_in("")
|
||||||
|
.succeeds()
|
||||||
|
.no_stdout()
|
||||||
|
.stderr_is("dd: warning: '0x' is a zero multiplier; use '00x' if that is intended\ndd: warning: '0x' is a zero multiplier; use '00x' if that is intended\n");
|
||||||
|
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&[format!("{}=1x0x1", arg).as_str(), "status=none"])
|
||||||
|
.pipe_in("")
|
||||||
|
.succeeds()
|
||||||
|
.no_stdout()
|
||||||
|
.stderr_contains("warning: '0x' is a zero multiplier; use '00x' if that is intended");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_final_stats_noxfer() {
|
fn test_final_stats_noxfer() {
|
||||||
new_ucmd!()
|
new_ucmd!()
|
||||||
|
|
|
@ -179,15 +179,15 @@ fn test_du_hard_link() {
|
||||||
|
|
||||||
#[cfg(target_vendor = "apple")]
|
#[cfg(target_vendor = "apple")]
|
||||||
fn _du_hard_link(s: &str) {
|
fn _du_hard_link(s: &str) {
|
||||||
assert_eq!(s, "12\tsubdir/links\n")
|
assert_eq!(s, "12\tsubdir/links\n");
|
||||||
}
|
}
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
fn _du_hard_link(s: &str) {
|
fn _du_hard_link(s: &str) {
|
||||||
assert_eq!(s, "8\tsubdir/links\n")
|
assert_eq!(s, "8\tsubdir/links\n");
|
||||||
}
|
}
|
||||||
#[cfg(target_os = "freebsd")]
|
#[cfg(target_os = "freebsd")]
|
||||||
fn _du_hard_link(s: &str) {
|
fn _du_hard_link(s: &str) {
|
||||||
assert_eq!(s, "16\tsubdir/links\n")
|
assert_eq!(s, "16\tsubdir/links\n");
|
||||||
}
|
}
|
||||||
#[cfg(all(
|
#[cfg(all(
|
||||||
not(target_vendor = "apple"),
|
not(target_vendor = "apple"),
|
||||||
|
|
|
@ -138,11 +138,19 @@ fn test_escape_short_octal() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_escape_no_octal() {
|
fn test_escape_nul() {
|
||||||
new_ucmd!()
|
new_ucmd!()
|
||||||
.args(&["-e", "foo\\0 bar"])
|
.args(&["-e", "foo\\0 bar"])
|
||||||
.succeeds()
|
.succeeds()
|
||||||
.stdout_only("foo\\0 bar\n");
|
.stdout_only("foo\0 bar\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_escape_octal_invalid_digit() {
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&["-e", "foo\\08 bar"])
|
||||||
|
.succeeds()
|
||||||
|
.stdout_only("foo\u{0}8 bar\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -219,7 +219,7 @@ fn test_change_directory() {
|
||||||
.args(&pwd)
|
.args(&pwd)
|
||||||
.succeeds()
|
.succeeds()
|
||||||
.stdout_move_str();
|
.stdout_move_str();
|
||||||
assert_eq!(out.trim(), temporary_path.as_os_str())
|
assert_eq!(out.trim(), temporary_path.as_os_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -1,6 +1,53 @@
|
||||||
use crate::common::util::*;
|
use crate::common::util::*;
|
||||||
|
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "netbsd"))]
|
||||||
|
use std::fs::OpenOptions;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_exit_code() {
|
fn test_exit_code() {
|
||||||
new_ucmd!().fails();
|
new_ucmd!().fails();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_version() {
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&["--version"])
|
||||||
|
.fails()
|
||||||
|
.stdout_contains("false");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_help() {
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&["--help"])
|
||||||
|
.fails()
|
||||||
|
.stdout_contains("false");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_short_options() {
|
||||||
|
for option in ["-h", "-V"] {
|
||||||
|
new_ucmd!().arg(option).fails().stdout_is("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_conflict() {
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&["--help", "--version"])
|
||||||
|
.fails()
|
||||||
|
.stdout_is("");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "netbsd"))]
|
||||||
|
fn test_full() {
|
||||||
|
for option in ["--version", "--help"] {
|
||||||
|
let dev_full = OpenOptions::new().write(true).open("/dev/full").unwrap();
|
||||||
|
|
||||||
|
new_ucmd!()
|
||||||
|
.arg(option)
|
||||||
|
.set_stdout(dev_full)
|
||||||
|
.fails()
|
||||||
|
.stderr_contains("No space left on device");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -306,6 +306,10 @@ fn test_head_invalid_num() {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&["-c", "-³"])
|
||||||
|
.fails()
|
||||||
|
.stderr_is("head: invalid number of bytes: '³'");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -815,6 +815,31 @@ fn test_install_backup_short_custom_suffix() {
|
||||||
assert!(at.file_exists(&format!("{}{}", file_b, suffix)));
|
assert!(at.file_exists(&format!("{}{}", file_b, suffix)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_install_backup_short_custom_suffix_hyphen_value() {
|
||||||
|
let scene = TestScenario::new(util_name!());
|
||||||
|
let at = &scene.fixtures;
|
||||||
|
|
||||||
|
let file_a = "test_install_backup_custom_suffix_file_a";
|
||||||
|
let file_b = "test_install_backup_custom_suffix_file_b";
|
||||||
|
let suffix = "-v";
|
||||||
|
|
||||||
|
at.touch(file_a);
|
||||||
|
at.touch(file_b);
|
||||||
|
scene
|
||||||
|
.ucmd()
|
||||||
|
.arg("-b")
|
||||||
|
.arg(format!("--suffix={}", suffix))
|
||||||
|
.arg(file_a)
|
||||||
|
.arg(file_b)
|
||||||
|
.succeeds()
|
||||||
|
.no_stderr();
|
||||||
|
|
||||||
|
assert!(at.file_exists(file_a));
|
||||||
|
assert!(at.file_exists(file_b));
|
||||||
|
assert!(at.file_exists(&format!("{}{}", file_b, suffix)));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_install_backup_custom_suffix_via_env() {
|
fn test_install_backup_custom_suffix_via_env() {
|
||||||
let scene = TestScenario::new(util_name!());
|
let scene = TestScenario::new(util_name!());
|
||||||
|
|
|
@ -180,6 +180,33 @@ fn test_symlink_custom_backup_suffix() {
|
||||||
assert_eq!(at.resolve_link(backup), file);
|
assert_eq!(at.resolve_link(backup), file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_symlink_custom_backup_suffix_hyphen_value() {
|
||||||
|
let (at, mut ucmd) = at_and_ucmd!();
|
||||||
|
let file = "test_symlink_custom_backup_suffix";
|
||||||
|
let link = "test_symlink_custom_backup_suffix_link";
|
||||||
|
let suffix = "-v";
|
||||||
|
|
||||||
|
at.touch(file);
|
||||||
|
at.symlink_file(file, link);
|
||||||
|
assert!(at.file_exists(file));
|
||||||
|
assert!(at.is_symlink(link));
|
||||||
|
assert_eq!(at.resolve_link(link), file);
|
||||||
|
|
||||||
|
let arg = &format!("--suffix={}", suffix);
|
||||||
|
ucmd.args(&["-b", arg, "-s", file, link])
|
||||||
|
.succeeds()
|
||||||
|
.no_stderr();
|
||||||
|
assert!(at.file_exists(file));
|
||||||
|
|
||||||
|
assert!(at.is_symlink(link));
|
||||||
|
assert_eq!(at.resolve_link(link), file);
|
||||||
|
|
||||||
|
let backup = &format!("{}{}", link, suffix);
|
||||||
|
assert!(at.is_symlink(backup));
|
||||||
|
assert_eq!(at.resolve_link(backup), file);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_symlink_backup_numbering() {
|
fn test_symlink_backup_numbering() {
|
||||||
let (at, mut ucmd) = at_and_ucmd!();
|
let (at, mut ucmd) = at_and_ucmd!();
|
||||||
|
|
|
@ -1327,18 +1327,15 @@ fn test_ls_order_time() {
|
||||||
// So the order should be 2 3 4 1
|
// So the order should be 2 3 4 1
|
||||||
for arg in &["-u", "--time=atime", "--time=access", "--time=use"] {
|
for arg in &["-u", "--time=atime", "--time=access", "--time=use"] {
|
||||||
let result = scene.ucmd().arg("-t").arg(arg).succeeds();
|
let result = scene.ucmd().arg("-t").arg(arg).succeeds();
|
||||||
let file3_access = at.open("test-3").metadata().unwrap().accessed().unwrap();
|
at.open("test-3").metadata().unwrap().accessed().unwrap();
|
||||||
let file4_access = at.open("test-4").metadata().unwrap().accessed().unwrap();
|
at.open("test-4").metadata().unwrap().accessed().unwrap();
|
||||||
|
|
||||||
// It seems to be dependent on the platform whether the access time is actually set
|
// It seems to be dependent on the platform whether the access time is actually set
|
||||||
if file3_access > file4_access {
|
#[cfg(unix)]
|
||||||
result.stdout_only("test-3\ntest-4\ntest-2\ntest-1\n");
|
result.stdout_only("test-3\ntest-4\ntest-2\ntest-1\n");
|
||||||
} else {
|
#[cfg(windows)]
|
||||||
// Access time does not seem to be set on Windows and some other
|
|
||||||
// systems so the order is 4 3 2 1
|
|
||||||
result.stdout_only("test-4\ntest-3\ntest-2\ntest-1\n");
|
result.stdout_only("test-4\ntest-3\ntest-2\ntest-1\n");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// test-2 had the last ctime change when the permissions were set
|
// test-2 had the last ctime change when the permissions were set
|
||||||
// So the order should be 2 4 3 1
|
// So the order should be 2 4 3 1
|
||||||
|
|
|
@ -340,6 +340,27 @@ fn test_mv_custom_backup_suffix() {
|
||||||
assert!(at.file_exists(&format!("{}{}", file_b, suffix)));
|
assert!(at.file_exists(&format!("{}{}", file_b, suffix)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_mv_custom_backup_suffix_hyphen_value() {
|
||||||
|
let (at, mut ucmd) = at_and_ucmd!();
|
||||||
|
let file_a = "test_mv_custom_backup_suffix_file_a";
|
||||||
|
let file_b = "test_mv_custom_backup_suffix_file_b";
|
||||||
|
let suffix = "-v";
|
||||||
|
|
||||||
|
at.touch(file_a);
|
||||||
|
at.touch(file_b);
|
||||||
|
ucmd.arg("-b")
|
||||||
|
.arg(format!("--suffix={}", suffix))
|
||||||
|
.arg(file_a)
|
||||||
|
.arg(file_b)
|
||||||
|
.succeeds()
|
||||||
|
.no_stderr();
|
||||||
|
|
||||||
|
assert!(!at.file_exists(file_a));
|
||||||
|
assert!(at.file_exists(file_b));
|
||||||
|
assert!(at.file_exists(&format!("{}{}", file_b, suffix)));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_mv_custom_backup_suffix_via_env() {
|
fn test_mv_custom_backup_suffix_via_env() {
|
||||||
let (at, mut ucmd) = at_and_ucmd!();
|
let (at, mut ucmd) = at_and_ucmd!();
|
||||||
|
|
|
@ -429,3 +429,19 @@ fn sub_any_specifiers_after_second_param() {
|
||||||
.succeeds()
|
.succeeds()
|
||||||
.stdout_only("3");
|
.stdout_only("3");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn stop_after_additional_escape() {
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&["A%sC\\cD%sF", "B", "E"]) //spell-checker:disable-line
|
||||||
|
.succeeds()
|
||||||
|
.stdout_only("ABC");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sub_float_leading_zeroes() {
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&["%010f", "1"])
|
||||||
|
.succeeds()
|
||||||
|
.stdout_only("001.000000");
|
||||||
|
}
|
||||||
|
|
|
@ -1066,10 +1066,13 @@ fn test_separator_null() {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_output_is_input() {
|
fn test_output_is_input() {
|
||||||
let input = "a\nb\nc\n";
|
let input = "a\nb\nc\n";
|
||||||
let (at, mut cmd) = at_and_ucmd!();
|
let scene = TestScenario::new(util_name!());
|
||||||
|
let at = &scene.fixtures;
|
||||||
at.touch("file");
|
at.touch("file");
|
||||||
at.append("file", input);
|
at.append("file", input);
|
||||||
cmd.args(&["-m", "-u", "-o", "file", "file", "file", "file"])
|
scene
|
||||||
|
.ucmd_keepenv()
|
||||||
|
.args(&["-m", "-u", "-o", "file", "file", "file", "file"])
|
||||||
.succeeds();
|
.succeeds();
|
||||||
assert_eq!(at.read("file"), input);
|
assert_eq!(at.read("file"), input);
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,12 @@ fn test_group_num() {
|
||||||
assert_eq!("", group_num(""));
|
assert_eq!("", group_num(""));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic]
|
||||||
|
fn test_group_num_panic_if_invalid_numeric_characters() {
|
||||||
|
group_num("³³³³³");
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test_generate_tokens {
|
mod test_generate_tokens {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -491,6 +491,10 @@ fn test_tail_invalid_num() {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&["-c", "-³"])
|
||||||
|
.fails()
|
||||||
|
.stderr_is("tail: invalid number of bytes: '³'");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -530,3 +530,12 @@ fn test_touch_permission_denied_error_msg() {
|
||||||
&full_path
|
&full_path
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_touch_no_args() {
|
||||||
|
let mut ucmd = new_ucmd!();
|
||||||
|
ucmd.fails().stderr_only(
|
||||||
|
r##"touch: missing file operand
|
||||||
|
Try 'touch --help' for more information."##,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,53 @@
|
||||||
use crate::common::util::*;
|
use crate::common::util::*;
|
||||||
|
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "netbsd"))]
|
||||||
|
use std::fs::OpenOptions;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_exit_code() {
|
fn test_exit_code() {
|
||||||
new_ucmd!().succeeds();
|
new_ucmd!().succeeds();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_version() {
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&["--version"])
|
||||||
|
.succeeds()
|
||||||
|
.stdout_contains("true");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_help() {
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&["--help"])
|
||||||
|
.succeeds()
|
||||||
|
.stdout_contains("true");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_short_options() {
|
||||||
|
for option in ["-h", "-V"] {
|
||||||
|
new_ucmd!().arg(option).succeeds().stdout_is("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_conflict() {
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&["--help", "--version"])
|
||||||
|
.succeeds()
|
||||||
|
.stdout_is("");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "netbsd"))]
|
||||||
|
fn test_full() {
|
||||||
|
for option in ["--version", "--help"] {
|
||||||
|
let dev_full = OpenOptions::new().write(true).open("/dev/full").unwrap();
|
||||||
|
|
||||||
|
new_ucmd!()
|
||||||
|
.arg(option)
|
||||||
|
.set_stdout(dev_full)
|
||||||
|
.fails()
|
||||||
|
.stderr_contains("No space left on device");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -250,13 +250,26 @@ fn test_size_and_reference() {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_error_filename_only() {
|
fn test_error_filename_only() {
|
||||||
// truncate: you must specify either '--size' or '--reference'
|
// truncate: you must specify either '--size' or '--reference'
|
||||||
new_ucmd!().args(&["file"]).fails().stderr_contains(
|
new_ucmd!()
|
||||||
|
.args(&["file"])
|
||||||
|
.fails()
|
||||||
|
.code_is(1)
|
||||||
|
.stderr_contains(
|
||||||
"error: The following required arguments were not provided:
|
"error: The following required arguments were not provided:
|
||||||
--reference <RFILE>
|
--reference <RFILE>
|
||||||
--size <SIZE>",
|
--size <SIZE>",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_invalid_option() {
|
||||||
|
// truncate: cli parsing error returns 1
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&["--this-arg-does-not-exist"])
|
||||||
|
.fails()
|
||||||
|
.code_is(1);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invalid_numbers() {
|
fn test_invalid_numbers() {
|
||||||
new_ucmd!()
|
new_ucmd!()
|
||||||
|
|
|
@ -15,21 +15,21 @@ ME_parent_dir_abs="$(realpath -mP -- "${ME_parent_dir}")"
|
||||||
|
|
||||||
# * `gh` available?
|
# * `gh` available?
|
||||||
unset GH
|
unset GH
|
||||||
gh --version 1>/dev/null 2>&1
|
if gh --version 1>/dev/null 2>&1; then
|
||||||
if [ $? -eq 0 ]; then export GH="gh"; fi
|
export GH="gh"
|
||||||
|
else
|
||||||
|
echo "ERR!: missing \`gh\` (see install instructions at <https://github.com/cli/cli>)" 1>&2
|
||||||
|
fi
|
||||||
|
|
||||||
# * `jq` available?
|
# * `jq` available?
|
||||||
unset JQ
|
unset JQ
|
||||||
jq --version 1>/dev/null 2>&1
|
if jq --version 1>/dev/null 2>&1; then
|
||||||
if [ $? -eq 0 ]; then export JQ="jq"; fi
|
export JQ="jq"
|
||||||
|
else
|
||||||
|
echo "ERR!: missing \`jq\` (install with \`sudo apt install jq\`)" 1>&2
|
||||||
|
fi
|
||||||
|
|
||||||
if [ -z "${GH}" ] || [ -z "${JQ}" ]; then
|
if [ -z "${GH}" ] || [ -z "${JQ}" ]; then
|
||||||
if [ -z "${GH}" ]; then
|
|
||||||
echo 'ERR!: missing `gh` (see install instructions at <https://github.com/cli/cli>)' 1>&2
|
|
||||||
fi
|
|
||||||
if [ -z "${JQ}" ]; then
|
|
||||||
echo 'ERR!: missing `jq` (install with `sudo apt install jq`)' 1>&2
|
|
||||||
fi
|
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
|
@ -8,12 +8,13 @@
|
||||||
|
|
||||||
FEATURES_OPTION="--features feat_os_unix"
|
FEATURES_OPTION="--features feat_os_unix"
|
||||||
|
|
||||||
ME_dir="$(dirname -- $(readlink -fm -- "$0"))"
|
ME_dir="$(dirname -- "$(readlink -fm -- "$0")")"
|
||||||
REPO_main_dir="$(dirname -- "${ME_dir}")"
|
REPO_main_dir="$(dirname -- "${ME_dir}")"
|
||||||
|
|
||||||
cd "${REPO_main_dir}"
|
cd "${REPO_main_dir}" &&
|
||||||
echo "[ \"$PWD\" ]"
|
echo "[ \"$PWD\" ]"
|
||||||
|
|
||||||
|
#shellcheck disable=SC2086
|
||||||
UTIL_LIST=$("${ME_dir}"/show-utils.sh ${FEATURES_OPTION})
|
UTIL_LIST=$("${ME_dir}"/show-utils.sh ${FEATURES_OPTION})
|
||||||
CARGO_INDIVIDUAL_PACKAGE_OPTIONS=""
|
CARGO_INDIVIDUAL_PACKAGE_OPTIONS=""
|
||||||
for UTIL in ${UTIL_LIST}; do
|
for UTIL in ${UTIL_LIST}; do
|
||||||
|
@ -30,10 +31,12 @@ export RUSTC_WRAPPER="" ## NOTE: RUSTC_WRAPPER=='sccache' breaks code covera
|
||||||
export RUSTFLAGS="-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort"
|
export RUSTFLAGS="-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort"
|
||||||
export RUSTDOCFLAGS="-Cpanic=abort"
|
export RUSTDOCFLAGS="-Cpanic=abort"
|
||||||
export RUSTUP_TOOLCHAIN="nightly-gnu"
|
export RUSTUP_TOOLCHAIN="nightly-gnu"
|
||||||
cargo build ${FEATURES_OPTION}
|
#shellcheck disable=SC2086
|
||||||
|
{ cargo build ${FEATURES_OPTION}
|
||||||
cargo test --no-run ${FEATURES_OPTION}
|
cargo test --no-run ${FEATURES_OPTION}
|
||||||
cargo test --quiet ${FEATURES_OPTION}
|
cargo test --quiet ${FEATURES_OPTION}
|
||||||
cargo test --quiet ${FEATURES_OPTION} ${CARGO_INDIVIDUAL_PACKAGE_OPTIONS}
|
cargo test --quiet ${FEATURES_OPTION} ${CARGO_INDIVIDUAL_PACKAGE_OPTIONS}
|
||||||
|
}
|
||||||
|
|
||||||
export COVERAGE_REPORT_DIR
|
export COVERAGE_REPORT_DIR
|
||||||
if [ -z "${COVERAGE_REPORT_DIR}" ]; then COVERAGE_REPORT_DIR="${REPO_main_dir}/target/debug/coverage-nix"; fi
|
if [ -z "${COVERAGE_REPORT_DIR}" ]; then COVERAGE_REPORT_DIR="${REPO_main_dir}/target/debug/coverage-nix"; fi
|
||||||
|
@ -47,8 +50,7 @@ mkdir -p "${COVERAGE_REPORT_DIR}"
|
||||||
grcov . --output-type lcov --output-path "${COVERAGE_REPORT_DIR}/../lcov.info" --branch --ignore build.rs --ignore '/*' --ignore '[A-Za-z]:/*' --ignore 'C:/Users/*' --excl-br-line '^\s*((debug_)?assert(_eq|_ne)?!|#\[derive\()'
|
grcov . --output-type lcov --output-path "${COVERAGE_REPORT_DIR}/../lcov.info" --branch --ignore build.rs --ignore '/*' --ignore '[A-Za-z]:/*' --ignore 'C:/Users/*' --excl-br-line '^\s*((debug_)?assert(_eq|_ne)?!|#\[derive\()'
|
||||||
# * build HTML
|
# * build HTML
|
||||||
# -- use `genhtml` if available for display of additional branch coverage information
|
# -- use `genhtml` if available for display of additional branch coverage information
|
||||||
genhtml --version 2>/dev/null 1>&2
|
if genhtml --version 2>/dev/null 1>&2; then
|
||||||
if [ $? -eq 0 ]; then
|
|
||||||
genhtml "${COVERAGE_REPORT_DIR}/../lcov.info" --output-directory "${COVERAGE_REPORT_DIR}" --branch-coverage --function-coverage | grep ": [0-9]"
|
genhtml "${COVERAGE_REPORT_DIR}/../lcov.info" --output-directory "${COVERAGE_REPORT_DIR}" --branch-coverage --function-coverage | grep ": [0-9]"
|
||||||
else
|
else
|
||||||
grcov . --output-type html --output-path "${COVERAGE_REPORT_DIR}" --branch --ignore build.rs --ignore '/*' --ignore '[A-Za-z]:/*' --ignore 'C:/Users/*' --excl-br-line '^\s*((debug_)?assert(_eq|_ne)?!|#\[derive\()'
|
grcov . --output-type html --output-path "${COVERAGE_REPORT_DIR}" --branch --ignore build.rs --ignore '/*' --ignore '[A-Za-z]:/*' --ignore 'C:/Users/*' --excl-br-line '^\s*((debug_)?assert(_eq|_ne)?!|#\[derive\()'
|
||||||
|
|
|
@ -15,7 +15,7 @@ if test ! -d ../gnulib; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
pushd $(pwd)
|
pushd "$PWD"
|
||||||
make PROFILE=release
|
make PROFILE=release
|
||||||
BUILDDIR="$PWD/target/release/"
|
BUILDDIR="$PWD/target/release/"
|
||||||
cp "${BUILDDIR}/install" "${BUILDDIR}/ginstall" # The GNU tests rename this script before running, to avoid confusion with the make target
|
cp "${BUILDDIR}/install" "${BUILDDIR}/ginstall" # The GNU tests rename this script before running, to avoid confusion with the make target
|
||||||
|
@ -49,7 +49,7 @@ make -j "$(nproc)"
|
||||||
# Used to be 36. Reduced to 20 to decrease the log size
|
# Used to be 36. Reduced to 20 to decrease the log size
|
||||||
for i in {00..20}
|
for i in {00..20}
|
||||||
do
|
do
|
||||||
make tests/factor/t${i}.sh
|
make "tests/factor/t${i}.sh"
|
||||||
done
|
done
|
||||||
|
|
||||||
# strip the long stuff
|
# strip the long stuff
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#!/bin/bash
|
#!/bin/sh
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
ARG=""
|
ARG=""
|
||||||
|
@ -6,26 +6,21 @@ if test "$1" != "--do-it"; then
|
||||||
ARG="--dry-run --allow-dirty"
|
ARG="--dry-run --allow-dirty"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cd src/uucore/
|
for dir in src/uucore/ src/uucore_procs/ src/uu/stdbuf/src/libstdbuf/ ; do
|
||||||
|
( cd "$dir"
|
||||||
|
#shellcheck disable=SC2086
|
||||||
cargo publish $ARG
|
cargo publish $ARG
|
||||||
cd -
|
)
|
||||||
sleep 2s
|
|
||||||
|
|
||||||
cd src/uucore_procs/
|
|
||||||
cargo publish $ARG
|
|
||||||
cd -
|
|
||||||
sleep 2s
|
|
||||||
|
|
||||||
cd src/uu/stdbuf/src/libstdbuf/
|
|
||||||
cargo publish $ARG
|
|
||||||
cd -
|
|
||||||
sleep 2s
|
sleep 2s
|
||||||
|
done
|
||||||
|
|
||||||
PROGS=$(ls -1d src/uu/*/)
|
PROGS=$(ls -1d src/uu/*/)
|
||||||
for p in $PROGS; do
|
for p in $PROGS; do
|
||||||
cd $p
|
( cd "$p"
|
||||||
|
#shellcheck disable=SC2086
|
||||||
cargo publish $ARG
|
cargo publish $ARG
|
||||||
cd -
|
)
|
||||||
done
|
done
|
||||||
|
|
||||||
|
#shellcheck disable=SC2086
|
||||||
cargo publish $ARG
|
cargo publish $ARG
|
||||||
|
|
|
@ -1,10 +1,28 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
# `$0 [TEST]`
|
||||||
|
# run GNU test (or all tests if TEST is missing/null)
|
||||||
# spell-checker:ignore (env/vars) BUILDDIR GNULIB SUBDIRS
|
# spell-checker:ignore (env/vars) BUILDDIR GNULIB SUBDIRS
|
||||||
cd "$(dirname "${BASH_SOURCE[0]}")/../.."
|
|
||||||
|
ME_dir="$(dirname -- "$(readlink -fm -- "$0")")"
|
||||||
|
REPO_main_dir="$(dirname -- "${ME_dir}")"
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
BUILDDIR="${PWD}/uutils/target/release"
|
|
||||||
GNULIB_DIR="${PWD}/gnulib"
|
### * config (from environment with fallback defaults)
|
||||||
pushd gnu
|
|
||||||
|
path_UUTILS=${path_UUTILS:-${REPO_main_dir}}
|
||||||
|
path_GNU=${path_GNU:-${path_UUTILS}/../gnu}
|
||||||
|
path_GNULIB=${path_GNULIB:-${path_UUTILS}/../gnulib}
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
BUILD_DIR="$(realpath -- "${path_UUTILS}/target/release")"
|
||||||
|
GNULIB_DIR="$(realpath -- "${path_GNULIB}")"
|
||||||
|
|
||||||
|
export BUILD_DIR
|
||||||
|
export GNULIB_DIR
|
||||||
|
|
||||||
|
pushd "$(realpath -- "${path_GNU}")"
|
||||||
|
|
||||||
export RUST_BACKTRACE=1
|
export RUST_BACKTRACE=1
|
||||||
|
|
||||||
|
@ -13,4 +31,5 @@ if test -n "$1"; then
|
||||||
export RUN_TEST="TESTS=$1"
|
export RUN_TEST="TESTS=$1"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
#shellcheck disable=SC2086
|
||||||
timeout -sKILL 2h make -j "$(nproc)" check $RUN_TEST SUBDIRS=. RUN_EXPENSIVE_TESTS=yes RUN_VERY_EXPENSIVE_TESTS=yes VERBOSE=no || : # Kill after 4 hours in case something gets stuck in make
|
timeout -sKILL 2h make -j "$(nproc)" check $RUN_TEST SUBDIRS=. RUN_EXPENSIVE_TESTS=yes RUN_VERY_EXPENSIVE_TESTS=yes VERBOSE=no || : # Kill after 4 hours in case something gets stuck in make
|
||||||
|
|
|
@ -2,15 +2,14 @@
|
||||||
|
|
||||||
# spell-checker:ignore (vars) OSID
|
# spell-checker:ignore (vars) OSID
|
||||||
|
|
||||||
ME_dir="$(dirname -- $(readlink -fm -- "$0"))"
|
ME_dir="$(dirname -- "$(readlink -fm -- "$0")")"
|
||||||
REPO_main_dir="$(dirname -- "${ME_dir}")"
|
REPO_main_dir="$(dirname -- "${ME_dir}")"
|
||||||
|
|
||||||
export COVERAGE_REPORT_DIR="${REPO_main_dir}/target/debug/coverage-nix"
|
export COVERAGE_REPORT_DIR="${REPO_main_dir}/target/debug/coverage-nix"
|
||||||
|
|
||||||
"${ME_dir}/build-code_coverage.sh"
|
if ! "${ME_dir}/build-code_coverage.sh"; then exit 1 ; fi
|
||||||
if [ $? -ne 0 ]; then exit 1 ; fi
|
|
||||||
|
|
||||||
case ";$OSID_tags;" in
|
case ";$OSID_tags;" in
|
||||||
*";wsl;"* ) powershell.exe -c $(wslpath -w "${COVERAGE_REPORT_DIR}"/index.html) ;;
|
*";wsl;"* ) powershell.exe -c "$(wslpath -w "${COVERAGE_REPORT_DIR}"/index.html)" ;;
|
||||||
* ) xdg-open --version >/dev/null 2>&1 && xdg-open "${COVERAGE_REPORT_DIR}"/index.html || echo "report available at '\"${COVERAGE_REPORT_DIR}\"/index.html'" ;;
|
* ) xdg-open --version >/dev/null 2>&1 && xdg-open "${COVERAGE_REPORT_DIR}"/index.html || echo "report available at '\"${COVERAGE_REPORT_DIR}\"/index.html'" ;;
|
||||||
esac ;
|
esac ;
|
||||||
|
|
|
@ -15,17 +15,13 @@ default_utils="base32 base64 basename cat cksum comm cp cut date dircolors dirna
|
||||||
|
|
||||||
project_main_dir="${ME_parent_dir_abs}"
|
project_main_dir="${ME_parent_dir_abs}"
|
||||||
# printf 'project_main_dir="%s"\n' "${project_main_dir}"
|
# printf 'project_main_dir="%s"\n' "${project_main_dir}"
|
||||||
cd "${project_main_dir}"
|
cd "${project_main_dir}" &&
|
||||||
|
|
||||||
# `jq` available?
|
# `jq` available?
|
||||||
unset JQ
|
if ! jq --version 1>/dev/null 2>&1; then
|
||||||
jq --version 1>/dev/null 2>&1
|
echo "WARN: missing \`jq\` (install with \`sudo apt install jq\`); falling back to default (only fully cross-platform) utility list" 1>&2
|
||||||
if [ $? -eq 0 ]; then export JQ="jq"; fi
|
echo "$default_utils"
|
||||||
|
|
||||||
if [ -z "${JQ}" ]; then
|
|
||||||
echo 'WARN: missing `jq` (install with `sudo apt install jq`); falling back to default (only fully cross-platform) utility list' 1>&2
|
|
||||||
echo $default_utils
|
|
||||||
else
|
else
|
||||||
cargo metadata $* --format-version 1 | jq -r "[.resolve.nodes[] | { id: .id, deps: [.deps[] | { name:.name, pkg:.pkg }] }] | .[] | select(.id|startswith(\"coreutils\")) | [.deps[] | select((.name|startswith(\"uu_\")) or (.pkg|startswith(\"uu_\")))] | [.[].pkg | match(\"^\\\w+\";\"g\")] | [.[].string | sub(\"^uu_\"; \"\")] | sort | join(\" \")"
|
cargo metadata "$*" --format-version 1 | jq -r "[.resolve.nodes[] | { id: .id, deps: [.deps[] | { name:.name, pkg:.pkg }] }] | .[] | select(.id|startswith(\"coreutils\")) | [.deps[] | select((.name|startswith(\"uu_\")) or (.pkg|startswith(\"uu_\")))] | [.[].pkg | match(\"^\\\w+\";\"g\")] | [.[].string | sub(\"^uu_\"; \"\")] | sort | join(\" \")"
|
||||||
# cargo metadata $* --format-version 1 | jq -r "[.resolve.nodes[] | { id: .id, deps: [.deps[] | { name:.name, pkg:.pkg }] }] | .[] | select(.id|startswith(\"coreutils\")) | [.deps[] | select((.name|startswith(\"uu_\")) or (.pkg|startswith(\"uu_\")))] | [.[].pkg | match(\"^\\\w+\";\"g\")] | [.[].string] | sort | join(\" \")"
|
# cargo metadata "$*" --format-version 1 | jq -r "[.resolve.nodes[] | { id: .id, deps: [.deps[] | { name:.name, pkg:.pkg }] }] | .[] | select(.id|startswith(\"coreutils\")) | [.deps[] | select((.name|startswith(\"uu_\")) or (.pkg|startswith(\"uu_\")))] | [.[].pkg | match(\"^\\\w+\";\"g\")] | [.[].string] | sort | join(\" \")"
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
#!/bin/bash
|
#!/bin/sh
|
||||||
# This is a stupid helper. I will mass replace all versions (including other crates)
|
# This is a stupid helper. I will mass replace all versions (including other crates)
|
||||||
# So, it should be triple-checked
|
# So, it should be triple-checked
|
||||||
|
|
||||||
# How to ship a new release:
|
# How to ship a new release:
|
||||||
# 1) update this script
|
# 1) update this script
|
||||||
# 2) run it: bash util/update-version.sh
|
# 2) run it: sh util/update-version.sh
|
||||||
# 3) Do a spot check with "git diff"
|
# 3) Do a spot check with "git diff"
|
||||||
# 4) cargo test --release --features unix
|
# 4) cargo test --release --features unix
|
||||||
# 5) Run util/publish.sh in dry mode (it will fail as packages needs more recent version of uucore)
|
# 5) Run util/publish.sh in dry mode (it will fail as packages needs more recent version of uucore)
|
||||||
|
@ -23,6 +23,7 @@ UUCORE_TO="0.0.11"
|
||||||
PROGS=$(ls -1d src/uu/*/Cargo.toml src/uu/stdbuf/src/libstdbuf/Cargo.toml Cargo.toml src/uu/base64/Cargo.toml)
|
PROGS=$(ls -1d src/uu/*/Cargo.toml src/uu/stdbuf/src/libstdbuf/Cargo.toml Cargo.toml src/uu/base64/Cargo.toml)
|
||||||
|
|
||||||
# update the version of all programs
|
# update the version of all programs
|
||||||
|
#shellcheck disable=SC2086
|
||||||
sed -i -e "s|version = \"$FROM\"|version = \"$TO\"|" $PROGS
|
sed -i -e "s|version = \"$FROM\"|version = \"$TO\"|" $PROGS
|
||||||
|
|
||||||
# Update uucore_procs
|
# Update uucore_procs
|
||||||
|
@ -35,6 +36,8 @@ sed -i -e "s|= { optional=true, version=\"$FROM\", package=\"uu_|= { optional=tr
|
||||||
# Update uucore itself
|
# Update uucore itself
|
||||||
sed -i -e "s|version = \"$UUCORE_FROM\"|version = \"$UUCORE_TO\"|" src/uucore/Cargo.toml
|
sed -i -e "s|version = \"$UUCORE_FROM\"|version = \"$UUCORE_TO\"|" src/uucore/Cargo.toml
|
||||||
# Update crates using uucore
|
# Update crates using uucore
|
||||||
|
#shellcheck disable=SC2086
|
||||||
sed -i -e "s|uucore = { version=\">=$UUCORE_FROM\",|uucore = { version=\">=$UUCORE_TO\",|" $PROGS
|
sed -i -e "s|uucore = { version=\">=$UUCORE_FROM\",|uucore = { version=\">=$UUCORE_TO\",|" $PROGS
|
||||||
# Update crates using uucore_procs
|
# Update crates using uucore_procs
|
||||||
|
#shellcheck disable=SC2086
|
||||||
sed -i -e "s|uucore_procs = { version=\">=$UUCORE_PROCS_FROM\",|uucore_procs = { version=\">=$UUCORE_PROCS_TO\",|" $PROGS
|
sed -i -e "s|uucore_procs = { version=\">=$UUCORE_PROCS_FROM\",|uucore_procs = { version=\">=$UUCORE_PROCS_TO\",|" $PROGS
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue