From d345a280bf588b325babb29c60dc7d84d2c22832 Mon Sep 17 00:00:00 2001 From: John Shin Date: Sun, 30 Jul 2023 14:37:17 -0700 Subject: [PATCH 001/208] seq: update inf and nan parsing --- src/uu/seq/src/numberparse.rs | 40 +++++++++++++++++------------------ 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/src/uu/seq/src/numberparse.rs b/src/uu/seq/src/numberparse.rs index 23d94ea2b79..1921e2e4c4e 100644 --- a/src/uu/seq/src/numberparse.rs +++ b/src/uu/seq/src/numberparse.rs @@ -69,27 +69,25 @@ fn parse_no_decimal_no_exponent(s: &str) -> Result { // Possibly "NaN" or "inf". - // - // TODO In Rust v1.53.0, this change - // https://github.com/rust-lang/rust/pull/78618 improves the - // parsing of floats to include being able to parse "NaN" - // and "inf". So when the minimum version of this crate is - // increased to 1.53.0, we should just use the built-in - // `f32` parsing instead. - if s.eq_ignore_ascii_case("inf") { - Ok(PreciseNumber::new( - Number::Float(ExtendedBigDecimal::Infinity), - 0, - 0, - )) - } else if s.eq_ignore_ascii_case("-inf") { - Ok(PreciseNumber::new( - Number::Float(ExtendedBigDecimal::MinusInfinity), - 0, - 0, - )) - } else if s.eq_ignore_ascii_case("nan") || s.eq_ignore_ascii_case("-nan") { - Err(ParseNumberError::Nan) + if let Ok(num) = f32::from_str(s) { + // pattern matching on floating point literal is not encouraged 'https://github.com/rust-lang/rust/issues/41620' + if num == f32::INFINITY { + Ok(PreciseNumber::new( + Number::Float(ExtendedBigDecimal::Infinity), + 0, + 0, + )) + } else if num == f32::NEG_INFINITY { + Ok(PreciseNumber::new( + Number::Float(ExtendedBigDecimal::MinusInfinity), + 0, + 0, + )) + } else if num.is_nan() { + Err(ParseNumberError::Nan) + } else { + Err(ParseNumberError::Float) + } } else { Err(ParseNumberError::Float) } From 2bb56d44a4161b1b40225f27f8e30090910aae38 Mon Sep 17 00:00:00 2001 From: John Shin Date: Sat, 5 Aug 2023 18:23:29 -0700 Subject: [PATCH 002/208] seq: add tests for infinity and -infinity args --- src/uu/seq/src/numberparse.rs | 12 ++++++++++++ tests/by-util/test_seq.rs | 10 ++++++++++ 2 files changed, 22 insertions(+) diff --git a/src/uu/seq/src/numberparse.rs b/src/uu/seq/src/numberparse.rs index 1921e2e4c4e..156f80fb91f 100644 --- a/src/uu/seq/src/numberparse.rs +++ b/src/uu/seq/src/numberparse.rs @@ -477,11 +477,23 @@ mod tests { #[test] fn test_parse_inf() { assert_eq!(parse("inf"), Number::Float(ExtendedBigDecimal::Infinity)); + assert_eq!( + parse("infinity"), + Number::Float(ExtendedBigDecimal::Infinity) + ); assert_eq!(parse("+inf"), Number::Float(ExtendedBigDecimal::Infinity)); + assert_eq!( + parse("+infinity"), + Number::Float(ExtendedBigDecimal::Infinity) + ); assert_eq!( parse("-inf"), Number::Float(ExtendedBigDecimal::MinusInfinity) ); + assert_eq!( + parse("-infinity"), + Number::Float(ExtendedBigDecimal::MinusInfinity) + ); } #[test] diff --git a/tests/by-util/test_seq.rs b/tests/by-util/test_seq.rs index de078191251..95de2f3cd0a 100644 --- a/tests/by-util/test_seq.rs +++ b/tests/by-util/test_seq.rs @@ -619,11 +619,21 @@ fn test_neg_inf() { run(&["--", "-inf", "0"], b"-inf\n-inf\n-inf\n"); } +#[test] +fn test_neg_infinity() { + run(&["--", "-infinity", "0"], b"-inf\n-inf\n-inf\n"); +} + #[test] fn test_inf() { run(&["inf"], b"1\n2\n3\n"); } +#[test] +fn test_infinity() { + run(&["infinity"], b"1\n2\n3\n"); +} + #[test] fn test_inf_width() { run( From 7d7d4fb04a6bd053c91cc8cdc469ca734000e60d Mon Sep 17 00:00:00 2001 From: zhitkoff Date: Tue, 22 Aug 2023 13:09:28 -0400 Subject: [PATCH 003/208] build-gnu.sh: `/usr/bin/timeout` should not be hardcoded to /usr/bin location Fixes #5193 --- util/build-gnu.sh | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/util/build-gnu.sh b/util/build-gnu.sh index d852ed66fb6..d02c8c842e6 100755 --- a/util/build-gnu.sh +++ b/util/build-gnu.sh @@ -82,7 +82,16 @@ else ./bootstrap --skip-po ./configure --quiet --disable-gcc-warnings #Add timeout to to protect against hangs - sed -i 's|^"\$@|/usr/bin/timeout 600 "\$@|' build-aux/test-driver + # On MacOS there is no system /usr/bin/timeout + # and trying to add it to /usr/bin (with symlink of copy binary) will fail unless system integrity protection is disabled (not ideal) + # ref: https://support.apple.com/en-us/102149 + # On MacOS the Homebrew coreutils could be installed and then "sudo ln -s /opt/homebrew/bin/timeout /usr/local/bin/timeout" + # Set to /usr/local/timeout instead if /usr/bin/timeout is not found + if [ -x /usr/bin/timeout ] ; then + sed -i 's|^"\$@|/usr/bin/timeout 600 "\$@|' build-aux/test-driver + else + sed -i 's|^"\$@|/usr/local/bin/timeout 600 "\$@|' build-aux/test-driver + fi # Change the PATH in the Makefile to test the uutils coreutils instead of the GNU coreutils sed -i "s/^[[:blank:]]*PATH=.*/ PATH='${UU_BUILD_DIR//\//\\/}\$(PATH_SEPARATOR)'\"\$\$PATH\" \\\/" Makefile sed -i 's| tr | /usr/bin/tr |' tests/init.sh @@ -153,13 +162,28 @@ sed -i 's|touch |/usr/bin/touch |' tests/cp/reflink-perm.sh tests/ls/block-size. sed -i 's|ln -|/usr/bin/ln -|' tests/cp/link-deref.sh sed -i 's|cp |/usr/bin/cp |' tests/mv/hard-2.sh sed -i 's|paste |/usr/bin/paste |' tests/misc/od-endian.sh -sed -i 's|timeout |/usr/bin/timeout |' tests/tail-2/follow-stdin.sh +# On MacOS there is no system /usr/bin/timeout +# and trying to add it to /usr/bin (with symlink of copy binary) will fail unless system integrity protection is disabled (not ideal) +# ref: https://support.apple.com/en-us/102149 +# On MacOS the Homebrew coreutils could be installed and then "sudo ln -s /opt/homebrew/bin/timeout /usr/local/bin/timeout" +# Set to /usr/local/timeout instead if /usr/bin/timeout is not found +if [ -x /usr/bin/timeout ] ; then + sed -i 's|timeout |/usr/bin/timeout |' tests/tail-2/follow-stdin.sh +else + sed -i 's|timeout |/usr/local/bin/timeout |' tests/tail-2/follow-stdin.sh +fi + # Add specific timeout to tests that currently hang to limit time spent waiting -sed -i 's|\(^\s*\)seq \$|\1/usr/bin/timeout 0.1 seq \$|' tests/misc/seq-precision.sh tests/misc/seq-long-double.sh +if [ -x /usr/bin/timeout ] ; then + sed -i 's|\(^\s*\)seq \$|\1/usr/bin/timeout 0.1 seq \$|' tests/misc/seq-precision.sh tests/misc/seq-long-double.sh +else + sed -i 's|\(^\s*\)seq \$|\1/usr/local/bin/timeout 0.1 seq \$|' tests/misc/seq-precision.sh tests/misc/seq-long-double.sh +fi -# Remove dup of /usr/bin/ when executed several times +# Remove dup of /usr/bin/ (and /usr/local/bin) when executed several times grep -rlE '/usr/bin/\s?/usr/bin' init.cfg tests/* | xargs --no-run-if-empty sed -Ei 's|/usr/bin/\s?/usr/bin/|/usr/bin/|g' +grep -rlE '/usr/local/bin/\s?/usr/local/bin' init.cfg tests/* | xargs --no-run-if-empty sed -Ei 's|/usr/local/bin/\s?/usr/local/bin/|/usr/local/bin/|g' #### Adjust tests to make them work with Rust/coreutils # in some cases, what we are doing in rust/coreutils is good (or better) From 75cc6e3fdccedf551e297e54715cc65ed6045c7c Mon Sep 17 00:00:00 2001 From: zhitkoff Date: Tue, 22 Aug 2023 13:22:24 -0400 Subject: [PATCH 004/208] added some TODO(s) for missing/moved locations --- util/build-gnu.sh | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/util/build-gnu.sh b/util/build-gnu.sh index d02c8c842e6..6c608a06077 100755 --- a/util/build-gnu.sh +++ b/util/build-gnu.sh @@ -151,6 +151,9 @@ sed -i -e '/tests\/misc\/seq-precision.sh/ D' \ sed -i '/INT_OFLOW/ D' tests/misc/printf.sh # Use the system coreutils where the test fails due to error in a util that is not the one being tested +# TODO : tests/tail-2/ does not appear to exist +# and have been moved to just tests/tail/ location +# Might need to update the section bvelow to reflect that sed -i 's|stat|/usr/bin/stat|' tests/touch/60-seconds.sh tests/misc/sort-compress-proc.sh sed -i 's|ls -|/usr/bin/ls -|' tests/cp/same-file.sh tests/misc/mknod.sh tests/mv/part-symlink.sh sed -i 's|chmod |/usr/bin/chmod |' tests/du/inacc-dir.sh tests/tail-2/tail-n0f.sh tests/cp/fail-perm.sh tests/mv/i-2.sh tests/misc/shuf.sh @@ -175,6 +178,9 @@ fi # Add specific timeout to tests that currently hang to limit time spent waiting +# TODO : tests/misc/seq-precision.sh tests/misc/seq-long-double.sh do not appear to exist +# and have been moved to tests/seq/ location +# Might need to update the section bvelow to reflect that if [ -x /usr/bin/timeout ] ; then sed -i 's|\(^\s*\)seq \$|\1/usr/bin/timeout 0.1 seq \$|' tests/misc/seq-precision.sh tests/misc/seq-long-double.sh else @@ -205,6 +211,9 @@ sed -i -e "s|rm: cannot remove 'rel': Permission denied|rm: cannot remove 'rel': # overlay-headers.sh test intends to check for inotify events, # however there's a bug because `---dis` is an alias for: `---disable-inotify` +# TODO : tests/tail-2/ does not appear to exist +# and have been moved to just tests/tail/ location +# Might need to update the section bvelow to reflect that sed -i -e "s|---dis ||g" tests/tail-2/overlay-headers.sh test -f "${UU_BUILD_DIR}/getlimits" || cp src/getlimits "${UU_BUILD_DIR}" From c44c3cd71649252ae333dca0c55ab56d9ca52163 Mon Sep 17 00:00:00 2001 From: zhitkoff Date: Tue, 22 Aug 2023 15:28:02 -0400 Subject: [PATCH 005/208] fixed spelling --- util/build-gnu.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/util/build-gnu.sh b/util/build-gnu.sh index 6c608a06077..4414b7f12ba 100755 --- a/util/build-gnu.sh +++ b/util/build-gnu.sh @@ -153,7 +153,7 @@ sed -i '/INT_OFLOW/ D' tests/misc/printf.sh # Use the system coreutils where the test fails due to error in a util that is not the one being tested # TODO : tests/tail-2/ does not appear to exist # and have been moved to just tests/tail/ location -# Might need to update the section bvelow to reflect that +# Might need to update the section below to reflect that sed -i 's|stat|/usr/bin/stat|' tests/touch/60-seconds.sh tests/misc/sort-compress-proc.sh sed -i 's|ls -|/usr/bin/ls -|' tests/cp/same-file.sh tests/misc/mknod.sh tests/mv/part-symlink.sh sed -i 's|chmod |/usr/bin/chmod |' tests/du/inacc-dir.sh tests/tail-2/tail-n0f.sh tests/cp/fail-perm.sh tests/mv/i-2.sh tests/misc/shuf.sh @@ -180,7 +180,7 @@ fi # Add specific timeout to tests that currently hang to limit time spent waiting # TODO : tests/misc/seq-precision.sh tests/misc/seq-long-double.sh do not appear to exist # and have been moved to tests/seq/ location -# Might need to update the section bvelow to reflect that +# Might need to update the section below to reflect that if [ -x /usr/bin/timeout ] ; then sed -i 's|\(^\s*\)seq \$|\1/usr/bin/timeout 0.1 seq \$|' tests/misc/seq-precision.sh tests/misc/seq-long-double.sh else @@ -213,7 +213,7 @@ sed -i -e "s|rm: cannot remove 'rel': Permission denied|rm: cannot remove 'rel': # however there's a bug because `---dis` is an alias for: `---disable-inotify` # TODO : tests/tail-2/ does not appear to exist # and have been moved to just tests/tail/ location -# Might need to update the section bvelow to reflect that +# Might need to update the section below to reflect that sed -i -e "s|---dis ||g" tests/tail-2/overlay-headers.sh test -f "${UU_BUILD_DIR}/getlimits" || cp src/getlimits "${UU_BUILD_DIR}" From 6468845850a0c847f42fb400ffecbdcf54f8e99c Mon Sep 17 00:00:00 2001 From: zhitkoff Date: Thu, 24 Aug 2023 20:36:52 -0400 Subject: [PATCH 006/208] refactor to check for system timeout once + commented out many moved/deleted test files that make script fail --- util/build-gnu.sh | 71 ++++++++++++++++++++++------------------------- 1 file changed, 33 insertions(+), 38 deletions(-) diff --git a/util/build-gnu.sh b/util/build-gnu.sh index 4414b7f12ba..c6991d15c1b 100755 --- a/util/build-gnu.sh +++ b/util/build-gnu.sh @@ -18,6 +18,20 @@ path_GNU="$(readlink -fm -- "${path_GNU:-${path_UUTILS}/../gnu}")" ### +# On MacOS there is no system /usr/bin/timeout +# and trying to add it to /usr/bin (with symlink of copy binary) will fail unless system integrity protection is disabled (not ideal) +# ref: https://support.apple.com/en-us/102149 +# On MacOS the Homebrew coreutils could be installed and then "sudo ln -s /opt/homebrew/bin/timeout /usr/local/bin/timeout" +# Set to /usr/local/bin/timeout instead if /usr/bin/timeout is not found +SYSTEM_TIMEOUT="timeout" +if [ -x /usr/bin/timeout ] ; then + SYSTEM_TIMEOUT="/usr/bin/timeout" +elif [ -x /usr/local/bin/timeout ] ; then + SYSTEM_TIMEOUT="/usr/local/bin/timeout" +fi + +### + if test ! -d "${path_GNU}"; then echo "Could not find GNU coreutils (expected at '${path_GNU}')" echo "Run the following to download into the expected path:" @@ -82,16 +96,7 @@ else ./bootstrap --skip-po ./configure --quiet --disable-gcc-warnings #Add timeout to to protect against hangs - # On MacOS there is no system /usr/bin/timeout - # and trying to add it to /usr/bin (with symlink of copy binary) will fail unless system integrity protection is disabled (not ideal) - # ref: https://support.apple.com/en-us/102149 - # On MacOS the Homebrew coreutils could be installed and then "sudo ln -s /opt/homebrew/bin/timeout /usr/local/bin/timeout" - # Set to /usr/local/timeout instead if /usr/bin/timeout is not found - if [ -x /usr/bin/timeout ] ; then - sed -i 's|^"\$@|/usr/bin/timeout 600 "\$@|' build-aux/test-driver - else - sed -i 's|^"\$@|/usr/local/bin/timeout 600 "\$@|' build-aux/test-driver - fi + sed -i 's|^"\$@|'"${SYSTEM_TIMEOUT}"' 600 "\$@|' build-aux/test-driver # Change the PATH in the Makefile to test the uutils coreutils instead of the GNU coreutils sed -i "s/^[[:blank:]]*PATH=.*/ PATH='${UU_BUILD_DIR//\//\\/}\$(PATH_SEPARATOR)'\"\$\$PATH\" \\\/" Makefile sed -i 's| tr | /usr/bin/tr |' tests/init.sh @@ -148,44 +153,32 @@ sed -i -e '/tests\/misc\/seq-precision.sh/ D' \ Makefile # printf doesn't limit the values used in its arg, so this produced ~2GB of output -sed -i '/INT_OFLOW/ D' tests/misc/printf.sh +# Looks like tests/misc/printf.sh does not exist anymore - comment it out for now +#sed -i '/INT_OFLOW/ D' tests/misc/printf.sh # Use the system coreutils where the test fails due to error in a util that is not the one being tested # TODO : tests/tail-2/ does not appear to exist # and have been moved to just tests/tail/ location # Might need to update the section below to reflect that -sed -i 's|stat|/usr/bin/stat|' tests/touch/60-seconds.sh tests/misc/sort-compress-proc.sh +# Also looks like tests/misc/sort-compress-proc.sh and tests/tail-2/tail-n0f.sh and tests/misc/shuf.sh and many others do not exist anymore or moved - comment it out for now +sed -i 's|stat|/usr/bin/stat|' tests/touch/60-seconds.sh #tests/misc/sort-compress-proc.sh sed -i 's|ls -|/usr/bin/ls -|' tests/cp/same-file.sh tests/misc/mknod.sh tests/mv/part-symlink.sh -sed -i 's|chmod |/usr/bin/chmod |' tests/du/inacc-dir.sh tests/tail-2/tail-n0f.sh tests/cp/fail-perm.sh tests/mv/i-2.sh tests/misc/shuf.sh -sed -i 's|sort |/usr/bin/sort |' tests/ls/hyperlink.sh tests/misc/test-N.sh -sed -i 's|split |/usr/bin/split |' tests/misc/factor-parallel.sh -sed -i 's|id -|/usr/bin/id -|' tests/misc/runcon-no-reorder.sh +sed -i 's|chmod |/usr/bin/chmod |' tests/du/inacc-dir.sh tests/cp/fail-perm.sh tests/mv/i-2.sh #tests/misc/shuf.sh #tests/tail-2/tail-n0f.sh +sed -i 's|sort |/usr/bin/sort |' tests/ls/hyperlink.sh #tests/misc/test-N.sh +#sed -i 's|split |/usr/bin/split |' tests/misc/factor-parallel.sh +#sed -i 's|id -|/usr/bin/id -|' tests/misc/runcon-no-reorder.sh # tests/ls/abmon-align.sh - https://github.com/uutils/coreutils/issues/3505 -sed -i 's|touch |/usr/bin/touch |' tests/cp/reflink-perm.sh tests/ls/block-size.sh tests/mv/update.sh tests/misc/ls-time.sh tests/misc/stat-nanoseconds.sh tests/misc/time-style.sh tests/misc/test-N.sh tests/ls/abmon-align.sh +sed -i 's|touch |/usr/bin/touch |' tests/cp/reflink-perm.sh tests/ls/block-size.sh tests/mv/update.sh tests/misc/time-style.sh tests/ls/abmon-align.sh #tests/misc/ls-time.sh tests/misc/stat-nanoseconds.sh tests/misc/test-N.sh sed -i 's|ln -|/usr/bin/ln -|' tests/cp/link-deref.sh sed -i 's|cp |/usr/bin/cp |' tests/mv/hard-2.sh -sed -i 's|paste |/usr/bin/paste |' tests/misc/od-endian.sh -# On MacOS there is no system /usr/bin/timeout -# and trying to add it to /usr/bin (with symlink of copy binary) will fail unless system integrity protection is disabled (not ideal) -# ref: https://support.apple.com/en-us/102149 -# On MacOS the Homebrew coreutils could be installed and then "sudo ln -s /opt/homebrew/bin/timeout /usr/local/bin/timeout" -# Set to /usr/local/timeout instead if /usr/bin/timeout is not found -if [ -x /usr/bin/timeout ] ; then - sed -i 's|timeout |/usr/bin/timeout |' tests/tail-2/follow-stdin.sh -else - sed -i 's|timeout |/usr/local/bin/timeout |' tests/tail-2/follow-stdin.sh -fi - +#sed -i 's|paste |/usr/bin/paste |' tests/misc/od-endian.sh +#sed -i 's|timeout |'"${SYSTEM_TIMEOUT}"' |' tests/tail-2/follow-stdin.sh # Add specific timeout to tests that currently hang to limit time spent waiting # TODO : tests/misc/seq-precision.sh tests/misc/seq-long-double.sh do not appear to exist # and have been moved to tests/seq/ location -# Might need to update the section below to reflect that -if [ -x /usr/bin/timeout ] ; then - sed -i 's|\(^\s*\)seq \$|\1/usr/bin/timeout 0.1 seq \$|' tests/misc/seq-precision.sh tests/misc/seq-long-double.sh -else - sed -i 's|\(^\s*\)seq \$|\1/usr/local/bin/timeout 0.1 seq \$|' tests/misc/seq-precision.sh tests/misc/seq-long-double.sh -fi +# Might need to update the section below to reflect that, but comment it out for now +#sed -i 's|\(^\s*\)seq \$|\1'"${SYSTEM_TIMEOUT}"' 0.1 seq \$|' tests/misc/seq-precision.sh tests/misc/seq-long-double.sh # Remove dup of /usr/bin/ (and /usr/local/bin) when executed several times grep -rlE '/usr/bin/\s?/usr/bin' init.cfg tests/* | xargs --no-run-if-empty sed -Ei 's|/usr/bin/\s?/usr/bin/|/usr/bin/|g' @@ -214,7 +207,7 @@ sed -i -e "s|rm: cannot remove 'rel': Permission denied|rm: cannot remove 'rel': # TODO : tests/tail-2/ does not appear to exist # and have been moved to just tests/tail/ location # Might need to update the section below to reflect that -sed -i -e "s|---dis ||g" tests/tail-2/overlay-headers.sh +#sed -i -e "s|---dis ||g" tests/tail-2/overlay-headers.sh test -f "${UU_BUILD_DIR}/getlimits" || cp src/getlimits "${UU_BUILD_DIR}" @@ -271,11 +264,13 @@ sed -i -e "s/Try 'mv --help' for more information/For more information, try '--h # GNU doesn't support width > INT_MAX # disable these test cases -sed -i -E "s|^([^#]*2_31.*)$|#\1|g" tests/misc/printf-cov.pl +# TODO: moved or deleted tests/misc/printf-cov.pl +#sed -i -E "s|^([^#]*2_31.*)$|#\1|g" tests/misc/printf-cov.pl sed -i -e "s/du: invalid -t argument/du: invalid --threshold argument/" -e "s/du: option requires an argument/error: a value is required for '--threshold ' but none was supplied/" -e "/Try 'du --help' for more information./d" tests/du/threshold.sh # disable two kind of tests: # "hostid BEFORE --help" doesn't fail for GNU. we fail. we are probably doing better # "hostid BEFORE --help AFTER " same for this -sed -i -e "s/env \$prog \$BEFORE \$opt > out2/env \$prog \$BEFORE \$opt > out2 #/" -e "s/env \$prog \$BEFORE \$opt AFTER > out3/env \$prog \$BEFORE \$opt AFTER > out3 #/" -e "s/compare exp out2/compare exp out2 #/" -e "s/compare exp out3/compare exp out3 #/" tests/misc/help-version-getopt.sh +# TODO moved or deleted tests/misc/help-version-getopt.sh +#sed -i -e "s/env \$prog \$BEFORE \$opt > out2/env \$prog \$BEFORE \$opt > out2 #/" -e "s/env \$prog \$BEFORE \$opt AFTER > out3/env \$prog \$BEFORE \$opt AFTER > out3 #/" -e "s/compare exp out2/compare exp out2 #/" -e "s/compare exp out3/compare exp out3 #/" tests/misc/help-version-getopt.sh From 350b9c3d486449f592b9fd92f349b884926073cb Mon Sep 17 00:00:00 2001 From: zhitkoff Date: Fri, 25 Aug 2023 13:43:48 -0400 Subject: [PATCH 007/208] reverted commented out test files and added more details in TODO(s) --- util/build-gnu.sh | 51 +++++++++++++++++++++-------------------------- 1 file changed, 23 insertions(+), 28 deletions(-) diff --git a/util/build-gnu.sh b/util/build-gnu.sh index c6991d15c1b..587cf858096 100755 --- a/util/build-gnu.sh +++ b/util/build-gnu.sh @@ -152,37 +152,37 @@ sed -i -e '/tests\/misc\/invalid-opt.pl/ D' \ sed -i -e '/tests\/misc\/seq-precision.sh/ D' \ Makefile +# TODO: many files and some directories modified with 'sed' in the sections below either no longer exist or have been moved +# TODO: Might need to review and updated sections below +# TODO: As a result this script will fail when executed normally as 'bash util/build-gnu.hs' due to the 'set -e' set at the beginning +# TODO: The rest of the 'sed' commands after first failure in this scenario will not be executed as bash will exit on first error +# TODO: However, the behaviour might be different when running it via GitHub actions (GnuTests) +# TODO: For now, when running in local a workaround would be to comment out the 'set -e' at the beginning of the file + # printf doesn't limit the values used in its arg, so this produced ~2GB of output -# Looks like tests/misc/printf.sh does not exist anymore - comment it out for now -#sed -i '/INT_OFLOW/ D' tests/misc/printf.sh +# TODO: this is the first one to likely fail as tests/misc/printf.sh does not exist/have been moved +sed -i '/INT_OFLOW/ D' tests/misc/printf.sh +# TODO: all commands below might not be executed # Use the system coreutils where the test fails due to error in a util that is not the one being tested -# TODO : tests/tail-2/ does not appear to exist -# and have been moved to just tests/tail/ location -# Might need to update the section below to reflect that -# Also looks like tests/misc/sort-compress-proc.sh and tests/tail-2/tail-n0f.sh and tests/misc/shuf.sh and many others do not exist anymore or moved - comment it out for now -sed -i 's|stat|/usr/bin/stat|' tests/touch/60-seconds.sh #tests/misc/sort-compress-proc.sh +sed -i 's|stat|/usr/bin/stat|' tests/touch/60-seconds.sh tests/misc/sort-compress-proc.sh sed -i 's|ls -|/usr/bin/ls -|' tests/cp/same-file.sh tests/misc/mknod.sh tests/mv/part-symlink.sh -sed -i 's|chmod |/usr/bin/chmod |' tests/du/inacc-dir.sh tests/cp/fail-perm.sh tests/mv/i-2.sh #tests/misc/shuf.sh #tests/tail-2/tail-n0f.sh -sed -i 's|sort |/usr/bin/sort |' tests/ls/hyperlink.sh #tests/misc/test-N.sh -#sed -i 's|split |/usr/bin/split |' tests/misc/factor-parallel.sh -#sed -i 's|id -|/usr/bin/id -|' tests/misc/runcon-no-reorder.sh +sed -i 's|chmod |/usr/bin/chmod |' tests/du/inacc-dir.sh tests/tail-2/tail-n0f.sh tests/cp/fail-perm.sh tests/mv/i-2.sh tests/misc/shuf.sh +sed -i 's|sort |/usr/bin/sort |' tests/ls/hyperlink.sh tests/misc/test-N.sh +sed -i 's|split |/usr/bin/split |' tests/misc/factor-parallel.sh +sed -i 's|id -|/usr/bin/id -|' tests/misc/runcon-no-reorder.sh # tests/ls/abmon-align.sh - https://github.com/uutils/coreutils/issues/3505 -sed -i 's|touch |/usr/bin/touch |' tests/cp/reflink-perm.sh tests/ls/block-size.sh tests/mv/update.sh tests/misc/time-style.sh tests/ls/abmon-align.sh #tests/misc/ls-time.sh tests/misc/stat-nanoseconds.sh tests/misc/test-N.sh +sed -i 's|touch |/usr/bin/touch |' tests/cp/reflink-perm.sh tests/ls/block-size.sh tests/mv/update.sh tests/misc/ls-time.sh tests/misc/stat-nanoseconds.sh tests/misc/time-style.sh tests/misc/test-N.sh tests/ls/abmon-align.sh sed -i 's|ln -|/usr/bin/ln -|' tests/cp/link-deref.sh sed -i 's|cp |/usr/bin/cp |' tests/mv/hard-2.sh -#sed -i 's|paste |/usr/bin/paste |' tests/misc/od-endian.sh -#sed -i 's|timeout |'"${SYSTEM_TIMEOUT}"' |' tests/tail-2/follow-stdin.sh +sed -i 's|paste |/usr/bin/paste |' tests/misc/od-endian.sh +sed -i 's|timeout |/usr/bin/timeout |' tests/tail-2/follow-stdin.sh # Add specific timeout to tests that currently hang to limit time spent waiting -# TODO : tests/misc/seq-precision.sh tests/misc/seq-long-double.sh do not appear to exist -# and have been moved to tests/seq/ location -# Might need to update the section below to reflect that, but comment it out for now -#sed -i 's|\(^\s*\)seq \$|\1'"${SYSTEM_TIMEOUT}"' 0.1 seq \$|' tests/misc/seq-precision.sh tests/misc/seq-long-double.sh +sed -i 's|\(^\s*\)seq \$|\1/usr/bin/timeout 0.1 seq \$|' tests/misc/seq-precision.sh tests/misc/seq-long-double.sh -# Remove dup of /usr/bin/ (and /usr/local/bin) when executed several times +# Remove dup of /usr/bin/ when executed several times grep -rlE '/usr/bin/\s?/usr/bin' init.cfg tests/* | xargs --no-run-if-empty sed -Ei 's|/usr/bin/\s?/usr/bin/|/usr/bin/|g' -grep -rlE '/usr/local/bin/\s?/usr/local/bin' init.cfg tests/* | xargs --no-run-if-empty sed -Ei 's|/usr/local/bin/\s?/usr/local/bin/|/usr/local/bin/|g' #### Adjust tests to make them work with Rust/coreutils # in some cases, what we are doing in rust/coreutils is good (or better) @@ -204,10 +204,7 @@ sed -i -e "s|rm: cannot remove 'rel': Permission denied|rm: cannot remove 'rel': # overlay-headers.sh test intends to check for inotify events, # however there's a bug because `---dis` is an alias for: `---disable-inotify` -# TODO : tests/tail-2/ does not appear to exist -# and have been moved to just tests/tail/ location -# Might need to update the section below to reflect that -#sed -i -e "s|---dis ||g" tests/tail-2/overlay-headers.sh +sed -i -e "s|---dis ||g" tests/tail-2/overlay-headers.sh test -f "${UU_BUILD_DIR}/getlimits" || cp src/getlimits "${UU_BUILD_DIR}" @@ -264,13 +261,11 @@ sed -i -e "s/Try 'mv --help' for more information/For more information, try '--h # GNU doesn't support width > INT_MAX # disable these test cases -# TODO: moved or deleted tests/misc/printf-cov.pl -#sed -i -E "s|^([^#]*2_31.*)$|#\1|g" tests/misc/printf-cov.pl +sed -i -E "s|^([^#]*2_31.*)$|#\1|g" tests/misc/printf-cov.pl sed -i -e "s/du: invalid -t argument/du: invalid --threshold argument/" -e "s/du: option requires an argument/error: a value is required for '--threshold ' but none was supplied/" -e "/Try 'du --help' for more information./d" tests/du/threshold.sh # disable two kind of tests: # "hostid BEFORE --help" doesn't fail for GNU. we fail. we are probably doing better # "hostid BEFORE --help AFTER " same for this -# TODO moved or deleted tests/misc/help-version-getopt.sh -#sed -i -e "s/env \$prog \$BEFORE \$opt > out2/env \$prog \$BEFORE \$opt > out2 #/" -e "s/env \$prog \$BEFORE \$opt AFTER > out3/env \$prog \$BEFORE \$opt AFTER > out3 #/" -e "s/compare exp out2/compare exp out2 #/" -e "s/compare exp out3/compare exp out3 #/" tests/misc/help-version-getopt.sh +sed -i -e "s/env \$prog \$BEFORE \$opt > out2/env \$prog \$BEFORE \$opt > out2 #/" -e "s/env \$prog \$BEFORE \$opt AFTER > out3/env \$prog \$BEFORE \$opt AFTER > out3 #/" -e "s/compare exp out2/compare exp out2 #/" -e "s/compare exp out3/compare exp out3 #/" tests/misc/help-version-getopt.sh From db342556c2ce93be472abccd6594b27e932aa7f6 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Mon, 14 Aug 2023 13:40:05 +0200 Subject: [PATCH 008/208] nl: implement -d/--section-delimiter --- src/uu/nl/src/helper.rs | 9 +++++ src/uu/nl/src/nl.rs | 44 ++++++++++++++++----- tests/by-util/test_nl.rs | 83 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 125 insertions(+), 11 deletions(-) diff --git a/src/uu/nl/src/helper.rs b/src/uu/nl/src/helper.rs index fe550e6a063..ae14a6d59cc 100644 --- a/src/uu/nl/src/helper.rs +++ b/src/uu/nl/src/helper.rs @@ -13,6 +13,15 @@ pub fn parse_options(settings: &mut crate::Settings, opts: &clap::ArgMatches) -> // This vector holds error messages encountered. let mut errs: Vec = vec![]; settings.renumber = opts.get_flag(options::NO_RENUMBER); + if let Some(delimiter) = opts.get_one::(options::SECTION_DELIMITER) { + // check whether the delimiter is a single ASCII char (1 byte) + // because GNU nl doesn't add a ':' to single non-ASCII chars + settings.section_delimiter = if delimiter.len() == 1 { + format!("{delimiter}:") + } else { + delimiter.to_owned() + }; + } if let Some(val) = opts.get_one::(options::NUMBER_SEPARATOR) { settings.number_separator = val.to_owned(); } diff --git a/src/uu/nl/src/nl.rs b/src/uu/nl/src/nl.rs index 61a0a9f35f4..839ec35db0f 100644 --- a/src/uu/nl/src/nl.rs +++ b/src/uu/nl/src/nl.rs @@ -23,7 +23,7 @@ pub struct Settings { body_numbering: NumberingStyle, footer_numbering: NumberingStyle, // The variable corresponding to -d - section_delimiter: [char; 2], + section_delimiter: String, // The variables corresponding to the options -v, -i, -l, -w. starting_line_number: i64, line_increment: i64, @@ -43,7 +43,7 @@ impl Default for Settings { header_numbering: NumberingStyle::None, body_numbering: NumberingStyle::NonEmpty, footer_numbering: NumberingStyle::None, - section_delimiter: ['\\', ':'], + section_delimiter: String::from("\\:"), starting_line_number: 1, line_increment: 1, join_blank_lines: 1, @@ -120,6 +120,32 @@ impl NumberFormat { } } +enum SectionDelimiter { + Header, + Body, + Footer, +} + +impl SectionDelimiter { + // A valid section delimiter contains the pattern one to three times, + // and nothing else. + fn parse(s: &str, pattern: &str) -> Option { + if s.is_empty() || pattern.is_empty() { + return None; + } + + let pattern_count = s.matches(pattern).count(); + let is_length_ok = pattern_count * pattern.len() == s.len(); + + match (pattern_count, is_length_ok) { + (3, true) => Some(Self::Header), + (2, true) => Some(Self::Body), + (1, true) => Some(Self::Footer), + _ => None, + } + } +} + pub mod options { pub const HELP: &str = "help"; pub const FILE: &str = "file"; @@ -299,14 +325,12 @@ fn nl(reader: &mut BufReader, settings: &Settings) -> UResult<()> { consecutive_empty_lines = 0; }; - // FIXME section delimiters are hardcoded and settings.section_delimiter is ignored - // because --section-delimiter is not correctly implemented yet - let _ = settings.section_delimiter; // XXX suppress "field never read" warning - let new_numbering_style = match line.as_str() { - "\\:\\:\\:" => Some(&settings.header_numbering), - "\\:\\:" => Some(&settings.body_numbering), - "\\:" => Some(&settings.footer_numbering), - _ => None, + let new_numbering_style = match SectionDelimiter::parse(&line, &settings.section_delimiter) + { + Some(SectionDelimiter::Header) => Some(&settings.header_numbering), + Some(SectionDelimiter::Body) => Some(&settings.body_numbering), + Some(SectionDelimiter::Footer) => Some(&settings.footer_numbering), + None => None, }; if let Some(new_style) = new_numbering_style { diff --git a/tests/by-util/test_nl.rs b/tests/by-util/test_nl.rs index b58c0c206fb..e21bb503721 100644 --- a/tests/by-util/test_nl.rs +++ b/tests/by-util/test_nl.rs @@ -2,7 +2,8 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -// spell-checker:ignore binvalid finvalid hinvalid iinvalid linvalid ninvalid vinvalid winvalid +// +// spell-checker:ignore binvalid finvalid hinvalid iinvalid linvalid nabcabc nabcabcabc ninvalid vinvalid winvalid use crate::common::util::TestScenario; #[test] @@ -486,3 +487,83 @@ fn test_line_number_overflow() { .stdout_is(format!("{}\ta\n", i64::MIN)) .stderr_is("nl: line number overflow\n"); } + +#[test] +fn test_section_delimiter() { + for arg in ["-dabc", "--section-delimiter=abc"] { + new_ucmd!() + .arg(arg) + .pipe_in("a\nabcabcabc\nb") // header section + .succeeds() + .stdout_is(" 1\ta\n\n b\n"); + + new_ucmd!() + .arg(arg) + .pipe_in("a\nabcabc\nb") // body section + .succeeds() + .stdout_is(" 1\ta\n\n 1\tb\n"); + + new_ucmd!() + .arg(arg) + .pipe_in("a\nabc\nb") // footer section + .succeeds() + .stdout_is(" 1\ta\n\n b\n"); + } +} + +#[test] +fn test_one_char_section_delimiter_expansion() { + for arg in ["-da", "--section-delimiter=a"] { + new_ucmd!() + .arg(arg) + .pipe_in("a\na:a:a:\nb") // header section + .succeeds() + .stdout_is(" 1\ta\n\n b\n"); + + new_ucmd!() + .arg(arg) + .pipe_in("a\na:a:\nb") // body section + .succeeds() + .stdout_is(" 1\ta\n\n 1\tb\n"); + + new_ucmd!() + .arg(arg) + .pipe_in("a\na:\nb") // footer section + .succeeds() + .stdout_is(" 1\ta\n\n b\n"); + } +} + +#[test] +fn test_non_ascii_one_char_section_delimiter() { + for arg in ["-dä", "--section-delimiter=ä"] { + new_ucmd!() + .arg(arg) + .pipe_in("a\näää\nb") // header section + .succeeds() + .stdout_is(" 1\ta\n\n b\n"); + + new_ucmd!() + .arg(arg) + .pipe_in("a\nää\nb") // body section + .succeeds() + .stdout_is(" 1\ta\n\n 1\tb\n"); + + new_ucmd!() + .arg(arg) + .pipe_in("a\nä\nb") // footer section + .succeeds() + .stdout_is(" 1\ta\n\n b\n"); + } +} + +#[test] +fn test_empty_section_delimiter() { + for arg in ["-d ''", "--section-delimiter=''"] { + new_ucmd!() + .arg(arg) + .pipe_in("a\n\nb") + .succeeds() + .stdout_is(" 1\ta\n \n 2\tb\n"); + } +} From 84d96f9d028a4dd8c0f98ba6db2ba6dc90c79dcb Mon Sep 17 00:00:00 2001 From: zhitkoff Date: Sat, 26 Aug 2023 11:11:46 -0400 Subject: [PATCH 009/208] split: refactor for more common use case --- src/uu/split/src/split.rs | 81 ++++++++++++++++++- tests/by-util/test_split.rs | 152 +++++++++++++++++++++++++++++++++++- 2 files changed, 229 insertions(+), 4 deletions(-) diff --git a/src/uu/split/src/split.rs b/src/uu/split/src/split.rs index 71aacfed006..c4b7e6d660e 100644 --- a/src/uu/split/src/split.rs +++ b/src/uu/split/src/split.rs @@ -51,14 +51,66 @@ const AFTER_HELP: &str = help_section!("after help", "split.md"); #[uucore::main] pub fn uumain(args: impl uucore::Args) -> UResult<()> { + let args = args.collect_lossy(); + + let (args, obs_lines) = handle_obsolete(&args[..]); + let matches = uu_app().try_get_matches_from(args)?; - match Settings::from(&matches) { + + match Settings::from(&matches, &obs_lines) { Ok(settings) => split(&settings), Err(e) if e.requires_usage() => Err(UUsageError::new(1, format!("{e}"))), Err(e) => Err(USimpleError::new(1, format!("{e}"))), } } +/// Extract obsolete shorthand (if any) for specifying lines in following scenarios (and similar) +/// `split -22 file` would mean `split -l 22 file` +/// `split -2de file` would mean `split -l 2 -d -e file` +/// `split -x300e file` would mean `split -x -l 300 -e file` +/// `split -x300e -22 file` would mean `split -x -e -l 22 file` (last obsolete lines option wins) +/// following GNU `split` behavior +fn handle_obsolete(args: &[String]) -> (Vec, Option) { + let mut v: Vec = vec![]; + let mut obs_lines = None; + for arg in args.iter() { + let slice = &arg; + if slice.starts_with('-') && !slice.starts_with("--") { + // start of the short option string + // extract numeric part and filter it out + let mut obs_lines_extracted: Vec = vec![]; + let filtered_slice: Vec = slice + .chars() + .filter(|c| { + if c.is_ascii_digit() { + obs_lines_extracted.push(*c); + false + } else { + true + } + }) + .collect(); + + if filtered_slice.get(1).is_some() { + // there were some short options in front of obsolete lines number + // i.e. '-xd100' or similar + // preserve it + v.push(filtered_slice.iter().collect()) + } + if !obs_lines_extracted.is_empty() { + // obsolete lines value was extracted + obs_lines = Some(obs_lines_extracted.iter().collect()); + } + } else { + // not a short option + // preserve it + v.push(arg.to_owned()); + } + } + println!("{:#?}",v); + (v, obs_lines) +} + pub fn uu_app() -> Command { Command::new(uucore::util_name()) .version(crate_version!()) @@ -357,6 +409,17 @@ impl fmt::Display for StrategyError { } impl Strategy { + /// Parse a strategy from obsolete line option value + fn from_obs(obs_value: &str) -> Result { + let n = parse_size(obs_value).map_err(StrategyError::Lines)?; + if n > 0 { + Ok(Self::Lines(n)) + } else { + Err(StrategyError::Lines(ParseSizeError::ParseFailure( + obs_value.to_string(), + ))) + } + } /// Parse a strategy from the command-line arguments. fn from(matches: &ArgMatches) -> Result { fn get_and_parse( @@ -506,7 +569,7 @@ impl fmt::Display for SettingsError { impl Settings { /// Parse a strategy from the command-line arguments. - fn from(matches: &ArgMatches) -> Result { + fn from(matches: &ArgMatches, obs_lines: &Option) -> Result { let additional_suffix = matches .get_one::(OPT_ADDITIONAL_SUFFIX) .unwrap() @@ -514,7 +577,19 @@ impl Settings { if additional_suffix.contains('/') { return Err(SettingsError::SuffixContainsSeparator(additional_suffix)); } - let strategy = Strategy::from(matches).map_err(SettingsError::Strategy)?; + + // obsolete lines option cannot be used simultaneously with OPT_LINES + let strategy = match obs_lines { + Some(obs_value) => { + if matches.value_source(OPT_LINES) == Some(ValueSource::CommandLine) { + return Err(SettingsError::Strategy(StrategyError::MultipleWays)); + } else { + Strategy::from_obs(obs_value).map_err(SettingsError::Strategy)? + } + } + None => Strategy::from(matches).map_err(SettingsError::Strategy)?, + }; + let (suffix_type, suffix_start) = suffix_type_from(matches)?; let suffix_length_str = matches.get_one::(OPT_SUFFIX_LENGTH).unwrap(); let suffix_length: usize = suffix_length_str diff --git a/tests/by-util/test_split.rs b/tests/by-util/test_split.rs index c5f32482ed6..fe753f0849f 100644 --- a/tests/by-util/test_split.rs +++ b/tests/by-util/test_split.rs @@ -2,7 +2,7 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -// spell-checker:ignore xzaaa sixhundredfiftyonebytes ninetyonebytes threebytes asciilowercase fghij klmno pqrst uvwxyz fivelines twohundredfortyonebytes onehundredlines nbbbb +// spell-checker:ignore xzaaa sixhundredfiftyonebytes ninetyonebytes threebytes asciilowercase fghij klmno pqrst uvwxyz fivelines twohundredfortyonebytes onehundredlines nbbbb dxen use crate::common::util::{AtPath, TestScenario}; use rand::{thread_rng, Rng, SeedableRng}; @@ -320,6 +320,156 @@ fn test_split_lines_number() { .stderr_only("split: invalid number of lines: '2fb'\n"); } +/// Test for obsolete lines option standalone +#[test] +fn test_split_obs_lines_standalone() { + let (at, mut ucmd) = at_and_ucmd!(); + let name = "obs-lines-standalone"; + RandomFile::new(&at, name).add_lines(4); + ucmd.args(&["-2", name]).succeeds().no_stderr().no_stdout(); + let glob = Glob::new(&at, ".", r"x[[:alpha:]][[:alpha:]]$"); + assert_eq!(glob.count(), 2); + assert_eq!(glob.collate(), at.read_bytes(name)); +} + +/// Test for invalid obsolete lines option +#[test] +fn test_split_invalid_obs_lines_standalone() { + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + at.touch("file"); + + scene + .ucmd() + .args(&["-2fb", "file"]) + .fails() + .code_is(1) + .stderr_only("error: unexpected argument '-f' found\n"); +} + +/// Test for obsolete lines option as part of combined short options +#[test] +fn test_split_obs_lines_within_combined_shorts() { + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + let name = "obs-lines-within-shorts"; + RandomFile::new(&at, name).add_lines(400); + + scene + .ucmd() + .args(&["-d200xe", name]) + .succeeds() + .no_stderr() + .no_stdout(); + let glob = Glob::new(&at, ".", r"x[[:alpha:]][[:alpha:]]$"); + assert_eq!(glob.count(), 2); + assert_eq!(glob.collate(), at.read_bytes(name)) +} + +/// Test for obsolete lines option starts as part of combined short options +#[test] +fn test_split_obs_lines_starts_combined_shorts() { + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + let name = "obs-lines-starts-shorts"; + RandomFile::new(&at, name).add_lines(400); + + scene + .ucmd() + .args(&["-x200d", name]) + .succeeds() + .no_stderr() + .no_stdout(); + let glob = Glob::new(&at, ".", r"x[[:alpha:]][[:alpha:]]$"); + assert_eq!(glob.count(), 2); + assert_eq!(glob.collate(), at.read_bytes(name)) +} + +/// Test for using both obsolete lines (standalone) option and short/long lines option simultaneously +#[test] +fn test_split_both_lines_and_obs_lines_standalone() { + // This test will ensure that if both lines option '-l' or '--lines' + // and obsolete lines option '-100' are used + // it fails + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + at.touch("file"); + + scene + .ucmd() + .args(&["-l", "2", "-2", "file"]) + .fails() + .code_is(1) + .stderr_contains("split: cannot split in more than one way\n"); + scene + .ucmd() + .args(&["--lines", "2", "-2", "file"]) + .fails() + .code_is(1) + .stderr_contains("split: cannot split in more than one way\n"); +} + +/// Test for using more than one obsolete lines option (standalone) +/// last one wins +#[test] +fn test_split_multiple_obs_lines_standalone() { + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + let name = "multiple-obs-lines"; + RandomFile::new(&at, name).add_lines(400); + + scene + .ucmd() + .args(&["-3000", "-200", name]) + .succeeds() + .no_stderr() + .no_stdout(); + let glob = Glob::new(&at, ".", r"x[[:alpha:]][[:alpha:]]$"); + assert_eq!(glob.count(), 2); + assert_eq!(glob.collate(), at.read_bytes(name)) +} + +/// Test for using more than one obsolete lines option within combined shorts +/// last one wins +#[test] +fn test_split_multiple_obs_lines_within_combined() { + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + let name = "multiple-obs-lines"; + RandomFile::new(&at, name).add_lines(400); + + scene + .ucmd() + .args(&["-x5000d -e200x", name]) + .succeeds() + .no_stderr() + .no_stdout(); + let glob = Glob::new(&at, ".", r"x[[:alpha:]][[:alpha:]]$"); + assert_eq!(glob.count(), 2); + assert_eq!(glob.collate(), at.read_bytes(name)) +} + +/// Test for using both obsolete lines option within combined shorts with conflicting -n option simultaneously +#[test] +fn test_split_obs_lines_within_combined_with_number() { + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + at.touch("file"); + + scene + .ucmd() + .args(&["-3dxen", "4", "file"]) + .fails() + .code_is(1) + .stderr_contains("split: cannot split in more than one way\n"); + scene + .ucmd() + .args(&["-dxe30n", "4", "file"]) + .fails() + .code_is(1) + .stderr_contains("split: cannot split in more than one way\n"); +} + #[test] fn test_split_invalid_bytes_size() { new_ucmd!() From 70dd8eb8dcdfffa7b69a22bc9a3162408d912f84 Mon Sep 17 00:00:00 2001 From: zhitkoff Date: Sat, 26 Aug 2023 13:22:36 -0400 Subject: [PATCH 010/208] split: updates to target correct GNU coreutils release --- util/build-gnu.sh | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/util/build-gnu.sh b/util/build-gnu.sh index 587cf858096..f6be73ff470 100755 --- a/util/build-gnu.sh +++ b/util/build-gnu.sh @@ -32,10 +32,16 @@ fi ### +release_tag_GNU="v9.3" + if test ! -d "${path_GNU}"; then echo "Could not find GNU coreutils (expected at '${path_GNU}')" echo "Run the following to download into the expected path:" echo "git clone --recurse-submodules https://github.com/coreutils/coreutils.git \"${path_GNU}\"" + echo "After downloading GNU coreutils to \"${path_GNU}\" run the following commands to cheout latest release tag" + echo "cd \"${path_GNU}\"" + echo "git fetch --all --tags" + echo "git checkout tags/\"${release_tag_GNU}\"" exit 1 fi @@ -152,17 +158,8 @@ sed -i -e '/tests\/misc\/invalid-opt.pl/ D' \ sed -i -e '/tests\/misc\/seq-precision.sh/ D' \ Makefile -# TODO: many files and some directories modified with 'sed' in the sections below either no longer exist or have been moved -# TODO: Might need to review and updated sections below -# TODO: As a result this script will fail when executed normally as 'bash util/build-gnu.hs' due to the 'set -e' set at the beginning -# TODO: The rest of the 'sed' commands after first failure in this scenario will not be executed as bash will exit on first error -# TODO: However, the behaviour might be different when running it via GitHub actions (GnuTests) -# TODO: For now, when running in local a workaround would be to comment out the 'set -e' at the beginning of the file - # printf doesn't limit the values used in its arg, so this produced ~2GB of output -# TODO: this is the first one to likely fail as tests/misc/printf.sh does not exist/have been moved sed -i '/INT_OFLOW/ D' tests/misc/printf.sh -# TODO: all commands below might not be executed # Use the system coreutils where the test fails due to error in a util that is not the one being tested sed -i 's|stat|/usr/bin/stat|' tests/touch/60-seconds.sh tests/misc/sort-compress-proc.sh From eac08f72c2ef97a7d7206fb3f98fc28a447172be Mon Sep 17 00:00:00 2001 From: zhitkoff Date: Sat, 26 Aug 2023 13:24:08 -0400 Subject: [PATCH 011/208] split: double quotes --- util/build-gnu.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/build-gnu.sh b/util/build-gnu.sh index f6be73ff470..74ec2967066 100755 --- a/util/build-gnu.sh +++ b/util/build-gnu.sh @@ -41,7 +41,7 @@ if test ! -d "${path_GNU}"; then echo "After downloading GNU coreutils to \"${path_GNU}\" run the following commands to cheout latest release tag" echo "cd \"${path_GNU}\"" echo "git fetch --all --tags" - echo "git checkout tags/\"${release_tag_GNU}\"" + echo "git checkout tags/${release_tag_GNU}" exit 1 fi From 4a4759c43c7cd10c81e53f9e352cd9f8f2a5fb63 Mon Sep 17 00:00:00 2001 From: zhitkoff Date: Sat, 26 Aug 2023 13:44:34 -0400 Subject: [PATCH 012/208] split: updated to SYSTEM_TIMEOUT in a few more places --- util/build-gnu.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/build-gnu.sh b/util/build-gnu.sh index 74ec2967066..4e196debe5c 100755 --- a/util/build-gnu.sh +++ b/util/build-gnu.sh @@ -173,10 +173,10 @@ sed -i 's|touch |/usr/bin/touch |' tests/cp/reflink-perm.sh tests/ls/block-size. sed -i 's|ln -|/usr/bin/ln -|' tests/cp/link-deref.sh sed -i 's|cp |/usr/bin/cp |' tests/mv/hard-2.sh sed -i 's|paste |/usr/bin/paste |' tests/misc/od-endian.sh -sed -i 's|timeout |/usr/bin/timeout |' tests/tail-2/follow-stdin.sh +sed -i 's|timeout |'"${SYSTEM_TIMEOUT}"' |' tests/tail-2/follow-stdin.sh # Add specific timeout to tests that currently hang to limit time spent waiting -sed -i 's|\(^\s*\)seq \$|\1/usr/bin/timeout 0.1 seq \$|' tests/misc/seq-precision.sh tests/misc/seq-long-double.sh +sed -i 's|\(^\s*\)seq \$|\1'"${SYSTEM_TIMEOUT}"' 0.1 seq \$|' tests/misc/seq-precision.sh tests/misc/seq-long-double.sh # Remove dup of /usr/bin/ when executed several times grep -rlE '/usr/bin/\s?/usr/bin' init.cfg tests/* | xargs --no-run-if-empty sed -Ei 's|/usr/bin/\s?/usr/bin/|/usr/bin/|g' From a384973b1a7e40435e165ab7a65a59ab26da7fd8 Mon Sep 17 00:00:00 2001 From: zhitkoff Date: Sat, 26 Aug 2023 13:51:21 -0400 Subject: [PATCH 013/208] split: remove dup for /usr/local/bin when executed several times --- util/build-gnu.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/util/build-gnu.sh b/util/build-gnu.sh index 4e196debe5c..ce15dd3f8e5 100755 --- a/util/build-gnu.sh +++ b/util/build-gnu.sh @@ -180,6 +180,7 @@ sed -i 's|\(^\s*\)seq \$|\1'"${SYSTEM_TIMEOUT}"' 0.1 seq \$|' tests/misc/seq-pre # Remove dup of /usr/bin/ when executed several times grep -rlE '/usr/bin/\s?/usr/bin' init.cfg tests/* | xargs --no-run-if-empty sed -Ei 's|/usr/bin/\s?/usr/bin/|/usr/bin/|g' +grep -rlE '/usr/local/bin/\s?/usr/local/bin' init.cfg tests/* | xargs --no-run-if-empty sed -Ei 's|/usr/local/bin/\s?/usr/local/bin/|/usr/local/bin/|g' #### Adjust tests to make them work with Rust/coreutils # in some cases, what we are doing in rust/coreutils is good (or better) From 0edba89b55ba51d974c90645079454f128c2ae6b Mon Sep 17 00:00:00 2001 From: zhitkoff Date: Sat, 26 Aug 2023 13:53:36 -0400 Subject: [PATCH 014/208] split: comments --- util/build-gnu.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/build-gnu.sh b/util/build-gnu.sh index ce15dd3f8e5..57764ed5076 100755 --- a/util/build-gnu.sh +++ b/util/build-gnu.sh @@ -178,7 +178,7 @@ sed -i 's|timeout |'"${SYSTEM_TIMEOUT}"' |' tests/tail-2/follow-stdin.sh # Add specific timeout to tests that currently hang to limit time spent waiting sed -i 's|\(^\s*\)seq \$|\1'"${SYSTEM_TIMEOUT}"' 0.1 seq \$|' tests/misc/seq-precision.sh tests/misc/seq-long-double.sh -# Remove dup of /usr/bin/ when executed several times +# Remove dup of /usr/bin/ and /usr/local/bin/ when executed several times grep -rlE '/usr/bin/\s?/usr/bin' init.cfg tests/* | xargs --no-run-if-empty sed -Ei 's|/usr/bin/\s?/usr/bin/|/usr/bin/|g' grep -rlE '/usr/local/bin/\s?/usr/local/bin' init.cfg tests/* | xargs --no-run-if-empty sed -Ei 's|/usr/local/bin/\s?/usr/local/bin/|/usr/local/bin/|g' From 4623575a6641bba9c4b45303d28386f7e92cc2b1 Mon Sep 17 00:00:00 2001 From: Terts Diepraam Date: Mon, 28 Aug 2023 22:51:45 +0200 Subject: [PATCH 015/208] echo: fix wrapping behavior of octal sequences --- src/uu/echo/src/echo.rs | 109 ++++++++++++++++++------------------- tests/by-util/test_echo.rs | 17 ++++++ 2 files changed, 71 insertions(+), 55 deletions(-) diff --git a/src/uu/echo/src/echo.rs b/src/uu/echo/src/echo.rs index cd9467714aa..4aba703a17d 100644 --- a/src/uu/echo/src/echo.rs +++ b/src/uu/echo/src/echo.rs @@ -21,73 +21,72 @@ mod options { pub const DISABLE_BACKSLASH_ESCAPE: &str = "disable_backslash_escape"; } -fn parse_code( - input: &mut Peekable, - base: u32, - max_digits: u32, - bits_per_digit: u32, -) -> Option { - let mut ret = 0x8000_0000; - for _ in 0..max_digits { - match input.peek().and_then(|c| c.to_digit(base)) { - Some(n) => ret = (ret << bits_per_digit) | n, +/// Parse the numeric part of the `\xHHH` and `\0NNN` escape sequences +fn parse_code(input: &mut Peekable, base: u8, max_digits: u32) -> Option { + // All arithmetic on `ret` needs to be wrapping, because octal input can + // take 3 digits, which is 9 bits, and therefore more than what fits in a + // `u8`. GNU just seems to wrap these values. + // Note that if we instead make `ret` a `u32` and use `char::from_u32` will + // yield incorrect results because it will interpret values larger than + // `u8::MAX` as unicode. + let mut ret = input.peek().and_then(|c| c.to_digit(base as u32))? as u8; + + // We can safely ifgnore the None case because we just peeked it. + let _ = input.next(); + + for _ in 1..max_digits { + match input.peek().and_then(|c| c.to_digit(base as u32)) { + Some(n) => ret = ret.wrapping_mul(base).wrapping_add(n as u8), None => break, } - input.next(); + // We can safely ifgnore the None case because we just peeked it. + let _ = input.next(); } - std::char::from_u32(ret) + + Some(ret.into()) } fn print_escaped(input: &str, mut output: impl Write) -> io::Result { - let mut should_stop = false; - - let mut buffer = ['\\'; 2]; - - // TODO `cargo +nightly clippy` complains that `.peek()` is never - // called on `iter`. However, `peek()` is called inside the - // `parse_code()` function that borrows `iter`. let mut iter = input.chars().peekable(); - while let Some(mut c) = iter.next() { - let mut start = 1; - - if c == '\\' { - if let Some(next) = iter.next() { - c = match next { - '\\' => '\\', - 'a' => '\x07', - 'b' => '\x08', - 'c' => { - should_stop = true; - break; - } - 'e' => '\x1b', - 'f' => '\x0c', - 'n' => '\n', - 'r' => '\r', - 't' => '\t', - 'v' => '\x0b', - 'x' => parse_code(&mut iter, 16, 2, 4).unwrap_or_else(|| { - start = 0; - next - }), - '0' => parse_code(&mut iter, 8, 3, 3).unwrap_or('\0'), - _ => { - start = 0; - next - } - }; - } + while let Some(c) = iter.next() { + if c != '\\' { + write!(output, "{c}")?; + continue; } - buffer[1] = c; - - // because printing char slices is apparently not available in the standard library - for ch in &buffer[start..] { - write!(output, "{ch}")?; + if let Some(next) = iter.next() { + let unescaped = match next { + '\\' => '\\', + 'a' => '\x07', + 'b' => '\x08', + 'c' => return Ok(true), + 'e' => '\x1b', + 'f' => '\x0c', + 'n' => '\n', + 'r' => '\r', + 't' => '\t', + 'v' => '\x0b', + 'x' => { + if let Some(c) = parse_code(&mut iter, 16, 2) { + c + } else { + write!(output, "\\")?; + 'x' + } + } + '0' => parse_code(&mut iter, 8, 3).unwrap_or('\0'), + c => { + write!(output, "\\")?; + c + } + }; + write!(output, "{unescaped}")?; + } else { + write!(output, "\\")?; } } - Ok(should_stop) + Ok(false) } #[uucore::main] diff --git a/tests/by-util/test_echo.rs b/tests/by-util/test_echo.rs index 3a8e7f86b34..82137c715ee 100644 --- a/tests/by-util/test_echo.rs +++ b/tests/by-util/test_echo.rs @@ -236,3 +236,20 @@ fn test_hyphen_values_between() { .success() .stdout_is("dumdum dum dum dum -e dum\n"); } + +#[test] +fn wrapping_octal() { + // Some odd behavior of GNU. Values of \0400 and greater do not fit in the + // u8 that we write to stdout. So we test that it wraps: + // + // We give it this input: + // \o501 = 1_0100_0001 (yes, **9** bits) + // This should be wrapped into: + // \o101 = 'A' = 0100_0001, + // because we only write a single character + new_ucmd!() + .arg("-e") + .arg("\\0501") + .succeeds() + .stdout_is("A\n"); +} From 2f35989ac34cda92b65328d325bdc615057e2dbe Mon Sep 17 00:00:00 2001 From: zhitkoff Date: Mon, 28 Aug 2023 18:26:48 -0400 Subject: [PATCH 016/208] split: comments --- src/uu/split/src/split.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/uu/split/src/split.rs b/src/uu/split/src/split.rs index 77c87e7f5c5..698c7818c03 100644 --- a/src/uu/split/src/split.rs +++ b/src/uu/split/src/split.rs @@ -108,7 +108,6 @@ fn handle_obsolete(args: &[String]) -> (Vec, Option) { v.push(arg.to_owned()); } } - // println!("{:#?} , {:#?}", v, obs_lines); (v, obs_lines) } @@ -465,8 +464,7 @@ impl Strategy { // Check that the user is not specifying more than one strategy. // // Note: right now, this exact behavior cannot be handled by - // `ArgGroup` since `ArgGroup` considers a default value `Arg` - // as "defined". + // overrides_with_all() due to obsolete lines value option match ( obs_lines, matches.value_source(OPT_LINES) == Some(ValueSource::CommandLine), From e79753c1cf25db25104e853e52e7f687dd776cfb Mon Sep 17 00:00:00 2001 From: zhitkoff Date: Tue, 29 Aug 2023 13:58:26 -0400 Subject: [PATCH 017/208] split: refactor handle_obsolete() function --- src/uu/split/src/split.rs | 71 +++++++++++++++++++++------------------ 1 file changed, 39 insertions(+), 32 deletions(-) diff --git a/src/uu/split/src/split.rs b/src/uu/split/src/split.rs index 698c7818c03..876d04606f0 100644 --- a/src/uu/split/src/split.rs +++ b/src/uu/split/src/split.rs @@ -72,43 +72,50 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { /// `split -x300e -22 file` would mean `split -x -e -l 22 file` (last obsolete lines option wins) /// following GNU `split` behavior fn handle_obsolete(args: &[String]) -> (Vec, Option) { - let mut v: Vec = vec![]; let mut obs_lines = None; - for arg in args.iter() { - let slice = &arg; - if slice.starts_with('-') && !slice.starts_with("--") { - // start of the short option string - // extract numeric part and filter it out - let mut obs_lines_extracted: Vec = vec![]; - let filtered_slice: Vec = slice - .chars() - .filter(|c| { - if c.is_ascii_digit() { - obs_lines_extracted.push(*c); - false + let filtered_args = args + .iter() + .filter_map(|slice| { + if slice.starts_with('-') && !slice.starts_with("--") { + // start of the short option string + // extract numeric part and filter it out + let mut obs_lines_extracted: Vec = vec![]; + let filtered_slice: Vec = slice + .chars() + .filter(|c| { + if c.is_ascii_digit() { + obs_lines_extracted.push(*c); + false + } else { + true + } + }) + .collect(); + + if obs_lines_extracted.is_empty() { + // no obsolete lines value found/extracted + Some(slice.to_owned()) + } else { + // obsolete lines value was extracted + obs_lines = Some(obs_lines_extracted.iter().collect()); + if filtered_slice.get(1).is_some() { + // there were some short options in front of or after obsolete lines value + // i.e. '-xd100' or '-100de' or similar, which after extraction of obsolete lines value + // would look like '-xd' or '-de' or similar + // preserve it + Some(filtered_slice.iter().collect()) } else { - true + None } - }) - .collect(); - - if filtered_slice.get(1).is_some() { - // there were some short options in front of obsolete lines number - // i.e. '-xd100' or similar + } + } else { + // not a short option // preserve it - v.push(filtered_slice.iter().collect()); - } - if !obs_lines_extracted.is_empty() { - // obsolete lines value was extracted - obs_lines = Some(obs_lines_extracted.iter().collect()); + Some(slice.to_owned()) } - } else { - // not a short option - // preserve it - v.push(arg.to_owned()); - } - } - (v, obs_lines) + }) + .collect(); + (filtered_args, obs_lines) } pub fn uu_app() -> Command { From 15c7170d20c532889ac8f3470c5d59bc12d10f2a Mon Sep 17 00:00:00 2001 From: zhitkoff Date: Tue, 29 Aug 2023 15:49:47 -0400 Subject: [PATCH 018/208] split: fix for GNU Tests regression + tests --- src/uu/split/src/split.rs | 15 ++++++--- tests/by-util/test_split.rs | 65 +++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 4 deletions(-) diff --git a/src/uu/split/src/split.rs b/src/uu/split/src/split.rs index 876d04606f0..dae24d36b7c 100644 --- a/src/uu/split/src/split.rs +++ b/src/uu/split/src/split.rs @@ -76,8 +76,16 @@ fn handle_obsolete(args: &[String]) -> (Vec, Option) { let filtered_args = args .iter() .filter_map(|slice| { - if slice.starts_with('-') && !slice.starts_with("--") { + if slice.starts_with('-') + && !slice.starts_with("--") + && !slice.starts_with("-a") + && !slice.starts_with("-b") + && !slice.starts_with("-C") + && !slice.starts_with("-l") + && !slice.starts_with("-n") + { // start of the short option string + // that can have obsolete lines option value in it // extract numeric part and filter it out let mut obs_lines_extracted: Vec = vec![]; let filtered_slice: Vec = slice @@ -102,15 +110,14 @@ fn handle_obsolete(args: &[String]) -> (Vec, Option) { // there were some short options in front of or after obsolete lines value // i.e. '-xd100' or '-100de' or similar, which after extraction of obsolete lines value // would look like '-xd' or '-de' or similar - // preserve it Some(filtered_slice.iter().collect()) } else { None } } } else { - // not a short option - // preserve it + // either not a short option + // or a short option that cannot have obsolete lines value in it Some(slice.to_owned()) } }) diff --git a/tests/by-util/test_split.rs b/tests/by-util/test_split.rs index aabfcbe90da..9fba2177e40 100644 --- a/tests/by-util/test_split.rs +++ b/tests/by-util/test_split.rs @@ -170,6 +170,22 @@ fn test_split_str_prefixed_chunks_by_bytes() { assert_eq!(glob.collate(), at.read_bytes(name)); } +// Test short bytes option concatenated with value +#[test] +fn test_split_by_bytes_short_concatenated_with_value() { + let (at, mut ucmd) = at_and_ucmd!(); + let name = "split_by_bytes_short_concatenated_with_value"; + RandomFile::new(&at, name).add_bytes(10000); + ucmd.args(&["-b1000", name]).succeeds(); + + let glob = Glob::new(&at, ".", r"x[[:alpha:]][[:alpha:]]$"); + assert_eq!(glob.count(), 10); + for filename in glob.collect() { + assert_eq!(glob.directory.metadata(&filename).len(), 1000); + } + assert_eq!(glob.collate(), at.read_bytes(name)); +} + // This is designed to test what happens when the desired part size is not a // multiple of the buffer size and we hopefully don't overshoot the desired part // size. @@ -326,6 +342,19 @@ fn test_split_lines_number() { .stderr_only("split: invalid number of lines: 'file'\n"); } +// Test short lines option with value concatenated +#[test] +fn test_split_lines_short_concatenated_with_value() { + let (at, mut ucmd) = at_and_ucmd!(); + let name = "split_num_prefixed_chunks_by_lines"; + RandomFile::new(&at, name).add_lines(10000); + ucmd.args(&["-l1000", name]).succeeds(); + + let glob = Glob::new(&at, ".", r"x[[:alpha:]][[:alpha:]]$"); + assert_eq!(glob.count(), 10); + assert_eq!(glob.collate(), at.read_bytes(name)); +} + /// Test for obsolete lines option standalone #[test] fn test_split_obs_lines_standalone() { @@ -692,6 +721,19 @@ fn test_invalid_suffix_length() { .stderr_contains("invalid suffix length: 'xyz'"); } +// Test short suffix length option with value concatenated +#[test] +fn test_split_suffix_length_short_concatenated_with_value() { + let (at, mut ucmd) = at_and_ucmd!(); + let name = "split_num_prefixed_chunks_by_lines"; + RandomFile::new(&at, name).add_lines(10000); + ucmd.args(&["-a4", name]).succeeds(); + + let glob = Glob::new(&at, ".", r"x[[:alpha:]][[:alpha:]][[:alpha:]][[:alpha:]]$"); + assert_eq!(glob.count(), 10); + assert_eq!(glob.collate(), at.read_bytes(name)); +} + #[test] fn test_include_newlines() { let (at, mut ucmd) = at_and_ucmd!(); @@ -710,6 +752,19 @@ fn test_include_newlines() { assert_eq!(s, "5\n"); } +// Test short number of chunks option concatenated with value +#[test] +fn test_split_number_chunks_short_concatenated_with_value() { + let (at, mut ucmd) = at_and_ucmd!(); + ucmd.args(&["-n3", "threebytes.txt"]) + .succeeds() + .no_stdout() + .no_stderr(); + assert_eq!(at.read("xaa"), "a"); + assert_eq!(at.read("xab"), "b"); + assert_eq!(at.read("xac"), "c"); +} + #[test] fn test_allow_empty_files() { let (at, mut ucmd) = at_and_ucmd!(); @@ -784,6 +839,16 @@ fn test_line_bytes() { assert_eq!(at.read("xad"), "ee\n"); } +#[test] +fn test_line_bytes_concatenated_with_value() { + let (at, mut ucmd) = at_and_ucmd!(); + ucmd.args(&["-C8", "letters.txt"]).succeeds(); + assert_eq!(at.read("xaa"), "aaaaaaaa"); + assert_eq!(at.read("xab"), "a\nbbbb\n"); + assert_eq!(at.read("xac"), "cccc\ndd\n"); + assert_eq!(at.read("xad"), "ee\n"); +} + #[test] fn test_line_bytes_no_final_newline() { let (at, mut ucmd) = at_and_ucmd!(); From 1ad10dd371cfe341993c23d302c7a002a85953ae Mon Sep 17 00:00:00 2001 From: Terts Diepraam Date: Tue, 29 Aug 2023 21:54:19 +0200 Subject: [PATCH 019/208] echo: add support for \NNN octal escape sequence --- src/uu/echo/src/echo.rs | 10 ++++++++++ tests/by-util/test_echo.rs | 27 +++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/src/uu/echo/src/echo.rs b/src/uu/echo/src/echo.rs index 4aba703a17d..565166842db 100644 --- a/src/uu/echo/src/echo.rs +++ b/src/uu/echo/src/echo.rs @@ -54,6 +54,16 @@ fn print_escaped(input: &str, mut output: impl Write) -> io::Result { continue; } + // This is for the \NNN syntax for octal sequences. + // Note that '0' is intentionally omitted because that + // would be the \0NNN syntax. + if let Some('1'..='8') = iter.peek() { + if let Some(parsed) = parse_code(&mut iter, 8, 3) { + write!(output, "{parsed}")?; + continue; + } + } + if let Some(next) = iter.next() { let unescaped = match next { '\\' => '\\', diff --git a/tests/by-util/test_echo.rs b/tests/by-util/test_echo.rs index 82137c715ee..7de963973a2 100644 --- a/tests/by-util/test_echo.rs +++ b/tests/by-util/test_echo.rs @@ -253,3 +253,30 @@ fn wrapping_octal() { .succeeds() .stdout_is("A\n"); } + +#[test] +fn old_octal_syntax() { + new_ucmd!() + .arg("-e") + .arg("\\1foo") + .succeeds() + .stdout_is("\x01foo\n"); + + new_ucmd!() + .arg("-e") + .arg("\\43foo") + .succeeds() + .stdout_is("#foo\n"); + + new_ucmd!() + .arg("-e") + .arg("\\101foo") + .succeeds() + .stdout_is("Afoo\n"); + + new_ucmd!() + .arg("-e") + .arg("\\1011") + .succeeds() + .stdout_is("A1\n"); +} From 7f905a3b8d6d08c271d71b25dcba3d9559f83959 Mon Sep 17 00:00:00 2001 From: zhitkoff Date: Tue, 29 Aug 2023 16:35:00 -0400 Subject: [PATCH 020/208] split: edge case for obs lines within combined shorts + test --- src/uu/split/src/split.rs | 9 ++++++++- tests/by-util/test_split.rs | 21 +++++++++++++++++---- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/uu/split/src/split.rs b/src/uu/split/src/split.rs index dae24d36b7c..afe635456bc 100644 --- a/src/uu/split/src/split.rs +++ b/src/uu/split/src/split.rs @@ -88,13 +88,20 @@ fn handle_obsolete(args: &[String]) -> (Vec, Option) { // that can have obsolete lines option value in it // extract numeric part and filter it out let mut obs_lines_extracted: Vec = vec![]; + let mut obs_lines_end_reached = false; let filtered_slice: Vec = slice .chars() .filter(|c| { - if c.is_ascii_digit() { + // To correctly process scenario like '-x200a4' + // we need to stop extracting digits once alphabetic character is encountered + // after we already have something in obs_lines_extracted + if c.is_ascii_digit() && !obs_lines_end_reached { obs_lines_extracted.push(*c); false } else { + if !obs_lines_extracted.is_empty() { + obs_lines_end_reached = true; + } true } }) diff --git a/tests/by-util/test_split.rs b/tests/by-util/test_split.rs index 9fba2177e40..3280fff675d 100644 --- a/tests/by-util/test_split.rs +++ b/tests/by-util/test_split.rs @@ -170,7 +170,7 @@ fn test_split_str_prefixed_chunks_by_bytes() { assert_eq!(glob.collate(), at.read_bytes(name)); } -// Test short bytes option concatenated with value +/// Test short bytes option concatenated with value #[test] fn test_split_by_bytes_short_concatenated_with_value() { let (at, mut ucmd) = at_and_ucmd!(); @@ -342,7 +342,7 @@ fn test_split_lines_number() { .stderr_only("split: invalid number of lines: 'file'\n"); } -// Test short lines option with value concatenated +/// Test short lines option with value concatenated #[test] fn test_split_lines_short_concatenated_with_value() { let (at, mut ucmd) = at_and_ucmd!(); @@ -401,6 +401,19 @@ fn test_split_obs_lines_within_combined_shorts() { assert_eq!(glob.collate(), at.read_bytes(name)) } +/// Test for obsolete lines option as part of combined short options with tailing suffix length with value +#[test] +fn test_split_obs_lines_within_combined_shorts_tailing_suffix_length() { + let (at, mut ucmd) = at_and_ucmd!(); + let name = "obs-lines-combined-shorts-tailing-suffix-length"; + RandomFile::new(&at, name).add_lines(1000); + ucmd.args(&["-d200a4", name]).succeeds(); + + let glob = Glob::new(&at, ".", r"x\d\d\d\d$"); + assert_eq!(glob.count(), 5); + assert_eq!(glob.collate(), at.read_bytes(name)); +} + /// Test for obsolete lines option starts as part of combined short options #[test] fn test_split_obs_lines_starts_combined_shorts() { @@ -721,7 +734,7 @@ fn test_invalid_suffix_length() { .stderr_contains("invalid suffix length: 'xyz'"); } -// Test short suffix length option with value concatenated +/// Test short suffix length option with value concatenated #[test] fn test_split_suffix_length_short_concatenated_with_value() { let (at, mut ucmd) = at_and_ucmd!(); @@ -752,7 +765,7 @@ fn test_include_newlines() { assert_eq!(s, "5\n"); } -// Test short number of chunks option concatenated with value +/// Test short number of chunks option concatenated with value #[test] fn test_split_number_chunks_short_concatenated_with_value() { let (at, mut ucmd) = at_and_ucmd!(); From b2ebe6a1d180fca68b5891ecf542f956e7df8331 Mon Sep 17 00:00:00 2001 From: zhitkoff Date: Wed, 30 Aug 2023 11:15:11 -0400 Subject: [PATCH 021/208] build-gnu.sh: target GNU release v9.4 --- util/build-gnu.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/build-gnu.sh b/util/build-gnu.sh index a6d8de29ef6..21a231bc01d 100755 --- a/util/build-gnu.sh +++ b/util/build-gnu.sh @@ -32,7 +32,7 @@ fi ### -release_tag_GNU="v9.3" +release_tag_GNU="v9.4" if test ! -d "${path_GNU}"; then echo "Could not find GNU coreutils (expected at '${path_GNU}')" From 6f37b4b4cfcbe45725d91d63dadabd1aa9873a2b Mon Sep 17 00:00:00 2001 From: zhitkoff Date: Wed, 30 Aug 2023 19:29:57 -0400 Subject: [PATCH 022/208] split: hyphenated values + tests --- src/uu/split/src/split.rs | 50 ++++++++++++++++++++--- tests/by-util/test_split.rs | 81 ++++++++++++++++++++++++++++++++++--- 2 files changed, 120 insertions(+), 11 deletions(-) diff --git a/src/uu/split/src/split.rs b/src/uu/split/src/split.rs index afe635456bc..7834dfe6821 100644 --- a/src/uu/split/src/split.rs +++ b/src/uu/split/src/split.rs @@ -73,11 +73,18 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { /// following GNU `split` behavior fn handle_obsolete(args: &[String]) -> (Vec, Option) { let mut obs_lines = None; + let mut preceding_long_opt_req_value = false; + let mut preceding_short_opt_req_value = false; let filtered_args = args .iter() .filter_map(|slice| { + let filter: Option; + // check if the slice is a true short option (and not hyphen prefixed value of an option) + // and if so, a short option that can contain obsolete lines value if slice.starts_with('-') && !slice.starts_with("--") + && !preceding_long_opt_req_value + && !preceding_short_opt_req_value && !slice.starts_with("-a") && !slice.starts_with("-b") && !slice.starts_with("-C") @@ -109,7 +116,7 @@ fn handle_obsolete(args: &[String]) -> (Vec, Option) { if obs_lines_extracted.is_empty() { // no obsolete lines value found/extracted - Some(slice.to_owned()) + filter = Some(slice.to_owned()); } else { // obsolete lines value was extracted obs_lines = Some(obs_lines_extracted.iter().collect()); @@ -117,16 +124,41 @@ fn handle_obsolete(args: &[String]) -> (Vec, Option) { // there were some short options in front of or after obsolete lines value // i.e. '-xd100' or '-100de' or similar, which after extraction of obsolete lines value // would look like '-xd' or '-de' or similar - Some(filtered_slice.iter().collect()) + filter = Some(filtered_slice.iter().collect()); } else { - None + filter = None; } } } else { // either not a short option // or a short option that cannot have obsolete lines value in it - Some(slice.to_owned()) + filter = Some(slice.to_owned()); } + // capture if current slice is a preceding long option that requires value and does not use '=' to assign that value + // following slice should be treaded as value for this option + // even if it starts with '-' (which would be treated as hyphen prefixed value) + if slice.starts_with("--") { + preceding_long_opt_req_value = &slice[2..] == OPT_BYTES + || &slice[2..] == OPT_LINE_BYTES + || &slice[2..] == OPT_LINES + || &slice[2..] == OPT_ADDITIONAL_SUFFIX + || &slice[2..] == OPT_FILTER + || &slice[2..] == OPT_NUMBER + || &slice[2..] == OPT_SUFFIX_LENGTH; + } + // capture if current slice is a preceding short option that requires value and does not have value in the same slice (value separated by whitespace) + // following slice should be treaded as value for this option + // even if it starts with '-' (which would be treated as hyphen prefixed value) + preceding_short_opt_req_value = + slice == "-b" || slice == "-C" || slice == "-l" || slice == "-n" || slice == "-a"; + // slice is a value + // reset preceding option flags + if !slice.starts_with('-') { + preceding_short_opt_req_value = false; + preceding_long_opt_req_value = false; + } + // return filter + filter }) .collect(); (filtered_args, obs_lines) @@ -144,6 +176,7 @@ pub fn uu_app() -> Command { Arg::new(OPT_BYTES) .short('b') .long(OPT_BYTES) + .allow_hyphen_values(true) .value_name("SIZE") .help("put SIZE bytes per output file"), ) @@ -151,14 +184,15 @@ pub fn uu_app() -> Command { Arg::new(OPT_LINE_BYTES) .short('C') .long(OPT_LINE_BYTES) + .allow_hyphen_values(true) .value_name("SIZE") - .default_value("2") .help("put at most SIZE bytes of lines per output file"), ) .arg( Arg::new(OPT_LINES) .short('l') .long(OPT_LINES) + .allow_hyphen_values(true) .value_name("NUMBER") .default_value("1000") .help("put NUMBER lines/records per output file"), @@ -167,6 +201,7 @@ pub fn uu_app() -> Command { Arg::new(OPT_NUMBER) .short('n') .long(OPT_NUMBER) + .allow_hyphen_values(true) .value_name("CHUNKS") .help("generate CHUNKS output files; see explanation below"), ) @@ -174,6 +209,7 @@ pub fn uu_app() -> Command { .arg( Arg::new(OPT_ADDITIONAL_SUFFIX) .long(OPT_ADDITIONAL_SUFFIX) + .allow_hyphen_values(true) .value_name("SUFFIX") .default_value("") .help("additional SUFFIX to append to output file names"), @@ -181,6 +217,7 @@ pub fn uu_app() -> Command { .arg( Arg::new(OPT_FILTER) .long(OPT_FILTER) + .allow_hyphen_values(true) .value_name("COMMAND") .value_hint(clap::ValueHint::CommandName) .help( @@ -250,9 +287,10 @@ pub fn uu_app() -> Command { Arg::new(OPT_SUFFIX_LENGTH) .short('a') .long(OPT_SUFFIX_LENGTH) + .allow_hyphen_values(true) .value_name("N") .default_value(OPT_DEFAULT_SUFFIX_LENGTH) - .help("use suffixes of fixed length N. 0 implies dynamic length."), + .help("use suffixes of fixed length N. 0 implies dynamic length, starting with 2"), ) .arg( Arg::new(OPT_VERBOSE) diff --git a/tests/by-util/test_split.rs b/tests/by-util/test_split.rs index 3280fff675d..466dabda9ab 100644 --- a/tests/by-util/test_split.rs +++ b/tests/by-util/test_split.rs @@ -254,6 +254,18 @@ fn test_additional_suffix_no_slash() { .usage_error("invalid suffix 'a/b', contains directory separator"); } +#[test] +fn test_split_additional_suffix_hyphen_value() { + let (at, mut ucmd) = at_and_ucmd!(); + let name = "split_additional_suffix"; + RandomFile::new(&at, name).add_lines(2000); + ucmd.args(&["--additional-suffix", "-300", name]).succeeds(); + + let glob = Glob::new(&at, ".", r"x[[:alpha:]][[:alpha:]]-300$"); + assert_eq!(glob.count(), 2); + assert_eq!(glob.collate(), at.read_bytes(name)); +} + // note: the test_filter* tests below are unix-only // windows support has been waived for now because of the difficulty of getting // the `cmd` call right @@ -436,9 +448,9 @@ fn test_split_obs_lines_starts_combined_shorts() { /// Test for using both obsolete lines (standalone) option and short/long lines option simultaneously #[test] fn test_split_both_lines_and_obs_lines_standalone() { - // This test will ensure that if both lines option '-l' or '--lines' - // and obsolete lines option '-100' are used - // it fails + // This test will ensure that: + // if both lines option '-l' or '--lines' (with value) and obsolete lines option '-100' are used - it fails + // if standalone lines option is used incorrectly and treated as a hyphen prefixed value of other option - it fails let scene = TestScenario::new(util_name!()); let at = &scene.fixtures; at.touch("file"); @@ -455,18 +467,77 @@ fn test_split_both_lines_and_obs_lines_standalone() { .fails() .code_is(1) .stderr_contains("split: cannot split in more than one way\n"); +} + +/// Test for using obsolete lines option incorrectly, so it is treated as a hyphen prefixed value of other option +#[test] +fn test_split_obs_lines_as_other_option_value() { + // This test will ensure that: + // if obsolete lines option is used incorrectly and treated as a hyphen prefixed value of other option - it fails + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + at.touch("file"); + scene .ucmd() .args(&["--lines", "-200", "file"]) .fails() .code_is(1) - .stderr_contains("split: cannot split in more than one way\n"); + .stderr_contains("split: invalid number of lines: '-200'\n"); scene .ucmd() .args(&["-l", "-200", "file"]) .fails() .code_is(1) - .stderr_contains("split: cannot split in more than one way\n"); + .stderr_contains("split: invalid number of lines: '-200'\n"); + scene + .ucmd() + .args(&["-a", "-200", "file"]) + .fails() + .code_is(1) + .stderr_contains("split: invalid suffix length: '-200'\n"); + scene + .ucmd() + .args(&["--suffix-length", "-d200e", "file"]) + .fails() + .code_is(1) + .stderr_contains("split: invalid suffix length: '-d200e'\n"); + scene + .ucmd() + .args(&["-C", "-200", "file"]) + .fails() + .code_is(1) + .stderr_contains("split: invalid number of bytes: '-200'\n"); + scene + .ucmd() + .args(&["--line-bytes", "-x200a4", "file"]) + .fails() + .code_is(1) + .stderr_contains("split: invalid number of bytes: '-x200a4'\n"); + scene + .ucmd() + .args(&["-b", "-200", "file"]) + .fails() + .code_is(1) + .stderr_contains("split: invalid number of bytes: '-200'\n"); + scene + .ucmd() + .args(&["--bytes", "-200xd", "file"]) + .fails() + .code_is(1) + .stderr_contains("split: invalid number of bytes: '-200xd'\n"); + scene + .ucmd() + .args(&["-n", "-200", "file"]) + .fails() + .code_is(1) + .stderr_contains("split: invalid number of chunks: -200\n"); + scene + .ucmd() + .args(&["--number", "-e200", "file"]) + .fails() + .code_is(1) + .stderr_contains("split: invalid number of chunks: -e200\n"); } /// Test for using more than one obsolete lines option (standalone) From 843540d05fafe427ed34d7b8b546357d327d3df5 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Thu, 31 Aug 2023 09:21:59 +0200 Subject: [PATCH 023/208] fix a typo --- util/build-gnu.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/build-gnu.sh b/util/build-gnu.sh index 21a231bc01d..157265f61bf 100755 --- a/util/build-gnu.sh +++ b/util/build-gnu.sh @@ -38,7 +38,7 @@ if test ! -d "${path_GNU}"; then echo "Could not find GNU coreutils (expected at '${path_GNU}')" echo "Run the following to download into the expected path:" echo "git clone --recurse-submodules https://github.com/coreutils/coreutils.git \"${path_GNU}\"" - echo "After downloading GNU coreutils to \"${path_GNU}\" run the following commands to cheout latest release tag" + echo "After downloading GNU coreutils to \"${path_GNU}\" run the following commands to checkout latest release tag" echo "cd \"${path_GNU}\"" echo "git fetch --all --tags" echo "git checkout tags/${release_tag_GNU}" From 5bfe9b19ef775adf40c3e915a731611dbc4a6256 Mon Sep 17 00:00:00 2001 From: zhitkoff Date: Thu, 31 Aug 2023 14:46:56 -0400 Subject: [PATCH 024/208] split: avoid using `collect_lossy` + test for invalid UTF8 arguments --- src/uu/split/src/split.rs | 167 +++++++++++++++++++----------------- tests/by-util/test_split.rs | 45 ++++++++++ 2 files changed, 134 insertions(+), 78 deletions(-) diff --git a/src/uu/split/src/split.rs b/src/uu/split/src/split.rs index 7834dfe6821..68692d03c01 100644 --- a/src/uu/split/src/split.rs +++ b/src/uu/split/src/split.rs @@ -13,6 +13,7 @@ use crate::filenames::FilenameIterator; use crate::filenames::SuffixType; use clap::{crate_version, parser::ValueSource, Arg, ArgAction, ArgMatches, Command}; use std::env; +use std::ffi::OsString; use std::fmt; use std::fs::{metadata, File}; use std::io; @@ -52,9 +53,8 @@ const AFTER_HELP: &str = help_section!("after help", "split.md"); #[uucore::main] pub fn uumain(args: impl uucore::Args) -> UResult<()> { - let args = args.collect_lossy(); - - let (args, obs_lines) = handle_obsolete(&args[..]); + + let (args, obs_lines) = handle_obsolete(args); let matches = uu_app().try_get_matches_from(args)?; @@ -71,91 +71,102 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { /// `split -x300e file` would mean `split -x -l 300 -e file` /// `split -x300e -22 file` would mean `split -x -e -l 22 file` (last obsolete lines option wins) /// following GNU `split` behavior -fn handle_obsolete(args: &[String]) -> (Vec, Option) { +fn handle_obsolete(args: impl uucore::Args) -> (Vec, Option) { let mut obs_lines = None; let mut preceding_long_opt_req_value = false; let mut preceding_short_opt_req_value = false; let filtered_args = args - .iter() - .filter_map(|slice| { - let filter: Option; - // check if the slice is a true short option (and not hyphen prefixed value of an option) - // and if so, a short option that can contain obsolete lines value - if slice.starts_with('-') - && !slice.starts_with("--") - && !preceding_long_opt_req_value - && !preceding_short_opt_req_value - && !slice.starts_with("-a") - && !slice.starts_with("-b") - && !slice.starts_with("-C") - && !slice.starts_with("-l") - && !slice.starts_with("-n") - { - // start of the short option string - // that can have obsolete lines option value in it - // extract numeric part and filter it out - let mut obs_lines_extracted: Vec = vec![]; - let mut obs_lines_end_reached = false; - let filtered_slice: Vec = slice - .chars() - .filter(|c| { - // To correctly process scenario like '-x200a4' - // we need to stop extracting digits once alphabetic character is encountered - // after we already have something in obs_lines_extracted - if c.is_ascii_digit() && !obs_lines_end_reached { - obs_lines_extracted.push(*c); - false - } else { - if !obs_lines_extracted.is_empty() { - obs_lines_end_reached = true; + .filter_map(|os_slice| { + let filter: Option; + if let Some(slice) = os_slice.to_str() { + // check if the slice is a true short option (and not hyphen prefixed value of an option) + // and if so, a short option that can contain obsolete lines value + if slice.starts_with('-') + && !slice.starts_with("--") + && !preceding_long_opt_req_value + && !preceding_short_opt_req_value + && !slice.starts_with("-a") + && !slice.starts_with("-b") + && !slice.starts_with("-C") + && !slice.starts_with("-l") + && !slice.starts_with("-n") + { + // start of the short option string + // that can have obsolete lines option value in it + // extract numeric part and filter it out + let mut obs_lines_extracted: Vec = vec![]; + let mut obs_lines_end_reached = false; + let filtered_slice: Vec = slice + .chars() + .filter(|c| { + // To correctly process scenario like '-x200a4' + // we need to stop extracting digits once alphabetic character is encountered + // after we already have something in obs_lines_extracted + if c.is_ascii_digit() && !obs_lines_end_reached { + obs_lines_extracted.push(*c); + false + } else { + if !obs_lines_extracted.is_empty() { + obs_lines_end_reached = true; + } + true } - true - } - }) - .collect(); + }) + .collect(); - if obs_lines_extracted.is_empty() { - // no obsolete lines value found/extracted - filter = Some(slice.to_owned()); - } else { - // obsolete lines value was extracted - obs_lines = Some(obs_lines_extracted.iter().collect()); - if filtered_slice.get(1).is_some() { - // there were some short options in front of or after obsolete lines value - // i.e. '-xd100' or '-100de' or similar, which after extraction of obsolete lines value - // would look like '-xd' or '-de' or similar - filter = Some(filtered_slice.iter().collect()); + if obs_lines_extracted.is_empty() { + // no obsolete lines value found/extracted + filter = Some(OsString::from(slice)); } else { - filter = None; + // obsolete lines value was extracted + obs_lines = Some(obs_lines_extracted.iter().collect()); + if filtered_slice.get(1).is_some() { + // there were some short options in front of or after obsolete lines value + // i.e. '-xd100' or '-100de' or similar, which after extraction of obsolete lines value + // would look like '-xd' or '-de' or similar + let filtered_slice: String = filtered_slice.iter().collect(); + filter = Some(OsString::from(filtered_slice)); + } else { + filter = None; + } } + } else { + // either not a short option + // or a short option that cannot have obsolete lines value in it + filter = Some(OsString::from(slice)); + } + // capture if current slice is a preceding long option that requires value and does not use '=' to assign that value + // following slice should be treaded as value for this option + // even if it starts with '-' (which would be treated as hyphen prefixed value) + if slice.starts_with("--") { + preceding_long_opt_req_value = &slice[2..] == OPT_BYTES + || &slice[2..] == OPT_LINE_BYTES + || &slice[2..] == OPT_LINES + || &slice[2..] == OPT_ADDITIONAL_SUFFIX + || &slice[2..] == OPT_FILTER + || &slice[2..] == OPT_NUMBER + || &slice[2..] == OPT_SUFFIX_LENGTH; + } + // capture if current slice is a preceding short option that requires value and does not have value in the same slice (value separated by whitespace) + // following slice should be treaded as value for this option + // even if it starts with '-' (which would be treated as hyphen prefixed value) + preceding_short_opt_req_value = slice == "-b" + || slice == "-C" + || slice == "-l" + || slice == "-n" + || slice == "-a"; + // slice is a value + // reset preceding option flags + if !slice.starts_with('-') { + preceding_short_opt_req_value = false; + preceding_long_opt_req_value = false; } } else { - // either not a short option - // or a short option that cannot have obsolete lines value in it - filter = Some(slice.to_owned()); - } - // capture if current slice is a preceding long option that requires value and does not use '=' to assign that value - // following slice should be treaded as value for this option - // even if it starts with '-' (which would be treated as hyphen prefixed value) - if slice.starts_with("--") { - preceding_long_opt_req_value = &slice[2..] == OPT_BYTES - || &slice[2..] == OPT_LINE_BYTES - || &slice[2..] == OPT_LINES - || &slice[2..] == OPT_ADDITIONAL_SUFFIX - || &slice[2..] == OPT_FILTER - || &slice[2..] == OPT_NUMBER - || &slice[2..] == OPT_SUFFIX_LENGTH; - } - // capture if current slice is a preceding short option that requires value and does not have value in the same slice (value separated by whitespace) - // following slice should be treaded as value for this option - // even if it starts with '-' (which would be treated as hyphen prefixed value) - preceding_short_opt_req_value = - slice == "-b" || slice == "-C" || slice == "-l" || slice == "-n" || slice == "-a"; - // slice is a value - // reset preceding option flags - if !slice.starts_with('-') { - preceding_short_opt_req_value = false; - preceding_long_opt_req_value = false; + // Cannot cleanly convert os_slice to UTF-8 + // Do not process and return as-is + // This will cause failure later on, but we should not handle it here + // and let clap panic on invalid UTF-8 argument + filter = Some(os_slice); } // return filter filter diff --git a/tests/by-util/test_split.rs b/tests/by-util/test_split.rs index 466dabda9ab..d24d2cf54d9 100644 --- a/tests/by-util/test_split.rs +++ b/tests/by-util/test_split.rs @@ -9,6 +9,7 @@ use rand::{thread_rng, Rng, SeedableRng}; use regex::Regex; #[cfg(not(windows))] use std::env; +use std::ffi::OsStr; use std::path::Path; use std::{ fs::{read_dir, File}, @@ -1287,3 +1288,47 @@ fn test_split_invalid_input() { .no_stdout() .stderr_contains("split: invalid number of chunks: 0"); } + +/// Test if there are invalid (non UTF-8) in the arguments - unix +/// clap is expected to fail/panic +#[cfg(unix)] +#[test] +fn test_split_non_utf8_argument_unix() { + use std::os::unix::ffi::OsStrExt; + + let (at, mut ucmd) = at_and_ucmd!(); + let name = "test_split_non_utf8_argument"; + let opt = OsStr::from_bytes("--additional-suffix".as_bytes()); + RandomFile::new(&at, name).add_lines(2000); + // Here, the values 0x66 and 0x6f correspond to 'f' and 'o' + // respectively. The value 0x80 is a lone continuation byte, invalid + // in a UTF-8 sequence. + let opt_value = [0x66, 0x6f, 0x80, 0x6f]; + let opt_value = OsStr::from_bytes(&opt_value[..]); + let name = OsStr::from_bytes(name.as_bytes()); + ucmd.args(&[opt, opt_value, name]) + .fails() + .stderr_contains("error: invalid UTF-8 was detected in one or more arguments"); +} + +/// Test if there are invalid (non UTF-8) in the arguments - windows +/// clap is expected to fail/panic +#[cfg(windows)] +#[test] +fn test_split_non_utf8_argument_windows() { + use std::os::windows::prelude::*; + + let (at, mut ucmd) = at_and_ucmd!(); + let name = "test_split_non_utf8_argument"; + let opt = OsStr::from_bytes("--additional-suffix".as_bytes()); + RandomFile::new(&at, name).add_lines(2000); + // Here the values 0x0066 and 0x006f correspond to 'f' and 'o' + // respectively. The value 0xD800 is a lone surrogate half, invalid + // in a UTF-16 sequence. + let opt_value = [0x0066, 0x006f, 0xD800, 0x006f]; + let opt_value = OsString::from_wide(&opt_value[..]); + let name = OsStr::from_bytes(name.as_bytes()); + ucmd.args(&[opt, opt_value, name]) + .fails() + .stderr_contains("error: invalid UTF-8 was detected in one or more arguments"); +} From 271a108fa9e1ea75ed8c6e42c2a207a6da935c81 Mon Sep 17 00:00:00 2001 From: zhitkoff Date: Thu, 31 Aug 2023 15:37:42 -0400 Subject: [PATCH 025/208] split: formatting --- src/uu/split/src/split.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/uu/split/src/split.rs b/src/uu/split/src/split.rs index 68692d03c01..d76bfb2de74 100644 --- a/src/uu/split/src/split.rs +++ b/src/uu/split/src/split.rs @@ -53,7 +53,6 @@ const AFTER_HELP: &str = help_section!("after help", "split.md"); #[uucore::main] pub fn uumain(args: impl uucore::Args) -> UResult<()> { - let (args, obs_lines) = handle_obsolete(args); let matches = uu_app().try_get_matches_from(args)?; From d2812cbbc387b7dc3ad8f5f31863aad09ce1e6ad Mon Sep 17 00:00:00 2001 From: zhitkoff Date: Thu, 31 Aug 2023 16:04:44 -0400 Subject: [PATCH 026/208] split: disable windows test for invalid UTF8 --- tests/by-util/test_split.rs | 46 +++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/tests/by-util/test_split.rs b/tests/by-util/test_split.rs index d24d2cf54d9..aef9ea040ed 100644 --- a/tests/by-util/test_split.rs +++ b/tests/by-util/test_split.rs @@ -9,7 +9,6 @@ use rand::{thread_rng, Rng, SeedableRng}; use regex::Regex; #[cfg(not(windows))] use std::env; -use std::ffi::OsStr; use std::path::Path; use std::{ fs::{read_dir, File}, @@ -1294,6 +1293,7 @@ fn test_split_invalid_input() { #[cfg(unix)] #[test] fn test_split_non_utf8_argument_unix() { + use std::ffi::OsStr; use std::os::unix::ffi::OsStrExt; let (at, mut ucmd) = at_and_ucmd!(); @@ -1311,24 +1311,26 @@ fn test_split_non_utf8_argument_unix() { .stderr_contains("error: invalid UTF-8 was detected in one or more arguments"); } -/// Test if there are invalid (non UTF-8) in the arguments - windows -/// clap is expected to fail/panic -#[cfg(windows)] -#[test] -fn test_split_non_utf8_argument_windows() { - use std::os::windows::prelude::*; - - let (at, mut ucmd) = at_and_ucmd!(); - let name = "test_split_non_utf8_argument"; - let opt = OsStr::from_bytes("--additional-suffix".as_bytes()); - RandomFile::new(&at, name).add_lines(2000); - // Here the values 0x0066 and 0x006f correspond to 'f' and 'o' - // respectively. The value 0xD800 is a lone surrogate half, invalid - // in a UTF-16 sequence. - let opt_value = [0x0066, 0x006f, 0xD800, 0x006f]; - let opt_value = OsString::from_wide(&opt_value[..]); - let name = OsStr::from_bytes(name.as_bytes()); - ucmd.args(&[opt, opt_value, name]) - .fails() - .stderr_contains("error: invalid UTF-8 was detected in one or more arguments"); -} +// Test if there are invalid (non UTF-8) in the arguments - windows +// clap is expected to fail/panic +// comment it out for now +// #[cfg(windows)] +// #[test] +// fn test_split_non_utf8_argument_windows() { +// use std::ffi::OsString; +// use std::os::windows::prelude::*; + +// let (at, mut ucmd) = at_and_ucmd!(); +// let name = "test_split_non_utf8_argument"; +// let opt = OsStr::from_bytes("--additional-suffix".as_bytes()); +// RandomFile::new(&at, name).add_lines(2000); +// // Here the values 0x0066 and 0x006f correspond to 'f' and 'o' +// // respectively. The value 0xD800 is a lone surrogate half, invalid +// // in a UTF-16 sequence. +// let opt_value = [0x0066, 0x006f, 0xD800, 0x006f]; +// let opt_value = OsString::from_wide(&opt_value[..]); +// let name = OsStr::from_bytes(name.as_bytes()); +// ucmd.args(&[opt, opt_value, name]) +// .fails() +// .stderr_contains("error: invalid UTF-8 was detected in one or more arguments"); +// } From e597189be7555164fc7f57df0b03f1ae788901ea Mon Sep 17 00:00:00 2001 From: zhitkoff Date: Thu, 31 Aug 2023 20:48:44 -0400 Subject: [PATCH 027/208] split: fixed windows test for invalid unicode args --- tests/by-util/test_split.rs | 47 ++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/tests/by-util/test_split.rs b/tests/by-util/test_split.rs index aef9ea040ed..965f2733e22 100644 --- a/tests/by-util/test_split.rs +++ b/tests/by-util/test_split.rs @@ -1290,8 +1290,8 @@ fn test_split_invalid_input() { /// Test if there are invalid (non UTF-8) in the arguments - unix /// clap is expected to fail/panic -#[cfg(unix)] #[test] +#[cfg(unix)] fn test_split_non_utf8_argument_unix() { use std::ffi::OsStr; use std::os::unix::ffi::OsStrExt; @@ -1311,26 +1311,25 @@ fn test_split_non_utf8_argument_unix() { .stderr_contains("error: invalid UTF-8 was detected in one or more arguments"); } -// Test if there are invalid (non UTF-8) in the arguments - windows -// clap is expected to fail/panic -// comment it out for now -// #[cfg(windows)] -// #[test] -// fn test_split_non_utf8_argument_windows() { -// use std::ffi::OsString; -// use std::os::windows::prelude::*; - -// let (at, mut ucmd) = at_and_ucmd!(); -// let name = "test_split_non_utf8_argument"; -// let opt = OsStr::from_bytes("--additional-suffix".as_bytes()); -// RandomFile::new(&at, name).add_lines(2000); -// // Here the values 0x0066 and 0x006f correspond to 'f' and 'o' -// // respectively. The value 0xD800 is a lone surrogate half, invalid -// // in a UTF-16 sequence. -// let opt_value = [0x0066, 0x006f, 0xD800, 0x006f]; -// let opt_value = OsString::from_wide(&opt_value[..]); -// let name = OsStr::from_bytes(name.as_bytes()); -// ucmd.args(&[opt, opt_value, name]) -// .fails() -// .stderr_contains("error: invalid UTF-8 was detected in one or more arguments"); -// } +/// Test if there are invalid (non UTF-8) in the arguments - windows +/// clap is expected to fail/panic +#[test] +#[cfg(windows)] +fn test_split_non_utf8_argument_windows() { + use std::ffi::OsString; + use std::os::windows::ffi::OsStringExt; + + let (at, mut ucmd) = at_and_ucmd!(); + let name = "test_split_non_utf8_argument"; + let opt = OsString::from("--additional-suffix"); + RandomFile::new(&at, name).add_lines(2000); + // Here the values 0x0066 and 0x006f correspond to 'f' and 'o' + // respectively. The value 0xD800 is a lone surrogate half, invalid + // in a UTF-16 sequence. + let opt_value = [0x0066, 0x006f, 0xD800, 0x006f]; + let opt_value = OsString::from_wide(&opt_value[..]); + let name = OsString::from(name); + ucmd.args(&[opt, opt_value, name]) + .fails() + .stderr_contains("error: invalid UTF-8 was detected in one or more arguments"); +} From f0923094d21c34e32f77b55400f2063a446eff05 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Mon, 4 Sep 2023 07:18:18 +0200 Subject: [PATCH 028/208] Bump MSRV to 1.70 --- .clippy.toml | 2 +- .github/workflows/CICD.yml | 2 +- Cargo.toml | 2 +- README.md | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.clippy.toml b/.clippy.toml index bee70857bd1..814e40b6960 100644 --- a/.clippy.toml +++ b/.clippy.toml @@ -1,2 +1,2 @@ -msrv = "1.64.0" +msrv = "1.70.0" cognitive-complexity-threshold = 10 diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index fe72de11067..d387f566ffa 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -11,7 +11,7 @@ env: PROJECT_NAME: coreutils PROJECT_DESC: "Core universal (cross-platform) utilities" PROJECT_AUTH: "uutils" - RUST_MIN_SRV: "1.64.0" + RUST_MIN_SRV: "1.70.0" # * style job configuration STYLE_FAIL_ON_FAULT: true ## (bool) fail the build if a style job contains a fault (error or warning); may be overridden on a per-job basis diff --git a/Cargo.toml b/Cargo.toml index 320aa0f4307..8d658c2994c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ repository = "https://github.com/uutils/coreutils" readme = "README.md" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] -rust-version = "1.64.0" +rust-version = "1.70.0" edition = "2021" build = "build.rs" diff --git a/README.md b/README.md index c5b4609c924..4f341638b3f 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ [![dependency status](https://deps.rs/repo/github/uutils/coreutils/status.svg)](https://deps.rs/repo/github/uutils/coreutils) [![CodeCov](https://codecov.io/gh/uutils/coreutils/branch/master/graph/badge.svg)](https://codecov.io/gh/uutils/coreutils) -![MSRV](https://img.shields.io/badge/MSRV-1.64.0-brightgreen) +![MSRV](https://img.shields.io/badge/MSRV-1.70.0-brightgreen) @@ -71,7 +71,7 @@ the [coreutils docs](https://github.com/uutils/uutils.github.io) repository. ### Rust Version uutils follows Rust's release channels and is tested against stable, beta and -nightly. The current Minimum Supported Rust Version (MSRV) is `1.64.0`. +nightly. The current Minimum Supported Rust Version (MSRV) is `1.70.0`. ## Building From 8920ac0123ceaec5a301e0e171d425b4bcbecd6f Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Mon, 4 Sep 2023 07:26:23 +0200 Subject: [PATCH 029/208] split: fix clippy warning in test --- tests/by-util/test_split.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/by-util/test_split.rs b/tests/by-util/test_split.rs index f3317d4c7a4..0b7bbfec6dd 100644 --- a/tests/by-util/test_split.rs +++ b/tests/by-util/test_split.rs @@ -871,7 +871,7 @@ fn test_short_combination() { assert_eq!(at.read("x00"), "a"); assert_eq!(at.read("x01"), "b"); assert_eq!(at.read("x02"), "c"); - assert_eq!(at.file_exists("x03"), false); + assert!(!at.file_exists("x03")); } /// Test for the last effective suffix, ignoring all others - numeric long last From c4c9c4730c5ebb830c995c42c590f1a9b2aba319 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 24 Aug 2023 18:30:39 +0000 Subject: [PATCH 030/208] chore(deps): update rust crate clap_complete to 4.4 --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f4885faa05c..13008ab9a60 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -286,9 +286,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.3.0" +version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a04ddfaacc3bc9e6ea67d024575fafc2a813027cf374b8f24f7bc233c6b6be12" +checksum = "586a385f7ef2f8b4d86bddaa0c094794e7ccbfe5ffef1f434fe928143fc783a5" dependencies = [ "clap", ] diff --git a/Cargo.toml b/Cargo.toml index 8d658c2994c..90ee3fb4ac7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -269,7 +269,7 @@ chrono = { version = "^0.4.28", default-features = false, features = [ "clock", ] } clap = { version = "4.3", features = ["wrap_help", "cargo"] } -clap_complete = "4.3" +clap_complete = "4.4" clap_mangen = "0.2" compare = "0.1.0" coz = { version = "0.1.3" } From 4992cb9b8616af5dd9fe52daa1e61641e66f37f4 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Mon, 4 Sep 2023 10:35:17 +0200 Subject: [PATCH 031/208] Use std::io::IsTerminal instead of is-terminal --- Cargo.lock | 8 -------- Cargo.toml | 2 -- src/uu/cat/Cargo.toml | 1 - src/uu/cat/src/cat.rs | 3 +-- src/uu/cut/Cargo.toml | 1 - src/uu/cut/src/cut.rs | 3 +-- src/uu/ls/Cargo.toml | 1 - src/uu/ls/src/ls.rs | 3 +-- src/uu/more/Cargo.toml | 1 - src/uu/more/src/more.rs | 3 +-- src/uu/nohup/Cargo.toml | 1 - src/uu/nohup/src/nohup.rs | 3 +-- src/uu/tail/Cargo.toml | 1 - src/uu/tail/src/args.rs | 2 +- src/uu/tty/Cargo.toml | 1 - src/uu/tty/src/tty.rs | 3 +-- tests/by-util/test_more.rs | 2 +- 17 files changed, 8 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f4885faa05c..55879e5850e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -389,7 +389,6 @@ dependencies = [ "filetime", "glob", "hex-literal", - "is-terminal", "libc", "nix", "once_cell", @@ -2311,7 +2310,6 @@ name = "uu_cat" version = "0.0.21" dependencies = [ "clap", - "is-terminal", "nix", "thiserror", "uucore", @@ -2411,7 +2409,6 @@ version = "0.0.21" dependencies = [ "bstr", "clap", - "is-terminal", "memchr", "uucore", ] @@ -2679,7 +2676,6 @@ dependencies = [ "chrono", "clap", "glob", - "is-terminal", "lscolors", "number_prefix", "once_cell", @@ -2732,7 +2728,6 @@ version = "0.0.21" dependencies = [ "clap", "crossterm", - "is-terminal", "nix", "unicode-segmentation", "unicode-width", @@ -2773,7 +2768,6 @@ name = "uu_nohup" version = "0.0.21" dependencies = [ "clap", - "is-terminal", "libc", "uucore", ] @@ -3072,7 +3066,6 @@ version = "0.0.21" dependencies = [ "clap", "fundu", - "is-terminal", "libc", "memchr", "notify", @@ -3162,7 +3155,6 @@ name = "uu_tty" version = "0.0.21" dependencies = [ "clap", - "is-terminal", "nix", "uucore", ] diff --git a/Cargo.toml b/Cargo.toml index 8d658c2994c..405ed08f8ea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -286,7 +286,6 @@ gcd = "2.3" glob = "0.3.1" half = "2.2" indicatif = "0.17" -is-terminal = "0.4.9" itertools = "0.11.0" libc = "0.2.147" lscolors = { version = "0.15.0", default-features = false, features = [ @@ -491,7 +490,6 @@ time = { workspace = true, features = ["local-offset"] } unindent = "0.2" uucore = { workspace = true, features = ["entries", "process", "signals"] } walkdir = { workspace = true } -is-terminal = { workspace = true } hex-literal = "0.4.1" rstest = { workspace = true } diff --git a/src/uu/cat/Cargo.toml b/src/uu/cat/Cargo.toml index d67cd1b254d..166e1823b58 100644 --- a/src/uu/cat/Cargo.toml +++ b/src/uu/cat/Cargo.toml @@ -17,7 +17,6 @@ path = "src/cat.rs" [dependencies] clap = { workspace = true } thiserror = { workspace = true } -is-terminal = { workspace = true } uucore = { workspace = true, features = ["fs", "pipes"] } [target.'cfg(unix)'.dependencies] diff --git a/src/uu/cat/src/cat.rs b/src/uu/cat/src/cat.rs index 8ce8052517b..d49f4aa0707 100644 --- a/src/uu/cat/src/cat.rs +++ b/src/uu/cat/src/cat.rs @@ -7,9 +7,8 @@ // last synced with: cat (GNU coreutils) 8.13 use clap::{crate_version, Arg, ArgAction, Command}; -use is_terminal::IsTerminal; use std::fs::{metadata, File}; -use std::io::{self, Read, Write}; +use std::io::{self, IsTerminal, Read, Write}; use thiserror::Error; use uucore::display::Quotable; use uucore::error::UResult; diff --git a/src/uu/cut/Cargo.toml b/src/uu/cut/Cargo.toml index affeb38058f..a55ce7d5862 100644 --- a/src/uu/cut/Cargo.toml +++ b/src/uu/cut/Cargo.toml @@ -19,7 +19,6 @@ clap = { workspace = true } uucore = { workspace = true, features = ["ranges"] } memchr = { workspace = true } bstr = { workspace = true } -is-terminal = { workspace = true } [[bin]] name = "cut" diff --git a/src/uu/cut/src/cut.rs b/src/uu/cut/src/cut.rs index 89128175354..4d3145c059c 100644 --- a/src/uu/cut/src/cut.rs +++ b/src/uu/cut/src/cut.rs @@ -7,9 +7,8 @@ use bstr::io::BufReadExt; use clap::{crate_version, Arg, ArgAction, Command}; -use is_terminal::IsTerminal; use std::fs::File; -use std::io::{stdin, stdout, BufReader, BufWriter, Read, Write}; +use std::io::{stdin, stdout, BufReader, BufWriter, IsTerminal, Read, Write}; use std::path::Path; use uucore::display::Quotable; use uucore::error::{FromIo, UResult, USimpleError}; diff --git a/src/uu/ls/Cargo.toml b/src/uu/ls/Cargo.toml index b1114644d40..f3fc2eb6bfd 100644 --- a/src/uu/ls/Cargo.toml +++ b/src/uu/ls/Cargo.toml @@ -25,7 +25,6 @@ glob = { workspace = true } lscolors = { workspace = true } uucore = { workspace = true, features = ["entries", "fs"] } once_cell = { workspace = true } -is-terminal = { workspace = true } selinux = { workspace = true, optional = true } [[bin]] diff --git a/src/uu/ls/src/ls.rs b/src/uu/ls/src/ls.rs index 1f5165e8303..652b978a5b0 100644 --- a/src/uu/ls/src/ls.rs +++ b/src/uu/ls/src/ls.rs @@ -10,12 +10,11 @@ use clap::{ crate_version, Arg, ArgAction, Command, }; use glob::{MatchOptions, Pattern}; -use is_terminal::IsTerminal; use lscolors::LsColors; use number_prefix::NumberPrefix; use once_cell::unsync::OnceCell; -use std::collections::HashSet; use std::num::IntErrorKind; +use std::{collections::HashSet, io::IsTerminal}; #[cfg(windows)] use std::os::windows::fs::MetadataExt; diff --git a/src/uu/more/Cargo.toml b/src/uu/more/Cargo.toml index ef224013307..bc0f2521794 100644 --- a/src/uu/more/Cargo.toml +++ b/src/uu/more/Cargo.toml @@ -18,7 +18,6 @@ path = "src/more.rs" clap = { workspace = true } uucore = { workspace = true } crossterm = { workspace = true } -is-terminal = { workspace = true } unicode-width = { workspace = true } unicode-segmentation = { workspace = true } diff --git a/src/uu/more/src/more.rs b/src/uu/more/src/more.rs index 75cf79c07a9..02ed0feea20 100644 --- a/src/uu/more/src/more.rs +++ b/src/uu/more/src/more.rs @@ -7,7 +7,7 @@ use std::{ fs::File, - io::{stdin, stdout, BufReader, Read, Stdout, Write}, + io::{stdin, stdout, BufReader, IsTerminal, Read, Stdout, Write}, path::Path, time::Duration, }; @@ -22,7 +22,6 @@ use crossterm::{ terminal::{self, Clear, ClearType}, }; -use is_terminal::IsTerminal; use unicode_segmentation::UnicodeSegmentation; use unicode_width::UnicodeWidthStr; use uucore::display::Quotable; diff --git a/src/uu/nohup/Cargo.toml b/src/uu/nohup/Cargo.toml index 2a454b9f433..9fbf125bf5b 100644 --- a/src/uu/nohup/Cargo.toml +++ b/src/uu/nohup/Cargo.toml @@ -17,7 +17,6 @@ path = "src/nohup.rs" [dependencies] clap = { workspace = true } libc = { workspace = true } -is-terminal = { workspace = true } uucore = { workspace = true, features = ["fs"] } [[bin]] diff --git a/src/uu/nohup/src/nohup.rs b/src/uu/nohup/src/nohup.rs index fdbed93956b..c64f7bf71ba 100644 --- a/src/uu/nohup/src/nohup.rs +++ b/src/uu/nohup/src/nohup.rs @@ -6,14 +6,13 @@ // spell-checker:ignore (ToDO) execvp SIGHUP cproc vprocmgr cstrs homeout use clap::{crate_version, Arg, ArgAction, Command}; -use is_terminal::IsTerminal; use libc::{c_char, dup2, execvp, signal}; use libc::{SIGHUP, SIG_IGN}; use std::env; use std::ffi::CString; use std::fmt::{Display, Formatter}; use std::fs::{File, OpenOptions}; -use std::io::Error; +use std::io::{Error, IsTerminal}; use std::os::unix::prelude::*; use std::path::{Path, PathBuf}; use uucore::display::Quotable; diff --git a/src/uu/tail/Cargo.toml b/src/uu/tail/Cargo.toml index 792b7fcf178..5ff532e60fc 100644 --- a/src/uu/tail/Cargo.toml +++ b/src/uu/tail/Cargo.toml @@ -22,7 +22,6 @@ memchr = { workspace = true } notify = { workspace = true } uucore = { workspace = true } same-file = { workspace = true } -is-terminal = { workspace = true } fundu = { workspace = true } [target.'cfg(windows)'.dependencies] diff --git a/src/uu/tail/src/args.rs b/src/uu/tail/src/args.rs index 795652f2650..388842a1422 100644 --- a/src/uu/tail/src/args.rs +++ b/src/uu/tail/src/args.rs @@ -10,9 +10,9 @@ use crate::{parse, platform, Quotable}; use clap::{crate_version, value_parser}; use clap::{Arg, ArgAction, ArgMatches, Command}; use fundu::{DurationParser, SaturatingInto}; -use is_terminal::IsTerminal; use same_file::Handle; use std::ffi::OsString; +use std::io::IsTerminal; use std::time::Duration; use uucore::error::{UResult, USimpleError, UUsageError}; use uucore::parse_size::{parse_size, ParseSizeError}; diff --git a/src/uu/tty/Cargo.toml b/src/uu/tty/Cargo.toml index fde233250ac..917d04c106a 100644 --- a/src/uu/tty/Cargo.toml +++ b/src/uu/tty/Cargo.toml @@ -17,7 +17,6 @@ path = "src/tty.rs" [dependencies] clap = { workspace = true } nix = { workspace = true, features = ["term"] } -is-terminal = { workspace = true } uucore = { workspace = true, features = ["fs"] } [[bin]] diff --git a/src/uu/tty/src/tty.rs b/src/uu/tty/src/tty.rs index 96d851d3738..efda4a7becc 100644 --- a/src/uu/tty/src/tty.rs +++ b/src/uu/tty/src/tty.rs @@ -8,8 +8,7 @@ // spell-checker:ignore (ToDO) ttyname filedesc use clap::{crate_version, Arg, ArgAction, Command}; -use is_terminal::IsTerminal; -use std::io::Write; +use std::io::{IsTerminal, Write}; use std::os::unix::io::AsRawFd; use uucore::error::{set_exit_code, UResult}; use uucore::{format_usage, help_about, help_usage}; diff --git a/tests/by-util/test_more.rs b/tests/by-util/test_more.rs index b6ded2298d8..e80020d3996 100644 --- a/tests/by-util/test_more.rs +++ b/tests/by-util/test_more.rs @@ -3,7 +3,7 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. use crate::common::util::TestScenario; -use is_terminal::IsTerminal; +use std::io::IsTerminal; #[test] fn test_more_no_arg() { From 044e09786ea2b1a6182d5a751aa8d0db66ff19a2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 4 Sep 2023 15:42:41 +0000 Subject: [PATCH 032/208] chore(deps): update actions/checkout action to v4 --- .github/workflows/CICD.yml | 36 +++++++++++++++--------------- .github/workflows/CheckScripts.yml | 4 ++-- .github/workflows/FixPR.yml | 4 ++-- .github/workflows/GnuTests.yml | 8 +++---- .github/workflows/android.yml | 2 +- .github/workflows/freebsd.yml | 4 ++-- 6 files changed, 29 insertions(+), 29 deletions(-) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index d387f566ffa..27378cd9672 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -30,7 +30,7 @@ jobs: name: Style/cargo-deny runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: EmbarkStudios/cargo-deny-action@v1 style_deps: @@ -47,7 +47,7 @@ jobs: - { os: macos-latest , features: "feat_Tier1,feat_require_unix,feat_require_unix_utmpx" } - { os: windows-latest , features: feat_os_windows } steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@nightly ## note: requires 'nightly' toolchain b/c `cargo-udeps` uses the `rustc` '-Z save-analysis' option ## * ... ref: @@ -91,7 +91,7 @@ jobs: job: - { os: ubuntu-latest , features: feat_os_unix } steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: toolchain: stable @@ -131,7 +131,7 @@ jobs: env: RUN_FOR: 60 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@nightly - name: Install `cargo-fuzz` run: cargo install cargo-fuzz @@ -182,7 +182,7 @@ jobs: - { os: macos-latest , features: feat_os_macos } - { os: windows-latest , features: feat_os_windows } steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: toolchain: stable @@ -238,7 +238,7 @@ jobs: job: - { os: ubuntu-latest , features: feat_os_unix } steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Initialize workflow variables id: vars shell: bash @@ -292,7 +292,7 @@ jobs: # - { os: macos-latest , features: feat_os_macos } # - { os: windows-latest , features: feat_os_windows } steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: toolchain: stable @@ -345,7 +345,7 @@ jobs: job: - { os: ubuntu-latest , features: feat_os_unix } steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: toolchain: ${{ env.RUST_MIN_SRV }} @@ -413,7 +413,7 @@ jobs: job: - { os: ubuntu-latest , features: feat_os_unix } steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 - name: "`cargo update` testing" @@ -436,7 +436,7 @@ jobs: job: - { os: ubuntu-latest , features: feat_os_unix } steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - uses: taiki-e/install-action@nextest - uses: Swatinem/rust-cache@v2 @@ -490,7 +490,7 @@ jobs: - { os: macos-latest , features: feat_os_macos } - { os: windows-latest , features: feat_os_windows } steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - uses: taiki-e/install-action@nextest - uses: Swatinem/rust-cache@v2 @@ -517,7 +517,7 @@ jobs: - { os: macos-latest , features: feat_os_macos } - { os: windows-latest , features: feat_os_windows } steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@nightly - uses: taiki-e/install-action@nextest - uses: Swatinem/rust-cache@v2 @@ -541,7 +541,7 @@ jobs: job: - { os: ubuntu-latest , features: feat_os_unix } steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 - name: Run sccache-cache @@ -661,7 +661,7 @@ jobs: - { os: windows-latest , target: x86_64-pc-windows-gnu , features: feat_os_windows } ## note: requires rust >= 1.43.0 to link correctly - { os: windows-latest , target: x86_64-pc-windows-msvc , features: feat_os_windows } steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: toolchain: ${{ env.RUST_MIN_SRV }} @@ -920,7 +920,7 @@ jobs: run: | ## VARs setup echo "TEST_SUMMARY_FILE=busybox-result.json" >> $GITHUB_OUTPUT - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: Swatinem/rust-cache@v2 - name: Run sccache-cache uses: mozilla-actions/sccache-action@v0.0.3 @@ -1000,7 +1000,7 @@ jobs: outputs() { step_id="${{ github.action }}"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo "${var}=${!var}" >> $GITHUB_OUTPUT; done; } TEST_SUMMARY_FILE="toybox-result.json" outputs TEST_SUMMARY_FILE - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: toolchain: ${{ env.RUST_MIN_SRV }} @@ -1071,7 +1071,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Clone repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Check run: npx --yes @taplo/cli fmt --check @@ -1091,7 +1091,7 @@ jobs: - { os: macos-latest , features: macos, toolchain: nightly } - { os: windows-latest , features: windows, toolchain: nightly-x86_64-pc-windows-gnu } steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.job.toolchain }} diff --git a/.github/workflows/CheckScripts.yml b/.github/workflows/CheckScripts.yml index e1b3b24d205..98ae6cb75ab 100644 --- a/.github/workflows/CheckScripts.yml +++ b/.github/workflows/CheckScripts.yml @@ -29,7 +29,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Run ShellCheck uses: ludeeus/action-shellcheck@master env: @@ -50,7 +50,7 @@ jobs: contents: write pull-requests: write steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup shfmt uses: mfinelli/setup-shfmt@v2 - name: Run shfmt diff --git a/.github/workflows/FixPR.yml b/.github/workflows/FixPR.yml index 97b0be34afa..7f5e5234d8b 100644 --- a/.github/workflows/FixPR.yml +++ b/.github/workflows/FixPR.yml @@ -26,7 +26,7 @@ jobs: job: - { os: ubuntu-latest , features: feat_os_unix } steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Initialize job variables id: vars shell: bash @@ -85,7 +85,7 @@ jobs: job: - { os: ubuntu-latest , features: feat_os_unix } steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Initialize job variables id: vars shell: bash diff --git a/.github/workflows/GnuTests.yml b/.github/workflows/GnuTests.yml index ccb5a6f7495..61f30eba4c1 100644 --- a/.github/workflows/GnuTests.yml +++ b/.github/workflows/GnuTests.yml @@ -55,7 +55,7 @@ jobs: TEST_FULL_SUMMARY_FILE='gnu-full-result.json' outputs SUITE_LOG_FILE ROOT_SUITE_LOG_FILE TEST_FILESET_PREFIX TEST_FILESET_SUFFIX TEST_LOGS_GLOB TEST_SUMMARY_FILE TEST_FULL_SUMMARY_FILE - name: Checkout code (uutil) - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: path: '${{ steps.vars.outputs.path_UUTILS }}' - uses: dtolnay/rust-toolchain@master @@ -66,7 +66,7 @@ jobs: with: workspaces: "./${{ steps.vars.outputs.path_UUTILS }} -> target" - name: Checkout code (GNU coreutils) - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: 'coreutils/coreutils' path: '${{ steps.vars.outputs.path_GNU }}' @@ -307,11 +307,11 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Checkout code uutil - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: path: 'uutils' - name: Checkout GNU coreutils - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: 'coreutils/coreutils' path: 'gnu' diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index a6929b171cc..5834aceffe8 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -26,7 +26,7 @@ jobs: env: TERMUX: v0.118.0 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Restore AVD cache uses: actions/cache/restore@v3 id: avd-cache diff --git a/.github/workflows/freebsd.yml b/.github/workflows/freebsd.yml index 095ec323098..5af3da320a7 100644 --- a/.github/workflows/freebsd.yml +++ b/.github/workflows/freebsd.yml @@ -30,7 +30,7 @@ jobs: SCCACHE_GHA_ENABLED: "true" RUSTC_WRAPPER: "sccache" steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: Swatinem/rust-cache@v2 - name: Run sccache-cache uses: mozilla-actions/sccache-action@v0.0.3 @@ -120,7 +120,7 @@ jobs: SCCACHE_GHA_ENABLED: "true" RUSTC_WRAPPER: "sccache" steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: Swatinem/rust-cache@v2 - name: Run sccache-cache uses: mozilla-actions/sccache-action@v0.0.3 From 8c1696084c08488146d6068a092ada14a6cf47b8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 4 Sep 2023 15:47:49 +0000 Subject: [PATCH 033/208] chore(deps): update rust crate clap to 4.4 --- Cargo.lock | 18 ++++++++---------- Cargo.toml | 2 +- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 13008ab9a60..c3e7ae92f2a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -34,16 +34,15 @@ dependencies = [ [[package]] name = "anstream" -version = "0.3.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" +checksum = "b1f58811cfac344940f1a400b6e6231ce35171f614f26439e80f8c1465c5cc0c" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", - "is-terminal", "utf8parse", ] @@ -73,9 +72,9 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "1.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" +checksum = "58f54d10c6dfa51283a066ceab3ec1ab78d13fae00aa49243a45e4571fb79dfd" dependencies = [ "anstyle", "windows-sys 0.48.0", @@ -263,23 +262,22 @@ dependencies = [ [[package]] name = "clap" -version = "4.3.21" +version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c27cdf28c0f604ba3f512b0c9a409f8de8513e4816705deb0498b627e7c3a3fd" +checksum = "6a13b88d2c62ff462f88e4a121f17a82c1af05693a2f192b5c38d14de73c19f6" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.3.21" +version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08a9f1ab5e9f01a9b81f202e8562eb9a10de70abf9eaeac1be465c28b75aa4aa" +checksum = "2bb9faaa7c2ef94b2743a21f5a29e6f0010dff4caa69ac8e9d6cf8b6fa74da08" dependencies = [ "anstream", "anstyle", "clap_lex", - "once_cell", "strsim", "terminal_size", ] diff --git a/Cargo.toml b/Cargo.toml index 90ee3fb4ac7..97edb75dd7a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -268,7 +268,7 @@ chrono = { version = "^0.4.28", default-features = false, features = [ "alloc", "clock", ] } -clap = { version = "4.3", features = ["wrap_help", "cargo"] } +clap = { version = "4.4", features = ["wrap_help", "cargo"] } clap_complete = "4.4" clap_mangen = "0.2" compare = "0.1.0" From 636c2bb7ae9444551d900d50829eb87414a23f8f Mon Sep 17 00:00:00 2001 From: zhitkoff Date: Mon, 4 Sep 2023 12:05:26 -0400 Subject: [PATCH 034/208] uucore: parse_size_max and split --- src/uu/split/src/split.rs | 28 ++++++++-------------- src/uucore/src/lib/parser/parse_size.rs | 32 +++++++++++++++++++++++-- 2 files changed, 40 insertions(+), 20 deletions(-) diff --git a/src/uu/split/src/split.rs b/src/uu/split/src/split.rs index d76bfb2de74..e39f6e93ffe 100644 --- a/src/uu/split/src/split.rs +++ b/src/uu/split/src/split.rs @@ -21,7 +21,7 @@ use std::io::{stdin, BufRead, BufReader, BufWriter, ErrorKind, Read, Write}; use std::path::Path; use uucore::display::Quotable; use uucore::error::{FromIo, UIoError, UResult, USimpleError, UUsageError}; -use uucore::parse_size::{parse_size, ParseSizeError}; +use uucore::parse_size::{parse_size_max, ParseSizeError}; use uucore::uio_error; use uucore::{format_usage, help_about, help_section, help_usage}; @@ -419,8 +419,7 @@ impl NumberType { let parts: Vec<&str> = s.split('/').collect(); match &parts[..] { [n_str] => { - let num_chunks = n_str - .parse() + let num_chunks = parse_size_max(n_str) .map_err(|_| NumberTypeError::NumberOfChunks(n_str.to_string()))?; if num_chunks > 0 { Ok(Self::Bytes(num_chunks)) @@ -429,32 +428,26 @@ impl NumberType { } } ["l", n_str] => { - let num_chunks = n_str - .parse() + let num_chunks = parse_size_max(n_str) .map_err(|_| NumberTypeError::NumberOfChunks(n_str.to_string()))?; Ok(Self::Lines(num_chunks)) } ["l", k_str, n_str] => { - let num_chunks = n_str - .parse() + let num_chunks = parse_size_max(n_str) .map_err(|_| NumberTypeError::NumberOfChunks(n_str.to_string()))?; - let chunk_number = k_str - .parse() + let chunk_number = parse_size_max(k_str) .map_err(|_| NumberTypeError::ChunkNumber(k_str.to_string()))?; Ok(Self::KthLines(chunk_number, num_chunks)) } ["r", n_str] => { - let num_chunks = n_str - .parse() + let num_chunks = parse_size_max(n_str) .map_err(|_| NumberTypeError::NumberOfChunks(n_str.to_string()))?; Ok(Self::RoundRobin(num_chunks)) } ["r", k_str, n_str] => { - let num_chunks = n_str - .parse() + let num_chunks = parse_size_max(n_str) .map_err(|_| NumberTypeError::NumberOfChunks(n_str.to_string()))?; - let chunk_number = k_str - .parse() + let chunk_number = parse_size_max(k_str) .map_err(|_| NumberTypeError::ChunkNumber(k_str.to_string()))?; Ok(Self::KthRoundRobin(chunk_number, num_chunks)) } @@ -523,7 +516,7 @@ impl Strategy { error: fn(ParseSizeError) -> StrategyError, ) -> Result { let s = matches.get_one::(option).unwrap(); - let n = parse_size(s).map_err(error)?; + let n = parse_size_max(s).map_err(error)?; if n > 0 { Ok(strategy(n)) } else { @@ -542,7 +535,7 @@ impl Strategy { matches.value_source(OPT_NUMBER) == Some(ValueSource::CommandLine), ) { (Some(v), false, false, false, false) => { - let v = parse_size(v).map_err(|_| { + let v = parse_size_max(v).map_err(|_| { StrategyError::Lines(ParseSizeError::ParseFailure(v.to_string())) })?; Ok(Self::Lines(v)) @@ -687,7 +680,6 @@ impl Settings { if additional_suffix.contains('/') { return Err(SettingsError::SuffixContainsSeparator(additional_suffix)); } - let strategy = Strategy::from(matches, obs_lines).map_err(SettingsError::Strategy)?; let (suffix_type, suffix_start) = suffix_type_from(matches)?; let suffix_length_str = matches.get_one::(OPT_SUFFIX_LENGTH).unwrap(); diff --git a/src/uucore/src/lib/parser/parse_size.rs b/src/uucore/src/lib/parser/parse_size.rs index 5f64afcd8a5..63039e6093b 100644 --- a/src/uucore/src/lib/parser/parse_size.rs +++ b/src/uucore/src/lib/parser/parse_size.rs @@ -7,6 +7,7 @@ use std::error::Error; use std::fmt; +use std::num::IntErrorKind; use crate::display::Quotable; @@ -201,8 +202,10 @@ impl<'parser> Parser<'parser> { radix: u32, original_size: &str, ) -> Result { - u64::from_str_radix(numeric_string, radix) - .map_err(|_| ParseSizeError::ParseFailure(original_size.to_string())) + u64::from_str_radix(numeric_string, radix).map_err(|e| match e.kind() { + IntErrorKind::PosOverflow => ParseSizeError::size_too_big(original_size), + _ => ParseSizeError::ParseFailure(original_size.to_string()), + }) } } @@ -232,6 +235,23 @@ pub fn parse_size(size: &str) -> Result { Parser::default().parse(size) } +/// Same as `parse_size()`, except returns `u64::MAX` on overflow +/// GNU lib/coreutils include similar functionality +/// and GNU test suite checks this behavior for some utils +pub fn parse_size_max(size: &str) -> Result { + let result = Parser::default().parse(size); + match result { + Ok(_) => result, + Err(error) => { + if let ParseSizeError::SizeTooBig(_) = error { + Ok(u64::MAX) + } else { + Err(error) + } + } + } +} + #[derive(Debug, PartialEq, Eq)] pub enum ParseSizeError { InvalidSuffix(String), // Suffix @@ -392,6 +412,14 @@ mod tests { ); } + #[test] + #[cfg(not(target_pointer_width = "128"))] + fn overflow_to_max_x64() { + assert_eq!(Ok(u64::MAX), parse_size_max("18446744073709551616")); + assert_eq!(Ok(u64::MAX), parse_size_max("10000000000000000000000")); + assert_eq!(Ok(u64::MAX), parse_size_max("1Y")); + } + #[test] fn invalid_suffix() { let test_strings = ["5mib", "1eb", "1H"]; From 862a2df924eabf9c2c99dae520b1d4d78e361b57 Mon Sep 17 00:00:00 2001 From: Yury Zhytkou <54360928+zhitkoff@users.noreply.github.com> Date: Mon, 4 Sep 2023 12:23:49 -0400 Subject: [PATCH 035/208] build-gnu.sh: fix for /usr/bin/timeout on MacOS (#5194) * build-gnu.sh: `/usr/bin/timeout` should not be hardcoded to /usr/bin location Fixes #5193 --- util/build-gnu.sh | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/util/build-gnu.sh b/util/build-gnu.sh index a3dd0b9ad53..157265f61bf 100755 --- a/util/build-gnu.sh +++ b/util/build-gnu.sh @@ -18,10 +18,30 @@ path_GNU="$(readlink -fm -- "${path_GNU:-${path_UUTILS}/../gnu}")" ### +# On MacOS there is no system /usr/bin/timeout +# and trying to add it to /usr/bin (with symlink of copy binary) will fail unless system integrity protection is disabled (not ideal) +# ref: https://support.apple.com/en-us/102149 +# On MacOS the Homebrew coreutils could be installed and then "sudo ln -s /opt/homebrew/bin/timeout /usr/local/bin/timeout" +# Set to /usr/local/bin/timeout instead if /usr/bin/timeout is not found +SYSTEM_TIMEOUT="timeout" +if [ -x /usr/bin/timeout ] ; then + SYSTEM_TIMEOUT="/usr/bin/timeout" +elif [ -x /usr/local/bin/timeout ] ; then + SYSTEM_TIMEOUT="/usr/local/bin/timeout" +fi + +### + +release_tag_GNU="v9.4" + if test ! -d "${path_GNU}"; then echo "Could not find GNU coreutils (expected at '${path_GNU}')" echo "Run the following to download into the expected path:" echo "git clone --recurse-submodules https://github.com/coreutils/coreutils.git \"${path_GNU}\"" + echo "After downloading GNU coreutils to \"${path_GNU}\" run the following commands to checkout latest release tag" + echo "cd \"${path_GNU}\"" + echo "git fetch --all --tags" + echo "git checkout tags/${release_tag_GNU}" exit 1 fi @@ -82,7 +102,7 @@ else ./bootstrap --skip-po ./configure --quiet --disable-gcc-warnings #Add timeout to to protect against hangs - sed -i 's|^"\$@|/usr/bin/timeout 600 "\$@|' build-aux/test-driver + sed -i 's|^"\$@|'"${SYSTEM_TIMEOUT}"' 600 "\$@|' build-aux/test-driver # Change the PATH in the Makefile to test the uutils coreutils instead of the GNU coreutils sed -i "s/^[[:blank:]]*PATH=.*/ PATH='${UU_BUILD_DIR//\//\\/}\$(PATH_SEPARATOR)'\"\$\$PATH\" \\\/" Makefile sed -i 's| tr | /usr/bin/tr |' tests/init.sh @@ -153,13 +173,14 @@ sed -i 's|touch |/usr/bin/touch |' tests/cp/reflink-perm.sh tests/ls/block-size. sed -i 's|ln -|/usr/bin/ln -|' tests/cp/link-deref.sh sed -i 's|cp |/usr/bin/cp |' tests/mv/hard-2.sh sed -i 's|paste |/usr/bin/paste |' tests/od/od-endian.sh -sed -i 's|timeout |/usr/bin/timeout |' tests/tail/follow-stdin.sh +sed -i 's|timeout |'"${SYSTEM_TIMEOUT}"' |' tests/tail/follow-stdin.sh # Add specific timeout to tests that currently hang to limit time spent waiting -sed -i 's|\(^\s*\)seq \$|\1/usr/bin/timeout 0.1 seq \$|' tests/seq/seq-precision.sh tests/seq/seq-long-double.sh +sed -i 's|\(^\s*\)seq \$|\1'"${SYSTEM_TIMEOUT}"' 0.1 seq \$|' tests/seq/seq-precision.sh tests/seq/seq-long-double.sh -# Remove dup of /usr/bin/ when executed several times +# Remove dup of /usr/bin/ and /usr/local/bin/ when executed several times grep -rlE '/usr/bin/\s?/usr/bin' init.cfg tests/* | xargs --no-run-if-empty sed -Ei 's|/usr/bin/\s?/usr/bin/|/usr/bin/|g' +grep -rlE '/usr/local/bin/\s?/usr/local/bin' init.cfg tests/* | xargs --no-run-if-empty sed -Ei 's|/usr/local/bin/\s?/usr/local/bin/|/usr/local/bin/|g' #### Adjust tests to make them work with Rust/coreutils # in some cases, what we are doing in rust/coreutils is good (or better) From 7eec0ddaa364464f0567945934876cf8ac30fcbd Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 5 Sep 2023 05:06:00 +0000 Subject: [PATCH 036/208] chore(deps): update rust crate half to 2.3 --- Cargo.lock | 5 +++-- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c3e7ae92f2a..26a38fd3662 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1028,10 +1028,11 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "half" -version = "2.2.1" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b4af3693f1b705df946e9fe5631932443781d0aabb423b62fcd4d73f6d2fd0" +checksum = "bc52e53916c08643f1b56ec082790d1e86a32e58dc5268f897f313fbae7b4872" dependencies = [ + "cfg-if", "crunchy", ] diff --git a/Cargo.toml b/Cargo.toml index 97edb75dd7a..9b92b342c66 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -284,7 +284,7 @@ fts-sys = "0.2" fundu = "2.0.0" gcd = "2.3" glob = "0.3.1" -half = "2.2" +half = "2.3" indicatif = "0.17" is-terminal = "0.4.9" itertools = "0.11.0" From f30b59efc0bf7d9fa49602058e7788b7edff47bf Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Tue, 5 Sep 2023 07:37:14 +0200 Subject: [PATCH 037/208] wc: use Rust's ilog10(), remove custom ilog10 fn --- src/uu/wc/src/wc.rs | 28 +--------------------------- 1 file changed, 1 insertion(+), 27 deletions(-) diff --git a/src/uu/wc/src/wc.rs b/src/uu/wc/src/wc.rs index 6d5894db058..663bbda151b 100644 --- a/src/uu/wc/src/wc.rs +++ b/src/uu/wc/src/wc.rs @@ -702,7 +702,7 @@ fn compute_number_width(inputs: &Inputs, settings: &Settings) -> usize { if total == 0 { minimum_width } else { - let total_width = (1 + ilog10_u64(total)) + let total_width = (1 + total.ilog10()) .try_into() .expect("ilog of a u64 should fit into a usize"); max(total_width, minimum_width) @@ -857,29 +857,3 @@ fn print_stats( writeln!(stdout) } } - -// TODO: remove and just use usize::ilog10 once the MSRV is >= 1.67. -fn ilog10_u64(mut u: u64) -> u32 { - if u == 0 { - panic!("cannot compute log of 0") - } - let mut log = 0; - if u >= 10_000_000_000 { - log += 10; - u /= 10_000_000_000; - } - if u >= 100_000 { - log += 5; - u /= 100_000; - } - // Rust's standard library in versions >= 1.67 does something even more clever than this, but - // this should work just fine for the time being. - log + match u { - 1..=9 => 0, - 10..=99 => 1, - 100..=999 => 2, - 1000..=9999 => 3, - 10000..=99999 => 4, - _ => unreachable!(), - } -} From e493b9c5275e8764d33bb464bf0be6e977b531f5 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Tue, 5 Sep 2023 08:16:25 +0200 Subject: [PATCH 038/208] yes: use let/else to fix todo --- src/uu/yes/src/yes.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/uu/yes/src/yes.rs b/src/uu/yes/src/yes.rs index e6868d9d85e..a58b734045b 100644 --- a/src/uu/yes/src/yes.rs +++ b/src/uu/yes/src/yes.rs @@ -58,10 +58,7 @@ fn args_into_buffer<'a>( buf: &mut Vec, i: Option>, ) -> Result<(), Box> { - // TODO: this should be replaced with let/else once available in the MSRV. - let i = if let Some(i) = i { - i - } else { + let Some(i) = i else { buf.extend_from_slice(b"y\n"); return Ok(()); }; From 862a63835048d0e9d534a4ea1e33f08fb016a176 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Tue, 5 Sep 2023 09:44:30 +0200 Subject: [PATCH 039/208] Remove is-terminal from Cargo.lock --- Cargo.lock | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 28ae46ccdb3..25377dc0cd6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1136,17 +1136,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "is-terminal" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" -dependencies = [ - "hermit-abi", - "rustix 0.38.8", - "windows-sys 0.48.0", -] - [[package]] name = "itertools" version = "0.11.0" From 1a086ead7f69526d70447c9450ad709f2175fa89 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Tue, 5 Sep 2023 10:05:58 +0200 Subject: [PATCH 040/208] build-gnu.sh: fix formatting issues --- util/build-gnu.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/util/build-gnu.sh b/util/build-gnu.sh index 157265f61bf..2d949bbb333 100755 --- a/util/build-gnu.sh +++ b/util/build-gnu.sh @@ -18,16 +18,16 @@ path_GNU="$(readlink -fm -- "${path_GNU:-${path_UUTILS}/../gnu}")" ### -# On MacOS there is no system /usr/bin/timeout +# On MacOS there is no system /usr/bin/timeout # and trying to add it to /usr/bin (with symlink of copy binary) will fail unless system integrity protection is disabled (not ideal) # ref: https://support.apple.com/en-us/102149 # On MacOS the Homebrew coreutils could be installed and then "sudo ln -s /opt/homebrew/bin/timeout /usr/local/bin/timeout" # Set to /usr/local/bin/timeout instead if /usr/bin/timeout is not found SYSTEM_TIMEOUT="timeout" -if [ -x /usr/bin/timeout ] ; then +if [ -x /usr/bin/timeout ]; then SYSTEM_TIMEOUT="/usr/bin/timeout" -elif [ -x /usr/local/bin/timeout ] ; then - SYSTEM_TIMEOUT="/usr/local/bin/timeout" +elif [ -x /usr/local/bin/timeout ]; then + SYSTEM_TIMEOUT="/usr/local/bin/timeout" fi ### From cefb4eb265b6ae13d57c003e9b9c636b76786b86 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Tue, 5 Sep 2023 10:43:26 +0200 Subject: [PATCH 041/208] Bump blake3 from 1.4.0 to 1.4.1 --- Cargo.lock | 14 ++++++++++---- Cargo.toml | 2 +- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 25377dc0cd6..86c37482800 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -161,20 +161,20 @@ checksum = "3c2f0dc9a68c6317d884f97cc36cf5a3d20ba14ce404227df55e1af708ab04bc" dependencies = [ "arrayref", "arrayvec", - "constant_time_eq", + "constant_time_eq 0.2.4", ] [[package]] name = "blake3" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "729b71f35bd3fa1a4c86b85d32c8b9069ea7fe14f7a53cfabb65f62d4265b888" +checksum = "199c42ab6972d92c9f8995f086273d25c42fc0f7b2a1fcefba465c1352d25ba5" dependencies = [ "arrayref", "arrayvec", "cc", "cfg-if", - "constant_time_eq", + "constant_time_eq 0.3.0", "digest", ] @@ -360,6 +360,12 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3ad85c1f65dc7b37604eb0e89748faf0b9653065f2a8ef69f96a687ec1e9279" +[[package]] +name = "constant_time_eq" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" + [[package]] name = "conv" version = "0.3.3" diff --git a/Cargo.toml b/Cargo.toml index 5e2b4230a1a..51b64aff898 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -339,7 +339,7 @@ sha1 = "0.10.5" sha2 = "0.10.7" sha3 = "0.10.8" blake2b_simd = "1.0.1" -blake3 = "1.4.0" +blake3 = "1.4.1" sm3 = "0.4.2" digest = "0.10.7" From 442e468efcda70a2bac6f18bc642a513bec2293e Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Tue, 5 Sep 2023 10:45:14 +0200 Subject: [PATCH 042/208] Bump constant_time_eq from 0.2.4 to 0.2.6 --- Cargo.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 86c37482800..7b476f4477f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -161,7 +161,7 @@ checksum = "3c2f0dc9a68c6317d884f97cc36cf5a3d20ba14ce404227df55e1af708ab04bc" dependencies = [ "arrayref", "arrayvec", - "constant_time_eq 0.2.4", + "constant_time_eq 0.2.6", ] [[package]] @@ -356,9 +356,9 @@ dependencies = [ [[package]] name = "constant_time_eq" -version = "0.2.4" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3ad85c1f65dc7b37604eb0e89748faf0b9653065f2a8ef69f96a687ec1e9279" +checksum = "21a53c0a4d288377e7415b53dcfc3c04da5cdc2cc95c8d5ac178b58f0b861ad6" [[package]] name = "constant_time_eq" From dd584b6332967ba76acafec983dbba2189af32f3 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Tue, 5 Sep 2023 10:49:32 +0200 Subject: [PATCH 043/208] deny.toml: add constant_time_eq to skip list --- deny.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/deny.toml b/deny.toml index c2e580a4de0..678fc352dc3 100644 --- a/deny.toml +++ b/deny.toml @@ -87,6 +87,8 @@ skip = [ { name = "syn", version = "1.0.109" }, # various crates { name = "bitflags", version = "1.3.2" }, + # blake2b_simd + { name = "constant_time_eq", version = "0.2.6" }, ] # spell-checker: enable From 3cce11b46ff2229fffc39f84237007bc86300ede Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 5 Sep 2023 14:17:59 +0000 Subject: [PATCH 044/208] chore(deps): update rust crate walkdir to 2.4 --- Cargo.lock | 5 ++--- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 25377dc0cd6..eb1cdd4b174 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3311,12 +3311,11 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "walkdir" -version = "2.3.2" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" dependencies = [ "same-file", - "winapi", "winapi-util", ] diff --git a/Cargo.toml b/Cargo.toml index 5e2b4230a1a..dbe5bd10e2a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -327,7 +327,7 @@ time = { version = "0.3" } unicode-segmentation = "1.10.1" unicode-width = "0.1.10" utf-8 = "0.7.6" -walkdir = "2.3" +walkdir = "2.4" winapi-util = "0.1.5" windows-sys = { version = "0.48.0", default-features = false } xattr = "1.0.1" From 2ae1d8d1ccd7c4fff24b51b8d914cda2be9de006 Mon Sep 17 00:00:00 2001 From: zhitkoff Date: Tue, 5 Sep 2023 17:13:30 -0400 Subject: [PATCH 045/208] split: missing functionality for --number option --- src/uu/split/src/split.rs | 218 ++++++++++++++++++++++-- src/uucore/src/lib/parser/parse_size.rs | 2 +- 2 files changed, 204 insertions(+), 16 deletions(-) diff --git a/src/uu/split/src/split.rs b/src/uu/split/src/split.rs index e39f6e93ffe..23520f7092b 100644 --- a/src/uu/split/src/split.rs +++ b/src/uu/split/src/split.rs @@ -19,9 +19,10 @@ use std::fs::{metadata, File}; use std::io; use std::io::{stdin, BufRead, BufReader, BufWriter, ErrorKind, Read, Write}; use std::path::Path; +use std::u64; use uucore::display::Quotable; use uucore::error::{FromIo, UIoError, UResult, USimpleError, UUsageError}; -use uucore::parse_size::{parse_size_max, ParseSizeError}; +use uucore::parse_size::{parse_size, parse_size_max, ParseSizeError}; use uucore::uio_error; use uucore::{format_usage, help_about, help_section, help_usage}; @@ -337,6 +338,10 @@ enum NumberType { /// Split into a specific number of chunks by byte. Bytes(u64), + /// Split into a specific number of chunks by byte + /// but output only the *k*th chunk. + KthBytes(u64, u64), + /// Split into a specific number of chunks by line (approximately). Lines(u64), @@ -349,7 +354,7 @@ enum NumberType { /// Assign lines via round-robin to the specified number of output /// chunks, but output only the *k*th chunk. - KthRoundRobin(u64, u64), + KthRoundRobin(u64, u64), // not yet implemented? } impl NumberType { @@ -357,6 +362,7 @@ impl NumberType { fn num_chunks(&self) -> u64 { match self { Self::Bytes(n) => *n, + Self::KthBytes(_, n) => *n, Self::Lines(n) => *n, Self::KthLines(_, n) => *n, Self::RoundRobin(n) => *n, @@ -375,6 +381,7 @@ enum NumberTypeError { /// /// ```ignore /// -n N + /// -n K/N /// -n l/N /// -n l/K/N /// -n r/N @@ -385,9 +392,12 @@ enum NumberTypeError { /// The chunk number was invalid. /// /// This can happen if the value of `K` in any of the following - /// command-line options is not a positive integer: + /// command-line options is not a positive integer + /// or if `K` is 0 + /// or if `K` is greater than `N`: /// /// ```ignore + /// -n K/N /// -n l/K/N /// -n r/K/N /// ``` @@ -401,6 +411,7 @@ impl NumberType { /// /// ```ignore /// "N" + /// "K/N" /// "l/N" /// "l/K/N" /// "r/N" @@ -412,14 +423,17 @@ impl NumberType { /// /// # Errors /// - /// If the string is not one of the valid number types, if `K` is - /// not a nonnegative integer, or if `N` is not a positive - /// integer, then this function returns [`NumberTypeError`]. + /// If the string is not one of the valid number types, + /// if `K` is not a nonnegative integer, + /// or if `K` is 0, + /// or if `N` is not a positive integer, + /// or if `K` is greater than `N` + /// then this function returns [`NumberTypeError`]. fn from(s: &str) -> Result { let parts: Vec<&str> = s.split('/').collect(); match &parts[..] { [n_str] => { - let num_chunks = parse_size_max(n_str) + let num_chunks = parse_size(n_str) .map_err(|_| NumberTypeError::NumberOfChunks(n_str.to_string()))?; if num_chunks > 0 { Ok(Self::Bytes(num_chunks)) @@ -427,28 +441,44 @@ impl NumberType { Err(NumberTypeError::NumberOfChunks(s.to_string())) } } + [k_str, n_str] if !k_str.starts_with('l') && !k_str.starts_with('r') => { + let num_chunks = parse_size(n_str) + .map_err(|_| NumberTypeError::NumberOfChunks(n_str.to_string()))?; + let chunk_number = parse_size(k_str) + .map_err(|_| NumberTypeError::ChunkNumber(k_str.to_string()))?; + if chunk_number > num_chunks || chunk_number == 0 { + return Err(NumberTypeError::ChunkNumber(k_str.to_string())); + } + Ok(Self::KthBytes(chunk_number, num_chunks)) + } ["l", n_str] => { - let num_chunks = parse_size_max(n_str) + let num_chunks = parse_size(n_str) .map_err(|_| NumberTypeError::NumberOfChunks(n_str.to_string()))?; Ok(Self::Lines(num_chunks)) } ["l", k_str, n_str] => { - let num_chunks = parse_size_max(n_str) + let num_chunks = parse_size(n_str) .map_err(|_| NumberTypeError::NumberOfChunks(n_str.to_string()))?; - let chunk_number = parse_size_max(k_str) + let chunk_number = parse_size(k_str) .map_err(|_| NumberTypeError::ChunkNumber(k_str.to_string()))?; + if chunk_number > num_chunks || chunk_number == 0 { + return Err(NumberTypeError::ChunkNumber(k_str.to_string())); + } Ok(Self::KthLines(chunk_number, num_chunks)) } ["r", n_str] => { - let num_chunks = parse_size_max(n_str) + let num_chunks = parse_size(n_str) .map_err(|_| NumberTypeError::NumberOfChunks(n_str.to_string()))?; Ok(Self::RoundRobin(num_chunks)) } ["r", k_str, n_str] => { - let num_chunks = parse_size_max(n_str) + let num_chunks = parse_size(n_str) .map_err(|_| NumberTypeError::NumberOfChunks(n_str.to_string()))?; - let chunk_number = parse_size_max(k_str) + let chunk_number = parse_size(k_str) .map_err(|_| NumberTypeError::ChunkNumber(k_str.to_string()))?; + if chunk_number > num_chunks || chunk_number == 0 { + return Err(NumberTypeError::ChunkNumber(k_str.to_string())); + } Ok(Self::KthRoundRobin(chunk_number, num_chunks)) } _ => Err(NumberTypeError::NumberOfChunks(s.to_string())), @@ -538,7 +568,13 @@ impl Strategy { let v = parse_size_max(v).map_err(|_| { StrategyError::Lines(ParseSizeError::ParseFailure(v.to_string())) })?; - Ok(Self::Lines(v)) + if v > 0 { + Ok(Self::Lines(v)) + } else { + Err(StrategyError::Lines(ParseSizeError::ParseFailure( + v.to_string(), + ))) + } } (None, false, false, false, false) => Ok(Self::Lines(1000)), (None, true, false, false, false) => { @@ -1263,6 +1299,93 @@ where } } +/// Print the k-th chunk of a file to stdout, splitting by byte. +/// +/// This function is like [`split_into_n_chunks_by_byte`], but instead +/// of writing each chunk to its own file, it only writes to stdout +/// the contents of the chunk identified by `chunk_number` +/// +/// # Errors +/// +/// This function returns an error if there is a problem reading from +/// `reader` or writing to stdout. +fn kth_chunks_by_byte( + settings: &Settings, + reader: &mut R, + chunk_number: u64, + num_chunks: u64, +) -> UResult<()> +where + R: BufRead, +{ + // Get the size of the input file in bytes and compute the number + // of bytes per chunk. + // + // If the requested number of chunks exceeds the number of bytes + // in the file - just write empty byte string to stdout + // NOTE: the `elide_empty_files` parameter is ignored here + // as we do not generate any files + // and instead writing to stdout + let metadata = metadata(&settings.input).map_err(|_| { + USimpleError::new(1, format!("{}: cannot determine file size", settings.input)) + })?; + + let num_bytes = metadata.len(); + // If input file is empty and we would have written zero chunks of output, + // then terminate immediately. + // This happens on `split -e -n 3 /dev/null`, for example. + if num_bytes == 0 { + return Ok(()); + } + + // Write to stdout instead of to a file. + let stdout = std::io::stdout(); + let mut writer = stdout.lock(); + + let chunk_size = (num_bytes / (num_chunks)).max(1); + let mut num_bytes: usize = num_bytes.try_into().unwrap(); + + let mut i = 1; + loop { + let buf: &mut Vec = &mut vec![]; + if num_bytes > 0 { + // Read `chunk_size` bytes from the reader into `buf` + // except the last. + // + // The last chunk gets all remaining bytes so that if the number + // of bytes in the input file was not evenly divisible by + // `num_chunks`, we don't leave any bytes behind. + let limit = { + if i == num_chunks { + num_bytes.try_into().unwrap() + } else { + chunk_size + } + }; + let n_bytes_read = reader.by_ref().take(limit).read_to_end(buf); + match n_bytes_read { + Ok(n_bytes) => { + num_bytes -= n_bytes; + } + Err(error) => { + return Err(USimpleError::new( + 1, + format!("{}: cannot read from input : {}", settings.input, error), + )); + } + } + if i == chunk_number { + writer.write_all(buf)?; + break; + } + i += 1; + } else { + break; + } + } + Ok(()) +} + /// Split a file into a specific number of chunks by line. /// /// This function always creates one output file for each chunk, even @@ -1438,6 +1561,50 @@ where Ok(()) } +/// Print the k-th chunk of a file, splitting by line, but +/// assign lines via round-robin to the specified number of output +/// chunks, but output only the *k*th chunk. +/// +/// This function is like [`kth_chunk_by_line`], as it only writes to stdout and +/// prints out only *k*th chunk +/// It is also like [`split_into_n_chunks_by_line_round_robin`], as it is assigning chunks +/// using round robin distribution +/// +/// # Errors +/// +/// This function returns an error if there is a problem reading from +/// `reader` or writing to one of the output files. +/// +/// # See also +/// +/// * [`split_into_n_chunks_by_line_round_robin`], which splits its input in the +/// same way, but writes each chunk to its own file. +fn kth_chunk_by_line_round_robin( + _settings: &Settings, + reader: &mut R, + chunk_number: u64, + num_chunks: u64, +) -> UResult<()> +where + R: BufRead, +{ + // Write to stdout instead of to a file. + let stdout = std::io::stdout(); + let mut writer = stdout.lock(); + + let num_chunks: usize = num_chunks.try_into().unwrap(); + let chunk_number: usize = chunk_number.try_into().unwrap(); + for (i, line_result) in reader.lines().enumerate() { + let line = line_result?; + let bytes = line.as_bytes(); + if (i % num_chunks) == chunk_number { + writer.write_all(bytes)?; + writer.write_all(b"\n")?; + } + } + Ok(()) +} + fn split(settings: &Settings) -> UResult<()> { let mut reader = BufReader::new(if settings.input == "-" { Box::new(stdin()) as Box @@ -1455,6 +1622,9 @@ fn split(settings: &Settings) -> UResult<()> { Strategy::Number(NumberType::Bytes(num_chunks)) => { split_into_n_chunks_by_byte(settings, &mut reader, num_chunks) } + Strategy::Number(NumberType::KthBytes(chunk_number, num_chunks)) => { + kth_chunks_by_byte(settings, &mut reader, chunk_number, num_chunks) + } Strategy::Number(NumberType::Lines(num_chunks)) => { split_into_n_chunks_by_line(settings, &mut reader, num_chunks) } @@ -1467,7 +1637,12 @@ fn split(settings: &Settings) -> UResult<()> { Strategy::Number(NumberType::RoundRobin(num_chunks)) => { split_into_n_chunks_by_line_round_robin(settings, &mut reader, num_chunks) } - Strategy::Number(_) => Err(USimpleError::new(1, "-n mode not yet fully implemented")), + Strategy::Number(NumberType::KthRoundRobin(chunk_number, num_chunks)) => { + // The chunk number is given as a 1-indexed number, but it + // is a little easier to deal with a 0-indexed number. + let chunk_number = chunk_number - 1; + kth_chunk_by_line_round_robin(settings, &mut reader, chunk_number, num_chunks) + } Strategy::Lines(chunk_size) => { let mut writer = LineChunkWriter::new(chunk_size, settings)?; match std::io::copy(&mut reader, &mut writer) { @@ -1570,6 +1745,18 @@ mod tests { NumberType::from("l/abc/456").unwrap_err(), NumberTypeError::ChunkNumber("abc".to_string()) ); + assert_eq!( + NumberType::from("l/456/123").unwrap_err(), + NumberTypeError::ChunkNumber("456".to_string()) + ); + assert_eq!( + NumberType::from("r/456/123").unwrap_err(), + NumberTypeError::ChunkNumber("456".to_string()) + ); + assert_eq!( + NumberType::from("456/123").unwrap_err(), + NumberTypeError::ChunkNumber("456".to_string()) + ); // In GNU split, the number of chunks get precedence: // // $ split -n l/abc/xyz @@ -1605,6 +1792,7 @@ mod tests { #[test] fn test_number_type_num_chunks() { assert_eq!(NumberType::from("123").unwrap().num_chunks(), 123); + assert_eq!(NumberType::from("123/456").unwrap().num_chunks(), 456); assert_eq!(NumberType::from("l/123").unwrap().num_chunks(), 123); assert_eq!(NumberType::from("l/123/456").unwrap().num_chunks(), 456); assert_eq!(NumberType::from("r/123").unwrap().num_chunks(), 123); diff --git a/src/uucore/src/lib/parser/parse_size.rs b/src/uucore/src/lib/parser/parse_size.rs index 63039e6093b..4d9968bb737 100644 --- a/src/uucore/src/lib/parser/parse_size.rs +++ b/src/uucore/src/lib/parser/parse_size.rs @@ -236,7 +236,7 @@ pub fn parse_size(size: &str) -> Result { } /// Same as `parse_size()`, except returns `u64::MAX` on overflow -/// GNU lib/coreutils include similar functionality +/// GNU lib/coreutils include similar functionality /// and GNU test suite checks this behavior for some utils pub fn parse_size_max(size: &str) -> Result { let result = Parser::default().parse(size); From 8be6338da9f87a49589752942186d328884c0d1a Mon Sep 17 00:00:00 2001 From: David Matos Date: Tue, 5 Sep 2023 23:35:14 +0200 Subject: [PATCH 046/208] cp: Error out if cp only contains source --- src/uu/cp/src/cp.rs | 3 +++ tests/by-util/test_cp.rs | 12 ++++++++++++ 2 files changed, 15 insertions(+) diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index 7dbf8aedb50..516f20fdad5 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -1073,6 +1073,9 @@ fn parse_path_args( if paths.is_empty() { // No files specified return Err("missing file operand".into()); + } else if paths.len() == 1 && options.target_dir.is_none() { + // Only one file specified + return Err(format!("missing destination file operand after {:?}", paths[0]).into()); } // Return an error if the user requested to copy more than one diff --git a/tests/by-util/test_cp.rs b/tests/by-util/test_cp.rs index f72316b5daa..c77be3d4ed9 100644 --- a/tests/by-util/test_cp.rs +++ b/tests/by-util/test_cp.rs @@ -3231,3 +3231,15 @@ fn test_cp_debug_sparse_always_reflink_auto() { panic!("Failure: stdout was \n{stdout_str}"); } } + +#[test] +fn test_cp_only_source_no_target() { + let ts = TestScenario::new(util_name!()); + let at = &ts.fixtures; + at.touch("a"); + let result = ts.ucmd().arg("a").fails(); + let stderr_str = result.stderr_str(); + if !stderr_str.contains("missing destination file operand after \"a\"") { + panic!("Failure: stderr was \n{stderr_str}"); + } +} From 2a0b4f88370f1ad787937d174b0ca8e6c2e4bf10 Mon Sep 17 00:00:00 2001 From: Yury Zhytkou <54360928+zhitkoff@users.noreply.github.com> Date: Tue, 5 Sep 2023 18:14:14 -0400 Subject: [PATCH 047/208] Update build-gnu.sh --- util/build-gnu.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/util/build-gnu.sh b/util/build-gnu.sh index b57b4fe769e..2d949bbb333 100755 --- a/util/build-gnu.sh +++ b/util/build-gnu.sh @@ -19,7 +19,6 @@ path_GNU="$(readlink -fm -- "${path_GNU:-${path_UUTILS}/../gnu}")" ### # On MacOS there is no system /usr/bin/timeout - # and trying to add it to /usr/bin (with symlink of copy binary) will fail unless system integrity protection is disabled (not ideal) # ref: https://support.apple.com/en-us/102149 # On MacOS the Homebrew coreutils could be installed and then "sudo ln -s /opt/homebrew/bin/timeout /usr/local/bin/timeout" From a0a9ee6491ca3402e9fdc3af5b7b3f5def3b8268 Mon Sep 17 00:00:00 2001 From: zhitkoff Date: Tue, 5 Sep 2023 18:38:22 -0400 Subject: [PATCH 048/208] split: fixing tests for parse_size_max() --- tests/by-util/test_split.rs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/tests/by-util/test_split.rs b/tests/by-util/test_split.rs index 053b6f8bf19..2d6474511fa 100644 --- a/tests/by-util/test_split.rs +++ b/tests/by-util/test_split.rs @@ -608,14 +608,6 @@ fn test_split_invalid_bytes_size() { .fails() .code_is(1) .stderr_only("split: invalid number of bytes: '1024R'\n"); - #[cfg(not(target_pointer_width = "128"))] - new_ucmd!() - .args(&["-b", "1Y"]) - .fails() - .code_is(1) - .stderr_only( - "split: invalid number of bytes: '1Y': Value too large for defined data type\n", - ); #[cfg(target_pointer_width = "32")] { let sizes = ["1000G", "10T"]; @@ -625,6 +617,18 @@ fn test_split_invalid_bytes_size() { } } +#[test] +fn test_split_overflow_bytes_size() { + #[cfg(not(target_pointer_width = "128"))] + let (at, mut ucmd) = at_and_ucmd!(); + let name = "test_split_overflow_bytes_size"; + RandomFile::new(&at, name).add_bytes(1000); + ucmd.args(&["-b", "1Y", name]).succeeds(); + let glob = Glob::new(&at, ".", r"x[[:alpha:]][[:alpha:]]$"); + assert_eq!(glob.count(), 1); + assert_eq!(glob.collate(), at.read_bytes(name)); +} + #[test] fn test_split_chunks_num_chunks_oversized_32() { #[cfg(target_pointer_width = "32")] From eaae32ec3bfbf842933b706001368314ee221851 Mon Sep 17 00:00:00 2001 From: zhitkoff Date: Tue, 5 Sep 2023 20:12:25 -0400 Subject: [PATCH 049/208] split: comments --- src/uu/split/src/split.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uu/split/src/split.rs b/src/uu/split/src/split.rs index 23520f7092b..6fb05dabb5f 100644 --- a/src/uu/split/src/split.rs +++ b/src/uu/split/src/split.rs @@ -354,7 +354,7 @@ enum NumberType { /// Assign lines via round-robin to the specified number of output /// chunks, but output only the *k*th chunk. - KthRoundRobin(u64, u64), // not yet implemented? + KthRoundRobin(u64, u64), } impl NumberType { From 5c93c592db69312c0460c66c78fdddb624fc7462 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Wed, 6 Sep 2023 09:47:50 +0200 Subject: [PATCH 050/208] uucore: turn version_cmp into a feature --- src/uu/ls/Cargo.toml | 2 +- src/uu/sort/Cargo.toml | 2 +- src/uucore/Cargo.toml | 1 + src/uucore/src/lib/features.rs | 2 ++ src/uucore/src/lib/{mods => features}/version_cmp.rs | 0 src/uucore/src/lib/lib.rs | 3 ++- src/uucore/src/lib/mods.rs | 1 - 7 files changed, 7 insertions(+), 4 deletions(-) rename src/uucore/src/lib/{mods => features}/version_cmp.rs (100%) diff --git a/src/uu/ls/Cargo.toml b/src/uu/ls/Cargo.toml index f3fc2eb6bfd..24b6947fd53 100644 --- a/src/uu/ls/Cargo.toml +++ b/src/uu/ls/Cargo.toml @@ -23,7 +23,7 @@ term_grid = { workspace = true } terminal_size = { workspace = true } glob = { workspace = true } lscolors = { workspace = true } -uucore = { workspace = true, features = ["entries", "fs"] } +uucore = { workspace = true, features = ["entries", "fs", "version-cmp"] } once_cell = { workspace = true } selinux = { workspace = true, optional = true } diff --git a/src/uu/sort/Cargo.toml b/src/uu/sort/Cargo.toml index 359a2b01274..981e7164144 100644 --- a/src/uu/sort/Cargo.toml +++ b/src/uu/sort/Cargo.toml @@ -27,7 +27,7 @@ rayon = { workspace = true } self_cell = { workspace = true } tempfile = { workspace = true } unicode-width = { workspace = true } -uucore = { workspace = true, features = ["fs"] } +uucore = { workspace = true, features = ["fs", "version-cmp"] } [[bin]] name = "sort" diff --git a/src/uucore/Cargo.toml b/src/uucore/Cargo.toml index fcbfdeac757..15a053ac4cd 100644 --- a/src/uucore/Cargo.toml +++ b/src/uucore/Cargo.toml @@ -100,4 +100,5 @@ sum = [ update-control = [] utf8 = [] utmpx = ["time", "time/macros", "libc", "dns-lookup"] +version-cmp = [] wide = [] diff --git a/src/uucore/src/lib/features.rs b/src/uucore/src/lib/features.rs index 786a6468239..49eee38439b 100644 --- a/src/uucore/src/lib/features.rs +++ b/src/uucore/src/lib/features.rs @@ -26,6 +26,8 @@ pub mod sum; mod tokenize; #[cfg(feature = "update-control")] pub mod update_control; +#[cfg(feature = "version-cmp")] +pub mod version_cmp; // * (platform-specific) feature-gated modules // ** non-windows (i.e. Unix + Fuchsia) diff --git a/src/uucore/src/lib/mods/version_cmp.rs b/src/uucore/src/lib/features/version_cmp.rs similarity index 100% rename from src/uucore/src/lib/mods/version_cmp.rs rename to src/uucore/src/lib/features/version_cmp.rs diff --git a/src/uucore/src/lib/lib.rs b/src/uucore/src/lib/lib.rs index 5b9f4ae1266..c0732f069a8 100644 --- a/src/uucore/src/lib/lib.rs +++ b/src/uucore/src/lib/lib.rs @@ -26,7 +26,6 @@ pub use crate::mods::line_ending; pub use crate::mods::os; pub use crate::mods::panic; pub use crate::mods::quoting_style; -pub use crate::mods::version_cmp; // * string parsing modules pub use crate::parser::parse_glob; @@ -55,6 +54,8 @@ pub use crate::features::ringbuffer; pub use crate::features::sum; #[cfg(feature = "update-control")] pub use crate::features::update_control; +#[cfg(feature = "version-cmp")] +pub use crate::features::version_cmp; // * (platform-specific) feature-gated modules // ** non-windows (i.e. Unix + Fuchsia) diff --git a/src/uucore/src/lib/mods.rs b/src/uucore/src/lib/mods.rs index caa8fcb9290..d3a2dc19e8b 100644 --- a/src/uucore/src/lib/mods.rs +++ b/src/uucore/src/lib/mods.rs @@ -9,6 +9,5 @@ pub mod error; pub mod line_ending; pub mod os; pub mod panic; -pub mod version_cmp; // dir and vdir also need access to the quoting_style module pub mod quoting_style; From d8a16a235149ab60099c93c889437314095426fb Mon Sep 17 00:00:00 2001 From: zhitkoff Date: Wed, 6 Sep 2023 12:42:49 -0400 Subject: [PATCH 051/208] split: tests --- tests/by-util/test_split.rs | 99 ++++++++++++++++++++++++++++++++++++- 1 file changed, 98 insertions(+), 1 deletion(-) diff --git a/tests/by-util/test_split.rs b/tests/by-util/test_split.rs index 2d6474511fa..da4a4475bec 100644 --- a/tests/by-util/test_split.rs +++ b/tests/by-util/test_split.rs @@ -377,6 +377,22 @@ fn test_split_obs_lines_standalone() { let glob = Glob::new(&at, ".", r"x[[:alpha:]][[:alpha:]]$"); assert_eq!(glob.count(), 2); assert_eq!(glob.collate(), at.read_bytes(name)); + ucmd.args(&["-99999999999999999991", name]).succeeds().no_stderr().no_stdout(); + let glob = Glob::new(&at, ".", r"x[[:alpha:]][[:alpha:]]$"); + assert_eq!(glob.count(), 2); + assert_eq!(glob.collate(), at.read_bytes(name)); +} + +/// Test for obsolete lines option standalone overflow +#[test] +fn test_split_obs_lines_standalone_overflow() { + let (at, mut ucmd) = at_and_ucmd!(); + let name = "obs-lines-standalone"; + RandomFile::new(&at, name).add_lines(4); + ucmd.args(&["-99999999999999999991", name]).succeeds().no_stderr().no_stdout(); + let glob = Glob::new(&at, ".", r"x[[:alpha:]][[:alpha:]]$"); + assert_eq!(glob.count(), 1); + assert_eq!(glob.collate(), at.read_bytes(name)); } /// Test for obsolete lines option as part of invalid combined short options @@ -756,7 +772,7 @@ creating file 'xaf' } #[test] -fn test_number() { +fn test_number_n() { let (at, mut ucmd) = at_and_ucmd!(); let file_read = |f| { let mut s = String::new(); @@ -771,6 +787,80 @@ fn test_number() { assert_eq!(file_read("xae"), "uvwxyz\n"); } +#[test] +fn test_number_kth_of_n() { + new_ucmd!() + .args(&["--number=3/5", "asciilowercase.txt"]) + .succeeds() + .stdout_only("klmno"); + new_ucmd!() + .args(&["-e", "--number=99/100", "asciilowercase.txt"]) + .succeeds() + .stdout_only(""); + new_ucmd!() + .args(&[ + "--number=r/9223372036854775807/18446744073709551615", + "asciilowercase.txt", + ]) + .succeeds() + .stdout_only(""); + new_ucmd!() + .args(&["--number=0/5", "asciilowercase.txt"]) + .fails() + .stderr_contains("split: invalid chunk number: 0"); + new_ucmd!() + .args(&["--number=10/5", "asciilowercase.txt"]) + .fails() + .stderr_contains("split: invalid chunk number: 10"); + new_ucmd!() + .args(&[ + "--number=9223372036854775807/18446744073709551616", + "asciilowercase.txt", + ]) + .fails() + .stderr_contains("split: invalid number of chunks: 18446744073709551616"); +} + +#[test] +fn test_number_kth_of_n_round_robin() { + new_ucmd!() + .args(&["--number", "r/2/3", "fivelines.txt"]) + .succeeds() + .stdout_only("2\n5\n"); + new_ucmd!() + .args(&["--number", "r/1/4", "fivelines.txt"]) + .succeeds() + .stdout_only("1\n5\n"); + new_ucmd!() + .args(&["-e", "--number", "r/7/7", "fivelines.txt"]) + .succeeds() + .stdout_only(""); + new_ucmd!() + .args(&[ + "--number", + "r/9223372036854775807/18446744073709551615", + "fivelines.txt", + ]) + .succeeds() + .stdout_only(""); + new_ucmd!() + .args(&[ + "--number", + "r/9223372036854775807/18446744073709551616", + "fivelines.txt", + ]) + .fails() + .stderr_contains("split: invalid number of chunks: 18446744073709551616"); + new_ucmd!() + .args(&["--number", "r/0/3", "fivelines.txt"]) + .fails() + .stderr_contains("split: invalid chunk number: 0"); + new_ucmd!() + .args(&["--number", "r/10/3", "fivelines.txt"]) + .fails() + .stderr_contains("split: invalid chunk number: 10"); +} + #[test] fn test_split_number_with_io_blksize() { let (at, mut ucmd) = at_and_ucmd!(); @@ -927,6 +1017,13 @@ fn test_line_bytes() { assert_eq!(at.read("xad"), "ee\n"); } +#[test] +fn test_line_bytes_overflow() { + let (at, mut ucmd) = at_and_ucmd!(); + ucmd.args(&["-C", "18446744073709551616", "letters.txt"]).succeeds(); + assert_eq!(at.read("xaa"), "aaaaaaaaa\nbbbb\ncccc\ndd\nee\n"); +} + #[test] fn test_line_bytes_concatenated_with_value() { let (at, mut ucmd) = at_and_ucmd!(); From e378454a265b103b98e940d5ed9b691985433f54 Mon Sep 17 00:00:00 2001 From: zhitkoff Date: Wed, 6 Sep 2023 13:15:35 -0400 Subject: [PATCH 052/208] split: formatting --- tests/by-util/test_split.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/tests/by-util/test_split.rs b/tests/by-util/test_split.rs index da4a4475bec..3ea74606cae 100644 --- a/tests/by-util/test_split.rs +++ b/tests/by-util/test_split.rs @@ -377,7 +377,10 @@ fn test_split_obs_lines_standalone() { let glob = Glob::new(&at, ".", r"x[[:alpha:]][[:alpha:]]$"); assert_eq!(glob.count(), 2); assert_eq!(glob.collate(), at.read_bytes(name)); - ucmd.args(&["-99999999999999999991", name]).succeeds().no_stderr().no_stdout(); + ucmd.args(&["-99999999999999999991", name]) + .succeeds() + .no_stderr() + .no_stdout(); let glob = Glob::new(&at, ".", r"x[[:alpha:]][[:alpha:]]$"); assert_eq!(glob.count(), 2); assert_eq!(glob.collate(), at.read_bytes(name)); @@ -389,7 +392,10 @@ fn test_split_obs_lines_standalone_overflow() { let (at, mut ucmd) = at_and_ucmd!(); let name = "obs-lines-standalone"; RandomFile::new(&at, name).add_lines(4); - ucmd.args(&["-99999999999999999991", name]).succeeds().no_stderr().no_stdout(); + ucmd.args(&["-99999999999999999991", name]) + .succeeds() + .no_stderr() + .no_stdout(); let glob = Glob::new(&at, ".", r"x[[:alpha:]][[:alpha:]]$"); assert_eq!(glob.count(), 1); assert_eq!(glob.collate(), at.read_bytes(name)); @@ -1020,7 +1026,8 @@ fn test_line_bytes() { #[test] fn test_line_bytes_overflow() { let (at, mut ucmd) = at_and_ucmd!(); - ucmd.args(&["-C", "18446744073709551616", "letters.txt"]).succeeds(); + ucmd.args(&["-C", "18446744073709551616", "letters.txt"]) + .succeeds(); assert_eq!(at.read("xaa"), "aaaaaaaaa\nbbbb\ncccc\ndd\nee\n"); } From 4fd598e4d513d3fec00876e3c897c7bd6024508f Mon Sep 17 00:00:00 2001 From: zhitkoff Date: Wed, 6 Sep 2023 13:20:58 -0400 Subject: [PATCH 053/208] split: tests --- tests/by-util/test_split.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/tests/by-util/test_split.rs b/tests/by-util/test_split.rs index 3ea74606cae..c3e8445a625 100644 --- a/tests/by-util/test_split.rs +++ b/tests/by-util/test_split.rs @@ -377,13 +377,6 @@ fn test_split_obs_lines_standalone() { let glob = Glob::new(&at, ".", r"x[[:alpha:]][[:alpha:]]$"); assert_eq!(glob.count(), 2); assert_eq!(glob.collate(), at.read_bytes(name)); - ucmd.args(&["-99999999999999999991", name]) - .succeeds() - .no_stderr() - .no_stdout(); - let glob = Glob::new(&at, ".", r"x[[:alpha:]][[:alpha:]]$"); - assert_eq!(glob.count(), 2); - assert_eq!(glob.collate(), at.read_bytes(name)); } /// Test for obsolete lines option standalone overflow From 1669a92694985c8e2ad3df661fe457731698d172 Mon Sep 17 00:00:00 2001 From: zhitkoff Date: Wed, 6 Sep 2023 14:02:08 -0400 Subject: [PATCH 054/208] split: tests overflow --- tests/by-util/test_split.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/by-util/test_split.rs b/tests/by-util/test_split.rs index c3e8445a625..849fdc7cfec 100644 --- a/tests/by-util/test_split.rs +++ b/tests/by-util/test_split.rs @@ -2,7 +2,7 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -// spell-checker:ignore xzaaa sixhundredfiftyonebytes ninetyonebytes threebytes asciilowercase fghij klmno pqrst uvwxyz fivelines twohundredfortyonebytes onehundredlines nbbbb dxen +// spell-checker:ignore xzaaa sixhundredfiftyonebytes ninetyonebytes threebytes asciilowercase fghij klmno pqrst uvwxyz fivelines twohundredfortyonebytes onehundredlines nbbbb dxen ncccc use crate::common::util::{AtPath, TestScenario}; use rand::{thread_rng, Rng, SeedableRng}; @@ -796,6 +796,7 @@ fn test_number_kth_of_n() { .args(&["-e", "--number=99/100", "asciilowercase.txt"]) .succeeds() .stdout_only(""); + #[cfg(target_pointer_width = "64")] new_ucmd!() .args(&[ "--number=r/9223372036854775807/18446744073709551615", @@ -811,6 +812,7 @@ fn test_number_kth_of_n() { .args(&["--number=10/5", "asciilowercase.txt"]) .fails() .stderr_contains("split: invalid chunk number: 10"); + #[cfg(target_pointer_width = "64")] new_ucmd!() .args(&[ "--number=9223372036854775807/18446744073709551616", @@ -834,6 +836,7 @@ fn test_number_kth_of_n_round_robin() { .args(&["-e", "--number", "r/7/7", "fivelines.txt"]) .succeeds() .stdout_only(""); + #[cfg(target_pointer_width = "64")] new_ucmd!() .args(&[ "--number", @@ -842,6 +845,7 @@ fn test_number_kth_of_n_round_robin() { ]) .succeeds() .stdout_only(""); + #[cfg(target_pointer_width = "64")] new_ucmd!() .args(&[ "--number", @@ -1018,6 +1022,7 @@ fn test_line_bytes() { #[test] fn test_line_bytes_overflow() { + #[cfg(target_pointer_width = "64")] let (at, mut ucmd) = at_and_ucmd!(); ucmd.args(&["-C", "18446744073709551616", "letters.txt"]) .succeeds(); From fbf5ac4329b83c8c6e6ffb3f87ab7c2fea09e263 Mon Sep 17 00:00:00 2001 From: zhitkoff Date: Wed, 6 Sep 2023 14:16:21 -0400 Subject: [PATCH 055/208] split: tests 32bit --- tests/by-util/test_split.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/by-util/test_split.rs b/tests/by-util/test_split.rs index 849fdc7cfec..247b3cc5786 100644 --- a/tests/by-util/test_split.rs +++ b/tests/by-util/test_split.rs @@ -1021,8 +1021,8 @@ fn test_line_bytes() { } #[test] +#[cfg(target_pointer_width = "64")] fn test_line_bytes_overflow() { - #[cfg(target_pointer_width = "64")] let (at, mut ucmd) = at_and_ucmd!(); ucmd.args(&["-C", "18446744073709551616", "letters.txt"]) .succeeds(); From e40e88702270930470d99301118e03eaba37ca6d Mon Sep 17 00:00:00 2001 From: zhitkoff Date: Wed, 6 Sep 2023 18:43:20 -0400 Subject: [PATCH 056/208] split: some refactoring for handle_obsolete() --- src/uu/split/src/split.rs | 241 +++++++++++++++++++++++--------------- 1 file changed, 146 insertions(+), 95 deletions(-) diff --git a/src/uu/split/src/split.rs b/src/uu/split/src/split.rs index 6fb05dabb5f..65032dfd9ed 100644 --- a/src/uu/split/src/split.rs +++ b/src/uu/split/src/split.rs @@ -75,104 +75,152 @@ fn handle_obsolete(args: impl uucore::Args) -> (Vec, Option) { let mut obs_lines = None; let mut preceding_long_opt_req_value = false; let mut preceding_short_opt_req_value = false; + let filtered_args = args .filter_map(|os_slice| { - let filter: Option; - if let Some(slice) = os_slice.to_str() { - // check if the slice is a true short option (and not hyphen prefixed value of an option) - // and if so, a short option that can contain obsolete lines value - if slice.starts_with('-') - && !slice.starts_with("--") - && !preceding_long_opt_req_value - && !preceding_short_opt_req_value - && !slice.starts_with("-a") - && !slice.starts_with("-b") - && !slice.starts_with("-C") - && !slice.starts_with("-l") - && !slice.starts_with("-n") - { - // start of the short option string - // that can have obsolete lines option value in it - // extract numeric part and filter it out - let mut obs_lines_extracted: Vec = vec![]; - let mut obs_lines_end_reached = false; - let filtered_slice: Vec = slice - .chars() - .filter(|c| { - // To correctly process scenario like '-x200a4' - // we need to stop extracting digits once alphabetic character is encountered - // after we already have something in obs_lines_extracted - if c.is_ascii_digit() && !obs_lines_end_reached { - obs_lines_extracted.push(*c); - false - } else { - if !obs_lines_extracted.is_empty() { - obs_lines_end_reached = true; - } - true - } - }) - .collect(); - - if obs_lines_extracted.is_empty() { - // no obsolete lines value found/extracted - filter = Some(OsString::from(slice)); - } else { - // obsolete lines value was extracted - obs_lines = Some(obs_lines_extracted.iter().collect()); - if filtered_slice.get(1).is_some() { - // there were some short options in front of or after obsolete lines value - // i.e. '-xd100' or '-100de' or similar, which after extraction of obsolete lines value - // would look like '-xd' or '-de' or similar - let filtered_slice: String = filtered_slice.iter().collect(); - filter = Some(OsString::from(filtered_slice)); - } else { - filter = None; - } - } - } else { - // either not a short option - // or a short option that cannot have obsolete lines value in it - filter = Some(OsString::from(slice)); - } - // capture if current slice is a preceding long option that requires value and does not use '=' to assign that value - // following slice should be treaded as value for this option - // even if it starts with '-' (which would be treated as hyphen prefixed value) - if slice.starts_with("--") { - preceding_long_opt_req_value = &slice[2..] == OPT_BYTES - || &slice[2..] == OPT_LINE_BYTES - || &slice[2..] == OPT_LINES - || &slice[2..] == OPT_ADDITIONAL_SUFFIX - || &slice[2..] == OPT_FILTER - || &slice[2..] == OPT_NUMBER - || &slice[2..] == OPT_SUFFIX_LENGTH; - } - // capture if current slice is a preceding short option that requires value and does not have value in the same slice (value separated by whitespace) - // following slice should be treaded as value for this option - // even if it starts with '-' (which would be treated as hyphen prefixed value) - preceding_short_opt_req_value = slice == "-b" - || slice == "-C" - || slice == "-l" - || slice == "-n" - || slice == "-a"; - // slice is a value - // reset preceding option flags - if !slice.starts_with('-') { - preceding_short_opt_req_value = false; - preceding_long_opt_req_value = false; - } + filter_args( + os_slice, + &mut obs_lines, + &mut preceding_long_opt_req_value, + &mut preceding_short_opt_req_value, + ) + }) + .collect(); + + (filtered_args, obs_lines) +} + +/// Helper function to [`handle_obsolete`] +/// Filters out obsolete lines option from args +fn filter_args( + os_slice: OsString, + obs_lines: &mut Option, + preceding_long_opt_req_value: &mut bool, + preceding_short_opt_req_value: &mut bool, +) -> Option { + let filter: Option; + if let Some(slice) = os_slice.to_str() { + if should_extract_obs_lines( + slice, + preceding_long_opt_req_value, + preceding_short_opt_req_value, + ) { + // start of the short option string + // that can have obsolete lines option value in it + filter = handle_extract_obs_lines(slice, obs_lines); + } else { + // either not a short option + // or a short option that cannot have obsolete lines value in it + filter = Some(OsString::from(slice)); + } + handle_preceding_options( + slice, + preceding_long_opt_req_value, + preceding_short_opt_req_value, + ); + } else { + // Cannot cleanly convert os_slice to UTF-8 + // Do not process and return as-is + // This will cause failure later on, but we should not handle it here + // and let clap panic on invalid UTF-8 argument + filter = Some(os_slice); + } + filter +} + +/// Helper function to [`filter_args`] +/// Checks if the slice is a true short option (and not hyphen prefixed value of an option) +/// and if so, a short option that can contain obsolete lines value +fn should_extract_obs_lines( + slice: &str, + preceding_long_opt_req_value: &bool, + preceding_short_opt_req_value: &bool, +) -> bool { + slice.starts_with('-') + && !slice.starts_with("--") + && !preceding_long_opt_req_value + && !preceding_short_opt_req_value + && !slice.starts_with("-a") + && !slice.starts_with("-b") + && !slice.starts_with("-C") + && !slice.starts_with("-l") + && !slice.starts_with("-n") +} + +/// Helper function to [`filter_args`] +/// Extracts obsolete lines numeric part from argument slice +/// and filters it out +fn handle_extract_obs_lines(slice: &str, obs_lines: &mut Option) -> Option { + let mut obs_lines_extracted: Vec = vec![]; + let mut obs_lines_end_reached = false; + let filtered_slice: Vec = slice + .chars() + .filter(|c| { + // To correctly process scenario like '-x200a4' + // we need to stop extracting digits once alphabetic character is encountered + // after we already have something in obs_lines_extracted + if c.is_ascii_digit() && !obs_lines_end_reached { + obs_lines_extracted.push(*c); + false } else { - // Cannot cleanly convert os_slice to UTF-8 - // Do not process and return as-is - // This will cause failure later on, but we should not handle it here - // and let clap panic on invalid UTF-8 argument - filter = Some(os_slice); + if !obs_lines_extracted.is_empty() { + obs_lines_end_reached = true; + } + true } - // return filter - filter }) .collect(); - (filtered_args, obs_lines) + + if obs_lines_extracted.is_empty() { + // no obsolete lines value found/extracted + Some(OsString::from(slice)) + } else { + // obsolete lines value was extracted + let extracted: String = obs_lines_extracted.iter().collect(); + *obs_lines = Some(extracted); + if filtered_slice.get(1).is_some() { + // there were some short options in front of or after obsolete lines value + // i.e. '-xd100' or '-100de' or similar, which after extraction of obsolete lines value + // would look like '-xd' or '-de' or similar + let filtered_slice: String = filtered_slice.iter().collect(); + Some(OsString::from(filtered_slice)) + } else { + None + } + } +} + +/// Helper function to [`handle_extract_obs_lines`] +/// Captures if current slice is a preceding option +/// that requires value +fn handle_preceding_options( + slice: &str, + preceding_long_opt_req_value: &mut bool, + preceding_short_opt_req_value: &mut bool, +) { + // capture if current slice is a preceding long option that requires value and does not use '=' to assign that value + // following slice should be treaded as value for this option + // even if it starts with '-' (which would be treated as hyphen prefixed value) + if slice.starts_with("--") { + *preceding_long_opt_req_value = &slice[2..] == OPT_BYTES + || &slice[2..] == OPT_LINE_BYTES + || &slice[2..] == OPT_LINES + || &slice[2..] == OPT_ADDITIONAL_SUFFIX + || &slice[2..] == OPT_FILTER + || &slice[2..] == OPT_NUMBER + || &slice[2..] == OPT_SUFFIX_LENGTH; + } + // capture if current slice is a preceding short option that requires value and does not have value in the same slice (value separated by whitespace) + // following slice should be treaded as value for this option + // even if it starts with '-' (which would be treated as hyphen prefixed value) + *preceding_short_opt_req_value = + slice == "-b" || slice == "-C" || slice == "-l" || slice == "-n" || slice == "-a"; + // slice is a value + // reset preceding option flags + if !slice.starts_with('-') { + *preceding_short_opt_req_value = false; + *preceding_long_opt_req_value = false; + } } pub fn uu_app() -> Command { @@ -430,6 +478,9 @@ impl NumberType { /// or if `K` is greater than `N` /// then this function returns [`NumberTypeError`]. fn from(s: &str) -> Result { + fn is_invalid_chunk(chunk_number: u64, num_chunks: u64) -> bool { + chunk_number > num_chunks || chunk_number == 0 + } let parts: Vec<&str> = s.split('/').collect(); match &parts[..] { [n_str] => { @@ -446,7 +497,7 @@ impl NumberType { .map_err(|_| NumberTypeError::NumberOfChunks(n_str.to_string()))?; let chunk_number = parse_size(k_str) .map_err(|_| NumberTypeError::ChunkNumber(k_str.to_string()))?; - if chunk_number > num_chunks || chunk_number == 0 { + if is_invalid_chunk(chunk_number, num_chunks) { return Err(NumberTypeError::ChunkNumber(k_str.to_string())); } Ok(Self::KthBytes(chunk_number, num_chunks)) @@ -461,7 +512,7 @@ impl NumberType { .map_err(|_| NumberTypeError::NumberOfChunks(n_str.to_string()))?; let chunk_number = parse_size(k_str) .map_err(|_| NumberTypeError::ChunkNumber(k_str.to_string()))?; - if chunk_number > num_chunks || chunk_number == 0 { + if is_invalid_chunk(chunk_number, num_chunks) { return Err(NumberTypeError::ChunkNumber(k_str.to_string())); } Ok(Self::KthLines(chunk_number, num_chunks)) @@ -476,7 +527,7 @@ impl NumberType { .map_err(|_| NumberTypeError::NumberOfChunks(n_str.to_string()))?; let chunk_number = parse_size(k_str) .map_err(|_| NumberTypeError::ChunkNumber(k_str.to_string()))?; - if chunk_number > num_chunks || chunk_number == 0 { + if is_invalid_chunk(chunk_number, num_chunks) { return Err(NumberTypeError::ChunkNumber(k_str.to_string())); } Ok(Self::KthRoundRobin(chunk_number, num_chunks)) From 3be284e0d94eb0c8d61e7ab684a9ff1bec5a4196 Mon Sep 17 00:00:00 2001 From: zhitkoff Date: Wed, 6 Sep 2023 19:49:26 -0400 Subject: [PATCH 057/208] split: more test coverage --- src/uu/split/src/split.rs | 2 +- tests/by-util/test_split.rs | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/uu/split/src/split.rs b/src/uu/split/src/split.rs index 65032dfd9ed..7762789ac6e 100644 --- a/src/uu/split/src/split.rs +++ b/src/uu/split/src/split.rs @@ -1295,7 +1295,7 @@ where // If we would have written zero chunks of output, then terminate // immediately. This happens on `split -e -n 3 /dev/null`, for // example. - if num_chunks == 0 { + if num_chunks == 0 || num_bytes == 0 { return Ok(()); } diff --git a/tests/by-util/test_split.rs b/tests/by-util/test_split.rs index 247b3cc5786..bbac3c73924 100644 --- a/tests/by-util/test_split.rs +++ b/tests/by-util/test_split.rs @@ -340,6 +340,18 @@ fn test_split_lines_number() { .succeeds() .no_stderr() .no_stdout(); + scene + .ucmd() + .args(&["--lines", "0", "file"]) + .fails() + .code_is(1) + .stderr_only("split: invalid number of lines: 0\n"); + scene + .ucmd() + .args(&["-0", "file"]) + .fails() + .code_is(1) + .stderr_only("split: invalid number of lines: 0\n"); scene .ucmd() .args(&["--lines", "2fb", "file"]) @@ -669,6 +681,15 @@ fn test_split_stdin_num_chunks() { .stderr_only("split: -: cannot determine file size\n"); } +#[test] +fn test_split_stdin_num_kth_chunk() { + new_ucmd!() + .args(&["--number=1/2"]) + .fails() + .code_is(1) + .stderr_only("split: -: cannot determine file size\n"); +} + fn file_read(at: &AtPath, filename: &str) -> String { let mut s = String::new(); at.open(filename).read_to_string(&mut s).unwrap(); @@ -784,6 +805,10 @@ fn test_number_n() { assert_eq!(file_read("xac"), "klmno"); assert_eq!(file_read("xad"), "pqrst"); assert_eq!(file_read("xae"), "uvwxyz\n"); + new_ucmd!() + .args(&["--number=100", "/dev/null"]) + .succeeds() + .stdout_only(""); } #[test] @@ -792,10 +817,18 @@ fn test_number_kth_of_n() { .args(&["--number=3/5", "asciilowercase.txt"]) .succeeds() .stdout_only("klmno"); + new_ucmd!() + .args(&["--number=5/5", "asciilowercase.txt"]) + .succeeds() + .stdout_only("uvwxyz\n"); new_ucmd!() .args(&["-e", "--number=99/100", "asciilowercase.txt"]) .succeeds() .stdout_only(""); + new_ucmd!() + .args(&["--number=3/10", "/dev/null"]) + .succeeds() + .stdout_only(""); #[cfg(target_pointer_width = "64")] new_ucmd!() .args(&[ From 8883f016d4ffd9929949130ab724a20b1c3cb132 Mon Sep 17 00:00:00 2001 From: zhitkoff Date: Wed, 6 Sep 2023 20:09:26 -0400 Subject: [PATCH 058/208] split: fix windows tests --- tests/by-util/test_split.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/by-util/test_split.rs b/tests/by-util/test_split.rs index bbac3c73924..c204a6238d2 100644 --- a/tests/by-util/test_split.rs +++ b/tests/by-util/test_split.rs @@ -805,6 +805,7 @@ fn test_number_n() { assert_eq!(file_read("xac"), "klmno"); assert_eq!(file_read("xad"), "pqrst"); assert_eq!(file_read("xae"), "uvwxyz\n"); + #[cfg(unix)] new_ucmd!() .args(&["--number=100", "/dev/null"]) .succeeds() @@ -825,6 +826,7 @@ fn test_number_kth_of_n() { .args(&["-e", "--number=99/100", "asciilowercase.txt"]) .succeeds() .stdout_only(""); + #[cfg(unix)] new_ucmd!() .args(&["--number=3/10", "/dev/null"]) .succeeds() From 3f065eed8a265e24ea8790a8680dd62410c5f024 Mon Sep 17 00:00:00 2001 From: zhitkoff Date: Wed, 6 Sep 2023 21:04:01 -0400 Subject: [PATCH 059/208] split: fixing test for 32bit --- tests/by-util/test_split.rs | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/tests/by-util/test_split.rs b/tests/by-util/test_split.rs index c204a6238d2..5eba5071f64 100644 --- a/tests/by-util/test_split.rs +++ b/tests/by-util/test_split.rs @@ -657,19 +657,17 @@ fn test_split_overflow_bytes_size() { } #[test] +#[cfg(target_pointer_width = "32")] fn test_split_chunks_num_chunks_oversized_32() { - #[cfg(target_pointer_width = "32")] - { - let scene = TestScenario::new(util_name!()); - let at = &scene.fixtures; - at.touch("file"); - scene - .ucmd() - .args(&["--number", "5000000000", "file"]) - .fails() - .code_is(1) - .stderr_only("split: Number of chunks too big\n"); - } + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + at.touch("file"); + scene + .ucmd() + .args(&["--number", "5000000000", "sixhundredfiftyonebytes.txt"]) + .fails() + .code_is(1) + .stderr_only("split: Number of chunks too big\n"); } #[test] From e3f6fd3c3f6f9d80078789e79e2a80b22708461b Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Thu, 7 Sep 2023 20:48:26 +0100 Subject: [PATCH 060/208] truncate clippy fix. --- src/uu/truncate/src/truncate.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uu/truncate/src/truncate.rs b/src/uu/truncate/src/truncate.rs index 3b1e9bd0910..6e1c19fde9f 100644 --- a/src/uu/truncate/src/truncate.rs +++ b/src/uu/truncate/src/truncate.rs @@ -102,7 +102,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { .unwrap_or_default(); if files.is_empty() { - return Err(UUsageError::new(1, "missing file operand")); + Err(UUsageError::new(1, "missing file operand")) } else { let io_blocks = matches.get_flag(options::IO_BLOCKS); let no_create = matches.get_flag(options::NO_CREATE); From a63603eea81a8999c0e4bd2d5ccc48d2203a3a4b Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Thu, 7 Sep 2023 22:08:11 +0200 Subject: [PATCH 061/208] echo's doc doesn't show correctly --- src/uu/echo/echo.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/uu/echo/echo.md b/src/uu/echo/echo.md index 4330ecd8837..f6f6f37ffcd 100644 --- a/src/uu/echo/echo.md +++ b/src/uu/echo/echo.md @@ -12,15 +12,15 @@ Echo the STRING(s) to standard output. If -e is in effect, the following sequences are recognized: -\\ backslash -\a alert (BEL) -\b backspace -\c produce no further output -\e escape -\f form feed -\n new line -\r carriage return -\t horizontal tab -\v vertical tab -\0NNN byte with octal value NNN (1 to 3 digits) -\xHH byte with hexadecimal value HH (1 to 2 digits) \ No newline at end of file +- `\` backslash +- `\a` alert (BEL) +- `\b` backspace +- `\c` produce no further output +- `\e` escape +- `\f` form feed +- `\n` new line +- `\r` carriage return +- `\t` horizontal tab +- `\v` vertical tab +- `\0NNN` byte with octal value NNN (1 to 3 digits) +- `\xHH` byte with hexadecimal value HH (1 to 2 digits) From 6a63acc983d2b4b3e9c6b8f7ec325501ba5523f6 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Fri, 8 Sep 2023 07:27:19 +0200 Subject: [PATCH 062/208] deny.toml: add redox_syscall to skip list --- deny.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/deny.toml b/deny.toml index 678fc352dc3..154b2bfe848 100644 --- a/deny.toml +++ b/deny.toml @@ -89,6 +89,8 @@ skip = [ { name = "bitflags", version = "1.3.2" }, # blake2b_simd { name = "constant_time_eq", version = "0.2.6" }, + # various crates + { name = "redox_syscall", version = "0.3.5" }, ] # spell-checker: enable From 7d2996bb1a8511563ee074c94dd0e8c4a84ea60a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 8 Sep 2023 06:51:03 +0000 Subject: [PATCH 063/208] chore(deps): update rust crate redox_syscall to 0.4 --- Cargo.lock | 17 +++++++++++++---- Cargo.toml | 2 +- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0f18b8b727f..c6f76c75b75 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -843,7 +843,7 @@ checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.3.5", "windows-sys 0.48.0", ] @@ -1510,7 +1510,7 @@ checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.3.5", "smallvec", "windows-targets 0.48.0", ] @@ -1743,6 +1743,15 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_syscall" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded0bce2d41cc3c57aefa284708ced249a64acb01745dbbe72bd78610bfd644c" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "reference-counted-singleton" version = "0.1.2" @@ -2115,7 +2124,7 @@ checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" dependencies = [ "cfg-if", "fastrand", - "redox_syscall", + "redox_syscall 0.3.5", "rustix 0.38.8", "windows-sys 0.48.0", ] @@ -3085,7 +3094,7 @@ version = "0.0.21" dependencies = [ "clap", "libc", - "redox_syscall", + "redox_syscall 0.4.0", "uucore", ] diff --git a/Cargo.toml b/Cargo.toml index 88047df009e..8ba13859636 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -309,7 +309,7 @@ quick-error = "2.0.1" rand = { version = "0.8", features = ["small_rng"] } rand_core = "0.6" rayon = "1.7" -redox_syscall = "0.3" +redox_syscall = "0.4" regex = "1.9.5" rstest = "0.18.2" rust-ini = "0.19.0" From 2c96a0d741e182f0fc7c28258ee248c5f192ff85 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Mon, 4 Sep 2023 14:46:01 +0200 Subject: [PATCH 064/208] Bump nix and ctrlc nix from 0.26.2 -> 0.27.1 ctrlc from 3.4.0 -> 3.4.1 --- Cargo.lock | 17 +++++------------ Cargo.toml | 2 +- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c6f76c75b75..ff9d65e469c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -684,9 +684,9 @@ dependencies = [ [[package]] name = "ctrlc" -version = "3.4.0" +version = "3.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a011bbe2c35ce9c1f143b7af6f94f29a167beb4cd1d29e6740ce836f723120e" +checksum = "82e95fbd621905b854affdc67943b043a0fbb6ed7385fd5a25650d19a8a6cfdf" dependencies = [ "nix", "windows-sys 0.48.0", @@ -1343,14 +1343,13 @@ dependencies = [ [[package]] name = "nix" -version = "0.26.2" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.3.3", "cfg-if", "libc", - "static_assertions", ] [[package]] @@ -2076,12 +2075,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - [[package]] name = "strsim" version = "0.10.0" diff --git a/Cargo.toml b/Cargo.toml index 8ba13859636..fa45b2bb179 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -293,7 +293,7 @@ lscolors = { version = "0.15.0", default-features = false, features = [ ] } memchr = "2" memmap2 = "0.7" -nix = { version = "0.26", default-features = false } +nix = { version = "0.27", default-features = false } nom = "7.1.3" notify = { version = "=6.0.1", features = ["macos_kqueue"] } num-bigint = "0.4.4" From 36d5013fac3d4fd4158d721e2fa24b685c775a07 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Thu, 7 Sep 2023 15:07:20 +0200 Subject: [PATCH 065/208] stty: adapt to API change in nix 0.27.x tcgetattr(fd: RawFd) changed to tcgetattr(fd: Fd), with RawFd not implementing AsFd. A similar change was applied to tcsetattr. --- src/uu/stty/src/stty.rs | 56 +++++++++++++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 13 deletions(-) diff --git a/src/uu/stty/src/stty.rs b/src/uu/stty/src/stty.rs index d55870730aa..669285750b4 100644 --- a/src/uu/stty/src/stty.rs +++ b/src/uu/stty/src/stty.rs @@ -14,10 +14,12 @@ use nix::sys::termios::{ OutputFlags, SpecialCharacterIndices, Termios, }; use nix::{ioctl_read_bad, ioctl_write_ptr_bad}; -use std::io::{self, stdout}; +use std::fs::File; +use std::io::{self, stdout, Stdout}; use std::ops::ControlFlow; +use std::os::fd::{AsFd, BorrowedFd}; use std::os::unix::fs::OpenOptionsExt; -use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd}; +use std::os::unix::io::{AsRawFd, RawFd}; use uucore::error::{UResult, USimpleError}; use uucore::{format_usage, help_about, help_usage}; @@ -91,10 +93,33 @@ mod options { struct Options<'a> { all: bool, save: bool, - file: RawFd, + file: Device, settings: Option>, } +enum Device { + File(File), + Stdout(Stdout), +} + +impl AsFd for Device { + fn as_fd(&self) -> BorrowedFd<'_> { + match self { + Self::File(f) => f.as_fd(), + Self::Stdout(stdout) => stdout.as_fd(), + } + } +} + +impl AsRawFd for Device { + fn as_raw_fd(&self) -> RawFd { + match self { + Self::File(f) => f.as_raw_fd(), + Self::Stdout(stdout) => stdout.as_raw_fd(), + } + } +} + impl<'a> Options<'a> { fn from(matches: &'a ArgMatches) -> io::Result { Ok(Self { @@ -110,12 +135,13 @@ impl<'a> Options<'a> { // will clean up the FD for us on exit, so it doesn't // matter. The alternative would be to have an enum of // BorrowedFd/OwnedFd to handle both cases. - Some(f) => std::fs::OpenOptions::new() - .read(true) - .custom_flags(O_NONBLOCK) - .open(f)? - .into_raw_fd(), - None => stdout().as_raw_fd(), + Some(f) => Device::File( + std::fs::OpenOptions::new() + .read(true) + .custom_flags(O_NONBLOCK) + .open(f)?, + ), + None => Device::Stdout(stdout()), }, settings: matches .get_many::(options::SETTINGS) @@ -175,7 +201,7 @@ fn stty(opts: &Options) -> UResult<()> { } // TODO: Figure out the right error message for when tcgetattr fails - let mut termios = tcgetattr(opts.file).expect("Could not get terminal attributes"); + let mut termios = tcgetattr(opts.file.as_fd()).expect("Could not get terminal attributes"); if let Some(settings) = &opts.settings { for setting in settings { @@ -187,8 +213,12 @@ fn stty(opts: &Options) -> UResult<()> { } } - tcsetattr(opts.file, nix::sys::termios::SetArg::TCSANOW, &termios) - .expect("Could not write terminal attributes"); + tcsetattr( + opts.file.as_fd(), + nix::sys::termios::SetArg::TCSANOW, + &termios, + ) + .expect("Could not write terminal attributes"); } else { print_settings(&termios, opts).expect("TODO: make proper error here from nix error"); } @@ -228,7 +258,7 @@ fn print_terminal_size(termios: &Termios, opts: &Options) -> nix::Result<()> { if opts.all { let mut size = TermSize::default(); - unsafe { tiocgwinsz(opts.file, &mut size as *mut _)? }; + unsafe { tiocgwinsz(opts.file.as_raw_fd(), &mut size as *mut _)? }; print!("rows {}; columns {}; ", size.rows, size.columns); } From 7679e908184e73035079fdf74382d155ce8c8574 Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Sat, 9 Sep 2023 08:47:08 +0100 Subject: [PATCH 066/208] clippy: remove some unnecessary mut removing useless mutability mostly. --- src/uu/cat/src/splice.rs | 2 +- src/uu/cksum/src/cksum.rs | 3 +-- src/uu/env/src/env.rs | 5 ++++- src/uu/hashsum/src/hashsum.rs | 3 +-- src/uu/sort/src/ext_sort.rs | 6 +++--- 5 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/uu/cat/src/splice.rs b/src/uu/cat/src/splice.rs index 5a9e8738ed2..6c2b6d3dac3 100644 --- a/src/uu/cat/src/splice.rs +++ b/src/uu/cat/src/splice.rs @@ -21,7 +21,7 @@ const BUF_SIZE: usize = 1024 * 16; /// copying or not. False means we don't have to. #[inline] pub(super) fn write_fast_using_splice( - handle: &mut InputHandle, + handle: &InputHandle, write_fd: &impl AsRawFd, ) -> CatResult { let (pipe_rd, pipe_wr) = pipe()?; diff --git a/src/uu/cksum/src/cksum.rs b/src/uu/cksum/src/cksum.rs index 6c9c795821b..629bb457fd8 100644 --- a/src/uu/cksum/src/cksum.rs +++ b/src/uu/cksum/src/cksum.rs @@ -207,8 +207,7 @@ fn digest_read( Ok((digest.result_str(), output_size)) } else { // Assume it's SHAKE. result_str() doesn't work with shake (as of 8/30/2016) - let mut bytes = Vec::new(); - bytes.resize((output_bits + 7) / 8, 0); + let mut bytes = vec![0; (output_bits + 7) / 8]; digest.hash_finalize(&mut bytes); Ok((encode(bytes), output_size)) } diff --git a/src/uu/env/src/env.rs b/src/uu/env/src/env.rs index e031e39e31f..d7c9687de60 100644 --- a/src/uu/env/src/env.rs +++ b/src/uu/env/src/env.rs @@ -101,7 +101,7 @@ fn load_config_file(opts: &mut Options) -> UResult<()> { #[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 Vec<&'b str>) -> (Cow<'b, str>, &'a [&'b str]) { let progname = Cow::from(args[0]); (progname, &args[1..]) } @@ -303,7 +303,10 @@ fn run_env(args: impl uucore::Args) -> UResult<()> { print_env(opts.line_ending); } else { // we need to execute a command + #[cfg(windows)] let (prog, args) = build_command(&mut opts.program); + #[cfg(not(windows))] + let (prog, args) = build_command(&opts.program); /* * On Unix-like systems Command::status either ends up calling either fork or posix_spawnp diff --git a/src/uu/hashsum/src/hashsum.rs b/src/uu/hashsum/src/hashsum.rs index f8f00f01119..d27b09b98ed 100644 --- a/src/uu/hashsum/src/hashsum.rs +++ b/src/uu/hashsum/src/hashsum.rs @@ -803,8 +803,7 @@ fn digest_reader( Ok(digest.result_str()) } else { // Assume it's SHAKE. result_str() doesn't work with shake (as of 8/30/2016) - let mut bytes = Vec::new(); - bytes.resize((output_bits + 7) / 8, 0); + let mut bytes = vec![0; (output_bits + 7) / 8]; digest.hash_finalize(&mut bytes); Ok(encode(bytes)) } diff --git a/src/uu/sort/src/ext_sort.rs b/src/uu/sort/src/ext_sort.rs index 08de4e33e0e..18309881229 100644 --- a/src/uu/sort/src/ext_sort.rs +++ b/src/uu/sort/src/ext_sort.rs @@ -224,7 +224,7 @@ fn read_write_loop( let mut sender_option = Some(sender); let mut tmp_files = vec![]; loop { - let mut chunk = match receiver.recv() { + let chunk = match receiver.recv() { Ok(it) => it, _ => { return Ok(ReadResult::WroteChunksToFile { tmp_files }); @@ -232,7 +232,7 @@ fn read_write_loop( }; let tmp_file = write::( - &mut chunk, + &chunk, tmp_dir.next_file()?, settings.compress_prog.as_deref(), separator, @@ -262,7 +262,7 @@ fn read_write_loop( /// Write the lines in `chunk` to `file`, separated by `separator`. /// `compress_prog` is used to optionally compress file contents. fn write( - chunk: &mut Chunk, + chunk: &Chunk, file: (File, PathBuf), compress_prog: Option<&str>, separator: u8, From d19b8b78d863de0ccab5b6074c28b46ad0eec90b Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Sun, 10 Sep 2023 15:07:37 +0200 Subject: [PATCH 067/208] uucore: turn quoting_style into a feature --- src/uu/dir/Cargo.toml | 2 +- src/uu/ls/Cargo.toml | 7 ++++++- src/uu/vdir/Cargo.toml | 2 +- src/uu/wc/Cargo.toml | 2 +- src/uucore/Cargo.toml | 1 + src/uucore/src/lib/features.rs | 2 ++ src/uucore/src/lib/{mods => features}/quoting_style.rs | 0 src/uucore/src/lib/lib.rs | 3 ++- src/uucore/src/lib/mods.rs | 2 -- 9 files changed, 14 insertions(+), 7 deletions(-) rename src/uucore/src/lib/{mods => features}/quoting_style.rs (100%) diff --git a/src/uu/dir/Cargo.toml b/src/uu/dir/Cargo.toml index 9cc5fbde4ca..2a709a80465 100644 --- a/src/uu/dir/Cargo.toml +++ b/src/uu/dir/Cargo.toml @@ -16,7 +16,7 @@ path = "src/dir.rs" [dependencies] clap = { workspace = true, features = ["env"] } -uucore = { workspace = true, features = ["entries", "fs"] } +uucore = { workspace = true, features = ["entries", "fs", "quoting-style"] } uu_ls = { workspace = true } [[bin]] diff --git a/src/uu/ls/Cargo.toml b/src/uu/ls/Cargo.toml index 24b6947fd53..e4100d07f8b 100644 --- a/src/uu/ls/Cargo.toml +++ b/src/uu/ls/Cargo.toml @@ -23,7 +23,12 @@ term_grid = { workspace = true } terminal_size = { workspace = true } glob = { workspace = true } lscolors = { workspace = true } -uucore = { workspace = true, features = ["entries", "fs", "version-cmp"] } +uucore = { workspace = true, features = [ + "entries", + "fs", + "quoting-style", + "version-cmp", +] } once_cell = { workspace = true } selinux = { workspace = true, optional = true } diff --git a/src/uu/vdir/Cargo.toml b/src/uu/vdir/Cargo.toml index dc57512b1b6..99bcf383b98 100644 --- a/src/uu/vdir/Cargo.toml +++ b/src/uu/vdir/Cargo.toml @@ -16,7 +16,7 @@ path = "src/vdir.rs" [dependencies] clap = { workspace = true, features = ["env"] } -uucore = { workspace = true, features = ["entries", "fs"] } +uucore = { workspace = true, features = ["entries", "fs", "quoting-style"] } uu_ls = { workspace = true } [[bin]] diff --git a/src/uu/wc/Cargo.toml b/src/uu/wc/Cargo.toml index 199cc4e0a29..5712fbd8339 100644 --- a/src/uu/wc/Cargo.toml +++ b/src/uu/wc/Cargo.toml @@ -16,7 +16,7 @@ path = "src/wc.rs" [dependencies] clap = { workspace = true } -uucore = { workspace = true, features = ["pipes"] } +uucore = { workspace = true, features = ["pipes", "quoting-style"] } bytecount = { workspace = true } thiserror = { workspace = true } unicode-width = { workspace = true } diff --git a/src/uucore/Cargo.toml b/src/uucore/Cargo.toml index 15a053ac4cd..90ae74aab44 100644 --- a/src/uucore/Cargo.toml +++ b/src/uucore/Cargo.toml @@ -82,6 +82,7 @@ mode = ["libc"] perms = ["libc", "walkdir"] pipes = [] process = ["libc"] +quoting-style = [] ranges = [] ringbuffer = [] signals = [] diff --git a/src/uucore/src/lib/features.rs b/src/uucore/src/lib/features.rs index 49eee38439b..3a99eb84f43 100644 --- a/src/uucore/src/lib/features.rs +++ b/src/uucore/src/lib/features.rs @@ -16,6 +16,8 @@ pub mod fsext; pub mod lines; #[cfg(feature = "memo")] pub mod memo; +#[cfg(feature = "quoting-style")] +pub mod quoting_style; #[cfg(feature = "ranges")] pub mod ranges; #[cfg(feature = "ringbuffer")] diff --git a/src/uucore/src/lib/mods/quoting_style.rs b/src/uucore/src/lib/features/quoting_style.rs similarity index 100% rename from src/uucore/src/lib/mods/quoting_style.rs rename to src/uucore/src/lib/features/quoting_style.rs diff --git a/src/uucore/src/lib/lib.rs b/src/uucore/src/lib/lib.rs index c0732f069a8..2f28195dae1 100644 --- a/src/uucore/src/lib/lib.rs +++ b/src/uucore/src/lib/lib.rs @@ -25,7 +25,6 @@ pub use crate::mods::error; pub use crate::mods::line_ending; pub use crate::mods::os; pub use crate::mods::panic; -pub use crate::mods::quoting_style; // * string parsing modules pub use crate::parser::parse_glob; @@ -46,6 +45,8 @@ pub use crate::features::fsext; pub use crate::features::lines; #[cfg(feature = "memo")] pub use crate::features::memo; +#[cfg(feature = "quoting-style")] +pub use crate::features::quoting_style; #[cfg(feature = "ranges")] pub use crate::features::ranges; #[cfg(feature = "ringbuffer")] diff --git a/src/uucore/src/lib/mods.rs b/src/uucore/src/lib/mods.rs index d3a2dc19e8b..986536d6dd5 100644 --- a/src/uucore/src/lib/mods.rs +++ b/src/uucore/src/lib/mods.rs @@ -9,5 +9,3 @@ pub mod error; pub mod line_ending; pub mod os; pub mod panic; -// dir and vdir also need access to the quoting_style module -pub mod quoting_style; From 55a62f56b954d9991d41206cfcff47ffcbf98ebe Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 10 Sep 2023 22:42:02 +0000 Subject: [PATCH 068/208] chore(deps): update rust crate blake2b_simd to 1.0.2 --- Cargo.lock | 14 ++++---------- Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ff9d65e469c..2601e09959d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -155,13 +155,13 @@ checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" [[package]] name = "blake2b_simd" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c2f0dc9a68c6317d884f97cc36cf5a3d20ba14ce404227df55e1af708ab04bc" +checksum = "23285ad32269793932e830392f2fe2f83e26488fd3ec778883a93c8323735780" dependencies = [ "arrayref", "arrayvec", - "constant_time_eq 0.2.6", + "constant_time_eq", ] [[package]] @@ -174,7 +174,7 @@ dependencies = [ "arrayvec", "cc", "cfg-if", - "constant_time_eq 0.3.0", + "constant_time_eq", "digest", ] @@ -354,12 +354,6 @@ dependencies = [ "tiny-keccak", ] -[[package]] -name = "constant_time_eq" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a53c0a4d288377e7415b53dcfc3c04da5cdc2cc95c8d5ac178b58f0b861ad6" - [[package]] name = "constant_time_eq" version = "0.3.0" diff --git a/Cargo.toml b/Cargo.toml index fa45b2bb179..f457af28e7b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -338,7 +338,7 @@ md-5 = "0.10.5" sha1 = "0.10.5" sha2 = "0.10.7" sha3 = "0.10.8" -blake2b_simd = "1.0.1" +blake2b_simd = "1.0.2" blake3 = "1.4.1" sm3 = "0.4.2" digest = "0.10.7" From 8916dc4087345d18af2e71a26a9e8a098566da4b Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Mon, 11 Sep 2023 07:06:23 +0200 Subject: [PATCH 069/208] deny.toml: remove constant_time_eq from skip list --- deny.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/deny.toml b/deny.toml index 154b2bfe848..c80588f51b1 100644 --- a/deny.toml +++ b/deny.toml @@ -87,8 +87,6 @@ skip = [ { name = "syn", version = "1.0.109" }, # various crates { name = "bitflags", version = "1.3.2" }, - # blake2b_simd - { name = "constant_time_eq", version = "0.2.6" }, # various crates { name = "redox_syscall", version = "0.3.5" }, ] From 6d0bac2842d5f58bc0988fc8b07debfa1dffbe99 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Mon, 11 Sep 2023 16:05:19 +0200 Subject: [PATCH 070/208] ls: use OnceCell from std instead of once_cell --- src/uu/ls/src/ls.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/uu/ls/src/ls.rs b/src/uu/ls/src/ls.rs index 652b978a5b0..18a1a221d96 100644 --- a/src/uu/ls/src/ls.rs +++ b/src/uu/ls/src/ls.rs @@ -12,8 +12,7 @@ use clap::{ use glob::{MatchOptions, Pattern}; use lscolors::LsColors; use number_prefix::NumberPrefix; -use once_cell::unsync::OnceCell; -use std::num::IntErrorKind; +use std::{cell::OnceCell, num::IntErrorKind}; use std::{collections::HashSet, io::IsTerminal}; #[cfg(windows)] From e131ecdc850c5e9125093316bb3e9a0bfa323c3f Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Fri, 8 Sep 2023 15:07:47 +0200 Subject: [PATCH 071/208] Bump chrono from 0.4.28 to 0.4.30 --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2601e09959d..f0863ef8685 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -239,9 +239,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.28" +version = "0.4.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ed24df0632f708f5f6d8082675bef2596f7084dee3dd55f632290bf35bfe0f" +checksum = "defd4e7873dbddba6c7c91e199c7fcb946abc4a6a4ac3195400bcfb01b5de877" dependencies = [ "android-tzdata", "iana-time-zone", diff --git a/Cargo.toml b/Cargo.toml index f457af28e7b..03f43c26aa4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -263,7 +263,7 @@ binary-heap-plus = "0.5.0" bstr = "1.6" bytecount = "0.6.3" byteorder = "1.4.3" -chrono = { version = "^0.4.28", default-features = false, features = [ +chrono = { version = "^0.4.30", default-features = false, features = [ "std", "alloc", "clock", From 6ce80758d5708d0b04852af2db8b66c7074f9452 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Tue, 12 Sep 2023 15:08:21 +0200 Subject: [PATCH 072/208] touch: fix deprecation warnings from chrono datetime_from_str() has been deprecated --- src/uu/touch/src/touch.rs | 27 +++++++++++++++++++-------- tests/by-util/test_touch.rs | 3 +-- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/uu/touch/src/touch.rs b/src/uu/touch/src/touch.rs index e9970cb2456..85eb97bc462 100644 --- a/src/uu/touch/src/touch.rs +++ b/src/uu/touch/src/touch.rs @@ -3,10 +3,13 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -// spell-checker:ignore (ToDO) filetime datetime lpszfilepath mktime DATETIME subsecond datelike timelike +// spell-checker:ignore (ToDO) filetime datetime lpszfilepath mktime DATETIME datelike timelike // spell-checker:ignore (FORMATS) MMDDhhmm YYYYMMDDHHMM YYMMDDHHMM YYYYMMDDHHMMS -use chrono::{DateTime, Datelike, Duration, Local, NaiveDate, NaiveTime, TimeZone, Timelike, Utc}; +use chrono::{ + DateTime, Datelike, Duration, Local, LocalResult, NaiveDate, NaiveDateTime, NaiveTime, + TimeZone, Timelike, +}; use clap::builder::ValueParser; use clap::{crate_version, Arg, ArgAction, ArgGroup, Command}; use filetime::{set_file_times, set_symlink_file_times, FileTime}; @@ -348,8 +351,8 @@ fn parse_date(s: &str) -> UResult { // Tue Dec 3 ... // ("%c", POSIX_LOCALE_FORMAT), // - if let Ok(parsed) = Local.datetime_from_str(s, format::POSIX_LOCALE) { - return Ok(datetime_to_filetime(&parsed)); + if let Ok(parsed) = NaiveDateTime::parse_from_str(s, format::POSIX_LOCALE) { + return Ok(datetime_to_filetime(&parsed.and_utc())); } // Also support other formats found in the GNU tests like @@ -361,8 +364,8 @@ fn parse_date(s: &str) -> UResult { format::YYYY_MM_DD_HH_MM, format::YYYYMMDDHHMM_OFFSET, ] { - if let Ok(parsed) = Utc.datetime_from_str(s, fmt) { - return Ok(datetime_to_filetime(&parsed)); + if let Ok(parsed) = NaiveDateTime::parse_from_str(s, fmt) { + return Ok(datetime_to_filetime(&parsed.and_utc())); } } @@ -411,9 +414,17 @@ fn parse_timestamp(s: &str) -> UResult { } }; - let mut local = chrono::Local - .datetime_from_str(&ts, format) + let local = NaiveDateTime::parse_from_str(&ts, format) .map_err(|_| USimpleError::new(1, format!("invalid date ts format {}", ts.quote())))?; + let mut local = match chrono::Local.from_local_datetime(&local) { + LocalResult::Single(dt) => dt, + _ => { + return Err(USimpleError::new( + 1, + format!("invalid date ts format {}", ts.quote()), + )) + } + }; // Chrono caps seconds at 59, but 60 is valid. It might be a leap second // or wrap to the next minute. But that doesn't really matter, because we diff --git a/tests/by-util/test_touch.rs b/tests/by-util/test_touch.rs index 942446a3383..c9c0d700e2a 100644 --- a/tests/by-util/test_touch.rs +++ b/tests/by-util/test_touch.rs @@ -5,7 +5,6 @@ // spell-checker:ignore (formats) cymdhm cymdhms mdhm mdhms ymdhm ymdhms datetime mktime use crate::common::util::{AtPath, TestScenario}; -use chrono::TimeZone; use filetime::{self, FileTime}; use std::fs::remove_file; use std::path::PathBuf; @@ -32,7 +31,7 @@ fn set_file_times(at: &AtPath, path: &str, atime: FileTime, mtime: FileTime) { } fn str_to_filetime(format: &str, s: &str) -> FileTime { - let tm = chrono::Utc.datetime_from_str(s, format).unwrap(); + let tm = chrono::NaiveDateTime::parse_from_str(s, format).unwrap(); FileTime::from_unix_time(tm.timestamp(), tm.timestamp_subsec_nanos()) } From 335e8d5464bb18ed2047550aaca570d690501173 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Tue, 12 Sep 2023 16:08:19 +0200 Subject: [PATCH 073/208] ci: replace deprecated "command" with "fix" markdownlint-cli2-action deprecated "command" input in favor of "fix" --- .github/workflows/CICD.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index 27378cd9672..01dfdd274c3 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -328,7 +328,7 @@ jobs: RUSTDOCFLAGS="-Dwarnings" cargo doc ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} --no-deps --workspace --document-private-items - uses: DavidAnson/markdownlint-cli2-action@v12 with: - command: fix + fix: "true" globs: | *.md docs/src/*.md From d4217c5a129464cec638c56f06a679830d64ff71 Mon Sep 17 00:00:00 2001 From: Guillaume Ranquet Date: Wed, 6 Sep 2023 16:07:59 +0200 Subject: [PATCH 074/208] split: catch broken pipe error for round robin strategy The broken pipe error is not handled in the case of the round robin strategy (typically used with --filter). Align to the other strategies to silence that error in that use case too. fixes #5191 Signed-off-by: Guillaume Ranquet --- src/uu/split/src/split.rs | 14 ++++++++++---- tests/by-util/test_split.rs | 12 ++++++++++++ 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/uu/split/src/split.rs b/src/uu/split/src/split.rs index 517031791bd..4a1624d07a5 100644 --- a/src/uu/split/src/split.rs +++ b/src/uu/split/src/split.rs @@ -1282,7 +1282,7 @@ fn split_into_n_chunks_by_line_round_robin( settings: &Settings, reader: &mut R, num_chunks: u64, -) -> UResult<()> +) -> std::io::Result<()> where R: BufRead, { @@ -1293,7 +1293,7 @@ where settings.suffix_length, settings.suffix_type, settings.suffix_start, - )?; + ).map_err(|e| io::Error::new(ErrorKind::Other, format!("{e}")))?; // Create one writer for each chunk. This will create each // of the underlying files (if not in `--filter` mode). @@ -1301,7 +1301,7 @@ where for _ in 0..num_chunks { let filename = filename_iterator .next() - .ok_or_else(|| USimpleError::new(1, "output file suffixes exhausted"))?; + .ok_or_else(|| io::Error::new(ErrorKind::Other, "output file suffixes exhausted"))?; let writer = settings.instantiate_current_writer(filename.as_str())?; writers.push(writer); } @@ -1346,7 +1346,13 @@ fn split(settings: &Settings) -> UResult<()> { kth_chunk_by_line(settings, &mut reader, chunk_number, num_chunks) } Strategy::Number(NumberType::RoundRobin(num_chunks)) => { - split_into_n_chunks_by_line_round_robin(settings, &mut reader, num_chunks) + match split_into_n_chunks_by_line_round_robin(settings, &mut reader, num_chunks) { + Ok(_) => Ok(()), + Err(e) => match e.kind() { + ErrorKind::BrokenPipe => Ok(()), + _ => Err(USimpleError::new(1, format!("{e}"))), + }, + } } Strategy::Number(_) => Err(USimpleError::new(1, "-n mode not yet fully implemented")), Strategy::Lines(chunk_size) => { diff --git a/tests/by-util/test_split.rs b/tests/by-util/test_split.rs index 0b7bbfec6dd..cb777578186 100644 --- a/tests/by-util/test_split.rs +++ b/tests/by-util/test_split.rs @@ -299,6 +299,18 @@ fn test_filter_command_fails() { .fails(); } +#[test] +#[cfg(unix)] +fn test_filter_broken_pipe() { + let (at, mut ucmd) = at_and_ucmd!(); + let name = "filter-big-input"; + + RandomFile::new(&at, name).add_lines(1024 * 10); + ucmd + .args(&["--filter=head -c1 > /dev/null", "-n", "r/1", name]) + .succeeds(); +} + #[test] fn test_split_lines_number() { // Test if stdout/stderr for '--lines' option is correct From aa7b39ae493f6fd888c7a0231eeecc36a64556eb Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Wed, 13 Sep 2023 10:45:38 +0200 Subject: [PATCH 075/208] split: fix formatting --- src/uu/split/src/split.rs | 3 ++- tests/by-util/test_split.rs | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/uu/split/src/split.rs b/src/uu/split/src/split.rs index 4a1624d07a5..5ba9d9db212 100644 --- a/src/uu/split/src/split.rs +++ b/src/uu/split/src/split.rs @@ -1293,7 +1293,8 @@ where settings.suffix_length, settings.suffix_type, settings.suffix_start, - ).map_err(|e| io::Error::new(ErrorKind::Other, format!("{e}")))?; + ) + .map_err(|e| io::Error::new(ErrorKind::Other, format!("{e}")))?; // Create one writer for each chunk. This will create each // of the underlying files (if not in `--filter` mode). diff --git a/tests/by-util/test_split.rs b/tests/by-util/test_split.rs index cb777578186..aba06162bf5 100644 --- a/tests/by-util/test_split.rs +++ b/tests/by-util/test_split.rs @@ -306,8 +306,7 @@ fn test_filter_broken_pipe() { let name = "filter-big-input"; RandomFile::new(&at, name).add_lines(1024 * 10); - ucmd - .args(&["--filter=head -c1 > /dev/null", "-n", "r/1", name]) + ucmd.args(&["--filter=head -c1 > /dev/null", "-n", "r/1", name]) .succeeds(); } From 2523d3813960666ddf87ea1aa77b78c0b4faa289 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 13 Sep 2023 11:51:31 +0000 Subject: [PATCH 076/208] chore(deps): update rust crate libc to 0.2.148 --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f0863ef8685..3362401cc13 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1203,9 +1203,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.147" +version = "0.2.148" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" [[package]] name = "libloading" diff --git a/Cargo.toml b/Cargo.toml index 03f43c26aa4..21b0fa30ae2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -287,7 +287,7 @@ glob = "0.3.1" half = "2.3" indicatif = "0.17" itertools = "0.11.0" -libc = "0.2.147" +libc = "0.2.148" lscolors = { version = "0.15.0", default-features = false, features = [ "nu-ansi-term", ] } From 0d07cefee8d287d082610a61ac023d3abb4b8b95 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 14 Sep 2023 17:49:59 +0000 Subject: [PATCH 077/208] chore(deps): update codecov/codecov-action action to v4 --- .github/workflows/CICD.yml | 2 +- .github/workflows/GnuTests.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index 01dfdd274c3..36df67de6d9 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -1199,7 +1199,7 @@ jobs: ~/.cargo/bin/grcov . --output-type lcov --output-path "${COVERAGE_REPORT_FILE}" --branch --ignore build.rs --ignore "vendor/*" --ignore "/*" --ignore "[a-zA-Z]:/*" --excl-br-line "^\s*((debug_)?assert(_eq|_ne)?!|#\[derive\()" echo "report=${COVERAGE_REPORT_FILE}" >> $GITHUB_OUTPUT - name: Upload coverage results (to Codecov.io) - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v4 # if: steps.vars.outputs.HAS_CODECOV_TOKEN with: # token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/GnuTests.yml b/.github/workflows/GnuTests.yml index 61f30eba4c1..b524f8fde41 100644 --- a/.github/workflows/GnuTests.yml +++ b/.github/workflows/GnuTests.yml @@ -368,7 +368,7 @@ jobs: grcov . --output-type lcov --output-path "${COVERAGE_REPORT_FILE}" --branch --ignore build.rs --ignore "vendor/*" --ignore "/*" --ignore "[a-zA-Z]:/*" --excl-br-line "^\s*((debug_)?assert(_eq|_ne)?!|#\[derive\()" echo "report=${COVERAGE_REPORT_FILE}" >> $GITHUB_OUTPUT - name: Upload coverage results (to Codecov.io) - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v4 with: file: ${{ steps.coverage.outputs.report }} flags: gnutests From dec788dec30339a6e0337664949b8c7c4dfc5109 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Fri, 15 Sep 2023 09:25:04 +0200 Subject: [PATCH 078/208] Revert "chore(deps): update codecov/codecov-action action to v4" --- .github/workflows/CICD.yml | 2 +- .github/workflows/GnuTests.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index 36df67de6d9..01dfdd274c3 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -1199,7 +1199,7 @@ jobs: ~/.cargo/bin/grcov . --output-type lcov --output-path "${COVERAGE_REPORT_FILE}" --branch --ignore build.rs --ignore "vendor/*" --ignore "/*" --ignore "[a-zA-Z]:/*" --excl-br-line "^\s*((debug_)?assert(_eq|_ne)?!|#\[derive\()" echo "report=${COVERAGE_REPORT_FILE}" >> $GITHUB_OUTPUT - name: Upload coverage results (to Codecov.io) - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@v3 # if: steps.vars.outputs.HAS_CODECOV_TOKEN with: # token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/GnuTests.yml b/.github/workflows/GnuTests.yml index b524f8fde41..61f30eba4c1 100644 --- a/.github/workflows/GnuTests.yml +++ b/.github/workflows/GnuTests.yml @@ -368,7 +368,7 @@ jobs: grcov . --output-type lcov --output-path "${COVERAGE_REPORT_FILE}" --branch --ignore build.rs --ignore "vendor/*" --ignore "/*" --ignore "[a-zA-Z]:/*" --excl-br-line "^\s*((debug_)?assert(_eq|_ne)?!|#\[derive\()" echo "report=${COVERAGE_REPORT_FILE}" >> $GITHUB_OUTPUT - name: Upload coverage results (to Codecov.io) - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@v3 with: file: ${{ steps.coverage.outputs.report }} flags: gnutests From e59285e2766abfa3a04c71945cbc1eb9f5d0b327 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Fri, 15 Sep 2023 08:11:28 +0200 Subject: [PATCH 079/208] deny.toml: add terminal_size to skip list --- deny.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/deny.toml b/deny.toml index c80588f51b1..b1707d035cb 100644 --- a/deny.toml +++ b/deny.toml @@ -89,6 +89,8 @@ skip = [ { name = "bitflags", version = "1.3.2" }, # various crates { name = "redox_syscall", version = "0.3.5" }, + # clap_builder, textwrap + { name = "terminal_size", version = "0.2.6" }, ] # spell-checker: enable From b8ff52f28b729efa44127adab2070792f37ae746 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 15 Sep 2023 15:59:41 +0000 Subject: [PATCH 080/208] chore(deps): update rust crate chrono to ^0.4.31 --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3362401cc13..3a0e176426d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -239,9 +239,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.30" +version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "defd4e7873dbddba6c7c91e199c7fcb946abc4a6a4ac3195400bcfb01b5de877" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" dependencies = [ "android-tzdata", "iana-time-zone", diff --git a/Cargo.toml b/Cargo.toml index 21b0fa30ae2..b15bf4e088e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -263,7 +263,7 @@ binary-heap-plus = "0.5.0" bstr = "1.6" bytecount = "0.6.3" byteorder = "1.4.3" -chrono = { version = "^0.4.30", default-features = false, features = [ +chrono = { version = "^0.4.31", default-features = false, features = [ "std", "alloc", "clock", From 37ee889003509f701e83d33fcb07b9dc30b88aff Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Fri, 15 Sep 2023 22:34:17 +0200 Subject: [PATCH 081/208] ls -l: show an error when symlink not readable switching to match and handle the error Will help with tests/ls/stat-failed.sh --- src/uu/ls/src/ls.rs | 97 ++++++++++++++++++++++------------------ tests/by-util/test_ls.rs | 19 ++++++++ 2 files changed, 72 insertions(+), 44 deletions(-) diff --git a/src/uu/ls/src/ls.rs b/src/uu/ls/src/ls.rs index 18a1a221d96..8d559ed15f1 100644 --- a/src/uu/ls/src/ls.rs +++ b/src/uu/ls/src/ls.rs @@ -2908,55 +2908,64 @@ fn display_file_name( && path.file_type(out).unwrap().is_symlink() && !path.must_dereference { - if let Ok(target) = path.p_buf.read_link() { - name.push_str(" -> "); - - // We might as well color the symlink output after the arrow. - // This makes extra system calls, but provides important information that - // people run `ls -l --color` are very interested in. - if let Some(ls_colors) = &config.color { - // We get the absolute path to be able to construct PathData with valid Metadata. - // This is because relative symlinks will fail to get_metadata. - let mut absolute_target = target.clone(); - if target.is_relative() { - if let Some(parent) = path.p_buf.parent() { - absolute_target = parent.join(absolute_target); + match path.p_buf.read_link() { + Ok(target) => { + name.push_str(" -> "); + + // We might as well color the symlink output after the arrow. + // This makes extra system calls, but provides important information that + // people run `ls -l --color` are very interested in. + if let Some(ls_colors) = &config.color { + // We get the absolute path to be able to construct PathData with valid Metadata. + // This is because relative symlinks will fail to get_metadata. + let mut absolute_target = target.clone(); + if target.is_relative() { + if let Some(parent) = path.p_buf.parent() { + absolute_target = parent.join(absolute_target); + } } - } - let target_data = PathData::new(absolute_target, None, None, config, false); + let target_data = PathData::new(absolute_target, None, None, config, false); - // If we have a symlink to a valid file, we use the metadata of said file. - // Because we use an absolute path, we can assume this is guaranteed to exist. - // Otherwise, we use path.md(), which will guarantee we color to the same - // color of non-existent symlinks according to style_for_path_with_metadata. - if path.md(out).is_none() - && get_metadata(target_data.p_buf.as_path(), target_data.must_dereference) - .is_err() - { - name.push_str(&path.p_buf.read_link().unwrap().to_string_lossy()); + // If we have a symlink to a valid file, we use the metadata of said file. + // Because we use an absolute path, we can assume this is guaranteed to exist. + // Otherwise, we use path.md(), which will guarantee we color to the same + // color of non-existent symlinks according to style_for_path_with_metadata. + if path.md(out).is_none() + && get_metadata(target_data.p_buf.as_path(), target_data.must_dereference) + .is_err() + { + name.push_str(&path.p_buf.read_link().unwrap().to_string_lossy()); + } else { + // Use fn get_metadata instead of md() here and above because ls + // should not exit with an err, if we are unable to obtain the target_metadata + let target_metadata = match get_metadata( + target_data.p_buf.as_path(), + target_data.must_dereference, + ) { + Ok(md) => md, + Err(_) => path.md(out).unwrap().to_owned(), + }; + + name.push_str(&color_name( + escape_name(target.as_os_str(), &config.quoting_style), + &target_data.p_buf, + Some(&target_metadata), + ls_colors, + )); + } } else { - // Use fn get_metadata instead of md() here and above because ls - // should not exit with an err, if we are unable to obtain the target_metadata - let target_metadata = match get_metadata( - target_data.p_buf.as_path(), - target_data.must_dereference, - ) { - Ok(md) => md, - Err(_) => path.md(out).unwrap().to_owned(), - }; - - name.push_str(&color_name( - escape_name(target.as_os_str(), &config.quoting_style), - &target_data.p_buf, - Some(&target_metadata), - ls_colors, - )); + // If no coloring is required, we just use target as is. + // Apply the right quoting + name.push_str(&escape_name(target.as_os_str(), &config.quoting_style)); } - } else { - // If no coloring is required, we just use target as is. - // Apply the right quoting - name.push_str(&escape_name(target.as_os_str(), &config.quoting_style)); + } + Err(err) => { + show!(LsError::IOErrorContext( + err, + path.p_buf.to_path_buf(), + false + )); } } } diff --git a/tests/by-util/test_ls.rs b/tests/by-util/test_ls.rs index f1815035834..5f2b7e44350 100644 --- a/tests/by-util/test_ls.rs +++ b/tests/by-util/test_ls.rs @@ -3503,3 +3503,22 @@ fn test_invalid_utf8() { at.touch(filename); ucmd.succeeds(); } + +#[cfg(all(unix, feature = "chmod"))] +#[test] +fn test_ls_perm_io_errors() { + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + at.mkdir("d"); + at.symlink_file("/", "d/s"); + + scene.ccmd("chmod").arg("600").arg("d").succeeds(); + + scene + .ucmd() + .arg("-l") + .arg("d") + .fails() + .code_is(1) + .stderr_contains("Permission denied"); +} From f9c91fda2f6bc8391f0bc33e102378d3740ec16b Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sat, 16 Sep 2023 15:37:23 +0200 Subject: [PATCH 082/208] Document how to run the pure Rust tests --- CONTRIBUTING.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bd87e2d05ba..6f67eb828db 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -137,6 +137,12 @@ If you also want to test the core utilities: cargo test -p uucore -p coreutils ``` +Or to test the pure Rust tests in the utility itself: + +```shell +cargo test -p uu_ls --lib +``` + Running the complete test suite might take a while. We use [nextest](https://nexte.st/index.html) in the CI and you might want to try it out locally. It can speed up the execution time of the whole test run significantly if the cpu has multiple cores. From 975b3286308adbaea9c1ea1e1df16f33498f14fc Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 17 Sep 2023 08:36:43 +0000 Subject: [PATCH 083/208] chore(deps): update rust crate terminal_size to 0.3.0 --- Cargo.lock | 16 +++++++++++++--- Cargo.toml | 2 +- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3a0e176426d..9b079d0907e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -279,7 +279,7 @@ dependencies = [ "anstyle", "clap_lex", "strsim", - "terminal_size", + "terminal_size 0.2.6", ] [[package]] @@ -2135,6 +2135,16 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "terminal_size" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" +dependencies = [ + "rustix 0.38.8", + "windows-sys 0.48.0", +] + [[package]] name = "textwrap" version = "0.16.0" @@ -2142,7 +2152,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" dependencies = [ "smawk", - "terminal_size", + "terminal_size 0.2.6", "unicode-linebreak", "unicode-width", ] @@ -2671,7 +2681,7 @@ dependencies = [ "once_cell", "selinux", "term_grid", - "terminal_size", + "terminal_size 0.3.0", "unicode-width", "uucore", ] diff --git a/Cargo.toml b/Cargo.toml index b15bf4e088e..f7a3a372541 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -320,7 +320,7 @@ signal-hook = "0.3.17" smallvec = { version = "1.11", features = ["union"] } tempfile = "3.8.0" term_grid = "0.1.5" -terminal_size = "0.2.6" +terminal_size = "0.3.0" textwrap = { version = "0.16.0", features = ["terminal_size"] } thiserror = "1.0" time = { version = "0.3" } From 416a4832b80596eb017869e5cfb8ca4851ae6c0d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 17 Sep 2023 17:46:16 +0000 Subject: [PATCH 084/208] fix(deps): update rust crate dns-lookup to 2.0.3 --- Cargo.lock | 4 ++-- src/uucore/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9b079d0907e..4efd163a3f1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -746,9 +746,9 @@ dependencies = [ [[package]] name = "dns-lookup" -version = "2.0.2" +version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f332aa79f9e9de741ac013237294ef42ce2e9c6394dc7d766725812f1238812" +checksum = "8d0fa3cd8dc96ada974e126a940d37d4079bbbe6a24aca15b1113c2f362441c5" dependencies = [ "cfg-if", "libc", diff --git a/src/uucore/Cargo.toml b/src/uucore/Cargo.toml index 90ae74aab44..93bc618cee2 100644 --- a/src/uucore/Cargo.toml +++ b/src/uucore/Cargo.toml @@ -20,7 +20,7 @@ path = "src/lib/lib.rs" [dependencies] clap = { workspace = true } uucore_procs = { workspace = true } -dns-lookup = { version = "2.0.2", optional = true } +dns-lookup = { version = "2.0.3", optional = true } dunce = { version = "1.0.4", optional = true } wild = "2.1" glob = { workspace = true } From d01fe6a10f4e325de8b3c5bf4fdd5c221b47ca03 Mon Sep 17 00:00:00 2001 From: Rojin Raju <46747837+rojin254@users.noreply.github.com> Date: Tue, 19 Sep 2023 01:17:02 +0530 Subject: [PATCH 085/208] ls.rs: rename variable name dfn to displayed_file Closes: #5282 --- src/uu/ls/src/ls.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/uu/ls/src/ls.rs b/src/uu/ls/src/ls.rs index 8d559ed15f1..cc741fcf229 100644 --- a/src/uu/ls/src/ls.rs +++ b/src/uu/ls/src/ls.rs @@ -2482,13 +2482,13 @@ fn display_item_long( } }; - let dfn = display_file_name(item, config, None, String::new(), out).contents; + let displayed_file = display_file_name(item, config, None, String::new(), out).contents; write!( out, " {} {}{}", display_date(md, config), - dfn, + displayed_file, config.line_ending )?; } else { @@ -2561,7 +2561,7 @@ fn display_item_long( write!(out, " {}", pad_right("?", padding.uname))?; } - let dfn = display_file_name(item, config, None, String::new(), out).contents; + let displayed_file = display_file_name(item, config, None, String::new(), out).contents; let date_len = 12; writeln!( @@ -2569,7 +2569,7 @@ fn display_item_long( " {} {} {}", pad_left("?", padding.size), pad_left("?", date_len), - dfn, + displayed_file, )?; } From 514315462c7e429599b89a7a6da38e5efadacda4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 18 Sep 2023 19:47:21 +0000 Subject: [PATCH 086/208] chore(deps): update mfinelli/setup-shfmt action to v3 --- .github/workflows/CheckScripts.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CheckScripts.yml b/.github/workflows/CheckScripts.yml index 98ae6cb75ab..995b5456f18 100644 --- a/.github/workflows/CheckScripts.yml +++ b/.github/workflows/CheckScripts.yml @@ -52,7 +52,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Setup shfmt - uses: mfinelli/setup-shfmt@v2 + uses: mfinelli/setup-shfmt@v3 - name: Run shfmt shell: bash run: | From 1107fadca9e263b8aa976d82ac61be7952d068ae Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Tue, 19 Sep 2023 10:47:00 +0200 Subject: [PATCH 087/208] nl: increase line number over multiple files --- src/uu/nl/src/nl.rs | 33 ++++++++++++++++++++++++--------- tests/by-util/test_nl.rs | 13 +++++++++++++ 2 files changed, 37 insertions(+), 9 deletions(-) diff --git a/src/uu/nl/src/nl.rs b/src/uu/nl/src/nl.rs index 61a0a9f35f4..3568822377f 100644 --- a/src/uu/nl/src/nl.rs +++ b/src/uu/nl/src/nl.rs @@ -55,6 +55,20 @@ impl Default for Settings { } } +struct Stats { + line_number: i64, + consecutive_empty_lines: u64, +} + +impl Stats { + fn new(starting_line_number: i64) -> Self { + Self { + line_number: starting_line_number, + consecutive_empty_lines: 0, + } + } +} + // NumberingStyle stores which lines are to be numbered. // The possible options are: // 1. Number all lines @@ -160,6 +174,8 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { None => vec!["-".to_owned()], }; + let mut stats = Stats::new(settings.starting_line_number); + for file in &files { if file == "-" { // If both file names and '-' are specified, we choose to treat first all @@ -170,12 +186,12 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { let path = Path::new(file); let reader = File::open(path).map_err_context(|| file.to_string())?; let mut buffer = BufReader::new(reader); - nl(&mut buffer, &settings)?; + nl(&mut buffer, &mut stats, &settings)?; } if read_stdin { let mut buffer = BufReader::new(stdin()); - nl(&mut buffer, &settings)?; + nl(&mut buffer, &mut stats, &settings)?; } Ok(()) } @@ -285,10 +301,9 @@ pub fn uu_app() -> Command { } // nl implements the main functionality for an individual buffer. -fn nl(reader: &mut BufReader, settings: &Settings) -> UResult<()> { +fn nl(reader: &mut BufReader, stats: &mut Stats, settings: &Settings) -> UResult<()> { let mut current_numbering_style = &settings.body_numbering; - let mut line_no = settings.starting_line_number; - let mut consecutive_empty_lines = 0; + let mut consecutive_empty_lines = stats.consecutive_empty_lines; for line in reader.lines() { let line = line.map_err_context(|| "could not read line".to_string())?; @@ -312,7 +327,7 @@ fn nl(reader: &mut BufReader, settings: &Settings) -> UResult<()> { if let Some(new_style) = new_numbering_style { current_numbering_style = new_style; if settings.renumber { - line_no = settings.starting_line_number; + stats.line_number = settings.starting_line_number; } println!(); } else { @@ -336,13 +351,13 @@ fn nl(reader: &mut BufReader, settings: &Settings) -> UResult<()> { "{}{}{}", settings .number_format - .format(line_no, settings.number_width), + .format(stats.line_number, settings.number_width), settings.number_separator, line ); // update line number for the potential next line - match line_no.checked_add(settings.line_increment) { - Some(new_line_no) => line_no = new_line_no, + match stats.line_number.checked_add(settings.line_increment) { + Some(new_line_number) => stats.line_number = new_line_number, None => return Err(USimpleError::new(1, "line number overflow")), } } else { diff --git a/tests/by-util/test_nl.rs b/tests/by-util/test_nl.rs index b58c0c206fb..336ab4c29af 100644 --- a/tests/by-util/test_nl.rs +++ b/tests/by-util/test_nl.rs @@ -311,6 +311,19 @@ fn test_default_body_numbering() { .stdout_is(" 1\ta\n \n 2\tb\n"); } +#[test] +fn test_default_body_numbering_multiple_files() { + let (at, mut ucmd) = at_and_ucmd!(); + + at.write("a.txt", "a"); + at.write("b.txt", "b"); + at.write("c.txt", "c"); + + ucmd.args(&["a.txt", "b.txt", "c.txt"]) + .succeeds() + .stdout_is(" 1\ta\n 2\tb\n 3\tc\n"); +} + #[test] fn test_body_numbering_all_lines_without_delimiter() { for arg in ["-ba", "--body-numbering=a"] { From 1a30a1b8b656afe0deb674a4b848f7b311a18c81 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Tue, 19 Sep 2023 10:47:20 +0200 Subject: [PATCH 088/208] nl: support --join-blank-lines over multiple files --- src/uu/nl/src/nl.rs | 7 +++---- tests/by-util/test_nl.rs | 25 +++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/uu/nl/src/nl.rs b/src/uu/nl/src/nl.rs index 3568822377f..fef9c030a2e 100644 --- a/src/uu/nl/src/nl.rs +++ b/src/uu/nl/src/nl.rs @@ -303,15 +303,14 @@ pub fn uu_app() -> Command { // nl implements the main functionality for an individual buffer. fn nl(reader: &mut BufReader, stats: &mut Stats, settings: &Settings) -> UResult<()> { let mut current_numbering_style = &settings.body_numbering; - let mut consecutive_empty_lines = stats.consecutive_empty_lines; for line in reader.lines() { let line = line.map_err_context(|| "could not read line".to_string())?; if line.is_empty() { - consecutive_empty_lines += 1; + stats.consecutive_empty_lines += 1; } else { - consecutive_empty_lines = 0; + stats.consecutive_empty_lines = 0; }; // FIXME section delimiters are hardcoded and settings.section_delimiter is ignored @@ -336,7 +335,7 @@ fn nl(reader: &mut BufReader, stats: &mut Stats, settings: &Settings // for numbering, and only number the last one NumberingStyle::All if line.is_empty() - && consecutive_empty_lines % settings.join_blank_lines != 0 => + && stats.consecutive_empty_lines % settings.join_blank_lines != 0 => { false } diff --git a/tests/by-util/test_nl.rs b/tests/by-util/test_nl.rs index 336ab4c29af..b008c61de77 100644 --- a/tests/by-util/test_nl.rs +++ b/tests/by-util/test_nl.rs @@ -284,6 +284,31 @@ fn test_join_blank_lines() { } } +#[test] +fn test_join_blank_lines_multiple_files() { + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + + at.write("a.txt", "\n\n"); + at.write("b.txt", "\n\n"); + at.write("c.txt", "\n\n"); + + for arg in ["-l3", "--join-blank-lines=3"] { + scene + .ucmd() + .args(&[arg, "--body-numbering=a", "a.txt", "b.txt", "c.txt"]) + .succeeds() + .stdout_is(concat!( + " \n", + " \n", + " 1\t\n", + " \n", + " \n", + " 2\t\n", + )); + } +} + #[test] fn test_join_blank_lines_zero() { for arg in ["-l0", "--join-blank-lines=0"] { From 9443985b49cbf630165d003ec6b1984db94d2b8c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 19 Sep 2023 18:13:47 +0000 Subject: [PATCH 089/208] chore(deps): update rust crate unicode-width to 0.1.11 --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4efd163a3f1..ece92cdfe46 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2241,9 +2241,9 @@ checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" [[package]] name = "unicode-width" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" [[package]] name = "unicode-xid" diff --git a/Cargo.toml b/Cargo.toml index f7a3a372541..866b1f58549 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -325,7 +325,7 @@ textwrap = { version = "0.16.0", features = ["terminal_size"] } thiserror = "1.0" time = { version = "0.3" } unicode-segmentation = "1.10.1" -unicode-width = "0.1.10" +unicode-width = "0.1.11" utf-8 = "0.7.6" walkdir = "2.4" winapi-util = "0.1.5" From 5586e7bf2bae7d165861e884004aa3c751171d82 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 20 Sep 2023 04:47:16 +0000 Subject: [PATCH 090/208] chore(deps): update davidanson/markdownlint-cli2-action action to v13 --- .github/workflows/CICD.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index 01dfdd274c3..577e235f9cf 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -326,7 +326,7 @@ jobs: shell: bash run: | RUSTDOCFLAGS="-Dwarnings" cargo doc ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} --no-deps --workspace --document-private-items - - uses: DavidAnson/markdownlint-cli2-action@v12 + - uses: DavidAnson/markdownlint-cli2-action@v13 with: fix: "true" globs: | From 9b4d2c6bc46cbedfa377e592d6188a643a88e3ed Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Wed, 20 Sep 2023 08:17:46 +0200 Subject: [PATCH 091/208] ls: implement --dired * Support ls --dired * stat-failed.sh: update of the test - we have a small difference * ls --dired: address some of the comments * fix warnings * use unwrap() * Improve test Co-authored-by: Daniel Hofstetter * Simplify test Co-authored-by: Daniel Hofstetter * Remove a word from the spell ignore Co-authored-by: Daniel Hofstetter * remove duplication of the spell ignore Co-authored-by: Daniel Hofstetter * rustfmt --------- Co-authored-by: Daniel Hofstetter --- src/uu/ls/src/dired.rs | 178 +++++++++++++++++++ src/uu/ls/src/ls.rs | 172 +++++++++++++----- src/uucore/src/lib/features/quoting_style.rs | 81 ++++++++- tests/by-util/test_ls.rs | 125 ++++++++++++- util/build-gnu.sh | 6 +- 5 files changed, 510 insertions(+), 52 deletions(-) create mode 100644 src/uu/ls/src/dired.rs diff --git a/src/uu/ls/src/dired.rs b/src/uu/ls/src/dired.rs new file mode 100644 index 00000000000..f66d22b38dd --- /dev/null +++ b/src/uu/ls/src/dired.rs @@ -0,0 +1,178 @@ +// This file is part of the uutils coreutils package. +// +// For the full copyright and license information, please view the LICENSE +// file that was distributed with this source code. +// spell-checker:ignore dired subdired + +use crate::Config; +use std::fmt; +use std::io::{BufWriter, Stdout, Write}; +use uucore::error::UResult; + +#[derive(Debug, Clone)] +pub struct BytePosition { + pub start: usize, + pub end: usize, +} + +/// Represents the output structure for DIRED, containing positions for both DIRED and SUBDIRED. +#[derive(Debug, Clone, Default)] +pub struct DiredOutput { + pub dired_positions: Vec, + pub subdired_positions: Vec, + pub just_printed_total: bool, +} + +impl fmt::Display for BytePosition { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{} {}", self.start, self.end) + } +} + +// When --dired is used, all lines starts with 2 spaces +static DIRED_TRAILING_OFFSET: usize = 2; + +/// Calculates the byte positions for DIRED +pub fn calculate_dired_byte_positions( + output_display_len: usize, + dfn_len: usize, + dired_positions: &[BytePosition], +) -> (usize, usize) { + let offset_from_previous_line = if let Some(last_position) = dired_positions.last() { + last_position.end + 1 + } else { + 0 + }; + + let start = output_display_len + offset_from_previous_line; + let end = start + dfn_len; + (start, end) +} + +pub fn indent(out: &mut BufWriter) -> UResult<()> { + write!(out, " ")?; + Ok(()) +} + +pub fn calculate_offset_and_push(dired: &mut DiredOutput, path_len: usize) { + let offset = if dired.subdired_positions.is_empty() { + DIRED_TRAILING_OFFSET + } else { + dired.subdired_positions[dired.subdired_positions.len() - 1].start + DIRED_TRAILING_OFFSET + }; + dired.subdired_positions.push(BytePosition { + start: offset, + end: path_len + offset, + }); +} + +/// Prints the dired output based on the given configuration and dired structure. +pub fn print_dired_output( + config: &Config, + dired: &DiredOutput, + out: &mut BufWriter, +) -> UResult<()> { + out.flush()?; + if config.recursive { + print_positions("//SUBDIRED//", &dired.subdired_positions); + } else if !dired.just_printed_total { + print_positions("//DIRED//", &dired.dired_positions); + } + println!("//DIRED-OPTIONS// --quoting-style={}", config.quoting_style); + Ok(()) +} + +/// Helper function to print positions with a given prefix. +fn print_positions(prefix: &str, positions: &Vec) { + print!("{}", prefix); + for c in positions { + print!(" {}", c); + } + println!(); +} + +pub fn add_total(total_len: usize, dired: &mut DiredOutput) { + dired.just_printed_total = true; + dired.dired_positions.push(BytePosition { + start: 0, + // the 2 is from the trailing spaces + // the 1 is from the line ending (\n) + end: total_len + DIRED_TRAILING_OFFSET - 1, + }); +} + +/// Calculates byte positions and updates the dired structure. +pub fn calculate_and_update_positions( + output_display_len: usize, + dfn_len: usize, + dired: &mut DiredOutput, +) { + let offset = dired + .dired_positions + .last() + .map_or(DIRED_TRAILING_OFFSET, |last_position| { + last_position.start + DIRED_TRAILING_OFFSET + }); + let start = output_display_len + offset + DIRED_TRAILING_OFFSET; + let end = start + dfn_len; + update_positions(start, end, dired, true); +} + +/// Updates the dired positions based on the given start and end positions. +/// update when it is the first element in the list (to manage "total X" +/// insert when it isn't the about total +pub fn update_positions(start: usize, end: usize, dired: &mut DiredOutput, adjust: bool) { + if dired.just_printed_total { + if let Some(last_position) = dired.dired_positions.last_mut() { + *last_position = BytePosition { + start: if adjust { + start + last_position.end + } else { + start + }, + end: if adjust { end + last_position.end } else { end }, + }; + dired.just_printed_total = false; + } + } else { + dired.dired_positions.push(BytePosition { start, end }); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_calculate_dired_byte_positions() { + let output_display = "sample_output".to_string(); + let dfn = "sample_file".to_string(); + let dired_positions = vec![BytePosition { start: 5, end: 10 }]; + let (start, end) = + calculate_dired_byte_positions(output_display.len(), dfn.len(), &dired_positions); + + assert_eq!(start, 24); + assert_eq!(end, 35); + } + + #[test] + fn test_dired_update_positions() { + let mut dired = DiredOutput { + dired_positions: vec![BytePosition { start: 5, end: 10 }], + subdired_positions: vec![], + just_printed_total: true, + }; + + // Test with adjust = true + update_positions(15, 20, &mut dired, true); + let last_position = dired.dired_positions.last().unwrap(); + assert_eq!(last_position.start, 25); // 15 + 10 (end of the previous position) + assert_eq!(last_position.end, 30); // 20 + 10 (end of the previous position) + + // Test with adjust = false + update_positions(30, 35, &mut dired, false); + let last_position = dired.dired_positions.last().unwrap(); + assert_eq!(last_position.start, 30); + assert_eq!(last_position.end, 35); + } +} diff --git a/src/uu/ls/src/ls.rs b/src/uu/ls/src/ls.rs index cc741fcf229..3d67cccaad1 100644 --- a/src/uu/ls/src/ls.rs +++ b/src/uu/ls/src/ls.rs @@ -3,7 +3,7 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -// spell-checker:ignore (ToDO) cpio svgz webm somegroup nlink rmvb xspf tabsize dired +// spell-checker:ignore (ToDO) cpio svgz webm somegroup nlink rmvb xspf tabsize dired subdired use clap::{ builder::{NonEmptyStringValueParser, ValueParser}, @@ -61,7 +61,8 @@ use uucore::{ version_cmp::version_cmp, }; use uucore::{help_about, help_section, help_usage, parse_glob, show, show_error, show_warning}; - +mod dired; +use dired::DiredOutput; #[cfg(not(feature = "selinux"))] static CONTEXT_HELP_TEXT: &str = "print any security context of each file (not enabled)"; #[cfg(feature = "selinux")] @@ -167,6 +168,7 @@ enum LsError { IOError(std::io::Error), IOErrorContext(std::io::Error, PathBuf, bool), BlockSizeParseError(String), + ConflictingArgumentDired(), AlreadyListedError(PathBuf), TimeStyleParseError(String, Vec), } @@ -179,6 +181,7 @@ impl UError for LsError { Self::IOErrorContext(_, _, false) => 1, Self::IOErrorContext(_, _, true) => 2, Self::BlockSizeParseError(_) => 1, + Self::ConflictingArgumentDired() => 1, Self::AlreadyListedError(_) => 2, Self::TimeStyleParseError(_, _) => 1, } @@ -193,6 +196,10 @@ impl Display for LsError { Self::BlockSizeParseError(s) => { write!(f, "invalid --block-size argument {}", s.quote()) } + Self::ConflictingArgumentDired() => { + write!(f, "--dired requires --format=long") + } + Self::TimeStyleParseError(s, possible_time_styles) => { write!( f, @@ -406,6 +413,7 @@ pub struct Config { selinux_supported: bool, group_directories_first: bool, line_ending: LineEnding, + dired: bool, } // Fields that can be removed or added to the long format @@ -610,6 +618,8 @@ fn extract_quoting_style(options: &clap::ArgMatches, show_control: bool) -> Quot QuotingStyle::C { quotes: quoting_style::Quotes::Double, } + } else if options.get_flag(options::DIRED) { + QuotingStyle::Literal { show_control } } else { // TODO: use environment variable if available QuotingStyle::Shell { @@ -954,6 +964,11 @@ impl Config { None }; + let dired = options.get_flag(options::DIRED); + if dired && format != Format::Long { + return Err(Box::new(LsError::ConflictingArgumentDired())); + } + let dereference = if options.get_flag(options::dereference::ALL) { Dereference::All } else if options.get_flag(options::dereference::ARGS) { @@ -1003,6 +1018,7 @@ impl Config { }, group_directories_first: options.get_flag(options::GROUP_DIRECTORIES_FIRST), line_ending: LineEnding::from_zero_flag(options.get_flag(options::ZERO)), + dired, }) } } @@ -1135,7 +1151,7 @@ pub fn uu_app() -> Command { Arg::new(options::DIRED) .long(options::DIRED) .short('D') - .hide(true) + .help("generate output designed for Emacs' dired (Directory Editor) mode") .action(ArgAction::SetTrue), ) // The next four arguments do not override with the other format @@ -1844,6 +1860,7 @@ pub fn list(locs: Vec<&Path>, config: &Config) -> UResult<()> { let mut files = Vec::::new(); let mut dirs = Vec::::new(); let mut out = BufWriter::new(stdout()); + let mut dired = DiredOutput::default(); let initial_locs_len = locs.len(); for loc in locs { @@ -1877,7 +1894,7 @@ pub fn list(locs: Vec<&Path>, config: &Config) -> UResult<()> { sort_entries(&mut files, config, &mut out); sort_entries(&mut dirs, config, &mut out); - display_items(&files, config, &mut out)?; + display_items(&files, config, &mut out, &mut dired)?; for (pos, path_data) in dirs.iter().enumerate() { // Do read_dir call here to match GNU semantics by printing @@ -1899,7 +1916,13 @@ pub fn list(locs: Vec<&Path>, config: &Config) -> UResult<()> { // Print dir heading - name... 'total' comes after error display if initial_locs_len > 1 || config.recursive { if pos.eq(&0usize) && files.is_empty() { + if config.dired { + dired::indent(&mut out)?; + } writeln!(out, "{}:", path_data.p_buf.display())?; + if config.dired { + dired::calculate_offset_and_push(&mut dired, path_data.display_name.len()); + } } else { writeln!(out, "\n{}:", path_data.p_buf.display())?; } @@ -1909,9 +1932,18 @@ pub fn list(locs: Vec<&Path>, config: &Config) -> UResult<()> { &path_data.p_buf, path_data.must_dereference, )?); - enter_directory(path_data, read_dir, config, &mut out, &mut listed_ancestors)?; + enter_directory( + path_data, + read_dir, + config, + &mut out, + &mut listed_ancestors, + &mut dired, + )?; + } + if config.dired { + dired::print_dired_output(config, &dired, &mut out)?; } - Ok(()) } @@ -2022,6 +2054,7 @@ fn enter_directory( config: &Config, out: &mut BufWriter, listed_ancestors: &mut HashSet, + dired: &mut DiredOutput, ) -> UResult<()> { // Create vec of entries with initial dot files let mut entries: Vec = if config.files == Files::All { @@ -2067,10 +2100,14 @@ fn enter_directory( // Print total after any error display if config.format == Format::Long || config.alloc_size { - display_total(&entries, config, out)?; + let total = return_total(&entries, config, out)?; + write!(out, "{}", total.as_str())?; + if config.dired { + dired::add_total(total.len(), dired); + } } - display_items(&entries, config, out)?; + display_items(&entries, config, out, dired)?; if config.recursive { for e in entries @@ -2095,7 +2132,7 @@ fn enter_directory( .insert(FileInformation::from_path(&e.p_buf, e.must_dereference)?) { writeln!(out, "\n{}:", e.p_buf.display())?; - enter_directory(e, rd, config, out, listed_ancestors)?; + enter_directory(e, rd, config, out, listed_ancestors, dired)?; listed_ancestors .remove(&FileInformation::from_path(&e.p_buf, e.must_dereference)?); } else { @@ -2154,7 +2191,11 @@ fn pad_right(string: &str, count: usize) -> String { format!("{string:) -> UResult<()> { +fn return_total( + items: &[PathData], + config: &Config, + out: &mut BufWriter, +) -> UResult { let mut total_size = 0; for item in items { total_size += item @@ -2162,13 +2203,14 @@ fn display_total(items: &[PathData], config: &Config, out: &mut BufWriter) -> UResult<()> { +fn display_items( + items: &[PathData], + config: &Config, + out: &mut BufWriter, + dired: &mut DiredOutput, +) -> UResult<()> { // `-Z`, `--context`: // Display the SELinux security context or '?' if none is found. When used with the `-l` // option, print the security context to the left of the size column. @@ -2220,6 +2267,7 @@ fn display_items(items: &[PathData], config: &Config, out: &mut BufWriter, + dired: &mut DiredOutput, ) -> UResult<()> { + let mut output_display: String = String::new(); + if config.dired { + output_display += " "; + } if let Some(md) = item.md(out) { write!( - out, + output_display, "{}{} {}", display_permissions(md, true), if item.security_context.len() > 1 { @@ -2416,49 +2469,54 @@ fn display_item_long( "" }, pad_left(&display_symlink_count(md), padding.link_count) - )?; + ) + .unwrap(); if config.long.owner { write!( - out, + output_display, " {}", pad_right(&display_uname(md, config), padding.uname) - )?; + ) + .unwrap(); } if config.long.group { write!( - out, + output_display, " {}", pad_right(&display_group(md, config), padding.group) - )?; + ) + .unwrap(); } if config.context { write!( - out, + output_display, " {}", pad_right(&item.security_context, padding.context) - )?; + ) + .unwrap(); } // Author is only different from owner on GNU/Hurd, so we reuse // the owner, since GNU/Hurd is not currently supported by Rust. if config.long.author { write!( - out, + output_display, " {}", pad_right(&display_uname(md, config), padding.uname) - )?; + ) + .unwrap(); } match display_len_or_rdev(md, config) { SizeOrDeviceId::Size(size) => { - write!(out, " {}", pad_left(&size, padding.size))?; + write!(output_display, " {}", pad_left(&size, padding.size)).unwrap(); } SizeOrDeviceId::Device(major, minor) => { write!( - out, + output_display, " {}, {}", pad_left( &major, @@ -2478,19 +2536,23 @@ fn display_item_long( #[cfg(unix)] padding.minor, ), - )?; + ) + .unwrap(); } }; - let displayed_file = display_file_name(item, config, None, String::new(), out).contents; + write!(output_display, " {} ", display_date(md, config)).unwrap(); - write!( - out, - " {} {}{}", - display_date(md, config), - displayed_file, - config.line_ending - )?; + let displayed_file = display_file_name(item, config, None, String::new(), out).contents; + if config.dired { + let (start, end) = dired::calculate_dired_byte_positions( + output_display.len(), + displayed_file.len(), + &dired.dired_positions, + ); + dired::update_positions(start, end, dired, false); + } + write!(output_display, "{}{}", displayed_file, config.line_ending).unwrap(); } else { #[cfg(unix)] let leading_char = { @@ -2526,7 +2588,7 @@ fn display_item_long( }; write!( - out, + output_display, "{}{} {}", format_args!("{leading_char}?????????"), if item.security_context.len() > 1 { @@ -2537,41 +2599,53 @@ fn display_item_long( "" }, pad_left("?", padding.link_count) - )?; + ) + .unwrap(); if config.long.owner { - write!(out, " {}", pad_right("?", padding.uname))?; + write!(output_display, " {}", pad_right("?", padding.uname)).unwrap(); } if config.long.group { - write!(out, " {}", pad_right("?", padding.group))?; + write!(output_display, " {}", pad_right("?", padding.group)).unwrap(); } if config.context { write!( - out, + output_display, " {}", pad_right(&item.security_context, padding.context) - )?; + ) + .unwrap(); } // Author is only different from owner on GNU/Hurd, so we reuse // the owner, since GNU/Hurd is not currently supported by Rust. if config.long.author { - write!(out, " {}", pad_right("?", padding.uname))?; + write!(output_display, " {}", pad_right("?", padding.uname)).unwrap(); } let displayed_file = display_file_name(item, config, None, String::new(), out).contents; let date_len = 12; - writeln!( - out, - " {} {} {}", + write!( + output_display, + " {} {} ", pad_left("?", padding.size), pad_left("?", date_len), - displayed_file, - )?; + ) + .unwrap(); + + if config.dired { + dired::calculate_and_update_positions( + output_display.len(), + displayed_file.trim().len(), + dired, + ); + } + write!(output_display, "{}{}", displayed_file, config.line_ending).unwrap(); } + write!(out, "{}", output_display)?; Ok(()) } diff --git a/src/uucore/src/lib/features/quoting_style.rs b/src/uucore/src/lib/features/quoting_style.rs index 1b9a76aae22..b517dbd8db5 100644 --- a/src/uucore/src/lib/features/quoting_style.rs +++ b/src/uucore/src/lib/features/quoting_style.rs @@ -4,12 +4,14 @@ // file that was distributed with this source code. use std::char::from_digit; use std::ffi::OsStr; +use std::fmt; // These are characters with special meaning in the shell (e.g. bash). // The first const contains characters that only have a special meaning when they appear at the beginning of a name. const SPECIAL_SHELL_CHARS_START: &[char] = &['~', '#']; const SPECIAL_SHELL_CHARS: &str = "`$&*()|[]{};\\'\"<>?! "; +#[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum QuotingStyle { Shell { escape: bool, @@ -24,7 +26,7 @@ pub enum QuotingStyle { }, } -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum Quotes { None, Single, @@ -316,6 +318,42 @@ pub fn escape_name(name: &OsStr, style: &QuotingStyle) -> String { } } +impl fmt::Display for QuotingStyle { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + Self::Shell { + escape, + always_quote, + show_control, + } => { + let mut style = "shell".to_string(); + if escape { + style.push_str("-escape"); + } + if always_quote { + style.push_str("-always-quote"); + } + if show_control { + style.push_str("-show-control"); + } + f.write_str(&style) + } + Self::C { .. } => f.write_str("C"), + Self::Literal { .. } => f.write_str("literal"), + } + } +} + +impl fmt::Display for Quotes { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + Self::None => f.write_str("None"), + Self::Single => f.write_str("Single"), + Self::Double => f.write_str("Double"), + } + } +} + #[cfg(test)] mod tests { use crate::quoting_style::{escape_name, Quotes, QuotingStyle}; @@ -732,4 +770,45 @@ mod tests { ], ); } + + #[test] + fn test_quoting_style_display() { + let style = QuotingStyle::Shell { + escape: true, + always_quote: false, + show_control: false, + }; + assert_eq!(format!("{}", style), "shell-escape"); + + let style = QuotingStyle::Shell { + escape: false, + always_quote: true, + show_control: false, + }; + assert_eq!(format!("{}", style), "shell-always-quote"); + + let style = QuotingStyle::Shell { + escape: false, + always_quote: false, + show_control: true, + }; + assert_eq!(format!("{}", style), "shell-show-control"); + + let style = QuotingStyle::C { + quotes: Quotes::Double, + }; + assert_eq!(format!("{}", style), "C"); + + let style = QuotingStyle::Literal { + show_control: false, + }; + assert_eq!(format!("{}", style), "literal"); + } + + #[test] + fn test_quotes_display() { + assert_eq!(format!("{}", Quotes::None), "None"); + assert_eq!(format!("{}", Quotes::Single), "Single"); + assert_eq!(format!("{}", Quotes::Double), "Double"); + } } diff --git a/tests/by-util/test_ls.rs b/tests/by-util/test_ls.rs index 5f2b7e44350..87b3066569a 100644 --- a/tests/by-util/test_ls.rs +++ b/tests/by-util/test_ls.rs @@ -2,7 +2,7 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -// spell-checker:ignore (words) READMECAREFULLY birthtime doesntexist oneline somebackup lrwx somefile somegroup somehiddenbackup somehiddenfile tabsize aaaaaaaa bbbb cccc dddddddd ncccc neee naaaaa nbcdef nfffff +// spell-checker:ignore (words) READMECAREFULLY birthtime doesntexist oneline somebackup lrwx somefile somegroup somehiddenbackup somehiddenfile tabsize aaaaaaaa bbbb cccc dddddddd ncccc neee naaaaa nbcdef nfffff dired subdired #[cfg(any(unix, feature = "feat_selinux"))] use crate::common::util::expected_result; @@ -3522,3 +3522,126 @@ fn test_ls_perm_io_errors() { .code_is(1) .stderr_contains("Permission denied"); } + +#[test] +fn test_ls_dired_incompatible() { + let scene = TestScenario::new(util_name!()); + + scene + .ucmd() + .arg("--dired") + .fails() + .code_is(1) + .stderr_contains("--dired requires --format=long"); +} + +#[test] +fn test_ls_dired_recursive() { + let scene = TestScenario::new(util_name!()); + + scene + .ucmd() + .arg("--dired") + .arg("-l") + .arg("-R") + .succeeds() + .stdout_does_not_contain("//DIRED//") + .stdout_contains(" total 0") + .stdout_contains("//SUBDIRED// 2 3") + .stdout_contains("//DIRED-OPTIONS// --quoting-style"); +} + +#[test] +fn test_ls_dired_simple() { + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + scene + .ucmd() + .arg("--dired") + .arg("-l") + .succeeds() + .stdout_contains(" total 0"); + + at.mkdir("d"); + at.touch("d/a1"); + let mut cmd = scene.ucmd(); + cmd.arg("--dired").arg("-l").arg("d"); + let result = cmd.succeeds(); + result.stdout_contains(" total 0"); + println!(" result.stdout = {:#?}", result.stdout_str()); + + let dired_line = result + .stdout_str() + .lines() + .find(|&line| line.starts_with("//DIRED//")) + .unwrap(); + let positions: Vec = dired_line + .split_whitespace() + .skip(1) + .map(|s| s.parse().unwrap()) + .collect(); + + assert_eq!(positions.len(), 2); + + let start_pos = positions[0]; + let end_pos = positions[1]; + + // Extract the filename using the positions + let filename = + String::from_utf8(result.stdout_str().as_bytes()[start_pos..end_pos].to_vec()).unwrap(); + + assert_eq!(filename, "a1"); +} + +#[test] +fn test_ls_dired_complex() { + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + + at.mkdir("d"); + at.mkdir("d/d"); + at.touch("d/a1"); + at.touch("d/a22"); + at.touch("d/a333"); + at.touch("d/a4444"); + + let mut cmd = scene.ucmd(); + cmd.arg("--dired").arg("-l").arg("d"); + let result = cmd.succeeds(); + // Number of blocks + #[cfg(target_os = "linux")] + result.stdout_contains(" total 4"); + + let output = result.stdout_str().to_string(); + println!("Output:\n{}", output); + + let dired_line = output + .lines() + .find(|&line| line.starts_with("//DIRED//")) + .unwrap(); + let positions: Vec = dired_line + .split_whitespace() + .skip(1) + .map(|s| s.parse().unwrap()) + .collect(); + println!("{:?}", positions); + println!("Parsed byte positions: {:?}", positions); + assert_eq!(positions.len() % 2, 0); // Ensure there's an even number of positions + + let filenames: Vec = positions + .chunks(2) + .map(|chunk| { + let start_pos = chunk[0]; + let end_pos = chunk[1]; + let filename = String::from_utf8(output.as_bytes()[start_pos..=end_pos].to_vec()) + .unwrap() + .trim() + .to_string(); + println!("Extracted filename: {}", filename); + filename + }) + .collect(); + + println!("Extracted filenames: {:?}", filenames); + assert_eq!(filenames, vec!["a1", "a22", "a333", "a4444", "d"]); +} diff --git a/util/build-gnu.sh b/util/build-gnu.sh index 2d949bbb333..75be52587dd 100755 --- a/util/build-gnu.sh +++ b/util/build-gnu.sh @@ -3,7 +3,7 @@ # # UU_MAKE_PROFILE == 'debug' | 'release' ## build profile for *uutils* build; may be supplied by caller, defaults to 'release' -# spell-checker:ignore (paths) abmon deref discrim eacces getlimits getopt ginstall inacc infloop inotify reflink ; (misc) INT_OFLOW OFLOW baddecode submodules ; (vars/env) SRCDIR vdir rcexp xpart +# spell-checker:ignore (paths) abmon deref discrim eacces getlimits getopt ginstall inacc infloop inotify reflink ; (misc) INT_OFLOW OFLOW baddecode submodules ; (vars/env) SRCDIR vdir rcexp xpart dired set -e @@ -261,6 +261,10 @@ sed -i -e "s/Try 'mv --help' for more information/For more information, try '--h # disable these test cases sed -i -E "s|^([^#]*2_31.*)$|#\1|g" tests/printf/printf-cov.pl + +# with ls --dired, in case of error, we have a slightly different error position +sed -i -e "s|44 45|47 48|" tests/ls/stat-failed.sh + sed -i -e "s/du: invalid -t argument/du: invalid --threshold argument/" -e "s/du: option requires an argument/error: a value is required for '--threshold ' but none was supplied/" -e "/Try 'du --help' for more information./d" tests/du/threshold.sh # disable two kind of tests: From 89aef112088f14372ba8e6e9226c25bb3034adff Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Wed, 20 Sep 2023 10:02:20 +0200 Subject: [PATCH 092/208] build-gnu.sh: fix formatting --- util/build-gnu.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/util/build-gnu.sh b/util/build-gnu.sh index 75be52587dd..521f2208e8e 100755 --- a/util/build-gnu.sh +++ b/util/build-gnu.sh @@ -261,9 +261,8 @@ sed -i -e "s/Try 'mv --help' for more information/For more information, try '--h # disable these test cases sed -i -E "s|^([^#]*2_31.*)$|#\1|g" tests/printf/printf-cov.pl - # with ls --dired, in case of error, we have a slightly different error position -sed -i -e "s|44 45|47 48|" tests/ls/stat-failed.sh +sed -i -e "s|44 45|47 48|" tests/ls/stat-failed.sh sed -i -e "s/du: invalid -t argument/du: invalid --threshold argument/" -e "s/du: option requires an argument/error: a value is required for '--threshold ' but none was supplied/" -e "/Try 'du --help' for more information./d" tests/du/threshold.sh From 5eb1fd0b024e23598925ef954f169f401473d5f4 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Wed, 20 Sep 2023 10:36:32 +0200 Subject: [PATCH 093/208] ci: remove committing from CheckScripts.yml --- .github/workflows/CheckScripts.yml | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/.github/workflows/CheckScripts.yml b/.github/workflows/CheckScripts.yml index 995b5456f18..c18c4733cbe 100644 --- a/.github/workflows/CheckScripts.yml +++ b/.github/workflows/CheckScripts.yml @@ -41,14 +41,9 @@ jobs: shell_fmt: name: ShellScript/Format - # no need to run in pr events - # shfmt will be performed on main branch when the PR is merged - if: github.event_name != 'pull_request' runs-on: ubuntu-latest - needs: [ shell_check ] permissions: - contents: write - pull-requests: write + contents: read steps: - uses: actions/checkout@v4 - name: Setup shfmt @@ -56,19 +51,6 @@ jobs: - name: Run shfmt shell: bash run: | - # show differs first for every files that need to be formatted # fmt options: bash syntax, 4 spaces indent, indent for switch-case echo "## show the differences between formatted and original scripts..." find ${{ env.SCRIPT_DIR }} -name "*.sh" -print0 | xargs -0 shfmt -ln=bash -i 4 -ci -d || true - # perform a shell format - echo "## perform a shell format..." - # ignore the error code because `-d` will always return false when the file has difference - find ${{ env.SCRIPT_DIR }} -name "*.sh" -print0 | xargs -0 shfmt -ln=bash -i 4 -ci -w - - name: Commit any changes - uses: EndBug/add-and-commit@v9 - with: - default_author: github_actions - message: "style: auto format by CI (shfmt)" - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - From 3bb180f4711dbbe56dfd3438e3165742a524630d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 20 Sep 2023 16:33:12 +0000 Subject: [PATCH 094/208] chore(deps): update rust crate winapi-util to 0.1.6 --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ece92cdfe46..d4340ff961c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3429,9 +3429,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" dependencies = [ "winapi", ] diff --git a/Cargo.toml b/Cargo.toml index 866b1f58549..42949b7c23c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -328,7 +328,7 @@ unicode-segmentation = "1.10.1" unicode-width = "0.1.11" utf-8 = "0.7.6" walkdir = "2.4" -winapi-util = "0.1.5" +winapi-util = "0.1.6" windows-sys = { version = "0.48.0", default-features = false } xattr = "1.0.1" zip = { version = "0.6.6", default_features = false, features = ["deflate"] } From 2ee4006bfdfbdab521cd56f24112a9157295bae5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 20 Sep 2023 23:08:42 +0000 Subject: [PATCH 095/208] chore(deps): update rust crate rayon to 1.8 --- Cargo.lock | 20 ++++---------------- Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ece92cdfe46..635b6ce11b7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1413,16 +1413,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi", - "libc", -] - [[package]] name = "num_threads" version = "0.1.6" @@ -1707,9 +1697,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" +checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" dependencies = [ "either", "rayon-core", @@ -1717,14 +1707,12 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" +checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" dependencies = [ - "crossbeam-channel", "crossbeam-deque", "crossbeam-utils", - "num_cpus", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 866b1f58549..d8eca9f3a10 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -308,7 +308,7 @@ platform-info = "2.0.2" quick-error = "2.0.1" rand = { version = "0.8", features = ["small_rng"] } rand_core = "0.6" -rayon = "1.7" +rayon = "1.8" redox_syscall = "0.4" regex = "1.9.5" rstest = "0.18.2" From 78e774a937fd83faeef676e1fbfea934c877339c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 21 Sep 2023 04:49:34 +0000 Subject: [PATCH 096/208] chore(deps): update rust crate blake3 to 1.5.0 --- Cargo.lock | 16 ++++------------ Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ece92cdfe46..44a85a20822 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -88,9 +88,9 @@ checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" [[package]] name = "arrayvec" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "autocfg" @@ -166,16 +166,15 @@ dependencies = [ [[package]] name = "blake3" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "199c42ab6972d92c9f8995f086273d25c42fc0f7b2a1fcefba465c1352d25ba5" +checksum = "0231f06152bf547e9c2b5194f247cd97aacf6dcd8b15d8e5ec0663f64580da87" dependencies = [ "arrayref", "arrayvec", "cc", "cfg-if", "constant_time_eq", - "digest", ] [[package]] @@ -732,7 +731,6 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", - "subtle", ] [[package]] @@ -2075,12 +2073,6 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" -[[package]] -name = "subtle" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" - [[package]] name = "syn" version = "1.0.109" diff --git a/Cargo.toml b/Cargo.toml index 866b1f58549..b7ca08b9483 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -339,7 +339,7 @@ sha1 = "0.10.5" sha2 = "0.10.7" sha3 = "0.10.8" blake2b_simd = "1.0.2" -blake3 = "1.4.1" +blake3 = "1.5.0" sm3 = "0.4.2" digest = "0.10.7" From 23ee9b622d19535da485346eaf9ff2d51740ff5f Mon Sep 17 00:00:00 2001 From: pin <90570748+0323pin@users.noreply.github.com> Date: Thu, 21 Sep 2023 12:24:08 +0200 Subject: [PATCH 097/208] Add NetBSD support to uucore. (#5289) * Add NetBSD support to uucore. Fixes https://github.com/uutils/coreutils/issues/5288 --- src/uucore/src/lib/features/fs.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/uucore/src/lib/features/fs.rs b/src/uucore/src/lib/features/fs.rs index 3def63ad6b1..719efc7a6e7 100644 --- a/src/uucore/src/lib/features/fs.rs +++ b/src/uucore/src/lib/features/fs.rs @@ -114,6 +114,7 @@ impl FileInformation { not(target_vendor = "apple"), not(target_os = "android"), not(target_os = "freebsd"), + not(target_os = "netbsd"), not(target_arch = "aarch64"), not(target_arch = "riscv64"), target_pointer_width = "64" @@ -125,6 +126,7 @@ impl FileInformation { target_vendor = "apple", target_os = "android", target_os = "freebsd", + target_os = "netbsd", target_arch = "aarch64", target_arch = "riscv64", not(target_pointer_width = "64") @@ -137,9 +139,16 @@ impl FileInformation { #[cfg(unix)] pub fn inode(&self) -> u64 { - #[cfg(all(not(target_os = "freebsd"), target_pointer_width = "64"))] + #[cfg(all( + not(any(target_os = "freebsd", target_os = "netbsd")), + target_pointer_width = "64" + ))] return self.0.st_ino; - #[cfg(any(target_os = "freebsd", not(target_pointer_width = "64")))] + #[cfg(any( + target_os = "freebsd", + target_os = "netbsd", + not(target_pointer_width = "64") + ))] return self.0.st_ino.into(); } } From df211cd45f6a052054f761d56f0efe9bddd5068e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 21 Sep 2023 16:11:20 +0000 Subject: [PATCH 098/208] chore(deps): update rust crate sha1 to 0.10.6 --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 665df51207f..58a4324a6a1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1943,9 +1943,9 @@ checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965" [[package]] name = "sha1" -version = "0.10.5" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", "cpufeatures", diff --git a/Cargo.toml b/Cargo.toml index fd5d40999d9..d0c012f4751 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -335,7 +335,7 @@ zip = { version = "0.6.6", default_features = false, features = ["deflate"] } hex = "0.4.3" md-5 = "0.10.5" -sha1 = "0.10.5" +sha1 = "0.10.6" sha2 = "0.10.7" sha3 = "0.10.8" blake2b_simd = "1.0.2" From 1d10e0c6748a5b98a1efed6d0a39c49aafbc52fb Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Wed, 20 Sep 2023 22:00:30 +0200 Subject: [PATCH 099/208] ls: rename the function for something more explicit --- src/uu/ls/src/dired.rs | 2 +- src/uu/ls/src/ls.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/uu/ls/src/dired.rs b/src/uu/ls/src/dired.rs index f66d22b38dd..ba8f0933596 100644 --- a/src/uu/ls/src/dired.rs +++ b/src/uu/ls/src/dired.rs @@ -54,7 +54,7 @@ pub fn indent(out: &mut BufWriter) -> UResult<()> { Ok(()) } -pub fn calculate_offset_and_push(dired: &mut DiredOutput, path_len: usize) { +pub fn calculate_subdired(dired: &mut DiredOutput, path_len: usize) { let offset = if dired.subdired_positions.is_empty() { DIRED_TRAILING_OFFSET } else { diff --git a/src/uu/ls/src/ls.rs b/src/uu/ls/src/ls.rs index 3d67cccaad1..7f977da2a2a 100644 --- a/src/uu/ls/src/ls.rs +++ b/src/uu/ls/src/ls.rs @@ -1921,7 +1921,7 @@ pub fn list(locs: Vec<&Path>, config: &Config) -> UResult<()> { } writeln!(out, "{}:", path_data.p_buf.display())?; if config.dired { - dired::calculate_offset_and_push(&mut dired, path_data.display_name.len()); + dired::calculate_subdired(&mut dired, path_data.display_name.len()); } } else { writeln!(out, "\n{}:", path_data.p_buf.display())?; From 8db6146dd3c6b131723bfd0cd3bf3b443a3efdc4 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Wed, 20 Sep 2023 22:00:38 +0200 Subject: [PATCH 100/208] ls remove old comment --- src/uu/ls/src/dired.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/uu/ls/src/dired.rs b/src/uu/ls/src/dired.rs index ba8f0933596..8e85a2e0fa8 100644 --- a/src/uu/ls/src/dired.rs +++ b/src/uu/ls/src/dired.rs @@ -95,7 +95,6 @@ pub fn add_total(total_len: usize, dired: &mut DiredOutput) { dired.just_printed_total = true; dired.dired_positions.push(BytePosition { start: 0, - // the 2 is from the trailing spaces // the 1 is from the line ending (\n) end: total_len + DIRED_TRAILING_OFFSET - 1, }); From a12dd2ee1eddb42d23646ef7964b919a4b752d3c Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Wed, 20 Sep 2023 22:09:34 +0200 Subject: [PATCH 101/208] ls rename the function for consistency --- src/uu/ls/src/dired.rs | 7 +++---- src/uu/ls/src/ls.rs | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/uu/ls/src/dired.rs b/src/uu/ls/src/dired.rs index 8e85a2e0fa8..288d7fc76dc 100644 --- a/src/uu/ls/src/dired.rs +++ b/src/uu/ls/src/dired.rs @@ -33,7 +33,7 @@ impl fmt::Display for BytePosition { static DIRED_TRAILING_OFFSET: usize = 2; /// Calculates the byte positions for DIRED -pub fn calculate_dired_byte_positions( +pub fn calculate_dired( output_display_len: usize, dfn_len: usize, dired_positions: &[BytePosition], @@ -143,12 +143,11 @@ mod tests { use super::*; #[test] - fn test_calculate_dired_byte_positions() { + fn test_calculate_dired() { let output_display = "sample_output".to_string(); let dfn = "sample_file".to_string(); let dired_positions = vec![BytePosition { start: 5, end: 10 }]; - let (start, end) = - calculate_dired_byte_positions(output_display.len(), dfn.len(), &dired_positions); + let (start, end) = calculate_dired(output_display.len(), dfn.len(), &dired_positions); assert_eq!(start, 24); assert_eq!(end, 35); diff --git a/src/uu/ls/src/ls.rs b/src/uu/ls/src/ls.rs index 7f977da2a2a..f7a3b79ae6a 100644 --- a/src/uu/ls/src/ls.rs +++ b/src/uu/ls/src/ls.rs @@ -2545,7 +2545,7 @@ fn display_item_long( let displayed_file = display_file_name(item, config, None, String::new(), out).contents; if config.dired { - let (start, end) = dired::calculate_dired_byte_positions( + let (start, end) = dired::calculate_dired( output_display.len(), displayed_file.len(), &dired.dired_positions, From 0794d1338df62f303e7077e3397acd1873dc2766 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Fri, 22 Sep 2023 10:23:01 +0200 Subject: [PATCH 102/208] ls: fix test which fails if /tmp uses tmpfs --- tests/by-util/test_ls.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/by-util/test_ls.rs b/tests/by-util/test_ls.rs index 87b3066569a..23dfafa3200 100644 --- a/tests/by-util/test_ls.rs +++ b/tests/by-util/test_ls.rs @@ -2,7 +2,7 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -// spell-checker:ignore (words) READMECAREFULLY birthtime doesntexist oneline somebackup lrwx somefile somegroup somehiddenbackup somehiddenfile tabsize aaaaaaaa bbbb cccc dddddddd ncccc neee naaaaa nbcdef nfffff dired subdired +// spell-checker:ignore (words) READMECAREFULLY birthtime doesntexist oneline somebackup lrwx somefile somegroup somehiddenbackup somehiddenfile tabsize aaaaaaaa bbbb cccc dddddddd ncccc neee naaaaa nbcdef nfffff dired subdired tmpfs #[cfg(any(unix, feature = "feat_selinux"))] use crate::common::util::expected_result; @@ -3608,9 +3608,13 @@ fn test_ls_dired_complex() { let mut cmd = scene.ucmd(); cmd.arg("--dired").arg("-l").arg("d"); let result = cmd.succeeds(); - // Number of blocks + + // Number of blocks. We run this test only if the default size of a newly created directory is + // 4096 bytes to prevent it from failing where this is not the case (e.g. using tmpfs for /tmp). #[cfg(target_os = "linux")] - result.stdout_contains(" total 4"); + if at.metadata("d/d").len() == 4096 { + result.stdout_contains(" total 4"); + } let output = result.stdout_str().to_string(); println!("Output:\n{}", output); From 38831c46d1b896bac7bd45af31644d2733be2c28 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Fri, 22 Sep 2023 10:58:12 +0200 Subject: [PATCH 103/208] relpath: show error if no argument provided Fixes #5300 --- src/uu/relpath/src/relpath.rs | 6 +++++- tests/by-util/test_relpath.rs | 7 +++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/uu/relpath/src/relpath.rs b/src/uu/relpath/src/relpath.rs index 46dd0d6631e..aa8b5caab96 100644 --- a/src/uu/relpath/src/relpath.rs +++ b/src/uu/relpath/src/relpath.rs @@ -82,6 +82,10 @@ pub fn uu_app() -> Command { .arg(Arg::new(options::DIR).short('d').help( "If any of FROM and TO is not subpath of DIR, output absolute path instead of relative", )) - .arg(Arg::new(options::TO).value_hint(clap::ValueHint::AnyPath)) + .arg( + Arg::new(options::TO) + .value_hint(clap::ValueHint::AnyPath) + .required(true), + ) .arg(Arg::new(options::FROM).value_hint(clap::ValueHint::AnyPath)) } diff --git a/tests/by-util/test_relpath.rs b/tests/by-util/test_relpath.rs index 8a3c91802e1..f506e01c5cc 100644 --- a/tests/by-util/test_relpath.rs +++ b/tests/by-util/test_relpath.rs @@ -180,3 +180,10 @@ fn test_relpath_no_from_with_d() { assert!(Path::new(&result_stdout).is_absolute()); } } + +#[test] +fn test_relpath_no_to() { + new_ucmd!() + .fails() + .stderr_contains("required arguments were not provided"); +} From cc6a1ae4f415e572733dbfbd9c56dc0d75f05061 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 22 Sep 2023 15:40:11 +0000 Subject: [PATCH 104/208] chore(deps): update rust crate md-5 to 0.10.6 --- Cargo.lock | 5 +++-- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 58a4324a6a1..3723c52da62 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1275,10 +1275,11 @@ checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" [[package]] name = "md-5" -version = "0.10.5" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" dependencies = [ + "cfg-if", "digest", ] diff --git a/Cargo.toml b/Cargo.toml index d0c012f4751..65b23faa2bb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -334,7 +334,7 @@ xattr = "1.0.1" zip = { version = "0.6.6", default_features = false, features = ["deflate"] } hex = "0.4.3" -md-5 = "0.10.5" +md-5 = "0.10.6" sha1 = "0.10.6" sha2 = "0.10.7" sha3 = "0.10.8" From 75044c1bc47321d8b360045693dd386515e27fa9 Mon Sep 17 00:00:00 2001 From: KAA the Wise Date: Fri, 22 Sep 2023 18:41:31 +0300 Subject: [PATCH 105/208] rm: make option types public Made `Options` and `InteractiveMode` public and added documentation for them. --- src/uu/rm/src/rm.rs | 50 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 40 insertions(+), 10 deletions(-) diff --git a/src/uu/rm/src/rm.rs b/src/uu/rm/src/rm.rs index d9421d0ae71..96e0fa7aa2b 100644 --- a/src/uu/rm/src/rm.rs +++ b/src/uu/rm/src/rm.rs @@ -18,22 +18,52 @@ use uucore::{format_usage, help_about, help_section, help_usage, prompt_yes, sho use walkdir::{DirEntry, WalkDir}; #[derive(Eq, PartialEq, Clone, Copy)] -enum InteractiveMode { +/// Enum, determining when the `rm` will prompt the user about the file deletion +pub enum InteractiveMode { + /// Never prompt Never, + /// Prompt once before removing more than three files, or when removing + /// recursively. Once, + /// Prompt before every removal Always, + /// TODO clarify what this option does PromptProtected, } -struct Options { - force: bool, - interactive: InteractiveMode, +/// Options for the `rm` command +/// +/// All options are public so that the options can be programmatically +/// constructed by other crates, such as Nushell. That means that this struct +/// is part of our public API. It should therefore not be changed without good +/// reason. +/// +/// The fields are documented with the arguments that determine their value. +pub struct Options { + /// `-f`, `--force` + pub force: bool, + /// Iterative mode, determines when the command will prompt. + /// + /// Set by the following arguments: + /// - `-i`: [`InteractiveMode::Always`] + /// - `-I`: [`InteractiveMode::Once`] + /// - `--interactive`: sets one of the above or [`InteractiveMode::Never`] + /// - `-f`: implicitly sets [`InteractiveMode::Never`] + /// + /// If no other option sets this mode, [`InteractiveMode::PromptProtected`] + /// is used + pub interactive: InteractiveMode, #[allow(dead_code)] - one_fs: bool, - preserve_root: bool, - recursive: bool, - dir: bool, - verbose: bool, + /// `--one-file-system` + pub one_fs: bool, + /// `--preserve-root`/`--no-preserve-root` + pub preserve_root: bool, + /// `-r`, `--recursive` + pub recursive: bool, + /// `-d`, `--dir` + pub dir: bool, + /// `-v`, `--verbose` + pub verbose: bool, } const ABOUT: &str = help_about!("rm.md"); @@ -268,7 +298,7 @@ fn remove(files: &[&OsStr], options: &Options) -> bool { // TODO: actually print out the specific error // TODO: When the error is not about missing files // (e.g., permission), even rm -f should fail with - // outputting the error, but there's no easy eay. + // outputting the error, but there's no easy way. if options.force { false } else { From 20fb473da852d9b09a592b258c4c183c3d97dc64 Mon Sep 17 00:00:00 2001 From: KAA the Wise Date: Fri, 22 Sep 2023 19:17:51 +0300 Subject: [PATCH 106/208] rm: make the `remove` function public --- src/uu/rm/src/rm.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/uu/rm/src/rm.rs b/src/uu/rm/src/rm.rs index 96e0fa7aa2b..19c749cf09b 100644 --- a/src/uu/rm/src/rm.rs +++ b/src/uu/rm/src/rm.rs @@ -279,7 +279,13 @@ pub fn uu_app() -> Command { } // TODO: implement one-file-system (this may get partially implemented in walkdir) -fn remove(files: &[&OsStr], options: &Options) -> bool { +/// Remove (or unlink) the given files +/// +/// Returns true if it has encountered an error. +/// +/// Behavior is determined by the `options` parameter, see [`Options`] for +/// details. +pub fn remove(files: &[&OsStr], options: &Options) -> bool { let mut had_err = false; for filename in files { From 30f1fceddcd77c79ec505b4dfad9b28e4d786d18 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sat, 23 Sep 2023 09:29:00 +0200 Subject: [PATCH 107/208] add nushell to the list of ignored names --- .vscode/cspell.dictionaries/acronyms+names.wordlist.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/.vscode/cspell.dictionaries/acronyms+names.wordlist.txt b/.vscode/cspell.dictionaries/acronyms+names.wordlist.txt index 8711913d99d..c004ea2f822 100644 --- a/.vscode/cspell.dictionaries/acronyms+names.wordlist.txt +++ b/.vscode/cspell.dictionaries/acronyms+names.wordlist.txt @@ -58,6 +58,7 @@ MinGW Minix NetBSD Novell +Nushell OpenBSD POSIX PowerPC From 48613b47176c30c414f979edd5350d7c1aa56ba6 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Fri, 22 Sep 2023 22:12:44 +0200 Subject: [PATCH 108/208] ls --dired: replace the previous "total: xx" padding method by something easier --- src/uu/ls/src/dired.rs | 47 ++++++++++++++++-------------------------- src/uu/ls/src/ls.rs | 2 +- util/build-gnu.sh | 6 +++--- 3 files changed, 22 insertions(+), 33 deletions(-) diff --git a/src/uu/ls/src/dired.rs b/src/uu/ls/src/dired.rs index 288d7fc76dc..8574e750e2d 100644 --- a/src/uu/ls/src/dired.rs +++ b/src/uu/ls/src/dired.rs @@ -20,7 +20,7 @@ pub struct BytePosition { pub struct DiredOutput { pub dired_positions: Vec, pub subdired_positions: Vec, - pub just_printed_total: bool, + pub padding: usize, } impl fmt::Display for BytePosition { @@ -75,7 +75,7 @@ pub fn print_dired_output( out.flush()?; if config.recursive { print_positions("//SUBDIRED//", &dired.subdired_positions); - } else if !dired.just_printed_total { + } else if dired.padding == 0 { print_positions("//DIRED//", &dired.dired_positions); } println!("//DIRED-OPTIONS// --quoting-style={}", config.quoting_style); @@ -92,12 +92,9 @@ fn print_positions(prefix: &str, positions: &Vec) { } pub fn add_total(total_len: usize, dired: &mut DiredOutput) { - dired.just_printed_total = true; - dired.dired_positions.push(BytePosition { - start: 0, - // the 1 is from the line ending (\n) - end: total_len + DIRED_TRAILING_OFFSET - 1, - }); + // when dealing with " total: xx", it isn't part of the //DIRED// + // so, we just keep the size line to add it to the position of the next file + dired.padding = total_len + DIRED_TRAILING_OFFSET; } /// Calculates byte positions and updates the dired structure. @@ -114,28 +111,20 @@ pub fn calculate_and_update_positions( }); let start = output_display_len + offset + DIRED_TRAILING_OFFSET; let end = start + dfn_len; - update_positions(start, end, dired, true); + update_positions(start, end, dired); } /// Updates the dired positions based on the given start and end positions. -/// update when it is the first element in the list (to manage "total X" +/// update when it is the first element in the list (to manage "total X") /// insert when it isn't the about total -pub fn update_positions(start: usize, end: usize, dired: &mut DiredOutput, adjust: bool) { - if dired.just_printed_total { - if let Some(last_position) = dired.dired_positions.last_mut() { - *last_position = BytePosition { - start: if adjust { - start + last_position.end - } else { - start - }, - end: if adjust { end + last_position.end } else { end }, - }; - dired.just_printed_total = false; - } - } else { - dired.dired_positions.push(BytePosition { start, end }); - } +pub fn update_positions(start: usize, end: usize, dired: &mut DiredOutput) { + // padding can be 0 but as it doesn't matter< + dired.dired_positions.push(BytePosition { + start: start + dired.padding, + end: end + dired.padding, + }); + // Remove the previous padding + dired.padding = 0; } #[cfg(test)] @@ -158,17 +147,17 @@ mod tests { let mut dired = DiredOutput { dired_positions: vec![BytePosition { start: 5, end: 10 }], subdired_positions: vec![], - just_printed_total: true, + padding: 10, }; // Test with adjust = true - update_positions(15, 20, &mut dired, true); + update_positions(15, 20, &mut dired); let last_position = dired.dired_positions.last().unwrap(); assert_eq!(last_position.start, 25); // 15 + 10 (end of the previous position) assert_eq!(last_position.end, 30); // 20 + 10 (end of the previous position) // Test with adjust = false - update_positions(30, 35, &mut dired, false); + update_positions(30, 35, &mut dired); let last_position = dired.dired_positions.last().unwrap(); assert_eq!(last_position.start, 30); assert_eq!(last_position.end, 35); diff --git a/src/uu/ls/src/ls.rs b/src/uu/ls/src/ls.rs index f7a3b79ae6a..301aefaf3a3 100644 --- a/src/uu/ls/src/ls.rs +++ b/src/uu/ls/src/ls.rs @@ -2550,7 +2550,7 @@ fn display_item_long( displayed_file.len(), &dired.dired_positions, ); - dired::update_positions(start, end, dired, false); + dired::update_positions(start, end, dired); } write!(output_display, "{}{}", displayed_file, config.line_ending).unwrap(); } else { diff --git a/util/build-gnu.sh b/util/build-gnu.sh index 521f2208e8e..e2c67539441 100755 --- a/util/build-gnu.sh +++ b/util/build-gnu.sh @@ -261,11 +261,11 @@ sed -i -e "s/Try 'mv --help' for more information/For more information, try '--h # disable these test cases sed -i -E "s|^([^#]*2_31.*)$|#\1|g" tests/printf/printf-cov.pl -# with ls --dired, in case of error, we have a slightly different error position -sed -i -e "s|44 45|47 48|" tests/ls/stat-failed.sh - sed -i -e "s/du: invalid -t argument/du: invalid --threshold argument/" -e "s/du: option requires an argument/error: a value is required for '--threshold ' but none was supplied/" -e "/Try 'du --help' for more information./d" tests/du/threshold.sh +# with ls --dired, in case of error, we have a slightly different error position +sed -i -e "s|44 45|48 49|" tests/ls/stat-failed.sh + # disable two kind of tests: # "hostid BEFORE --help" doesn't fail for GNU. we fail. we are probably doing better # "hostid BEFORE --help AFTER " same for this From 06219350fa4d9277b8a1adea1050c180db39ff32 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Sat, 23 Sep 2023 14:44:30 +0200 Subject: [PATCH 109/208] nl: fix output order if stdin and files are mixed --- src/uu/nl/src/nl.rs | 20 +++++++------------- tests/by-util/test_nl.rs | 13 +++++++++++++ 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/uu/nl/src/nl.rs b/src/uu/nl/src/nl.rs index fef9c030a2e..6e1cb683534 100644 --- a/src/uu/nl/src/nl.rs +++ b/src/uu/nl/src/nl.rs @@ -168,7 +168,6 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { )); } - let mut read_stdin = false; let files: Vec = match matches.get_many::(options::FILE) { Some(v) => v.clone().map(|v| v.to_owned()).collect(), None => vec!["-".to_owned()], @@ -178,21 +177,16 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { for file in &files { if file == "-" { - // If both file names and '-' are specified, we choose to treat first all - // regular files, and then read from stdin last. - read_stdin = true; - continue; + let mut buffer = BufReader::new(stdin()); + nl(&mut buffer, &mut stats, &settings)?; + } else { + let path = Path::new(file); + let reader = File::open(path).map_err_context(|| file.to_string())?; + let mut buffer = BufReader::new(reader); + nl(&mut buffer, &mut stats, &settings)?; } - let path = Path::new(file); - let reader = File::open(path).map_err_context(|| file.to_string())?; - let mut buffer = BufReader::new(reader); - nl(&mut buffer, &mut stats, &settings)?; } - if read_stdin { - let mut buffer = BufReader::new(stdin()); - nl(&mut buffer, &mut stats, &settings)?; - } Ok(()) } diff --git a/tests/by-util/test_nl.rs b/tests/by-util/test_nl.rs index b008c61de77..118c4cf047f 100644 --- a/tests/by-util/test_nl.rs +++ b/tests/by-util/test_nl.rs @@ -349,6 +349,19 @@ fn test_default_body_numbering_multiple_files() { .stdout_is(" 1\ta\n 2\tb\n 3\tc\n"); } +#[test] +fn test_default_body_numbering_multiple_files_and_stdin() { + let (at, mut ucmd) = at_and_ucmd!(); + + at.write("a.txt", "a"); + at.write("c.txt", "c"); + + ucmd.args(&["a.txt", "-", "c.txt"]) + .pipe_in("b") + .succeeds() + .stdout_is(" 1\ta\n 2\tb\n 3\tc\n"); +} + #[test] fn test_body_numbering_all_lines_without_delimiter() { for arg in ["-ba", "--body-numbering=a"] { From 4b76b2f33220b268f3ac148fa3ac0a110947b5a1 Mon Sep 17 00:00:00 2001 From: KAA the Wise Date: Sat, 23 Sep 2023 15:55:57 +0300 Subject: [PATCH 110/208] rm: document PromptProtected interactive mode option --- src/uu/rm/src/rm.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uu/rm/src/rm.rs b/src/uu/rm/src/rm.rs index 19c749cf09b..87767b904bd 100644 --- a/src/uu/rm/src/rm.rs +++ b/src/uu/rm/src/rm.rs @@ -27,7 +27,7 @@ pub enum InteractiveMode { Once, /// Prompt before every removal Always, - /// TODO clarify what this option does + /// Prompt only on write-protected files PromptProtected, } From 306a58a73147cd35491a3bd9facb6e0ee057752e Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Tue, 29 Nov 2022 10:20:04 +0100 Subject: [PATCH 111/208] tail: remove unused var "_event_counter" --- src/uu/tail/src/follow/watch.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/uu/tail/src/follow/watch.rs b/src/uu/tail/src/follow/watch.rs index 3ecb47f6734..cd1dd49c367 100644 --- a/src/uu/tail/src/follow/watch.rs +++ b/src/uu/tail/src/follow/watch.rs @@ -479,7 +479,6 @@ pub fn follow(mut observer: Observer, settings: &Settings) -> UResult<()> { let mut process = platform::ProcessChecker::new(observer.pid); - let mut _event_counter = 0; let mut _timeout_counter = 0; // main follow loop @@ -529,7 +528,6 @@ pub fn follow(mut observer: Observer, settings: &Settings) -> UResult<()> { .receiver .recv_timeout(settings.sleep_sec); if rx_result.is_ok() { - _event_counter += 1; _timeout_counter = 0; } From a4a69c8ee7e1b93d87500504800d91186d3972d6 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Tue, 29 Nov 2022 10:25:48 +0100 Subject: [PATCH 112/208] tail: "_timeout_counter" -> "timeout_counter" --- src/uu/tail/src/follow/watch.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/uu/tail/src/follow/watch.rs b/src/uu/tail/src/follow/watch.rs index cd1dd49c367..fbda27aa03d 100644 --- a/src/uu/tail/src/follow/watch.rs +++ b/src/uu/tail/src/follow/watch.rs @@ -479,7 +479,7 @@ pub fn follow(mut observer: Observer, settings: &Settings) -> UResult<()> { let mut process = platform::ProcessChecker::new(observer.pid); - let mut _timeout_counter = 0; + let mut timeout_counter = 0; // main follow loop loop { @@ -528,7 +528,7 @@ pub fn follow(mut observer: Observer, settings: &Settings) -> UResult<()> { .receiver .recv_timeout(settings.sleep_sec); if rx_result.is_ok() { - _timeout_counter = 0; + timeout_counter = 0; } let mut paths = vec![]; // Paths worth checking for new content to print @@ -567,7 +567,7 @@ pub fn follow(mut observer: Observer, settings: &Settings) -> UResult<()> { } Ok(Err(e)) => return Err(USimpleError::new(1, format!("NotifyError: {e}"))), Err(mpsc::RecvTimeoutError::Timeout) => { - _timeout_counter += 1; + timeout_counter += 1; } Err(e) => return Err(USimpleError::new(1, format!("RecvTimeoutError: {e}"))), } @@ -584,7 +584,7 @@ pub fn follow(mut observer: Observer, settings: &Settings) -> UResult<()> { _read_some = observer.files.tail_file(path, settings.verbose)?; } - if _timeout_counter == settings.max_unchanged_stats { + if timeout_counter == settings.max_unchanged_stats { /* TODO: [2021-10; jhscheer] implement timeout_counter for each file. ‘--max-unchanged-stats=n’ From 616c3f4a7fb199a525bcf148ed799516e13cf739 Mon Sep 17 00:00:00 2001 From: Benjamin Bara Date: Sat, 22 Jul 2023 11:25:39 +0200 Subject: [PATCH 113/208] util: extend run_ucmd_as_root for stdin/stdout This enables to give path to files (as str) which are then used as either stdin or stdout. --- tests/common/util.rs | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/tests/common/util.rs b/tests/common/util.rs index 0fbc58cd540..6f4e76d4228 100644 --- a/tests/common/util.rs +++ b/tests/common/util.rs @@ -2532,6 +2532,16 @@ pub fn expected_result(ts: &TestScenario, args: &[&str]) -> std::result::Result< pub fn run_ucmd_as_root( ts: &TestScenario, args: &[&str], +) -> std::result::Result { + run_ucmd_as_root_with_stdin_stdout(ts, args, None, None) +} + +#[cfg(unix)] +pub fn run_ucmd_as_root_with_stdin_stdout( + ts: &TestScenario, + args: &[&str], + stdin: Option<&str>, + stdout: Option<&str>, ) -> std::result::Result { if is_ci() { Err(format!("{UUTILS_INFO}: {}", "cannot run inside CI")) @@ -2546,16 +2556,21 @@ pub fn run_ucmd_as_root( Ok(output) if String::from_utf8_lossy(&output.stdout).eq("root\n") => { // we can run sudo and we're root // run ucmd as root: - Ok(ts - .cmd("sudo") - .env("PATH", PATH) + let mut cmd = ts.cmd("sudo"); + cmd.env("PATH", PATH) .envs(DEFAULT_ENV) .arg("-E") .arg("--non-interactive") .arg(&ts.bin_path) .arg(&ts.util_name) - .args(args) - .run()) + .args(args); + if let Some(stdin) = stdin { + cmd.set_stdin(File::open(stdin).unwrap()); + } + if let Some(stdout) = stdout { + cmd.set_stdout(File::open(stdout).unwrap()); + } + Ok(cmd.run()) } Ok(output) if String::from_utf8_lossy(&output.stderr).eq("sudo: a password is required\n") => From 8a9b29ddfb5b0486c6461b6d85cebb64c63586a5 Mon Sep 17 00:00:00 2001 From: Benjamin Bara Date: Sat, 18 Mar 2023 08:54:47 +0100 Subject: [PATCH 114/208] dd: fix GNU test 'dd/skip-seek-past-dev' --- src/uu/dd/src/dd.rs | 57 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 48 insertions(+), 9 deletions(-) diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index 4ac2aa78006..b79ae22da4e 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -3,7 +3,7 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -// spell-checker:ignore fname, tname, fpath, specfile, testfile, unspec, ifile, ofile, outfile, fullblock, urand, fileio, atoe, atoibm, behaviour, bmax, bremain, cflags, creat, ctable, ctty, datastructures, doesnt, etoa, fileout, fname, gnudd, iconvflags, iseek, nocache, noctty, noerror, nofollow, nolinks, nonblock, oconvflags, oseek, outfile, parseargs, rlen, rmax, rremain, rsofar, rstat, sigusr, wlen, wstat seekable oconv canonicalized fadvise Fadvise FADV DONTNEED ESPIPE +// spell-checker:ignore fname, ftype, tname, fpath, specfile, testfile, unspec, ifile, ofile, outfile, fullblock, urand, fileio, atoe, atoibm, behaviour, bmax, bremain, cflags, creat, ctable, ctty, datastructures, doesnt, etoa, fileout, fname, gnudd, iconvflags, iseek, nocache, noctty, noerror, nofollow, nolinks, nonblock, oconvflags, oseek, outfile, parseargs, rlen, rmax, rremain, rsofar, rstat, sigusr, wlen, wstat seekable oconv canonicalized fadvise Fadvise FADV DONTNEED ESPIPE mod datastructures; use datastructures::*; @@ -49,6 +49,8 @@ use nix::{ fcntl::{posix_fadvise, PosixFadviseAdvice}, }; use uucore::display::Quotable; +#[cfg(unix)] +use uucore::error::set_exit_code; use uucore::error::{FromIo, UResult}; use uucore::{format_usage, help_about, help_section, help_usage, show_error}; #[cfg(target_os = "linux")] @@ -200,14 +202,25 @@ impl Source { Err(e) => Err(e), }, #[cfg(unix)] - Self::StdinFile(f) => match io::copy(&mut f.take(n), &mut io::sink()) { - Ok(m) if m < n => { - show_error!("'standard input': cannot skip to specified offset"); - Ok(m) + Self::StdinFile(f) => { + if let Ok(Some(len)) = try_get_len_of_block_device(f) { + if len < n { + // GNU compatibility: + // this case prints the stats but sets the exit code to 1 + show_error!("'standard input': cannot skip: Invalid argument"); + set_exit_code(1); + return Ok(len); + } } - Ok(m) => Ok(m), - Err(e) => Err(e), - }, + match io::copy(&mut f.take(n), &mut io::sink()) { + Ok(m) if m < n => { + show_error!("'standard input': cannot skip to specified offset"); + Ok(m) + } + Ok(m) => Ok(m), + Err(e) => Err(e), + } + } Self::File(f) => f.seek(io::SeekFrom::Start(n)), #[cfg(unix)] Self::Fifo(f) => io::copy(&mut f.take(n), &mut io::sink()), @@ -527,7 +540,19 @@ impl Dest { fn seek(&mut self, n: u64) -> io::Result { match self { Self::Stdout(stdout) => io::copy(&mut io::repeat(0).take(n), stdout), - Self::File(f, _) => f.seek(io::SeekFrom::Start(n)), + Self::File(f, _) => { + #[cfg(unix)] + if let Ok(Some(len)) = try_get_len_of_block_device(f) { + if len < n { + // GNU compatibility: + // this case prints the stats but sets the exit code to 1 + show_error!("'standard output': cannot seek: Invalid argument"); + set_exit_code(1); + return Ok(len); + } + } + f.seek(io::SeekFrom::Start(n)) + } #[cfg(unix)] Self::Fifo(f) => { // Seeking in a named pipe means *reading* from the pipe. @@ -1133,6 +1158,20 @@ fn is_stdout_redirected_to_seekable_file() -> bool { } } +/// Try to get the len if it is a block device +#[cfg(unix)] +fn try_get_len_of_block_device(file: &mut File) -> io::Result> { + let ftype = file.metadata()?.file_type(); + if !ftype.is_block_device() { + return Ok(None); + } + + // FIXME: this can be replaced by file.stream_len() when stable. + let len = file.seek(SeekFrom::End(0))?; + file.rewind()?; + Ok(Some(len)) +} + /// Decide whether the named file is a named pipe, also known as a FIFO. #[cfg(unix)] fn is_fifo(filename: &str) -> bool { From 17f4d17021148b038e2f2c7942eea6004e06a06c Mon Sep 17 00:00:00 2001 From: Benjamin Bara Date: Sat, 22 Jul 2023 11:26:56 +0200 Subject: [PATCH 115/208] tests: dd: add skip-seek-past-dev tests These tests try to read or write past a block device, where the block device is either given as stdin or stdout. It requires access to the block device, and therefore is executed as root. For now, it is assumed that a block device "/dev/sda1" with a size smaller than 10000000000000000 exists. --- tests/by-util/test_dd.rs | 44 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/tests/by-util/test_dd.rs b/tests/by-util/test_dd.rs index fe38acca47b..8ebf57c1c84 100644 --- a/tests/by-util/test_dd.rs +++ b/tests/by-util/test_dd.rs @@ -4,6 +4,8 @@ // file that was distributed with this source code. // 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, iseek, nocache, noctty, noerror, nofollow, nolinks, nonblock, oconvflags, oseek, outfile, parseargs, rlen, rmax, rposition, rremain, rsofar, rstat, sigusr, sigval, wlen, wstat abcdefghijklm abcdefghi nabcde nabcdefg abcdefg +#[cfg(unix)] +use crate::common::util::run_ucmd_as_root_with_stdin_stdout; use crate::common::util::TestScenario; #[cfg(all(not(windows), feature = "printf"))] use crate::common::util::{UCommand, TESTS_BINARY}; @@ -1566,3 +1568,45 @@ fn test_nocache_file() { .succeeds() .stderr_only("2048+0 records in\n2048+0 records out\n"); } + +#[test] +#[cfg(unix)] +fn test_skip_past_dev() { + // NOTE: This test intends to trigger code which can only be reached with root permissions. + let ts = TestScenario::new(util_name!()); + + if let Ok(result) = run_ucmd_as_root_with_stdin_stdout( + &ts, + &["bs=1", "skip=10000000000000000", "count=0", "status=noxfer"], + Some("/dev/sda1"), + None, + ) { + result.stderr_contains("dd: 'standard input': cannot skip: Invalid argument"); + result.stderr_contains("0+0 records in"); + result.stderr_contains("0+0 records out"); + result.code_is(1); + } else { + print!("TEST SKIPPED"); + } +} + +#[test] +#[cfg(unix)] +fn test_seek_past_dev() { + // NOTE: This test intends to trigger code which can only be reached with root permissions. + let ts = TestScenario::new(util_name!()); + + if let Ok(result) = run_ucmd_as_root_with_stdin_stdout( + &ts, + &["bs=1", "seek=10000000000000000", "count=0", "status=noxfer"], + None, + Some("/dev/sda1"), + ) { + result.stderr_contains("dd: 'standard output': cannot seek: Invalid argument"); + result.stderr_contains("0+0 records in"); + result.stderr_contains("0+0 records out"); + result.code_is(1); + } else { + print!("TEST SKIPPED"); + } +} From bd0fb817a7ae32f0dc6fafe6aa2ade7e81d3d05b Mon Sep 17 00:00:00 2001 From: tommady Date: Sun, 24 Sep 2023 16:53:27 +0800 Subject: [PATCH 116/208] cp: fix the result of inodes are not the same when preserve links is flagged (#5064) Should fix: ``` rm -rf a b c touch a ln -s a b mkdir c ./target/debug/coreutils cp --preserve=links -R -H a b c a_inode=$(ls -i c/a|sed 's,c/.*,,') b_inode=$(ls -i c/b|sed 's,c/.*,,') echo "$a_inode" = "$b_inode" ``` --- src/uu/cp/src/copydir.rs | 57 ++++++----- src/uu/cp/src/cp.rs | 158 ++++++++++++++----------------- tests/by-util/test_cp.rs | 199 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 296 insertions(+), 118 deletions(-) diff --git a/src/uu/cp/src/copydir.rs b/src/uu/cp/src/copydir.rs index 430548d0cf3..ac44ce68752 100644 --- a/src/uu/cp/src/copydir.rs +++ b/src/uu/cp/src/copydir.rs @@ -8,7 +8,7 @@ //! See the [`copy_directory`] function for more information. #[cfg(windows)] use std::borrow::Cow; -use std::collections::HashSet; +use std::collections::{HashMap, HashSet}; use std::env; use std::fs; use std::io; @@ -24,8 +24,8 @@ use uucore::uio_error; use walkdir::{DirEntry, WalkDir}; use crate::{ - aligned_ancestors, context_for, copy_attributes, copy_file, copy_link, preserve_hardlinks, - CopyResult, Error, Options, + aligned_ancestors, context_for, copy_attributes, copy_file, copy_link, CopyResult, Error, + Options, }; /// Ensure a Windows path starts with a `\\?`. @@ -200,7 +200,7 @@ fn copy_direntry( options: &Options, symlinked_files: &mut HashSet, preserve_hard_links: bool, - hard_links: &mut Vec<(String, u64)>, + copied_files: &mut HashMap, ) -> CopyResult<()> { let Entry { source_absolute, @@ -240,30 +240,27 @@ fn copy_direntry( // If the source is not a directory, then we need to copy the file. if !source_absolute.is_dir() { if preserve_hard_links { - let dest = local_to_target.as_path().to_path_buf(); - let found_hard_link = preserve_hardlinks(hard_links, &source_absolute, &dest)?; - if !found_hard_link { - match copy_file( - progress_bar, - &source_absolute, - local_to_target.as_path(), - options, - symlinked_files, - false, - ) { - Ok(_) => Ok(()), - Err(err) => { - if source_absolute.is_symlink() { - // silent the error with a symlink - // In case we do --archive, we might copy the symlink - // before the file itself - Ok(()) - } else { - Err(err) - } + match copy_file( + progress_bar, + &source_absolute, + local_to_target.as_path(), + options, + symlinked_files, + copied_files, + false, + ) { + Ok(_) => Ok(()), + Err(err) => { + if source_absolute.is_symlink() { + // silent the error with a symlink + // In case we do --archive, we might copy the symlink + // before the file itself + Ok(()) + } else { + Err(err) } - }?; - } + } + }?; } else { // At this point, `path` is just a plain old file. // Terminate this function immediately if there is any @@ -277,6 +274,7 @@ fn copy_direntry( local_to_target.as_path(), options, symlinked_files, + copied_files, false, ) { Ok(_) => {} @@ -310,6 +308,7 @@ pub(crate) fn copy_directory( target: &Path, options: &Options, symlinked_files: &mut HashSet, + copied_files: &mut HashMap, source_in_command_line: bool, ) -> CopyResult<()> { if !options.recursive { @@ -324,6 +323,7 @@ pub(crate) fn copy_directory( target, options, symlinked_files, + copied_files, source_in_command_line, ); } @@ -372,7 +372,6 @@ pub(crate) fn copy_directory( }; let target = tmp.as_path(); - let mut hard_links: Vec<(String, u64)> = vec![]; let preserve_hard_links = options.preserve_hard_links(); // Collect some paths here that are invariant during the traversal @@ -397,7 +396,7 @@ pub(crate) fn copy_directory( options, symlinked_files, preserve_hard_links, - &mut hard_links, + copied_files, )?; } // Print an error message, but continue traversing the directory. diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index 516f20fdad5..05261c22fe2 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -9,7 +9,7 @@ use quick_error::quick_error; use std::borrow::Cow; use std::cmp::Ordering; -use std::collections::HashSet; +use std::collections::{HashMap, HashSet}; use std::env; #[cfg(not(windows))] use std::ffi::CString; @@ -1106,67 +1106,6 @@ fn parse_path_args( Ok((paths, target)) } -/// Get the inode information for a file. -fn get_inode(file_info: &FileInformation) -> u64 { - #[cfg(unix)] - let result = file_info.inode(); - #[cfg(windows)] - let result = file_info.file_index(); - result -} - -#[cfg(target_os = "redox")] -fn preserve_hardlinks( - hard_links: &mut Vec<(String, u64)>, - source: &std::path::Path, - dest: &std::path::Path, - found_hard_link: &mut bool, -) -> CopyResult<()> { - // Redox does not currently support hard links - Ok(()) -} - -/// Hard link a pair of files if needed _and_ record if this pair is a new hard link. -#[cfg(not(target_os = "redox"))] -fn preserve_hardlinks( - hard_links: &mut Vec<(String, u64)>, - source: &std::path::Path, - dest: &std::path::Path, -) -> CopyResult { - let info = FileInformation::from_path(source, false) - .context(format!("cannot stat {}", source.quote()))?; - let inode = get_inode(&info); - let nlinks = info.number_of_links(); - let mut found_hard_link = false; - #[allow(clippy::explicit_iter_loop)] - for (link, link_inode) in hard_links.iter() { - if *link_inode == inode { - // Consider the following files: - // - // * `src/f` - a regular file - // * `src/link` - a hard link to `src/f` - // * `dest/src/f` - a different regular file - // - // In this scenario, if we do `cp -a src/ dest/`, it is - // possible that the order of traversal causes `src/link` - // to get copied first (to `dest/src/link`). In that case, - // in order to make sure `dest/src/link` is a hard link to - // `dest/src/f` and `dest/src/f` has the contents of - // `src/f`, we delete the existing file to allow the hard - // linking. - if file_or_link_exists(dest) && file_or_link_exists(Path::new(link)) { - std::fs::remove_file(dest)?; - } - std::fs::hard_link(link, dest).unwrap(); - found_hard_link = true; - } - } - if !found_hard_link && nlinks > 1 { - hard_links.push((dest.to_str().unwrap().to_string(), inode)); - } - Ok(found_hard_link) -} - /// When handling errors, we don't always want to show them to the user. This function handles that. fn show_error_if_needed(error: &Error) { match error { @@ -1195,14 +1134,19 @@ pub fn copy(sources: &[PathBuf], target: &Path, options: &Options) -> CopyResult let target_type = TargetType::determine(sources, target); verify_target_type(target, &target_type)?; - let preserve_hard_links = options.preserve_hard_links(); - - let mut hard_links: Vec<(String, u64)> = vec![]; - let mut non_fatal_errors = false; let mut seen_sources = HashSet::with_capacity(sources.len()); let mut symlinked_files = HashSet::new(); + // to remember the copied files for further usage. + // the FileInformation implemented the Hash trait by using + // 1. inode number + // 2. device number + // the combination of a file's inode number and device number is unique throughout all the file systems. + // + // key is the source file's information and the value is the destination filepath. + let mut copied_files: HashMap = HashMap::with_capacity(sources.len()); + let progress_bar = if options.progress_bar { let pb = ProgressBar::new(disk_usage(sources, options.recursive)?) .with_style( @@ -1222,28 +1166,19 @@ pub fn copy(sources: &[PathBuf], target: &Path, options: &Options) -> CopyResult if seen_sources.contains(source) { // FIXME: compare sources by the actual file they point to, not their path. (e.g. dir/file == dir/../dir/file in most cases) show_warning!("source {} specified more than once", source.quote()); - } else { - let found_hard_link = if preserve_hard_links && !source.is_dir() { - let dest = construct_dest_path(source, target, &target_type, options)?; - preserve_hardlinks(&mut hard_links, source, &dest)? - } else { - false - }; - if !found_hard_link { - if let Err(error) = copy_source( - &progress_bar, - source, - target, - &target_type, - options, - &mut symlinked_files, - ) { - show_error_if_needed(&error); - non_fatal_errors = true; - } - } - seen_sources.insert(source); + } else if let Err(error) = copy_source( + &progress_bar, + source, + target, + &target_type, + options, + &mut symlinked_files, + &mut copied_files, + ) { + show_error_if_needed(&error); + non_fatal_errors = true; } + seen_sources.insert(source); } if let Some(pb) = progress_bar { @@ -1295,11 +1230,20 @@ fn copy_source( target_type: &TargetType, options: &Options, symlinked_files: &mut HashSet, + copied_files: &mut HashMap, ) -> CopyResult<()> { let source_path = Path::new(&source); if source_path.is_dir() { // Copy as directory - copy_directory(progress_bar, source, target, options, symlinked_files, true) + copy_directory( + progress_bar, + source, + target, + options, + symlinked_files, + copied_files, + true, + ) } else { // Copy as file let dest = construct_dest_path(source_path, target, target_type, options)?; @@ -1309,6 +1253,7 @@ fn copy_source( dest.as_path(), options, symlinked_files, + copied_files, true, ); if options.parents { @@ -1570,6 +1515,24 @@ fn handle_existing_dest( OverwriteMode::Clobber(ClobberMode::RemoveDestination) => { fs::remove_file(dest)?; } + OverwriteMode::Clobber(ClobberMode::Standard) => { + // Consider the following files: + // + // * `src/f` - a regular file + // * `src/link` - a hard link to `src/f` + // * `dest/src/f` - a different regular file + // + // In this scenario, if we do `cp -a src/ dest/`, it is + // possible that the order of traversal causes `src/link` + // to get copied first (to `dest/src/link`). In that case, + // in order to make sure `dest/src/link` is a hard link to + // `dest/src/f` and `dest/src/f` has the contents of + // `src/f`, we delete the existing file to allow the hard + // linking. + if options.preserve_hard_links() { + fs::remove_file(dest)?; + } + } _ => (), }; @@ -1643,6 +1606,7 @@ fn copy_file( dest: &Path, options: &Options, symlinked_files: &mut HashSet, + copied_files: &mut HashMap, source_in_command_line: bool, ) -> CopyResult<()> { if (options.update == UpdateMode::ReplaceIfOlder || options.update == UpdateMode::ReplaceNone) @@ -1686,6 +1650,19 @@ fn copy_file( handle_existing_dest(source, dest, options, source_in_command_line)?; } + if options.preserve_hard_links() { + // if we encounter a matching device/inode pair in the source tree + // we can arrange to create a hard link between the corresponding names + // in the destination tree. + if let Some(new_source) = copied_files.get( + &FileInformation::from_path(source, options.dereference(source_in_command_line)) + .context(format!("cannot stat {}", source.quote()))?, + ) { + std::fs::hard_link(new_source, dest)?; + return Ok(()); + }; + } + if options.verbose { if let Some(pb) = progress_bar { // Suspend (hide) the progress bar so the println won't overlap with the progress bar. @@ -1873,6 +1850,11 @@ fn copy_file( copy_attributes(source, dest, &options.attributes)?; + copied_files.insert( + FileInformation::from_path(source, options.dereference(source_in_command_line))?, + dest.to_path_buf(), + ); + if let Some(progress_bar) = progress_bar { progress_bar.inc(fs::metadata(source)?.len()); } diff --git a/tests/by-util/test_cp.rs b/tests/by-util/test_cp.rs index c77be3d4ed9..afac4cd3a6c 100644 --- a/tests/by-util/test_cp.rs +++ b/tests/by-util/test_cp.rs @@ -11,7 +11,7 @@ use std::fs::set_permissions; #[cfg(not(windows))] use std::os::unix::fs; -#[cfg(all(unix, not(target_os = "freebsd")))] +#[cfg(unix)] use std::os::unix::fs::MetadataExt; #[cfg(all(unix, not(target_os = "freebsd")))] use std::os::unix::fs::PermissionsExt; @@ -1353,6 +1353,203 @@ fn test_cp_preserve_xattr_fails_on_android() { .fails(); } +#[test] +// android platform will causing stderr = cp: Permission denied (os error 13) +#[cfg(not(target_os = "android"))] +fn test_cp_preserve_links_case_1() { + let (at, mut ucmd) = at_and_ucmd!(); + + at.touch("a"); + at.hard_link("a", "b"); + at.mkdir("c"); + + ucmd.arg("-d").arg("a").arg("b").arg("c").succeeds(); + + assert!(at.dir_exists("c")); + assert!(at.plus("c").join("a").exists()); + assert!(at.plus("c").join("b").exists()); + + #[cfg(unix)] + { + let metadata_a = std::fs::metadata(at.subdir.join("c").join("a")).unwrap(); + let metadata_b = std::fs::metadata(at.subdir.join("c").join("b")).unwrap(); + + assert_eq!(metadata_a.ino(), metadata_b.ino()); + } +} + +#[test] +// android platform will causing stderr = cp: Permission denied (os error 13) +#[cfg(not(target_os = "android"))] +fn test_cp_preserve_links_case_2() { + let (at, mut ucmd) = at_and_ucmd!(); + + at.touch("a"); + at.symlink_file("a", "b"); + at.mkdir("c"); + + ucmd.arg("--preserve=links") + .arg("-R") + .arg("-H") + .arg("a") + .arg("b") + .arg("c") + .succeeds(); + + assert!(at.dir_exists("c")); + assert!(at.plus("c").join("a").exists()); + assert!(at.plus("c").join("b").exists()); + + #[cfg(unix)] + { + let metadata_a = std::fs::metadata(at.subdir.join("c").join("a")).unwrap(); + let metadata_b = std::fs::metadata(at.subdir.join("c").join("b")).unwrap(); + + assert_eq!(metadata_a.ino(), metadata_b.ino()); + } +} + +#[test] +// android platform will causing stderr = cp: Permission denied (os error 13) +#[cfg(not(target_os = "android"))] +fn test_cp_preserve_links_case_3() { + let (at, mut ucmd) = at_and_ucmd!(); + + at.mkdir("d"); + at.touch("d/a"); + at.symlink_file("d/a", "d/b"); + + ucmd.arg("--preserve=links") + .arg("-R") + .arg("-L") + .arg("d") + .arg("c") + .succeeds(); + + assert!(at.dir_exists("c")); + assert!(at.plus("c").join("a").exists()); + assert!(at.plus("c").join("b").exists()); + + #[cfg(unix)] + { + let metadata_a = std::fs::metadata(at.subdir.join("c").join("a")).unwrap(); + let metadata_b = std::fs::metadata(at.subdir.join("c").join("b")).unwrap(); + + assert_eq!(metadata_a.ino(), metadata_b.ino()); + } +} + +#[test] +// android platform will causing stderr = cp: Permission denied (os error 13) +#[cfg(not(target_os = "android"))] +fn test_cp_preserve_links_case_4() { + let (at, mut ucmd) = at_and_ucmd!(); + + at.mkdir("d"); + at.touch("d/a"); + at.hard_link("d/a", "d/b"); + + ucmd.arg("--preserve=links") + .arg("-R") + .arg("-L") + .arg("d") + .arg("c") + .succeeds(); + + assert!(at.dir_exists("c")); + assert!(at.plus("c").join("a").exists()); + assert!(at.plus("c").join("b").exists()); + + #[cfg(unix)] + { + let metadata_a = std::fs::metadata(at.subdir.join("c").join("a")).unwrap(); + let metadata_b = std::fs::metadata(at.subdir.join("c").join("b")).unwrap(); + + assert_eq!(metadata_a.ino(), metadata_b.ino()); + } +} + +#[test] +// android platform will causing stderr = cp: Permission denied (os error 13) +#[cfg(not(target_os = "android"))] +fn test_cp_preserve_links_case_5() { + let (at, mut ucmd) = at_and_ucmd!(); + + at.mkdir("d"); + at.touch("d/a"); + at.hard_link("d/a", "d/b"); + + ucmd.arg("-dR") + .arg("--no-preserve=links") + .arg("d") + .arg("c") + .succeeds(); + + assert!(at.dir_exists("c")); + assert!(at.plus("c").join("a").exists()); + assert!(at.plus("c").join("b").exists()); + + #[cfg(unix)] + { + let metadata_a = std::fs::metadata(at.subdir.join("c").join("a")).unwrap(); + let metadata_b = std::fs::metadata(at.subdir.join("c").join("b")).unwrap(); + + assert_eq!(metadata_a.ino(), metadata_b.ino()); + } +} + +#[test] +// android platform will causing stderr = cp: Permission denied (os error 13) +#[cfg(not(target_os = "android"))] +fn test_cp_preserve_links_case_6() { + let (at, mut ucmd) = at_and_ucmd!(); + + at.touch("a"); + at.hard_link("a", "b"); + at.mkdir("c"); + + ucmd.arg("-d").arg("a").arg("b").arg("c").succeeds(); + + assert!(at.dir_exists("c")); + assert!(at.plus("c").join("a").exists()); + assert!(at.plus("c").join("b").exists()); + + #[cfg(unix)] + { + let metadata_a = std::fs::metadata(at.subdir.join("c").join("a")).unwrap(); + let metadata_b = std::fs::metadata(at.subdir.join("c").join("b")).unwrap(); + + assert_eq!(metadata_a.ino(), metadata_b.ino()); + } +} + +#[test] +// android platform will causing stderr = cp: Permission denied (os error 13) +#[cfg(not(target_os = "android"))] +fn test_cp_preserve_links_case_7() { + let (at, mut ucmd) = at_and_ucmd!(); + + at.mkdir("src"); + at.touch("src/f"); + at.hard_link("src/f", "src/g"); + + at.mkdir("dest"); + at.touch("dest/g"); + + ucmd.arg("-n") + .arg("--preserve=links") + .arg("src/f") + .arg("src/g") + .arg("dest") + .fails() + .stderr_contains("not replacing"); + (); + + assert!(at.dir_exists("dest")); + assert!(at.plus("dest").join("f").exists()); + assert!(at.plus("dest").join("g").exists()); +} + #[test] // For now, disable the test on Windows. Symlinks aren't well support on Windows. // It works on Unix for now and it works locally when run from a powershell From 1dcf3e63535d876b2c9e7e9680cf3fae28b5e8a0 Mon Sep 17 00:00:00 2001 From: Terts Diepraam Date: Sun, 24 Sep 2023 14:13:16 +0200 Subject: [PATCH 117/208] expose whoami function (for nushell!) --- src/uu/whoami/src/whoami.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/uu/whoami/src/whoami.rs b/src/uu/whoami/src/whoami.rs index cb8bcd9c735..738f7509a4d 100644 --- a/src/uu/whoami/src/whoami.rs +++ b/src/uu/whoami/src/whoami.rs @@ -5,6 +5,8 @@ /* last synced with: whoami (GNU coreutils) 8.21 */ +use std::ffi::OsString; + use clap::{crate_version, Command}; use uucore::display::println_verbatim; @@ -19,11 +21,16 @@ const USAGE: &str = help_usage!("whoami.md"); #[uucore::main] pub fn uumain(args: impl uucore::Args) -> UResult<()> { uu_app().try_get_matches_from(args)?; - let username = platform::get_username().map_err_context(|| "failed to get username".into())?; + let username = whoami()?; println_verbatim(username).map_err_context(|| "failed to print username".into())?; Ok(()) } +/// Get the current username +pub fn whoami() -> UResult { + platform::get_username().map_err_context(|| "failed to get username".into()) +} + pub fn uu_app() -> Command { Command::new(uucore::util_name()) .version(crate_version!()) From b90b59c0035f3296b3defc0fc1196660f1e3ade6 Mon Sep 17 00:00:00 2001 From: Leviticoh <67531886+Leviticoh@users.noreply.github.com> Date: Sun, 24 Sep 2023 14:44:44 +0200 Subject: [PATCH 118/208] uniq: added support for deprecated `-N` option (#4228) --- src/uu/uniq/src/uniq.rs | 80 +++++++++++++++++- tests/by-util/test_uniq.rs | 92 +++++++++++++++------ tests/fixtures/uniq/skip-2-fields.expected | 3 + tests/fixtures/uniq/skip-21-fields.expected | 3 + tests/fixtures/uniq/skip-fields.txt | 3 + 5 files changed, 150 insertions(+), 31 deletions(-) create mode 100644 tests/fixtures/uniq/skip-21-fields.expected diff --git a/src/uu/uniq/src/uniq.rs b/src/uu/uniq/src/uniq.rs index 3aac7b83467..72338bf9602 100644 --- a/src/uu/uniq/src/uniq.rs +++ b/src/uu/uniq/src/uniq.rs @@ -3,7 +3,7 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -use clap::{builder::ValueParser, crate_version, Arg, ArgAction, ArgMatches, Command}; +use clap::{builder::ValueParser, crate_version, Arg, ArgAction, ArgGroup, ArgMatches, Command}; use std::ffi::{OsStr, OsString}; use std::fs::File; use std::io::{self, stdin, stdout, BufRead, BufReader, BufWriter, Write}; @@ -23,6 +23,7 @@ pub mod options { pub static IGNORE_CASE: &str = "ignore-case"; pub static REPEATED: &str = "repeated"; pub static SKIP_FIELDS: &str = "skip-fields"; + pub static OBSOLETE_SKIP_FIELDS: &str = "obsolete_skip_field"; pub static SKIP_CHARS: &str = "skip-chars"; pub static UNIQUE: &str = "unique"; pub static ZERO_TERMINATED: &str = "zero-terminated"; @@ -53,6 +54,8 @@ struct Uniq { zero_terminated: bool, } +const OBSOLETE_SKIP_FIELDS_DIGITS: [&str; 10] = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]; + macro_rules! write_line_terminator { ($writer:expr, $line_terminator:expr) => { $writer @@ -236,6 +239,41 @@ fn opt_parsed(opt_name: &str, matches: &ArgMatches) -> UResult UResult> { + for opt_text in OBSOLETE_SKIP_FIELDS_DIGITS { + let argument = matches.get_one::(opt_text); + if matches.contains_id(opt_text) { + let mut full = opt_text.to_owned(); + if let Some(ar) = argument { + full.push_str(ar); + } + let value = full.parse::(); + + if let Ok(val) = value { + return Ok(Some(val)); + } else { + return Err(USimpleError { + code: 1, + message: format!("Invalid argument for skip-fields: {}", full), + } + .into()); + } + } + } + Ok(None) +} + #[uucore::main] pub fn uumain(args: impl uucore::Args) -> UResult<()> { let matches = uu_app().after_help(AFTER_HELP).try_get_matches_from(args)?; @@ -247,6 +285,10 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { .map(|mut fi| (fi.next(), fi.next())) .unwrap_or_default(); + let skip_fields_modern: Option = opt_parsed(options::SKIP_FIELDS, &matches)?; + + let skip_fields_old: Option = obsolete_skip_field(&matches)?; + let uniq = Uniq { repeats_only: matches.get_flag(options::REPEATED) || matches.contains_id(options::ALL_REPEATED), @@ -255,7 +297,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { || matches.contains_id(options::GROUP), delimiters: get_delimiter(&matches), show_counts: matches.get_flag(options::COUNT), - skip_fields: opt_parsed(options::SKIP_FIELDS, &matches)?, + skip_fields: skip_fields_modern.or(skip_fields_old), slice_start: opt_parsed(options::SKIP_CHARS, &matches)?, slice_stop: opt_parsed(options::CHECK_CHARS, &matches)?, ignore_case: matches.get_flag(options::IGNORE_CASE), @@ -276,7 +318,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { } pub fn uu_app() -> Command { - Command::new(uucore::util_name()) + let mut cmd = Command::new(uucore::util_name()) .version(crate_version!()) .about(ABOUT) .override_usage(format_usage(USAGE)) @@ -355,6 +397,7 @@ pub fn uu_app() -> Command { Arg::new(options::SKIP_FIELDS) .short('f') .long(options::SKIP_FIELDS) + .overrides_with_all(OBSOLETE_SKIP_FIELDS_DIGITS) .help("avoid comparing the first N fields") .value_name("N"), ) @@ -372,13 +415,42 @@ pub fn uu_app() -> Command { .help("end lines with 0 byte, not newline") .action(ArgAction::SetTrue), ) + .group( + // in GNU `uniq` every every digit of these arguments + // would be interpreted as a simple flag, + // these flags then are concatenated to get + // the number of fields to skip. + // in this way `uniq -1 -z -2` would be + // equal to `uniq -12 -q`, since this behavior + // is counterintuitive and it's hard to do in clap + // we handle it more like GNU `fold`: we have a flag + // for each possible initial digit, that takes the + // rest of the value as argument. + // we disallow explicitly multiple occurrences + // because then it would have a different behavior + // from GNU + ArgGroup::new(options::OBSOLETE_SKIP_FIELDS) + .multiple(false) + .args(OBSOLETE_SKIP_FIELDS_DIGITS) + ) .arg( Arg::new(ARG_FILES) .action(ArgAction::Append) .value_parser(ValueParser::os_string()) .num_args(0..=2) .value_hint(clap::ValueHint::FilePath), - ) + ); + + for i in OBSOLETE_SKIP_FIELDS_DIGITS { + cmd = cmd.arg( + Arg::new(i) + .short(i.chars().next().unwrap()) + .num_args(0..=1) + .hide(true), + ); + } + + cmd } fn get_delimiter(matches: &ArgMatches) -> Delimiters { diff --git a/tests/by-util/test_uniq.rs b/tests/by-util/test_uniq.rs index 97a829b9f7a..aa41de8274d 100644 --- a/tests/by-util/test_uniq.rs +++ b/tests/by-util/test_uniq.rs @@ -80,7 +80,7 @@ fn test_stdin_skip_and_check_2_chars() { } #[test] -fn test_stdin_skip_1_field() { +fn test_stdin_skip_2_fields() { new_ucmd!() .args(&["-f2"]) .pipe_in_fixture(SKIP_FIELDS) @@ -88,6 +88,42 @@ fn test_stdin_skip_1_field() { .stdout_is_fixture("skip-2-fields.expected"); } +#[test] +fn test_stdin_skip_2_fields_obsolete() { + new_ucmd!() + .args(&["-2"]) + .pipe_in_fixture(SKIP_FIELDS) + .run() + .stdout_is_fixture("skip-2-fields.expected"); +} + +#[test] +fn test_stdin_skip_21_fields() { + new_ucmd!() + .args(&["-f21"]) + .pipe_in_fixture(SKIP_FIELDS) + .run() + .stdout_is_fixture("skip-21-fields.expected"); +} + +#[test] +fn test_stdin_skip_21_fields_obsolete() { + new_ucmd!() + .args(&["-21"]) + .pipe_in_fixture(SKIP_FIELDS) + .run() + .stdout_is_fixture("skip-21-fields.expected"); +} + +#[test] +fn test_stdin_skip_invalid_fields_obsolete() { + new_ucmd!() + .args(&["-5deadbeef"]) + .run() + .failure() + .stderr_only("uniq: Invalid argument for skip-fields: 5deadbeef\n"); +} + #[test] fn test_stdin_all_repeated() { new_ucmd!() @@ -436,15 +472,15 @@ fn gnu_tests() { stderr: None, exit: None, }, - // // Obsolete syntax for "-f 1" - // TestCase { - // name: "obs30", - // args: &["-1"], - // input: "a a\nb a\n", - // stdout: Some("a a\n"), - // stderr: None, - // exit: None, - // }, + // Obsolete syntax for "-f 1" + TestCase { + name: "obs30", + args: &["-1"], + input: "a a\nb a\n", + stdout: Some("a a\n"), + stderr: None, + exit: None, + }, TestCase { name: "31", args: &["-f", "1"], @@ -518,23 +554,25 @@ fn gnu_tests() { stderr: None, exit: None, }, - // // Obsolete syntax for "-s 1" - // TestCase { - // name: "obs-plus44", - // args: &["+1", "--"], - // input: "aaa\naaa\n", - // stdout: Some("aaa\n"), - // stderr: None, - // exit: None, - // }, - // TestCase { - // name: "obs-plus45", - // args: &["+1", "--"], - // input: "baa\naaa\n", - // stdout: Some("baa\n"), - // stderr: None, - // exit: None, - // }, + /* + // Obsolete syntax for "-s 1" + TestCase { + name: "obs-plus44", + args: &["+1", "--"], + input: "aaa\naaa\n", + stdout: Some("aaa\n"), + stderr: None, + exit: None, + }, + TestCase { + name: "obs-plus45", + args: &["+1", "--"], + input: "baa\naaa\n", + stdout: Some("baa\n"), + stderr: None, + exit: None, + }, + */ TestCase { name: "50", args: &["-f", "1", "-s", "1"], diff --git a/tests/fixtures/uniq/skip-2-fields.expected b/tests/fixtures/uniq/skip-2-fields.expected index b971c2b2b39..c7f9bde9d28 100644 --- a/tests/fixtures/uniq/skip-2-fields.expected +++ b/tests/fixtures/uniq/skip-2-fields.expected @@ -1,2 +1,5 @@ aaa ⟪⟫ a aa a +a a a a a a a a a a a a a a a a a a a b a a a +a a a a a a a a a a a a a a a a a a a a b a a +a a a a a a a a a a a a a a a a a a a a a b a diff --git a/tests/fixtures/uniq/skip-21-fields.expected b/tests/fixtures/uniq/skip-21-fields.expected new file mode 100644 index 00000000000..1f5295afa5f --- /dev/null +++ b/tests/fixtures/uniq/skip-21-fields.expected @@ -0,0 +1,3 @@ + aaa ⟪⟫ a +a a a a a a a a a a a a a a a a a a a b a a a +a a a a a a a a a a a a a a a a a a a a a b a diff --git a/tests/fixtures/uniq/skip-fields.txt b/tests/fixtures/uniq/skip-fields.txt index 4ec2744c61d..0ca708a74ca 100644 --- a/tests/fixtures/uniq/skip-fields.txt +++ b/tests/fixtures/uniq/skip-fields.txt @@ -6,3 +6,6 @@ ZZZ aa a aa a a +a a a a a a a a a a a a a a a a a a a b a a a +a a a a a a a a a a a a a a a a a a a a b a a +a a a a a a a a a a a a a a a a a a a a a b a From ec7ced2518386be229d154fdf42675b8255a5f5d Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Mon, 20 Mar 2023 20:55:45 +0100 Subject: [PATCH 119/208] Fuzz the expr command --- .github/workflows/CICD.yml | 7 ++ fuzz/Cargo.toml | 7 ++ fuzz/fuzz_targets/fuzz_common.rs | 30 +++++ fuzz/fuzz_targets/fuzz_expr.rs | 184 +++++++++++++++++++++++++++++++ 4 files changed, 228 insertions(+) create mode 100644 fuzz/fuzz_targets/fuzz_common.rs create mode 100644 fuzz/fuzz_targets/fuzz_expr.rs diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index 577e235f9cf..a9f04e3cf1d 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -149,6 +149,13 @@ jobs: ## Run it cd fuzz cargo +nightly fuzz run fuzz_test -- -max_total_time=${{ env.RUN_FOR }} -detect_leaks=0 + - name: Run fuzz_expr for XX seconds + continue-on-error: true + shell: bash + run: | + ## Run it + cd fuzz + cargo +nightly fuzz run fuzz_expr -- -max_total_time=${{ env.RUN_FOR }} -detect_leaks=0 - name: Run fuzz_parse_glob for XX seconds shell: bash run: | diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 91a85b45a0f..549f9a6b762 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -15,6 +15,7 @@ rand = { version = "0.8", features = ["small_rng"] } uucore = { path = "../src/uucore/" } uu_date = { path = "../src/uu/date/" } uu_test = { path = "../src/uu/test/" } +uu_expr = { path = "../src/uu/expr/" } # Prevent this from interfering with workspaces @@ -27,6 +28,12 @@ path = "fuzz_targets/fuzz_date.rs" test = false doc = false +[[bin]] +name = "fuzz_expr" +path = "fuzz_targets/fuzz_expr.rs" +test = false +doc = false + [[bin]] name = "fuzz_test" path = "fuzz_targets/fuzz_test.rs" diff --git a/fuzz/fuzz_targets/fuzz_common.rs b/fuzz/fuzz_targets/fuzz_common.rs new file mode 100644 index 00000000000..fb1f498e90b --- /dev/null +++ b/fuzz/fuzz_targets/fuzz_common.rs @@ -0,0 +1,30 @@ +// This file is part of the uutils coreutils package. +// +// For the full copyright and license information, please view the LICENSE +// file that was distributed with this source code. + +use std::process::Command; +use std::sync::atomic::Ordering; +use std::sync::{atomic::AtomicBool, Once}; + +static CHECK_GNU: Once = Once::new(); +static IS_GNU: AtomicBool = AtomicBool::new(false); + +pub fn is_gnu_cmd(cmd_path: &str) -> Result<(), std::io::Error> { + CHECK_GNU.call_once(|| { + let version_output = Command::new(cmd_path).arg("--version").output().unwrap(); + + println!("version_output {:#?}", version_output); + + let version_str = String::from_utf8_lossy(&version_output.stdout).to_string(); + if version_str.contains("GNU coreutils") { + IS_GNU.store(true, Ordering::Relaxed); + } + }); + + if IS_GNU.load(Ordering::Relaxed) { + Ok(()) + } else { + panic!("Not the GNU implementation"); + } +} diff --git a/fuzz/fuzz_targets/fuzz_expr.rs b/fuzz/fuzz_targets/fuzz_expr.rs new file mode 100644 index 00000000000..e364342b8bc --- /dev/null +++ b/fuzz/fuzz_targets/fuzz_expr.rs @@ -0,0 +1,184 @@ +// This file is part of the uutils coreutils package. +// +// For the full copyright and license information, please view the LICENSE +// file that was distributed with this source code. +// spell-checker:ignore parens + +#![no_main] +use libfuzzer_sys::fuzz_target; +use uu_expr::uumain; + +use rand::seq::SliceRandom; +use rand::Rng; +use std::ffi::OsString; + +use libc::{dup, dup2, STDOUT_FILENO}; +use std::process::Command; +mod fuzz_common; +use crate::fuzz_common::is_gnu_cmd; + +static CMD_PATH: &str = "expr"; + +fn run_gnu_expr(args: &[OsString]) -> Result<(String, i32), std::io::Error> { + is_gnu_cmd(CMD_PATH)?; // Check if it's a GNU implementation + + let mut command = Command::new(CMD_PATH); + for arg in args { + command.arg(arg); + } + let output = command.output()?; + let exit_code = output.status.code().unwrap_or(-1); + if output.status.success() { + Ok(( + String::from_utf8_lossy(&output.stdout).to_string(), + exit_code, + )) + } else { + Err(std::io::Error::new( + std::io::ErrorKind::Other, + format!("GNU expr execution failed with exit code {}", exit_code), + )) + } +} + +fn generate_random_string(max_length: usize) -> String { + let mut rng = rand::thread_rng(); + let valid_utf8: Vec = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" + .chars() + .collect(); + let invalid_utf8 = [0xC3, 0x28]; // Invalid UTF-8 sequence + let mut result = String::new(); + + for _ in 0..rng.gen_range(1..=max_length) { + if rng.gen_bool(0.9) { + let ch = valid_utf8.choose(&mut rng).unwrap(); + result.push(*ch); + } else { + let ch = invalid_utf8.choose(&mut rng).unwrap(); + if let Some(c) = char::from_u32(*ch as u32) { + result.push(c); + } + } + } + + result +} + +fn generate_expr(max_depth: u32) -> String { + let mut rng = rand::thread_rng(); + let ops = ["+", "-", "*", "/", "%", "<", ">", "=", "&", "|"]; + + let mut expr = String::new(); + let mut depth = 0; + let mut last_was_operator = false; + + while depth <= max_depth { + if last_was_operator || depth == 0 { + // Add a number + expr.push_str(&rng.gen_range(1..=100).to_string()); + last_was_operator = false; + } else { + // 90% chance to add an operator followed by a number + if rng.gen_bool(0.9) { + let op = *ops.choose(&mut rng).unwrap(); + expr.push_str(&format!(" {} ", op)); + last_was_operator = true; + } + // 10% chance to add a random string (potentially invalid syntax) + else { + let random_str = generate_random_string(rng.gen_range(1..=10)); + expr.push_str(&random_str); + last_was_operator = false; + } + } + depth += 1; + } + + // Ensure the expression ends with a number if it ended with an operator + if last_was_operator { + expr.push_str(&rng.gen_range(1..=100).to_string()); + } + + expr +} + +fuzz_target!(|_data: &[u8]| { + let mut rng = rand::thread_rng(); + let expr = generate_expr(rng.gen_range(0..=20)); + let mut args = vec![OsString::from("expr")]; + args.extend(expr.split_whitespace().map(OsString::from)); + + // Save the original stdout file descriptor + let original_stdout_fd = unsafe { dup(STDOUT_FILENO) }; + + // Create a pipe to capture stdout + let mut pipe_fds = [-1; 2]; + unsafe { libc::pipe(pipe_fds.as_mut_ptr()) }; + let uumain_exit_code; + { + // Redirect stdout to the write end of the pipe + unsafe { dup2(pipe_fds[1], STDOUT_FILENO) }; + + // Run uumain with the provided arguments + uumain_exit_code = uumain(args.clone().into_iter()); + + // Restore original stdout + unsafe { dup2(original_stdout_fd, STDOUT_FILENO) }; + unsafe { libc::close(original_stdout_fd) }; + } + // Close the write end of the pipe + unsafe { libc::close(pipe_fds[1]) }; + + // Read captured output from the read end of the pipe + let mut captured_output = Vec::new(); + let mut read_buffer = [0; 1024]; + loop { + let bytes_read = unsafe { + libc::read( + pipe_fds[0], + read_buffer.as_mut_ptr() as *mut libc::c_void, + read_buffer.len(), + ) + }; + if bytes_read <= 0 { + break; + } + captured_output.extend_from_slice(&read_buffer[..bytes_read as usize]); + } + + // Close the read end of the pipe + unsafe { libc::close(pipe_fds[0]) }; + + // Convert captured output to a string + let rust_output = String::from_utf8_lossy(&captured_output) + .to_string() + .trim() + .to_owned(); + + // Run GNU expr with the provided arguments and compare the output + match run_gnu_expr(&args[1..]) { + Ok((gnu_output, gnu_exit_code)) => { + let gnu_output = gnu_output.trim().to_owned(); + if uumain_exit_code != gnu_exit_code { + println!("Expression: {}", expr); + println!("Rust code: {}", uumain_exit_code); + println!("GNU code: {}", gnu_exit_code); + panic!("Different error codes"); + } + if rust_output != gnu_output { + println!("Expression: {}", expr); + println!("Rust output: {}", rust_output); + println!("GNU output: {}", gnu_output); + panic!("Different output between Rust & GNU"); + } else { + println!( + "Outputs matched for expression: {} => Result: {}", + expr, rust_output + ); + } + } + Err(_) => { + println!("GNU expr execution failed for expression: {}", expr); + } + } +}); From 6aba3a4d67200d7ade6639d688a5a6c4510bd3f2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 24 Sep 2023 20:04:53 +0000 Subject: [PATCH 120/208] fix(deps): update rust crate wild to 2.2 --- Cargo.lock | 4 ++-- src/uucore/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3723c52da62..5dc160b4dd5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3385,9 +3385,9 @@ dependencies = [ [[package]] name = "wild" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b116685a6be0c52f5a103334cbff26db643826c7b3735fc0a3ba9871310a74" +checksum = "10d01931a94d5a115a53f95292f51d316856b68a035618eb831bbba593a30b67" dependencies = [ "glob", ] diff --git a/src/uucore/Cargo.toml b/src/uucore/Cargo.toml index 93bc618cee2..5e555da4da4 100644 --- a/src/uucore/Cargo.toml +++ b/src/uucore/Cargo.toml @@ -22,7 +22,7 @@ clap = { workspace = true } uucore_procs = { workspace = true } dns-lookup = { version = "2.0.3", optional = true } dunce = { version = "1.0.4", optional = true } -wild = "2.1" +wild = "2.2" glob = { workspace = true } # * optional itertools = { workspace = true, optional = true } From 3da58099103813c0143b3215a9e4c2f9fb83e41c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 25 Sep 2023 01:21:46 +0000 Subject: [PATCH 121/208] chore(deps): update rust crate memmap2 to 0.8 --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3723c52da62..c51f70851e4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1291,9 +1291,9 @@ checksum = "5486aed0026218e61b8a01d5fbd5a0a134649abb71a0e53b7bc088529dced86e" [[package]] name = "memmap2" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180d4b35be83d33392d1d1bfbd2ae1eca7ff5de1a94d3fc87faaa99a069e7cbd" +checksum = "43a5a03cefb0d953ec0be133036f14e109412fa594edc2f77227249db66cc3ed" dependencies = [ "libc", ] diff --git a/Cargo.toml b/Cargo.toml index 65b23faa2bb..15351798d9f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -292,7 +292,7 @@ lscolors = { version = "0.15.0", default-features = false, features = [ "nu-ansi-term", ] } memchr = "2" -memmap2 = "0.7" +memmap2 = "0.8" nix = { version = "0.27", default-features = false } nom = "7.1.3" notify = { version = "=6.0.1", features = ["macos_kqueue"] } From 45487d47b88d7aeb792869f48250f7c01f58507e Mon Sep 17 00:00:00 2001 From: Terts Diepraam Date: Mon, 25 Sep 2023 11:43:21 +0200 Subject: [PATCH 122/208] extract Base enum --- src/uu/echo/src/echo.rs | 32 ++++++++++++++++++++++++-------- tests/by-util/test_echo.rs | 4 ++-- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/src/uu/echo/src/echo.rs b/src/uu/echo/src/echo.rs index 565166842db..5ccc6a32a95 100644 --- a/src/uu/echo/src/echo.rs +++ b/src/uu/echo/src/echo.rs @@ -21,8 +21,24 @@ mod options { pub const DISABLE_BACKSLASH_ESCAPE: &str = "disable_backslash_escape"; } +#[repr(u8)] +#[derive(Clone, Copy)] +enum Base { + Oct = 8, + Hex = 16, +} + +impl Base { + fn max_digits(&self) -> u8 { + match self { + Self::Oct => 3, + Self::Hex => 2, + } + } +} + /// Parse the numeric part of the `\xHHH` and `\0NNN` escape sequences -fn parse_code(input: &mut Peekable, base: u8, max_digits: u32) -> Option { +fn parse_code(input: &mut Peekable, base: Base) -> Option { // All arithmetic on `ret` needs to be wrapping, because octal input can // take 3 digits, which is 9 bits, and therefore more than what fits in a // `u8`. GNU just seems to wrap these values. @@ -31,15 +47,15 @@ fn parse_code(input: &mut Peekable, base: u8, max_digits: u32) -> Option< // `u8::MAX` as unicode. let mut ret = input.peek().and_then(|c| c.to_digit(base as u32))? as u8; - // We can safely ifgnore the None case because we just peeked it. + // We can safely ignore the None case because we just peeked it. let _ = input.next(); - for _ in 1..max_digits { + for _ in 1..base.max_digits() { match input.peek().and_then(|c| c.to_digit(base as u32)) { - Some(n) => ret = ret.wrapping_mul(base).wrapping_add(n as u8), + Some(n) => ret = ret.wrapping_mul(base as u8).wrapping_add(n as u8), None => break, } - // We can safely ifgnore the None case because we just peeked it. + // We can safely ignore the None case because we just peeked it. let _ = input.next(); } @@ -58,7 +74,7 @@ fn print_escaped(input: &str, mut output: impl Write) -> io::Result { // Note that '0' is intentionally omitted because that // would be the \0NNN syntax. if let Some('1'..='8') = iter.peek() { - if let Some(parsed) = parse_code(&mut iter, 8, 3) { + if let Some(parsed) = parse_code(&mut iter, Base::Oct) { write!(output, "{parsed}")?; continue; } @@ -77,14 +93,14 @@ fn print_escaped(input: &str, mut output: impl Write) -> io::Result { 't' => '\t', 'v' => '\x0b', 'x' => { - if let Some(c) = parse_code(&mut iter, 16, 2) { + if let Some(c) = parse_code(&mut iter, Base::Hex) { c } else { write!(output, "\\")?; 'x' } } - '0' => parse_code(&mut iter, 8, 3).unwrap_or('\0'), + '0' => parse_code(&mut iter, Base::Oct).unwrap_or('\0'), c => { write!(output, "\\")?; c diff --git a/tests/by-util/test_echo.rs b/tests/by-util/test_echo.rs index 7de963973a2..dce5a4c9520 100644 --- a/tests/by-util/test_echo.rs +++ b/tests/by-util/test_echo.rs @@ -270,9 +270,9 @@ fn old_octal_syntax() { new_ucmd!() .arg("-e") - .arg("\\101foo") + .arg("\\101 foo") .succeeds() - .stdout_is("Afoo\n"); + .stdout_is("A foo\n"); new_ucmd!() .arg("-e") From 9fcf4cd8e5a0e3688fc2deefd36e65b927b45917 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sun, 24 Sep 2023 22:35:27 +0200 Subject: [PATCH 123/208] run-gnu-test.sh: show if we can't find the file Otherwise, the error can be cryptic Co-authored-by: Daniel Hofstetter --- util/run-gnu-test.sh | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/util/run-gnu-test.sh b/util/run-gnu-test.sh index 55ba8cefc28..fde066ae196 100755 --- a/util/run-gnu-test.sh +++ b/util/run-gnu-test.sh @@ -33,7 +33,17 @@ if test $# -ge 1; then # if set, run only the tests passed SPECIFIC_TESTS="" for t in "$@"; do - SPECIFIC_TESTS="$SPECIFIC_TESTS $t" + + # Construct the full path + full_path="$path_GNU/$t" + + # Check if the file exists with .sh, .pl extension or without any extension in the $path_GNU directory + if [ -f "$full_path" ] || [ -f "$full_path.sh" ] || [ -f "$full_path.pl" ]; then + SPECIFIC_TESTS="$SPECIFIC_TESTS $t" + else + echo "Error: Test file $full_path, $full_path.sh, or $full_path.pl does not exist!" + exit 1 + fi done # trim it SPECIFIC_TESTS=$(echo $SPECIFIC_TESTS | xargs) From 22f72544a8bf96e94204929aae5475624af749dc Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Tue, 26 Sep 2023 09:33:14 +0200 Subject: [PATCH 124/208] run-gnu-test.sh: accept "run-root" as first param --- util/run-gnu-test.sh | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/util/run-gnu-test.sh b/util/run-gnu-test.sh index fde066ae196..1abb476b72b 100755 --- a/util/run-gnu-test.sh +++ b/util/run-gnu-test.sh @@ -29,25 +29,27 @@ cd "${path_GNU}" && echo "[ pwd:'${PWD}' ]" export RUST_BACKTRACE=1 -if test $# -ge 1; then - # if set, run only the tests passed - SPECIFIC_TESTS="" - for t in "$@"; do - - # Construct the full path - full_path="$path_GNU/$t" - - # Check if the file exists with .sh, .pl extension or without any extension in the $path_GNU directory - if [ -f "$full_path" ] || [ -f "$full_path.sh" ] || [ -f "$full_path.pl" ]; then - SPECIFIC_TESTS="$SPECIFIC_TESTS $t" - else - echo "Error: Test file $full_path, $full_path.sh, or $full_path.pl does not exist!" - exit 1 - fi - done - # trim it - SPECIFIC_TESTS=$(echo $SPECIFIC_TESTS | xargs) - echo "Running specific tests: $SPECIFIC_TESTS" +if test "$1" != "run-root"; then + if test $# -ge 1; then + # if set, run only the tests passed + SPECIFIC_TESTS="" + for t in "$@"; do + + # Construct the full path + full_path="$path_GNU/$t" + + # Check if the file exists with .sh, .pl extension or without any extension in the $path_GNU directory + if [ -f "$full_path" ] || [ -f "$full_path.sh" ] || [ -f "$full_path.pl" ]; then + SPECIFIC_TESTS="$SPECIFIC_TESTS $t" + else + echo "Error: Test file $full_path, $full_path.sh, or $full_path.pl does not exist!" + exit 1 + fi + done + # trim it + SPECIFIC_TESTS=$(echo $SPECIFIC_TESTS | xargs) + echo "Running specific tests: $SPECIFIC_TESTS" + fi fi # * timeout used to kill occasionally errant/"stuck" processes (note: 'release' testing takes ~1 hour; 'debug' testing takes ~2.5 hours) From 99120d32c13f01b6e146b1f5546c5e2520d73a16 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sun, 24 Sep 2023 23:13:34 +0200 Subject: [PATCH 125/208] cut: fail when the input == usize::MAX --- src/uucore/src/lib/features/ranges.rs | 2 ++ tests/by-util/test_cut.rs | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/src/uucore/src/lib/features/ranges.rs b/src/uucore/src/lib/features/ranges.rs index 29f40218363..7c09f922d93 100644 --- a/src/uucore/src/lib/features/ranges.rs +++ b/src/uucore/src/lib/features/ranges.rs @@ -38,6 +38,8 @@ impl FromStr for Range { fn parse(s: &str) -> Result { match s.parse::() { Ok(0) => Err("fields and positions are numbered from 1"), + // GNU fails when we are at the limit. Match their behavior + Ok(n) if n == usize::MAX => Err("byte/character offset is too large"), Ok(n) => Ok(n), Err(_) => Err("failed to parse range"), } diff --git a/tests/by-util/test_cut.rs b/tests/by-util/test_cut.rs index 7f86c803e8f..184e413a867 100644 --- a/tests/by-util/test_cut.rs +++ b/tests/by-util/test_cut.rs @@ -117,6 +117,14 @@ fn test_whitespace_with_char() { .code_is(1); } +#[test] +fn test_too_large() { + new_ucmd!() + .args(&["-b1-18446744073709551615", "/dev/null"]) + .fails() + .code_is(1); +} + #[test] fn test_specify_delimiter() { for param in ["-d", "--delimiter", "--del"] { From 265c25871385eaf33c1945609ae5cf6521a790f3 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sun, 24 Sep 2023 23:23:27 +0200 Subject: [PATCH 126/208] ranges: add unit tests to verify the parsing To test them: $ cargo test -p uucore --features ranges --- src/uucore/src/lib/features/ranges.rs | 30 +++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/uucore/src/lib/features/ranges.rs b/src/uucore/src/lib/features/ranges.rs index 7c09f922d93..19ba23fb277 100644 --- a/src/uucore/src/lib/features/ranges.rs +++ b/src/uucore/src/lib/features/ranges.rs @@ -161,6 +161,7 @@ pub fn contain(ranges: &[Range], n: usize) -> bool { #[cfg(test)] mod test { use super::{complement, Range}; + use std::str::FromStr; fn m(a: Vec, b: &[Range]) { assert_eq!(Range::merge(a), b); @@ -231,4 +232,33 @@ mod test { // With start and end assert_eq!(complement(&[r(1, 4), r(6, usize::MAX - 1)]), vec![r(5, 5)]); } + + #[test] + fn test_from_str() { + assert_eq!(Range::from_str("5"), Ok(Range { low: 5, high: 5 })); + assert_eq!(Range::from_str("3-5"), Ok(Range { low: 3, high: 5 })); + assert_eq!( + Range::from_str("5-3"), + Err("high end of range less than low end") + ); + assert_eq!(Range::from_str("-"), Err("invalid range with no endpoint")); + assert_eq!( + Range::from_str("3-"), + Ok(Range { + low: 3, + high: usize::MAX - 1 + }) + ); + assert_eq!(Range::from_str("-5"), Ok(Range { low: 1, high: 5 })); + assert_eq!( + Range::from_str("0"), + Err("fields and positions are numbered from 1") + ); + + let max_value = format!("{}", usize::MAX); + assert_eq!( + Range::from_str(&max_value), + Err("byte/character offset is too large") + ); + } } From 2a97eab685786f7a81dea294cfbac199d4848957 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 26 Sep 2023 07:45:23 +0000 Subject: [PATCH 127/208] chore(deps): update rust crate exacl to 0.11.0 --- Cargo.lock | 16 ++++++++-------- Cargo.toml | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 53e3232fc74..24ef766d019 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -149,9 +149,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.3.3" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" +checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" [[package]] name = "blake2b_simd" @@ -640,7 +640,7 @@ version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" dependencies = [ - "bitflags 2.3.3", + "bitflags 2.4.0", "crossterm_winapi", "libc", "mio", @@ -805,11 +805,11 @@ dependencies = [ [[package]] name = "exacl" -version = "0.10.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfeb22a59deb24c3262c43ffcafd1eb807180f371f9fcc99098d181b5d639be" +checksum = "c695152c1c2777163ea93fff517edc6dd1f8fc226c14b0d60cdcde0beb316d9f" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.0", "log", "scopeguard", "uuid", @@ -1340,7 +1340,7 @@ version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" dependencies = [ - "bitflags 2.3.3", + "bitflags 2.4.0", "cfg-if", "libc", ] @@ -1876,7 +1876,7 @@ version = "0.38.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19ed4fa021d81c8392ce04db050a3da9a60299050b7ae1cf482d862b54a7218f" dependencies = [ - "bitflags 2.3.3", + "bitflags 2.4.0", "errno", "libc", "linux-raw-sys 0.4.5", diff --git a/Cargo.toml b/Cargo.toml index 15351798d9f..2ee4444b184 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -275,7 +275,7 @@ compare = "0.1.0" coz = { version = "0.1.3" } crossterm = ">=0.27.0" ctrlc = { version = "3.4", features = ["termination"] } -exacl = "0.10.0" +exacl = "0.11.0" file_diff = "1.0.0" filetime = "0.2" fnv = "1.0.7" From ff500d7d6f51fa64e6ba2b526c4fd1b5748f9850 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Tue, 26 Sep 2023 16:12:59 +0200 Subject: [PATCH 128/208] expr: interpret numbers != 0 as true for | and & --- src/uu/expr/src/syntax_tree.rs | 6 +++--- tests/by-util/test_expr.rs | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/uu/expr/src/syntax_tree.rs b/src/uu/expr/src/syntax_tree.rs index 0e0795bd4fb..b3cd329ba6e 100644 --- a/src/uu/expr/src/syntax_tree.rs +++ b/src/uu/expr/src/syntax_tree.rs @@ -8,10 +8,10 @@ //! * `` //! -// spell-checker:ignore (ToDO) binop binops ints paren prec multibytes +// spell-checker:ignore (ToDO) ints paren prec multibytes use num_bigint::BigInt; -use num_traits::{One, Zero}; +use num_traits::Zero; use onig::{Regex, RegexOptions, Syntax}; use crate::tokens::Token; @@ -515,7 +515,7 @@ fn value_as_bool(s: &str) -> bool { return false; } match s.parse::() { - Ok(n) => n.is_one(), + Ok(n) => n != Zero::zero(), Err(_) => true, } } diff --git a/tests/by-util/test_expr.rs b/tests/by-util/test_expr.rs index 665f89615e4..ea5a964d999 100644 --- a/tests/by-util/test_expr.rs +++ b/tests/by-util/test_expr.rs @@ -113,6 +113,16 @@ fn test_or() { .args(&["foo", "|", "bar"]) .succeeds() .stdout_only("foo\n"); + + new_ucmd!() + .args(&["14", "|", "1"]) + .succeeds() + .stdout_only("14\n"); + + new_ucmd!() + .args(&["-14", "|", "1"]) + .succeeds() + .stdout_only("-14\n"); } #[test] @@ -123,6 +133,13 @@ fn test_and() { .stdout_only("foo\n"); new_ucmd!().args(&["", "&", "1"]).run().stdout_is("0\n"); + + new_ucmd!().args(&["14", "&", "1"]).run().stdout_is("14\n"); + + new_ucmd!() + .args(&["-14", "&", "1"]) + .run() + .stdout_is("-14\n"); } #[test] From 6c7a20fc1868bf636b2c331bc1fcca39983d810b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 26 Sep 2023 16:57:54 +0000 Subject: [PATCH 129/208] chore(deps): update rust crate sha2 to 0.10.8 --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 24ef766d019..c007551987f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1955,9 +1955,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.7" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if", "cpufeatures", diff --git a/Cargo.toml b/Cargo.toml index 2ee4444b184..a40d066d4ef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -336,7 +336,7 @@ zip = { version = "0.6.6", default_features = false, features = ["deflate"] } hex = "0.4.3" md-5 = "0.10.6" sha1 = "0.10.6" -sha2 = "0.10.7" +sha2 = "0.10.8" sha3 = "0.10.8" blake2b_simd = "1.0.2" blake3 = "1.5.0" From 2789885648053e1ff1fe9c745dddefe0a5f2840b Mon Sep 17 00:00:00 2001 From: tommady Date: Wed, 27 Sep 2023 19:16:10 +0800 Subject: [PATCH 130/208] cp: fix cp -dR --no-preserve=links d c should have different inodes (#5320) * fix issue 5308 --- src/uu/cp/src/cp.rs | 38 ++++++++++++++++++++++++-------------- tests/by-util/test_cp.rs | 2 +- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index 05261c22fe2..b6270719cc9 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -931,23 +931,33 @@ impl Options { }; // Parse attributes to preserve - let attributes = if let Some(attribute_strs) = matches.get_many::(options::PRESERVE) - { - if attribute_strs.len() == 0 { + let mut attributes = + if let Some(attribute_strs) = matches.get_many::(options::PRESERVE) { + if attribute_strs.len() == 0 { + Attributes::DEFAULT + } else { + Attributes::parse_iter(attribute_strs)? + } + } else if matches.get_flag(options::ARCHIVE) { + // --archive is used. Same as --preserve=all + Attributes::ALL + } else if matches.get_flag(options::NO_DEREFERENCE_PRESERVE_LINKS) { + Attributes::LINKS + } else if matches.get_flag(options::PRESERVE_DEFAULT_ATTRIBUTES) { Attributes::DEFAULT } else { - Attributes::parse_iter(attribute_strs)? + Attributes::NONE + }; + + // handling no-preserve options and adjusting the attributes + if let Some(attribute_strs) = matches.get_many::(options::NO_PRESERVE) { + if attribute_strs.len() > 0 { + let no_preserve_attributes = Attributes::parse_iter(attribute_strs)?; + if matches!(no_preserve_attributes.links, Preserve::Yes { .. }) { + attributes.links = Preserve::No; + } } - } else if matches.get_flag(options::ARCHIVE) { - // --archive is used. Same as --preserve=all - Attributes::ALL - } else if matches.get_flag(options::NO_DEREFERENCE_PRESERVE_LINKS) { - Attributes::LINKS - } else if matches.get_flag(options::PRESERVE_DEFAULT_ATTRIBUTES) { - Attributes::DEFAULT - } else { - Attributes::NONE - }; + } #[cfg(not(feature = "feat_selinux"))] if let Preserve::Yes { required } = attributes.context { diff --git a/tests/by-util/test_cp.rs b/tests/by-util/test_cp.rs index afac4cd3a6c..1ce74572d5d 100644 --- a/tests/by-util/test_cp.rs +++ b/tests/by-util/test_cp.rs @@ -1494,7 +1494,7 @@ fn test_cp_preserve_links_case_5() { let metadata_a = std::fs::metadata(at.subdir.join("c").join("a")).unwrap(); let metadata_b = std::fs::metadata(at.subdir.join("c").join("b")).unwrap(); - assert_eq!(metadata_a.ino(), metadata_b.ino()); + assert_ne!(metadata_a.ino(), metadata_b.ino()); } } From aea986948df6375a90b3963e6c930f3a1adfce45 Mon Sep 17 00:00:00 2001 From: KAA the Wise Date: Wed, 27 Sep 2023 23:05:29 +0300 Subject: [PATCH 131/208] mkdir: make `mkdir` public and document it - Made the `mkdir` function public. Added documentation, describing the behavior and parameters - Moved the GNU dot-stripping behavior from `exec` to `mkdir`. --- src/uu/mkdir/src/mkdir.rs | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/src/uu/mkdir/src/mkdir.rs b/src/uu/mkdir/src/mkdir.rs index 2044855e45e..354335d8c30 100644 --- a/src/uu/mkdir/src/mkdir.rs +++ b/src/uu/mkdir/src/mkdir.rs @@ -141,21 +141,34 @@ pub fn uu_app() -> Command { */ fn exec(dirs: ValuesRef, recursive: bool, mode: u32, verbose: bool) -> UResult<()> { for dir in dirs { - // Special case to match GNU's behavior: - // mkdir -p foo/. should work and just create foo/ - // std::fs::create_dir("foo/."); fails in pure Rust - let path = if recursive { - dir_strip_dot_for_creation(&PathBuf::from(dir)) - } else { - // Normal case - PathBuf::from(dir) - }; - show_if_err!(mkdir(path.as_path(), recursive, mode, verbose)); + let path_buf = PathBuf::from(dir); + let path = path_buf.as_path(); + + show_if_err!(mkdir(path, recursive, mode, verbose)); } Ok(()) } -fn mkdir(path: &Path, recursive: bool, mode: u32, verbose: bool) -> UResult<()> { +/// Create directory at a given `path`. +/// +/// ## Options +/// +/// * `recursive` --- create parent directories for the `path`, if they do not +/// exist. +/// * `mode` --- file mode for the directories (not implemented on windows). +/// * `verbose` --- print a message for each printed directory. +/// +/// ## Trailing dot +/// +/// To match the GNU behavior, a path with the last directory being a single dot +/// (like `some/path/to/.`) is created (with the dot stripped). +pub fn mkdir(path: &Path, recursive: bool, mode: u32, verbose: bool) -> UResult<()> { + // Special case to match GNU's behavior: + // mkdir -p foo/. should work and just create foo/ + // std::fs::create_dir("foo/."); fails in pure Rust + let path_buf = dir_strip_dot_for_creation(path); + let path = path_buf.as_path(); + create_dir(path, recursive, verbose, false)?; chmod(path, mode) } From 9c0b9cae5610a81c2c9d152edb7fcca1c77e83dd Mon Sep 17 00:00:00 2001 From: John Shin Date: Wed, 27 Sep 2023 14:55:08 -0700 Subject: [PATCH 132/208] seq: simplyfy nan and inf parsing --- src/uu/seq/src/numberparse.rs | 29 +++++++---------------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/src/uu/seq/src/numberparse.rs b/src/uu/seq/src/numberparse.rs index 156f80fb91f..07d853d586c 100644 --- a/src/uu/seq/src/numberparse.rs +++ b/src/uu/seq/src/numberparse.rs @@ -69,28 +69,13 @@ fn parse_no_decimal_no_exponent(s: &str) -> Result { // Possibly "NaN" or "inf". - if let Ok(num) = f32::from_str(s) { - // pattern matching on floating point literal is not encouraged 'https://github.com/rust-lang/rust/issues/41620' - if num == f32::INFINITY { - Ok(PreciseNumber::new( - Number::Float(ExtendedBigDecimal::Infinity), - 0, - 0, - )) - } else if num == f32::NEG_INFINITY { - Ok(PreciseNumber::new( - Number::Float(ExtendedBigDecimal::MinusInfinity), - 0, - 0, - )) - } else if num.is_nan() { - Err(ParseNumberError::Nan) - } else { - Err(ParseNumberError::Float) - } - } else { - Err(ParseNumberError::Float) - } + let float_val = match s.to_ascii_lowercase().as_str() { + "inf" | "infinity" => ExtendedBigDecimal::Infinity, + "-inf" | "-infinity" => ExtendedBigDecimal::MinusInfinity, + "nan" | "-nan" => return Err(ParseNumberError::Nan), + _ => return Err(ParseNumberError::Float), + }; + Ok(PreciseNumber::new(Number::Float(float_val), 0, 0)) } } } From f0f64bd3b728c905944aaf3534f618d85ded30c6 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Thu, 28 Sep 2023 08:42:30 +0200 Subject: [PATCH 133/208] fuzz: move the common duplicated code into a function --- fuzz/fuzz_targets/fuzz_common.rs | 47 ++++++++++++++++++++++++ fuzz/fuzz_targets/fuzz_expr.rs | 49 ++----------------------- fuzz/fuzz_targets/fuzz_test.rs | 61 ++++++-------------------------- 3 files changed, 59 insertions(+), 98 deletions(-) diff --git a/fuzz/fuzz_targets/fuzz_common.rs b/fuzz/fuzz_targets/fuzz_common.rs index fb1f498e90b..a33d603f130 100644 --- a/fuzz/fuzz_targets/fuzz_common.rs +++ b/fuzz/fuzz_targets/fuzz_common.rs @@ -3,6 +3,8 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. +use libc::{dup, dup2, STDOUT_FILENO}; +use std::ffi::OsString; use std::process::Command; use std::sync::atomic::Ordering; use std::sync::{atomic::AtomicBool, Once}; @@ -28,3 +30,48 @@ pub fn is_gnu_cmd(cmd_path: &str) -> Result<(), std::io::Error> { panic!("Not the GNU implementation"); } } + +pub fn generate_and_run_uumain(args: &mut Vec, uumain_function: F) -> (String, i32) +where + F: FnOnce(std::vec::IntoIter) -> i32, +{ + let uumain_exit_status; + + let original_stdout_fd = unsafe { dup(STDOUT_FILENO) }; + println!("Running test {:?}", &args[1..]); + let mut pipe_fds = [-1; 2]; + unsafe { libc::pipe(pipe_fds.as_mut_ptr()) }; + + { + unsafe { dup2(pipe_fds[1], STDOUT_FILENO) }; + uumain_exit_status = uumain_function(args.clone().into_iter()); + unsafe { dup2(original_stdout_fd, STDOUT_FILENO) }; + unsafe { libc::close(original_stdout_fd) }; + } + unsafe { libc::close(pipe_fds[1]) }; + + let mut captured_output = Vec::new(); + let mut read_buffer = [0; 1024]; + loop { + let bytes_read = unsafe { + libc::read( + pipe_fds[0], + read_buffer.as_mut_ptr() as *mut libc::c_void, + read_buffer.len(), + ) + }; + if bytes_read <= 0 { + break; + } + captured_output.extend_from_slice(&read_buffer[..bytes_read as usize]); + } + + unsafe { libc::close(pipe_fds[0]) }; + + let my_output = String::from_utf8_lossy(&captured_output) + .to_string() + .trim() + .to_owned(); + + (my_output, uumain_exit_status) +} diff --git a/fuzz/fuzz_targets/fuzz_expr.rs b/fuzz/fuzz_targets/fuzz_expr.rs index e364342b8bc..28fded99e07 100644 --- a/fuzz/fuzz_targets/fuzz_expr.rs +++ b/fuzz/fuzz_targets/fuzz_expr.rs @@ -12,9 +12,9 @@ use rand::seq::SliceRandom; use rand::Rng; use std::ffi::OsString; -use libc::{dup, dup2, STDOUT_FILENO}; use std::process::Command; mod fuzz_common; +use crate::fuzz_common::generate_and_run_uumain; use crate::fuzz_common::is_gnu_cmd; static CMD_PATH: &str = "expr"; @@ -108,52 +108,7 @@ fuzz_target!(|_data: &[u8]| { let mut args = vec![OsString::from("expr")]; args.extend(expr.split_whitespace().map(OsString::from)); - // Save the original stdout file descriptor - let original_stdout_fd = unsafe { dup(STDOUT_FILENO) }; - - // Create a pipe to capture stdout - let mut pipe_fds = [-1; 2]; - unsafe { libc::pipe(pipe_fds.as_mut_ptr()) }; - let uumain_exit_code; - { - // Redirect stdout to the write end of the pipe - unsafe { dup2(pipe_fds[1], STDOUT_FILENO) }; - - // Run uumain with the provided arguments - uumain_exit_code = uumain(args.clone().into_iter()); - - // Restore original stdout - unsafe { dup2(original_stdout_fd, STDOUT_FILENO) }; - unsafe { libc::close(original_stdout_fd) }; - } - // Close the write end of the pipe - unsafe { libc::close(pipe_fds[1]) }; - - // Read captured output from the read end of the pipe - let mut captured_output = Vec::new(); - let mut read_buffer = [0; 1024]; - loop { - let bytes_read = unsafe { - libc::read( - pipe_fds[0], - read_buffer.as_mut_ptr() as *mut libc::c_void, - read_buffer.len(), - ) - }; - if bytes_read <= 0 { - break; - } - captured_output.extend_from_slice(&read_buffer[..bytes_read as usize]); - } - - // Close the read end of the pipe - unsafe { libc::close(pipe_fds[0]) }; - - // Convert captured output to a string - let rust_output = String::from_utf8_lossy(&captured_output) - .to_string() - .trim() - .to_owned(); + let (rust_output, uumain_exit_code) = generate_and_run_uumain(&mut args, uumain); // Run GNU expr with the provided arguments and compare the output match run_gnu_expr(&args[1..]) { diff --git a/fuzz/fuzz_targets/fuzz_test.rs b/fuzz/fuzz_targets/fuzz_test.rs index 537e21abda5..535696b45dc 100644 --- a/fuzz/fuzz_targets/fuzz_test.rs +++ b/fuzz/fuzz_targets/fuzz_test.rs @@ -12,9 +12,11 @@ use rand::seq::SliceRandom; use rand::Rng; use std::ffi::OsString; -use libc::{dup, dup2, STDOUT_FILENO}; use std::process::Command; +mod fuzz_common; +use crate::fuzz_common::generate_and_run_uumain; + #[derive(PartialEq, Debug, Clone)] enum ArgType { STRING, @@ -26,8 +28,11 @@ enum ArgType { // Add any other types as needed } +static CMD_PATH: &str = "/usr/bin/test"; + fn run_gnu_test(args: &[OsString]) -> Result<(String, i32), std::io::Error> { - let mut command = Command::new("test"); + let mut command = Command::new(CMD_PATH); + for arg in args { command.arg(arg); } @@ -210,58 +215,12 @@ fuzz_target!(|_data: &[u8]| { let mut rng = rand::thread_rng(); let max_args = rng.gen_range(1..=6); let mut args = vec![OsString::from("test")]; - let uumain_exit_status; for _ in 0..max_args { args.push(OsString::from(generate_test_arg())); } - // Save the original stdout file descriptor - let original_stdout_fd = unsafe { dup(STDOUT_FILENO) }; - println!("Running test {:?}", &args[1..]); - // Create a pipe to capture stdout - let mut pipe_fds = [-1; 2]; - unsafe { libc::pipe(pipe_fds.as_mut_ptr()) }; - - { - // Redirect stdout to the write end of the pipe - unsafe { dup2(pipe_fds[1], STDOUT_FILENO) }; - - // Run uumain with the provided arguments - uumain_exit_status = uumain(args.clone().into_iter()); - - // Restore original stdout - unsafe { dup2(original_stdout_fd, STDOUT_FILENO) }; - unsafe { libc::close(original_stdout_fd) }; - } - // Close the write end of the pipe - unsafe { libc::close(pipe_fds[1]) }; - - // Read captured output from the read end of the pipe - let mut captured_output = Vec::new(); - let mut read_buffer = [0; 1024]; - loop { - let bytes_read = unsafe { - libc::read( - pipe_fds[0], - read_buffer.as_mut_ptr() as *mut libc::c_void, - read_buffer.len(), - ) - }; - if bytes_read <= 0 { - break; - } - captured_output.extend_from_slice(&read_buffer[..bytes_read as usize]); - } - - // Close the read end of the pipe - unsafe { libc::close(pipe_fds[0]) }; - - // Convert captured output to a string - let my_output = String::from_utf8_lossy(&captured_output) - .to_string() - .trim() - .to_owned(); + let (rust_output, uumain_exit_status) = generate_and_run_uumain(&mut args, uumain); // Run GNU test with the provided arguments and compare the output match run_gnu_test(&args[1..]) { @@ -269,10 +228,10 @@ fuzz_target!(|_data: &[u8]| { let gnu_output = gnu_output.trim().to_owned(); println!("gnu_exit_status {}", gnu_exit_status); println!("uumain_exit_status {}", uumain_exit_status); - if my_output != gnu_output || uumain_exit_status != gnu_exit_status { + if rust_output != gnu_output || uumain_exit_status != gnu_exit_status { println!("Discrepancy detected!"); println!("Test: {:?}", &args[1..]); - println!("My output: {}", my_output); + println!("My output: {}", rust_output); println!("GNU output: {}", gnu_output); println!("My exit status: {}", uumain_exit_status); println!("GNU exit status: {}", gnu_exit_status); From 51eb20a15d768b0b24b60a78b98c27602ed5cc50 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Thu, 28 Sep 2023 10:50:56 +0200 Subject: [PATCH 134/208] fmt: use clap's value parser for goal & width --- src/uu/fmt/src/fmt.rs | 30 ++++++++---------------------- tests/by-util/test_fmt.rs | 22 ++++++++++++++++++++++ 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/src/uu/fmt/src/fmt.rs b/src/uu/fmt/src/fmt.rs index c5eac707303..c30d923b76b 100644 --- a/src/uu/fmt/src/fmt.rs +++ b/src/uu/fmt/src/fmt.rs @@ -131,16 +131,8 @@ fn parse_arguments(args: impl uucore::Args) -> UResult<(Vec, FmtOptions) fmt_opts.use_anti_prefix = true; }; - if let Some(s) = matches.get_one::(OPT_WIDTH) { - fmt_opts.width = match s.parse::() { - Ok(t) => t, - Err(e) => { - return Err(USimpleError::new( - 1, - format!("Invalid WIDTH specification: {}: {}", s.quote(), e), - )); - } - }; + if let Some(width) = matches.get_one::(OPT_WIDTH) { + fmt_opts.width = *width; if fmt_opts.width > MAX_WIDTH { return Err(USimpleError::new( 1, @@ -156,16 +148,8 @@ fn parse_arguments(args: impl uucore::Args) -> UResult<(Vec, FmtOptions) ); }; - if let Some(s) = matches.get_one::(OPT_GOAL) { - fmt_opts.goal = match s.parse::() { - Ok(t) => t, - Err(e) => { - return Err(USimpleError::new( - 1, - format!("Invalid GOAL specification: {}: {}", s.quote(), e), - )); - } - }; + if let Some(goal) = matches.get_one::(OPT_GOAL) { + fmt_opts.goal = *goal; if !matches.contains_id(OPT_WIDTH) { fmt_opts.width = cmp::max( fmt_opts.goal * 100 / DEFAULT_GOAL_TO_WIDTH_RATIO, @@ -372,14 +356,16 @@ pub fn uu_app() -> Command { .short('w') .long("width") .help("Fill output lines up to a maximum of WIDTH columns, default 75.") - .value_name("WIDTH"), + .value_name("WIDTH") + .value_parser(clap::value_parser!(usize)), ) .arg( Arg::new(OPT_GOAL) .short('g') .long("goal") .help("Goal width, default of 93% of WIDTH. Must be less than WIDTH.") - .value_name("GOAL"), + .value_name("GOAL") + .value_parser(clap::value_parser!(usize)), ) .arg( Arg::new(OPT_QUICK) diff --git a/tests/by-util/test_fmt.rs b/tests/by-util/test_fmt.rs index 7d23cbd52f1..4fd05908019 100644 --- a/tests/by-util/test_fmt.rs +++ b/tests/by-util/test_fmt.rs @@ -48,6 +48,17 @@ fn test_fmt_width_too_big() { } } +#[test] +fn test_fmt_invalid_width() { + for param in ["-w", "--width"] { + new_ucmd!() + .args(&["one-word-per-line.txt", param, "invalid"]) + .fails() + .code_is(1) + .stderr_contains("invalid value 'invalid'"); + } +} + #[ignore] #[test] fn test_fmt_goal() { @@ -70,6 +81,17 @@ fn test_fmt_goal_too_big() { } } +#[test] +fn test_fmt_invalid_goal() { + for param in ["-g", "--goal"] { + new_ucmd!() + .args(&["one-word-per-line.txt", param, "invalid"]) + .fails() + .code_is(1) + .stderr_contains("invalid value 'invalid'"); + } +} + #[test] fn test_fmt_set_goal_not_contain_width() { for param in ["-g", "--goal"] { From a17ede9ef02ca864dc7fcd5bda681e990339b674 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Thu, 28 Sep 2023 15:36:06 +0200 Subject: [PATCH 135/208] Remove the full path to test --- fuzz/fuzz_targets/fuzz_test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzz/fuzz_targets/fuzz_test.rs b/fuzz/fuzz_targets/fuzz_test.rs index 535696b45dc..dbd2db54ab0 100644 --- a/fuzz/fuzz_targets/fuzz_test.rs +++ b/fuzz/fuzz_targets/fuzz_test.rs @@ -28,7 +28,7 @@ enum ArgType { // Add any other types as needed } -static CMD_PATH: &str = "/usr/bin/test"; +static CMD_PATH: &str = "test"; fn run_gnu_test(args: &[OsString]) -> Result<(String, i32), std::io::Error> { let mut command = Command::new(CMD_PATH); From e6f9e358d4f3365ea62ef86ddbe5b5b3f71a3dbf Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Thu, 28 Sep 2023 21:48:34 +0200 Subject: [PATCH 136/208] fuzz: create a function run_gnu_cmd to deduplicate the code --- fuzz/fuzz_targets/fuzz_common.rs | 30 ++++++++++++++++++++++++++++++ fuzz/fuzz_targets/fuzz_expr.rs | 28 ++-------------------------- fuzz/fuzz_targets/fuzz_test.rs | 20 ++------------------ 3 files changed, 34 insertions(+), 44 deletions(-) diff --git a/fuzz/fuzz_targets/fuzz_common.rs b/fuzz/fuzz_targets/fuzz_common.rs index a33d603f130..351a7e8e8af 100644 --- a/fuzz/fuzz_targets/fuzz_common.rs +++ b/fuzz/fuzz_targets/fuzz_common.rs @@ -5,6 +5,7 @@ use libc::{dup, dup2, STDOUT_FILENO}; use std::ffi::OsString; +use std::io; use std::process::Command; use std::sync::atomic::Ordering; use std::sync::{atomic::AtomicBool, Once}; @@ -75,3 +76,32 @@ where (my_output, uumain_exit_status) } + +pub fn run_gnu_cmd( + cmd_path: &str, + args: &[OsString], + check_gnu: bool, +) -> Result<(String, i32), io::Error> { + if check_gnu { + is_gnu_cmd(cmd_path)?; // Check if it's a GNU implementation + } + + let mut command = Command::new(cmd_path); + for arg in args { + command.arg(arg); + } + + let output = command.output()?; + let exit_code = output.status.code().unwrap_or(-1); + if output.status.success() || !check_gnu { + Ok(( + String::from_utf8_lossy(&output.stdout).to_string(), + exit_code, + )) + } else { + Err(io::Error::new( + io::ErrorKind::Other, + format!("GNU command execution failed with exit code {}", exit_code), + )) + } +} diff --git a/fuzz/fuzz_targets/fuzz_expr.rs b/fuzz/fuzz_targets/fuzz_expr.rs index 28fded99e07..6344c052575 100644 --- a/fuzz/fuzz_targets/fuzz_expr.rs +++ b/fuzz/fuzz_targets/fuzz_expr.rs @@ -12,35 +12,11 @@ use rand::seq::SliceRandom; use rand::Rng; use std::ffi::OsString; -use std::process::Command; mod fuzz_common; -use crate::fuzz_common::generate_and_run_uumain; -use crate::fuzz_common::is_gnu_cmd; +use crate::fuzz_common::{generate_and_run_uumain, run_gnu_cmd}; static CMD_PATH: &str = "expr"; -fn run_gnu_expr(args: &[OsString]) -> Result<(String, i32), std::io::Error> { - is_gnu_cmd(CMD_PATH)?; // Check if it's a GNU implementation - - let mut command = Command::new(CMD_PATH); - for arg in args { - command.arg(arg); - } - let output = command.output()?; - let exit_code = output.status.code().unwrap_or(-1); - if output.status.success() { - Ok(( - String::from_utf8_lossy(&output.stdout).to_string(), - exit_code, - )) - } else { - Err(std::io::Error::new( - std::io::ErrorKind::Other, - format!("GNU expr execution failed with exit code {}", exit_code), - )) - } -} - fn generate_random_string(max_length: usize) -> String { let mut rng = rand::thread_rng(); let valid_utf8: Vec = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" @@ -111,7 +87,7 @@ fuzz_target!(|_data: &[u8]| { let (rust_output, uumain_exit_code) = generate_and_run_uumain(&mut args, uumain); // Run GNU expr with the provided arguments and compare the output - match run_gnu_expr(&args[1..]) { + match run_gnu_cmd(CMD_PATH, &args[1..], true) { Ok((gnu_output, gnu_exit_code)) => { let gnu_output = gnu_output.trim().to_owned(); if uumain_exit_code != gnu_exit_code { diff --git a/fuzz/fuzz_targets/fuzz_test.rs b/fuzz/fuzz_targets/fuzz_test.rs index dbd2db54ab0..bfde25246d9 100644 --- a/fuzz/fuzz_targets/fuzz_test.rs +++ b/fuzz/fuzz_targets/fuzz_test.rs @@ -12,10 +12,8 @@ use rand::seq::SliceRandom; use rand::Rng; use std::ffi::OsString; -use std::process::Command; - mod fuzz_common; -use crate::fuzz_common::generate_and_run_uumain; +use crate::fuzz_common::{generate_and_run_uumain, run_gnu_cmd}; #[derive(PartialEq, Debug, Clone)] enum ArgType { @@ -30,20 +28,6 @@ enum ArgType { static CMD_PATH: &str = "test"; -fn run_gnu_test(args: &[OsString]) -> Result<(String, i32), std::io::Error> { - let mut command = Command::new(CMD_PATH); - - for arg in args { - command.arg(arg); - } - let output = command.output()?; - let exit_status = output.status.code().unwrap_or(-1); // Capture the exit status code - Ok(( - String::from_utf8_lossy(&output.stdout).to_string(), - exit_status, - )) -} - fn generate_random_string(max_length: usize) -> String { let mut rng = rand::thread_rng(); let valid_utf8: Vec = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" @@ -223,7 +207,7 @@ fuzz_target!(|_data: &[u8]| { let (rust_output, uumain_exit_status) = generate_and_run_uumain(&mut args, uumain); // Run GNU test with the provided arguments and compare the output - match run_gnu_test(&args[1..]) { + match run_gnu_cmd(CMD_PATH, &args[1..], false) { Ok((gnu_output, gnu_exit_status)) => { let gnu_output = gnu_output.trim().to_owned(); println!("gnu_exit_status {}", gnu_exit_status); From a576054d42373ee3e502f015ffb998cc232df824 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Thu, 28 Sep 2023 21:52:26 +0200 Subject: [PATCH 137/208] fuzz: fix clippy warnings --- fuzz/fuzz_targets/fuzz_common.rs | 4 ++-- fuzz/fuzz_targets/fuzz_date.rs | 2 +- fuzz/fuzz_targets/fuzz_expr.rs | 14 +++++++------- fuzz/fuzz_targets/fuzz_parse_glob.rs | 2 +- fuzz/fuzz_targets/fuzz_test.rs | 4 ++-- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/fuzz/fuzz_targets/fuzz_common.rs b/fuzz/fuzz_targets/fuzz_common.rs index 351a7e8e8af..a94963ef024 100644 --- a/fuzz/fuzz_targets/fuzz_common.rs +++ b/fuzz/fuzz_targets/fuzz_common.rs @@ -32,7 +32,7 @@ pub fn is_gnu_cmd(cmd_path: &str) -> Result<(), std::io::Error> { } } -pub fn generate_and_run_uumain(args: &mut Vec, uumain_function: F) -> (String, i32) +pub fn generate_and_run_uumain(args: &[OsString], uumain_function: F) -> (String, i32) where F: FnOnce(std::vec::IntoIter) -> i32, { @@ -45,7 +45,7 @@ where { unsafe { dup2(pipe_fds[1], STDOUT_FILENO) }; - uumain_exit_status = uumain_function(args.clone().into_iter()); + uumain_exit_status = uumain_function(args.to_owned().into_iter()); unsafe { dup2(original_stdout_fd, STDOUT_FILENO) }; unsafe { libc::close(original_stdout_fd) }; } diff --git a/fuzz/fuzz_targets/fuzz_date.rs b/fuzz/fuzz_targets/fuzz_date.rs index 96c56cc6bf2..0f9cb262c06 100644 --- a/fuzz/fuzz_targets/fuzz_date.rs +++ b/fuzz/fuzz_targets/fuzz_date.rs @@ -9,6 +9,6 @@ fuzz_target!(|data: &[u8]| { let args = data .split(|b| *b == delim) .filter_map(|e| std::str::from_utf8(e).ok()) - .map(|e| OsString::from(e)); + .map(OsString::from); uumain(args); }); diff --git a/fuzz/fuzz_targets/fuzz_expr.rs b/fuzz/fuzz_targets/fuzz_expr.rs index 6344c052575..fb7b1730956 100644 --- a/fuzz/fuzz_targets/fuzz_expr.rs +++ b/fuzz/fuzz_targets/fuzz_expr.rs @@ -84,7 +84,7 @@ fuzz_target!(|_data: &[u8]| { let mut args = vec![OsString::from("expr")]; args.extend(expr.split_whitespace().map(OsString::from)); - let (rust_output, uumain_exit_code) = generate_and_run_uumain(&mut args, uumain); + let (rust_output, uumain_exit_code) = generate_and_run_uumain(&args, uumain); // Run GNU expr with the provided arguments and compare the output match run_gnu_cmd(CMD_PATH, &args[1..], true) { @@ -96,16 +96,16 @@ fuzz_target!(|_data: &[u8]| { println!("GNU code: {}", gnu_exit_code); panic!("Different error codes"); } - if rust_output != gnu_output { - println!("Expression: {}", expr); - println!("Rust output: {}", rust_output); - println!("GNU output: {}", gnu_output); - panic!("Different output between Rust & GNU"); - } else { + if rust_output == gnu_output { println!( "Outputs matched for expression: {} => Result: {}", expr, rust_output ); + } else { + println!("Expression: {}", expr); + println!("Rust output: {}", rust_output); + println!("GNU output: {}", gnu_output); + panic!("Different output between Rust & GNU"); } } Err(_) => { diff --git a/fuzz/fuzz_targets/fuzz_parse_glob.rs b/fuzz/fuzz_targets/fuzz_parse_glob.rs index 061569bc418..e235c0c9d89 100644 --- a/fuzz/fuzz_targets/fuzz_parse_glob.rs +++ b/fuzz/fuzz_targets/fuzz_parse_glob.rs @@ -5,6 +5,6 @@ use uucore::parse_glob; fuzz_target!(|data: &[u8]| { if let Ok(s) = std::str::from_utf8(data) { - _ = parse_glob::from_str(s) + _ = parse_glob::from_str(s); } }); diff --git a/fuzz/fuzz_targets/fuzz_test.rs b/fuzz/fuzz_targets/fuzz_test.rs index bfde25246d9..4805a41af16 100644 --- a/fuzz/fuzz_targets/fuzz_test.rs +++ b/fuzz/fuzz_targets/fuzz_test.rs @@ -142,7 +142,7 @@ fn generate_test_arg() -> String { 0 => { arg.push_str(&rng.gen_range(-100..=100).to_string()); } - 1 | 2 | 3 => { + 1..=3 => { let test_arg = test_args .choose(&mut rng) .expect("Failed to choose a random test argument"); @@ -204,7 +204,7 @@ fuzz_target!(|_data: &[u8]| { args.push(OsString::from(generate_test_arg())); } - let (rust_output, uumain_exit_status) = generate_and_run_uumain(&mut args, uumain); + let (rust_output, uumain_exit_status) = generate_and_run_uumain(&args, uumain); // Run GNU test with the provided arguments and compare the output match run_gnu_cmd(CMD_PATH, &args[1..], false) { From 55fd1f3617958209180b596c5e75fd017a2b1fb9 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Thu, 28 Sep 2023 21:25:38 +0200 Subject: [PATCH 138/208] Replace list of digit by is_ascii_digit and some ride along simplification --- src/uu/chmod/src/chmod.rs | 14 +++++------ src/uu/install/src/mode.rs | 5 +--- src/uu/mkdir/src/mkdir.rs | 36 +++++++++++++---------------- src/uu/mknod/src/parsemode.rs | 3 +-- src/uucore/src/lib/features/mode.rs | 3 +-- 5 files changed, 26 insertions(+), 35 deletions(-) diff --git a/src/uu/chmod/src/chmod.rs b/src/uu/chmod/src/chmod.rs index b007bb1d735..31663b1af9c 100644 --- a/src/uu/chmod/src/chmod.rs +++ b/src/uu/chmod/src/chmod.rs @@ -335,9 +335,7 @@ impl Chmoder { let mut new_mode = fperm; let mut naively_expected_new_mode = new_mode; for mode in cmode_unwrapped.split(',') { - // cmode is guaranteed to be Some in this case - let arr: &[char] = &['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; - let result = if mode.contains(arr) { + let result = if mode.chars().any(|c| c.is_ascii_digit()) { mode::parse_numeric(new_mode, mode, file.is_dir()).map(|v| (v, v)) } else { mode::parse_symbolic(new_mode, mode, get_umask(), file.is_dir()).map(|m| { @@ -352,20 +350,22 @@ impl Chmoder { (m, naive_mode) }) }; + match result { Ok((mode, naive_mode)) => { new_mode = mode; naively_expected_new_mode = naive_mode; } Err(f) => { - if self.quiet { - return Err(ExitCode::new(1)); + return if self.quiet { + Err(ExitCode::new(1)) } else { - return Err(USimpleError::new(1, f)); - } + Err(USimpleError::new(1, f)) + }; } } } + self.change_file(fperm, new_mode, file)?; // if a permission would have been removed if umask was 0, but it wasn't because umask was not 0, print an error and fail if (new_mode & !naively_expected_new_mode) != 0 { diff --git a/src/uu/install/src/mode.rs b/src/uu/install/src/mode.rs index f9018e16f8e..ebdec14afe6 100644 --- a/src/uu/install/src/mode.rs +++ b/src/uu/install/src/mode.rs @@ -9,10 +9,7 @@ use uucore::mode; /// Takes a user-supplied string and tries to parse to u16 mode bitmask. pub fn parse(mode_string: &str, considering_dir: bool, umask: u32) -> Result { - let numbers: &[char] = &['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; - - // Passing 000 as the existing permissions seems to mirror GNU behavior. - if mode_string.contains(numbers) { + if mode_string.chars().any(|c| c.is_ascii_digit()) { mode::parse_numeric(0, mode_string, considering_dir) } else { mode::parse_symbolic(0, mode_string, umask, considering_dir) diff --git a/src/uu/mkdir/src/mkdir.rs b/src/uu/mkdir/src/mkdir.rs index 2044855e45e..76aa51f079c 100644 --- a/src/uu/mkdir/src/mkdir.rs +++ b/src/uu/mkdir/src/mkdir.rs @@ -38,31 +38,27 @@ fn get_mode(_matches: &ArgMatches, _mode_had_minus_prefix: bool) -> Result Result { - let digits: &[char] = &['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; - // Translate a ~str in octal form to u16, default to 777 // Not tested on Windows let mut new_mode = DEFAULT_PERM; - match matches.get_one::(options::MODE) { - Some(m) => { - for mode in m.split(',') { - if mode.contains(digits) { - new_mode = mode::parse_numeric(new_mode, m, true)?; + + if let Some(m) = matches.get_one::(options::MODE) { + for mode in m.split(',') { + if mode.chars().any(|c| c.is_ascii_digit()) { + new_mode = mode::parse_numeric(new_mode, m, true)?; + } else { + let cmode = if mode_had_minus_prefix { + // clap parsing is finished, now put prefix back + format!("-{mode}") } else { - let cmode = if mode_had_minus_prefix { - // clap parsing is finished, now put prefix back - format!("-{mode}") - } else { - mode.to_string() - }; - new_mode = mode::parse_symbolic(new_mode, &cmode, mode::get_umask(), true)?; - } + mode.to_string() + }; + new_mode = mode::parse_symbolic(new_mode, &cmode, mode::get_umask(), true)?; } - Ok(new_mode) - } - None => { - // If no mode argument is specified return the mode derived from umask - Ok(!mode::get_umask() & 0o0777) } + Ok(new_mode) + } else { + // If no mode argument is specified return the mode derived from umask + Ok(!mode::get_umask() & 0o0777) } } diff --git a/src/uu/mknod/src/parsemode.rs b/src/uu/mknod/src/parsemode.rs index adacaa45bcd..c38800bcb0d 100644 --- a/src/uu/mknod/src/parsemode.rs +++ b/src/uu/mknod/src/parsemode.rs @@ -11,8 +11,7 @@ use uucore::mode; pub const MODE_RW_UGO: mode_t = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; pub fn parse_mode(mode: &str) -> Result { - let arr: &[char] = &['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; - let result = if mode.contains(arr) { + let result = if mode.chars().any(|c| c.is_ascii_digit()) { mode::parse_numeric(MODE_RW_UGO as u32, mode) } else { mode::parse_symbolic(MODE_RW_UGO as u32, mode, true) diff --git a/src/uucore/src/lib/features/mode.rs b/src/uucore/src/lib/features/mode.rs index cbaea71bf01..147624891bd 100644 --- a/src/uucore/src/lib/features/mode.rs +++ b/src/uucore/src/lib/features/mode.rs @@ -147,8 +147,7 @@ pub fn parse_mode(mode: &str) -> Result { #[cfg(any(target_os = "freebsd", target_vendor = "apple", target_os = "android"))] let fperm = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) as u32; - let arr: &[char] = &['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; - let result = if mode.contains(arr) { + let result = if mode.chars().any(|c| c.is_ascii_digit()) { parse_numeric(fperm, mode, true) } else { parse_symbolic(fperm, mode, get_umask(), true) From f92066cee2554813aacd5dceac19c5d79b571b0f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 30 Sep 2023 13:25:14 +0000 Subject: [PATCH 139/208] chore(deps): update rust crate regex to 1.9.6 --- Cargo.lock | 8 ++++---- Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c007551987f..4dc36c88f99 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1740,9 +1740,9 @@ checksum = "f1bfbf25d7eb88ddcbb1ec3d755d0634da8f7657b2cb8b74089121409ab8228f" [[package]] name = "regex" -version = "1.9.5" +version = "1.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47" +checksum = "ebee201405406dbf528b8b672104ae6d6d63e6d118cb10e4d51abbc7b58044ff" dependencies = [ "aho-corasick", "memchr", @@ -1752,9 +1752,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795" +checksum = "59b23e92ee4318893fa3fe3e6fb365258efbfe6ac6ab30f090cdcbb7aa37efa9" dependencies = [ "aho-corasick", "memchr", diff --git a/Cargo.toml b/Cargo.toml index a40d066d4ef..831d4ae4851 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -310,7 +310,7 @@ rand = { version = "0.8", features = ["small_rng"] } rand_core = "0.6" rayon = "1.8" redox_syscall = "0.4" -regex = "1.9.5" +regex = "1.9.6" rstest = "0.18.2" rust-ini = "0.19.0" same-file = "1.0.6" From 95ccc54d05892db5630e1a9413474041605fe2f0 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Sat, 30 Sep 2023 15:35:05 +0200 Subject: [PATCH 140/208] nl: defer showing "line number overflow" error --- src/uu/nl/src/nl.rs | 17 ++++++++++------- tests/by-util/test_nl.rs | 16 ++++++++++++++++ 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/src/uu/nl/src/nl.rs b/src/uu/nl/src/nl.rs index ea37e00dce1..71b4aac2886 100644 --- a/src/uu/nl/src/nl.rs +++ b/src/uu/nl/src/nl.rs @@ -56,14 +56,14 @@ impl Default for Settings { } struct Stats { - line_number: i64, + line_number: Option, consecutive_empty_lines: u64, } impl Stats { fn new(starting_line_number: i64) -> Self { Self { - line_number: starting_line_number, + line_number: Some(starting_line_number), consecutive_empty_lines: 0, } } @@ -344,7 +344,7 @@ fn nl(reader: &mut BufReader, stats: &mut Stats, settings: &Settings if let Some(new_style) = new_numbering_style { current_numbering_style = new_style; if settings.renumber { - stats.line_number = settings.starting_line_number; + stats.line_number = Some(settings.starting_line_number); } println!(); } else { @@ -364,18 +364,21 @@ fn nl(reader: &mut BufReader, stats: &mut Stats, settings: &Settings }; if is_line_numbered { + let Some(line_number) = stats.line_number else { + return Err(USimpleError::new(1, "line number overflow")); + }; println!( "{}{}{}", settings .number_format - .format(stats.line_number, settings.number_width), + .format(line_number, settings.number_width), settings.number_separator, line ); // update line number for the potential next line - match stats.line_number.checked_add(settings.line_increment) { - Some(new_line_number) => stats.line_number = new_line_number, - None => return Err(USimpleError::new(1, "line number overflow")), + match line_number.checked_add(settings.line_increment) { + Some(new_line_number) => stats.line_number = Some(new_line_number), + None => stats.line_number = None, // overflow } } else { let spaces = " ".repeat(settings.number_width + 1); diff --git a/tests/by-util/test_nl.rs b/tests/by-util/test_nl.rs index 87f218166e2..78c8975a849 100644 --- a/tests/by-util/test_nl.rs +++ b/tests/by-util/test_nl.rs @@ -539,6 +539,22 @@ fn test_line_number_overflow() { .stderr_is("nl: line number overflow\n"); } +#[test] +fn test_line_number_no_overflow() { + new_ucmd!() + .arg(format!("--starting-line-number={}", i64::MAX)) + .pipe_in("a\n\\:\\:\nb") + .succeeds() + .stdout_is(format!("{0}\ta\n\n{0}\tb\n", i64::MAX)); + + new_ucmd!() + .arg(format!("--starting-line-number={}", i64::MIN)) + .arg("--line-increment=-1") + .pipe_in("a\n\\:\\:\nb") + .succeeds() + .stdout_is(format!("{0}\ta\n\n{0}\tb\n", i64::MIN)); +} + #[test] fn test_section_delimiter() { for arg in ["-dabc", "--section-delimiter=abc"] { From 7337cd51691b0345308c1e1c37f2a7d1e1b3390e Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sun, 1 Oct 2023 09:58:09 +0200 Subject: [PATCH 141/208] ls -R1: add a test to replicate GNU's recursive.sh --- tests/by-util/test_ls.rs | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tests/by-util/test_ls.rs b/tests/by-util/test_ls.rs index 23dfafa3200..7d0f86298cd 100644 --- a/tests/by-util/test_ls.rs +++ b/tests/by-util/test_ls.rs @@ -1928,6 +1928,35 @@ fn test_ls_recursive() { result.stdout_contains("a\\b:\nb"); } +#[test] +fn test_ls_recursive_1() { + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + at.mkdir("x"); + at.mkdir("y"); + at.mkdir("a"); + at.mkdir("b"); + at.mkdir("c"); + at.mkdir("a/1"); + at.mkdir("a/2"); + at.mkdir("a/3"); + at.touch("f"); + at.touch("a/1/I"); + at.touch("a/1/II"); + #[cfg(unix)] + let out = "a:\n1\n2\n3\n\na/1:\nI\nII\n\na/2:\n\na/3:\n\nb:\n\nc:\n"; + #[cfg(windows)] + let out = "a:\n1\n2\n3\n\na\\1:\nI\nII\n\na\\2:\n\na\\3:\n\nb:\n\nc:\n"; + scene + .ucmd() + .arg("-R1") + .arg("a") + .arg("b") + .arg("c") + .succeeds() + .stdout_is(out); +} + #[test] fn test_ls_color() { let scene = TestScenario::new(util_name!()); From c27fcc4084cf0b3de255b16a34071e82e96d67c6 Mon Sep 17 00:00:00 2001 From: Daniel Clarke Date: Sun, 1 Oct 2023 12:01:46 -0400 Subject: [PATCH 142/208] Update parse_datetime to 0.5.0 (#5313) --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- src/uu/date/src/date.rs | 4 +++- src/uu/touch/src/touch.rs | 41 +++++++++++++------------------------ tests/by-util/test_touch.rs | 12 +++++++++++ 5 files changed, 32 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4dc36c88f99..98f95032f8f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1499,9 +1499,9 @@ dependencies = [ [[package]] name = "parse_datetime" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fecceaede7767a9a98058687a321bc91742eff7670167a34104afb30fc8757df" +checksum = "3bbf4e25b13841080e018a1e666358adfe5e39b6d353f986ca5091c210b586a1" dependencies = [ "chrono", "regex", diff --git a/Cargo.toml b/Cargo.toml index 831d4ae4851..d6feeb0caa7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -301,7 +301,7 @@ num-traits = "0.2.16" number_prefix = "0.4" once_cell = "1.18.0" onig = { version = "~6.4", default-features = false } -parse_datetime = "0.4.0" +parse_datetime = "0.5.0" phf = "0.11.2" phf_codegen = "0.11.2" platform-info = "2.0.2" diff --git a/src/uu/date/src/date.rs b/src/uu/date/src/date.rs index 745fd54239c..b5ab8993acd 100644 --- a/src/uu/date/src/date.rs +++ b/src/uu/date/src/date.rs @@ -166,7 +166,9 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { }; let date_source = if let Some(date) = matches.get_one::(OPT_DATE) { - if let Ok(duration) = parse_datetime::from_str(date.as_str()) { + let ref_time = Local::now(); + if let Ok(new_time) = parse_datetime::parse_datetime_at_date(ref_time, date.as_str()) { + let duration = new_time.signed_duration_since(ref_time); DateSource::Human(duration) } else { DateSource::Custom(date.into()) diff --git a/src/uu/touch/src/touch.rs b/src/uu/touch/src/touch.rs index 85eb97bc462..6555773eedd 100644 --- a/src/uu/touch/src/touch.rs +++ b/src/uu/touch/src/touch.rs @@ -68,6 +68,10 @@ fn datetime_to_filetime(dt: &DateTime) -> FileTime { FileTime::from_unix_time(dt.timestamp(), dt.timestamp_subsec_nanos()) } +fn filetime_to_datetime(ft: &FileTime) -> Option> { + Some(DateTime::from_timestamp(ft.unix_seconds(), ft.nanoseconds())?.into()) +} + #[uucore::main] #[allow(clippy::cognitive_complexity)] pub fn uumain(args: impl uucore::Args) -> UResult<()> { @@ -88,35 +92,19 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { ) { (Some(reference), Some(date)) => { let (atime, mtime) = stat(Path::new(reference), !matches.get_flag(options::NO_DEREF))?; - if let Ok(offset) = parse_datetime::from_str(date) { - let seconds = offset.num_seconds(); - let nanos = offset.num_nanoseconds().unwrap_or(0) % 1_000_000_000; - - let ref_atime_secs = atime.unix_seconds(); - let ref_atime_nanos = atime.nanoseconds(); - let atime = FileTime::from_unix_time( - ref_atime_secs + seconds, - ref_atime_nanos + nanos as u32, - ); - - let ref_mtime_secs = mtime.unix_seconds(); - let ref_mtime_nanos = mtime.nanoseconds(); - let mtime = FileTime::from_unix_time( - ref_mtime_secs + seconds, - ref_mtime_nanos + nanos as u32, - ); - - (atime, mtime) - } else { - let timestamp = parse_date(date)?; - (timestamp, timestamp) - } + let atime = filetime_to_datetime(&atime).ok_or_else(|| { + USimpleError::new(1, "Could not process the reference access time") + })?; + let mtime = filetime_to_datetime(&mtime).ok_or_else(|| { + USimpleError::new(1, "Could not process the reference modification time") + })?; + (parse_date(atime, date)?, parse_date(mtime, date)?) } (Some(reference), None) => { stat(Path::new(reference), !matches.get_flag(options::NO_DEREF))? } (None, Some(date)) => { - let timestamp = parse_date(date)?; + let timestamp = parse_date(Local::now(), date)?; (timestamp, timestamp) } (None, None) => { @@ -336,7 +324,7 @@ fn stat(path: &Path, follow: bool) -> UResult<(FileTime, FileTime)> { )) } -fn parse_date(s: &str) -> UResult { +fn parse_date(ref_time: DateTime, s: &str) -> UResult { // This isn't actually compatible with GNU touch, but there doesn't seem to // be any simple specification for what format this parameter allows and I'm // not about to implement GNU parse_datetime. @@ -385,8 +373,7 @@ fn parse_date(s: &str) -> UResult { } } - if let Ok(duration) = parse_datetime::from_str(s) { - let dt = Local::now() + duration; + if let Ok(dt) = parse_datetime::parse_datetime_at_date(ref_time, s) { return Ok(datetime_to_filetime(&dt)); } diff --git a/tests/by-util/test_touch.rs b/tests/by-util/test_touch.rs index c9c0d700e2a..7b659fc5155 100644 --- a/tests/by-util/test_touch.rs +++ b/tests/by-util/test_touch.rs @@ -844,3 +844,15 @@ fn test_touch_dash() { ucmd.args(&["-h", "-"]).succeeds().no_stderr().no_stdout(); } + +#[test] +// Chrono panics for now +#[ignore] +fn test_touch_invalid_date_format() { + let (_at, mut ucmd) = at_and_ucmd!(); + let file = "test_touch_invalid_date_format"; + + ucmd.args(&["-m", "-t", "+1000000000000 years", file]) + .fails() + .stderr_contains("touch: invalid date format ‘+1000000000000 years’"); +} From d4220e9bb75a524707474dfca4f3c04f71f1d275 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 1 Oct 2023 20:03:02 +0000 Subject: [PATCH 143/208] chore(deps): update rust crate bytecount to 0.6.4 --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 98f95032f8f..76e1dd72c31 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -205,9 +205,9 @@ checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" [[package]] name = "bytecount" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c676a478f63e9fa2dd5368a42f28bba0d6c560b775f38583c8bbaa7fcd67c9c" +checksum = "ad152d03a2c813c80bb94fedbf3a3f02b28f793e39e7c214c8a0bcc196343de7" [[package]] name = "byteorder" diff --git a/Cargo.toml b/Cargo.toml index d6feeb0caa7..c4c3fc2ff0a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -261,7 +261,7 @@ test = ["uu_test"] bigdecimal = "0.4" binary-heap-plus = "0.5.0" bstr = "1.6" -bytecount = "0.6.3" +bytecount = "0.6.4" byteorder = "1.4.3" chrono = { version = "^0.4.31", default-features = false, features = [ "std", From fda762b91cfe1833830583d03668dda6ff0c0e28 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Mon, 2 Oct 2023 14:31:41 +0200 Subject: [PATCH 144/208] mv: show no "skipped" msg with -vi/-vin --- src/uu/mv/src/mv.rs | 10 +--------- tests/by-util/test_mv.rs | 5 +++-- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/src/uu/mv/src/mv.rs b/src/uu/mv/src/mv.rs index 9f7a9661885..43f8eb6b651 100644 --- a/src/uu/mv/src/mv.rs +++ b/src/uu/mv/src/mv.rs @@ -448,19 +448,11 @@ fn rename( match b.overwrite { OverwriteMode::NoClobber => { - let err_msg = if b.verbose { - println!("skipped {}", to.quote()); - String::new() - } else { - format!("not replacing {}", to.quote()) - }; + let err_msg = format!("not replacing {}", to.quote()); return Err(io::Error::new(io::ErrorKind::Other, err_msg)); } OverwriteMode::Interactive => { if !prompt_yes!("overwrite {}?", to.quote()) { - if b.verbose { - println!("skipped {}", to.quote()); - } return Err(io::Error::new(io::ErrorKind::Other, "")); } } diff --git a/tests/by-util/test_mv.rs b/tests/by-util/test_mv.rs index eb8a30ac4e5..0490d41193f 100644 --- a/tests/by-util/test_mv.rs +++ b/tests/by-util/test_mv.rs @@ -1350,7 +1350,7 @@ fn test_mv_arg_interactive_skipped() { .ignore_stdin_write_error() .fails() .stderr_is("mv: overwrite 'b'? ") - .stdout_is("skipped 'b'\n"); + .no_stdout(); } #[test] @@ -1360,7 +1360,8 @@ fn test_mv_arg_interactive_skipped_vin() { at.touch("b"); ucmd.args(&["-vin", "a", "b"]) .fails() - .stdout_is("skipped 'b'\n"); + .stderr_is("mv: not replacing 'b'\n") + .no_stdout(); } #[test] From 8ee69d2b92b7cffa54e7118c989b0044d854ce60 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Mon, 2 Oct 2023 15:29:13 +0200 Subject: [PATCH 145/208] cp: show no "skipped" msg with -vi/-vin --- src/uu/cp/src/cp.rs | 19 ++++++------------- tests/by-util/test_cp.rs | 6 ++++-- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index b6270719cc9..f9f6d87634a 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -1276,23 +1276,16 @@ fn copy_source( } impl OverwriteMode { - fn verify(&self, path: &Path, verbose: bool) -> CopyResult<()> { + fn verify(&self, path: &Path) -> CopyResult<()> { match *self { Self::NoClobber => { - if verbose { - println!("skipped {}", path.quote()); - } else { - eprintln!("{}: not replacing {}", util_name(), path.quote()); - } + eprintln!("{}: not replacing {}", util_name(), path.quote()); Err(Error::NotAllFilesCopied) } Self::Interactive(_) => { if prompt_yes!("overwrite {}?", path.quote()) { Ok(()) } else { - if verbose { - println!("skipped {}", path.quote()); - } Err(Error::Skipped) } } @@ -1500,7 +1493,7 @@ fn handle_existing_dest( return Err(format!("{} and {} are the same file", source.quote(), dest.quote()).into()); } - options.overwrite.verify(dest, options.verbose)?; + options.overwrite.verify(dest)?; let backup_path = backup_control::get_backup_path(options.backup, dest, &options.backup_suffix); if let Some(backup_path) = backup_path { @@ -1895,7 +1888,7 @@ fn copy_helper( File::create(dest).context(dest.display().to_string())?; } else if source_is_fifo && options.recursive && !options.copy_contents { #[cfg(unix)] - copy_fifo(dest, options.overwrite, options.verbose)?; + copy_fifo(dest, options.overwrite)?; } else if source_is_symlink { copy_link(source, dest, symlinked_files)?; } else { @@ -1920,9 +1913,9 @@ fn copy_helper( // "Copies" a FIFO by creating a new one. This workaround is because Rust's // built-in fs::copy does not handle FIFOs (see rust-lang/rust/issues/79390). #[cfg(unix)] -fn copy_fifo(dest: &Path, overwrite: OverwriteMode, verbose: bool) -> CopyResult<()> { +fn copy_fifo(dest: &Path, overwrite: OverwriteMode) -> CopyResult<()> { if dest.exists() { - overwrite.verify(dest, verbose)?; + overwrite.verify(dest)?; fs::remove_file(dest)?; } diff --git a/tests/by-util/test_cp.rs b/tests/by-util/test_cp.rs index 1ce74572d5d..70d4038bd9e 100644 --- a/tests/by-util/test_cp.rs +++ b/tests/by-util/test_cp.rs @@ -483,7 +483,8 @@ fn test_cp_arg_interactive_verbose() { ucmd.args(&["-vi", "a", "b"]) .pipe_in("N\n") .fails() - .stdout_is("skipped 'b'\n"); + .stderr_is("cp: overwrite 'b'? ") + .no_stdout(); } #[test] @@ -494,7 +495,8 @@ fn test_cp_arg_interactive_verbose_clobber() { at.touch("b"); ucmd.args(&["-vin", "a", "b"]) .fails() - .stdout_is("skipped 'b'\n"); + .stderr_is("cp: not replacing 'b'\n") + .no_stdout(); } #[test] From 9f6a720582f8a373cce9521cfe54610dab06f89c Mon Sep 17 00:00:00 2001 From: Yury Zhytkou <54360928+zhitkoff@users.noreply.github.com> Date: Mon, 2 Oct 2023 15:46:00 -0400 Subject: [PATCH 146/208] Introducing DEVELOPMENT.md (#5209) --- CONTRIBUTING.md | 225 +-------------------------------- DEVELOPMENT.md | 329 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 335 insertions(+), 219 deletions(-) create mode 100644 DEVELOPMENT.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6f67eb828db..695e5ad18ef 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -38,205 +38,15 @@ CI. However, you can use `#[cfg(...)]` attributes to create platform dependent f VirtualBox and Parallels) for development: -## Tools +## Setting up your development environment -We have an extensive CI that will check your code before it can be merged. This -section explains how to run those checks locally to avoid waiting for the CI. +To setup your local development environment for this project please follow [DEVELOPMENT.md guide](DEVELOPMENT.md) -### pre-commit hooks +It covers [installation of necessary tools and prerequisites](DEVELOPMENT.md#tools) as well as using those tools to [test your code changes locally](DEVELOPMENT.md#testing) -A configuration for `pre-commit` is provided in the repository. It allows -automatically checking every git commit you make to ensure it compiles, and -passes `clippy` and `rustfmt` without warnings. +## Improving the GNU compatibility -To use the provided hook: - -1. [Install `pre-commit`](https://pre-commit.com/#install) -1. Run `pre-commit install` while in the repository directory - -Your git commits will then automatically be checked. If a check fails, an error -message will explain why, and your commit will be canceled. You can then make -the suggested changes, and run `git commit ...` again. - -### clippy - -```shell -cargo clippy --all-targets --all-features -``` - -The `msrv` key in the clippy configuration file `clippy.toml` is used to disable -lints pertaining to newer features by specifying the minimum supported Rust -version (MSRV). - -### rustfmt - -```shell -cargo fmt --all -``` - -### cargo-deny - -This project uses [cargo-deny](https://github.com/EmbarkStudios/cargo-deny/) to -detect duplicate dependencies, checks licenses, etc. To run it locally, first -install it and then run with: - -``` -cargo deny --all-features check all -``` - -### Markdown linter - -We use [markdownlint](https://github.com/DavidAnson/markdownlint) to lint the -Markdown files in the repository. - -### Spell checker - -We use `cspell` as spell checker for all files in the project. If you are using -VS Code, you can install the -[code spell checker](https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker) -extension to enable spell checking within your editor. Otherwise, you can -install [cspell](https://cspell.org/) separately. - -If you want to make the spell checker ignore a word, you can add - -```rust -// spell-checker:ignore word_to_ignore -``` - -at the top of the file. - -## Testing - -Testing can be done using either Cargo or `make`. - -### Testing with Cargo - -Just like with building, we follow the standard procedure for testing using -Cargo: - -```shell -cargo test -``` - -By default, `cargo test` only runs the common programs. To run also platform -specific tests, run: - -```shell -cargo test --features unix -``` - -If you would prefer to test a select few utilities: - -```shell -cargo test --features "chmod mv tail" --no-default-features -``` - -If you also want to test the core utilities: - -```shell -cargo test -p uucore -p coreutils -``` - -Or to test the pure Rust tests in the utility itself: - -```shell -cargo test -p uu_ls --lib -``` - -Running the complete test suite might take a while. We use [nextest](https://nexte.st/index.html) in -the CI and you might want to try it out locally. It can speed up the execution time of the whole -test run significantly if the cpu has multiple cores. - -```shell -cargo nextest run --features unix --no-fail-fast -``` - -To debug: - -```shell -gdb --args target/debug/coreutils ls -(gdb) b ls.rs:79 -(gdb) run -``` - -### Testing with GNU Make - -To simply test all available utilities: - -```shell -make test -``` - -To test all but a few of the available utilities: - -```shell -make SKIP_UTILS='UTILITY_1 UTILITY_2' test -``` - -To test only a few of the available utilities: - -```shell -make UTILS='UTILITY_1 UTILITY_2' test -``` - -To include tests for unimplemented behavior: - -```shell -make UTILS='UTILITY_1 UTILITY_2' SPEC=y test -``` - -To run tests with `nextest` just use the nextest target. Note you'll need to -[install](https://nexte.st/book/installation.html) `nextest` first. The `nextest` target accepts the -same arguments like the default `test` target, so it's possible to pass arguments to `nextest run` -via `CARGOFLAGS`: - -```shell -make CARGOFLAGS='--no-fail-fast' UTILS='UTILITY_1 UTILITY_2' nextest -``` - -### Run Busybox Tests - -This testing functionality is only available on *nix operating systems and -requires `make`. - -To run busybox tests for all utilities for which busybox has tests - -```shell -make busytest -``` - -To run busybox tests for a few of the available utilities - -```shell -make UTILS='UTILITY_1 UTILITY_2' busytest -``` - -To pass an argument like "-v" to the busybox test runtime - -```shell -make UTILS='UTILITY_1 UTILITY_2' RUNTEST_ARGS='-v' busytest -``` - -### Comparing with GNU - -To run uutils against the GNU test suite locally, run the following commands: - -```shell -bash util/build-gnu.sh -# Build uutils without release optimizations -UU_MAKE_PROFILE=debug bash util/build-gnu.sh -bash util/run-gnu-test.sh -# To run a single test: -bash util/run-gnu-test.sh tests/touch/not-owner.sh # for example -# To run several tests: -bash util/run-gnu-test.sh tests/touch/not-owner.sh tests/rm/no-give-up.sh # for example -# If this is a perl (.pl) test, to run in debug: -DEBUG=1 bash util/run-gnu-test.sh tests/misc/sm3sum.pl -``` - -Note that it relies on individual utilities (not the multicall binary). - -### Improving the GNU compatibility +Please make sure you have installed [GNU utils and prerequisites](DEVELOPMENT.md#gnu-utils-and-prerequisites) and can execute commands described in [Comparing with GNU](DEVELOPMENT.md#comparing-with-gnu) section of [DEVELOPMENT.md](DEVELOPMENT.md) The Python script `./util/remaining-gnu-error.py` shows the list of failing tests in the CI. @@ -326,30 +136,7 @@ gitignore: add temporary files ## Code coverage - - -Code coverage report can be generated using [grcov](https://github.com/mozilla/grcov). - -### Using Nightly Rust - -To generate [gcov-based](https://github.com/mozilla/grcov#example-how-to-generate-gcda-files-for-a-rust-project) coverage report - -```shell -export CARGO_INCREMENTAL=0 -export RUSTFLAGS="-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort" -export RUSTDOCFLAGS="-Cpanic=abort" -cargo build # e.g., --features feat_os_unix -cargo test # e.g., --features feat_os_unix test_pathchk -grcov . -s . --binary-path ./target/debug/ -t html --branch --ignore-not-existing --ignore build.rs --excl-br-line "^\s*((debug_)?assert(_eq|_ne)?\#\[derive\()" -o ./target/debug/coverage/ -# open target/debug/coverage/index.html in browser -``` - -if changes are not reflected in the report then run `cargo clean` and run the above commands. - -### Using Stable Rust - -If you are using stable version of Rust that doesn't enable code coverage instrumentation by default -then add `-Z-Zinstrument-coverage` flag to `RUSTFLAGS` env variable specified above. +To generate code coverage report locally please follow [Code coverage report](DEVELOPMENT.md#code-coverage-report) section of [DEVELOPMENT.md](DEVELOPMENT.md) ## Other implementations diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md new file mode 100644 index 00000000000..24a1bdeb52c --- /dev/null +++ b/DEVELOPMENT.md @@ -0,0 +1,329 @@ + + +# Setting up your local development environment + +For contributing rules and best practices please refer to [CONTRIBUTING.md](CONTRIBUTING.md) + +## Before you start + +For this guide we assume that you already have GitHub account and have `git` and your favorite code editor or IDE installed and configured. +Before you start working on coreutils, please follow these steps: + +1. Fork the [coreutils repository](https://github.com/uutils/coreutils) to your GitHub account. +***Tip:*** See [this GitHub guide](https://docs.github.com/en/get-started/quickstart/fork-a-repo) for more information on this step. +2. Clone that fork to your local development environment: + +```shell +git clone https://github.com/YOUR-GITHUB-ACCOUNT/coreutils +cd coreutils +``` + +## Tools + +You will need the tools mentioned in this section to build and test your code changes locally. +This section will explain how to install and configure these tools. +We also have an extensive CI that uses these tools and will check your code before it can be merged. +The next section [Testing](#testing) will explain how to run those checks locally to avoid waiting for the CI. + +### Rust toolchain + +[Install Rust](https://www.rust-lang.org/tools/install) + +If you're using rustup to install and manage your Rust toolchains, `clippy` and `rustfmt` are usually already installed. If you are using one of the alternative methods, please make sure to install them manually. See following sub-sections for their usage: [clippy](#clippy) [rustfmt](#rustfmt). + +***Tip*** You might also need to add 'llvm-tools' component if you are going to [generate code coverage reports locally](#code-coverage-report): + +```shell +rustup component add llvm-tools-preview +``` + +### GNU utils and prerequisites + +If you are developing on Linux, most likely you already have all/most GNU utilities and prerequisites installed. + +To make sure, please check GNU coreutils [README-prereq](https://github.com/coreutils/coreutils/blob/master/README-prereq). + +You will need these to [run uutils against the GNU test suite locally](#comparing-with-gnu). + +For MacOS and Windows platform specific setup please check [MacOS GNU utils](#macos-gnu-utils) and [Windows GNU utils](#windows-gnu-utils) sections respectfully. + +### pre-commit hooks + +A configuration for `pre-commit` is provided in the repository. It allows +automatically checking every git commit you make to ensure it compiles, and +passes `clippy` and `rustfmt` without warnings. + +To use the provided hook: + +1. [Install `pre-commit`](https://pre-commit.com/#install) +1. Run `pre-commit install` while in the repository directory + +Your git commits will then automatically be checked. If a check fails, an error +message will explain why, and your commit will be canceled. You can then make +the suggested changes, and run `git commit ...` again. + +**NOTE: On MacOS** the pre-commit hooks are currently broken. There are workarounds involving switching to unstable nightly Rust and components. + +### clippy + +```shell +cargo clippy --all-targets --all-features +``` + +The `msrv` key in the clippy configuration file `clippy.toml` is used to disable +lints pertaining to newer features by specifying the minimum supported Rust +version (MSRV). + +### rustfmt + +```shell +cargo fmt --all +``` + +### cargo-deny + +This project uses [cargo-deny](https://github.com/EmbarkStudios/cargo-deny/) to +detect duplicate dependencies, checks licenses, etc. To run it locally, first +install it and then run with: + +```shell +cargo deny --all-features check all +``` + +### Markdown linter + +We use [markdownlint](https://github.com/DavidAnson/markdownlint) to lint the +Markdown files in the repository. + +### Spell checker + +We use `cspell` as spell checker for all files in the project. If you are using +VS Code, you can install the +[code spell checker](https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker) +extension to enable spell checking within your editor. Otherwise, you can +install [cspell](https://cspell.org/) separately. + +If you want to make the spell checker ignore a word, you can add + +```rust +// spell-checker:ignore word_to_ignore +``` + +at the top of the file. + +## Testing + +This section explains how to run our CI checks locally. +Testing can be done using either Cargo or `make`. + +### Testing with Cargo + +Just like with building, we follow the standard procedure for testing using +Cargo: + +```shell +cargo test +``` + +By default, `cargo test` only runs the common programs. To run also platform +specific tests, run: + +```shell +cargo test --features unix +``` + +If you would prefer to test a select few utilities: + +```shell +cargo test --features "chmod mv tail" --no-default-features +``` + +If you also want to test the core utilities: + +```shell +cargo test -p uucore -p coreutils +``` + +Running the complete test suite might take a while. We use [nextest](https://nexte.st/index.html) in +the CI and you might want to try it out locally. It can speed up the execution time of the whole +test run significantly if the cpu has multiple cores. + +```shell +cargo nextest run --features unix --no-fail-fast +``` + +To debug: + +```shell +gdb --args target/debug/coreutils ls +(gdb) b ls.rs:79 +(gdb) run +``` + +### Testing with GNU Make + +To simply test all available utilities: + +```shell +make test +``` + +To test all but a few of the available utilities: + +```shell +make SKIP_UTILS='UTILITY_1 UTILITY_2' test +``` + +To test only a few of the available utilities: + +```shell +make UTILS='UTILITY_1 UTILITY_2' test +``` + +To include tests for unimplemented behavior: + +```shell +make UTILS='UTILITY_1 UTILITY_2' SPEC=y test +``` + +To run tests with `nextest` just use the nextest target. Note you'll need to +[install](https://nexte.st/book/installation.html) `nextest` first. The `nextest` target accepts the +same arguments like the default `test` target, so it's possible to pass arguments to `nextest run` +via `CARGOFLAGS`: + +```shell +make CARGOFLAGS='--no-fail-fast' UTILS='UTILITY_1 UTILITY_2' nextest +``` + +### Run Busybox Tests + +This testing functionality is only available on *nix operating systems and +requires `make`. + +To run busybox tests for all utilities for which busybox has tests + +```shell +make busytest +``` + +To run busybox tests for a few of the available utilities + +```shell +make UTILS='UTILITY_1 UTILITY_2' busytest +``` + +To pass an argument like "-v" to the busybox test runtime + +```shell +make UTILS='UTILITY_1 UTILITY_2' RUNTEST_ARGS='-v' busytest +``` + +### Comparing with GNU + +To run uutils against the GNU test suite locally, run the following commands: + +```shell +bash util/build-gnu.sh +# Build uutils without release optimizations +UU_MAKE_PROFILE=debug bash util/build-gnu.sh +bash util/run-gnu-test.sh +# To run a single test: +bash util/run-gnu-test.sh tests/touch/not-owner.sh # for example +# To run several tests: +bash util/run-gnu-test.sh tests/touch/not-owner.sh tests/rm/no-give-up.sh # for example +# If this is a perl (.pl) test, to run in debug: +DEBUG=1 bash util/run-gnu-test.sh tests/misc/sm3sum.pl +``` + +***Tip:*** First time you run `bash util/build-gnu.sh` command, it will provide instructions on how to checkout GNU coreutils repository at the correct release tag. Please follow those instructions and when done, run `bash util/build-gnu.sh` command again. + +Note that GNU test suite relies on individual utilities (not the multicall binary). + +## Code coverage report + +Code coverage report can be generated using [grcov](https://github.com/mozilla/grcov). + +### Using Nightly Rust + +To generate [gcov-based](https://github.com/mozilla/grcov#example-how-to-generate-gcda-files-for-a-rust-project) coverage report + +```shell +export CARGO_INCREMENTAL=0 +export RUSTFLAGS="-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort" +export RUSTDOCFLAGS="-Cpanic=abort" +cargo build # e.g., --features feat_os_unix +cargo test # e.g., --features feat_os_unix test_pathchk +grcov . -s . --binary-path ./target/debug/ -t html --branch --ignore-not-existing --ignore build.rs --excl-br-line "^\s*((debug_)?assert(_eq|_ne)?\#\[derive\()" -o ./target/debug/coverage/ +# open target/debug/coverage/index.html in browser +``` + +if changes are not reflected in the report then run `cargo clean` and run the above commands. + +### Using Stable Rust + +If you are using stable version of Rust that doesn't enable code coverage instrumentation by default +then add `-Z-Zinstrument-coverage` flag to `RUSTFLAGS` env variable specified above. + +## Tips for setting up on Mac + +### C Compiler and linker + +On MacOS you'll need to install C compiler & linker: + +```shell +xcode-select --install +``` + +### MacOS GNU utils + +On MacOS you will need to install [Homebrew](https://docs.brew.sh/Installation) and use it to install the following Homebrew formulas: + +```shell +brew install \ + coreutils \ + autoconf \ + gettext \ + wget \ + texinfo \ + xz \ + automake \ + gnu-sed \ + m4 \ + bison \ + pre-commit \ + findutils +``` + +After installing these Homebrew formulas, please make sure to add the following lines to your `zsh` or `bash` rc file, i.e. `~/.profile` or `~/.zshrc` or `~/.bashrc` ... +(assuming Homebrew is installed at default location `/opt/homebrew`): + +```shell +eval "$(/opt/homebrew/bin/brew shellenv)" +export PATH="/opt/homebrew/opt/coreutils/libexec/gnubin:$PATH" +export PATH="/opt/homebrew/opt/bison/bin:$PATH" +export PATH="/opt/homebrew/opt/findutils/libexec/gnubin:$PATH" +``` + +Last step is to link Homebrew coreutils version of `timeout` to `/usr/local/bin` (as admin user): + +```shell +sudo ln -s /opt/homebrew/bin/timeout /usr/local/bin/timeout +``` + +Do not forget to either source updated rc file or restart you terminal session to update environment variables. + +## Tips for setting up on Windows + +### MSVC build tools + +On Windows you'll need the MSVC build tools for Visual Studio 2013 or later. + +If you are using `rustup-init.exe` to install Rust toolchain, it will guide you through the process of downloading and installing these prerequisites. + +Otherwise please follow [this guide](https://learn.microsoft.com/en-us/windows/dev-environment/rust/setup). + +### Windows GNU utils + +If you have used [Git for Windows](https://gitforwindows.org) to install `git` on you Windows system you might already have some GNU core utilities installed as part of "GNU Bash" included in Git for Windows package, but it is not a complete package. [This article](https://gist.github.com/evanwill/0207876c3243bbb6863e65ec5dc3f058) provides instruction on how to add more to it. + +Alternatively you can install [Cygwin](https://www.cygwin.com) and/or use [WSL2](https://learn.microsoft.com/en-us/windows/wsl/compare-versions#whats-new-in-wsl-2) to get access to all GNU core utilities on Windows. From c5a0aa92f8b31741e2be6128fbb9c5f29d22ec69 Mon Sep 17 00:00:00 2001 From: Yury Zhytkou <54360928+zhitkoff@users.noreply.github.com> Date: Mon, 2 Oct 2023 18:42:46 -0400 Subject: [PATCH 147/208] split: implementing separator option (#5331) * split: implementing separator option * split: separator option - handle multiple update * split: style * split: separator tests * split: separator tests - stdin in ci/cd * split: tests - ci/cd stdin errors * split: refactor based on feedback * split: improve test coverage * split: fix broken pipe error in tests with stdin * split: fix for handle_multiple_separator_options * split: comments * split: refactor separator code * split: changes based on feedback * split: changes based on feedback --- src/uu/split/split.md | 13 + src/uu/split/src/split.rs | 133 ++++++++--- tests/by-util/test_split.rs | 239 +++++++++++++++++++ tests/fixtures/split/separator_nul.txt | Bin 0 -> 10 bytes tests/fixtures/split/separator_semicolon.txt | 1 + 5 files changed, 348 insertions(+), 38 deletions(-) create mode 100644 tests/fixtures/split/separator_nul.txt create mode 100644 tests/fixtures/split/separator_semicolon.txt diff --git a/src/uu/split/split.md b/src/uu/split/split.md index d3a481fd307..836e3a0c69a 100644 --- a/src/uu/split/split.md +++ b/src/uu/split/split.md @@ -11,3 +11,16 @@ Create output files containing consecutive or interleaved sections of input ## After Help Output fixed-size pieces of INPUT to PREFIXaa, PREFIXab, ...; default size is 1000, and default PREFIX is 'x'. With no INPUT, or when INPUT is -, read standard input. + +The SIZE argument is an integer and optional unit (example: 10K is 10*1024). +Units are K,M,G,T,P,E,Z,Y,R,Q (powers of 1024) or KB,MB,... (powers of 1000). +Binary prefixes can be used, too: KiB=K, MiB=M, and so on. + +CHUNKS may be: + +- N split into N files based on size of input +- K/N output Kth of N to stdout +- l/N split into N files without splitting lines/records +- l/K/N output Kth of N to stdout without splitting lines/records +- r/N like 'l' but use round robin distribution +- r/K/N likewise but only output Kth of N to stdout diff --git a/src/uu/split/src/split.rs b/src/uu/split/src/split.rs index a61c0e812de..75624853953 100644 --- a/src/uu/split/src/split.rs +++ b/src/uu/split/src/split.rs @@ -11,7 +11,7 @@ mod platform; use crate::filenames::FilenameIterator; use crate::filenames::SuffixType; -use clap::{crate_version, parser::ValueSource, Arg, ArgAction, ArgMatches, Command}; +use clap::{crate_version, parser::ValueSource, Arg, ArgAction, ArgMatches, Command, ValueHint}; use std::env; use std::ffi::OsString; use std::fmt; @@ -39,6 +39,7 @@ static OPT_HEX_SUFFIXES_SHORT: &str = "-x"; static OPT_SUFFIX_LENGTH: &str = "suffix-length"; static OPT_DEFAULT_SUFFIX_LENGTH: &str = "0"; static OPT_VERBOSE: &str = "verbose"; +static OPT_SEPARATOR: &str = "separator"; //The ---io and ---io-blksize parameters are consumed and ignored. //The parameter is included to make GNU coreutils tests pass. static OPT_IO: &str = "-io"; @@ -55,7 +56,6 @@ const AFTER_HELP: &str = help_section!("after help", "split.md"); #[uucore::main] pub fn uumain(args: impl uucore::Args) -> UResult<()> { let (args, obs_lines) = handle_obsolete(args); - let matches = uu_app().try_get_matches_from(args)?; match Settings::from(&matches, &obs_lines) { @@ -145,6 +145,7 @@ fn should_extract_obs_lines( && !slice.starts_with("-C") && !slice.starts_with("-l") && !slice.starts_with("-n") + && !slice.starts_with("-t") } /// Helper function to [`filter_args`] @@ -208,13 +209,18 @@ fn handle_preceding_options( || &slice[2..] == OPT_ADDITIONAL_SUFFIX || &slice[2..] == OPT_FILTER || &slice[2..] == OPT_NUMBER - || &slice[2..] == OPT_SUFFIX_LENGTH; + || &slice[2..] == OPT_SUFFIX_LENGTH + || &slice[2..] == OPT_SEPARATOR; } // capture if current slice is a preceding short option that requires value and does not have value in the same slice (value separated by whitespace) // following slice should be treaded as value for this option // even if it starts with '-' (which would be treated as hyphen prefixed value) - *preceding_short_opt_req_value = - slice == "-b" || slice == "-C" || slice == "-l" || slice == "-n" || slice == "-a"; + *preceding_short_opt_req_value = slice == "-b" + || slice == "-C" + || slice == "-l" + || slice == "-n" + || slice == "-a" + || slice == "-t"; // slice is a value // reset preceding option flags if !slice.starts_with('-') { @@ -278,7 +284,7 @@ pub fn uu_app() -> Command { .long(OPT_FILTER) .allow_hyphen_values(true) .value_name("COMMAND") - .value_hint(clap::ValueHint::CommandName) + .value_hint(ValueHint::CommandName) .help( "write to shell COMMAND; file name is $FILE (Currently not implemented for Windows)", ), @@ -293,7 +299,7 @@ pub fn uu_app() -> Command { .arg( Arg::new(OPT_NUMERIC_SUFFIXES_SHORT) .short('d') - .action(clap::ArgAction::SetTrue) + .action(ArgAction::SetTrue) .overrides_with_all([ OPT_NUMERIC_SUFFIXES, OPT_NUMERIC_SUFFIXES_SHORT, @@ -314,12 +320,13 @@ pub fn uu_app() -> Command { OPT_HEX_SUFFIXES, OPT_HEX_SUFFIXES_SHORT ]) + .value_name("FROM") .help("same as -d, but allow setting the start value"), ) .arg( Arg::new(OPT_HEX_SUFFIXES_SHORT) .short('x') - .action(clap::ArgAction::SetTrue) + .action(ArgAction::SetTrue) .overrides_with_all([ OPT_NUMERIC_SUFFIXES, OPT_NUMERIC_SUFFIXES_SHORT, @@ -340,6 +347,7 @@ pub fn uu_app() -> Command { OPT_HEX_SUFFIXES, OPT_HEX_SUFFIXES_SHORT ]) + .value_name("FROM") .help("same as -x, but allow setting the start value"), ) .arg( @@ -357,6 +365,15 @@ pub fn uu_app() -> Command { .help("print a diagnostic just before each output file is opened") .action(ArgAction::SetTrue), ) + .arg( + Arg::new(OPT_SEPARATOR) + .short('t') + .long(OPT_SEPARATOR) + .allow_hyphen_values(true) + .value_name("SEP") + .action(ArgAction::Append) + .help("use SEP instead of newline as the record separator; '\0' (zero) specifies the NUL character"), + ) .arg( Arg::new(OPT_IO) .long("io") @@ -372,7 +389,7 @@ pub fn uu_app() -> Command { .arg( Arg::new(ARG_INPUT) .default_value("-") - .value_hint(clap::ValueHint::FilePath), + .value_hint(ValueHint::FilePath), ) .arg( Arg::new(ARG_PREFIX) @@ -696,6 +713,7 @@ struct Settings { filter: Option, strategy: Strategy, verbose: bool, + separator: u8, /// Whether to *not* produce empty files when using `-n`. /// @@ -722,6 +740,12 @@ enum SettingsError { /// Suffix is not large enough to split into specified chunks SuffixTooSmall(usize), + /// Multi-character (Invalid) separator + MultiCharacterSeparator(String), + + /// Multiple different separator characters + MultipleSeparatorCharacters, + /// The `--filter` option is not supported on Windows. #[cfg(windows)] NotSupported, @@ -743,6 +767,12 @@ impl fmt::Display for SettingsError { Self::Strategy(e) => e.fmt(f), Self::SuffixNotParsable(s) => write!(f, "invalid suffix length: {}", s.quote()), Self::SuffixTooSmall(i) => write!(f, "the suffix length needs to be at least {i}"), + Self::MultiCharacterSeparator(s) => { + write!(f, "multi-character separator {}", s.quote()) + } + Self::MultipleSeparatorCharacters => { + write!(f, "multiple separator characters specified") + } Self::SuffixContainsSeparator(s) => write!( f, "invalid suffix {}, contains directory separator", @@ -783,6 +813,26 @@ impl Settings { } } } + + // Make sure that separator is only one UTF8 character (if specified) + // defaults to '\n' - newline character + // If the same separator (the same value) was used multiple times - `split` should NOT fail + // If the separator was used multiple times but with different values (not all values are the same) - `split` should fail + let separator = match matches.get_many::(OPT_SEPARATOR) { + Some(mut sep_values) => { + let first = sep_values.next().unwrap(); // it is safe to just unwrap here since Clap should not return empty ValuesRef<'_,String> in the option from get_many() call + if !sep_values.all(|s| s == first) { + return Err(SettingsError::MultipleSeparatorCharacters); + } + match first.as_str() { + "\\0" => b'\0', + s if s.as_bytes().len() == 1 => s.as_bytes()[0], + s => return Err(SettingsError::MultiCharacterSeparator(s.to_owned())), + } + } + None => b'\n', + }; + let result = Self { suffix_length: suffix_length_str .parse() @@ -791,6 +841,7 @@ impl Settings { suffix_start, additional_suffix, verbose: matches.value_source("verbose") == Some(ValueSource::CommandLine), + separator, strategy, input: matches.get_one::(ARG_INPUT).unwrap().to_owned(), prefix: matches.get_one::(ARG_PREFIX).unwrap().to_owned(), @@ -1019,7 +1070,8 @@ impl<'a> Write for LineChunkWriter<'a> { // corresponds to the current chunk number. let mut prev = 0; let mut total_bytes_written = 0; - for i in memchr::memchr_iter(b'\n', buf) { + let sep = self.settings.separator; + for i in memchr::memchr_iter(sep, buf) { // If we have exceeded the number of lines to write in the // current chunk, then start a new chunk and its // corresponding writer. @@ -1036,8 +1088,8 @@ impl<'a> Write for LineChunkWriter<'a> { } // Write the line, starting from *after* the previous - // newline character and ending *after* the current - // newline character. + // separator character and ending *after* the current + // separator character. let n = self.inner.write(&buf[prev..i + 1])?; total_bytes_written += n; prev = i + 1; @@ -1175,21 +1227,22 @@ impl<'a> Write for LineBytesChunkWriter<'a> { self.num_bytes_remaining_in_current_chunk = self.chunk_size.try_into().unwrap(); } - // Find the first newline character in the buffer. - match memchr::memchr(b'\n', buf) { - // If there is no newline character and the buffer is + // Find the first separator (default - newline character) in the buffer. + let sep = self.settings.separator; + match memchr::memchr(sep, buf) { + // If there is no separator character and the buffer is // not empty, then write as many bytes as we can and // then move on to the next chunk if necessary. None => { let end = self.num_bytes_remaining_in_current_chunk; // This is ugly but here to match GNU behavior. If the input - // doesn't end with a \n, pretend that it does for handling + // doesn't end with a separator, pretend that it does for handling // the second to last segment chunk. See `line-bytes.sh`. if end == buf.len() && self.num_bytes_remaining_in_current_chunk < self.chunk_size.try_into().unwrap() - && buf[buf.len() - 1] != b'\n' + && buf[buf.len() - 1] != sep { self.num_bytes_remaining_in_current_chunk = 0; } else { @@ -1200,8 +1253,8 @@ impl<'a> Write for LineBytesChunkWriter<'a> { } } - // If there is a newline character and the line - // (including the newline character) will fit in the + // If there is a separator character and the line + // (including the separator character) will fit in the // current chunk, then write the entire line and // continue to the next iteration. (See chunk 1 in the // example comment above.) @@ -1212,8 +1265,8 @@ impl<'a> Write for LineBytesChunkWriter<'a> { buf = &buf[num_bytes_written..]; } - // If there is a newline character, the line - // (including the newline character) will not fit in + // If there is a separator character, the line + // (including the separator character) will not fit in // the current chunk, *and* no other lines have been // written to the current chunk, then write as many // bytes as we can and continue to the next @@ -1230,8 +1283,8 @@ impl<'a> Write for LineBytesChunkWriter<'a> { buf = &buf[num_bytes_written..]; } - // If there is a newline character, the line - // (including the newline character) will not fit in + // If there is a separator character, the line + // (including the separator character) will not fit in // the current chunk, and at least one other line has // been written to the current chunk, then signal to // the next iteration that a new chunk needs to be @@ -1489,15 +1542,16 @@ where let mut num_bytes_remaining_in_current_chunk = chunk_size; let mut i = 0; - for line_result in reader.lines() { + let sep = settings.separator; + for line_result in reader.split(sep) { let line = line_result.unwrap(); let maybe_writer = writers.get_mut(i); let writer = maybe_writer.unwrap(); - let bytes = line.as_bytes(); + let bytes = line.as_slice(); writer.write_all(bytes)?; - writer.write_all(b"\n")?; + writer.write_all(&[sep])?; - // Add one byte for the newline character. + // Add one byte for the separator character. let num_bytes = bytes.len() + 1; if num_bytes > num_bytes_remaining_in_current_chunk { num_bytes_remaining_in_current_chunk = chunk_size; @@ -1546,15 +1600,16 @@ where let mut num_bytes_remaining_in_current_chunk = chunk_size; let mut i = 0; - for line_result in reader.lines() { + let sep = settings.separator; + for line_result in reader.split(sep) { let line = line_result?; - let bytes = line.as_bytes(); + let bytes = line.as_slice(); if i == chunk_number { writer.write_all(bytes)?; - writer.write_all(b"\n")?; + writer.write_all(&[sep])?; } - // Add one byte for the newline character. + // Add one byte for the separator character. let num_bytes = bytes.len() + 1; if num_bytes >= num_bytes_remaining_in_current_chunk { num_bytes_remaining_in_current_chunk = chunk_size; @@ -1601,13 +1656,14 @@ where } let num_chunks: usize = num_chunks.try_into().unwrap(); - for (i, line_result) in reader.lines().enumerate() { + let sep = settings.separator; + for (i, line_result) in reader.split(sep).enumerate() { let line = line_result.unwrap(); let maybe_writer = writers.get_mut(i % num_chunks); let writer = maybe_writer.unwrap(); - let bytes = line.as_bytes(); + let bytes = line.as_slice(); writer.write_all(bytes)?; - writer.write_all(b"\n")?; + writer.write_all(&[sep])?; } Ok(()) @@ -1632,7 +1688,7 @@ where /// * [`split_into_n_chunks_by_line_round_robin`], which splits its input in the /// same way, but writes each chunk to its own file. fn kth_chunk_by_line_round_robin( - _settings: &Settings, + settings: &Settings, reader: &mut R, chunk_number: u64, num_chunks: u64, @@ -1646,12 +1702,13 @@ where let num_chunks: usize = num_chunks.try_into().unwrap(); let chunk_number: usize = chunk_number.try_into().unwrap(); - for (i, line_result) in reader.lines().enumerate() { + let sep = settings.separator; + for (i, line_result) in reader.split(sep).enumerate() { let line = line_result?; - let bytes = line.as_bytes(); + let bytes = line.as_slice(); if (i % num_chunks) == chunk_number { writer.write_all(bytes)?; - writer.write_all(b"\n")?; + writer.write_all(&[sep])?; } } Ok(()) diff --git a/tests/by-util/test_split.rs b/tests/by-util/test_split.rs index 7c6d271e6cc..6fc3a370613 100644 --- a/tests/by-util/test_split.rs +++ b/tests/by-util/test_split.rs @@ -1483,3 +1483,242 @@ fn test_split_non_utf8_argument_windows() { .fails() .stderr_contains("error: invalid UTF-8 was detected in one or more arguments"); } + +// Test '--separator' / '-t' option following GNU tests example +// test separators: '\n' , '\0' , ';' +// test with '--lines=2' , '--line-bytes=4' , '--number=l/3' , '--number=r/3' , '--number=l/1/3' , '--number=r/1/3' +#[test] +fn test_split_separator_nl_lines() { + let (at, mut ucmd) = at_and_ucmd!(); + ucmd.args(&["--lines=2", "-t", "\n"]) + .pipe_in("1\n2\n3\n4\n5\n") + .succeeds(); + + assert_eq!(file_read(&at, "xaa"), "1\n2\n"); + assert_eq!(file_read(&at, "xab"), "3\n4\n"); + assert_eq!(file_read(&at, "xac"), "5\n"); + assert!(!at.plus("xad").exists()); +} + +#[test] +fn test_split_separator_nl_line_bytes() { + let (at, mut ucmd) = at_and_ucmd!(); + ucmd.args(&["--line-bytes=4", "-t", "\n"]) + .pipe_in("1\n2\n3\n4\n5\n") + .succeeds(); + + assert_eq!(file_read(&at, "xaa"), "1\n2\n"); + assert_eq!(file_read(&at, "xab"), "3\n4\n"); + assert_eq!(file_read(&at, "xac"), "5\n"); + assert!(!at.plus("xad").exists()); +} + +#[test] +fn test_split_separator_nl_number_l() { + let (at, mut ucmd) = at_and_ucmd!(); + ucmd.args(&["--number=l/3", "--separator=\n", "fivelines.txt"]) + .succeeds(); + + assert_eq!(file_read(&at, "xaa"), "1\n2\n"); + assert_eq!(file_read(&at, "xab"), "3\n4\n"); + assert_eq!(file_read(&at, "xac"), "5\n"); + assert!(!at.plus("xad").exists()); +} + +#[test] +fn test_split_separator_nl_number_r() { + let (at, mut ucmd) = at_and_ucmd!(); + ucmd.args(&["--number=r/3", "--separator", "\n", "fivelines.txt"]) + .succeeds(); + + assert_eq!(file_read(&at, "xaa"), "1\n4\n"); + assert_eq!(file_read(&at, "xab"), "2\n5\n"); + assert_eq!(file_read(&at, "xac"), "3\n"); + assert!(!at.plus("xad").exists()); +} + +#[test] +fn test_split_separator_nul_lines() { + let (at, mut ucmd) = at_and_ucmd!(); + ucmd.args(&["--lines=2", "-t", "\\0", "separator_nul.txt"]) + .succeeds(); + + assert_eq!(file_read(&at, "xaa"), "1\02\0"); + assert_eq!(file_read(&at, "xab"), "3\04\0"); + assert_eq!(file_read(&at, "xac"), "5\0"); + assert!(!at.plus("xad").exists()); +} + +#[test] +fn test_split_separator_nul_line_bytes() { + let (at, mut ucmd) = at_and_ucmd!(); + ucmd.args(&["--line-bytes=4", "-t", "\\0", "separator_nul.txt"]) + .succeeds(); + + assert_eq!(file_read(&at, "xaa"), "1\02\0"); + assert_eq!(file_read(&at, "xab"), "3\04\0"); + assert_eq!(file_read(&at, "xac"), "5\0"); + assert!(!at.plus("xad").exists()); +} + +#[test] +fn test_split_separator_nul_number_l() { + let (at, mut ucmd) = at_and_ucmd!(); + ucmd.args(&["--number=l/3", "--separator=\\0", "separator_nul.txt"]) + .succeeds(); + + assert_eq!(file_read(&at, "xaa"), "1\02\0"); + assert_eq!(file_read(&at, "xab"), "3\04\0"); + assert_eq!(file_read(&at, "xac"), "5\0"); + assert!(!at.plus("xad").exists()); +} + +#[test] +fn test_split_separator_nul_number_r() { + let (at, mut ucmd) = at_and_ucmd!(); + ucmd.args(&["--number=r/3", "--separator=\\0", "separator_nul.txt"]) + .succeeds(); + + assert_eq!(file_read(&at, "xaa"), "1\04\0"); + assert_eq!(file_read(&at, "xab"), "2\05\0"); + assert_eq!(file_read(&at, "xac"), "3\0"); + assert!(!at.plus("xad").exists()); +} + +#[test] +fn test_split_separator_semicolon_lines() { + let (at, mut ucmd) = at_and_ucmd!(); + ucmd.args(&["--lines=2", "-t", ";", "separator_semicolon.txt"]) + .succeeds(); + + assert_eq!(file_read(&at, "xaa"), "1;2;"); + assert_eq!(file_read(&at, "xab"), "3;4;"); + assert_eq!(file_read(&at, "xac"), "5;"); + assert!(!at.plus("xad").exists()); +} + +#[test] +fn test_split_separator_semicolon_line_bytes() { + let (at, mut ucmd) = at_and_ucmd!(); + ucmd.args(&["--line-bytes=4", "-t", ";", "separator_semicolon.txt"]) + .succeeds(); + + assert_eq!(file_read(&at, "xaa"), "1;2;"); + assert_eq!(file_read(&at, "xab"), "3;4;"); + assert_eq!(file_read(&at, "xac"), "5;"); + assert!(!at.plus("xad").exists()); +} + +#[test] +fn test_split_separator_semicolon_number_l() { + let (at, mut ucmd) = at_and_ucmd!(); + ucmd.args(&["--number=l/3", "--separator=;", "separator_semicolon.txt"]) + .succeeds(); + + assert_eq!(file_read(&at, "xaa"), "1;2;"); + assert_eq!(file_read(&at, "xab"), "3;4;"); + assert_eq!(file_read(&at, "xac"), "5;"); + assert!(!at.plus("xad").exists()); +} + +#[test] +fn test_split_separator_semicolon_number_r() { + let (at, mut ucmd) = at_and_ucmd!(); + ucmd.args(&["--number=r/3", "--separator=;", "separator_semicolon.txt"]) + .succeeds(); + + assert_eq!(file_read(&at, "xaa"), "1;4;"); + assert_eq!(file_read(&at, "xab"), "2;5;"); + assert_eq!(file_read(&at, "xac"), "3;"); + assert!(!at.plus("xad").exists()); +} + +#[test] +fn test_split_separator_semicolon_number_kth_l() { + new_ucmd!() + .args(&[ + "--number=l/1/3", + "--separator", + ";", + "separator_semicolon.txt", + ]) + .succeeds() + .stdout_only("1;2;"); +} + +#[test] +fn test_split_separator_semicolon_number_kth_r() { + new_ucmd!() + .args(&[ + "--number=r/1/3", + "--separator", + ";", + "separator_semicolon.txt", + ]) + .succeeds() + .stdout_only("1;4;"); +} + +// Test error edge cases for separator option +#[test] +fn test_split_separator_no_value() { + new_ucmd!() + .args(&["-t"]) + .ignore_stdin_write_error() + .pipe_in("a\n") + .fails() + .stderr_contains( + "error: a value is required for '--separator ' but none was supplied", + ); +} + +#[test] +fn test_split_separator_invalid_usage() { + let scene = TestScenario::new(util_name!()); + scene + .ucmd() + .args(&["--separator=xx"]) + .ignore_stdin_write_error() + .pipe_in("a\n") + .fails() + .no_stdout() + .stderr_contains("split: multi-character separator 'xx'"); + scene + .ucmd() + .args(&["-ta", "-tb"]) + .ignore_stdin_write_error() + .pipe_in("a\n") + .fails() + .no_stdout() + .stderr_contains("split: multiple separator characters specified"); + scene + .ucmd() + .args(&["-t'\n'", "-tb"]) + .ignore_stdin_write_error() + .pipe_in("a\n") + .fails() + .no_stdout() + .stderr_contains("split: multiple separator characters specified"); +} + +// Test using same separator multiple times +#[test] +fn test_split_separator_same_multiple() { + let scene = TestScenario::new(util_name!()); + scene + .ucmd() + .args(&["--separator=:", "--separator=:", "fivelines.txt"]) + .succeeds(); + scene + .ucmd() + .args(&["-t:", "--separator=:", "fivelines.txt"]) + .succeeds(); + scene + .ucmd() + .args(&["-t", ":", "-t", ":", "fivelines.txt"]) + .succeeds(); + scene + .ucmd() + .args(&["-t:", "-t:", "-t,", "fivelines.txt"]) + .fails(); +} diff --git a/tests/fixtures/split/separator_nul.txt b/tests/fixtures/split/separator_nul.txt new file mode 100644 index 0000000000000000000000000000000000000000..c4c49609dbd79d27ec1457c489741e63995dc080 GIT binary patch literal 10 RcmXqHFk&!fFkvud000H>0RR91 literal 0 HcmV?d00001 diff --git a/tests/fixtures/split/separator_semicolon.txt b/tests/fixtures/split/separator_semicolon.txt new file mode 100644 index 00000000000..a8396d8eef5 --- /dev/null +++ b/tests/fixtures/split/separator_semicolon.txt @@ -0,0 +1 @@ +1;2;3;4;5; \ No newline at end of file From 252d01ac33b2eb4fdd0640a07840d0ef3318f886 Mon Sep 17 00:00:00 2001 From: PGIII Date: Mon, 2 Oct 2023 18:48:22 -0400 Subject: [PATCH 148/208] cp: fail when trying to copy to read only file on mac (#5261) * cp: fail when trying to copy to read only file * fix spelling error in macos.rs * simplify permissions check * add comment * fix typo --- src/uu/cp/src/platform/macos.rs | 11 +++++++++-- tests/by-util/test_cp.rs | 16 ++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/uu/cp/src/platform/macos.rs b/src/uu/cp/src/platform/macos.rs index 8c62c78d95d..77bdbbbdb83 100644 --- a/src/uu/cp/src/platform/macos.rs +++ b/src/uu/cp/src/platform/macos.rs @@ -63,8 +63,15 @@ pub(crate) fn copy_on_write( { // clonefile(2) fails if the destination exists. Remove it and try again. Do not // bother to check if removal worked because we're going to try to clone again. - let _ = fs::remove_file(dest); - error = pfn(src.as_ptr(), dst.as_ptr(), 0); + // first lets make sure the dest file is not read only + if fs::metadata(dest).map_or(false, |md| !md.permissions().readonly()) { + // remove and copy again + // TODO: rewrite this to better match linux behavior + // linux first opens the source file and destination file then uses the file + // descriptors to do the clone. + let _ = fs::remove_file(dest); + error = pfn(src.as_ptr(), dst.as_ptr(), 0); + } } } } diff --git a/tests/by-util/test_cp.rs b/tests/by-util/test_cp.rs index 1ce74572d5d..fe56d11601e 100644 --- a/tests/by-util/test_cp.rs +++ b/tests/by-util/test_cp.rs @@ -3440,3 +3440,19 @@ fn test_cp_only_source_no_target() { panic!("Failure: stderr was \n{stderr_str}"); } } + +#[test] +fn test_cp_dest_no_permissions() { + let ts = TestScenario::new(util_name!()); + let at = &ts.fixtures; + + at.touch("valid.txt"); + at.touch("invalid_perms.txt"); + at.set_readonly("invalid_perms.txt"); + + ts.ucmd() + .args(&["valid.txt", "invalid_perms.txt"]) + .fails() + .stderr_contains("invalid_perms.txt") + .stderr_contains("denied"); +} From a107374c8286796b170fbabbd65a6a28656a7415 Mon Sep 17 00:00:00 2001 From: Terts Diepraam Date: Tue, 3 Oct 2023 12:10:20 +0200 Subject: [PATCH 149/208] echo: use controlflow instead of bool --- src/uu/echo/src/echo.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/uu/echo/src/echo.rs b/src/uu/echo/src/echo.rs index 5ccc6a32a95..94788721004 100644 --- a/src/uu/echo/src/echo.rs +++ b/src/uu/echo/src/echo.rs @@ -6,6 +6,7 @@ use clap::{crate_version, Arg, ArgAction, Command}; use std::io::{self, Write}; use std::iter::Peekable; +use std::ops::ControlFlow; use std::str::Chars; use uucore::error::{FromIo, UResult}; use uucore::{format_usage, help_about, help_section, help_usage}; @@ -62,7 +63,7 @@ fn parse_code(input: &mut Peekable, base: Base) -> Option { Some(ret.into()) } -fn print_escaped(input: &str, mut output: impl Write) -> io::Result { +fn print_escaped(input: &str, mut output: impl Write) -> io::Result> { let mut iter = input.chars().peekable(); while let Some(c) = iter.next() { if c != '\\' { @@ -85,7 +86,7 @@ fn print_escaped(input: &str, mut output: impl Write) -> io::Result { '\\' => '\\', 'a' => '\x07', 'b' => '\x08', - 'c' => return Ok(true), + 'c' => return Ok(ControlFlow::Break(())), 'e' => '\x1b', 'f' => '\x0c', 'n' => '\n', @@ -112,7 +113,7 @@ fn print_escaped(input: &str, mut output: impl Write) -> io::Result { } } - Ok(false) + Ok(ControlFlow::Continue(())) } #[uucore::main] @@ -173,8 +174,7 @@ fn execute(no_newline: bool, escaped: bool, free: &[String]) -> io::Result<()> { write!(output, " ")?; } if escaped { - let should_stop = print_escaped(input, &mut output)?; - if should_stop { + if print_escaped(input, &mut output)?.is_break() { break; } } else { From c29bcb219f6d10028ba5b9137730f45c158f93b1 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Tue, 3 Oct 2023 14:37:15 +0200 Subject: [PATCH 150/208] echo: don't output "\n" if "\c" is encountered --- src/uu/echo/src/echo.rs | 2 +- tests/by-util/test_echo.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/uu/echo/src/echo.rs b/src/uu/echo/src/echo.rs index 94788721004..b3707b6f898 100644 --- a/src/uu/echo/src/echo.rs +++ b/src/uu/echo/src/echo.rs @@ -175,7 +175,7 @@ fn execute(no_newline: bool, escaped: bool, free: &[String]) -> io::Result<()> { } if escaped { if print_escaped(input, &mut output)?.is_break() { - break; + return Ok(()); } } else { write!(output, "{input}")?; diff --git a/tests/by-util/test_echo.rs b/tests/by-util/test_echo.rs index dce5a4c9520..875ff66cb14 100644 --- a/tests/by-util/test_echo.rs +++ b/tests/by-util/test_echo.rs @@ -122,7 +122,7 @@ fn test_escape_no_further_output() { new_ucmd!() .args(&["-e", "a\\cb", "c"]) .succeeds() - .stdout_only("a\n"); + .stdout_only("a"); } #[test] From 54ba81ecbb9a810d3672ae6e378effb5c0dcb352 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Tue, 3 Oct 2023 15:09:20 +0200 Subject: [PATCH 151/208] mv: fix typo in test function name test_mv_info_self -> test_mv_into_self --- tests/by-util/test_mv.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/by-util/test_mv.rs b/tests/by-util/test_mv.rs index 0490d41193f..f7f9622f52e 100644 --- a/tests/by-util/test_mv.rs +++ b/tests/by-util/test_mv.rs @@ -1323,7 +1323,7 @@ fn test_mv_interactive_error() { } #[test] -fn test_mv_info_self() { +fn test_mv_into_self() { let scene = TestScenario::new(util_name!()); let at = &scene.fixtures; let dir1 = "dir1"; From b591bedcab64851ec8d5091cc092db1144687369 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 3 Oct 2023 15:34:37 +0000 Subject: [PATCH 152/208] chore(deps): update rust crate memmap2 to 0.9 --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 76e1dd72c31..33ee7ff1477 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1291,9 +1291,9 @@ checksum = "5486aed0026218e61b8a01d5fbd5a0a134649abb71a0e53b7bc088529dced86e" [[package]] name = "memmap2" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a5a03cefb0d953ec0be133036f14e109412fa594edc2f77227249db66cc3ed" +checksum = "deaba38d7abf1d4cca21cc89e932e542ba2b9258664d2a9ef0e61512039c9375" dependencies = [ "libc", ] diff --git a/Cargo.toml b/Cargo.toml index c4c3fc2ff0a..e7fc2851bd8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -292,7 +292,7 @@ lscolors = { version = "0.15.0", default-features = false, features = [ "nu-ansi-term", ] } memchr = "2" -memmap2 = "0.8" +memmap2 = "0.9" nix = { version = "0.27", default-features = false } nom = "7.1.3" notify = { version = "=6.0.1", features = ["macos_kqueue"] } From e88183174b9592c6a490658f0ef09938459ffcdd Mon Sep 17 00:00:00 2001 From: boxdot Date: Wed, 4 Oct 2023 16:12:02 +0200 Subject: [PATCH 153/208] relpath: remove Closes #5236 --- Cargo.lock | 9 -- Cargo.toml | 2 - GNUmakefile | 1 - docs/compiles_table.csv | 42 ++++---- src/uu/relpath/Cargo.toml | 23 ----- src/uu/relpath/LICENSE | 1 - src/uu/relpath/relpath.md | 8 -- src/uu/relpath/src/main.rs | 1 - src/uu/relpath/src/relpath.rs | 91 ---------------- tests/by-util/test_relpath.rs | 189 ---------------------------------- tests/tests.rs | 4 - util/show-utils.BAT | 4 +- util/show-utils.sh | 4 +- 13 files changed, 25 insertions(+), 354 deletions(-) delete mode 100644 src/uu/relpath/Cargo.toml delete mode 120000 src/uu/relpath/LICENSE delete mode 100644 src/uu/relpath/relpath.md delete mode 100644 src/uu/relpath/src/main.rs delete mode 100644 src/uu/relpath/src/relpath.rs delete mode 100644 tests/by-util/test_relpath.rs diff --git a/Cargo.lock b/Cargo.lock index 33ee7ff1477..252be5d676e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -470,7 +470,6 @@ dependencies = [ "uu_pwd", "uu_readlink", "uu_realpath", - "uu_relpath", "uu_rm", "uu_rmdir", "uu_runcon", @@ -2866,14 +2865,6 @@ dependencies = [ "uucore", ] -[[package]] -name = "uu_relpath" -version = "0.0.21" -dependencies = [ - "clap", - "uucore", -] - [[package]] name = "uu_rm" version = "0.0.21" diff --git a/Cargo.toml b/Cargo.toml index e7fc2851bd8..88bd1635b9a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -100,7 +100,6 @@ feat_common_core = [ "pwd", "readlink", "realpath", - "relpath", "rm", "rmdir", "seq", @@ -430,7 +429,6 @@ ptx = { optional = true, version = "0.0.21", package = "uu_ptx", path = "src/uu/ pwd = { optional = true, version = "0.0.21", package = "uu_pwd", path = "src/uu/pwd" } readlink = { optional = true, version = "0.0.21", package = "uu_readlink", path = "src/uu/readlink" } realpath = { optional = true, version = "0.0.21", package = "uu_realpath", path = "src/uu/realpath" } -relpath = { optional = true, version = "0.0.21", package = "uu_relpath", path = "src/uu/relpath" } rm = { optional = true, version = "0.0.21", package = "uu_rm", path = "src/uu/rm" } rmdir = { optional = true, version = "0.0.21", package = "uu_rmdir", path = "src/uu/rmdir" } runcon = { optional = true, version = "0.0.21", package = "uu_runcon", path = "src/uu/runcon" } diff --git a/GNUmakefile b/GNUmakefile index c672458a1cd..ad2d38081f3 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -102,7 +102,6 @@ PROGS := \ pwd \ readlink \ realpath \ - relpath \ rm \ rmdir \ seq \ diff --git a/docs/compiles_table.csv b/docs/compiles_table.csv index d18854e0e91..e263067b78d 100644 --- a/docs/compiles_table.csv +++ b/docs/compiles_table.csv @@ -1,21 +1,21 @@ -target,arch,base32,base64,basename,cat,chgrp,chmod,chown,chroot,cksum,comm,cp,csplit,cut,date,df,dircolors,dirname,du,echo,env,expand,expr,factor,false,fmt,fold,groups,hashsum,head,hostid,hostname,id,install,join,kill,link,ln,logname,ls,mkdir,mkfifo,mknod,mktemp,more,mv,nice,nl,nohup,nproc,numfmt,od,paste,pathchk,pinky,printenv,printf,ptx,pwd,readlink,realpath,relpath,rm,rmdir,seq,shred,shuf,sleep,sort,split,stat,stdbuf,sum,sync,tac,tail,tee,test,timeout,touch,tr,true,truncate,tsort,tty,uname,unexpand,uniq,unlink,uptime,users,wc,who,whoami,yes,chcon,pr,dir,vdir,dd,basenc,runcon -aarch64-unknown-linux-gnu,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 -i686-unknown-linux-gnu,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 -powerpc64-unknown-linux-gnu,0,0,0,0,0,0,0,0,0,0,0,101,0,0,0,0,0,0,0,0,0,0,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 -riscv64gc-unknown-linux-gnu,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101 -x86_64-unknown-linux-gnu,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,101,0,0,0,0,0,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 -aarch64-pc-windows-msvc,0,0,0,0,0,101,101,101,101,0,0,0,0,0,0,0,0,0,101,0,0,0,101,0,0,0,0,101,0,0,0,0,101,101,0,101,0,0,0,101,0,101,101,0,0,0,101,0,101,0,0,0,0,101,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,101,101,0,0,0,0,0,0,101,0,0,0,0,0,101,0,0,0,101,0,101,0,101,101,0,0,0,0,0,0,0,0 -i686-pc-windows-gnu,0,0,0,0,0,101,101,101,101,0,0,0,0,0,0,0,0,0,101,0,0,0,101,0,0,0,0,101,0,0,0,0,101,101,0,101,0,0,0,0,0,101,101,0,0,0,101,0,101,0,0,0,0,101,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,101,101,0,0,0,0,0,0,101,0,0,0,0,0,0,0,0,0,101,0,101,0,101,0,0,0,0,0,0,0,0,0 -i686-pc-windows-msvc,0,0,0,0,0,101,101,101,101,0,0,0,0,0,0,0,0,0,101,0,0,0,101,0,0,0,0,101,0,0,0,0,101,101,0,101,0,0,0,0,0,101,101,0,0,0,101,0,101,0,0,0,0,101,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,101,101,0,0,0,0,0,0,101,0,0,0,0,0,101,0,0,0,101,0,101,0,101,0,0,0,0,0,0,0,0,0 -x86_64-pc-windows-gnu,0,0,0,0,0,101,101,101,101,0,0,0,0,0,0,0,0,0,101,0,0,0,101,0,0,0,0,101,0,0,0,0,101,101,0,101,0,0,0,0,0,101,101,0,0,0,101,0,101,0,0,0,0,101,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,101,101,0,0,0,0,0,0,101,0,0,0,0,0,0,0,0,0,101,0,101,0,101,0,0,0,0,0,0,0,0,0 -x86_64-pc-windows-msvc,0,0,0,0,0,101,101,101,101,0,0,0,0,0,0,0,0,0,101,0,0,0,101,0,0,0,0,101,0,0,0,0,101,101,0,101,0,0,0,0,0,101,101,0,0,0,101,0,101,0,0,0,0,101,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,101,101,0,0,0,0,0,0,101,0,0,0,0,0,101,0,0,0,101,0,101,0,101,0,0,0,0,0,0,0,0,0 -x86_64-apple-darwin,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 -x86_64-unknown-freebsd,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 -x86_64-unknown-netbsd,0,0,0,0,0,0,0,0,101,0,0,0,0,0,0,101,0,0,0,0,0,0,101,0,0,0,0,0,0,0,0,0,101,0,0,101,0,0,0,0,0,0,0,0,0,0,0,0,101,0,0,0,0,0,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,101,0,0,0,0,0,0,101,0,0,0,101,101,0,101,0,0,0,0,0,0,0,0,0 -aarch64-linux-android,0,0,0,0,0,0,0,0,101,0,0,0,0,0,0,101,0,0,0,0,0,0,101,0,0,0,0,0,0,0,0,0,101,0,0,101,0,0,0,0,0,0,0,0,0,0,0,0,101,0,0,0,0,0,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,101,0,0,101,0,0,0,0,101,0,0,0,0,0,0,101,0,0,0,101,101,0,101,0,0,0,0,0,0,0,0,0 -x86_64-linux-android,0,0,0,0,0,0,0,0,101,0,0,0,0,0,0,101,0,0,0,0,0,0,101,0,0,0,0,0,0,0,0,0,101,0,0,101,0,0,0,0,0,0,0,0,0,0,0,0,101,0,0,0,0,0,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,101,0,0,101,0,0,0,0,101,0,0,0,0,0,0,101,0,0,0,101,101,0,101,0,0,0,0,0,0,0,0,0 -x86_64-sun-solaris,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101 -wasm32-wasi,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101 -x86_64-unknown-redox,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101 -aarch64-fuchsia,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101 -x86_64-fuchsia,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101 +target,arch,base32,base64,basename,cat,chgrp,chmod,chown,chroot,cksum,comm,cp,csplit,cut,date,df,dircolors,dirname,du,echo,env,expand,expr,factor,false,fmt,fold,groups,hashsum,head,hostid,hostname,id,install,join,kill,link,ln,logname,ls,mkdir,mkfifo,mknod,mktemp,more,mv,nice,nl,nohup,nproc,numfmt,od,paste,pathchk,pinky,printenv,printf,ptx,pwd,readlink,realpath,rm,rmdir,seq,shred,shuf,sleep,sort,split,stat,stdbuf,sum,sync,tac,tail,tee,test,timeout,touch,tr,true,truncate,tsort,tty,uname,unexpand,uniq,unlink,uptime,users,wc,who,whoami,yes,chcon,pr,dir,vdir,dd,basenc,runcon +aarch64-unknown-linux-gnu,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +i686-unknown-linux-gnu,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +powerpc64-unknown-linux-gnu,0,0,0,0,0,0,0,0,0,0,0,101,0,0,0,0,0,0,0,0,0,0,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +riscv64gc-unknown-linux-gnu,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101 +x86_64-unknown-linux-gnu,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,101,0,0,0,0,0,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +aarch64-pc-windows-msvc,0,0,0,0,0,101,101,101,101,0,0,0,0,0,0,0,0,0,101,0,0,0,101,0,0,0,0,101,0,0,0,0,101,101,0,101,0,0,0,101,0,101,101,0,0,0,101,0,101,0,0,0,0,101,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,101,101,0,0,0,0,0,0,101,0,0,0,0,0,101,0,0,0,101,0,101,0,101,101,0,0,0,0,0,0,0,0 +i686-pc-windows-gnu,0,0,0,0,0,101,101,101,101,0,0,0,0,0,0,0,0,0,101,0,0,0,101,0,0,0,0,101,0,0,0,0,101,101,0,101,0,0,0,0,0,101,101,0,0,0,101,0,101,0,0,0,0,101,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,101,101,0,0,0,0,0,0,101,0,0,0,0,0,0,0,0,0,101,0,101,0,101,0,0,0,0,0,0,0,0,0 +i686-pc-windows-msvc,0,0,0,0,0,101,101,101,101,0,0,0,0,0,0,0,0,0,101,0,0,0,101,0,0,0,0,101,0,0,0,0,101,101,0,101,0,0,0,0,0,101,101,0,0,0,101,0,101,0,0,0,0,101,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,101,101,0,0,0,0,0,0,101,0,0,0,0,0,101,0,0,0,101,0,101,0,101,0,0,0,0,0,0,0,0,0 +x86_64-pc-windows-gnu,0,0,0,0,0,101,101,101,101,0,0,0,0,0,0,0,0,0,101,0,0,0,101,0,0,0,0,101,0,0,0,0,101,101,0,101,0,0,0,0,0,101,101,0,0,0,101,0,101,0,0,0,0,101,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,101,101,0,0,0,0,0,0,101,0,0,0,0,0,0,0,0,0,101,0,101,0,101,0,0,0,0,0,0,0,0,0 +x86_64-pc-windows-msvc,0,0,0,0,0,101,101,101,101,0,0,0,0,0,0,0,0,0,101,0,0,0,101,0,0,0,0,101,0,0,0,0,101,101,0,101,0,0,0,0,0,101,101,0,0,0,101,0,101,0,0,0,0,101,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,101,101,0,0,0,0,0,0,101,0,0,0,0,0,101,0,0,0,101,0,101,0,101,0,0,0,0,0,0,0,0,0 +x86_64-apple-darwin,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +x86_64-unknown-freebsd,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +x86_64-unknown-netbsd,0,0,0,0,0,0,0,0,101,0,0,0,0,0,0,101,0,0,0,0,0,0,101,0,0,0,0,0,0,0,0,0,101,0,0,101,0,0,0,0,0,0,0,0,0,0,0,0,101,0,0,0,0,0,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,101,0,0,0,0,0,0,101,0,0,0,101,101,0,101,0,0,0,0,0,0,0,0,0 +aarch64-linux-android,0,0,0,0,0,0,0,0,101,0,0,0,0,0,0,101,0,0,0,0,0,0,101,0,0,0,0,0,0,0,0,0,101,0,0,101,0,0,0,0,0,0,0,0,0,0,0,0,101,0,0,0,0,0,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,101,0,0,101,0,0,0,0,101,0,0,0,0,0,0,101,0,0,0,101,101,0,101,0,0,0,0,0,0,0,0,0 +x86_64-linux-android,0,0,0,0,0,0,0,0,101,0,0,0,0,0,0,101,0,0,0,0,0,0,101,0,0,0,0,0,0,0,0,0,101,0,0,101,0,0,0,0,0,0,0,0,0,0,0,0,101,0,0,0,0,0,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,101,0,0,101,0,0,0,0,101,0,0,0,0,0,0,101,0,0,0,101,101,0,101,0,0,0,0,0,0,0,0,0 +x86_64-sun-solaris,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101 +wasm32-wasi,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101 +x86_64-unknown-redox,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101 +aarch64-fuchsia,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101 +x86_64-fuchsia,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101 diff --git a/src/uu/relpath/Cargo.toml b/src/uu/relpath/Cargo.toml deleted file mode 100644 index 0df32efd2b7..00000000000 --- a/src/uu/relpath/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "uu_relpath" -version = "0.0.21" -authors = ["uutils developers"] -license = "MIT" -description = "relpath ~ (uutils) display relative path of PATHNAME_TO from PATHNAME_FROM" - -homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/main/src/uu/relpath" -keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] -categories = ["command-line-utilities"] -edition = "2021" - -[lib] -path = "src/relpath.rs" - -[dependencies] -clap = { workspace = true } -uucore = { workspace = true, features = ["fs"] } - -[[bin]] -name = "relpath" -path = "src/main.rs" diff --git a/src/uu/relpath/LICENSE b/src/uu/relpath/LICENSE deleted file mode 120000 index 5853aaea53b..00000000000 --- a/src/uu/relpath/LICENSE +++ /dev/null @@ -1 +0,0 @@ -../../../LICENSE \ No newline at end of file diff --git a/src/uu/relpath/relpath.md b/src/uu/relpath/relpath.md deleted file mode 100644 index a1ff9bf4a37..00000000000 --- a/src/uu/relpath/relpath.md +++ /dev/null @@ -1,8 +0,0 @@ -# relpath - -``` -relpath [-d DIR] TO [FROM] -``` - -Convert TO destination to the relative path from the FROM dir. -If FROM path is omitted, current working dir will be used. \ No newline at end of file diff --git a/src/uu/relpath/src/main.rs b/src/uu/relpath/src/main.rs deleted file mode 100644 index b7dba76ce4d..00000000000 --- a/src/uu/relpath/src/main.rs +++ /dev/null @@ -1 +0,0 @@ -uucore::bin!(uu_relpath); diff --git a/src/uu/relpath/src/relpath.rs b/src/uu/relpath/src/relpath.rs deleted file mode 100644 index aa8b5caab96..00000000000 --- a/src/uu/relpath/src/relpath.rs +++ /dev/null @@ -1,91 +0,0 @@ -// This file is part of the uutils coreutils package. -// -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -// spell-checker:ignore (ToDO) subpath absto absfrom absbase - -use clap::{crate_version, Arg, Command}; -use std::env; -use std::path::{Path, PathBuf}; -use uucore::display::println_verbatim; -use uucore::error::{FromIo, UResult}; -use uucore::fs::{canonicalize, MissingHandling, ResolveMode}; -use uucore::{format_usage, help_about, help_usage}; - -const USAGE: &str = help_usage!("relpath.md"); -const ABOUT: &str = help_about!("relpath.md"); - -mod options { - pub const DIR: &str = "DIR"; - pub const TO: &str = "TO"; - pub const FROM: &str = "FROM"; -} - -#[uucore::main] -pub fn uumain(args: impl uucore::Args) -> UResult<()> { - let args = args.collect_lossy(); - - let matches = uu_app().get_matches_from(args); - - let to = Path::new(matches.get_one::(options::TO).unwrap()).to_path_buf(); // required - let from = match matches.get_one::(options::FROM) { - Some(p) => Path::new(p).to_path_buf(), - None => env::current_dir().unwrap(), - }; - let absto = canonicalize(to, MissingHandling::Normal, ResolveMode::Logical) - .map_err_context(String::new)?; - let absfrom = canonicalize(from, MissingHandling::Normal, ResolveMode::Logical) - .map_err_context(String::new)?; - - if matches.contains_id(options::DIR) { - let base = Path::new(&matches.get_one::(options::DIR).unwrap()).to_path_buf(); - let absbase = canonicalize(base, MissingHandling::Normal, ResolveMode::Logical) - .map_err_context(String::new)?; - if !absto.as_path().starts_with(absbase.as_path()) - || !absfrom.as_path().starts_with(absbase.as_path()) - { - return println_verbatim(absto).map_err_context(String::new); - } - } - - let mut suffix_pos = 0; - for (f, t) in absfrom.components().zip(absto.components()) { - if f == t { - suffix_pos += 1; - } else { - break; - } - } - - let mut result = PathBuf::new(); - absfrom - .components() - .skip(suffix_pos) - .map(|_| result.push("..")) - .last(); - absto - .components() - .skip(suffix_pos) - .map(|x| result.push(x.as_os_str())) - .last(); - - println_verbatim(result).map_err_context(String::new) -} - -pub fn uu_app() -> Command { - Command::new(uucore::util_name()) - .version(crate_version!()) - .about(ABOUT) - .override_usage(format_usage(USAGE)) - .infer_long_args(true) - .arg(Arg::new(options::DIR).short('d').help( - "If any of FROM and TO is not subpath of DIR, output absolute path instead of relative", - )) - .arg( - Arg::new(options::TO) - .value_hint(clap::ValueHint::AnyPath) - .required(true), - ) - .arg(Arg::new(options::FROM).value_hint(clap::ValueHint::AnyPath)) -} diff --git a/tests/by-util/test_relpath.rs b/tests/by-util/test_relpath.rs deleted file mode 100644 index f506e01c5cc..00000000000 --- a/tests/by-util/test_relpath.rs +++ /dev/null @@ -1,189 +0,0 @@ -// This file is part of the uutils coreutils package. -// -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. -use crate::common::util::TestScenario; -use std::borrow::Cow; -use std::path::Path; - -struct TestCase<'a> { - from: &'a str, - to: &'a str, - expected: &'a str, -} - -const TESTS: [TestCase; 10] = [ - TestCase { - from: "A/B/C", - to: "A", - expected: "../..", - }, - TestCase { - from: "A/B/C", - to: "A/B", - expected: "..", - }, - TestCase { - from: "A/B/C", - to: "A/B/C", - expected: "", - }, - TestCase { - from: "A/B/C", - to: "A/B/C/D", - expected: "D", - }, - TestCase { - from: "A/B/C", - to: "A/B/C/D/E", - expected: "D/E", - }, - TestCase { - from: "A/B/C", - to: "A/B/D", - expected: "../D", - }, - TestCase { - from: "A/B/C", - to: "A/B/D/E", - expected: "../D/E", - }, - TestCase { - from: "A/B/C", - to: "A/D", - expected: "../../D", - }, - TestCase { - from: "A/B/C", - to: "D/E/F", - expected: "../../../D/E/F", - }, - TestCase { - from: "A/B/C", - to: "A/D/E", - expected: "../../D/E", - }, -]; - -#[allow(clippy::needless_lifetimes)] -fn convert_path<'a>(path: &'a str) -> Cow<'a, str> { - #[cfg(windows)] - return path.replace('/', "\\").into(); - #[cfg(not(windows))] - return path.into(); -} - -#[test] -fn test_relpath_with_from_no_d() { - let scene = TestScenario::new(util_name!()); - let at = &scene.fixtures; - - for test in &TESTS { - let from: &str = &convert_path(test.from); - let to: &str = &convert_path(test.to); - let expected: &str = &convert_path(test.expected); - - at.mkdir_all(to); - at.mkdir_all(from); - - scene - .ucmd() - .arg(to) - .arg(from) - .succeeds() - .stdout_only(&format!("{expected}\n")); - } -} - -#[test] -fn test_relpath_with_from_with_d() { - let scene = TestScenario::new(util_name!()); - let at = &scene.fixtures; - - for test in &TESTS { - let from: &str = &convert_path(test.from); - let to: &str = &convert_path(test.to); - let pwd = at.as_string(); - at.mkdir_all(to); - at.mkdir_all(from); - - // d is part of subpath -> expect relative path - let mut _result_stdout = scene - .ucmd() - .arg(to) - .arg(from) - .arg(&format!("-d{pwd}")) - .succeeds() - .stdout_move_str(); - // relax rules for windows test environment - #[cfg(not(windows))] - assert!(Path::new(&_result_stdout).is_relative()); - - // d is not part of subpath -> expect absolute path - _result_stdout = scene - .ucmd() - .arg(to) - .arg(from) - .arg("-dnon_existing") // spell-checker:disable-line - .succeeds() - .stdout_move_str(); - assert!(Path::new(&_result_stdout).is_absolute()); - } -} - -#[test] -fn test_relpath_no_from_no_d() { - let scene = TestScenario::new(util_name!()); - let at = &scene.fixtures; - - for test in &TESTS { - let to: &str = &convert_path(test.to); - at.mkdir_all(to); - - let _result_stdout = scene.ucmd().arg(to).succeeds().stdout_move_str(); - #[cfg(not(windows))] - assert_eq!(_result_stdout, format!("{to}\n")); - // relax rules for windows test environment - #[cfg(windows)] - assert!(_result_stdout.ends_with(&format!("{to}\n"))); - } -} - -#[test] -fn test_relpath_no_from_with_d() { - let scene = TestScenario::new(util_name!()); - let at = &scene.fixtures; - - for test in &TESTS { - let to: &str = &convert_path(test.to); - let pwd = at.as_string(); - at.mkdir_all(to); - - // d is part of subpath -> expect relative path - let _result_stdout = scene - .ucmd() - .arg(to) - .arg(&format!("-d{pwd}")) - .succeeds() - .stdout_move_str(); - // relax rules for windows test environment - #[cfg(not(windows))] - assert!(Path::new(&_result_stdout).is_relative()); - - // d is not part of subpath -> expect absolute path - let result_stdout = scene - .ucmd() - .arg(to) - .arg("-dnon_existing") // spell-checker:disable-line - .succeeds() - .stdout_move_str(); - assert!(Path::new(&result_stdout).is_absolute()); - } -} - -#[test] -fn test_relpath_no_to() { - new_ucmd!() - .fails() - .stderr_contains("required arguments were not provided"); -} diff --git a/tests/tests.rs b/tests/tests.rs index 8d4a6855c9e..1fb5735eb71 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -269,10 +269,6 @@ mod test_readlink; #[path = "by-util/test_realpath.rs"] mod test_realpath; -#[cfg(feature = "relpath")] -#[path = "by-util/test_relpath.rs"] -mod test_relpath; - #[cfg(feature = "rm")] #[path = "by-util/test_rm.rs"] mod test_rm; diff --git a/util/show-utils.BAT b/util/show-utils.BAT index 9fa5e2ffae4..f6d9007349c 100644 --- a/util/show-utils.BAT +++ b/util/show-utils.BAT @@ -2,7 +2,7 @@ @echo off @rem ::# spell-checker:ignore (CMD) ERRORLEVEL -@rem ::# spell-checker:ignore (utils) cksum coreutils dircolors hashsum mkdir mktemp printenv printf readlink realpath relpath rmdir shuf tsort unexpand +@rem ::# spell-checker:ignore (utils) cksum coreutils dircolors hashsum mkdir mktemp printenv printf readlink realpath rmdir shuf tsort unexpand @rem ::# spell-checker:ignore (jq) deps startswith set "ME=%~0" @@ -12,7 +12,7 @@ set "ME_parent_dir=%~dp0.\.." @rem refs: , @rem :: default ("Tier 1" cross-platform) utility list -set "default_utils=base32 base64 basename cat cksum comm cp cut date dircolors dirname echo env expand expr factor false fmt fold hashsum head join link ln ls mkdir mktemp more mv nl od paste printenv printf ptx pwd readlink realpath relpath rm rmdir seq shred shuf sleep sort split sum tac tail tee test tr true truncate tsort unexpand uniq wc yes" +set "default_utils=base32 base64 basename cat cksum comm cp cut date dircolors dirname echo env expand expr factor false fmt fold hashsum head join link ln ls mkdir mktemp more mv nl od paste printenv printf ptx pwd readlink realpath rm rmdir seq shred shuf sleep sort split sum tac tail tee test tr true truncate tsort unexpand uniq wc yes" set "project_dir=%ME_parent_dir%" cd "%project_dir%" diff --git a/util/show-utils.sh b/util/show-utils.sh index b6a0f9856d8..dda01abe2d2 100755 --- a/util/show-utils.sh +++ b/util/show-utils.sh @@ -1,6 +1,6 @@ #!/bin/sh -# spell-checker:ignore (utils) cksum coreutils dircolors hashsum mkdir mktemp printenv printf readlink realpath relpath rmdir shuf tsort unexpand +# spell-checker:ignore (utils) cksum coreutils dircolors hashsum mkdir mktemp printenv printf readlink realpath rmdir shuf tsort unexpand # spell-checker:ignore (jq) deps startswith ME="${0}" @@ -12,7 +12,7 @@ ME_parent_dir_abs="$(realpath -mP -- "${ME_parent_dir}" || realpath -- "${ME_par # refs: , # default ("Tier 1" cross-platform) utility list -default_utils="base32 base64 basename cat cksum comm cp cut date dircolors dirname echo env expand expr factor false fmt fold hashsum head join link ln ls mkdir mktemp more mv nl od paste printenv printf ptx pwd readlink realpath relpath rm rmdir seq shred shuf sleep sort split sum tac tail tee test tr true truncate tsort unexpand uniq wc yes" +default_utils="base32 base64 basename cat cksum comm cp cut date dircolors dirname echo env expand expr factor false fmt fold hashsum head join link ln ls mkdir mktemp more mv nl od paste printenv printf ptx pwd readlink realpath rm rmdir seq shred shuf sleep sort split sum tac tail tee test tr true truncate tsort unexpand uniq wc yes" project_main_dir="${ME_parent_dir_abs}" # printf 'project_main_dir="%s"\n' "${project_main_dir}" From b87e1f56764db61beae80970d3a7a52fee53cfa7 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Thu, 5 Oct 2023 09:31:43 +0200 Subject: [PATCH 154/208] chown: move uid & gid detections into their own functions --- src/uu/chown/src/chown.rs | 95 ++++++++++++++++++++------------------- 1 file changed, 49 insertions(+), 46 deletions(-) diff --git a/src/uu/chown/src/chown.rs b/src/uu/chown/src/chown.rs index 8e97d565242..ea25050bb88 100644 --- a/src/uu/chown/src/chown.rs +++ b/src/uu/chown/src/chown.rs @@ -195,6 +195,53 @@ pub fn uu_app() -> Command { ) } +/// Parses the user string to extract the UID. +fn parse_uid(user: &str, spec: &str, sep: char) -> UResult> { + if user.is_empty() { + return Ok(None); + } + match Passwd::locate(user) { + Ok(u) => Ok(Some(u.uid)), // We have been able to get the uid + Err(_) => { + // we have NOT been able to find the uid + // but we could be in the case where we have user.group + if spec.contains('.') && !spec.contains(':') && sep == ':' { + // but the input contains a '.' but not a ':' + // we might have something like username.groupname + // So, try to parse it this way + return parse_spec(spec, '.').map(|(uid, _)| uid); + } else { + // It's possible that the `user` string contains a + // numeric user ID, in which case, we respect that. + match user.parse() { + Ok(uid) => Ok(Some(uid)), + Err(_) => Err(USimpleError::new( + 1, + format!("invalid user: {}", spec.quote()), + )), + } + } + } + } +} + +/// Parses the group string to extract the GID. +fn parse_gid(group: &str, spec: &str) -> UResult> { + if group.is_empty() { + return Ok(None); + } + match Group::locate(group) { + Ok(g) => Ok(Some(g.gid)), + Err(_) => match group.parse() { + Ok(gid) => Ok(Some(gid)), + Err(_) => Err(USimpleError::new( + 1, + format!("invalid group: {}", spec.quote()), + )), + }, + } +} + /// Parse the owner/group specifier string into a user ID and a group ID. /// /// The `spec` can be of the form: @@ -213,52 +260,8 @@ fn parse_spec(spec: &str, sep: char) -> UResult<(Option, Option)> { let user = args.next().unwrap_or(""); let group = args.next().unwrap_or(""); - let uid = if user.is_empty() { - None - } else { - Some(match Passwd::locate(user) { - Ok(u) => u.uid, // We have been able to get the uid - Err(_) => - // we have NOT been able to find the uid - // but we could be in the case where we have user.group - { - if spec.contains('.') && !spec.contains(':') && sep == ':' { - // but the input contains a '.' but not a ':' - // we might have something like username.groupname - // So, try to parse it this way - return parse_spec(spec, '.'); - } else { - // It's possible that the `user` string contains a - // numeric user ID, in which case, we respect that. - match user.parse() { - Ok(uid) => uid, - Err(_) => { - return Err(USimpleError::new( - 1, - format!("invalid user: {}", spec.quote()), - )) - } - } - } - } - }) - }; - let gid = if group.is_empty() { - None - } else { - Some(match Group::locate(group) { - Ok(g) => g.gid, - Err(_) => match group.parse() { - Ok(gid) => gid, - Err(_) => { - return Err(USimpleError::new( - 1, - format!("invalid group: {}", spec.quote()), - )); - } - }, - }) - }; + let uid = parse_uid(user, spec, sep)?; + let gid = parse_gid(group, spec)?; if user.chars().next().map(char::is_numeric).unwrap_or(false) && group.is_empty() From d756a05be6f420b56a96a7d103c37227e720f23e Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Thu, 5 Oct 2023 19:18:00 +0200 Subject: [PATCH 155/208] touch: fix clippy warning - redundant guard --- src/uu/touch/src/touch.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uu/touch/src/touch.rs b/src/uu/touch/src/touch.rs index 6555773eedd..d9399a051f6 100644 --- a/src/uu/touch/src/touch.rs +++ b/src/uu/touch/src/touch.rs @@ -489,7 +489,7 @@ fn pathbuf_from_stdout() -> UResult { format!("GetFinalPathNameByHandleW failed with code {ret}"), )) } - e if e == 0 => { + 0 => { return Err(USimpleError::new( 1, format!( From 7c3de4084294bef465ff38132b60884a7c40c4b3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 6 Oct 2023 05:01:49 +0000 Subject: [PATCH 156/208] chore(deps): update rust crate byteorder to 1.5.0 --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 33ee7ff1477..19ea899d80e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -211,9 +211,9 @@ checksum = "ad152d03a2c813c80bb94fedbf3a3f02b28f793e39e7c214c8a0bcc196343de7" [[package]] name = "byteorder" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cc" diff --git a/Cargo.toml b/Cargo.toml index e7fc2851bd8..83697cf008d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -262,7 +262,7 @@ bigdecimal = "0.4" binary-heap-plus = "0.5.0" bstr = "1.6" bytecount = "0.6.4" -byteorder = "1.4.3" +byteorder = "1.5.0" chrono = { version = "^0.4.31", default-features = false, features = [ "std", "alloc", From 4760b1f340dfe30f23fdc8676211c7ae48b963da Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Fri, 6 Oct 2023 07:49:36 +0200 Subject: [PATCH 157/208] chown: remove unnecessary return --- src/uu/chown/src/chown.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uu/chown/src/chown.rs b/src/uu/chown/src/chown.rs index ea25050bb88..0e9b8b2423c 100644 --- a/src/uu/chown/src/chown.rs +++ b/src/uu/chown/src/chown.rs @@ -209,7 +209,7 @@ fn parse_uid(user: &str, spec: &str, sep: char) -> UResult> { // but the input contains a '.' but not a ':' // we might have something like username.groupname // So, try to parse it this way - return parse_spec(spec, '.').map(|(uid, _)| uid); + parse_spec(spec, '.').map(|(uid, _)| uid) } else { // It's possible that the `user` string contains a // numeric user ID, in which case, we respect that. From 6dd53c7a717daac2dcc5f71f4efcf3c4351e7154 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Fri, 6 Oct 2023 10:11:26 +0200 Subject: [PATCH 158/208] clippy: fix warnings in tests --- tests/by-util/test_cp.rs | 1 - tests/by-util/test_split.rs | 40 ++++++++++++++++++------------------- tests/by-util/test_tee.rs | 17 +++++++++++++--- 3 files changed, 34 insertions(+), 24 deletions(-) diff --git a/tests/by-util/test_cp.rs b/tests/by-util/test_cp.rs index 15713587c7f..9894087e8b2 100644 --- a/tests/by-util/test_cp.rs +++ b/tests/by-util/test_cp.rs @@ -1545,7 +1545,6 @@ fn test_cp_preserve_links_case_7() { .arg("dest") .fails() .stderr_contains("not replacing"); - (); assert!(at.dir_exists("dest")); assert!(at.plus("dest").join("f").exists()); diff --git a/tests/by-util/test_split.rs b/tests/by-util/test_split.rs index 6fc3a370613..ce80844cf3e 100644 --- a/tests/by-util/test_split.rs +++ b/tests/by-util/test_split.rs @@ -438,7 +438,7 @@ fn test_split_obs_lines_within_combined_shorts() { let scene = TestScenario::new(util_name!()); let at = &scene.fixtures; let name = "obs-lines-within-shorts"; - RandomFile::new(&at, name).add_lines(400); + RandomFile::new(at, name).add_lines(400); scene .ucmd() @@ -446,9 +446,9 @@ fn test_split_obs_lines_within_combined_shorts() { .succeeds() .no_stderr() .no_stdout(); - let glob = Glob::new(&at, ".", r"x\d\d$"); + let glob = Glob::new(at, ".", r"x\d\d$"); assert_eq!(glob.count(), 2); - assert_eq!(glob.collate(), at.read_bytes(name)) + assert_eq!(glob.collate(), at.read_bytes(name)); } /// Test for obsolete lines option as part of combined short options with tailing suffix length with value @@ -470,7 +470,7 @@ fn test_split_obs_lines_starts_combined_shorts() { let scene = TestScenario::new(util_name!()); let at = &scene.fixtures; let name = "obs-lines-starts-shorts"; - RandomFile::new(&at, name).add_lines(400); + RandomFile::new(at, name).add_lines(400); scene .ucmd() @@ -478,9 +478,9 @@ fn test_split_obs_lines_starts_combined_shorts() { .succeeds() .no_stderr() .no_stdout(); - let glob = Glob::new(&at, ".", r"x\d\d$"); + let glob = Glob::new(at, ".", r"x\d\d$"); assert_eq!(glob.count(), 2); - assert_eq!(glob.collate(), at.read_bytes(name)) + assert_eq!(glob.collate(), at.read_bytes(name)); } /// Test for using both obsolete lines (standalone) option and short/long lines option simultaneously @@ -585,7 +585,7 @@ fn test_split_multiple_obs_lines_standalone() { let scene = TestScenario::new(util_name!()); let at = &scene.fixtures; let name = "multiple-obs-lines"; - RandomFile::new(&at, name).add_lines(400); + RandomFile::new(at, name).add_lines(400); scene .ucmd() @@ -593,9 +593,9 @@ fn test_split_multiple_obs_lines_standalone() { .succeeds() .no_stderr() .no_stdout(); - let glob = Glob::new(&at, ".", r"x[[:alpha:]][[:alpha:]]$"); + let glob = Glob::new(at, ".", r"x[[:alpha:]][[:alpha:]]$"); assert_eq!(glob.count(), 2); - assert_eq!(glob.collate(), at.read_bytes(name)) + assert_eq!(glob.collate(), at.read_bytes(name)); } /// Test for using more than one obsolete lines option within combined shorts @@ -605,7 +605,7 @@ fn test_split_multiple_obs_lines_within_combined() { let scene = TestScenario::new(util_name!()); let at = &scene.fixtures; let name = "multiple-obs-lines"; - RandomFile::new(&at, name).add_lines(400); + RandomFile::new(at, name).add_lines(400); scene .ucmd() @@ -613,9 +613,9 @@ fn test_split_multiple_obs_lines_within_combined() { .succeeds() .no_stderr() .no_stdout(); - let glob = Glob::new(&at, ".", r"x\d\d$"); + let glob = Glob::new(at, ".", r"x\d\d$"); assert_eq!(glob.count(), 2); - assert_eq!(glob.collate(), at.read_bytes(name)) + assert_eq!(glob.collate(), at.read_bytes(name)); } /// Test for using both obsolete lines option within combined shorts with conflicting -n option simultaneously @@ -1543,8 +1543,8 @@ fn test_split_separator_nul_lines() { ucmd.args(&["--lines=2", "-t", "\\0", "separator_nul.txt"]) .succeeds(); - assert_eq!(file_read(&at, "xaa"), "1\02\0"); - assert_eq!(file_read(&at, "xab"), "3\04\0"); + assert_eq!(file_read(&at, "xaa"), "1\x002\0"); + assert_eq!(file_read(&at, "xab"), "3\x004\0"); assert_eq!(file_read(&at, "xac"), "5\0"); assert!(!at.plus("xad").exists()); } @@ -1555,8 +1555,8 @@ fn test_split_separator_nul_line_bytes() { ucmd.args(&["--line-bytes=4", "-t", "\\0", "separator_nul.txt"]) .succeeds(); - assert_eq!(file_read(&at, "xaa"), "1\02\0"); - assert_eq!(file_read(&at, "xab"), "3\04\0"); + assert_eq!(file_read(&at, "xaa"), "1\x002\0"); + assert_eq!(file_read(&at, "xab"), "3\x004\0"); assert_eq!(file_read(&at, "xac"), "5\0"); assert!(!at.plus("xad").exists()); } @@ -1567,8 +1567,8 @@ fn test_split_separator_nul_number_l() { ucmd.args(&["--number=l/3", "--separator=\\0", "separator_nul.txt"]) .succeeds(); - assert_eq!(file_read(&at, "xaa"), "1\02\0"); - assert_eq!(file_read(&at, "xab"), "3\04\0"); + assert_eq!(file_read(&at, "xaa"), "1\x002\0"); + assert_eq!(file_read(&at, "xab"), "3\x004\0"); assert_eq!(file_read(&at, "xac"), "5\0"); assert!(!at.plus("xad").exists()); } @@ -1579,8 +1579,8 @@ fn test_split_separator_nul_number_r() { ucmd.args(&["--number=r/3", "--separator=\\0", "separator_nul.txt"]) .succeeds(); - assert_eq!(file_read(&at, "xaa"), "1\04\0"); - assert_eq!(file_read(&at, "xab"), "2\05\0"); + assert_eq!(file_read(&at, "xaa"), "1\x004\0"); + assert_eq!(file_read(&at, "xab"), "2\x005\0"); assert_eq!(file_read(&at, "xac"), "3\0"); assert!(!at.plus("xad").exists()); } diff --git a/tests/by-util/test_tee.rs b/tests/by-util/test_tee.rs index 6f04edfc37a..2b3fd2670ab 100644 --- a/tests/by-util/test_tee.rs +++ b/tests/by-util/test_tee.rs @@ -3,6 +3,7 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. use crate::common::util::TestScenario; +use std::fmt::Write; // tests for basic tee functionality. // inspired by: @@ -74,7 +75,10 @@ fn test_tee_append() { fn test_tee_no_more_writeable_1() { // equals to 'tee /dev/full out2 (); + let content = (1..=10).fold(String::new(), |mut output, x| { + let _ = writeln!(output, "{x}"); + output + }); let file_out = "tee_file_out"; ucmd.arg("/dev/full") @@ -94,7 +98,10 @@ fn test_tee_no_more_writeable_2() { // but currently there is no way to redirect stdout to /dev/full // so this test is disabled let (_at, mut ucmd) = at_and_ucmd!(); - let _content = (1..=10).map(|x| format!("{x}\n")).collect::(); + let _content = (1..=10).fold(String::new(), |mut output, x| { + let _ = writeln!(output, "{x}"); + output + }); let file_out_a = "tee_file_out_a"; let file_out_b = "tee_file_out_b"; @@ -114,6 +121,7 @@ fn test_tee_no_more_writeable_2() { mod linux_only { use crate::common::util::{AtPath, TestScenario, UCommand}; + use std::fmt::Write; use std::fs::File; use std::process::{Output, Stdio}; @@ -135,7 +143,10 @@ mod linux_only { } fn run_tee(proc: &mut UCommand) -> (String, Output) { - let content = (1..=100_000).map(|x| format!("{x}\n")).collect::(); + let content = (1..=100_000).fold(String::new(), |mut output, x| { + let _ = writeln!(output, "{x}"); + output + }); #[allow(deprecated)] let output = proc From fa2a14ccd262f102ca04e47529d5f79a556a1692 Mon Sep 17 00:00:00 2001 From: Zhuoxun Yang Date: Fri, 6 Oct 2023 22:55:30 +0800 Subject: [PATCH 159/208] expr: unify debug message --- src/uu/expr/src/syntax_tree.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uu/expr/src/syntax_tree.rs b/src/uu/expr/src/syntax_tree.rs index b3cd329ba6e..335405d06b2 100644 --- a/src/uu/expr/src/syntax_tree.rs +++ b/src/uu/expr/src/syntax_tree.rs @@ -52,7 +52,7 @@ impl AstNode { operands, } => { println!( - "Node( {} ) at #{} (evaluate -> {:?})", + "Node( {} ) at #{} ( evaluate -> {:?} )", op_type, token_idx, self.evaluate() From 6091bafe08178f88f57aed9d28a1e1dda659bfd4 Mon Sep 17 00:00:00 2001 From: PThorpe92 Date: Mon, 25 Sep 2023 18:24:20 -0400 Subject: [PATCH 160/208] feat(mv): expose functionality, document for nushell --- src/uu/mv/src/mv.rs | 171 +++++++++++++++++++++++++++++--------------- 1 file changed, 113 insertions(+), 58 deletions(-) diff --git a/src/uu/mv/src/mv.rs b/src/uu/mv/src/mv.rs index 43f8eb6b651..267179bbf1b 100644 --- a/src/uu/mv/src/mv.rs +++ b/src/uu/mv/src/mv.rs @@ -3,7 +3,7 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -// spell-checker:ignore (ToDO) sourcepath targetpath +// spell-checker:ignore (ToDO) sourcepath targetpath nushell mod error; @@ -23,6 +23,7 @@ use uucore::backup_control::{self, source_is_target_backup, BackupMode}; use uucore::display::Quotable; use uucore::error::{set_exit_code, FromIo, UError, UResult, USimpleError, UUsageError}; use uucore::fs::{are_hardlinks_or_one_way_symlink_to_same_file, are_hardlinks_to_same_file}; +use uucore::libc::ENOTEMPTY; use uucore::update_control::{self, UpdateMode}; use uucore::{format_usage, help_about, help_section, help_usage, prompt_yes, show}; @@ -32,23 +33,56 @@ use fs_extra::dir::{ }; use crate::error::MvError; - -pub struct Behavior { - overwrite: OverwriteMode, - backup: BackupMode, - suffix: String, - update: UpdateMode, - target_dir: Option, - no_target_dir: bool, - verbose: bool, - strip_slashes: bool, - progress_bar: bool, +/// Options contains all the possible behaviors and flags for mv. +/// +/// All options are public so that the options can be programmatically +/// constructed by other crates, such as nushell. That means that this struct +/// is part of our public API. It should therefore not be changed without good +/// reason. +/// The fields are documented with the arguments that determine their value. +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct Options { + /// specifies overwrite behavior + /// '-n' '--no-clobber' + /// '-i' '--interactive' + /// '-f' '--force' + pub overwrite: OverwriteMode, + + /// `--backup[=CONTROL]`, `-b` + pub backup: BackupMode, + + /// '-S' --suffix' backup suffix + pub suffix: String, + + /// Available update mode "--update-mode=all|none|older" + pub update: UpdateMode, + + /// Specifies target directory + /// '-t, --target-directory=DIRECTORY' + pub target_dir: Option, + + /// Treat destination as a normal file + /// '-T, --no-target-directory + pub no_target_dir: bool, + + /// '-v, --verbose' + pub verbose: bool, + + /// '--strip-trailing-slashes' + pub strip_slashes: bool, + + /// '-g, --progress' + pub progress_bar: bool, } -#[derive(Clone, Eq, PartialEq)] +/// specifies behavior of the overwrite flag +#[derive(Clone, Debug, Eq, PartialEq)] pub enum OverwriteMode { + /// '-n' '--no-clobber' do not overwrite NoClobber, + /// '-i' '--interactive' prompt before overwrite Interactive, + ///'-f' '--force' overwrite without prompt Force, } @@ -116,7 +150,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { } } - let behavior = Behavior { + let opts = Options { overwrite: overwrite_mode, backup: backup_mode, suffix: backup_suffix, @@ -128,7 +162,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { progress_bar: matches.get_flag(OPT_PROGRESS), }; - exec(&files[..], &behavior) + exec_mv(&files[..], &opts) } pub fn uu_app() -> Command { @@ -220,7 +254,7 @@ pub fn uu_app() -> Command { ) } -fn determine_overwrite_mode(matches: &ArgMatches) -> OverwriteMode { +pub fn determine_overwrite_mode(matches: &ArgMatches) -> OverwriteMode { // This does not exactly match the GNU implementation: // The GNU mv defaults to Force, but if more than one of the // overwrite options are supplied, only the last takes effect. @@ -235,10 +269,10 @@ fn determine_overwrite_mode(matches: &ArgMatches) -> OverwriteMode { } } -fn parse_paths(files: &[OsString], b: &Behavior) -> Vec { +fn parse_paths(files: &[OsString], opts: &Options) -> Vec { let paths = files.iter().map(Path::new); - if b.strip_slashes { + if opts.strip_slashes { paths .map(|p| p.components().as_path().to_owned()) .collect::>() @@ -247,8 +281,10 @@ fn parse_paths(files: &[OsString], b: &Behavior) -> Vec { } } -fn handle_two_paths(source: &Path, target: &Path, b: &Behavior) -> UResult<()> { - if b.backup == BackupMode::SimpleBackup && source_is_target_backup(source, target, &b.suffix) { +fn handle_two_paths(source: &Path, target: &Path, opts: &Options) -> UResult<()> { + if opts.backup == BackupMode::SimpleBackup + && source_is_target_backup(source, target, &opts.suffix) + { return Err(io::Error::new( io::ErrorKind::NotFound, format!( @@ -266,7 +302,7 @@ fn handle_two_paths(source: &Path, target: &Path, b: &Behavior) -> UResult<()> { if (source.eq(target) || are_hardlinks_to_same_file(source, target) || are_hardlinks_or_one_way_symlink_to_same_file(source, target)) - && b.backup == BackupMode::NoBackup + && opts.backup == BackupMode::NoBackup { if source.eq(Path::new(".")) || source.ends_with("/.") || source.is_file() { return Err( @@ -278,19 +314,19 @@ fn handle_two_paths(source: &Path, target: &Path, b: &Behavior) -> UResult<()> { } if target.is_dir() { - if b.no_target_dir { + if opts.no_target_dir { if source.is_dir() { - rename(source, target, b, None).map_err_context(|| { + rename(source, target, opts, None).map_err_context(|| { format!("cannot move {} to {}", source.quote(), target.quote()) }) } else { Err(MvError::DirectoryToNonDirectory(target.quote().to_string()).into()) } } else { - move_files_into_dir(&[source.to_path_buf()], target, b) + move_files_into_dir(&[source.to_path_buf()], target, opts) } } else if target.exists() && source.is_dir() { - match b.overwrite { + match opts.overwrite { OverwriteMode::NoClobber => return Ok(()), OverwriteMode::Interactive => { if !prompt_yes!("overwrite {}? ", target.quote()) { @@ -305,12 +341,12 @@ fn handle_two_paths(source: &Path, target: &Path, b: &Behavior) -> UResult<()> { ) .into()) } else { - rename(source, target, b, None).map_err(|e| USimpleError::new(1, format!("{e}"))) + rename(source, target, opts, None).map_err(|e| USimpleError::new(1, format!("{e}"))) } } -fn handle_multiple_paths(paths: &[PathBuf], b: &Behavior) -> UResult<()> { - if b.no_target_dir { +fn handle_multiple_paths(paths: &[PathBuf], opts: &Options) -> UResult<()> { + if opts.no_target_dir { return Err(UUsageError::new( 1, format!("mv: extra operand {}", paths[2].quote()), @@ -319,24 +355,29 @@ fn handle_multiple_paths(paths: &[PathBuf], b: &Behavior) -> UResult<()> { let target_dir = paths.last().unwrap(); let sources = &paths[..paths.len() - 1]; - move_files_into_dir(sources, target_dir, b) + move_files_into_dir(sources, target_dir, opts) } -fn exec(files: &[OsString], b: &Behavior) -> UResult<()> { - let paths = parse_paths(files, b); +/// Execute mv command, moving 'source' to 'target', where +/// 'target' is a directory. If 'target' does not exist, and source is a single +/// file or directory, then 'source' will be renamed to 'target'. +/// +/// returns MvError | UError +pub fn exec_mv(files: &[OsString], opts: &Options) -> UResult<()> { + let paths = parse_paths(files, opts); - if let Some(ref name) = b.target_dir { - return move_files_into_dir(&paths, &PathBuf::from(name), b); + if let Some(ref name) = opts.target_dir { + return move_files_into_dir(&paths, &PathBuf::from(name), opts); } match paths.len() { - 2 => handle_two_paths(&paths[0], &paths[1], b), - _ => handle_multiple_paths(&paths, b), + 2 => handle_two_paths(&paths[0], &paths[1], opts), + _ => handle_multiple_paths(&paths, opts), } } #[allow(clippy::cognitive_complexity)] -fn move_files_into_dir(files: &[PathBuf], target_dir: &Path, b: &Behavior) -> UResult<()> { +fn move_files_into_dir(files: &[PathBuf], target_dir: &Path, opts: &Options) -> UResult<()> { if !target_dir.is_dir() { return Err(MvError::NotADirectory(target_dir.quote().to_string()).into()); } @@ -345,7 +386,7 @@ fn move_files_into_dir(files: &[PathBuf], target_dir: &Path, b: &Behavior) -> UR .canonicalize() .unwrap_or_else(|_| target_dir.to_path_buf()); - let multi_progress = b.progress_bar.then(MultiProgress::new); + let multi_progress = opts.progress_bar.then(MultiProgress::new); let count_progress = if let Some(ref multi_progress) = multi_progress { if files.len() > 1 { @@ -396,24 +437,37 @@ fn move_files_into_dir(files: &[PathBuf], target_dir: &Path, b: &Behavior) -> UR } } - match rename(sourcepath, &targetpath, b, multi_progress.as_ref()) { + match rename(sourcepath, &targetpath, opts, multi_progress.as_ref()) { Err(e) if e.to_string().is_empty() => set_exit_code(1), Err(e) => { - let e = e.map_err_context(|| { - format!( - "cannot move {} to {}", - sourcepath.quote(), - targetpath.quote() - ) - }); - match multi_progress { - Some(ref pb) => pb.suspend(|| show!(e)), - None => show!(e), - }; + match e.raw_os_error() { + Some(ENOTEMPTY) => { + // The error message was changed to match GNU's decision + // when an issue was filed. These will match when merged upstream. + let e = e + .map_err_context(|| format!("cannot overwrite {}", targetpath.quote())); + match multi_progress { + Some(ref pb) => pb.suspend(|| show!(e)), + None => show!(e), + }; + } + _ => { + let e = e.map_err_context(|| { + format!( + "cannot move {} to {}", + sourcepath.quote(), + targetpath.quote() + ) + }); + match multi_progress { + Some(ref pb) => pb.suspend(|| show!(e)), + None => show!(e), + }; + } + } } Ok(()) => (), } - if let Some(ref pb) = count_progress { pb.inc(1); } @@ -424,29 +478,30 @@ fn move_files_into_dir(files: &[PathBuf], target_dir: &Path, b: &Behavior) -> UR fn rename( from: &Path, to: &Path, - b: &Behavior, + opts: &Options, multi_progress: Option<&MultiProgress>, ) -> io::Result<()> { let mut backup_path = None; if to.exists() { - if b.update == UpdateMode::ReplaceIfOlder && b.overwrite == OverwriteMode::Interactive { + if opts.update == UpdateMode::ReplaceIfOlder && opts.overwrite == OverwriteMode::Interactive + { // `mv -i --update old new` when `new` exists doesn't move anything // and exit with 0 return Ok(()); } - if b.update == UpdateMode::ReplaceNone { + if opts.update == UpdateMode::ReplaceNone { return Ok(()); } - if (b.update == UpdateMode::ReplaceIfOlder) + if (opts.update == UpdateMode::ReplaceIfOlder) && fs::metadata(from)?.modified()? <= fs::metadata(to)?.modified()? { return Ok(()); } - match b.overwrite { + match opts.overwrite { OverwriteMode::NoClobber => { let err_msg = format!("not replacing {}", to.quote()); return Err(io::Error::new(io::ErrorKind::Other, err_msg)); @@ -459,7 +514,7 @@ fn rename( OverwriteMode::Force => {} }; - backup_path = backup_control::get_backup_path(b.backup, to, &b.suffix); + backup_path = backup_control::get_backup_path(opts.backup, to, &opts.suffix); if let Some(ref backup_path) = backup_path { rename_with_fallback(to, backup_path, multi_progress)?; } @@ -472,14 +527,14 @@ fn rename( if is_empty_dir(to) { fs::remove_dir(to)?; } else { - return Err(io::Error::new(io::ErrorKind::Other, "Directory not empty")); + return Err(io::Error::from_raw_os_error(ENOTEMPTY)); } } } rename_with_fallback(from, to, multi_progress)?; - if b.verbose { + if opts.verbose { let message = match backup_path { Some(path) => format!( "renamed {} -> {} (backup: {})", From 4a44a106a0442cf53f02efb2fc317a9b695125e9 Mon Sep 17 00:00:00 2001 From: PThorpe92 Date: Thu, 28 Sep 2023 15:11:48 -0400 Subject: [PATCH 161/208] feat: expose mv externals + document for nushell --- src/uu/mv/src/mv.rs | 59 +++++++++++++++++---------------------------- 1 file changed, 22 insertions(+), 37 deletions(-) diff --git a/src/uu/mv/src/mv.rs b/src/uu/mv/src/mv.rs index 267179bbf1b..9888389aefe 100644 --- a/src/uu/mv/src/mv.rs +++ b/src/uu/mv/src/mv.rs @@ -19,11 +19,11 @@ use std::os::unix; #[cfg(windows)] use std::os::windows; use std::path::{Path, PathBuf}; -use uucore::backup_control::{self, source_is_target_backup, BackupMode}; +pub use uucore::backup_control::BackupMode; +use uucore::backup_control::{self, source_is_target_backup}; use uucore::display::Quotable; use uucore::error::{set_exit_code, FromIo, UError, UResult, USimpleError, UUsageError}; use uucore::fs::{are_hardlinks_or_one_way_symlink_to_same_file, are_hardlinks_to_same_file}; -use uucore::libc::ENOTEMPTY; use uucore::update_control::{self, UpdateMode}; use uucore::{format_usage, help_about, help_section, help_usage, prompt_yes, show}; @@ -33,12 +33,13 @@ use fs_extra::dir::{ }; use crate::error::MvError; + /// Options contains all the possible behaviors and flags for mv. /// /// All options are public so that the options can be programmatically -/// constructed by other crates, such as nushell. That means that this struct -/// is part of our public API. It should therefore not be changed without good -/// reason. +/// constructed by other crates, such as nushell. That means that this struct is +/// part of our public API. It should therefore not be changed without good reason. +/// /// The fields are documented with the arguments that determine their value. #[derive(Debug, Clone, Eq, PartialEq)] pub struct Options { @@ -162,7 +163,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { progress_bar: matches.get_flag(OPT_PROGRESS), }; - exec_mv(&files[..], &opts) + mv(&files[..], &opts) } pub fn uu_app() -> Command { @@ -254,7 +255,7 @@ pub fn uu_app() -> Command { ) } -pub fn determine_overwrite_mode(matches: &ArgMatches) -> OverwriteMode { +fn determine_overwrite_mode(matches: &ArgMatches) -> OverwriteMode { // This does not exactly match the GNU implementation: // The GNU mv defaults to Force, but if more than one of the // overwrite options are supplied, only the last takes effect. @@ -358,12 +359,10 @@ fn handle_multiple_paths(paths: &[PathBuf], opts: &Options) -> UResult<()> { move_files_into_dir(sources, target_dir, opts) } -/// Execute mv command, moving 'source' to 'target', where +/// Execute the mv command. This moves 'source' to 'target', where /// 'target' is a directory. If 'target' does not exist, and source is a single /// file or directory, then 'source' will be renamed to 'target'. -/// -/// returns MvError | UError -pub fn exec_mv(files: &[OsString], opts: &Options) -> UResult<()> { +pub fn mv(files: &[OsString], opts: &Options) -> UResult<()> { let paths = parse_paths(files, opts); if let Some(ref name) = opts.target_dir { @@ -440,31 +439,17 @@ fn move_files_into_dir(files: &[PathBuf], target_dir: &Path, opts: &Options) -> match rename(sourcepath, &targetpath, opts, multi_progress.as_ref()) { Err(e) if e.to_string().is_empty() => set_exit_code(1), Err(e) => { - match e.raw_os_error() { - Some(ENOTEMPTY) => { - // The error message was changed to match GNU's decision - // when an issue was filed. These will match when merged upstream. - let e = e - .map_err_context(|| format!("cannot overwrite {}", targetpath.quote())); - match multi_progress { - Some(ref pb) => pb.suspend(|| show!(e)), - None => show!(e), - }; - } - _ => { - let e = e.map_err_context(|| { - format!( - "cannot move {} to {}", - sourcepath.quote(), - targetpath.quote() - ) - }); - match multi_progress { - Some(ref pb) => pb.suspend(|| show!(e)), - None => show!(e), - }; - } - } + let e = e.map_err_context(|| { + format!( + "cannot move {} to {}", + sourcepath.quote(), + targetpath.quote() + ) + }); + match multi_progress { + Some(ref pb) => pb.suspend(|| show!(e)), + None => show!(e), + }; } Ok(()) => (), } @@ -527,7 +512,7 @@ fn rename( if is_empty_dir(to) { fs::remove_dir(to)?; } else { - return Err(io::Error::from_raw_os_error(ENOTEMPTY)); + return Err(io::Error::new(io::ErrorKind::Other, "Directory not empty")); } } } From 2c2e01205cfbe4b6337a207102485fd2dc64e443 Mon Sep 17 00:00:00 2001 From: Zhuoxun Yang Date: Fri, 6 Oct 2023 23:50:22 +0800 Subject: [PATCH 162/208] expr: short-circuit evaluation for | --- src/uu/expr/src/syntax_tree.rs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/uu/expr/src/syntax_tree.rs b/src/uu/expr/src/syntax_tree.rs index 335405d06b2..ad421edb7f3 100644 --- a/src/uu/expr/src/syntax_tree.rs +++ b/src/uu/expr/src/syntax_tree.rs @@ -155,8 +155,24 @@ impl AstNode { } } pub fn operand_values(&self) -> Result, String> { - if let Self::Node { operands, .. } = self { + if let Self::Node { + operands, op_type, .. + } = self + { let mut out = Vec::with_capacity(operands.len()); + let mut operands = operands.iter(); + + if op_type == "|" { + if let Some(value) = operands.next() { + let value = value.evaluate()?; + out.push(value.clone()); + if value_as_bool(&value) { + out.push(String::from("dummy")); + return Ok(out); + } + } + } + for operand in operands { let value = operand.evaluate()?; out.push(value); From 5a732dd21a9aa0987f973eda4ffe919b9985be29 Mon Sep 17 00:00:00 2001 From: Zhuoxun Yang Date: Fri, 6 Oct 2023 23:50:44 +0800 Subject: [PATCH 163/208] tests/expr: add test expr 1 \| a / 5 --- tests/by-util/test_expr.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/by-util/test_expr.rs b/tests/by-util/test_expr.rs index ea5a964d999..99445d56641 100644 --- a/tests/by-util/test_expr.rs +++ b/tests/by-util/test_expr.rs @@ -123,6 +123,11 @@ fn test_or() { .args(&["-14", "|", "1"]) .succeeds() .stdout_only("-14\n"); + + new_ucmd!() + .args(&["1", "|", "a", "/", "5"]) + .succeeds() + .stdout_only("1\n"); } #[test] From 0df561e256905c9477bd316b68a9fb56d36a47c6 Mon Sep 17 00:00:00 2001 From: Luv_Ray Date: Sat, 7 Oct 2023 00:19:56 +0800 Subject: [PATCH 164/208] expr: add comment in syntax_tree.rs --- src/uu/expr/src/syntax_tree.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/uu/expr/src/syntax_tree.rs b/src/uu/expr/src/syntax_tree.rs index ad421edb7f3..eaa6be1f51e 100644 --- a/src/uu/expr/src/syntax_tree.rs +++ b/src/uu/expr/src/syntax_tree.rs @@ -161,7 +161,8 @@ impl AstNode { { let mut out = Vec::with_capacity(operands.len()); let mut operands = operands.iter(); - + // check the first value before `|`, stop evaluate and return directly if it is true. + // push dummy to pass the check of `len() == 2` if op_type == "|" { if let Some(value) = operands.next() { let value = value.evaluate()?; From 7bf4b7f6748812e29daf3fb91180e4dfceb6ce30 Mon Sep 17 00:00:00 2001 From: Luv_Ray Date: Sat, 7 Oct 2023 00:42:04 +0800 Subject: [PATCH 165/208] tests/expr: add tests in test_expr.rs --- tests/by-util/test_expr.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/by-util/test_expr.rs b/tests/by-util/test_expr.rs index 99445d56641..a463f086b72 100644 --- a/tests/by-util/test_expr.rs +++ b/tests/by-util/test_expr.rs @@ -128,6 +128,16 @@ fn test_or() { .args(&["1", "|", "a", "/", "5"]) .succeeds() .stdout_only("1\n"); + + new_ucmd!() + .args(&["foo", "|", "a", "/", "5"]) + .succeeds() + .stdout_only("foo\n"); + + new_ucmd!() + .args(&["0", "|", "10", "/", "5"]) + .succeeds() + .stdout_only("2\n"); } #[test] From 98cfb0c322b33d571647ba7c0ac6831624266eae Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Fri, 6 Oct 2023 22:08:04 +0200 Subject: [PATCH 166/208] Binary sizes: handle when 0 (relpath removal) --- .github/workflows/CICD.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index a9f04e3cf1d..61c12dad0cd 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -610,6 +610,12 @@ jobs: check() { # Warn if the size increases by more than 5% threshold='1.05' + + if [[ "$2" -eq 0 || "$3" -eq 0 ]]; then + echo "::warning file=$4::Invalid size for $1. Sizes cannot be 0." + return + fi + ratio=$(jq -n "$2 / $3") echo "$1: size=$2, previous_size=$3, ratio=$ratio, threshold=$threshold" if [[ "$(jq -n "$ratio > $threshold")" == 'true' ]]; then From e5d70d444ae7ca3a3100190ea87c22edfecab121 Mon Sep 17 00:00:00 2001 From: Zhuoxun Yang Date: Sat, 7 Oct 2023 10:41:10 +0800 Subject: [PATCH 167/208] tests/expr: format --- tests/by-util/test_expr.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/by-util/test_expr.rs b/tests/by-util/test_expr.rs index a463f086b72..4637d51c739 100644 --- a/tests/by-util/test_expr.rs +++ b/tests/by-util/test_expr.rs @@ -128,7 +128,7 @@ fn test_or() { .args(&["1", "|", "a", "/", "5"]) .succeeds() .stdout_only("1\n"); - + new_ucmd!() .args(&["foo", "|", "a", "/", "5"]) .succeeds() From 959bfa0160165b8ed23f1db4cd9ea43264f3aadd Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 7 Oct 2023 02:43:46 +0000 Subject: [PATCH 168/208] chore(deps): update rust crate libc to 0.2.149 --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a2c872e2d93..a3c3e709627 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1200,9 +1200,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.148" +version = "0.2.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" +checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" [[package]] name = "libloading" diff --git a/Cargo.toml b/Cargo.toml index 783c84ccf0b..e59ed7bd7b1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -286,7 +286,7 @@ glob = "0.3.1" half = "2.3" indicatif = "0.17" itertools = "0.11.0" -libc = "0.2.148" +libc = "0.2.149" lscolors = { version = "0.15.0", default-features = false, features = [ "nu-ansi-term", ] } From e1b7f254a63a00b1b33a8842ca6280b183df8eb6 Mon Sep 17 00:00:00 2001 From: Sanpi Date: Sat, 7 Oct 2023 10:39:19 +0200 Subject: [PATCH 169/208] ls: fix panic when file removed too quickly Fixes #5371 --- src/uu/ls/src/ls.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/uu/ls/src/ls.rs b/src/uu/ls/src/ls.rs index 301aefaf3a3..c80d4cbe641 100644 --- a/src/uu/ls/src/ls.rs +++ b/src/uu/ls/src/ls.rs @@ -2888,7 +2888,11 @@ fn classify_file(path: &PathData, out: &mut BufWriter) -> Option { Some('=') } else if file_type.is_fifo() { Some('|') - } else if file_type.is_file() && file_is_executable(path.md(out).as_ref().unwrap()) { + } else if file_type.is_file() + // Safe unwrapping if the file was removed between listing and display + // See https://github.com/uutils/coreutils/issues/5371 + && path.md(out).map(file_is_executable).unwrap_or_default() + { Some('*') } else { None From 3e1d3cacebe447dfa4efa8d5b722abc9ad0bca3d Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sat, 7 Oct 2023 13:37:24 +0200 Subject: [PATCH 170/208] head: remove clippy::cognitive_complexity by moving some content in a function (#5366) * head: remove a clippy::cognitive_complexity by moving some content into a function * Remove duplicate comment --- src/uu/head/src/parse.rs | 129 +++++++++++++++++++++------------------ 1 file changed, 69 insertions(+), 60 deletions(-) diff --git a/src/uu/head/src/parse.rs b/src/uu/head/src/parse.rs index 90e1f2ce008..179435c7ae5 100644 --- a/src/uu/head/src/parse.rs +++ b/src/uu/head/src/parse.rs @@ -13,7 +13,6 @@ pub enum ParseError { } /// Parses obsolete syntax /// head -NUM\[kmzv\] // spell-checker:disable-line -#[allow(clippy::cognitive_complexity)] pub fn parse_obsolete(src: &str) -> Option, ParseError>> { let mut chars = src.char_indices(); if let Some((_, '-')) = chars.next() { @@ -30,65 +29,7 @@ pub fn parse_obsolete(src: &str) -> Option } } if has_num { - match src[1..=num_end].parse::() { - Ok(num) => { - let mut quiet = false; - let mut verbose = false; - let mut zero_terminated = false; - let mut multiplier = None; - let mut c = last_char; - loop { - // not that here, we only match lower case 'k', 'c', and 'm' - match c { - // we want to preserve order - // this also saves us 1 heap allocation - 'q' => { - quiet = true; - verbose = false; - } - 'v' => { - verbose = true; - quiet = false; - } - 'z' => zero_terminated = true, - 'c' => multiplier = Some(1), - 'b' => multiplier = Some(512), - 'k' => multiplier = Some(1024), - 'm' => multiplier = Some(1024 * 1024), - '\0' => {} - _ => return Some(Err(ParseError::Syntax)), - } - if let Some((_, next)) = chars.next() { - c = next; - } else { - break; - } - } - let mut options = Vec::new(); - if quiet { - options.push(OsString::from("-q")); - } - if verbose { - options.push(OsString::from("-v")); - } - if zero_terminated { - options.push(OsString::from("-z")); - } - if let Some(n) = multiplier { - options.push(OsString::from("-c")); - let num = match num.checked_mul(n) { - Some(n) => n, - None => return Some(Err(ParseError::Overflow)), - }; - options.push(OsString::from(format!("{num}"))); - } else { - options.push(OsString::from("-n")); - options.push(OsString::from(format!("{num}"))); - } - Some(Ok(options.into_iter())) - } - Err(_) => Some(Err(ParseError::Overflow)), - } + process_num_block(&src[1..=num_end], last_char, &mut chars) } else { None } @@ -96,6 +37,74 @@ pub fn parse_obsolete(src: &str) -> Option None } } + +/// Processes the numeric block of the input string to generate the appropriate options. +fn process_num_block( + src: &str, + last_char: char, + chars: &mut std::str::CharIndices, +) -> Option, ParseError>> { + match src.parse::() { + Ok(num) => { + let mut quiet = false; + let mut verbose = false; + let mut zero_terminated = false; + let mut multiplier = None; + let mut c = last_char; + loop { + // note that here, we only match lower case 'k', 'c', and 'm' + match c { + // we want to preserve order + // this also saves us 1 heap allocation + 'q' => { + quiet = true; + verbose = false; + } + 'v' => { + verbose = true; + quiet = false; + } + 'z' => zero_terminated = true, + 'c' => multiplier = Some(1), + 'b' => multiplier = Some(512), + 'k' => multiplier = Some(1024), + 'm' => multiplier = Some(1024 * 1024), + '\0' => {} + _ => return Some(Err(ParseError::Syntax)), + } + if let Some((_, next)) = chars.next() { + c = next; + } else { + break; + } + } + let mut options = Vec::new(); + if quiet { + options.push(OsString::from("-q")); + } + if verbose { + options.push(OsString::from("-v")); + } + if zero_terminated { + options.push(OsString::from("-z")); + } + if let Some(n) = multiplier { + options.push(OsString::from("-c")); + let num = match num.checked_mul(n) { + Some(n) => n, + None => return Some(Err(ParseError::Overflow)), + }; + options.push(OsString::from(format!("{num}"))); + } else { + options.push(OsString::from("-n")); + options.push(OsString::from(format!("{num}"))); + } + Some(Ok(options.into_iter())) + } + Err(_) => Some(Err(ParseError::Overflow)), + } +} + /// Parses an -c or -n argument, /// the bool specifies whether to read from the end pub fn parse_num(src: &str) -> Result<(u64, bool), ParseSizeError> { From 2ba7400d05ec32320acdd52a395546c9ee0f2327 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Sat, 7 Oct 2023 15:15:44 +0200 Subject: [PATCH 171/208] clippy: suppress cognitive_complexity lint for two functions --- src/uu/ls/src/ls.rs | 1 + src/uu/split/src/split.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/uu/ls/src/ls.rs b/src/uu/ls/src/ls.rs index 301aefaf3a3..9dec8749d1b 100644 --- a/src/uu/ls/src/ls.rs +++ b/src/uu/ls/src/ls.rs @@ -1856,6 +1856,7 @@ impl PathData { } } +#[allow(clippy::cognitive_complexity)] pub fn list(locs: Vec<&Path>, config: &Config) -> UResult<()> { let mut files = Vec::::new(); let mut dirs = Vec::::new(); diff --git a/src/uu/split/src/split.rs b/src/uu/split/src/split.rs index 75624853953..bfd595e4f86 100644 --- a/src/uu/split/src/split.rs +++ b/src/uu/split/src/split.rs @@ -1714,6 +1714,7 @@ where Ok(()) } +#[allow(clippy::cognitive_complexity)] fn split(settings: &Settings) -> UResult<()> { let mut reader = BufReader::new(if settings.input == "-" { Box::new(stdin()) as Box From f86469f6d5c6d0903162aa04cedf872fc0520d67 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Sat, 7 Oct 2023 14:11:15 +0200 Subject: [PATCH 172/208] head: add some empty lines --- src/uu/head/src/head.rs | 13 ++++++++++++- src/uu/head/src/parse.rs | 8 ++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/uu/head/src/head.rs b/src/uu/head/src/head.rs index c60bbfe994a..d6e4db5a78e 100644 --- a/src/uu/head/src/head.rs +++ b/src/uu/head/src/head.rs @@ -3,7 +3,7 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -// spell-checker:ignore (vars) zlines BUFWRITER seekable +// spell-checker:ignore (vars) BUFWRITER seekable use clap::{crate_version, Arg, ArgAction, ArgMatches, Command}; use std::ffi::OsString; @@ -31,6 +31,7 @@ mod options { pub const FILES_NAME: &str = "FILE"; pub const PRESUME_INPUT_PIPE: &str = "-PRESUME-INPUT-PIPE"; } + mod parse; mod take; use take::take_all_but; @@ -519,6 +520,7 @@ mod tests { use std::io::Cursor; use super::*; + fn options(args: &str) -> Result { let combined = "head ".to_owned() + args; let args = combined.split_whitespace().map(OsString::from); @@ -526,6 +528,7 @@ mod tests { .get_matches_from(arg_iterate(args).map_err(|_| String::from("Arg iterate failed"))?); HeadOptions::get_from(&matches) } + #[test] fn test_args_modes() { let args = options("-n -10M -vz").unwrap(); @@ -533,6 +536,7 @@ mod tests { assert!(args.verbose); assert_eq!(args.mode, Mode::AllButLastLines(10 * 1024 * 1024)); } + #[test] fn test_gnu_compatibility() { let args = options("-n 1 -c 1 -n 5 -c kiB -vqvqv").unwrap(); // spell-checker:disable-line @@ -542,6 +546,7 @@ mod tests { assert_eq!(options("-2b").unwrap().mode, Mode::FirstBytes(1024)); assert_eq!(options("-5 -c 1").unwrap().mode, Mode::FirstBytes(1)); } + #[test] fn all_args_test() { assert!(options("--silent").unwrap().quiet); @@ -559,11 +564,13 @@ mod tests { assert_eq!(options("--bytes 15").unwrap().mode, Mode::FirstBytes(15)); assert_eq!(options("-c 15").unwrap().mode, Mode::FirstBytes(15)); } + #[test] fn test_options_errors() { assert!(options("-n IsThisTheRealLife?").is_err()); assert!(options("-c IsThisJustFantasy").is_err()); } + #[test] fn test_options_correct_defaults() { let opts = HeadOptions::default(); @@ -574,6 +581,7 @@ mod tests { assert_eq!(opts.mode, Mode::FirstLines(10)); assert!(opts.files.is_empty()); } + fn arg_outputs(src: &str) -> Result { let split = src.split_whitespace().map(OsString::from); match arg_iterate(split) { @@ -586,6 +594,7 @@ mod tests { Err(_) => Err(()), } } + #[test] fn test_arg_iterate() { // test that normal args remain unchanged @@ -610,6 +619,7 @@ mod tests { //test that empty args remain unchanged assert_eq!(arg_outputs("head"), Ok("head".to_owned())); } + #[test] #[cfg(target_os = "linux")] fn test_arg_iterate_bad_encoding() { @@ -618,6 +628,7 @@ mod tests { // this arises from a conversion from OsString to &str assert!(arg_iterate(vec![OsString::from("head"), invalid].into_iter()).is_err()); } + #[test] fn read_early_exit() { let mut empty = std::io::BufReader::new(std::io::Cursor::new(Vec::new())); diff --git a/src/uu/head/src/parse.rs b/src/uu/head/src/parse.rs index 179435c7ae5..7f7dd48f8c5 100644 --- a/src/uu/head/src/parse.rs +++ b/src/uu/head/src/parse.rs @@ -11,6 +11,7 @@ pub enum ParseError { Syntax, Overflow, } + /// Parses obsolete syntax /// head -NUM\[kmzv\] // spell-checker:disable-line pub fn parse_obsolete(src: &str) -> Option, ParseError>> { @@ -135,6 +136,7 @@ pub fn parse_num(src: &str) -> Result<(u64, bool), ParseSizeError> { #[cfg(test)] mod tests { use super::*; + fn obsolete(src: &str) -> Option, ParseError>> { let r = parse_obsolete(src); match r { @@ -145,9 +147,11 @@ mod tests { None => None, } } + fn obsolete_result(src: &[&str]) -> Option, ParseError>> { Some(Ok(src.iter().map(|s| s.to_string()).collect())) } + #[test] fn test_parse_numbers_obsolete() { assert_eq!(obsolete("-5"), obsolete_result(&["-n", "5"])); @@ -167,16 +171,19 @@ mod tests { obsolete_result(&["-z", "-c", "110100480"]) ); } + #[test] fn test_parse_errors_obsolete() { assert_eq!(obsolete("-5n"), Some(Err(ParseError::Syntax))); assert_eq!(obsolete("-5c5"), Some(Err(ParseError::Syntax))); } + #[test] fn test_parse_obsolete_no_match() { assert_eq!(obsolete("-k"), None); assert_eq!(obsolete("asd"), None); } + #[test] #[cfg(target_pointer_width = "64")] fn test_parse_obsolete_overflow_x64() { @@ -189,6 +196,7 @@ mod tests { Some(Err(ParseError::Overflow)) ); } + #[test] #[cfg(target_pointer_width = "32")] fn test_parse_obsolete_overflow_x32() { From 849051f9681b7d65b319942ae0c3bcd8fbe234b4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 7 Oct 2023 22:22:06 +0000 Subject: [PATCH 173/208] chore(deps): update rust crate num-traits to 0.2.17 --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a3c3e709627..197368abf3b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1404,9 +1404,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", ] diff --git a/Cargo.toml b/Cargo.toml index e59ed7bd7b1..8eecb2b8f65 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -296,7 +296,7 @@ nix = { version = "0.27", default-features = false } nom = "7.1.3" notify = { version = "=6.0.1", features = ["macos_kqueue"] } num-bigint = "0.4.4" -num-traits = "0.2.16" +num-traits = "0.2.17" number_prefix = "0.4" once_cell = "1.18.0" onig = { version = "~6.4", default-features = false } From 7e08562ee6bc71fb5e6b6d0aa19a80549627f5eb Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Sun, 8 Oct 2023 15:02:01 +0200 Subject: [PATCH 174/208] expr: add some empty lines --- src/uu/expr/src/syntax_tree.rs | 8 ++++++++ src/uu/expr/src/tokens.rs | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/src/uu/expr/src/syntax_tree.rs b/src/uu/expr/src/syntax_tree.rs index eaa6be1f51e..4ca723a4d8c 100644 --- a/src/uu/expr/src/syntax_tree.rs +++ b/src/uu/expr/src/syntax_tree.rs @@ -31,10 +31,12 @@ pub enum AstNode { operands: OperandsList, }, } + impl AstNode { fn debug_dump(&self) { self.debug_dump_impl(1); } + fn debug_dump_impl(&self, depth: usize) { for _ in 0..depth { print!("\t",); @@ -71,12 +73,14 @@ impl AstNode { operands, }) } + fn new_leaf(token_idx: usize, value: &str) -> Box { Box::new(Self::Leaf { token_idx, value: value.into(), }) } + pub fn evaluate(&self) -> Result { match self { Self::Leaf { value, .. } => Ok(value.clone()), @@ -154,6 +158,7 @@ impl AstNode { }, } } + pub fn operand_values(&self) -> Result, String> { if let Self::Node { operands, op_type, .. @@ -257,6 +262,7 @@ fn ast_from_rpn(rpn: &mut TokenStack) -> Result, String> { } } } + fn maybe_ast_node( token_idx: usize, op_type: &str, @@ -520,6 +526,7 @@ fn prefix_operator_substr(values: &[String]) -> String { fn bool_as_int(b: bool) -> u8 { u8::from(b) } + fn bool_as_string(b: bool) -> String { if b { "1".to_string() @@ -527,6 +534,7 @@ fn bool_as_string(b: bool) -> String { "0".to_string() } } + fn value_as_bool(s: &str) -> bool { if s.is_empty() { return false; diff --git a/src/uu/expr/src/tokens.rs b/src/uu/expr/src/tokens.rs index b4e4c7da58d..3c5cac060f9 100644 --- a/src/uu/expr/src/tokens.rs +++ b/src/uu/expr/src/tokens.rs @@ -38,6 +38,7 @@ pub enum Token { value: String, }, } + impl Token { fn new_infix_op(v: &str, left_assoc: bool, precedence: u8) -> Self { Self::InfixOp { @@ -46,6 +47,7 @@ impl Token { value: v.into(), } } + fn new_value(v: &str) -> Self { Self::Value { value: v.into() } } @@ -56,12 +58,14 @@ impl Token { _ => false, } } + fn is_a_number(&self) -> bool { match self { Self::Value { value, .. } => value.parse::().is_ok(), _ => false, } } + fn is_a_close_paren(&self) -> bool { matches!(*self, Self::ParClose) } From 4ed58718a8bb0b167520c538df2c1722f8fffe45 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sun, 8 Oct 2023 17:31:50 +0200 Subject: [PATCH 175/208] github action: extract the warnings into a variable --- .github/workflows/CICD.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index 61c12dad0cd..f7db7b8a253 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -231,10 +231,11 @@ jobs: run: | ## `cargo clippy` lint testing unset fault + CLIPPY_FLAGS="-W clippy::default_trait_access -W clippy::manual_string_new" fault_type="${{ steps.vars.outputs.FAULT_TYPE }}" fault_prefix=$(echo "$fault_type" | tr '[:lower:]' '[:upper:]') # * convert any warnings to GHA UI annotations; ref: - S=$(cargo clippy --all-targets ${{ matrix.job.cargo-options }} ${{ steps.vars.outputs.CARGO_UTILITY_LIST_OPTIONS }} -- -W clippy::default_trait_access -W clippy::manual_string_new -D warnings 2>&1) && printf "%s\n" "$S" || { printf "%s\n" "$S" ; printf "%s" "$S" | sed -E -n -e '/^error:/{' -e "N; s/^error:[[:space:]]+(.*)\\n[[:space:]]+-->[[:space:]]+(.*):([0-9]+):([0-9]+).*$/::${fault_type} file=\2,line=\3,col=\4::${fault_prefix}: \`cargo clippy\`: \1 (file:'\2', line:\3)/p;" -e '}' ; fault=true ; } + S=$(cargo clippy --all-targets ${{ matrix.job.cargo-options }} ${{ steps.vars.outputs.CARGO_UTILITY_LIST_OPTIONS }} -- ${CLIPPY_FLAGS} -D warnings 2>&1) && printf "%s\n" "$S" || { printf "%s\n" "$S" ; printf "%s" "$S" | sed -E -n -e '/^error:/{' -e "N; s/^error:[[:space:]]+(.*)\\n[[:space:]]+-->[[:space:]]+(.*):([0-9]+):([0-9]+).*$/::${fault_type} file=\2,line=\3,col=\4::${fault_prefix}: \`cargo clippy\`: \1 (file:'\2', line:\3)/p;" -e '}' ; fault=true ; } if [ -n "${{ steps.vars.outputs.FAIL_ON_FAULT }}" ] && [ -n "$fault" ]; then exit 1 ; fi style_spellcheck: From 6d245491563d5f40c7cfece8ffcfa49695e5ddc3 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sun, 8 Oct 2023 17:32:03 +0200 Subject: [PATCH 176/208] github action: enable clippy::cognitive_complexity --- .github/workflows/CICD.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index f7db7b8a253..b448b76d06f 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -231,7 +231,7 @@ jobs: run: | ## `cargo clippy` lint testing unset fault - CLIPPY_FLAGS="-W clippy::default_trait_access -W clippy::manual_string_new" + CLIPPY_FLAGS="-W clippy::default_trait_access -W clippy::manual_string_new -W clippy::cognitive_complexity" fault_type="${{ steps.vars.outputs.FAULT_TYPE }}" fault_prefix=$(echo "$fault_type" | tr '[:lower:]' '[:upper:]') # * convert any warnings to GHA UI annotations; ref: From 5ac1aef20ec3d639db72207e537d84750c828c87 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sun, 8 Oct 2023 17:34:37 +0200 Subject: [PATCH 177/208] github action: move the fuzzing action into it own file/task --- .github/workflows/CICD.yml | 50 --------------------------- .github/workflows/fuzzing.yml | 64 +++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 50 deletions(-) create mode 100644 .github/workflows/fuzzing.yml diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index 61c12dad0cd..39eea159326 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -125,56 +125,6 @@ jobs: S=$(cargo fmt -- --check) && printf "%s\n" "$S" || { printf "%s\n" "$S" ; printf "%s\n" "$S" | sed -E -n -e "s/^Diff[[:space:]]+in[[:space:]]+${PWD//\//\\/}\/(.*)[[:space:]]+at[[:space:]]+[^0-9]+([0-9]+).*$/::${fault_type} file=\1,line=\2::${fault_prefix}: \`cargo fmt\`: style violation (file:'\1', line:\2; use \`cargo fmt -- \"\1\"\`)/p" ; fault=true ; } if [ -n "${{ steps.vars.outputs.FAIL_ON_FAULT }}" ] && [ -n "$fault" ]; then exit 1 ; fi - fuzz: - name: Run the fuzzers - runs-on: ubuntu-latest - env: - RUN_FOR: 60 - steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@nightly - - name: Install `cargo-fuzz` - run: cargo install cargo-fuzz - - uses: Swatinem/rust-cache@v2 - - name: Run fuzz_date for XX seconds - shell: bash - run: | - ## Run it - cd fuzz - cargo +nightly fuzz run fuzz_date -- -max_total_time=${{ env.RUN_FOR }} -detect_leaks=0 - - name: Run fuzz_test for XX seconds - continue-on-error: true - shell: bash - run: | - ## Run it - cd fuzz - cargo +nightly fuzz run fuzz_test -- -max_total_time=${{ env.RUN_FOR }} -detect_leaks=0 - - name: Run fuzz_expr for XX seconds - continue-on-error: true - shell: bash - run: | - ## Run it - cd fuzz - cargo +nightly fuzz run fuzz_expr -- -max_total_time=${{ env.RUN_FOR }} -detect_leaks=0 - - name: Run fuzz_parse_glob for XX seconds - shell: bash - run: | - ## Run it - cd fuzz - cargo +nightly fuzz run fuzz_parse_glob -- -max_total_time=${{ env.RUN_FOR }} -detect_leaks=0 - - name: Run fuzz_parse_size for XX seconds - shell: bash - run: | - ## Run it - cd fuzz - cargo +nightly fuzz run fuzz_parse_size -- -max_total_time=${{ env.RUN_FOR }} -detect_leaks=0 - - name: Run fuzz_parse_time for XX seconds - shell: bash - run: | - ## Run it - cd fuzz - cargo +nightly fuzz run fuzz_parse_time -- -max_total_time=${{ env.RUN_FOR }} -detect_leaks=0 - style_lint: name: Style/lint runs-on: ${{ matrix.job.os }} diff --git a/.github/workflows/fuzzing.yml b/.github/workflows/fuzzing.yml new file mode 100644 index 00000000000..677df2c9f18 --- /dev/null +++ b/.github/workflows/fuzzing.yml @@ -0,0 +1,64 @@ +name: Fuzzing + +# spell-checker:ignore fuzzer + +on: [push, pull_request] + +permissions: + contents: read # to fetch code (actions/checkout) + +# End the current execution if there is a new changeset in the PR. +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} + +jobs: + fuzz: + name: Run the fuzzers + runs-on: ubuntu-latest + env: + RUN_FOR: 60 + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@nightly + - name: Install `cargo-fuzz` + run: cargo install cargo-fuzz + - uses: Swatinem/rust-cache@v2 + - name: Run fuzz_date for XX seconds + shell: bash + run: | + ## Run it + cd fuzz + cargo +nightly fuzz run fuzz_date -- -max_total_time=${{ env.RUN_FOR }} -detect_leaks=0 + - name: Run fuzz_test for XX seconds + continue-on-error: true + shell: bash + run: | + ## Run it + cd fuzz + cargo +nightly fuzz run fuzz_test -- -max_total_time=${{ env.RUN_FOR }} -detect_leaks=0 + - name: Run fuzz_expr for XX seconds + continue-on-error: true + shell: bash + run: | + ## Run it + cd fuzz + cargo +nightly fuzz run fuzz_expr -- -max_total_time=${{ env.RUN_FOR }} -detect_leaks=0 + - name: Run fuzz_parse_glob for XX seconds + shell: bash + run: | + ## Run it + cd fuzz + cargo +nightly fuzz run fuzz_parse_glob -- -max_total_time=${{ env.RUN_FOR }} -detect_leaks=0 + - name: Run fuzz_parse_size for XX seconds + shell: bash + run: | + ## Run it + cd fuzz + cargo +nightly fuzz run fuzz_parse_size -- -max_total_time=${{ env.RUN_FOR }} -detect_leaks=0 + - name: Run fuzz_parse_time for XX seconds + shell: bash + run: | + ## Run it + cd fuzz + cargo +nightly fuzz run fuzz_parse_time -- -max_total_time=${{ env.RUN_FOR }} -detect_leaks=0 From 8e8a91be5d4d9628542e23f0c4daaaced42622e4 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sun, 8 Oct 2023 17:46:11 +0200 Subject: [PATCH 178/208] Ignore more cognitive_complexity --- src/uu/cp/src/copydir.rs | 1 + src/uu/id/src/id.rs | 1 + src/uu/install/src/install.rs | 1 + src/uu/od/src/inputdecoder.rs | 1 + src/uu/od/src/output_info.rs | 1 + src/uu/od/src/parse_inputs.rs | 2 ++ src/uu/od/src/parse_nrofbytes.rs | 1 + src/uu/od/src/peekreader.rs | 1 + src/uu/od/src/prn_char.rs | 2 ++ src/uu/od/src/prn_float.rs | 2 ++ src/uu/od/src/prn_int.rs | 2 ++ src/uu/stat/src/stat.rs | 2 ++ 12 files changed, 17 insertions(+) diff --git a/src/uu/cp/src/copydir.rs b/src/uu/cp/src/copydir.rs index ac44ce68752..a8b941364a7 100644 --- a/src/uu/cp/src/copydir.rs +++ b/src/uu/cp/src/copydir.rs @@ -447,6 +447,7 @@ mod tests { use super::ends_with_slash_dot; #[test] + #[allow(clippy::cognitive_complexity)] fn test_ends_with_slash_dot() { assert!(ends_with_slash_dot("/.")); assert!(ends_with_slash_dot("./.")); diff --git a/src/uu/id/src/id.rs b/src/uu/id/src/id.rs index 49ad549257d..8b16ba8b733 100644 --- a/src/uu/id/src/id.rs +++ b/src/uu/id/src/id.rs @@ -109,6 +109,7 @@ struct State { } #[uucore::main] +#[allow(clippy::cognitive_complexity)] pub fn uumain(args: impl uucore::Args) -> UResult<()> { let matches = uu_app().after_help(AFTER_HELP).try_get_matches_from(args)?; diff --git a/src/uu/install/src/install.rs b/src/uu/install/src/install.rs index 02cf8345ddf..63ba52b1c8d 100644 --- a/src/uu/install/src/install.rs +++ b/src/uu/install/src/install.rs @@ -526,6 +526,7 @@ fn is_potential_directory_path(path: &Path) -> bool { /// /// Returns a Result type with the Err variant containing the error message. /// +#[allow(clippy::cognitive_complexity)] fn standard(mut paths: Vec, b: &Behavior) -> UResult<()> { // first check that paths contains at least one element if paths.is_empty() { diff --git a/src/uu/od/src/inputdecoder.rs b/src/uu/od/src/inputdecoder.rs index 6f08eba124e..e63eaed148f 100644 --- a/src/uu/od/src/inputdecoder.rs +++ b/src/uu/od/src/inputdecoder.rs @@ -166,6 +166,7 @@ mod tests { #[test] #[allow(clippy::float_cmp)] + #[allow(clippy::cognitive_complexity)] fn smoke_test() { let data = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xff, 0xff]; let mut input = PeekReader::new(Cursor::new(&data)); diff --git a/src/uu/od/src/output_info.rs b/src/uu/od/src/output_info.rs index 96c01f906ed..993bba32918 100644 --- a/src/uu/od/src/output_info.rs +++ b/src/uu/od/src/output_info.rs @@ -208,6 +208,7 @@ impl TypeSizeInfo for TypeInfo { } #[test] +#[allow(clippy::cognitive_complexity)] fn test_calculate_alignment() { // For this example `byte_size_block` is 8 and 'print_width_block' is 23: // 1777777777777777777777 1777777777777777777777 diff --git a/src/uu/od/src/parse_inputs.rs b/src/uu/od/src/parse_inputs.rs index 74dc5a13a83..241d842af9f 100644 --- a/src/uu/od/src/parse_inputs.rs +++ b/src/uu/od/src/parse_inputs.rs @@ -213,6 +213,7 @@ mod tests { } #[test] + #[allow(clippy::cognitive_complexity)] fn test_parse_inputs_with_offset() { // offset is found without filename, so stdin will be used. assert_eq!( @@ -355,6 +356,7 @@ mod tests { } #[test] + #[allow(clippy::cognitive_complexity)] fn test_parse_offset_operand() { assert_eq!(8, parse_offset_operand_str("10").unwrap()); // default octal assert_eq!(0, parse_offset_operand_str("0").unwrap()); diff --git a/src/uu/od/src/parse_nrofbytes.rs b/src/uu/od/src/parse_nrofbytes.rs index a5c81f68ba9..431b2a71fde 100644 --- a/src/uu/od/src/parse_nrofbytes.rs +++ b/src/uu/od/src/parse_nrofbytes.rs @@ -79,6 +79,7 @@ pub fn parse_number_of_bytes(s: &str) -> Result { } #[test] +#[allow(clippy::cognitive_complexity)] fn test_parse_number_of_bytes() { // octal input assert_eq!(8, parse_number_of_bytes("010").unwrap()); diff --git a/src/uu/od/src/peekreader.rs b/src/uu/od/src/peekreader.rs index 239faab92ee..82f139c726e 100644 --- a/src/uu/od/src/peekreader.rs +++ b/src/uu/od/src/peekreader.rs @@ -186,6 +186,7 @@ mod tests { } #[test] + #[allow(clippy::cognitive_complexity)] fn test_peek_read_with_smaller_buffer() { let mut sut = PeekReader::new(Cursor::new(&b"abcdefghij"[..])); diff --git a/src/uu/od/src/prn_char.rs b/src/uu/od/src/prn_char.rs index db12d9d4fc4..36a00a67bb3 100644 --- a/src/uu/od/src/prn_char.rs +++ b/src/uu/od/src/prn_char.rs @@ -99,6 +99,7 @@ pub fn format_ascii_dump(bytes: &[u8]) -> String { } #[test] +#[allow(clippy::cognitive_complexity)] fn test_format_item_a() { assert_eq!(" nul", format_item_a(0x00)); assert_eq!(" soh", format_item_a(0x01)); @@ -114,6 +115,7 @@ fn test_format_item_a() { } #[test] +#[allow(clippy::cognitive_complexity)] fn test_format_item_c() { assert_eq!(" \\0", format_item_c(&[0x00])); assert_eq!(" 001", format_item_c(&[0x01])); diff --git a/src/uu/od/src/prn_float.rs b/src/uu/od/src/prn_float.rs index af632d66b24..f4d018bd84f 100644 --- a/src/uu/od/src/prn_float.rs +++ b/src/uu/od/src/prn_float.rs @@ -91,6 +91,7 @@ fn format_float(f: f64, width: usize, precision: usize) -> String { #[test] #[allow(clippy::excessive_precision)] +#[allow(clippy::cognitive_complexity)] fn test_format_flo32() { assert_eq!(format_flo32(1.0), " 1.0000000"); assert_eq!(format_flo32(9.9999990), " 9.9999990"); @@ -167,6 +168,7 @@ fn test_format_flo32() { } #[test] +#[allow(clippy::cognitive_complexity)] fn test_format_flo64() { assert_eq!(format_flo64(1.0), " 1.0000000000000000"); assert_eq!(format_flo64(10.0), " 10.000000000000000"); diff --git a/src/uu/od/src/prn_int.rs b/src/uu/od/src/prn_int.rs index c214b7525eb..f843ff77ca2 100644 --- a/src/uu/od/src/prn_int.rs +++ b/src/uu/od/src/prn_int.rs @@ -89,6 +89,7 @@ int_writer_signed!(FORMAT_ITEM_DEC32S, 4, 12, format_item_dec_s32, DEC!()); // m int_writer_signed!(FORMAT_ITEM_DEC64S, 8, 21, format_item_dec_s64, DEC!()); // max: -9223372036854775808 #[test] +#[allow(clippy::cognitive_complexity)] fn test_sign_extend() { assert_eq!( 0xffff_ffff_ffff_ff80u64 as i64, @@ -179,6 +180,7 @@ fn test_format_item_dec_u() { } #[test] +#[allow(clippy::cognitive_complexity)] fn test_format_item_dec_s() { assert_eq!(" 0", format_item_dec_s8(0)); assert_eq!(" 127", format_item_dec_s8(0x7f)); diff --git a/src/uu/stat/src/stat.rs b/src/uu/stat/src/stat.rs index c36b450067b..055393578e2 100644 --- a/src/uu/stat/src/stat.rs +++ b/src/uu/stat/src/stat.rs @@ -404,6 +404,7 @@ fn print_unsigned_hex( } impl Stater { + #[allow(clippy::cognitive_complexity)] fn generate_tokens(format_str: &str, use_printf: bool) -> UResult> { let mut tokens = Vec::new(); let bound = format_str.len(); @@ -609,6 +610,7 @@ impl Stater { ret } + #[allow(clippy::cognitive_complexity)] fn do_stat(&self, file: &OsStr, stdin_is_fifo: bool) -> i32 { let display_name = file.to_string_lossy(); let file = if cfg!(unix) && display_name == "-" { From 4d122d7dd0e03d2e3f30ad91576947ee06e616b8 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sun, 8 Oct 2023 17:51:06 +0200 Subject: [PATCH 179/208] Move rustfmt + clippy + typo check into it own workflow --- .github/workflows/CICD.yml | 160 -------------------------- .github/workflows/code-quality.yml | 175 +++++++++++++++++++++++++++++ 2 files changed, 175 insertions(+), 160 deletions(-) create mode 100644 .github/workflows/code-quality.yml diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index b448b76d06f..3b78fa6e2a7 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -82,49 +82,6 @@ jobs: grep --ignore-case "all deps seem to have been used" udeps.log || { printf "%s\n" "::${fault_type} ::${fault_prefix}: \`cargo udeps\`: style violation (unused dependency found)" ; fault=true ; } if [ -n "${{ steps.vars.outputs.FAIL_ON_FAULT }}" ] && [ -n "$fault" ]; then exit 1 ; fi - style_format: - name: Style/format - runs-on: ${{ matrix.job.os }} - strategy: - fail-fast: false - matrix: - job: - - { os: ubuntu-latest , features: feat_os_unix } - steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@master - with: - toolchain: stable - components: rustfmt - - uses: Swatinem/rust-cache@v2 - - name: Initialize workflow variables - id: vars - shell: bash - run: | - ## VARs setup - outputs() { step_id="${{ github.action }}"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo "${var}=${!var}" >> $GITHUB_OUTPUT; done; } - # failure mode - unset FAIL_ON_FAULT ; case '${{ env.STYLE_FAIL_ON_FAULT }}' in - ''|0|f|false|n|no|off) FAULT_TYPE=warning ;; - *) FAIL_ON_FAULT=true ; FAULT_TYPE=error ;; - esac; - outputs FAIL_ON_FAULT FAULT_TYPE - # target-specific options - # * CARGO_FEATURES_OPTION - CARGO_FEATURES_OPTION='' ; - if [ -n "${{ matrix.job.features }}" ]; then CARGO_FEATURES_OPTION='--features "${{ matrix.job.features }}"' ; fi - outputs CARGO_FEATURES_OPTION - - name: "`cargo fmt` testing" - shell: bash - run: | - ## `cargo fmt` testing - unset fault - fault_type="${{ steps.vars.outputs.FAULT_TYPE }}" - fault_prefix=$(echo "$fault_type" | tr '[:lower:]' '[:upper:]') - # * convert any errors/warnings to GHA UI annotations; ref: - S=$(cargo fmt -- --check) && printf "%s\n" "$S" || { printf "%s\n" "$S" ; printf "%s\n" "$S" | sed -E -n -e "s/^Diff[[:space:]]+in[[:space:]]+${PWD//\//\\/}\/(.*)[[:space:]]+at[[:space:]]+[^0-9]+([0-9]+).*$/::${fault_type} file=\1,line=\2::${fault_prefix}: \`cargo fmt\`: style violation (file:'\1', line:\2; use \`cargo fmt -- \"\1\"\`)/p" ; fault=true ; } - if [ -n "${{ steps.vars.outputs.FAIL_ON_FAULT }}" ] && [ -n "$fault" ]; then exit 1 ; fi - fuzz: name: Run the fuzzers runs-on: ubuntu-latest @@ -175,114 +132,6 @@ jobs: cd fuzz cargo +nightly fuzz run fuzz_parse_time -- -max_total_time=${{ env.RUN_FOR }} -detect_leaks=0 - style_lint: - name: Style/lint - runs-on: ${{ matrix.job.os }} - env: - SCCACHE_GHA_ENABLED: "true" - RUSTC_WRAPPER: "sccache" - strategy: - fail-fast: false - matrix: - job: - - { os: ubuntu-latest , features: feat_os_unix } - - { os: macos-latest , features: feat_os_macos } - - { os: windows-latest , features: feat_os_windows } - steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@master - with: - toolchain: stable - components: clippy - - uses: Swatinem/rust-cache@v2 - - name: Run sccache-cache - uses: mozilla-actions/sccache-action@v0.0.3 - - name: Initialize workflow variables - id: vars - shell: bash - run: | - ## VARs setup - outputs() { step_id="${{ github.action }}"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo "${var}=${!var}" >> $GITHUB_OUTPUT; done; } - # failure mode - unset FAIL_ON_FAULT ; case '${{ env.STYLE_FAIL_ON_FAULT }}' in - ''|0|f|false|n|no|off) FAULT_TYPE=warning ;; - *) FAIL_ON_FAULT=true ; FAULT_TYPE=error ;; - esac; - outputs FAIL_ON_FAULT FAULT_TYPE - # target-specific options - # * CARGO_FEATURES_OPTION - CARGO_FEATURES_OPTION='--all-features' ; - if [ -n "${{ matrix.job.features }}" ]; then CARGO_FEATURES_OPTION='--features ${{ matrix.job.features }}' ; fi - outputs CARGO_FEATURES_OPTION - # * determine sub-crate utility list - UTILITY_LIST="$(./util/show-utils.sh ${CARGO_FEATURES_OPTION})" - echo UTILITY_LIST=${UTILITY_LIST} - CARGO_UTILITY_LIST_OPTIONS="$(for u in ${UTILITY_LIST}; do echo -n "-puu_${u} "; done;)" - outputs CARGO_UTILITY_LIST_OPTIONS - - name: Install/setup prerequisites - shell: bash - run: | - ## Install/setup prerequisites - case '${{ matrix.job.os }}' in - macos-latest) brew install coreutils ;; # needed for show-utils.sh - esac - - name: "`cargo clippy` lint testing" - shell: bash - run: | - ## `cargo clippy` lint testing - unset fault - CLIPPY_FLAGS="-W clippy::default_trait_access -W clippy::manual_string_new -W clippy::cognitive_complexity" - fault_type="${{ steps.vars.outputs.FAULT_TYPE }}" - fault_prefix=$(echo "$fault_type" | tr '[:lower:]' '[:upper:]') - # * convert any warnings to GHA UI annotations; ref: - S=$(cargo clippy --all-targets ${{ matrix.job.cargo-options }} ${{ steps.vars.outputs.CARGO_UTILITY_LIST_OPTIONS }} -- ${CLIPPY_FLAGS} -D warnings 2>&1) && printf "%s\n" "$S" || { printf "%s\n" "$S" ; printf "%s" "$S" | sed -E -n -e '/^error:/{' -e "N; s/^error:[[:space:]]+(.*)\\n[[:space:]]+-->[[:space:]]+(.*):([0-9]+):([0-9]+).*$/::${fault_type} file=\2,line=\3,col=\4::${fault_prefix}: \`cargo clippy\`: \1 (file:'\2', line:\3)/p;" -e '}' ; fault=true ; } - if [ -n "${{ steps.vars.outputs.FAIL_ON_FAULT }}" ] && [ -n "$fault" ]; then exit 1 ; fi - - style_spellcheck: - name: Style/spelling - runs-on: ${{ matrix.job.os }} - strategy: - matrix: - job: - - { os: ubuntu-latest , features: feat_os_unix } - steps: - - uses: actions/checkout@v4 - - name: Initialize workflow variables - id: vars - shell: bash - run: | - ## VARs setup - outputs() { step_id="${{ github.action }}"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo "${var}=${!var}" >> $GITHUB_OUTPUT; done; } - # failure mode - unset FAIL_ON_FAULT ; case '${{ env.STYLE_FAIL_ON_FAULT }}' in - ''|0|f|false|n|no|off) FAULT_TYPE=warning ;; - *) FAIL_ON_FAULT=true ; FAULT_TYPE=error ;; - esac; - outputs FAIL_ON_FAULT FAULT_TYPE - - name: Install/setup prerequisites - shell: bash - run: | - ## Install/setup prerequisites - # * pin installed cspell to v4.2.8 (cspell v5+ is broken for NodeJS < v12) - ## maint: [2021-11-10; rivy] `cspell` version may be advanced to v5 when used with NodeJS >= v12 - sudo apt-get -y update ; sudo apt-get -y install npm ; sudo npm install cspell@4.2.8 -g ; - - name: Run `cspell` - shell: bash - run: | - ## Run `cspell` - unset fault - fault_type="${{ steps.vars.outputs.FAULT_TYPE }}" - fault_prefix=$(echo "$fault_type" | tr '[:lower:]' '[:upper:]') - # * find cspell configuration ; note: avoid quotes around ${cfg_file} b/c `cspell` (v4) doesn't correctly dequote the config argument (or perhaps a subshell expansion issue?) - cfg_files=($(shopt -s nullglob ; echo {.vscode,.}/{,.}c[sS]pell{.json,.config{.js,.cjs,.json,.yaml,.yml},.yaml,.yml} ;)) - cfg_file=${cfg_files[0]} - unset CSPELL_CFG_OPTION ; if [ -n "$cfg_file" ]; then CSPELL_CFG_OPTION="--config $cfg_file" ; fi - # * `cspell` - ## maint: [2021-11-10; rivy] the `--no-progress` option for `cspell` is a `cspell` v5+ option - # S=$(cspell ${CSPELL_CFG_OPTION} --no-summary --no-progress "**/*") && printf "%s\n" "$S" || { printf "%s\n" "$S" ; printf "%s" "$S" | sed -E -n "s/${PWD//\//\\/}\/(.*):(.*):(.*) - (.*)/::${fault_type} file=\1,line=\2,col=\3::${fault_type^^}: \4 (file:'\1', line:\2)/p" ; fault=true ; true ; } - S=$(cspell ${CSPELL_CFG_OPTION} --no-summary "**/*") && printf "%s\n" "$S" || { printf "%s\n" "$S" ; printf "%s" "$S" | sed -E -n "s/${PWD//\//\\/}\/(.*):(.*):(.*) - (.*)/::${fault_type} file=\1,line=\2,col=\3::${fault_type^^}: \4 (file:'\1', line:\2)/p" ; fault=true ; true ; } - if [ -n "${{ steps.vars.outputs.FAIL_ON_FAULT }}" ] && [ -n "$fault" ]; then exit 1 ; fi - doc_warnings: name: Documentation/warnings runs-on: ${{ matrix.job.os }} @@ -1081,15 +930,6 @@ jobs: name: toybox-result.json path: ${{ steps.vars.outputs.TEST_SUMMARY_FILE }} - toml_format: - runs-on: ubuntu-latest - steps: - - name: Clone repository - uses: actions/checkout@v4 - - - name: Check - run: npx --yes @taplo/cli fmt --check - coverage: name: Code Coverage runs-on: ${{ matrix.job.os }} diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml new file mode 100644 index 00000000000..c67483280f0 --- /dev/null +++ b/.github/workflows/code-quality.yml @@ -0,0 +1,175 @@ +name: Code Quality + +# spell-checker:ignore TERMUX reactivecircus Swatinem noaudio pkill swiftshader dtolnay juliangruber + +on: [push, pull_request] + +permissions: + contents: read # to fetch code (actions/checkout) + +# End the current execution if there is a new changeset in the PR. +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} + +jobs: + + style_format: + name: Style/format + runs-on: ${{ matrix.job.os }} + strategy: + fail-fast: false + matrix: + job: + - { os: ubuntu-latest , features: feat_os_unix } + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: stable + components: rustfmt + - uses: Swatinem/rust-cache@v2 + - name: Initialize workflow variables + id: vars + shell: bash + run: | + ## VARs setup + outputs() { step_id="${{ github.action }}"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo "${var}=${!var}" >> $GITHUB_OUTPUT; done; } + # failure mode + unset FAIL_ON_FAULT ; case '${{ env.STYLE_FAIL_ON_FAULT }}' in + ''|0|f|false|n|no|off) FAULT_TYPE=warning ;; + *) FAIL_ON_FAULT=true ; FAULT_TYPE=error ;; + esac; + outputs FAIL_ON_FAULT FAULT_TYPE + # target-specific options + # * CARGO_FEATURES_OPTION + CARGO_FEATURES_OPTION='' ; + if [ -n "${{ matrix.job.features }}" ]; then CARGO_FEATURES_OPTION='--features "${{ matrix.job.features }}"' ; fi + outputs CARGO_FEATURES_OPTION + - name: "`cargo fmt` testing" + shell: bash + run: | + ## `cargo fmt` testing + unset fault + fault_type="${{ steps.vars.outputs.FAULT_TYPE }}" + fault_prefix=$(echo "$fault_type" | tr '[:lower:]' '[:upper:]') + # * convert any errors/warnings to GHA UI annotations; ref: + S=$(cargo fmt -- --check) && printf "%s\n" "$S" || { printf "%s\n" "$S" ; printf "%s\n" "$S" | sed -E -n -e "s/^Diff[[:space:]]+in[[:space:]]+${PWD//\//\\/}\/(.*)[[:space:]]+at[[:space:]]+[^0-9]+([0-9]+).*$/::${fault_type} file=\1,line=\2::${fault_prefix}: \`cargo fmt\`: style violation (file:'\1', line:\2; use \`cargo fmt -- \"\1\"\`)/p" ; fault=true ; } + if [ -n "${{ steps.vars.outputs.FAIL_ON_FAULT }}" ] && [ -n "$fault" ]; then exit 1 ; fi + + style_lint: + name: Style/lint + runs-on: ${{ matrix.job.os }} + env: + SCCACHE_GHA_ENABLED: "true" + RUSTC_WRAPPER: "sccache" + strategy: + fail-fast: false + matrix: + job: + - { os: ubuntu-latest , features: feat_os_unix } + - { os: macos-latest , features: feat_os_macos } + - { os: windows-latest , features: feat_os_windows } + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: stable + components: clippy + - uses: Swatinem/rust-cache@v2 + - name: Run sccache-cache + uses: mozilla-actions/sccache-action@v0.0.3 + - name: Initialize workflow variables + id: vars + shell: bash + run: | + ## VARs setup + outputs() { step_id="${{ github.action }}"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo "${var}=${!var}" >> $GITHUB_OUTPUT; done; } + # failure mode + unset FAIL_ON_FAULT ; case '${{ env.STYLE_FAIL_ON_FAULT }}' in + ''|0|f|false|n|no|off) FAULT_TYPE=warning ;; + *) FAIL_ON_FAULT=true ; FAULT_TYPE=error ;; + esac; + outputs FAIL_ON_FAULT FAULT_TYPE + # target-specific options + # * CARGO_FEATURES_OPTION + CARGO_FEATURES_OPTION='--all-features' ; + if [ -n "${{ matrix.job.features }}" ]; then CARGO_FEATURES_OPTION='--features ${{ matrix.job.features }}' ; fi + outputs CARGO_FEATURES_OPTION + # * determine sub-crate utility list + UTILITY_LIST="$(./util/show-utils.sh ${CARGO_FEATURES_OPTION})" + echo UTILITY_LIST=${UTILITY_LIST} + CARGO_UTILITY_LIST_OPTIONS="$(for u in ${UTILITY_LIST}; do echo -n "-puu_${u} "; done;)" + outputs CARGO_UTILITY_LIST_OPTIONS + - name: Install/setup prerequisites + shell: bash + run: | + ## Install/setup prerequisites + case '${{ matrix.job.os }}' in + macos-latest) brew install coreutils ;; # needed for show-utils.sh + esac + - name: "`cargo clippy` lint testing" + shell: bash + run: | + ## `cargo clippy` lint testing + unset fault + CLIPPY_FLAGS="-W clippy::default_trait_access -W clippy::manual_string_new -W clippy::cognitive_complexity" + fault_type="${{ steps.vars.outputs.FAULT_TYPE }}" + fault_prefix=$(echo "$fault_type" | tr '[:lower:]' '[:upper:]') + # * convert any warnings to GHA UI annotations; ref: + S=$(cargo clippy --all-targets ${{ matrix.job.cargo-options }} ${{ steps.vars.outputs.CARGO_UTILITY_LIST_OPTIONS }} -- ${CLIPPY_FLAGS} -D warnings 2>&1) && printf "%s\n" "$S" || { printf "%s\n" "$S" ; printf "%s" "$S" | sed -E -n -e '/^error:/{' -e "N; s/^error:[[:space:]]+(.*)\\n[[:space:]]+-->[[:space:]]+(.*):([0-9]+):([0-9]+).*$/::${fault_type} file=\2,line=\3,col=\4::${fault_prefix}: \`cargo clippy\`: \1 (file:'\2', line:\3)/p;" -e '}' ; fault=true ; } + if [ -n "${{ steps.vars.outputs.FAIL_ON_FAULT }}" ] && [ -n "$fault" ]; then exit 1 ; fi + + style_spellcheck: + name: Style/spelling + runs-on: ${{ matrix.job.os }} + strategy: + matrix: + job: + - { os: ubuntu-latest , features: feat_os_unix } + steps: + - uses: actions/checkout@v4 + - name: Initialize workflow variables + id: vars + shell: bash + run: | + ## VARs setup + outputs() { step_id="${{ github.action }}"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo "${var}=${!var}" >> $GITHUB_OUTPUT; done; } + # failure mode + unset FAIL_ON_FAULT ; case '${{ env.STYLE_FAIL_ON_FAULT }}' in + ''|0|f|false|n|no|off) FAULT_TYPE=warning ;; + *) FAIL_ON_FAULT=true ; FAULT_TYPE=error ;; + esac; + outputs FAIL_ON_FAULT FAULT_TYPE + - name: Install/setup prerequisites + shell: bash + run: | + ## Install/setup prerequisites + # * pin installed cspell to v4.2.8 (cspell v5+ is broken for NodeJS < v12) + ## maint: [2021-11-10; rivy] `cspell` version may be advanced to v5 when used with NodeJS >= v12 + sudo apt-get -y update ; sudo apt-get -y install npm ; sudo npm install cspell@4.2.8 -g ; + - name: Run `cspell` + shell: bash + run: | + ## Run `cspell` + unset fault + fault_type="${{ steps.vars.outputs.FAULT_TYPE }}" + fault_prefix=$(echo "$fault_type" | tr '[:lower:]' '[:upper:]') + # * find cspell configuration ; note: avoid quotes around ${cfg_file} b/c `cspell` (v4) doesn't correctly dequote the config argument (or perhaps a subshell expansion issue?) + cfg_files=($(shopt -s nullglob ; echo {.vscode,.}/{,.}c[sS]pell{.json,.config{.js,.cjs,.json,.yaml,.yml},.yaml,.yml} ;)) + cfg_file=${cfg_files[0]} + unset CSPELL_CFG_OPTION ; if [ -n "$cfg_file" ]; then CSPELL_CFG_OPTION="--config $cfg_file" ; fi + # * `cspell` + ## maint: [2021-11-10; rivy] the `--no-progress` option for `cspell` is a `cspell` v5+ option + # S=$(cspell ${CSPELL_CFG_OPTION} --no-summary --no-progress "**/*") && printf "%s\n" "$S" || { printf "%s\n" "$S" ; printf "%s" "$S" | sed -E -n "s/${PWD//\//\\/}\/(.*):(.*):(.*) - (.*)/::${fault_type} file=\1,line=\2,col=\3::${fault_type^^}: \4 (file:'\1', line:\2)/p" ; fault=true ; true ; } + S=$(cspell ${CSPELL_CFG_OPTION} --no-summary "**/*") && printf "%s\n" "$S" || { printf "%s\n" "$S" ; printf "%s" "$S" | sed -E -n "s/${PWD//\//\\/}\/(.*):(.*):(.*) - (.*)/::${fault_type} file=\1,line=\2,col=\3::${fault_type^^}: \4 (file:'\1', line:\2)/p" ; fault=true ; true ; } + if [ -n "${{ steps.vars.outputs.FAIL_ON_FAULT }}" ] && [ -n "$fault" ]; then exit 1 ; fi + + toml_format: + runs-on: ubuntu-latest + steps: + - name: Clone repository + uses: actions/checkout@v4 + + - name: Check + run: npx --yes @taplo/cli fmt --check From 02ab93c141f3b17d713276226ff1eaa555847c6c Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sun, 8 Oct 2023 17:54:36 +0200 Subject: [PATCH 180/208] github action: name the toml task --- .github/workflows/code-quality.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml index c67483280f0..abcfd90ab96 100644 --- a/.github/workflows/code-quality.yml +++ b/.github/workflows/code-quality.yml @@ -166,6 +166,7 @@ jobs: if [ -n "${{ steps.vars.outputs.FAIL_ON_FAULT }}" ] && [ -n "$fault" ]; then exit 1 ; fi toml_format: + name: Style/toml runs-on: ubuntu-latest steps: - name: Clone repository From 2c9e091ebee4e9d5b85ac46015d8727ae654ffdc Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Mon, 9 Oct 2023 07:07:38 +0200 Subject: [PATCH 181/208] Bump errno from 0.3.1 to 0.3.5 --- Cargo.lock | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 197368abf3b..d4da117364d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -783,25 +783,14 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.1" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" dependencies = [ - "errno-dragonfly", "libc", "windows-sys 0.48.0", ] -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - [[package]] name = "exacl" version = "0.11.0" From 2883c0a9689ead143ae4a8b38536447939d890fd Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Mon, 9 Oct 2023 15:16:50 +0200 Subject: [PATCH 182/208] uucore: remove commented out import --- src/uucore/src/lib/features/tokenize/sub.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/uucore/src/lib/features/tokenize/sub.rs b/src/uucore/src/lib/features/tokenize/sub.rs index 447616ae6a6..c65a37a689b 100644 --- a/src/uucore/src/lib/features/tokenize/sub.rs +++ b/src/uucore/src/lib/features/tokenize/sub.rs @@ -18,7 +18,6 @@ use std::iter::Peekable; use std::process::exit; use std::slice::Iter; use std::str::Chars; -// use std::collections::HashSet; use super::num_format::format_field::{FieldType, FormatField}; use super::num_format::num_format; From b38ac2cb7488e0006c5a98f87bc36b9ce07dc150 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Mon, 9 Oct 2023 15:28:12 +0200 Subject: [PATCH 183/208] uucore: remove commented out enum FChar --- .../tokenize/num_format/format_field.rs | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/uucore/src/lib/features/tokenize/num_format/format_field.rs b/src/uucore/src/lib/features/tokenize/num_format/format_field.rs index 036ee286d25..bd57b0ecdaf 100644 --- a/src/uucore/src/lib/features/tokenize/num_format/format_field.rs +++ b/src/uucore/src/lib/features/tokenize/num_format/format_field.rs @@ -17,23 +17,6 @@ pub enum FieldType { Charf, } -// #[allow(non_camel_case_types)] -// pub enum FChar { -// d, -// e, -// E, -// i, -// f, -// F, -// g, -// G, -// u, -// x, -// X, -// o -// } -// - // a Sub Tokens' fields are stored // as a single object so they can be more simply // passed by ref to num_format in a Sub method From d2cacdfce27adbf9c64b1cd4d72f05daf6d48f45 Mon Sep 17 00:00:00 2001 From: Howard Su Date: Mon, 9 Oct 2023 09:38:06 +0800 Subject: [PATCH 184/208] Fix overflow error on WSL. Default to 0 when the values are non-sense. --- src/uu/df/src/table.rs | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/src/uu/df/src/table.rs b/src/uu/df/src/table.rs index 4c3d08f4533..e1f7fd82ec3 100644 --- a/src/uu/df/src/table.rs +++ b/src/uu/df/src/table.rs @@ -152,8 +152,10 @@ impl From for Row { ffree, .. } = fs.usage; - let bused = blocks - bfree; - let fused = files - ffree; + + // On Windows WSL, files can be less than ffree. Protect such cases via saturating_sub. + let bused = blocks.saturating_sub(bfree); + let fused = files.saturating_sub(ffree); Self { file: fs.file, fs_device: dev_name, @@ -815,4 +817,35 @@ mod tests { assert_eq!(get_formatted_values(1000, 1000, 0), vec!("1", "1", "0")); assert_eq!(get_formatted_values(1001, 1000, 1), vec!("2", "1", "1")); } + + #[test] + fn test_row_converter_with_invalid_numbers() { + // copy from wsl linux + let d = crate::Filesystem { + file: None, + mount_info: crate::MountInfo { + dev_id: "28".to_string(), + dev_name: "none".to_string(), + fs_type: "9p".to_string(), + mount_dir: "/usr/lib/wsl/drivers".to_string(), + mount_option: "ro,nosuid,nodev,noatime".to_string(), + mount_root: "/".to_string(), + remote: false, + dummy: false, + }, + usage: crate::table::FsUsage { + blocksize: 4096, + blocks: 244029695, + bfree: 125085030, + bavail: 125085030, + bavail_top_bit_set: false, + files: 999, + ffree: 1000000, + }, + }; + + let row = Row::from(d); + + assert_eq!(row.inodes_used, 0); + } } From 536dbae90aed66f2a6261e84363342a94e4f59a7 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Tue, 10 Oct 2023 07:58:16 +0200 Subject: [PATCH 185/208] Bump bstr and regex bstr from 1.6 -> 1.7 regex from 1.9.6 -> 1.10.0 --- Cargo.lock | 16 ++++++++-------- Cargo.toml | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d4da117364d..9d43cc65d1a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -188,9 +188,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6798148dccfbff0fae41c7574d2fa8f1ef3492fba0face179de5d8d447d67b05" +checksum = "c79ad7fb2dd38f3dabd76b09c6a5a20c038fc0213ef1e9afd30eb777f120f019" dependencies = [ "memchr", "regex-automata", @@ -1728,9 +1728,9 @@ checksum = "f1bfbf25d7eb88ddcbb1ec3d755d0634da8f7657b2cb8b74089121409ab8228f" [[package]] name = "regex" -version = "1.9.6" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebee201405406dbf528b8b672104ae6d6d63e6d118cb10e4d51abbc7b58044ff" +checksum = "d119d7c7ca818f8a53c300863d4f87566aac09943aef5b355bb83969dae75d87" dependencies = [ "aho-corasick", "memchr", @@ -1740,9 +1740,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.9" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59b23e92ee4318893fa3fe3e6fb365258efbfe6ac6ab30f090cdcbb7aa37efa9" +checksum = "465c6fc0621e4abc4187a2bda0937bfd4f722c2730b29562e19689ea796c9a4b" dependencies = [ "aho-corasick", "memchr", @@ -1751,9 +1751,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" +checksum = "c3cbb081b9784b07cceb8824c8583f86db4814d172ab043f3c23f7dc600bf83d" [[package]] name = "relative-path" diff --git a/Cargo.toml b/Cargo.toml index 8eecb2b8f65..df729a76521 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -259,7 +259,7 @@ test = ["uu_test"] [workspace.dependencies] bigdecimal = "0.4" binary-heap-plus = "0.5.0" -bstr = "1.6" +bstr = "1.7" bytecount = "0.6.4" byteorder = "1.5.0" chrono = { version = "^0.4.31", default-features = false, features = [ @@ -309,7 +309,7 @@ rand = { version = "0.8", features = ["small_rng"] } rand_core = "0.6" rayon = "1.8" redox_syscall = "0.4" -regex = "1.9.6" +regex = "1.10.0" rstest = "0.18.2" rust-ini = "0.19.0" same-file = "1.0.6" From 95a1a08de3d44240112fc1c3e959b268cf216678 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Tue, 10 Oct 2023 09:07:32 +0200 Subject: [PATCH 186/208] Add two words to the ignore spell list --- src/uu/df/src/table.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uu/df/src/table.rs b/src/uu/df/src/table.rs index e1f7fd82ec3..f6e09420482 100644 --- a/src/uu/df/src/table.rs +++ b/src/uu/df/src/table.rs @@ -2,7 +2,7 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -// spell-checker:ignore tmpfs Pcent Itotal Iused Iavail Ipcent +// spell-checker:ignore tmpfs Pcent Itotal Iused Iavail Ipcent nosuid nodev //! The filesystem usage data table. //! //! A table ([`Table`]) comprises a header row ([`Header`]) and a From 3b971e480624036b00aa5e9b5f3813dd5e6b71ba Mon Sep 17 00:00:00 2001 From: Miles Liu Date: Wed, 11 Oct 2023 16:26:36 +0800 Subject: [PATCH 187/208] ls: move to uutils-term-grid --- Cargo.lock | 20 ++++++++++---------- Cargo.toml | 2 +- src/uu/ls/Cargo.toml | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9d43cc65d1a..a0a62d13ba6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2085,15 +2085,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "term_grid" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "230d3e804faaed5a39b08319efb797783df2fd9671b39b7596490cb486d702cf" -dependencies = [ - "unicode-width", -] - [[package]] name = "terminal_size" version = "0.2.6" @@ -2649,10 +2640,10 @@ dependencies = [ "number_prefix", "once_cell", "selinux", - "term_grid", "terminal_size 0.3.0", "unicode-width", "uucore", + "uutils_term_grid", ] [[package]] @@ -3276,6 +3267,15 @@ version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "422ee0de9031b5b948b97a8fc04e3aa35230001a722ddd27943e0be31564ce4c" +[[package]] +name = "uutils_term_grid" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b389452a568698688dda38802068378a16c15c4af9b153cdd99b65391292bbc7" +dependencies = [ + "unicode-width", +] + [[package]] name = "version_check" version = "0.9.4" diff --git a/Cargo.toml b/Cargo.toml index df729a76521..820568e616e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -318,7 +318,7 @@ selinux = "0.4" signal-hook = "0.3.17" smallvec = { version = "1.11", features = ["union"] } tempfile = "3.8.0" -term_grid = "0.1.5" +uutils_term_grid = "0.3" terminal_size = "0.3.0" textwrap = { version = "0.16.0", features = ["terminal_size"] } thiserror = "1.0" diff --git a/src/uu/ls/Cargo.toml b/src/uu/ls/Cargo.toml index e4100d07f8b..4394865caba 100644 --- a/src/uu/ls/Cargo.toml +++ b/src/uu/ls/Cargo.toml @@ -19,7 +19,7 @@ clap = { workspace = true, features = ["env"] } chrono = { workspace = true } unicode-width = { workspace = true } number_prefix = { workspace = true } -term_grid = { workspace = true } +uutils_term_grid = { workspace = true } terminal_size = { workspace = true } glob = { workspace = true } lscolors = { workspace = true } From a920464952df36e6c1797bc5dccf29f2976edf83 Mon Sep 17 00:00:00 2001 From: zhitkoff Date: Wed, 11 Oct 2023 12:13:22 -0400 Subject: [PATCH 188/208] split: undocumented options aliases + help fix --- src/uu/split/src/split.rs | 4 +++- tests/by-util/test_split.rs | 26 ++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/uu/split/src/split.rs b/src/uu/split/src/split.rs index bfd595e4f86..020ada93bb5 100644 --- a/src/uu/split/src/split.rs +++ b/src/uu/split/src/split.rs @@ -311,6 +311,7 @@ pub fn uu_app() -> Command { .arg( Arg::new(OPT_NUMERIC_SUFFIXES) .long(OPT_NUMERIC_SUFFIXES) + .alias("numeric") .require_equals(true) .default_missing_value("0") .num_args(0..=1) @@ -338,6 +339,7 @@ pub fn uu_app() -> Command { .arg( Arg::new(OPT_HEX_SUFFIXES) .long(OPT_HEX_SUFFIXES) + .alias("hex") .default_missing_value("0") .require_equals(true) .num_args(0..=1) @@ -372,7 +374,7 @@ pub fn uu_app() -> Command { .allow_hyphen_values(true) .value_name("SEP") .action(ArgAction::Append) - .help("use SEP instead of newline as the record separator; '\0' (zero) specifies the NUL character"), + .help("use SEP instead of newline as the record separator; '\\0' (zero) specifies the NUL character"), ) .arg( Arg::new(OPT_IO) diff --git a/tests/by-util/test_split.rs b/tests/by-util/test_split.rs index ce80844cf3e..113c0fb87ff 100644 --- a/tests/by-util/test_split.rs +++ b/tests/by-util/test_split.rs @@ -1192,6 +1192,19 @@ fn test_numeric_suffix() { assert_eq!(at.read("x12"), ""); } +#[test] +fn test_numeric_suffix_alias() { + let (at, mut ucmd) = at_and_ucmd!(); + ucmd.args(&["-n", "4", "--numeric=9", "threebytes.txt"]) + .succeeds() + .no_stdout() + .no_stderr(); + assert_eq!(at.read("x09"), "a"); + assert_eq!(at.read("x10"), "b"); + assert_eq!(at.read("x11"), "c"); + assert_eq!(at.read("x12"), ""); +} + #[test] fn test_hex_suffix() { let (at, mut ucmd) = at_and_ucmd!(); @@ -1205,6 +1218,19 @@ fn test_hex_suffix() { assert_eq!(at.read("x0c"), ""); } +#[test] +fn test_hex_suffix_alias() { + let (at, mut ucmd) = at_and_ucmd!(); + ucmd.args(&["-n", "4", "--hex=9", "threebytes.txt"]) + .succeeds() + .no_stdout() + .no_stderr(); + assert_eq!(at.read("x09"), "a"); + assert_eq!(at.read("x0a"), "b"); + assert_eq!(at.read("x0b"), "c"); + assert_eq!(at.read("x0c"), ""); +} + #[test] fn test_numeric_suffix_no_equal() { new_ucmd!() From 2bd5e263172772b595412fb978668709d2033230 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Thu, 12 Oct 2023 17:20:00 +0200 Subject: [PATCH 189/208] DEVELOPMENT.md: improve doc on how to run some tests --- DEVELOPMENT.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 24a1bdeb52c..308daba9818 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -142,6 +142,8 @@ If you also want to test the core utilities: ```shell cargo test -p uucore -p coreutils +# or +cargo test --all-features -p uucore ``` Running the complete test suite might take a while. We use [nextest](https://nexte.st/index.html) in From 29a5a13ce6a153b15d6eba742dd7747b0abce824 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Thu, 12 Oct 2023 14:29:26 +0200 Subject: [PATCH 190/208] fs: split get_file_display into its function --- src/uucore/src/lib/features/fs.rs | 54 ++++++++++++++++++++++++------- 1 file changed, 43 insertions(+), 11 deletions(-) diff --git a/src/uucore/src/lib/features/fs.rs b/src/uucore/src/lib/features/fs.rs index 719efc7a6e7..97238b10df4 100644 --- a/src/uucore/src/lib/features/fs.rs +++ b/src/uucore/src/lib/features/fs.rs @@ -465,6 +465,35 @@ pub fn display_permissions(metadata: &fs::Metadata, display_file_type: bool) -> display_permissions_unix(mode, display_file_type) } +/// Returns a character representation of the file type based on its mode. +/// This function is specific to Unix-like systems. +/// +/// - `mode`: The mode of the file, typically obtained from file metadata. +/// +/// # Returns +/// - 'd' for directories +/// - 'c' for character devices +/// - 'b' for block devices +/// - '-' for regular files +/// - 'p' for FIFOs (named pipes) +/// - 'l' for symbolic links +/// - 's' for sockets +/// - '?' for any other unrecognized file types +#[cfg(unix)] +fn get_file_display(mode: mode_t) -> char { + match mode & S_IFMT { + S_IFDIR => 'd', + S_IFCHR => 'c', + S_IFBLK => 'b', + S_IFREG => '-', + S_IFIFO => 'p', + S_IFLNK => 'l', + S_IFSOCK => 's', + // TODO: Other file types + _ => '?', + } +} + // The logic below is more readable written this way. #[allow(clippy::if_not_else)] #[allow(clippy::cognitive_complexity)] @@ -474,17 +503,7 @@ pub fn display_permissions_unix(mode: mode_t, display_file_type: bool) -> String let mut result; if display_file_type { result = String::with_capacity(10); - result.push(match mode & S_IFMT { - S_IFDIR => 'd', - S_IFCHR => 'c', - S_IFBLK => 'b', - S_IFREG => '-', - S_IFIFO => 'p', - S_IFLNK => 'l', - S_IFSOCK => 's', - // TODO: Other file types - _ => '?', - }); + result.push(get_file_display(mode)); } else { result = String::with_capacity(9); } @@ -881,4 +900,17 @@ mod tests { assert!(are_hardlinks_to_same_file(&path1, &path2)); } + + #[cfg(unix)] + #[test] + fn test_get_file_display() { + assert_eq!(get_file_display(S_IFDIR | 0o755), 'd'); + assert_eq!(get_file_display(S_IFCHR | 0o644), 'c'); + assert_eq!(get_file_display(S_IFBLK | 0o600), 'b'); + assert_eq!(get_file_display(S_IFREG | 0o777), '-'); + assert_eq!(get_file_display(S_IFIFO | 0o666), 'p'); + assert_eq!(get_file_display(S_IFLNK | 0o777), 'l'); + assert_eq!(get_file_display(S_IFSOCK | 0o600), 's'); + assert_eq!(get_file_display(0o777), '?'); + } } From 94972d45c7aa0532c3b44d2c131b4028b63f5555 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Thu, 12 Oct 2023 23:07:27 +0200 Subject: [PATCH 191/208] ls: Document a bit tests/ls/stat-dtype.sh --- src/uu/ls/src/ls.rs | 8 ++++---- tests/by-util/test_ls.rs | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/uu/ls/src/ls.rs b/src/uu/ls/src/ls.rs index 39bab79823f..8a05e81f89d 100644 --- a/src/uu/ls/src/ls.rs +++ b/src/uu/ls/src/ls.rs @@ -2395,10 +2395,10 @@ fn display_grid( writeln!(out)?; } } else { - let mut grid = Grid::new(GridOptions { - filling: Filling::Spaces(2), - direction, - }); + // To match gnu/tests/ls/stat-dtype.sh + // we might want to have Text("\t".to_string()); + let filling = Filling::Spaces(2); + let mut grid = Grid::new(GridOptions { filling, direction }); for name in names { grid.add(name); diff --git a/tests/by-util/test_ls.rs b/tests/by-util/test_ls.rs index 7d0f86298cd..d9c1c8740e9 100644 --- a/tests/by-util/test_ls.rs +++ b/tests/by-util/test_ls.rs @@ -3678,3 +3678,17 @@ fn test_ls_dired_complex() { println!("Extracted filenames: {:?}", filenames); assert_eq!(filenames, vec!["a1", "a22", "a333", "a4444", "d"]); } + +#[ignore = "issue #5396"] +#[test] +fn test_ls_tabsize_cf() { + let (at, mut ucmd) = at_and_ucmd!(); + + at.mkdir("e"); + at.mkdir("e/a2345"); + at.mkdir("e/b"); + + ucmd.args(&["-CF", "e"]) + .succeeds() + .stdout_is("a2345/\tb/\n"); +} From cd51eb8eb58ca8c893ba775707537bc475e54e4a Mon Sep 17 00:00:00 2001 From: Miles Liu Date: Fri, 13 Oct 2023 14:13:10 +0800 Subject: [PATCH 192/208] ci: code-quality workflow doesn't fail on clippy errors --- .github/workflows/code-quality.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml index abcfd90ab96..0a619e09773 100644 --- a/.github/workflows/code-quality.yml +++ b/.github/workflows/code-quality.yml @@ -4,6 +4,10 @@ name: Code Quality on: [push, pull_request] +env: + # * style job configuration + STYLE_FAIL_ON_FAULT: true ## (bool) fail the build if a style job contains a fault (error or warning); may be overridden on a per-job basis + permissions: contents: read # to fetch code (actions/checkout) From f1f4823feb91dd3a8644287a1995e5f5433b8ef7 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Fri, 13 Oct 2023 08:26:06 +0200 Subject: [PATCH 193/208] who: suppress cognitive_complexity lint --- src/uu/who/src/who.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/uu/who/src/who.rs b/src/uu/who/src/who.rs index 29929b13866..5d952efffb2 100644 --- a/src/uu/who/src/who.rs +++ b/src/uu/who/src/who.rs @@ -326,6 +326,7 @@ fn current_tty() -> String { } impl Who { + #[allow(clippy::cognitive_complexity)] fn exec(&mut self) -> UResult<()> { let run_level_chk = |_record: i16| { #[cfg(not(target_os = "linux"))] From 8826e47d36086737370cb11d647b1399ab6d0b01 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Fri, 13 Oct 2023 10:30:31 +0200 Subject: [PATCH 194/208] ignore dtype in the spell --- src/uu/ls/src/ls.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uu/ls/src/ls.rs b/src/uu/ls/src/ls.rs index 8a05e81f89d..bff7c446050 100644 --- a/src/uu/ls/src/ls.rs +++ b/src/uu/ls/src/ls.rs @@ -3,7 +3,7 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -// spell-checker:ignore (ToDO) cpio svgz webm somegroup nlink rmvb xspf tabsize dired subdired +// spell-checker:ignore (ToDO) cpio svgz webm somegroup nlink rmvb xspf tabsize dired subdired dtype use clap::{ builder::{NonEmptyStringValueParser, ValueParser}, From 8931cfa93df1cd003ce081b413524fabbe8537f1 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Fri, 13 Oct 2023 11:18:00 +0200 Subject: [PATCH 195/208] improve the wordin Co-authored-by: Daniel Hofstetter --- src/uu/ls/src/ls.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/uu/ls/src/ls.rs b/src/uu/ls/src/ls.rs index bff7c446050..dfa637d2300 100644 --- a/src/uu/ls/src/ls.rs +++ b/src/uu/ls/src/ls.rs @@ -2395,8 +2395,8 @@ fn display_grid( writeln!(out)?; } } else { - // To match gnu/tests/ls/stat-dtype.sh - // we might want to have Text("\t".to_string()); + // TODO: To match gnu/tests/ls/stat-dtype.sh + // we might want to have Filling::Text("\t".to_string()); let filling = Filling::Spaces(2); let mut grid = Grid::new(GridOptions { filling, direction }); From 41188a915ee33195f45ff86ac253cc0ef11c240c Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Fri, 13 Oct 2023 11:35:45 +0200 Subject: [PATCH 196/208] rename the test Co-authored-by: Daniel Hofstetter --- tests/by-util/test_ls.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/by-util/test_ls.rs b/tests/by-util/test_ls.rs index d9c1c8740e9..8b0032065d1 100644 --- a/tests/by-util/test_ls.rs +++ b/tests/by-util/test_ls.rs @@ -3681,7 +3681,7 @@ fn test_ls_dired_complex() { #[ignore = "issue #5396"] #[test] -fn test_ls_tabsize_cf() { +fn test_ls_cf_output_should_be_delimited_by_tab() { let (at, mut ucmd) = at_and_ucmd!(); at.mkdir("e"); From a69d48fb8c1d5b907a5943c99156352c13858316 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Fri, 13 Oct 2023 14:34:54 +0200 Subject: [PATCH 197/208] clippy: suppress cognitive_complexity lint --- src/uu/csplit/src/csplit.rs | 2 ++ src/uu/csplit/src/patterns.rs | 2 ++ src/uu/dd/src/numbers.rs | 1 + src/uu/dd/src/parseargs/unit_tests.rs | 1 + src/uu/df/src/blocks.rs | 1 + src/uu/head/src/head.rs | 1 + src/uu/head/src/parse.rs | 1 + src/uu/nl/src/nl.rs | 1 + src/uu/numfmt/src/format.rs | 1 + src/uu/numfmt/src/options.rs | 1 + src/uu/od/src/prn_float.rs | 1 + src/uu/seq/src/numberparse.rs | 2 ++ src/uu/split/src/number.rs | 4 ++++ src/uu/split/src/split.rs | 1 + src/uu/stat/src/stat.rs | 1 + 15 files changed, 21 insertions(+) diff --git a/src/uu/csplit/src/csplit.rs b/src/uu/csplit/src/csplit.rs index 6c9a776c388..6e03c2e5c83 100644 --- a/src/uu/csplit/src/csplit.rs +++ b/src/uu/csplit/src/csplit.rs @@ -664,6 +664,7 @@ mod tests { use super::*; #[test] + #[allow(clippy::cognitive_complexity)] fn input_splitter() { let input = vec![ Ok(String::from("aaa")), @@ -736,6 +737,7 @@ mod tests { } #[test] + #[allow(clippy::cognitive_complexity)] fn input_splitter_interrupt_rewind() { let input = vec![ Ok(String::from("aaa")), diff --git a/src/uu/csplit/src/patterns.rs b/src/uu/csplit/src/patterns.rs index fd96fd9fb9e..8e7b76e6bb4 100644 --- a/src/uu/csplit/src/patterns.rs +++ b/src/uu/csplit/src/patterns.rs @@ -211,6 +211,7 @@ mod tests { } #[test] + #[allow(clippy::cognitive_complexity)] fn up_to_match_pattern() { let input: Vec = vec![ "/test1.*end$/", @@ -264,6 +265,7 @@ mod tests { } #[test] + #[allow(clippy::cognitive_complexity)] fn skip_to_match_pattern() { let input: Vec = vec![ "%test1.*end$%", diff --git a/src/uu/dd/src/numbers.rs b/src/uu/dd/src/numbers.rs index 2911f7e58c5..8a6fa5a7a37 100644 --- a/src/uu/dd/src/numbers.rs +++ b/src/uu/dd/src/numbers.rs @@ -115,6 +115,7 @@ mod tests { } #[test] + #[allow(clippy::cognitive_complexity)] fn test_to_magnitude_and_suffix_not_powers_of_1024() { assert_eq!(to_magnitude_and_suffix(1, SuffixType::Si), "1.0 B"); assert_eq!(to_magnitude_and_suffix(999, SuffixType::Si), "999 B"); diff --git a/src/uu/dd/src/parseargs/unit_tests.rs b/src/uu/dd/src/parseargs/unit_tests.rs index a190fd75bd2..142e49fd0ba 100644 --- a/src/uu/dd/src/parseargs/unit_tests.rs +++ b/src/uu/dd/src/parseargs/unit_tests.rs @@ -103,6 +103,7 @@ fn test_status_level_none() { } #[test] +#[allow(clippy::cognitive_complexity)] fn test_all_top_level_args_no_leading_dashes() { let args = &[ "if=foo.file", diff --git a/src/uu/df/src/blocks.rs b/src/uu/df/src/blocks.rs index 9bc16b7820a..fad8f7ac0c4 100644 --- a/src/uu/df/src/blocks.rs +++ b/src/uu/df/src/blocks.rs @@ -239,6 +239,7 @@ mod tests { } #[test] + #[allow(clippy::cognitive_complexity)] fn test_to_magnitude_and_suffix_not_powers_of_1024() { assert_eq!(to_magnitude_and_suffix(1, SuffixType::Si), "1B"); assert_eq!(to_magnitude_and_suffix(999, SuffixType::Si), "999B"); diff --git a/src/uu/head/src/head.rs b/src/uu/head/src/head.rs index d6e4db5a78e..c533f5a5df0 100644 --- a/src/uu/head/src/head.rs +++ b/src/uu/head/src/head.rs @@ -548,6 +548,7 @@ mod tests { } #[test] + #[allow(clippy::cognitive_complexity)] fn all_args_test() { assert!(options("--silent").unwrap().quiet); assert!(options("--quiet").unwrap().quiet); diff --git a/src/uu/head/src/parse.rs b/src/uu/head/src/parse.rs index 7f7dd48f8c5..062a1844ce8 100644 --- a/src/uu/head/src/parse.rs +++ b/src/uu/head/src/parse.rs @@ -153,6 +153,7 @@ mod tests { } #[test] + #[allow(clippy::cognitive_complexity)] fn test_parse_numbers_obsolete() { assert_eq!(obsolete("-5"), obsolete_result(&["-n", "5"])); assert_eq!(obsolete("-100"), obsolete_result(&["-n", "100"])); diff --git a/src/uu/nl/src/nl.rs b/src/uu/nl/src/nl.rs index 71b4aac2886..61ca8406f1e 100644 --- a/src/uu/nl/src/nl.rs +++ b/src/uu/nl/src/nl.rs @@ -394,6 +394,7 @@ mod test { use super::*; #[test] + #[allow(clippy::cognitive_complexity)] fn test_format() { assert_eq!(NumberFormat::Left.format(12, 1), "12"); assert_eq!(NumberFormat::Left.format(-12, 1), "-12"); diff --git a/src/uu/numfmt/src/format.rs b/src/uu/numfmt/src/format.rs index 08bb0c2e774..034d900e92d 100644 --- a/src/uu/numfmt/src/format.rs +++ b/src/uu/numfmt/src/format.rs @@ -430,6 +430,7 @@ mod tests { use super::*; #[test] + #[allow(clippy::cognitive_complexity)] fn test_round_with_precision() { let rm = RoundMethod::FromZero; assert_eq!(1.0, round_with_precision(0.12345, rm, 0)); diff --git a/src/uu/numfmt/src/options.rs b/src/uu/numfmt/src/options.rs index 07b364f18d8..88e64e963e3 100644 --- a/src/uu/numfmt/src/options.rs +++ b/src/uu/numfmt/src/options.rs @@ -266,6 +266,7 @@ mod tests { } #[test] + #[allow(clippy::cognitive_complexity)] fn test_parse_format_with_invalid_formats() { assert!("".parse::().is_err()); assert!("hello".parse::().is_err()); diff --git a/src/uu/od/src/prn_float.rs b/src/uu/od/src/prn_float.rs index f4d018bd84f..f44abf7c41c 100644 --- a/src/uu/od/src/prn_float.rs +++ b/src/uu/od/src/prn_float.rs @@ -198,6 +198,7 @@ fn test_format_flo64() { } #[test] +#[allow(clippy::cognitive_complexity)] fn test_format_flo16() { assert_eq!(format_flo16(f16::from_bits(0x8400u16)), "-6.104e-5"); assert_eq!(format_flo16(f16::from_bits(0x8401u16)), "-6.109e-5"); diff --git a/src/uu/seq/src/numberparse.rs b/src/uu/seq/src/numberparse.rs index 46991725547..3f4b213955f 100644 --- a/src/uu/seq/src/numberparse.rs +++ b/src/uu/seq/src/numberparse.rs @@ -538,6 +538,7 @@ mod tests { } #[test] + #[allow(clippy::cognitive_complexity)] fn test_num_integral_digits() { // no decimal, no exponent assert_eq!(num_integral_digits("123"), 3); @@ -578,6 +579,7 @@ mod tests { } #[test] + #[allow(clippy::cognitive_complexity)] fn test_num_fractional_digits() { // no decimal, no exponent assert_eq!(num_fractional_digits("123"), 0); diff --git a/src/uu/split/src/number.rs b/src/uu/split/src/number.rs index 39d64f9272e..a01701c80e3 100644 --- a/src/uu/split/src/number.rs +++ b/src/uu/split/src/number.rs @@ -398,6 +398,7 @@ mod tests { } #[test] + #[allow(clippy::cognitive_complexity)] fn test_dynamic_width_number_display_alphabetic() { fn num(n: usize) -> Number { let mut number = Number::DynamicWidth(DynamicWidthNumber::new(26, 0)); @@ -443,6 +444,7 @@ mod tests { } #[test] + #[allow(clippy::cognitive_complexity)] fn test_dynamic_width_number_display_numeric_hexadecimal() { fn num(n: usize) -> Number { let mut number = Number::DynamicWidth(DynamicWidthNumber::new(16, 0)); @@ -467,6 +469,7 @@ mod tests { } #[test] + #[allow(clippy::cognitive_complexity)] fn test_fixed_width_number_increment() { let mut n = Number::FixedWidth(FixedWidthNumber::new(3, 2, 0).unwrap()); assert_eq!(n.digits(), vec![0, 0]); @@ -490,6 +493,7 @@ mod tests { } #[test] + #[allow(clippy::cognitive_complexity)] fn test_fixed_width_number_display_alphabetic() { fn num(n: usize) -> Result { let mut number = Number::FixedWidth(FixedWidthNumber::new(26, 2, 0).unwrap()); diff --git a/src/uu/split/src/split.rs b/src/uu/split/src/split.rs index 020ada93bb5..84b5900cc2c 100644 --- a/src/uu/split/src/split.rs +++ b/src/uu/split/src/split.rs @@ -1846,6 +1846,7 @@ mod tests { } #[test] + #[allow(clippy::cognitive_complexity)] fn test_number_type_from_error() { assert_eq!( NumberType::from("xyz").unwrap_err(), diff --git a/src/uu/stat/src/stat.rs b/src/uu/stat/src/stat.rs index 055393578e2..7d1fd574c25 100644 --- a/src/uu/stat/src/stat.rs +++ b/src/uu/stat/src/stat.rs @@ -947,6 +947,7 @@ mod tests { } #[test] + #[allow(clippy::cognitive_complexity)] fn test_group_num() { assert_eq!("12,379,821,234", group_num("12379821234")); assert_eq!("21,234", group_num("21234")); From ae1c4ccfd21e5b1c8541462c8afeac5c0f5fde05 Mon Sep 17 00:00:00 2001 From: Zhuoxun Yang Date: Sat, 14 Oct 2023 01:56:38 +0800 Subject: [PATCH 198/208] expr: short-circuit evaluation for `&` --- src/uu/expr/src/syntax_tree.rs | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/src/uu/expr/src/syntax_tree.rs b/src/uu/expr/src/syntax_tree.rs index 4ca723a4d8c..e0e786b3a3b 100644 --- a/src/uu/expr/src/syntax_tree.rs +++ b/src/uu/expr/src/syntax_tree.rs @@ -166,16 +166,26 @@ impl AstNode { { let mut out = Vec::with_capacity(operands.len()); let mut operands = operands.iter(); - // check the first value before `|`, stop evaluate and return directly if it is true. - // push dummy to pass the check of `len() == 2` - if op_type == "|" { - if let Some(value) = operands.next() { - let value = value.evaluate()?; - out.push(value.clone()); - if value_as_bool(&value) { - out.push(String::from("dummy")); - return Ok(out); + + if let Some(value) = operands.next() { + let value = value.evaluate()?; + out.push(value.clone()); + // short-circuit evaluation for `|` and `&` + // push dummy to pass `assert!(values.len() == 2);` + match op_type.as_ref() { + "|" => { + if value_as_bool(&value) { + out.push(String::from("dummy")); + return Ok(out); + } + } + "&" => { + if !value_as_bool(&value) { + out.push(String::from("dummy")); + return Ok(out); + } } + _ => {} } } From 40f05a331e26a6f27a86dfd13cd1eb8095b7b549 Mon Sep 17 00:00:00 2001 From: Zhuoxun Yang Date: Sat, 14 Oct 2023 01:58:53 +0800 Subject: [PATCH 199/208] tests/expr: add tests for test_and --- tests/by-util/test_expr.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/by-util/test_expr.rs b/tests/by-util/test_expr.rs index 4637d51c739..1064ef525ee 100644 --- a/tests/by-util/test_expr.rs +++ b/tests/by-util/test_expr.rs @@ -155,6 +155,21 @@ fn test_and() { .args(&["-14", "&", "1"]) .run() .stdout_is("-14\n"); + + new_ucmd!() + .args(&["0", "&", "a", "/", "5"]) + .run() + .stdout_only("0\n"); + + new_ucmd!() + .args(&["", "&", "a", "/", "5"]) + .run() + .stdout_only("0\n"); + + new_ucmd!() + .args(&["-1", "&", "10", "/", "5"]) + .succeeds() + .stdout_only("-1\n"); } #[test] From f979f148c11780a28fc23aafc2c68feab20b340a Mon Sep 17 00:00:00 2001 From: tommady Date: Sat, 14 Oct 2023 13:33:43 +0800 Subject: [PATCH 200/208] fuzz: store the corpus using GitHub Cache (#5363) --- .github/workflows/CICD.yml | 1 - .github/workflows/fuzzing.yml | 26 +++++++++++++------------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index b971b0e00f1..6583a009474 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -1012,4 +1012,3 @@ jobs: flags: ${{ steps.vars.outputs.CODECOV_FLAGS }} name: codecov-umbrella fail_ci_if_error: false - diff --git a/.github/workflows/fuzzing.yml b/.github/workflows/fuzzing.yml index 677df2c9f18..311d6a0d7ac 100644 --- a/.github/workflows/fuzzing.yml +++ b/.github/workflows/fuzzing.yml @@ -24,41 +24,41 @@ jobs: - name: Install `cargo-fuzz` run: cargo install cargo-fuzz - uses: Swatinem/rust-cache@v2 + - name: Restore Cached Corpus + uses: actions/cache/restore@v3 + with: + key: corpus-cache + path: | + fuzz/corpus - name: Run fuzz_date for XX seconds + continue-on-error: true shell: bash run: | - ## Run it - cd fuzz cargo +nightly fuzz run fuzz_date -- -max_total_time=${{ env.RUN_FOR }} -detect_leaks=0 - name: Run fuzz_test for XX seconds - continue-on-error: true shell: bash run: | - ## Run it - cd fuzz cargo +nightly fuzz run fuzz_test -- -max_total_time=${{ env.RUN_FOR }} -detect_leaks=0 - name: Run fuzz_expr for XX seconds continue-on-error: true shell: bash run: | - ## Run it - cd fuzz cargo +nightly fuzz run fuzz_expr -- -max_total_time=${{ env.RUN_FOR }} -detect_leaks=0 - name: Run fuzz_parse_glob for XX seconds shell: bash run: | - ## Run it - cd fuzz cargo +nightly fuzz run fuzz_parse_glob -- -max_total_time=${{ env.RUN_FOR }} -detect_leaks=0 - name: Run fuzz_parse_size for XX seconds shell: bash run: | - ## Run it - cd fuzz cargo +nightly fuzz run fuzz_parse_size -- -max_total_time=${{ env.RUN_FOR }} -detect_leaks=0 - name: Run fuzz_parse_time for XX seconds shell: bash run: | - ## Run it - cd fuzz cargo +nightly fuzz run fuzz_parse_time -- -max_total_time=${{ env.RUN_FOR }} -detect_leaks=0 + - name: Save Corpus Cache + uses: actions/cache/save@v3 + with: + key: corpus-cache + path: | + fuzz/corpus From f557c5936430de6b864e8b1f120a41b160d65f74 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sat, 14 Oct 2023 11:43:07 +0200 Subject: [PATCH 201/208] doc: recommend the rust-gdb helper --- DEVELOPMENT.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 308daba9818..9cb81a88ce4 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -157,7 +157,7 @@ cargo nextest run --features unix --no-fail-fast To debug: ```shell -gdb --args target/debug/coreutils ls +rust-gdb --args target/debug/coreutils ls (gdb) b ls.rs:79 (gdb) run ``` From f6880bff8f5faca21a2e420261d0aac258ac9eb7 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Sat, 14 Oct 2023 14:58:41 +0200 Subject: [PATCH 202/208] expr: test some invalid syntaxes --- tests/by-util/test_expr.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/by-util/test_expr.rs b/tests/by-util/test_expr.rs index 1064ef525ee..3f403c7ea37 100644 --- a/tests/by-util/test_expr.rs +++ b/tests/by-util/test_expr.rs @@ -275,3 +275,16 @@ fn test_invalid_substr() { .code_is(1) .stdout_only("\n"); } + +#[test] +fn test_invalid_syntax() { + let invalid_syntaxes = [["12", "12"], ["12", "|"], ["|", "12"]]; + + for invalid_syntax in invalid_syntaxes { + new_ucmd!() + .args(&invalid_syntax) + .fails() + .code_is(2) + .stderr_contains("syntax error"); + } +} From 5b1755387f85217680831e582b97fb62d002db0c Mon Sep 17 00:00:00 2001 From: Zhuoxun Yang Date: Sat, 14 Oct 2023 23:18:15 +0800 Subject: [PATCH 203/208] tests/expr: test escape --- tests/by-util/test_expr.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/by-util/test_expr.rs b/tests/by-util/test_expr.rs index 1064ef525ee..b70c60fb37e 100644 --- a/tests/by-util/test_expr.rs +++ b/tests/by-util/test_expr.rs @@ -138,6 +138,11 @@ fn test_or() { .args(&["0", "|", "10", "/", "5"]) .succeeds() .stdout_only("2\n"); + + new_ucmd!() + .args(&["12", "|", "9a", "+", "1"]) + .succeeds() + .stdout_only("12\n"); } #[test] @@ -275,3 +280,23 @@ fn test_invalid_substr() { .code_is(1) .stdout_only("\n"); } + +#[test] +fn test_escape() { + new_ucmd!().args(&["+", "1"]).succeeds().stdout_only("1\n"); + + new_ucmd!() + .args(&["1", "+", "+", "1"]) + .succeeds() + .stdout_only("2\n"); + + new_ucmd!() + .args(&["2", "*", "+", "3"]) + .succeeds() + .stdout_only("6\n"); + + new_ucmd!() + .args(&["(", "1", ")", "+", "1"]) + .succeeds() + .stdout_only("2\n"); +} From 4f20773b4f78dcd1d8648bb93c11c5141b6f9095 Mon Sep 17 00:00:00 2001 From: Zhuoxun Yang Date: Sat, 14 Oct 2023 23:20:45 +0800 Subject: [PATCH 204/208] expr: fix escape --- src/uu/expr/src/tokens.rs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/uu/expr/src/tokens.rs b/src/uu/expr/src/tokens.rs index 3c5cac060f9..8961935151b 100644 --- a/src/uu/expr/src/tokens.rs +++ b/src/uu/expr/src/tokens.rs @@ -16,8 +16,6 @@ // spell-checker:ignore (ToDO) paren -use num_bigint::BigInt; - #[derive(Debug, Clone)] pub enum Token { Value { @@ -59,11 +57,8 @@ impl Token { } } - fn is_a_number(&self) -> bool { - match self { - Self::Value { value, .. } => value.parse::().is_ok(), - _ => false, - } + fn is_a_value(&self) -> bool { + matches!(*self, Self::Value { .. }) } fn is_a_close_paren(&self) -> bool { @@ -131,14 +126,14 @@ fn maybe_dump_tokens_acc(tokens_acc: &[(usize, Token)]) { } fn push_token_if_not_escaped(acc: &mut Vec<(usize, Token)>, tok_idx: usize, token: Token, s: &str) { - // Smells heuristics... :( + // `+` may escaped such as `expr + 1` and `expr 1 + + 1` let prev_is_plus = match acc.last() { None => false, Some(t) => t.1.is_infix_plus(), }; let should_use_as_escaped = if prev_is_plus && acc.len() >= 2 { let pre_prev = &acc[acc.len() - 2]; - !(pre_prev.1.is_a_number() || pre_prev.1.is_a_close_paren()) + !(pre_prev.1.is_a_value() || pre_prev.1.is_a_close_paren()) } else { prev_is_plus }; From f89e6943b7ad7b00b4b0843cf756d3274b48357b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 14 Oct 2023 15:34:14 +0000 Subject: [PATCH 205/208] chore(deps): update rust crate regex to 1.10.1 --- Cargo.lock | 12 ++++++------ Cargo.toml | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a0a62d13ba6..f9be5346f61 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1728,9 +1728,9 @@ checksum = "f1bfbf25d7eb88ddcbb1ec3d755d0634da8f7657b2cb8b74089121409ab8228f" [[package]] name = "regex" -version = "1.10.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d119d7c7ca818f8a53c300863d4f87566aac09943aef5b355bb83969dae75d87" +checksum = "aaac441002f822bc9705a681810a4dd2963094b9ca0ddc41cb963a4c189189ea" dependencies = [ "aho-corasick", "memchr", @@ -1740,9 +1740,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "465c6fc0621e4abc4187a2bda0937bfd4f722c2730b29562e19689ea796c9a4b" +checksum = "5011c7e263a695dc8ca064cddb722af1be54e517a280b12a5356f98366899e5d" dependencies = [ "aho-corasick", "memchr", @@ -1751,9 +1751,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3cbb081b9784b07cceb8824c8583f86db4814d172ab043f3c23f7dc600bf83d" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "relative-path" diff --git a/Cargo.toml b/Cargo.toml index 820568e616e..975cced35bb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -309,7 +309,7 @@ rand = { version = "0.8", features = ["small_rng"] } rand_core = "0.6" rayon = "1.8" redox_syscall = "0.4" -regex = "1.10.0" +regex = "1.10.1" rstest = "0.18.2" rust-ini = "0.19.0" same-file = "1.0.6" From 6ef5b272da37e44c65ea69369f6b187fe871a177 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Sun, 15 Oct 2023 14:30:17 +0200 Subject: [PATCH 206/208] expr: add missing word to comment --- src/uu/expr/src/tokens.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uu/expr/src/tokens.rs b/src/uu/expr/src/tokens.rs index 8961935151b..f499881c138 100644 --- a/src/uu/expr/src/tokens.rs +++ b/src/uu/expr/src/tokens.rs @@ -126,7 +126,7 @@ fn maybe_dump_tokens_acc(tokens_acc: &[(usize, Token)]) { } fn push_token_if_not_escaped(acc: &mut Vec<(usize, Token)>, tok_idx: usize, token: Token, s: &str) { - // `+` may escaped such as `expr + 1` and `expr 1 + + 1` + // `+` may be escaped such as `expr + 1` and `expr 1 + + 1` let prev_is_plus = match acc.last() { None => false, Some(t) => t.1.is_infix_plus(), From e1bd47d5496b468607dfec1643b29fa2fbc8e0ee Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sun, 15 Oct 2023 14:20:18 +0200 Subject: [PATCH 207/208] 0.0.21 => 0.0.22 --- Cargo.lock | 214 ++++++++++++------------- Cargo.toml | 206 ++++++++++++------------ src/uu/arch/Cargo.toml | 2 +- src/uu/base32/Cargo.toml | 2 +- src/uu/base64/Cargo.toml | 2 +- src/uu/basename/Cargo.toml | 2 +- src/uu/basenc/Cargo.toml | 2 +- src/uu/cat/Cargo.toml | 2 +- src/uu/chcon/Cargo.toml | 2 +- src/uu/chgrp/Cargo.toml | 2 +- src/uu/chmod/Cargo.toml | 2 +- src/uu/chown/Cargo.toml | 2 +- src/uu/chroot/Cargo.toml | 2 +- src/uu/cksum/Cargo.toml | 2 +- src/uu/comm/Cargo.toml | 2 +- src/uu/cp/Cargo.toml | 2 +- src/uu/csplit/Cargo.toml | 2 +- src/uu/cut/Cargo.toml | 2 +- src/uu/date/Cargo.toml | 2 +- src/uu/dd/Cargo.toml | 2 +- src/uu/df/Cargo.toml | 2 +- src/uu/dir/Cargo.toml | 2 +- src/uu/dircolors/Cargo.toml | 2 +- src/uu/dirname/Cargo.toml | 2 +- src/uu/du/Cargo.toml | 2 +- src/uu/echo/Cargo.toml | 2 +- src/uu/env/Cargo.toml | 2 +- src/uu/expand/Cargo.toml | 2 +- src/uu/expr/Cargo.toml | 2 +- src/uu/factor/Cargo.toml | 2 +- src/uu/false/Cargo.toml | 2 +- src/uu/fmt/Cargo.toml | 2 +- src/uu/fold/Cargo.toml | 2 +- src/uu/groups/Cargo.toml | 2 +- src/uu/hashsum/Cargo.toml | 2 +- src/uu/head/Cargo.toml | 2 +- src/uu/hostid/Cargo.toml | 2 +- src/uu/hostname/Cargo.toml | 2 +- src/uu/id/Cargo.toml | 2 +- src/uu/install/Cargo.toml | 2 +- src/uu/join/Cargo.toml | 2 +- src/uu/kill/Cargo.toml | 2 +- src/uu/link/Cargo.toml | 2 +- src/uu/ln/Cargo.toml | 2 +- src/uu/logname/Cargo.toml | 2 +- src/uu/ls/Cargo.toml | 2 +- src/uu/mkdir/Cargo.toml | 2 +- src/uu/mkfifo/Cargo.toml | 2 +- src/uu/mknod/Cargo.toml | 2 +- src/uu/mktemp/Cargo.toml | 2 +- src/uu/more/Cargo.toml | 2 +- src/uu/mv/Cargo.toml | 2 +- src/uu/nice/Cargo.toml | 2 +- src/uu/nl/Cargo.toml | 2 +- src/uu/nohup/Cargo.toml | 2 +- src/uu/nproc/Cargo.toml | 2 +- src/uu/numfmt/Cargo.toml | 2 +- src/uu/od/Cargo.toml | 2 +- src/uu/paste/Cargo.toml | 2 +- src/uu/pathchk/Cargo.toml | 2 +- src/uu/pinky/Cargo.toml | 2 +- src/uu/pr/Cargo.toml | 2 +- src/uu/printenv/Cargo.toml | 2 +- src/uu/printf/Cargo.toml | 2 +- src/uu/ptx/Cargo.toml | 2 +- src/uu/pwd/Cargo.toml | 2 +- src/uu/readlink/Cargo.toml | 2 +- src/uu/realpath/Cargo.toml | 2 +- src/uu/rm/Cargo.toml | 2 +- src/uu/rmdir/Cargo.toml | 2 +- src/uu/runcon/Cargo.toml | 2 +- src/uu/seq/Cargo.toml | 2 +- src/uu/shred/Cargo.toml | 2 +- src/uu/shuf/Cargo.toml | 2 +- src/uu/sleep/Cargo.toml | 2 +- src/uu/sort/Cargo.toml | 2 +- src/uu/split/Cargo.toml | 2 +- src/uu/stat/Cargo.toml | 2 +- src/uu/stdbuf/Cargo.toml | 4 +- src/uu/stdbuf/src/libstdbuf/Cargo.toml | 2 +- src/uu/stty/Cargo.toml | 2 +- src/uu/sum/Cargo.toml | 2 +- src/uu/sync/Cargo.toml | 2 +- src/uu/tac/Cargo.toml | 2 +- src/uu/tail/Cargo.toml | 2 +- src/uu/tee/Cargo.toml | 2 +- src/uu/test/Cargo.toml | 2 +- src/uu/timeout/Cargo.toml | 2 +- src/uu/touch/Cargo.toml | 2 +- src/uu/tr/Cargo.toml | 2 +- src/uu/true/Cargo.toml | 2 +- src/uu/truncate/Cargo.toml | 2 +- src/uu/tsort/Cargo.toml | 2 +- src/uu/tty/Cargo.toml | 2 +- src/uu/uname/Cargo.toml | 2 +- src/uu/unexpand/Cargo.toml | 2 +- src/uu/uniq/Cargo.toml | 2 +- src/uu/unlink/Cargo.toml | 2 +- src/uu/uptime/Cargo.toml | 2 +- src/uu/users/Cargo.toml | 2 +- src/uu/vdir/Cargo.toml | 2 +- src/uu/wc/Cargo.toml | 2 +- src/uu/who/Cargo.toml | 2 +- src/uu/whoami/Cargo.toml | 2 +- src/uu/yes/Cargo.toml | 2 +- src/uucore/Cargo.toml | 2 +- src/uucore_procs/Cargo.toml | 4 +- src/uuhelp_parser/Cargo.toml | 2 +- util/update-version.sh | 4 +- 109 files changed, 320 insertions(+), 320 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f9be5346f61..a54656d7c4a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -376,7 +376,7 @@ checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" [[package]] name = "coreutils" -version = "0.0.21" +version = "0.0.22" dependencies = [ "chrono", "clap", @@ -2225,7 +2225,7 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "uu_arch" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "platform-info", @@ -2234,7 +2234,7 @@ dependencies = [ [[package]] name = "uu_base32" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "uucore", @@ -2242,7 +2242,7 @@ dependencies = [ [[package]] name = "uu_base64" -version = "0.0.21" +version = "0.0.22" dependencies = [ "uu_base32", "uucore", @@ -2250,7 +2250,7 @@ dependencies = [ [[package]] name = "uu_basename" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "uucore", @@ -2258,7 +2258,7 @@ dependencies = [ [[package]] name = "uu_basenc" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "uu_base32", @@ -2267,7 +2267,7 @@ dependencies = [ [[package]] name = "uu_cat" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "nix", @@ -2277,7 +2277,7 @@ dependencies = [ [[package]] name = "uu_chcon" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "fts-sys", @@ -2289,7 +2289,7 @@ dependencies = [ [[package]] name = "uu_chgrp" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "uucore", @@ -2297,7 +2297,7 @@ dependencies = [ [[package]] name = "uu_chmod" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "libc", @@ -2306,7 +2306,7 @@ dependencies = [ [[package]] name = "uu_chown" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "uucore", @@ -2314,7 +2314,7 @@ dependencies = [ [[package]] name = "uu_chroot" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "uucore", @@ -2322,7 +2322,7 @@ dependencies = [ [[package]] name = "uu_cksum" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "hex", @@ -2331,7 +2331,7 @@ dependencies = [ [[package]] name = "uu_comm" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "uucore", @@ -2339,7 +2339,7 @@ dependencies = [ [[package]] name = "uu_cp" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "exacl", @@ -2355,7 +2355,7 @@ dependencies = [ [[package]] name = "uu_csplit" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "regex", @@ -2365,7 +2365,7 @@ dependencies = [ [[package]] name = "uu_cut" -version = "0.0.21" +version = "0.0.22" dependencies = [ "bstr", "clap", @@ -2375,7 +2375,7 @@ dependencies = [ [[package]] name = "uu_date" -version = "0.0.21" +version = "0.0.22" dependencies = [ "chrono", "clap", @@ -2387,7 +2387,7 @@ dependencies = [ [[package]] name = "uu_dd" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "gcd", @@ -2399,7 +2399,7 @@ dependencies = [ [[package]] name = "uu_df" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "tempfile", @@ -2409,7 +2409,7 @@ dependencies = [ [[package]] name = "uu_dir" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "uu_ls", @@ -2418,7 +2418,7 @@ dependencies = [ [[package]] name = "uu_dircolors" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "uucore", @@ -2426,7 +2426,7 @@ dependencies = [ [[package]] name = "uu_dirname" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "uucore", @@ -2434,7 +2434,7 @@ dependencies = [ [[package]] name = "uu_du" -version = "0.0.21" +version = "0.0.22" dependencies = [ "chrono", "clap", @@ -2445,7 +2445,7 @@ dependencies = [ [[package]] name = "uu_echo" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "uucore", @@ -2453,7 +2453,7 @@ dependencies = [ [[package]] name = "uu_env" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "nix", @@ -2463,7 +2463,7 @@ dependencies = [ [[package]] name = "uu_expand" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "unicode-width", @@ -2472,7 +2472,7 @@ dependencies = [ [[package]] name = "uu_expr" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "num-bigint", @@ -2483,7 +2483,7 @@ dependencies = [ [[package]] name = "uu_factor" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "coz", @@ -2496,7 +2496,7 @@ dependencies = [ [[package]] name = "uu_false" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "uucore", @@ -2504,7 +2504,7 @@ dependencies = [ [[package]] name = "uu_fmt" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "unicode-width", @@ -2513,7 +2513,7 @@ dependencies = [ [[package]] name = "uu_fold" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "uucore", @@ -2521,7 +2521,7 @@ dependencies = [ [[package]] name = "uu_groups" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "uucore", @@ -2529,7 +2529,7 @@ dependencies = [ [[package]] name = "uu_hashsum" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "hex", @@ -2540,7 +2540,7 @@ dependencies = [ [[package]] name = "uu_head" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "memchr", @@ -2549,7 +2549,7 @@ dependencies = [ [[package]] name = "uu_hostid" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "libc", @@ -2558,7 +2558,7 @@ dependencies = [ [[package]] name = "uu_hostname" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "hostname", @@ -2568,7 +2568,7 @@ dependencies = [ [[package]] name = "uu_id" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "selinux", @@ -2577,7 +2577,7 @@ dependencies = [ [[package]] name = "uu_install" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "file_diff", @@ -2588,7 +2588,7 @@ dependencies = [ [[package]] name = "uu_join" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "memchr", @@ -2597,7 +2597,7 @@ dependencies = [ [[package]] name = "uu_kill" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "nix", @@ -2606,7 +2606,7 @@ dependencies = [ [[package]] name = "uu_link" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "uucore", @@ -2614,7 +2614,7 @@ dependencies = [ [[package]] name = "uu_ln" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "uucore", @@ -2622,7 +2622,7 @@ dependencies = [ [[package]] name = "uu_logname" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "libc", @@ -2631,7 +2631,7 @@ dependencies = [ [[package]] name = "uu_ls" -version = "0.0.21" +version = "0.0.22" dependencies = [ "chrono", "clap", @@ -2648,7 +2648,7 @@ dependencies = [ [[package]] name = "uu_mkdir" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "uucore", @@ -2656,7 +2656,7 @@ dependencies = [ [[package]] name = "uu_mkfifo" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "libc", @@ -2665,7 +2665,7 @@ dependencies = [ [[package]] name = "uu_mknod" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "libc", @@ -2674,7 +2674,7 @@ dependencies = [ [[package]] name = "uu_mktemp" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "rand", @@ -2684,7 +2684,7 @@ dependencies = [ [[package]] name = "uu_more" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "crossterm", @@ -2696,7 +2696,7 @@ dependencies = [ [[package]] name = "uu_mv" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "fs_extra", @@ -2706,7 +2706,7 @@ dependencies = [ [[package]] name = "uu_nice" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "libc", @@ -2716,7 +2716,7 @@ dependencies = [ [[package]] name = "uu_nl" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "regex", @@ -2725,7 +2725,7 @@ dependencies = [ [[package]] name = "uu_nohup" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "libc", @@ -2734,7 +2734,7 @@ dependencies = [ [[package]] name = "uu_nproc" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "libc", @@ -2743,7 +2743,7 @@ dependencies = [ [[package]] name = "uu_numfmt" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "uucore", @@ -2751,7 +2751,7 @@ dependencies = [ [[package]] name = "uu_od" -version = "0.0.21" +version = "0.0.22" dependencies = [ "byteorder", "clap", @@ -2761,7 +2761,7 @@ dependencies = [ [[package]] name = "uu_paste" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "uucore", @@ -2769,7 +2769,7 @@ dependencies = [ [[package]] name = "uu_pathchk" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "libc", @@ -2778,7 +2778,7 @@ dependencies = [ [[package]] name = "uu_pinky" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "uucore", @@ -2786,7 +2786,7 @@ dependencies = [ [[package]] name = "uu_pr" -version = "0.0.21" +version = "0.0.22" dependencies = [ "chrono", "clap", @@ -2798,7 +2798,7 @@ dependencies = [ [[package]] name = "uu_printenv" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "uucore", @@ -2806,7 +2806,7 @@ dependencies = [ [[package]] name = "uu_printf" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "uucore", @@ -2814,7 +2814,7 @@ dependencies = [ [[package]] name = "uu_ptx" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "regex", @@ -2823,7 +2823,7 @@ dependencies = [ [[package]] name = "uu_pwd" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "uucore", @@ -2831,7 +2831,7 @@ dependencies = [ [[package]] name = "uu_readlink" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "uucore", @@ -2839,7 +2839,7 @@ dependencies = [ [[package]] name = "uu_realpath" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "uucore", @@ -2847,7 +2847,7 @@ dependencies = [ [[package]] name = "uu_rm" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "libc", @@ -2858,7 +2858,7 @@ dependencies = [ [[package]] name = "uu_rmdir" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "libc", @@ -2867,7 +2867,7 @@ dependencies = [ [[package]] name = "uu_runcon" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "libc", @@ -2878,7 +2878,7 @@ dependencies = [ [[package]] name = "uu_seq" -version = "0.0.21" +version = "0.0.22" dependencies = [ "bigdecimal", "clap", @@ -2889,7 +2889,7 @@ dependencies = [ [[package]] name = "uu_shred" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "libc", @@ -2899,7 +2899,7 @@ dependencies = [ [[package]] name = "uu_shuf" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "memchr", @@ -2910,7 +2910,7 @@ dependencies = [ [[package]] name = "uu_sleep" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "fundu", @@ -2919,7 +2919,7 @@ dependencies = [ [[package]] name = "uu_sort" -version = "0.0.21" +version = "0.0.22" dependencies = [ "binary-heap-plus", "clap", @@ -2938,7 +2938,7 @@ dependencies = [ [[package]] name = "uu_split" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "memchr", @@ -2947,7 +2947,7 @@ dependencies = [ [[package]] name = "uu_stat" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "uucore", @@ -2955,7 +2955,7 @@ dependencies = [ [[package]] name = "uu_stdbuf" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "tempfile", @@ -2965,7 +2965,7 @@ dependencies = [ [[package]] name = "uu_stdbuf_libstdbuf" -version = "0.0.21" +version = "0.0.22" dependencies = [ "cpp", "cpp_build", @@ -2975,7 +2975,7 @@ dependencies = [ [[package]] name = "uu_stty" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "nix", @@ -2984,7 +2984,7 @@ dependencies = [ [[package]] name = "uu_sum" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "uucore", @@ -2992,7 +2992,7 @@ dependencies = [ [[package]] name = "uu_sync" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "libc", @@ -3003,7 +3003,7 @@ dependencies = [ [[package]] name = "uu_tac" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "memchr", @@ -3014,7 +3014,7 @@ dependencies = [ [[package]] name = "uu_tail" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "fundu", @@ -3030,7 +3030,7 @@ dependencies = [ [[package]] name = "uu_tee" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "libc", @@ -3039,7 +3039,7 @@ dependencies = [ [[package]] name = "uu_test" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "libc", @@ -3049,7 +3049,7 @@ dependencies = [ [[package]] name = "uu_timeout" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "libc", @@ -3059,7 +3059,7 @@ dependencies = [ [[package]] name = "uu_touch" -version = "0.0.21" +version = "0.0.22" dependencies = [ "chrono", "clap", @@ -3071,7 +3071,7 @@ dependencies = [ [[package]] name = "uu_tr" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "nom", @@ -3080,7 +3080,7 @@ dependencies = [ [[package]] name = "uu_true" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "uucore", @@ -3088,7 +3088,7 @@ dependencies = [ [[package]] name = "uu_truncate" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "uucore", @@ -3096,7 +3096,7 @@ dependencies = [ [[package]] name = "uu_tsort" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "uucore", @@ -3104,7 +3104,7 @@ dependencies = [ [[package]] name = "uu_tty" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "nix", @@ -3113,7 +3113,7 @@ dependencies = [ [[package]] name = "uu_uname" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "platform-info", @@ -3122,7 +3122,7 @@ dependencies = [ [[package]] name = "uu_unexpand" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "unicode-width", @@ -3131,7 +3131,7 @@ dependencies = [ [[package]] name = "uu_uniq" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "uucore", @@ -3139,7 +3139,7 @@ dependencies = [ [[package]] name = "uu_unlink" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "uucore", @@ -3147,7 +3147,7 @@ dependencies = [ [[package]] name = "uu_uptime" -version = "0.0.21" +version = "0.0.22" dependencies = [ "chrono", "clap", @@ -3156,7 +3156,7 @@ dependencies = [ [[package]] name = "uu_users" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "uucore", @@ -3164,7 +3164,7 @@ dependencies = [ [[package]] name = "uu_vdir" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "uu_ls", @@ -3173,7 +3173,7 @@ dependencies = [ [[package]] name = "uu_wc" -version = "0.0.21" +version = "0.0.22" dependencies = [ "bytecount", "clap", @@ -3186,7 +3186,7 @@ dependencies = [ [[package]] name = "uu_who" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "uucore", @@ -3194,7 +3194,7 @@ dependencies = [ [[package]] name = "uu_whoami" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "libc", @@ -3204,7 +3204,7 @@ dependencies = [ [[package]] name = "uu_yes" -version = "0.0.21" +version = "0.0.22" dependencies = [ "clap", "itertools", @@ -3214,7 +3214,7 @@ dependencies = [ [[package]] name = "uucore" -version = "0.0.21" +version = "0.0.22" dependencies = [ "blake2b_simd", "blake3", @@ -3250,7 +3250,7 @@ dependencies = [ [[package]] name = "uucore_procs" -version = "0.0.21" +version = "0.0.22" dependencies = [ "proc-macro2", "quote", @@ -3259,7 +3259,7 @@ dependencies = [ [[package]] name = "uuhelp_parser" -version = "0.0.21" +version = "0.0.22" [[package]] name = "uuid" diff --git a/Cargo.toml b/Cargo.toml index 975cced35bb..39232b50d64 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ [package] name = "coreutils" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "coreutils ~ GNU coreutils (updated); implemented as universal (cross-platform) utils, written in Rust" @@ -361,109 +361,109 @@ zip = { workspace = true, optional = true } uuhelp_parser = { optional = true, version = ">=0.0.19", path = "src/uuhelp_parser" } # * uutils -uu_test = { optional = true, version = "0.0.21", package = "uu_test", path = "src/uu/test" } +uu_test = { optional = true, version = "0.0.22", package = "uu_test", path = "src/uu/test" } # -arch = { optional = true, version = "0.0.21", package = "uu_arch", path = "src/uu/arch" } -base32 = { optional = true, version = "0.0.21", package = "uu_base32", path = "src/uu/base32" } -base64 = { optional = true, version = "0.0.21", package = "uu_base64", path = "src/uu/base64" } -basename = { optional = true, version = "0.0.21", package = "uu_basename", path = "src/uu/basename" } -basenc = { optional = true, version = "0.0.21", package = "uu_basenc", path = "src/uu/basenc" } -cat = { optional = true, version = "0.0.21", package = "uu_cat", path = "src/uu/cat" } -chcon = { optional = true, version = "0.0.21", package = "uu_chcon", path = "src/uu/chcon" } -chgrp = { optional = true, version = "0.0.21", package = "uu_chgrp", path = "src/uu/chgrp" } -chmod = { optional = true, version = "0.0.21", package = "uu_chmod", path = "src/uu/chmod" } -chown = { optional = true, version = "0.0.21", package = "uu_chown", path = "src/uu/chown" } -chroot = { optional = true, version = "0.0.21", package = "uu_chroot", path = "src/uu/chroot" } -cksum = { optional = true, version = "0.0.21", package = "uu_cksum", path = "src/uu/cksum" } -comm = { optional = true, version = "0.0.21", package = "uu_comm", path = "src/uu/comm" } -cp = { optional = true, version = "0.0.21", package = "uu_cp", path = "src/uu/cp" } -csplit = { optional = true, version = "0.0.21", package = "uu_csplit", path = "src/uu/csplit" } -cut = { optional = true, version = "0.0.21", package = "uu_cut", path = "src/uu/cut" } -date = { optional = true, version = "0.0.21", package = "uu_date", path = "src/uu/date" } -dd = { optional = true, version = "0.0.21", package = "uu_dd", path = "src/uu/dd" } -df = { optional = true, version = "0.0.21", package = "uu_df", path = "src/uu/df" } -dir = { optional = true, version = "0.0.21", package = "uu_dir", path = "src/uu/dir" } -dircolors = { optional = true, version = "0.0.21", package = "uu_dircolors", path = "src/uu/dircolors" } -dirname = { optional = true, version = "0.0.21", package = "uu_dirname", path = "src/uu/dirname" } -du = { optional = true, version = "0.0.21", package = "uu_du", path = "src/uu/du" } -echo = { optional = true, version = "0.0.21", package = "uu_echo", path = "src/uu/echo" } -env = { optional = true, version = "0.0.21", package = "uu_env", path = "src/uu/env" } -expand = { optional = true, version = "0.0.21", package = "uu_expand", path = "src/uu/expand" } -expr = { optional = true, version = "0.0.21", package = "uu_expr", path = "src/uu/expr" } -factor = { optional = true, version = "0.0.21", package = "uu_factor", path = "src/uu/factor" } -false = { optional = true, version = "0.0.21", package = "uu_false", path = "src/uu/false" } -fmt = { optional = true, version = "0.0.21", package = "uu_fmt", path = "src/uu/fmt" } -fold = { optional = true, version = "0.0.21", package = "uu_fold", path = "src/uu/fold" } -groups = { optional = true, version = "0.0.21", package = "uu_groups", path = "src/uu/groups" } -hashsum = { optional = true, version = "0.0.21", package = "uu_hashsum", path = "src/uu/hashsum" } -head = { optional = true, version = "0.0.21", package = "uu_head", path = "src/uu/head" } -hostid = { optional = true, version = "0.0.21", package = "uu_hostid", path = "src/uu/hostid" } -hostname = { optional = true, version = "0.0.21", package = "uu_hostname", path = "src/uu/hostname" } -id = { optional = true, version = "0.0.21", package = "uu_id", path = "src/uu/id" } -install = { optional = true, version = "0.0.21", package = "uu_install", path = "src/uu/install" } -join = { optional = true, version = "0.0.21", package = "uu_join", path = "src/uu/join" } -kill = { optional = true, version = "0.0.21", package = "uu_kill", path = "src/uu/kill" } -link = { optional = true, version = "0.0.21", package = "uu_link", path = "src/uu/link" } -ln = { optional = true, version = "0.0.21", package = "uu_ln", path = "src/uu/ln" } -ls = { optional = true, version = "0.0.21", package = "uu_ls", path = "src/uu/ls" } -logname = { optional = true, version = "0.0.21", package = "uu_logname", path = "src/uu/logname" } -mkdir = { optional = true, version = "0.0.21", package = "uu_mkdir", path = "src/uu/mkdir" } -mkfifo = { optional = true, version = "0.0.21", package = "uu_mkfifo", path = "src/uu/mkfifo" } -mknod = { optional = true, version = "0.0.21", package = "uu_mknod", path = "src/uu/mknod" } -mktemp = { optional = true, version = "0.0.21", package = "uu_mktemp", path = "src/uu/mktemp" } -more = { optional = true, version = "0.0.21", package = "uu_more", path = "src/uu/more" } -mv = { optional = true, version = "0.0.21", package = "uu_mv", path = "src/uu/mv" } -nice = { optional = true, version = "0.0.21", package = "uu_nice", path = "src/uu/nice" } -nl = { optional = true, version = "0.0.21", package = "uu_nl", path = "src/uu/nl" } -nohup = { optional = true, version = "0.0.21", package = "uu_nohup", path = "src/uu/nohup" } -nproc = { optional = true, version = "0.0.21", package = "uu_nproc", path = "src/uu/nproc" } -numfmt = { optional = true, version = "0.0.21", package = "uu_numfmt", path = "src/uu/numfmt" } -od = { optional = true, version = "0.0.21", package = "uu_od", path = "src/uu/od" } -paste = { optional = true, version = "0.0.21", package = "uu_paste", path = "src/uu/paste" } -pathchk = { optional = true, version = "0.0.21", package = "uu_pathchk", path = "src/uu/pathchk" } -pinky = { optional = true, version = "0.0.21", package = "uu_pinky", path = "src/uu/pinky" } -pr = { optional = true, version = "0.0.21", package = "uu_pr", path = "src/uu/pr" } -printenv = { optional = true, version = "0.0.21", package = "uu_printenv", path = "src/uu/printenv" } -printf = { optional = true, version = "0.0.21", package = "uu_printf", path = "src/uu/printf" } -ptx = { optional = true, version = "0.0.21", package = "uu_ptx", path = "src/uu/ptx" } -pwd = { optional = true, version = "0.0.21", package = "uu_pwd", path = "src/uu/pwd" } -readlink = { optional = true, version = "0.0.21", package = "uu_readlink", path = "src/uu/readlink" } -realpath = { optional = true, version = "0.0.21", package = "uu_realpath", path = "src/uu/realpath" } -rm = { optional = true, version = "0.0.21", package = "uu_rm", path = "src/uu/rm" } -rmdir = { optional = true, version = "0.0.21", package = "uu_rmdir", path = "src/uu/rmdir" } -runcon = { optional = true, version = "0.0.21", package = "uu_runcon", path = "src/uu/runcon" } -seq = { optional = true, version = "0.0.21", package = "uu_seq", path = "src/uu/seq" } -shred = { optional = true, version = "0.0.21", package = "uu_shred", path = "src/uu/shred" } -shuf = { optional = true, version = "0.0.21", package = "uu_shuf", path = "src/uu/shuf" } -sleep = { optional = true, version = "0.0.21", package = "uu_sleep", path = "src/uu/sleep" } -sort = { optional = true, version = "0.0.21", package = "uu_sort", path = "src/uu/sort" } -split = { optional = true, version = "0.0.21", package = "uu_split", path = "src/uu/split" } -stat = { optional = true, version = "0.0.21", package = "uu_stat", path = "src/uu/stat" } -stdbuf = { optional = true, version = "0.0.21", package = "uu_stdbuf", path = "src/uu/stdbuf" } -stty = { optional = true, version = "0.0.21", package = "uu_stty", path = "src/uu/stty" } -sum = { optional = true, version = "0.0.21", package = "uu_sum", path = "src/uu/sum" } -sync = { optional = true, version = "0.0.21", package = "uu_sync", path = "src/uu/sync" } -tac = { optional = true, version = "0.0.21", package = "uu_tac", path = "src/uu/tac" } -tail = { optional = true, version = "0.0.21", package = "uu_tail", path = "src/uu/tail" } -tee = { optional = true, version = "0.0.21", package = "uu_tee", path = "src/uu/tee" } -timeout = { optional = true, version = "0.0.21", package = "uu_timeout", path = "src/uu/timeout" } -touch = { optional = true, version = "0.0.21", package = "uu_touch", path = "src/uu/touch" } -tr = { optional = true, version = "0.0.21", package = "uu_tr", path = "src/uu/tr" } -true = { optional = true, version = "0.0.21", package = "uu_true", path = "src/uu/true" } -truncate = { optional = true, version = "0.0.21", package = "uu_truncate", path = "src/uu/truncate" } -tsort = { optional = true, version = "0.0.21", package = "uu_tsort", path = "src/uu/tsort" } -tty = { optional = true, version = "0.0.21", package = "uu_tty", path = "src/uu/tty" } -uname = { optional = true, version = "0.0.21", package = "uu_uname", path = "src/uu/uname" } -unexpand = { optional = true, version = "0.0.21", package = "uu_unexpand", path = "src/uu/unexpand" } -uniq = { optional = true, version = "0.0.21", package = "uu_uniq", path = "src/uu/uniq" } -unlink = { optional = true, version = "0.0.21", package = "uu_unlink", path = "src/uu/unlink" } -uptime = { optional = true, version = "0.0.21", package = "uu_uptime", path = "src/uu/uptime" } -users = { optional = true, version = "0.0.21", package = "uu_users", path = "src/uu/users" } -vdir = { optional = true, version = "0.0.21", package = "uu_vdir", path = "src/uu/vdir" } -wc = { optional = true, version = "0.0.21", package = "uu_wc", path = "src/uu/wc" } -who = { optional = true, version = "0.0.21", package = "uu_who", path = "src/uu/who" } -whoami = { optional = true, version = "0.0.21", package = "uu_whoami", path = "src/uu/whoami" } -yes = { optional = true, version = "0.0.21", package = "uu_yes", path = "src/uu/yes" } +arch = { optional = true, version = "0.0.22", package = "uu_arch", path = "src/uu/arch" } +base32 = { optional = true, version = "0.0.22", package = "uu_base32", path = "src/uu/base32" } +base64 = { optional = true, version = "0.0.22", package = "uu_base64", path = "src/uu/base64" } +basename = { optional = true, version = "0.0.22", package = "uu_basename", path = "src/uu/basename" } +basenc = { optional = true, version = "0.0.22", package = "uu_basenc", path = "src/uu/basenc" } +cat = { optional = true, version = "0.0.22", package = "uu_cat", path = "src/uu/cat" } +chcon = { optional = true, version = "0.0.22", package = "uu_chcon", path = "src/uu/chcon" } +chgrp = { optional = true, version = "0.0.22", package = "uu_chgrp", path = "src/uu/chgrp" } +chmod = { optional = true, version = "0.0.22", package = "uu_chmod", path = "src/uu/chmod" } +chown = { optional = true, version = "0.0.22", package = "uu_chown", path = "src/uu/chown" } +chroot = { optional = true, version = "0.0.22", package = "uu_chroot", path = "src/uu/chroot" } +cksum = { optional = true, version = "0.0.22", package = "uu_cksum", path = "src/uu/cksum" } +comm = { optional = true, version = "0.0.22", package = "uu_comm", path = "src/uu/comm" } +cp = { optional = true, version = "0.0.22", package = "uu_cp", path = "src/uu/cp" } +csplit = { optional = true, version = "0.0.22", package = "uu_csplit", path = "src/uu/csplit" } +cut = { optional = true, version = "0.0.22", package = "uu_cut", path = "src/uu/cut" } +date = { optional = true, version = "0.0.22", package = "uu_date", path = "src/uu/date" } +dd = { optional = true, version = "0.0.22", package = "uu_dd", path = "src/uu/dd" } +df = { optional = true, version = "0.0.22", package = "uu_df", path = "src/uu/df" } +dir = { optional = true, version = "0.0.22", package = "uu_dir", path = "src/uu/dir" } +dircolors = { optional = true, version = "0.0.22", package = "uu_dircolors", path = "src/uu/dircolors" } +dirname = { optional = true, version = "0.0.22", package = "uu_dirname", path = "src/uu/dirname" } +du = { optional = true, version = "0.0.22", package = "uu_du", path = "src/uu/du" } +echo = { optional = true, version = "0.0.22", package = "uu_echo", path = "src/uu/echo" } +env = { optional = true, version = "0.0.22", package = "uu_env", path = "src/uu/env" } +expand = { optional = true, version = "0.0.22", package = "uu_expand", path = "src/uu/expand" } +expr = { optional = true, version = "0.0.22", package = "uu_expr", path = "src/uu/expr" } +factor = { optional = true, version = "0.0.22", package = "uu_factor", path = "src/uu/factor" } +false = { optional = true, version = "0.0.22", package = "uu_false", path = "src/uu/false" } +fmt = { optional = true, version = "0.0.22", package = "uu_fmt", path = "src/uu/fmt" } +fold = { optional = true, version = "0.0.22", package = "uu_fold", path = "src/uu/fold" } +groups = { optional = true, version = "0.0.22", package = "uu_groups", path = "src/uu/groups" } +hashsum = { optional = true, version = "0.0.22", package = "uu_hashsum", path = "src/uu/hashsum" } +head = { optional = true, version = "0.0.22", package = "uu_head", path = "src/uu/head" } +hostid = { optional = true, version = "0.0.22", package = "uu_hostid", path = "src/uu/hostid" } +hostname = { optional = true, version = "0.0.22", package = "uu_hostname", path = "src/uu/hostname" } +id = { optional = true, version = "0.0.22", package = "uu_id", path = "src/uu/id" } +install = { optional = true, version = "0.0.22", package = "uu_install", path = "src/uu/install" } +join = { optional = true, version = "0.0.22", package = "uu_join", path = "src/uu/join" } +kill = { optional = true, version = "0.0.22", package = "uu_kill", path = "src/uu/kill" } +link = { optional = true, version = "0.0.22", package = "uu_link", path = "src/uu/link" } +ln = { optional = true, version = "0.0.22", package = "uu_ln", path = "src/uu/ln" } +ls = { optional = true, version = "0.0.22", package = "uu_ls", path = "src/uu/ls" } +logname = { optional = true, version = "0.0.22", package = "uu_logname", path = "src/uu/logname" } +mkdir = { optional = true, version = "0.0.22", package = "uu_mkdir", path = "src/uu/mkdir" } +mkfifo = { optional = true, version = "0.0.22", package = "uu_mkfifo", path = "src/uu/mkfifo" } +mknod = { optional = true, version = "0.0.22", package = "uu_mknod", path = "src/uu/mknod" } +mktemp = { optional = true, version = "0.0.22", package = "uu_mktemp", path = "src/uu/mktemp" } +more = { optional = true, version = "0.0.22", package = "uu_more", path = "src/uu/more" } +mv = { optional = true, version = "0.0.22", package = "uu_mv", path = "src/uu/mv" } +nice = { optional = true, version = "0.0.22", package = "uu_nice", path = "src/uu/nice" } +nl = { optional = true, version = "0.0.22", package = "uu_nl", path = "src/uu/nl" } +nohup = { optional = true, version = "0.0.22", package = "uu_nohup", path = "src/uu/nohup" } +nproc = { optional = true, version = "0.0.22", package = "uu_nproc", path = "src/uu/nproc" } +numfmt = { optional = true, version = "0.0.22", package = "uu_numfmt", path = "src/uu/numfmt" } +od = { optional = true, version = "0.0.22", package = "uu_od", path = "src/uu/od" } +paste = { optional = true, version = "0.0.22", package = "uu_paste", path = "src/uu/paste" } +pathchk = { optional = true, version = "0.0.22", package = "uu_pathchk", path = "src/uu/pathchk" } +pinky = { optional = true, version = "0.0.22", package = "uu_pinky", path = "src/uu/pinky" } +pr = { optional = true, version = "0.0.22", package = "uu_pr", path = "src/uu/pr" } +printenv = { optional = true, version = "0.0.22", package = "uu_printenv", path = "src/uu/printenv" } +printf = { optional = true, version = "0.0.22", package = "uu_printf", path = "src/uu/printf" } +ptx = { optional = true, version = "0.0.22", package = "uu_ptx", path = "src/uu/ptx" } +pwd = { optional = true, version = "0.0.22", package = "uu_pwd", path = "src/uu/pwd" } +readlink = { optional = true, version = "0.0.22", package = "uu_readlink", path = "src/uu/readlink" } +realpath = { optional = true, version = "0.0.22", package = "uu_realpath", path = "src/uu/realpath" } +rm = { optional = true, version = "0.0.22", package = "uu_rm", path = "src/uu/rm" } +rmdir = { optional = true, version = "0.0.22", package = "uu_rmdir", path = "src/uu/rmdir" } +runcon = { optional = true, version = "0.0.22", package = "uu_runcon", path = "src/uu/runcon" } +seq = { optional = true, version = "0.0.22", package = "uu_seq", path = "src/uu/seq" } +shred = { optional = true, version = "0.0.22", package = "uu_shred", path = "src/uu/shred" } +shuf = { optional = true, version = "0.0.22", package = "uu_shuf", path = "src/uu/shuf" } +sleep = { optional = true, version = "0.0.22", package = "uu_sleep", path = "src/uu/sleep" } +sort = { optional = true, version = "0.0.22", package = "uu_sort", path = "src/uu/sort" } +split = { optional = true, version = "0.0.22", package = "uu_split", path = "src/uu/split" } +stat = { optional = true, version = "0.0.22", package = "uu_stat", path = "src/uu/stat" } +stdbuf = { optional = true, version = "0.0.22", package = "uu_stdbuf", path = "src/uu/stdbuf" } +stty = { optional = true, version = "0.0.22", package = "uu_stty", path = "src/uu/stty" } +sum = { optional = true, version = "0.0.22", package = "uu_sum", path = "src/uu/sum" } +sync = { optional = true, version = "0.0.22", package = "uu_sync", path = "src/uu/sync" } +tac = { optional = true, version = "0.0.22", package = "uu_tac", path = "src/uu/tac" } +tail = { optional = true, version = "0.0.22", package = "uu_tail", path = "src/uu/tail" } +tee = { optional = true, version = "0.0.22", package = "uu_tee", path = "src/uu/tee" } +timeout = { optional = true, version = "0.0.22", package = "uu_timeout", path = "src/uu/timeout" } +touch = { optional = true, version = "0.0.22", package = "uu_touch", path = "src/uu/touch" } +tr = { optional = true, version = "0.0.22", package = "uu_tr", path = "src/uu/tr" } +true = { optional = true, version = "0.0.22", package = "uu_true", path = "src/uu/true" } +truncate = { optional = true, version = "0.0.22", package = "uu_truncate", path = "src/uu/truncate" } +tsort = { optional = true, version = "0.0.22", package = "uu_tsort", path = "src/uu/tsort" } +tty = { optional = true, version = "0.0.22", package = "uu_tty", path = "src/uu/tty" } +uname = { optional = true, version = "0.0.22", package = "uu_uname", path = "src/uu/uname" } +unexpand = { optional = true, version = "0.0.22", package = "uu_unexpand", path = "src/uu/unexpand" } +uniq = { optional = true, version = "0.0.22", package = "uu_uniq", path = "src/uu/uniq" } +unlink = { optional = true, version = "0.0.22", package = "uu_unlink", path = "src/uu/unlink" } +uptime = { optional = true, version = "0.0.22", package = "uu_uptime", path = "src/uu/uptime" } +users = { optional = true, version = "0.0.22", package = "uu_users", path = "src/uu/users" } +vdir = { optional = true, version = "0.0.22", package = "uu_vdir", path = "src/uu/vdir" } +wc = { optional = true, version = "0.0.22", package = "uu_wc", path = "src/uu/wc" } +who = { optional = true, version = "0.0.22", package = "uu_who", path = "src/uu/who" } +whoami = { optional = true, version = "0.0.22", package = "uu_whoami", path = "src/uu/whoami" } +yes = { optional = true, version = "0.0.22", package = "uu_yes", path = "src/uu/yes" } # this breaks clippy linting with: "tests/by-util/test_factor_benches.rs: No such file or directory (os error 2)" # factor_benches = { optional = true, version = "0.0.0", package = "uu_factor_benches", path = "tests/benches/factor" } diff --git a/src/uu/arch/Cargo.toml b/src/uu/arch/Cargo.toml index cd12cb4d483..9686c3f0acd 100644 --- a/src/uu/arch/Cargo.toml +++ b/src/uu/arch/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_arch" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "arch ~ (uutils) display machine architecture" diff --git a/src/uu/base32/Cargo.toml b/src/uu/base32/Cargo.toml index 044b92d23fc..f3bca4a1972 100644 --- a/src/uu/base32/Cargo.toml +++ b/src/uu/base32/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_base32" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "base32 ~ (uutils) decode/encode input (base32-encoding)" diff --git a/src/uu/base64/Cargo.toml b/src/uu/base64/Cargo.toml index 3bc04515280..ddd669b89b6 100644 --- a/src/uu/base64/Cargo.toml +++ b/src/uu/base64/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_base64" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "base64 ~ (uutils) decode/encode input (base64-encoding)" diff --git a/src/uu/basename/Cargo.toml b/src/uu/basename/Cargo.toml index 878a5d9e102..e74ad761635 100644 --- a/src/uu/basename/Cargo.toml +++ b/src/uu/basename/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_basename" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "basename ~ (uutils) display PATHNAME with leading directory components removed" diff --git a/src/uu/basenc/Cargo.toml b/src/uu/basenc/Cargo.toml index 92912e5dfbf..7f418844070 100644 --- a/src/uu/basenc/Cargo.toml +++ b/src/uu/basenc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_basenc" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "basenc ~ (uutils) decode/encode input" diff --git a/src/uu/cat/Cargo.toml b/src/uu/cat/Cargo.toml index 166e1823b58..33488cd072d 100644 --- a/src/uu/cat/Cargo.toml +++ b/src/uu/cat/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_cat" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "cat ~ (uutils) concatenate and display input" diff --git a/src/uu/chcon/Cargo.toml b/src/uu/chcon/Cargo.toml index c83c8abfc14..07bd73f4b4a 100644 --- a/src/uu/chcon/Cargo.toml +++ b/src/uu/chcon/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_chcon" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "chcon ~ (uutils) change file security context" diff --git a/src/uu/chgrp/Cargo.toml b/src/uu/chgrp/Cargo.toml index 7f43e846625..9591e13651a 100644 --- a/src/uu/chgrp/Cargo.toml +++ b/src/uu/chgrp/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_chgrp" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "chgrp ~ (uutils) change the group ownership of FILE" diff --git a/src/uu/chmod/Cargo.toml b/src/uu/chmod/Cargo.toml index 2a9c73d9db3..d10d0e08dac 100644 --- a/src/uu/chmod/Cargo.toml +++ b/src/uu/chmod/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_chmod" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "chmod ~ (uutils) change mode of FILE" diff --git a/src/uu/chown/Cargo.toml b/src/uu/chown/Cargo.toml index 82b32875d65..bbd75db0321 100644 --- a/src/uu/chown/Cargo.toml +++ b/src/uu/chown/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_chown" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "chown ~ (uutils) change the ownership of FILE" diff --git a/src/uu/chroot/Cargo.toml b/src/uu/chroot/Cargo.toml index 3c836add869..847b18ef5e2 100644 --- a/src/uu/chroot/Cargo.toml +++ b/src/uu/chroot/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_chroot" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "chroot ~ (uutils) run COMMAND under a new root directory" diff --git a/src/uu/cksum/Cargo.toml b/src/uu/cksum/Cargo.toml index 49a881f706f..e0d54edb9fc 100644 --- a/src/uu/cksum/Cargo.toml +++ b/src/uu/cksum/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_cksum" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "cksum ~ (uutils) display CRC and size of input" diff --git a/src/uu/comm/Cargo.toml b/src/uu/comm/Cargo.toml index e4a1dac82fa..07430f9d9f1 100644 --- a/src/uu/comm/Cargo.toml +++ b/src/uu/comm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_comm" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "comm ~ (uutils) compare sorted inputs" diff --git a/src/uu/cp/Cargo.toml b/src/uu/cp/Cargo.toml index 177525ff5f2..49ba75ca089 100644 --- a/src/uu/cp/Cargo.toml +++ b/src/uu/cp/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_cp" -version = "0.0.21" +version = "0.0.22" authors = [ "Jordy Dickinson ", "Joshua S. Miller ", diff --git a/src/uu/csplit/Cargo.toml b/src/uu/csplit/Cargo.toml index ba051ff8ab4..a30ebc4f67b 100644 --- a/src/uu/csplit/Cargo.toml +++ b/src/uu/csplit/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_csplit" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "csplit ~ (uutils) Output pieces of FILE separated by PATTERN(s) to files 'xx00', 'xx01', ..., and output byte counts of each piece to standard output" diff --git a/src/uu/cut/Cargo.toml b/src/uu/cut/Cargo.toml index a55ce7d5862..695777a72c1 100644 --- a/src/uu/cut/Cargo.toml +++ b/src/uu/cut/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_cut" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "cut ~ (uutils) display byte/field columns of input lines" diff --git a/src/uu/date/Cargo.toml b/src/uu/date/Cargo.toml index 7590b0b746c..cd860ce581b 100644 --- a/src/uu/date/Cargo.toml +++ b/src/uu/date/Cargo.toml @@ -1,7 +1,7 @@ # spell-checker:ignore datetime [package] name = "uu_date" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "date ~ (uutils) display or set the current time" diff --git a/src/uu/dd/Cargo.toml b/src/uu/dd/Cargo.toml index 482128e1d08..f56e46eaa21 100644 --- a/src/uu/dd/Cargo.toml +++ b/src/uu/dd/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_dd" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "dd ~ (uutils) copy and convert files" diff --git a/src/uu/df/Cargo.toml b/src/uu/df/Cargo.toml index b547a35d5ee..2aae7bd551f 100644 --- a/src/uu/df/Cargo.toml +++ b/src/uu/df/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_df" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "df ~ (uutils) display file system information" diff --git a/src/uu/dir/Cargo.toml b/src/uu/dir/Cargo.toml index 2a709a80465..0bca7bb9d80 100644 --- a/src/uu/dir/Cargo.toml +++ b/src/uu/dir/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_dir" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "shortcut to ls -C -b" diff --git a/src/uu/dircolors/Cargo.toml b/src/uu/dircolors/Cargo.toml index 9248f5ea12f..a4a101326a8 100644 --- a/src/uu/dircolors/Cargo.toml +++ b/src/uu/dircolors/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_dircolors" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "dircolors ~ (uutils) display commands to set LS_COLORS" diff --git a/src/uu/dirname/Cargo.toml b/src/uu/dirname/Cargo.toml index 6de444499db..67806986cc9 100644 --- a/src/uu/dirname/Cargo.toml +++ b/src/uu/dirname/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_dirname" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "dirname ~ (uutils) display parent directory of PATHNAME" diff --git a/src/uu/du/Cargo.toml b/src/uu/du/Cargo.toml index c87595db6ca..158fa97864f 100644 --- a/src/uu/du/Cargo.toml +++ b/src/uu/du/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_du" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "du ~ (uutils) display disk usage" diff --git a/src/uu/echo/Cargo.toml b/src/uu/echo/Cargo.toml index 5d27bae1b64..92d5d4924fc 100644 --- a/src/uu/echo/Cargo.toml +++ b/src/uu/echo/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_echo" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "echo ~ (uutils) display TEXT" diff --git a/src/uu/env/Cargo.toml b/src/uu/env/Cargo.toml index b2854edbf05..78b133733ab 100644 --- a/src/uu/env/Cargo.toml +++ b/src/uu/env/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_env" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "env ~ (uutils) set each NAME to VALUE in the environment and run COMMAND" diff --git a/src/uu/expand/Cargo.toml b/src/uu/expand/Cargo.toml index 58647d8e9d5..c329e61b6b2 100644 --- a/src/uu/expand/Cargo.toml +++ b/src/uu/expand/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_expand" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "expand ~ (uutils) convert input tabs to spaces" diff --git a/src/uu/expr/Cargo.toml b/src/uu/expr/Cargo.toml index ae31c66901a..3f0b5ca76ad 100644 --- a/src/uu/expr/Cargo.toml +++ b/src/uu/expr/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_expr" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "expr ~ (uutils) display the value of EXPRESSION" diff --git a/src/uu/factor/Cargo.toml b/src/uu/factor/Cargo.toml index 57b5e9d54ad..8ecee86c577 100644 --- a/src/uu/factor/Cargo.toml +++ b/src/uu/factor/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_factor" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "factor ~ (uutils) display the prime factors of each NUMBER" diff --git a/src/uu/false/Cargo.toml b/src/uu/false/Cargo.toml index 80f3553c0ef..3b1ca97f968 100644 --- a/src/uu/false/Cargo.toml +++ b/src/uu/false/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_false" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "false ~ (uutils) do nothing and fail" diff --git a/src/uu/fmt/Cargo.toml b/src/uu/fmt/Cargo.toml index 5b181937bc3..c49f52741b4 100644 --- a/src/uu/fmt/Cargo.toml +++ b/src/uu/fmt/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_fmt" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "fmt ~ (uutils) reformat each paragraph of input" diff --git a/src/uu/fold/Cargo.toml b/src/uu/fold/Cargo.toml index ff2f2f6d564..af36f0466ee 100644 --- a/src/uu/fold/Cargo.toml +++ b/src/uu/fold/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_fold" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "fold ~ (uutils) wrap each line of input" diff --git a/src/uu/groups/Cargo.toml b/src/uu/groups/Cargo.toml index d503d3e83f6..5f786acfd9f 100644 --- a/src/uu/groups/Cargo.toml +++ b/src/uu/groups/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_groups" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "groups ~ (uutils) display group memberships for USERNAME" diff --git a/src/uu/hashsum/Cargo.toml b/src/uu/hashsum/Cargo.toml index 1b075fb4871..23b60a7014a 100644 --- a/src/uu/hashsum/Cargo.toml +++ b/src/uu/hashsum/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_hashsum" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "hashsum ~ (uutils) display or check input digests" diff --git a/src/uu/head/Cargo.toml b/src/uu/head/Cargo.toml index bdbe45dc185..7c73f3941a7 100644 --- a/src/uu/head/Cargo.toml +++ b/src/uu/head/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_head" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "head ~ (uutils) display the first lines of input" diff --git a/src/uu/hostid/Cargo.toml b/src/uu/hostid/Cargo.toml index f34fee820a5..2e6dcc33952 100644 --- a/src/uu/hostid/Cargo.toml +++ b/src/uu/hostid/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_hostid" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "hostid ~ (uutils) display the numeric identifier of the current host" diff --git a/src/uu/hostname/Cargo.toml b/src/uu/hostname/Cargo.toml index 8e36e672a80..49cb14bf2b1 100644 --- a/src/uu/hostname/Cargo.toml +++ b/src/uu/hostname/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_hostname" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "hostname ~ (uutils) display or set the host name of the current host" diff --git a/src/uu/id/Cargo.toml b/src/uu/id/Cargo.toml index 77cda20be89..bf1eaf44ca5 100644 --- a/src/uu/id/Cargo.toml +++ b/src/uu/id/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_id" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "id ~ (uutils) display user and group information for USER" diff --git a/src/uu/install/Cargo.toml b/src/uu/install/Cargo.toml index e66cbeb72c7..b5353eace6a 100644 --- a/src/uu/install/Cargo.toml +++ b/src/uu/install/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_install" -version = "0.0.21" +version = "0.0.22" authors = ["Ben Eills ", "uutils developers"] license = "MIT" description = "install ~ (uutils) copy files from SOURCE to DESTINATION (with specified attributes)" diff --git a/src/uu/join/Cargo.toml b/src/uu/join/Cargo.toml index 971f8e4552b..c52f66de44e 100644 --- a/src/uu/join/Cargo.toml +++ b/src/uu/join/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_join" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "join ~ (uutils) merge lines from inputs with matching join fields" diff --git a/src/uu/kill/Cargo.toml b/src/uu/kill/Cargo.toml index a675f00eda3..a606c81e745 100644 --- a/src/uu/kill/Cargo.toml +++ b/src/uu/kill/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_kill" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "kill ~ (uutils) send a signal to a process" diff --git a/src/uu/link/Cargo.toml b/src/uu/link/Cargo.toml index 2406476cabc..c79ca604199 100644 --- a/src/uu/link/Cargo.toml +++ b/src/uu/link/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_link" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "link ~ (uutils) create a hard (file system) link to FILE" diff --git a/src/uu/ln/Cargo.toml b/src/uu/ln/Cargo.toml index 6488963cce3..e9f0e6eb749 100644 --- a/src/uu/ln/Cargo.toml +++ b/src/uu/ln/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_ln" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "ln ~ (uutils) create a (file system) link to TARGET" diff --git a/src/uu/logname/Cargo.toml b/src/uu/logname/Cargo.toml index 29116455b40..41cbcf73b03 100644 --- a/src/uu/logname/Cargo.toml +++ b/src/uu/logname/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_logname" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "logname ~ (uutils) display the login name of the current user" diff --git a/src/uu/ls/Cargo.toml b/src/uu/ls/Cargo.toml index 4394865caba..64d1cf1364d 100644 --- a/src/uu/ls/Cargo.toml +++ b/src/uu/ls/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_ls" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "ls ~ (uutils) display directory contents" diff --git a/src/uu/mkdir/Cargo.toml b/src/uu/mkdir/Cargo.toml index c1a6ce2a863..c0d60dc9d50 100644 --- a/src/uu/mkdir/Cargo.toml +++ b/src/uu/mkdir/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_mkdir" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "mkdir ~ (uutils) create DIRECTORY" diff --git a/src/uu/mkfifo/Cargo.toml b/src/uu/mkfifo/Cargo.toml index 0c28f52de79..285fc2b3429 100644 --- a/src/uu/mkfifo/Cargo.toml +++ b/src/uu/mkfifo/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_mkfifo" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "mkfifo ~ (uutils) create FIFOs (named pipes)" diff --git a/src/uu/mknod/Cargo.toml b/src/uu/mknod/Cargo.toml index a230bec74ce..2e660171473 100644 --- a/src/uu/mknod/Cargo.toml +++ b/src/uu/mknod/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_mknod" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "mknod ~ (uutils) create special file NAME of TYPE" diff --git a/src/uu/mktemp/Cargo.toml b/src/uu/mktemp/Cargo.toml index 5dbc8492a0b..8d8a885b158 100644 --- a/src/uu/mktemp/Cargo.toml +++ b/src/uu/mktemp/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_mktemp" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "mktemp ~ (uutils) create and display a temporary file or directory from TEMPLATE" diff --git a/src/uu/more/Cargo.toml b/src/uu/more/Cargo.toml index bc0f2521794..5614a0495a3 100644 --- a/src/uu/more/Cargo.toml +++ b/src/uu/more/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_more" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "more ~ (uutils) input perusal filter" diff --git a/src/uu/mv/Cargo.toml b/src/uu/mv/Cargo.toml index b7fcf2d109b..0be31ad7254 100644 --- a/src/uu/mv/Cargo.toml +++ b/src/uu/mv/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_mv" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "mv ~ (uutils) move (rename) SOURCE to DESTINATION" diff --git a/src/uu/nice/Cargo.toml b/src/uu/nice/Cargo.toml index 65e1520d1b3..465b7b76595 100644 --- a/src/uu/nice/Cargo.toml +++ b/src/uu/nice/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_nice" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "nice ~ (uutils) run PROGRAM with modified scheduling priority" diff --git a/src/uu/nl/Cargo.toml b/src/uu/nl/Cargo.toml index db2bfacbd70..003e1791385 100644 --- a/src/uu/nl/Cargo.toml +++ b/src/uu/nl/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_nl" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "nl ~ (uutils) display input with added line numbers" diff --git a/src/uu/nohup/Cargo.toml b/src/uu/nohup/Cargo.toml index 9fbf125bf5b..41c98982af0 100644 --- a/src/uu/nohup/Cargo.toml +++ b/src/uu/nohup/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_nohup" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "nohup ~ (uutils) run COMMAND, ignoring hangup signals" diff --git a/src/uu/nproc/Cargo.toml b/src/uu/nproc/Cargo.toml index b6052de2ed3..0acc0e95113 100644 --- a/src/uu/nproc/Cargo.toml +++ b/src/uu/nproc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_nproc" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "nproc ~ (uutils) display the number of processing units available" diff --git a/src/uu/numfmt/Cargo.toml b/src/uu/numfmt/Cargo.toml index 8fbf886a241..cf2f0864606 100644 --- a/src/uu/numfmt/Cargo.toml +++ b/src/uu/numfmt/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_numfmt" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "numfmt ~ (uutils) reformat NUMBER" diff --git a/src/uu/od/Cargo.toml b/src/uu/od/Cargo.toml index d369456f32a..b7799e05200 100644 --- a/src/uu/od/Cargo.toml +++ b/src/uu/od/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_od" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "od ~ (uutils) display formatted representation of input" diff --git a/src/uu/paste/Cargo.toml b/src/uu/paste/Cargo.toml index d93d4cbb6b9..dd030478c09 100644 --- a/src/uu/paste/Cargo.toml +++ b/src/uu/paste/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_paste" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "paste ~ (uutils) merge lines from inputs" diff --git a/src/uu/pathchk/Cargo.toml b/src/uu/pathchk/Cargo.toml index f62c027c4fd..708ae2afbaf 100644 --- a/src/uu/pathchk/Cargo.toml +++ b/src/uu/pathchk/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_pathchk" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "pathchk ~ (uutils) diagnose invalid or non-portable PATHNAME" diff --git a/src/uu/pinky/Cargo.toml b/src/uu/pinky/Cargo.toml index f6d291e26e9..c4739e7fcb6 100644 --- a/src/uu/pinky/Cargo.toml +++ b/src/uu/pinky/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_pinky" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "pinky ~ (uutils) display user information" diff --git a/src/uu/pr/Cargo.toml b/src/uu/pr/Cargo.toml index 4453d1cbaa6..9a12e10f5bf 100644 --- a/src/uu/pr/Cargo.toml +++ b/src/uu/pr/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_pr" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "pr ~ (uutils) convert text files for printing" diff --git a/src/uu/printenv/Cargo.toml b/src/uu/printenv/Cargo.toml index 72965811011..64435127f6e 100644 --- a/src/uu/printenv/Cargo.toml +++ b/src/uu/printenv/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_printenv" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "printenv ~ (uutils) display value of environment VAR" diff --git a/src/uu/printf/Cargo.toml b/src/uu/printf/Cargo.toml index d55d3732a3a..a4fd773da28 100644 --- a/src/uu/printf/Cargo.toml +++ b/src/uu/printf/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_printf" -version = "0.0.21" +version = "0.0.22" authors = ["Nathan Ross", "uutils developers"] license = "MIT" description = "printf ~ (uutils) FORMAT and display ARGUMENTS" diff --git a/src/uu/ptx/Cargo.toml b/src/uu/ptx/Cargo.toml index 96d635caff6..ea241600bce 100644 --- a/src/uu/ptx/Cargo.toml +++ b/src/uu/ptx/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_ptx" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "ptx ~ (uutils) display a permuted index of input" diff --git a/src/uu/pwd/Cargo.toml b/src/uu/pwd/Cargo.toml index 1567a935f9a..5f15245b5d4 100644 --- a/src/uu/pwd/Cargo.toml +++ b/src/uu/pwd/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_pwd" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "pwd ~ (uutils) display current working directory" diff --git a/src/uu/readlink/Cargo.toml b/src/uu/readlink/Cargo.toml index 309d4079429..a696abdfaa2 100644 --- a/src/uu/readlink/Cargo.toml +++ b/src/uu/readlink/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_readlink" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "readlink ~ (uutils) display resolved path of PATHNAME" diff --git a/src/uu/realpath/Cargo.toml b/src/uu/realpath/Cargo.toml index e458bf89d13..6d207275322 100644 --- a/src/uu/realpath/Cargo.toml +++ b/src/uu/realpath/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_realpath" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "realpath ~ (uutils) display resolved absolute path of PATHNAME" diff --git a/src/uu/rm/Cargo.toml b/src/uu/rm/Cargo.toml index 87fccbfa875..a1b3999d4d4 100644 --- a/src/uu/rm/Cargo.toml +++ b/src/uu/rm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_rm" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "rm ~ (uutils) remove PATHNAME" diff --git a/src/uu/rmdir/Cargo.toml b/src/uu/rmdir/Cargo.toml index 41d2f7432d1..8288309f258 100644 --- a/src/uu/rmdir/Cargo.toml +++ b/src/uu/rmdir/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_rmdir" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "rmdir ~ (uutils) remove empty DIRECTORY" diff --git a/src/uu/runcon/Cargo.toml b/src/uu/runcon/Cargo.toml index ac4ec54d5f0..04d3aae7139 100644 --- a/src/uu/runcon/Cargo.toml +++ b/src/uu/runcon/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_runcon" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "runcon ~ (uutils) run command with specified security context" diff --git a/src/uu/seq/Cargo.toml b/src/uu/seq/Cargo.toml index f25d995a2d6..b8a5c084cde 100644 --- a/src/uu/seq/Cargo.toml +++ b/src/uu/seq/Cargo.toml @@ -1,7 +1,7 @@ # spell-checker:ignore bigdecimal [package] name = "uu_seq" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "seq ~ (uutils) display a sequence of numbers" diff --git a/src/uu/shred/Cargo.toml b/src/uu/shred/Cargo.toml index 70045eaf07f..0e7ad2ad317 100644 --- a/src/uu/shred/Cargo.toml +++ b/src/uu/shred/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_shred" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "shred ~ (uutils) hide former FILE contents with repeated overwrites" diff --git a/src/uu/shuf/Cargo.toml b/src/uu/shuf/Cargo.toml index 2f0466b86fa..6a86c6d9858 100644 --- a/src/uu/shuf/Cargo.toml +++ b/src/uu/shuf/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_shuf" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "shuf ~ (uutils) display random permutations of input lines" diff --git a/src/uu/sleep/Cargo.toml b/src/uu/sleep/Cargo.toml index 454f2297ed5..6ca686c78ce 100644 --- a/src/uu/sleep/Cargo.toml +++ b/src/uu/sleep/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_sleep" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "sleep ~ (uutils) pause for DURATION" diff --git a/src/uu/sort/Cargo.toml b/src/uu/sort/Cargo.toml index 981e7164144..0cbafc5cfac 100644 --- a/src/uu/sort/Cargo.toml +++ b/src/uu/sort/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_sort" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "sort ~ (uutils) sort input lines" diff --git a/src/uu/split/Cargo.toml b/src/uu/split/Cargo.toml index fb53914e291..c5a34847324 100644 --- a/src/uu/split/Cargo.toml +++ b/src/uu/split/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_split" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "split ~ (uutils) split input into output files" diff --git a/src/uu/stat/Cargo.toml b/src/uu/stat/Cargo.toml index f258aca1135..2974562f352 100644 --- a/src/uu/stat/Cargo.toml +++ b/src/uu/stat/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_stat" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "stat ~ (uutils) display FILE status" diff --git a/src/uu/stdbuf/Cargo.toml b/src/uu/stdbuf/Cargo.toml index f6101490715..8168733affe 100644 --- a/src/uu/stdbuf/Cargo.toml +++ b/src/uu/stdbuf/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_stdbuf" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "stdbuf ~ (uutils) run COMMAND with modified standard stream buffering" @@ -20,7 +20,7 @@ tempfile = { workspace = true } uucore = { workspace = true } [build-dependencies] -libstdbuf = { version = "0.0.21", package = "uu_stdbuf_libstdbuf", path = "src/libstdbuf" } +libstdbuf = { version = "0.0.22", package = "uu_stdbuf_libstdbuf", path = "src/libstdbuf" } [[bin]] name = "stdbuf" diff --git a/src/uu/stdbuf/src/libstdbuf/Cargo.toml b/src/uu/stdbuf/src/libstdbuf/Cargo.toml index 8061c61a642..5fdfa27e035 100644 --- a/src/uu/stdbuf/src/libstdbuf/Cargo.toml +++ b/src/uu/stdbuf/src/libstdbuf/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_stdbuf_libstdbuf" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "stdbuf/libstdbuf ~ (uutils); dynamic library required for stdbuf" diff --git a/src/uu/stty/Cargo.toml b/src/uu/stty/Cargo.toml index eb86ae68102..965de5cd885 100644 --- a/src/uu/stty/Cargo.toml +++ b/src/uu/stty/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_stty" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "stty ~ (uutils) print or change terminal characteristics" diff --git a/src/uu/sum/Cargo.toml b/src/uu/sum/Cargo.toml index aa722c69433..58fa7a08d4c 100644 --- a/src/uu/sum/Cargo.toml +++ b/src/uu/sum/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_sum" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "sum ~ (uutils) display checksum and block counts for input" diff --git a/src/uu/sync/Cargo.toml b/src/uu/sync/Cargo.toml index b74e076e509..2367561f873 100644 --- a/src/uu/sync/Cargo.toml +++ b/src/uu/sync/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_sync" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "sync ~ (uutils) synchronize cache writes to storage" diff --git a/src/uu/tac/Cargo.toml b/src/uu/tac/Cargo.toml index 385ef37c89b..1716a6d0363 100644 --- a/src/uu/tac/Cargo.toml +++ b/src/uu/tac/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "uu_tac" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "tac ~ (uutils) concatenate and display input lines in reverse order" diff --git a/src/uu/tail/Cargo.toml b/src/uu/tail/Cargo.toml index 5ff532e60fc..66775c8d9d8 100644 --- a/src/uu/tail/Cargo.toml +++ b/src/uu/tail/Cargo.toml @@ -1,7 +1,7 @@ # spell-checker:ignore (libs) kqueue fundu [package] name = "uu_tail" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "tail ~ (uutils) display the last lines of input" diff --git a/src/uu/tee/Cargo.toml b/src/uu/tee/Cargo.toml index 597299fc7d4..787c0cb326f 100644 --- a/src/uu/tee/Cargo.toml +++ b/src/uu/tee/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_tee" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "tee ~ (uutils) display input and copy to FILE" diff --git a/src/uu/test/Cargo.toml b/src/uu/test/Cargo.toml index fc70677a6c2..696fa366214 100644 --- a/src/uu/test/Cargo.toml +++ b/src/uu/test/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_test" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "test ~ (uutils) evaluate comparison and file type expressions" diff --git a/src/uu/timeout/Cargo.toml b/src/uu/timeout/Cargo.toml index 949ac9da5f7..b6cb700a47f 100644 --- a/src/uu/timeout/Cargo.toml +++ b/src/uu/timeout/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_timeout" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "timeout ~ (uutils) run COMMAND with a DURATION time limit" diff --git a/src/uu/touch/Cargo.toml b/src/uu/touch/Cargo.toml index 54682e1ddf4..b44e8cf69ff 100644 --- a/src/uu/touch/Cargo.toml +++ b/src/uu/touch/Cargo.toml @@ -1,7 +1,7 @@ # spell-checker:ignore datetime [package] name = "uu_touch" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "touch ~ (uutils) change FILE timestamps" diff --git a/src/uu/tr/Cargo.toml b/src/uu/tr/Cargo.toml index 065fdab5274..c7e7bd60644 100644 --- a/src/uu/tr/Cargo.toml +++ b/src/uu/tr/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_tr" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "tr ~ (uutils) translate characters within input and display" diff --git a/src/uu/true/Cargo.toml b/src/uu/true/Cargo.toml index 432dfa54e91..ec6c5d2f909 100644 --- a/src/uu/true/Cargo.toml +++ b/src/uu/true/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_true" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "true ~ (uutils) do nothing and succeed" diff --git a/src/uu/truncate/Cargo.toml b/src/uu/truncate/Cargo.toml index fbabe1fcd4f..a9cc179a67f 100644 --- a/src/uu/truncate/Cargo.toml +++ b/src/uu/truncate/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_truncate" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "truncate ~ (uutils) truncate (or extend) FILE to SIZE" diff --git a/src/uu/tsort/Cargo.toml b/src/uu/tsort/Cargo.toml index 2f276f98eea..f72aeeda083 100644 --- a/src/uu/tsort/Cargo.toml +++ b/src/uu/tsort/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_tsort" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "tsort ~ (uutils) topologically sort input (partially ordered) pairs" diff --git a/src/uu/tty/Cargo.toml b/src/uu/tty/Cargo.toml index 917d04c106a..66d4b42c795 100644 --- a/src/uu/tty/Cargo.toml +++ b/src/uu/tty/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_tty" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "tty ~ (uutils) display the name of the terminal connected to standard input" diff --git a/src/uu/uname/Cargo.toml b/src/uu/uname/Cargo.toml index ccb3651ec63..10363b63cb0 100644 --- a/src/uu/uname/Cargo.toml +++ b/src/uu/uname/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_uname" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "uname ~ (uutils) display system information" diff --git a/src/uu/unexpand/Cargo.toml b/src/uu/unexpand/Cargo.toml index 6a12d48ab6b..bab5b4e915b 100644 --- a/src/uu/unexpand/Cargo.toml +++ b/src/uu/unexpand/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_unexpand" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "unexpand ~ (uutils) convert input spaces to tabs" diff --git a/src/uu/uniq/Cargo.toml b/src/uu/uniq/Cargo.toml index 0afcffc118b..2fb0552ce7b 100644 --- a/src/uu/uniq/Cargo.toml +++ b/src/uu/uniq/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_uniq" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "uniq ~ (uutils) filter identical adjacent lines from input" diff --git a/src/uu/unlink/Cargo.toml b/src/uu/unlink/Cargo.toml index ce86b81d8d1..aae56bf0bff 100644 --- a/src/uu/unlink/Cargo.toml +++ b/src/uu/unlink/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_unlink" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "unlink ~ (uutils) remove a (file system) link to FILE" diff --git a/src/uu/uptime/Cargo.toml b/src/uu/uptime/Cargo.toml index 7bef7cc051d..f5fa083f9f5 100644 --- a/src/uu/uptime/Cargo.toml +++ b/src/uu/uptime/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_uptime" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "uptime ~ (uutils) display dynamic system information" diff --git a/src/uu/users/Cargo.toml b/src/uu/users/Cargo.toml index 4701c694f46..d894ff4ba66 100644 --- a/src/uu/users/Cargo.toml +++ b/src/uu/users/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_users" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "users ~ (uutils) display names of currently logged-in users" diff --git a/src/uu/vdir/Cargo.toml b/src/uu/vdir/Cargo.toml index 99bcf383b98..b63f8c9eafc 100644 --- a/src/uu/vdir/Cargo.toml +++ b/src/uu/vdir/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_vdir" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "shortcut to ls -l -b" diff --git a/src/uu/wc/Cargo.toml b/src/uu/wc/Cargo.toml index 5712fbd8339..6a233c1ade3 100644 --- a/src/uu/wc/Cargo.toml +++ b/src/uu/wc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_wc" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "wc ~ (uutils) display newline, word, and byte counts for input" diff --git a/src/uu/who/Cargo.toml b/src/uu/who/Cargo.toml index b8b90b2ef31..fdfc0589757 100644 --- a/src/uu/who/Cargo.toml +++ b/src/uu/who/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_who" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "who ~ (uutils) display information about currently logged-in users" diff --git a/src/uu/whoami/Cargo.toml b/src/uu/whoami/Cargo.toml index 331755a9651..59d011209fd 100644 --- a/src/uu/whoami/Cargo.toml +++ b/src/uu/whoami/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_whoami" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "whoami ~ (uutils) display user name of current effective user ID" diff --git a/src/uu/yes/Cargo.toml b/src/uu/yes/Cargo.toml index eff09155119..c36c2b1897d 100644 --- a/src/uu/yes/Cargo.toml +++ b/src/uu/yes/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_yes" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "yes ~ (uutils) repeatedly display a line with STRING (or 'y')" diff --git a/src/uucore/Cargo.toml b/src/uucore/Cargo.toml index 5e555da4da4..8ce252dc561 100644 --- a/src/uucore/Cargo.toml +++ b/src/uucore/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "uucore" -version = "0.0.21" +version = "0.0.22" authors = ["uutils developers"] license = "MIT" description = "uutils ~ 'core' uutils code library (cross-platform)" diff --git a/src/uucore_procs/Cargo.toml b/src/uucore_procs/Cargo.toml index 1ad4023f9b1..55d88096135 100644 --- a/src/uucore_procs/Cargo.toml +++ b/src/uucore_procs/Cargo.toml @@ -1,7 +1,7 @@ # spell-checker:ignore uuhelp [package] name = "uucore_procs" -version = "0.0.21" +version = "0.0.22" authors = ["Roy Ivy III "] license = "MIT" description = "uutils ~ 'uucore' proc-macros" @@ -19,4 +19,4 @@ proc-macro = true [dependencies] proc-macro2 = "1.0" quote = "1.0" -uuhelp_parser = { path = "../uuhelp_parser", version = "0.0.21" } +uuhelp_parser = { path = "../uuhelp_parser", version = "0.0.22" } diff --git a/src/uuhelp_parser/Cargo.toml b/src/uuhelp_parser/Cargo.toml index fdff271b053..007f1ebb2c2 100644 --- a/src/uuhelp_parser/Cargo.toml +++ b/src/uuhelp_parser/Cargo.toml @@ -1,7 +1,7 @@ # spell-checker:ignore uuhelp [package] name = "uuhelp_parser" -version = "0.0.21" +version = "0.0.22" edition = "2021" license = "MIT" description = "A collection of functions to parse the markdown code of help files" diff --git a/util/update-version.sh b/util/update-version.sh index 6738f19b66d..59bdb4d751a 100755 --- a/util/update-version.sh +++ b/util/update-version.sh @@ -14,8 +14,8 @@ # 7) Run util/publish.sh --do-it # 8) In some cases, you might have to fix dependencies and run import -FROM="0.0.20" -TO="0.0.21" +FROM="0.0.21" +TO="0.0.22" PROGS=$(ls -1d src/uu/*/Cargo.toml src/uu/stdbuf/src/libstdbuf/Cargo.toml src/uucore/Cargo.toml Cargo.toml) From fcd0817b3b4a2fd6e831c23aef400c7c018f84a3 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sun, 15 Oct 2023 14:24:05 +0200 Subject: [PATCH 208/208] update of the release doc --- util/update-version.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/util/update-version.sh b/util/update-version.sh index 6738f19b66d..86e7f329ba3 100755 --- a/util/update-version.sh +++ b/util/update-version.sh @@ -9,10 +9,13 @@ # 2) run it: sh util/update-version.sh # 3) Do a spot check with "git diff" # 4) cargo test --release --features unix -# 5) git commit -m "New release" +# 5) git commit -m "New release" (make sure it includes Cargo.lock) # 6) Run util/publish.sh in dry mode (it will fail as packages needs more recent version of uucore) # 7) Run util/publish.sh --do-it # 8) In some cases, you might have to fix dependencies and run import +# 9) Tag the release - "git tag 0.0.X && git push --tags" +# 10) Create the release on github https://github.com/uutils/coreutils/releases/new +# 11) Make sure we have good release notes FROM="0.0.20" TO="0.0.21"