152 Commits

Author SHA1 Message Date
Sylvestre Ledru b94b5f0122 Merge pull request #524 from tavianator/cmp-signs
find: Allow comparable values with signs
2025-04-06 18:46:33 +02:00
Sylvestre Ledru 4f8fbf55a1 Merge pull request #528 from sylvestre/ver
prepare version 0.8.0
2025-04-06 18:41:08 +02:00
Sylvestre Ledru 3dbdbe8442 prepare version 0.8.0 2025-04-06 18:35:31 +02:00
Sylvestre Ledru 949bcb83c8 Merge pull request #526 from cakebaker/find_fix_typo_in_test
find: fix typo in test (slashs -> slashes)
2025-04-05 10:24:14 +02:00
Daniel Hofstetter 61b396ac31 find: fix typo in test (slashs -> slashes) 2025-04-05 10:14:03 +02:00
Daniel Hofstetter 6c99dc175f Merge pull request #497 from uutils/dependabot/cargo/chrono-0.4.40
build(deps): bump chrono from 0.4.38 to 0.4.40
2025-04-05 10:07:02 +02:00
dependabot[bot] 778abdb6a9 build(deps): bump chrono from 0.4.38 to 0.4.40
Bumps [chrono](https://github.com/chronotope/chrono) from 0.4.38 to 0.4.40.
- [Release notes](https://github.com/chronotope/chrono/releases)
- [Changelog](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md)
- [Commits](https://github.com/chronotope/chrono/compare/v0.4.38...v0.4.40)

---
updated-dependencies:
- dependency-name: chrono
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-05 09:57:27 +02:00
Tavian Barnes a61993b705 find: Allow comparable values with signs
find -uid -+10 means "UID less than +10".
2025-04-05 09:57:10 +02:00
Daniel Hofstetter bd159d7627 Merge pull request #525 from sylvestre/cov
ci: adjust to code cov v5
2025-04-05 09:49:52 +02:00
Sylvestre Ledru ad61338bb2 ci: use github for gnulib 2025-04-04 23:04:33 +02:00
Sylvestre Ledru 4a1c683834 ci: remove trailing spaces 2025-04-04 23:00:59 +02:00
Sylvestre Ledru 2ec695381e ci: adjust to code cov v5 2025-04-04 22:27:47 +02:00
Sylvestre Ledru a04b499e15 Merge pull request #521 from tavianator/user-group-ids
find: User/group ID fixes
2025-04-04 14:01:21 +02:00
Tavian Barnes de42f9bd83 find: Implement -uid [-+]N, -gid [-+]N
GNU find's -uid/-gid matchers take a comparable value.
2025-04-04 09:51:19 +02:00
Tavian Barnes 6d264dbbcf find: Allow -user UID / -group GID
POSIX says

> -user uname
>   The primary shall evaluate as true if the file belongs to the user
>   uname. If uname is a decimal integer and the getpwnam() (or
>   equivalent) function does not return a valid user name, uname shall
>   be interpreted as a user ID.

and similarly for -group.

Link: https://pubs.opengroup.org/onlinepubs/9799919799/utilities/find.html
2025-04-04 09:51:19 +02:00
Sylvestre Ledru 81c39aad7d Merge pull request #522 from cakebaker/clippy_fix_warnings_from_rust_1_86
clippy: fix warnings from Rust 1.86
2025-04-04 09:50:42 +02:00
Daniel Hofstetter 5cb273eecb clippy: fix error from manual_div_ceil lint 2025-04-04 09:21:12 +02:00
Daniel Hofstetter 55021afb69 clippy: fix errors from double_ended_iterator_last
lint
2025-04-04 09:21:12 +02:00
Sylvestre Ledru 013be1b093 Merge pull request #520 from tavianator/exec-multi-deterministic
tests: Make -exec {} + tests deterministic
2025-04-04 09:20:25 +02:00
Tavian Barnes 0429507f15 tests: Make -exec {} + tests deterministic
These tests failed on my system because directory entries were returned
in a different order, causing an output mismatch.  Add --sort to the
testing_commandline to make the output deterministic.
2025-04-02 22:11:27 -04:00
milkory 5235d78291 find: Implement exec[dir] + (#515)
* add argmax dependency

* implement finished and finished_dir callback

* implement multi exec matcher

* fix finished_dir not working correctly

* add more test for multi exec matcher

* fix execdir multi in current directory not working

* add more test for parsing multi exec

* set exit code when exec + failed

* fix clippy warnings

* prevent multi exec panics on long paths

---------

Co-authored-by: hanbings <hanbings@hanbings.io>
2025-04-02 21:04:48 +08:00
cryptodarth 1a40442f20 Merge pull request #509 from Crypto-Darth/main
Implement : -files0-from (#378)
2025-04-02 20:47:51 +08:00
Daniel Hofstetter cb83b6db3a Merge pull request #519 from uutils/dependabot/cargo/clap-4.5.35
build(deps): bump clap from 4.5.34 to 4.5.35
2025-04-02 07:01:01 +02:00
dependabot[bot] e92ebc9288 build(deps): bump clap from 4.5.34 to 4.5.35
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.34 to 4.5.35.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.34...clap_complete-v4.5.35)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-02 03:05:31 +00:00
Daniel Hofstetter d6dc18973d Merge pull request #517 from hanbings/clippy
clippy: run cargo clippy and cargo fmt.
2025-03-29 13:55:57 +01:00
hanbings 9fdfb1ee08 Run cargo clippy and cargo fmt. 2025-03-29 15:58:41 +08:00
hanbings 830298ed22 Merge pull request #510 from uutils/sylvestre-patch-1
clippy: add the same rules as coreutils
2025-03-29 15:51:24 +08:00
hanbings 8784923b54 Merge pull request #516 from tavianator/set-exit-code
find: Use matcher_io.set_exit_code()
2025-03-29 15:29:25 +08:00
hanbings 89f98f1aff Merge pull request #514 from cakebaker/bump_uucore
Bump `uucore` from `0.0.29` to `0.0.30`
2025-03-29 15:05:45 +08:00
Tavian Barnes f911610d93 find: Use matcher_io.set_exit_code()
The uucore function has the downside of leaking the failure state
between unrelated tests, because the exit code is global.  PR #436
introduced a method on MatcherIO which solves the problem, but future
PRs added new uses of uucore::error::set_exit_code().
2025-03-28 12:56:30 -04:00
Daniel Hofstetter 22c5f7dcc3 Bump uucore from 0.0.29 to 0.0.30 2025-03-27 10:01:46 +01:00
Daniel Hofstetter f3a70e9eb4 Merge pull request #513 from uutils/dependabot/cargo/clap-4.5.34
build(deps): bump clap from 4.5.32 to 4.5.34
2025-03-27 07:11:18 +01:00
dependabot[bot] 55298be4ad build(deps): bump clap from 4.5.32 to 4.5.34
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.32 to 4.5.34.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.32...clap_complete-v4.5.34)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-27 03:08:45 +00:00
hanbings 764be30111 Merge pull request #512 from Every2/main
Fix incorrect triple slashes behavior
2025-03-26 23:11:13 +08:00
Every2 6d54482ed4 add one more test to check if case 2025-03-26 04:08:58 -03:00
Every2 49975f5c62 fix windows not running 2025-03-26 03:43:35 -03:00
Every2 dacc473c85 fix incorrect matchs in -name and fix incorrect slashes test 2025-03-26 03:35:38 -03:00
hanbings 5bad2e0afe Merge branch 'main' into sylvestre-patch-1 2025-03-25 10:58:13 -07:00
hanbings 28b1c88d9c Merge pull request #508 from Every2/main
-name / should match ///
2025-03-25 10:56:48 -07:00
Every2 c7b89d5341 fix typo 2025-03-24 18:20:50 -03:00
Every2 b4c5c4946b add find cmd tests 2025-03-24 18:18:33 -03:00
Every2 16bb731937 add test for triple slash case 2025-03-23 14:52:38 +01:00
Every2 6d366cbbda fix -name not returning the same character 2025-03-23 14:52:38 +01:00
Sylvestre Ledru 280381bb28 disable clippy::warn for now 2025-03-23 14:40:56 +01:00
Sylvestre Ledru 1809a3edca fix many clippy warnings
Done with
cargo clippy --fix
2025-03-23 14:38:11 +01:00
Sylvestre Ledru 0a54e7c6d9 clippy: add the same rules as coreutils 2025-03-23 14:35:35 +01:00
Daniel Hofstetter 5d264c9c7b Merge pull request #507 from uutils/dependabot/cargo/tempfile-3.19.1
build(deps): bump tempfile from 3.19.0 to 3.19.1
2025-03-20 07:06:01 +01:00
dependabot[bot] d8355d48a3 build(deps): bump tempfile from 3.19.0 to 3.19.1
Bumps [tempfile](https://github.com/Stebalien/tempfile) from 3.19.0 to 3.19.1.
- [Changelog](https://github.com/Stebalien/tempfile/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Stebalien/tempfile/compare/v3.19.0...v3.19.1)

---
updated-dependencies:
- dependency-name: tempfile
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-20 03:42:20 +00:00
Sylvestre Ledru a7b596daef Merge pull request #506 from cakebaker/ci_update_dist
ci: update `dist` from `0.12` to `0.28`
2025-03-14 10:21:51 +01:00
Daniel Hofstetter 22a58468b8 ci: update dist from 0.12 to 0.28 2025-03-14 10:05:01 +01:00
Daniel Hofstetter 0f9ca9eace Merge pull request #505 from uutils/dependabot/cargo/tempfile-3.19.0
build(deps): bump tempfile from 3.18.0 to 3.19.0
2025-03-14 07:20:48 +01:00
dependabot[bot] a90f505510 build(deps): bump tempfile from 3.18.0 to 3.19.0
Bumps [tempfile](https://github.com/Stebalien/tempfile) from 3.18.0 to 3.19.0.
- [Changelog](https://github.com/Stebalien/tempfile/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Stebalien/tempfile/compare/v3.18.0...v3.19.0)

---
updated-dependencies:
- dependency-name: tempfile
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-14 03:05:16 +00:00
Daniel Hofstetter e32e754102 Merge pull request #502 from uutils/dependabot/cargo/once_cell-1.21.0
build(deps): bump once_cell from 1.20.3 to 1.21.0
2025-03-11 09:40:13 +01:00
dependabot[bot] 83dc5fe7de build(deps): bump once_cell from 1.20.3 to 1.21.0
Bumps [once_cell](https://github.com/matklad/once_cell) from 1.20.3 to 1.21.0.
- [Changelog](https://github.com/matklad/once_cell/blob/master/CHANGELOG.md)
- [Commits](https://github.com/matklad/once_cell/compare/v1.20.3...v1.21.0)

---
updated-dependencies:
- dependency-name: once_cell
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-11 08:29:42 +00:00
Sylvestre Ledru cb3f3cfea4 Merge pull request #503 from cakebaker/remove_unused_once_cell
Remove unused `once_cell` dependency
2025-03-11 09:28:39 +01:00
Daniel Hofstetter 1f9d90eb4f Remove unused once_cell dependency 2025-03-11 09:11:34 +01:00
Daniel Hofstetter 98ce63a65e Merge pull request #501 from uutils/dependabot/cargo/clap-4.5.32
build(deps): bump clap from 4.5.31 to 4.5.32
2025-03-11 07:16:03 +01:00
dependabot[bot] a547d24f46 build(deps): bump clap from 4.5.31 to 4.5.32
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.31 to 4.5.32.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.5.31...clap_complete-v4.5.32)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-11 03:40:49 +00:00
Daniel Hofstetter b263b20801 Merge pull request #500 from uutils/dependabot/cargo/tempfile-3.18.0
build(deps): bump tempfile from 3.17.1 to 3.18.0
2025-03-07 07:05:56 +01:00
dependabot[bot] 5ae9315c1b build(deps): bump tempfile from 3.17.1 to 3.18.0
Bumps [tempfile](https://github.com/Stebalien/tempfile) from 3.17.1 to 3.18.0.
- [Changelog](https://github.com/Stebalien/tempfile/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Stebalien/tempfile/compare/v3.17.1...v3.18.0)

---
updated-dependencies:
- dependency-name: tempfile
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-07 03:51:50 +00:00
hanbings 87459296e6 Merge pull request #499 from cakebaker/bump_dependencies_to_remove_older_windows_sys
Bump dependencies to remove two `windows-sys` versions
2025-03-02 16:55:08 +08:00
Daniel Hofstetter e9374afc99 Bump deps to remove two windows-sys versions
anstream from 0.6.12 to 0.6.18
anstyle-query from 1.0.0 to 1.1.2
anstyle-wincon from 3.0.1 to 3.0.7
parking_lot_core from 0.9.9 to 0.9.10
winapi-util from 0.1.8 to 0.1.9
2025-03-01 15:52:30 +01:00
hanbings 1ea16258c3 Merge pull request #498 from cakebaker/find_fix_unused_import_on_windows
find: fix "unused import" warning on Windows
2025-03-01 00:12:14 +08:00
Daniel Hofstetter 9226ff10b6 find: fix "unused import" warning on Windows 2025-02-28 16:52:46 +01:00
Daniel Hofstetter 8c93719bfc Merge pull request #495 from uutils/dependabot/cargo/tempfile-3.17.1
build(deps): bump tempfile from 3.13.0 to 3.17.1
2025-02-28 16:51:03 +01:00
Daniel Hofstetter 629f5b793d Merge pull request #485 from uutils/dependabot/cargo/predicates-3.1.3
build(deps): bump predicates from 3.1.2 to 3.1.3
2025-02-28 16:50:14 +01:00
Daniel Hofstetter d7ce651130 Merge pull request #491 from uutils/dependabot/cargo/once_cell-1.20.3
build(deps): bump once_cell from 1.20.2 to 1.20.3
2025-02-28 16:48:51 +01:00
Daniel Hofstetter 7be665cbbe Merge pull request #496 from uutils/dependabot/cargo/clap-4.5.31
build(deps): bump clap from 4.5.20 to 4.5.31
2025-02-28 16:43:05 +01:00
dependabot[bot] 5319654dfb build(deps): bump clap from 4.5.20 to 4.5.31
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.20 to 4.5.31.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.20...v4.5.31)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-25 03:03:17 +00:00
dependabot[bot] 145b96871a build(deps): bump tempfile from 3.13.0 to 3.17.1
Bumps [tempfile](https://github.com/Stebalien/tempfile) from 3.13.0 to 3.17.1.
- [Changelog](https://github.com/Stebalien/tempfile/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Stebalien/tempfile/compare/v3.13.0...v3.17.1)

---
updated-dependencies:
- dependency-name: tempfile
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-18 03:38:09 +00:00
dependabot[bot] fb4108192c build(deps): bump once_cell from 1.20.2 to 1.20.3
Bumps [once_cell](https://github.com/matklad/once_cell) from 1.20.2 to 1.20.3.
- [Changelog](https://github.com/matklad/once_cell/blob/master/CHANGELOG.md)
- [Commits](https://github.com/matklad/once_cell/compare/v1.20.2...v1.20.3)

---
updated-dependencies:
- dependency-name: once_cell
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-14 08:06:30 +00:00
Sylvestre Ledru e0397917e4 Merge pull request #470 from cakebaker/ci_fix_code_coverage
ci: use `-Cinstrument-coverage` instead of `-Zprofile`
2025-02-13 18:16:44 +01:00
Daniel Hofstetter 9d1a97768f ci: use -Cinstrument-coverage instead of -Zprofile
and use cargo-llvm-cov instead of grcov.

Support for -Zprofile has been removed from Rust nightly: https://github.com/rust-lang/rust/pull/131829
2025-02-07 14:45:53 +01:00
dependabot[bot] fc3de09ca2 build(deps): bump predicates from 3.1.2 to 3.1.3
Bumps [predicates](https://github.com/assert-rs/predicates-rs) from 3.1.2 to 3.1.3.
- [Changelog](https://github.com/assert-rs/predicates-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/assert-rs/predicates-rs/compare/v3.1.2...v3.1.3)

---
updated-dependencies:
- dependency-name: predicates
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-20 09:34:31 +00:00
hanbings 78e520106c Merge pull request #486 from sylvestre/uucore
Update to uucore 0.0.29
2025-01-20 17:33:23 +08:00
Sylvestre Ledru a21c379953 Update to uucore 0.0.29 2025-01-20 10:12:10 +01:00
Sylvestre Ledru c45e474c7e Merge pull request #487 from hanbings/fix-ci
ci: Fix issues related to downloading builds in ci.
2025-01-20 10:11:35 +01:00
hanbings f536f813e2 Run cargo clippy. 2025-01-20 16:22:17 +08:00
hanbings 8685fa5c64 Switch to using js script to download builds. 2025-01-20 16:16:28 +08:00
Sylvestre Ledru b7f93ea816 Merge pull request #475 from cakebaker/find_fix_clippy_warnings
find: fix new clippy warnings
2025-01-20 08:47:09 +01:00
hanbings 10c24ca5f1 Update action-download-artifact to try to fix the download issue. 2025-01-20 03:25:00 +08:00
hanbings 33cd6573aa Adjust CI to new configuration files for Ubuntu 24.04. 2025-01-20 03:00:24 +08:00
hanbings 6ce5ffaa9e Adjust CI to new configuration files for Ubuntu 24.04. 2025-01-20 02:49:28 +08:00
dependabot[bot] 78b264deae build(deps): bump serial_test from 3.1.1 to 3.2.0 (#471)
Bumps [serial_test](https://github.com/palfrey/serial_test) from 3.1.1 to 3.2.0.
- [Release notes](https://github.com/palfrey/serial_test/releases)
- [Commits](https://github.com/palfrey/serial_test/compare/v3.1.1...v3.2.0)

---
updated-dependencies:
- dependency-name: serial_test
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-20 02:25:13 +08:00
Daniel Hofstetter 3fa75542c4 find: fix needless_lifetimes warning 2024-11-29 10:23:25 +01:00
Daniel Hofstetter 4e954a7a41 find: fix empty_line_after_outer_attr warnings 2024-11-29 10:20:45 +01:00
Daniel Hofstetter 50b7ec67e0 Merge pull request #468 from uutils/dependabot/cargo/regex-1.11.1
build(deps): bump regex from 1.11.0 to 1.11.1
2024-10-25 07:03:50 +02:00
dependabot[bot] b3f4deda63 build(deps): bump regex from 1.11.0 to 1.11.1
Bumps [regex](https://github.com/rust-lang/regex) from 1.11.0 to 1.11.1.
- [Release notes](https://github.com/rust-lang/regex/releases)
- [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/regex/compare/1.11.0...1.11.1)

---
updated-dependencies:
- dependency-name: regex
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-25 03:10:12 +00:00
dependabot[bot] 1b3ad03b1f build(deps): bump clap from 4.5.18 to 4.5.20 (#465)
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.18 to 4.5.20.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.18...clap_complete-v4.5.20)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: hanbings <hanbings@hanbings.io>
2024-10-19 15:19:54 +08:00
dependabot[bot] 11a0b64c91 build(deps): bump once_cell from 1.20.0 to 1.20.2 (#464)
Bumps [once_cell](https://github.com/matklad/once_cell) from 1.20.0 to 1.20.2.
- [Changelog](https://github.com/matklad/once_cell/blob/master/CHANGELOG.md)
- [Commits](https://github.com/matklad/once_cell/compare/v1.20.0...v1.20.2)

---
updated-dependencies:
- dependency-name: once_cell
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: hanbings <hanbings@hanbings.io>
2024-10-19 15:03:20 +08:00
dependabot[bot] 2e9e90887f build(deps): bump tempfile from 3.12.0 to 3.13.0 (#458)
Bumps [tempfile](https://github.com/Stebalien/tempfile) from 3.12.0 to 3.13.0.
- [Changelog](https://github.com/Stebalien/tempfile/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Stebalien/tempfile/compare/v3.12.0...v3.13.0)

---
updated-dependencies:
- dependency-name: tempfile
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: hanbings <hanbings@hanbings.io>
2024-10-19 14:53:16 +08:00
Phillips Horselover 526079b5d3 find/time: calculate midnight with local time zone (#467)
* find/time: calculate midnight with local time zone

* find/time: remove deprecated functions

* find/time: add test for local midnight calculation
2024-10-19 14:33:37 +08:00
Daniel Hofstetter dce8b2dda5 Merge pull request #460 from uutils/dependabot/cargo/regex-1.11.0
build(deps): bump regex from 1.10.6 to 1.11.0
2024-09-30 08:25:19 +02:00
dependabot[bot] fb7a56be63 build(deps): bump regex from 1.10.6 to 1.11.0
Bumps [regex](https://github.com/rust-lang/regex) from 1.10.6 to 1.11.0.
- [Release notes](https://github.com/rust-lang/regex/releases)
- [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/regex/compare/1.10.6...1.11.0)

---
updated-dependencies:
- dependency-name: regex
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-30 03:03:45 +00:00
Tavian Barnes acbfcc52ca find/glob: Don't use $. as a non-matching regex (#457)
The oniguruma version built by onig_sys does not include the commit
6f7b0aa ("add ONIG_SYN_BRE_ANCHOR_AT_EDGE_OF_SUBEXP into
ONIG_SYNTAX_POSIX_BASIC and ONIG_SYNTAX_GREP"), causing `$.` to actually
match a literal newline.

Using `$.` is not portable anyway; POSIX says

> A <dollar-sign> ( '$' ) shall be an anchor when used as the last
> character of an entire BRE.
2024-09-27 13:23:31 +08:00
Andrew Liebenow b641b85239 Print help/version without error prefix (#455)
* xargs: print help/version without error prefix

xargs, when executed with -h/--help or -V/--version, is printing an
"Error: " prefix before the help and version text. The exit code is
also incorrect (1, instead of 0).

Display the help/version text without the erroneous prefix, and exit
with the correct exit code (success, 0).

Add tests for this bug to prevent regressions.
2024-09-23 22:20:05 +02:00
Daniel Hofstetter eefc5804d4 Merge pull request #456 from uutils/dependabot/cargo/clap-4.5.18
build(deps): bump clap from 4.5.17 to 4.5.18
2024-09-23 07:08:29 +02:00
dependabot[bot] 309d3d9211 build(deps): bump clap from 4.5.17 to 4.5.18
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.17 to 4.5.18.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.17...clap_complete-v4.5.18)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-23 03:24:18 +00:00
Sylvestre Ledru 9d66be6b7d manage the special case 'find %A+ (#454)
* declare the variable where it is used

* manage the special case 'find %A+'

Closes: #451
2024-09-22 16:15:53 +08:00
hanbings 27d95aa1cc Implement -fprintf (#444) 2024-09-21 21:49:04 +02:00
dependabot[bot] 05493375c7 build(deps): bump once_cell from 1.19.0 to 1.20.0 (#452)
Bumps [once_cell](https://github.com/matklad/once_cell) from 1.19.0 to 1.20.0.
- [Changelog](https://github.com/matklad/once_cell/blob/master/CHANGELOG.md)
- [Commits](https://github.com/matklad/once_cell/compare/v1.19.0...v1.20.0)

---
updated-dependencies:
- dependency-name: once_cell
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-16 12:43:27 +08:00
dependabot[bot] 7f3c00db69 build(deps): bump pretty_assertions from 1.4.0 to 1.4.1 (#453)
Bumps [pretty_assertions](https://github.com/rust-pretty-assertions/rust-pretty-assertions) from 1.4.0 to 1.4.1.
- [Release notes](https://github.com/rust-pretty-assertions/rust-pretty-assertions/releases)
- [Changelog](https://github.com/rust-pretty-assertions/rust-pretty-assertions/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rust-pretty-assertions/rust-pretty-assertions/compare/v1.4.0...v1.4.1)

---
updated-dependencies:
- dependency-name: pretty_assertions
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-16 12:39:30 +08:00
hanbings 4d367891dc Implement -fprint0 (#443) 2024-09-09 18:36:44 +02:00
Sylvestre Ledru e3f4224f12 prepare version 0.7.0 2024-09-08 10:04:46 +02:00
Sylvestre Ledru 8fc5ddb299 Merge pull request #450 from cakebaker/find_fix_clippy_warning
find: use array instead of closure
2024-09-06 08:53:55 +02:00
Daniel Hofstetter b4f22f0747 find: use array instead of closure
fix warning from manual_pattern_char_comparison lint
2024-09-06 07:44:59 +02:00
hanbings db8e265547 Clean up unused generated during Windows platform build process. (#448)
* Fix Windows' warning in build.

* Fix Windows' warning in build.

* Fix Windows' warning in build.

* Run cargo fmt.
2024-09-05 10:10:34 +02:00
hanbings f9d74ada2e Implement -ls and -fls (#435) 2024-09-05 09:06:54 +02:00
Daniel Hofstetter 907f3841ab Merge pull request #449 from uutils/dependabot/cargo/clap-4.5.17
build(deps): bump clap from 4.5.16 to 4.5.17
2024-09-05 07:11:53 +02:00
dependabot[bot] 888d3cd516 build(deps): bump clap from 4.5.16 to 4.5.17
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.16 to 4.5.17.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.16...clap_complete-v4.5.17)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-05 03:38:19 +00:00
Daniel Hofstetter f2e49d8d48 Merge pull request #447 from uutils/dependabot/cargo/filetime-0.2.25
build(deps): bump filetime from 0.2.24 to 0.2.25
2024-08-28 07:05:08 +02:00
dependabot[bot] d847a7bf7e build(deps): bump filetime from 0.2.24 to 0.2.25
Bumps [filetime](https://github.com/alexcrichton/filetime) from 0.2.24 to 0.2.25.
- [Commits](https://github.com/alexcrichton/filetime/compare/0.2.24...0.2.25)

---
updated-dependencies:
- dependency-name: filetime
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-28 03:28:47 +00:00
Tavian Barnes abf851dd7d ci: Update bfs to 4.0 (#446)
And fix diff-bfs.sh for the new test result format.
2024-08-17 08:13:30 +01:00
Daniel Hofstetter d9d2a4e865 Merge pull request #445 from uutils/dependabot/cargo/clap-4.5.16
build(deps): bump clap from 4.5.15 to 4.5.16
2024-08-16 09:28:56 +02:00
dependabot[bot] 9b0fea9880 build(deps): bump clap from 4.5.15 to 4.5.16
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.15 to 4.5.16.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.15...clap_complete-v4.5.16)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-16 03:39:20 +00:00
Tavian Barnes a7a73c325d Wrap walkdir::DirEntry in a new type (#436)
* Add -follow support.

* tests/find: Serialize find_time()

find_time() relies on the working directory, but e.g.
delete_on_dot_dir() will temporarily change directories, causing
find_time() to fail when run in parallel.

* find: Don't use uutils::error::set_exit_code()

The global exit code can polute the results of other tests.

Link: https://github.com/uutils/coreutils/issues/5777

* find: New WalkEntry wrapper

The new type wraps DirEntry when possible, but also lets us pass a valid
entry to matchers when walkdir returns a broken symlink error.  It also
implements a Metadata cache (part of #430).

* find: Implement -H, -L, -P flags

* find: Fix -follow -samefile

* find: Fix -follow -newer

* find: Implement -xtype

* find: Fix -delete error handling

---------

Co-authored-by: hanbings <hanbings@hanbings.io>
2024-08-15 04:01:19 +08:00
Daniel Hofstetter 465856ce24 Merge pull request #442 from uutils/dependabot/cargo/filetime-0.2.24
build(deps): bump filetime from 0.2.23 to 0.2.24
2024-08-12 08:16:29 +02:00
Daniel Hofstetter e456c23d5c Merge pull request #441 from uutils/dependabot/cargo/clap-4.5.15
build(deps): bump clap from 4.5.14 to 4.5.15
2024-08-12 08:14:51 +02:00
dependabot[bot] 4dc350e5fb build(deps): bump filetime from 0.2.23 to 0.2.24
Bumps [filetime](https://github.com/alexcrichton/filetime) from 0.2.23 to 0.2.24.
- [Commits](https://github.com/alexcrichton/filetime/compare/0.2.23...0.2.24)

---
updated-dependencies:
- dependency-name: filetime
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-12 03:51:06 +00:00
dependabot[bot] fcc929a978 build(deps): bump clap from 4.5.14 to 4.5.15
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.14 to 4.5.15.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.14...v4.5.15)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-12 03:51:01 +00:00
hanbings 423453eaf8 Implement -fprint (#421) 2024-08-11 09:38:05 +02:00
Daniel Hofstetter 79986a26f2 Merge pull request #437 from uutils/dependabot/cargo/assert_cmd-2.0.16
build(deps): bump assert_cmd from 2.0.15 to 2.0.16
2024-08-09 08:07:12 +02:00
Daniel Hofstetter de1bc84812 Merge pull request #438 from uutils/dependabot/cargo/clap-4.5.14
build(deps): bump clap from 4.5.13 to 4.5.14
2024-08-09 08:05:59 +02:00
dependabot[bot] f0cd3748b8 build(deps): bump clap from 4.5.13 to 4.5.14
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.13 to 4.5.14.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.13...v4.5.14)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-09 03:24:36 +00:00
dependabot[bot] 780d106b34 build(deps): bump assert_cmd from 2.0.15 to 2.0.16
Bumps [assert_cmd](https://github.com/assert-rs/assert_cmd) from 2.0.15 to 2.0.16.
- [Changelog](https://github.com/assert-rs/assert_cmd/blob/master/CHANGELOG.md)
- [Commits](https://github.com/assert-rs/assert_cmd/compare/v2.0.15...v2.0.16)

---
updated-dependencies:
- dependency-name: assert_cmd
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-09 03:24:28 +00:00
Daniel Hofstetter 4043c00bde Merge pull request #434 from uutils/dependabot/cargo/tempfile-3.12.0
build(deps): bump tempfile from 3.11.0 to 3.12.0
2024-08-07 08:01:56 +02:00
dependabot[bot] f0c3562aca build(deps): bump tempfile from 3.11.0 to 3.12.0
Bumps [tempfile](https://github.com/Stebalien/tempfile) from 3.11.0 to 3.12.0.
- [Changelog](https://github.com/Stebalien/tempfile/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Stebalien/tempfile/commits)

---
updated-dependencies:
- dependency-name: tempfile
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-07 03:44:37 +00:00
dependabot[bot] d7bf7da4a1 build(deps): bump predicates from 3.1.0 to 3.1.2 (#424)
Bumps [predicates](https://github.com/assert-rs/predicates-rs) from 3.1.0 to 3.1.2.
- [Changelog](https://github.com/assert-rs/predicates-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/assert-rs/predicates-rs/compare/v3.1.0...v3.1.2)

---
updated-dependencies:
- dependency-name: predicates
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: hanbings <hanbings@hanbings.io>
2024-08-06 00:39:50 +08:00
dependabot[bot] 73dcd94827 build(deps): bump assert_cmd from 2.0.14 to 2.0.15 (#426)
Bumps [assert_cmd](https://github.com/assert-rs/assert_cmd) from 2.0.14 to 2.0.15.
- [Changelog](https://github.com/assert-rs/assert_cmd/blob/master/CHANGELOG.md)
- [Commits](https://github.com/assert-rs/assert_cmd/compare/v2.0.14...v2.0.15)

---
updated-dependencies:
- dependency-name: assert_cmd
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: hanbings <hanbings@hanbings.io>
2024-08-06 00:29:40 +08:00
Tavian Barnes 60e4a8cf92 find: -daystart returns true (#428) 2024-08-06 00:22:42 +08:00
Daniel Hofstetter 8b295e7d8a Merge pull request #433 from uutils/dependabot/cargo/tempfile-3.11.0
build(deps): bump tempfile from 3.10.1 to 3.11.0
2024-08-05 07:59:49 +02:00
Daniel Hofstetter 2dc916f980 Merge pull request #432 from uutils/dependabot/cargo/regex-1.10.6
build(deps): bump regex from 1.10.5 to 1.10.6
2024-08-05 07:58:59 +02:00
dependabot[bot] 19012452e1 build(deps): bump tempfile from 3.10.1 to 3.11.0
Bumps [tempfile](https://github.com/Stebalien/tempfile) from 3.10.1 to 3.11.0.
- [Changelog](https://github.com/Stebalien/tempfile/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Stebalien/tempfile/compare/v3.10.1...v3.11.0)

---
updated-dependencies:
- dependency-name: tempfile
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-05 03:55:36 +00:00
dependabot[bot] 6042d5a47f build(deps): bump regex from 1.10.5 to 1.10.6
Bumps [regex](https://github.com/rust-lang/regex) from 1.10.5 to 1.10.6.
- [Release notes](https://github.com/rust-lang/regex/releases)
- [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/regex/compare/1.10.5...1.10.6)

---
updated-dependencies:
- dependency-name: regex
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-05 03:55:28 +00:00
Daniel Hofstetter 45e5964e6a Merge pull request #429 from uutils/dependabot/cargo/clap-4.5.13
build(deps): bump clap from 4.5.10 to 4.5.13
2024-08-01 09:33:03 +02:00
dependabot[bot] 9544f78977 build(deps): bump clap from 4.5.10 to 4.5.13
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.10 to 4.5.13.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.10...v4.5.13)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-01 03:35:21 +00:00
Sylvestre Ledru 5be00c85d7 Merge pull request #427 from tavianator/simplify-matcher-io
Remove unnecessary lifetime parameter from Dependencies
2024-07-28 09:53:25 +02:00
Tavian Barnes 8771861b0c Remove unnecessary lifetime parameter from Dependencies 2024-07-27 19:54:28 -04:00
Daniel Hofstetter c4a56e9c3f find: fix needless borrow clippy warning (#423) 2024-07-26 03:15:07 +08:00
Daniel Hofstetter 3b222d6040 Merge pull request #422 from uutils/dependabot/cargo/clap-4.5.10
build(deps): bump clap from 4.5.9 to 4.5.10
2024-07-24 07:27:06 +02:00
dependabot[bot] 6f53585a4c build(deps): bump clap from 4.5.9 to 4.5.10
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.9 to 4.5.10.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.9...v4.5.10)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-24 03:12:02 +00:00
hanbings 1b9904eb5f Implement -daystart (#413) 2024-07-21 16:06:15 +02:00
Tavian Barnes 4807264faa find: ctime is "changed" time, not "created" time (#416)
Fixes #358
2024-07-17 21:14:00 +08:00
Chen Chen 9f44977270 Implement simple caching for -fstype (#419)
* Implement simple caching for -fstype

* Using lstat instead of stat in -fstype
2024-07-13 01:43:56 +08:00
Sylvestre Ledru 7f4423e703 Merge pull request #417 from tavianator/options-return-true
options return true
2024-07-11 21:49:40 +02:00
Tavian Barnes e9634f1ed4 find: Stop parsing after we see -help or -version
GNU find accepts things like

    $ find \( -help -foo -not
2024-07-10 11:55:14 -04:00
Tavian Barnes 37a543b9d6 find: Options like -maxdepth return true
Previously, this would give an error:

    $ find -maxdepth 0 -a -print
    Error: invalid expression; you have used a binary operator '-a' with nothing before it.

Now, options are treated like other primary expressions:

    $ find -maxdepth 0 -a print
    .
2024-07-10 11:42:58 -04:00
hanbings 9c11f11045 Implement -noleaf (#414) 2024-07-09 23:27:18 +02:00
Daniel Hofstetter a0b5713b61 Merge pull request #415 from uutils/dependabot/cargo/clap-4.5.9
build(deps): bump clap from 4.5.8 to 4.5.9
2024-07-09 08:23:11 +02:00
dependabot[bot] 5d4b9ca5e2 build(deps): bump clap from 4.5.8 to 4.5.9
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.8 to 4.5.9.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.5.8...v4.5.9)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-09 03:09:20 +00:00
Daniel Hofstetter 3bf5567397 Merge pull request #410 from uutils/dependabot/cargo/clap-4.5.8
build(deps): bump clap from 4.5.7 to 4.5.8
2024-07-01 07:41:29 +02:00
dependabot[bot] bbc9f06543 build(deps): bump clap from 4.5.7 to 4.5.8
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.7 to 4.5.8.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.7...v4.5.8)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-01 03:30:04 +00:00
42 changed files with 3675 additions and 1176 deletions
+8 -33
View File
@@ -105,49 +105,24 @@ jobs:
- name: Install `rust` toolchain
run: |
## Install `rust` toolchain
rustup toolchain install nightly --no-self-update -c rustfmt --profile minimal
rustup default nightly
- name: "`grcov` ~ install"
run: cargo install grcov
- name: Install cargo-llvm-cov
uses: taiki-e/install-action@cargo-llvm-cov
- name: Generate code coverage
run: cargo llvm-cov --all-features --lcov --branch --output-path lcov.info
- name: cargo test
run: |
cargo test --all --no-fail-fast ${{ matrix.cargo_flags }}
env:
CARGO_INCREMENTAL: "0"
RUSTFLAGS: "-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort -Cdebug-assertions=off"
RUSTDOCFLAGS: "-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort -Cdebug-assertions=off"
- name: Generate coverage data
id: grcov
run: |
grcov target/debug/ \
--branch \
--llvm \
--source-dir . \
--output-path lcov.info \
--ignore-not-existing \
--excl-line "#\\[derive\\(" \
--excl-br-line "#\\[derive\\(" \
--excl-start "#\\[cfg\\(test\\)\\]" \
--excl-br-start "#\\[cfg\\(test\\)\\]" \
--commit-sha ${{ github.sha }} \
--service-job-id ${{ github.job }} \
--service-name "GitHub Actions" \
--service-number ${{ github.run_id }}
- name: Upload coverage as artifact
uses: actions/upload-artifact@v4
with:
name: lcov.info
# path: ${{ steps.grcov.outputs.report }}
path: lcov.info
- name: Upload coverage to codecov.io
uses: codecov/codecov-action@v4
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
# file: ${{ steps.grcov.outputs.report }}
file: lcov.info
files: lcov.info
fail_ci_if_error: true
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
+95 -42
View File
@@ -4,6 +4,9 @@ name: External-testsuites
jobs:
gnu-tests:
permissions:
actions: read
name: Run GNU findutils tests
runs-on: ubuntu-latest
steps:
@@ -17,7 +20,15 @@ jobs:
repository: gnu-mirror-unofficial/findutils
path: findutils.gnu
ref: 5768a03ddfb5e18b1682e339d6cdd24ff721c510
submodules: true
submodules: false
- name: Override submodule URL and initialize submodules
# Use github instead of upstream git server
run: |
git submodule sync --recursive
git config submodule.gnulib.url https://github.com/coreutils/gnulib.git
git submodule update --init --recursive --depth 1
working-directory: findutils.gnu
- name: Install `rust` toolchain
run: |
## Install `rust` toolchain
@@ -27,7 +38,7 @@ jobs:
shell: bash
run: |
# Enable sources & install dependencies
sudo find /etc/apt/sources.list* -type f -exec sed -i 'p; s/^deb /deb-src /' '{}' +
sudo sed -i 's/^Types: deb$/Types: deb deb-src/' /etc/apt/sources.list.d/ubuntu.sources
sudo apt-get update
sudo apt-get build-dep findutils
- name: Run GNU tests
@@ -38,6 +49,7 @@ jobs:
- name: Extract testing info
shell: bash
run: |
- name: Upload gnu-test-report
uses: actions/upload-artifact@v4
with:
@@ -51,24 +63,43 @@ jobs:
with:
name: gnu-result
path: gnu-result.json
- name: Download the result
uses: dawidd6/action-download-artifact@v6
- name: Download artifacts (gnu-result and gnu-test-report)
uses: actions/github-script@v7
with:
workflow: compat.yml
workflow_conclusion: completed
name: gnu-result
repo: uutils/findutils
branch: main
path: dl
- name: Download the log
uses: dawidd6/action-download-artifact@v6
with:
workflow: compat.yml
workflow_conclusion: completed
name: gnu-test-report
repo: uutils/findutils
branch: main
path: dl
script: |
let fs = require('fs');
fs.mkdirSync('${{ github.workspace }}/dl', { recursive: true });
async function downloadArtifact(artifactName) {
// List all artifacts from the workflow run
let artifacts = await github.rest.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: ${{ github.run_id }},
});
// Find the specified artifact
let matchArtifact = artifacts.data.artifacts.find((artifact) => artifact.name === artifactName);
if (!matchArtifact) {
throw new Error(`Artifact "${artifactName}" not found.`);
}
// Download the artifact
let download = await github.rest.actions.downloadArtifact({
owner: context.repo.owner,
repo: context.repo.repo,
artifact_id: matchArtifact.id,
archive_format: 'zip',
});
// Save the artifact to a file
fs.writeFileSync(`${{ github.workspace }}/dl/${artifactName}.zip`, Buffer.from(download.data));
}
// Download the required artifacts
await downloadArtifact("gnu-result");
await downloadArtifact("gnu-test-report");
- name: Compare failing tests against master
shell: bash
run: |
@@ -76,6 +107,8 @@ jobs:
- name: Compare against main results
shell: bash
run: |
unzip dl/gnu-result.zip -d dl/
unzip dl/gnu-test-report.zip -d dl/
mv dl/gnu-result.json latest-gnu-result.json
python findutils/util/compare_gnu_result.py
@@ -92,7 +125,7 @@ jobs:
with:
repository: tavianator/bfs
path: bfs
ref: "3.1.3"
ref: "4.0"
- name: Install `rust` toolchain
run: |
## Install `rust` toolchain
@@ -102,7 +135,7 @@ jobs:
shell: bash
run: |
# Enable sources & install dependencies
sudo find /etc/apt/sources.list* -type f -exec sed -i 'p; s/^deb /deb-src /' '{}' +
sudo sed -i 's/^Types: deb$/Types: deb deb-src/' /etc/apt/sources.list.d/ubuntu.sources
sudo apt-get update
sudo apt-get build-dep bfs
- name: Run BFS tests
@@ -120,24 +153,42 @@ jobs:
with:
name: bfs-result
path: bfs-result.json
- name: Download the result
uses: dawidd6/action-download-artifact@v6
- name: Download artifacts (gnu-result and bfs-test-report)
uses: actions/github-script@v7
with:
workflow: compat.yml
workflow_conclusion: completed
name: bfs-result
repo: uutils/findutils
branch: main
path: dl
- name: Download the log
uses: dawidd6/action-download-artifact@v6
with:
workflow: compat.yml
workflow_conclusion: completed
name: bfs-test-report
repo: uutils/findutils
branch: main
path: dl
script: |
let fs = require('fs');
fs.mkdirSync('${{ github.workspace }}/dl', { recursive: true });
async function downloadArtifact(artifactName) {
// List all artifacts from the workflow run
let artifacts = await github.rest.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: ${{ github.run_id }},
});
// Find the specified artifact
let matchArtifact = artifacts.data.artifacts.find((artifact) => artifact.name === artifactName);
if (!matchArtifact) {
throw new Error(`Artifact "${artifactName}" not found.`);
}
// Download the artifact
let download = await github.rest.actions.downloadArtifact({
owner: context.repo.owner,
repo: context.repo.repo,
artifact_id: matchArtifact.id,
archive_format: 'zip',
});
// Save the artifact to a file
fs.writeFileSync(`${{ github.workspace }}/dl/${artifactName}.zip`, Buffer.from(download.data));
}
// Download the required artifacts
await downloadArtifact("bfs-result");
await downloadArtifact("bfs-test-report");
- name: Compare failing tests against main
shell: bash
run: |
@@ -145,6 +196,8 @@ jobs:
- name: Compare against main results
shell: bash
run: |
unzip dl/bfs-result.zip -d dl/
unzip dl/bfs-test-report.zip -d dl/
mv dl/bfs-result.json latest-bfs-result.json
python findutils/util/compare_bfs_result.py
@@ -157,7 +210,7 @@ jobs:
steps:
- name: List Annotations
uses: actions/github-script@v7
with:
script: |
let runs = await github.rest.checks.listForRef({
@@ -177,7 +230,7 @@ jobs:
repo: context.repo.repo,
check_run_id: result.id
});
run.data.forEach(data => {
annotations.data.push({
run: result.name,
@@ -187,7 +240,7 @@ jobs:
}
// Remove duplicate items.
annotations.data = annotations.data.filter((value, index, self) =>
annotations.data = annotations.data.filter((value, index, self) =>
self.findIndex(v => v.annotation.message === value.annotation.message) === index);
let fs = require('fs');
@@ -197,4 +250,4 @@ jobs:
uses: actions/upload-artifact@v4
with:
name: comment
path: annotations.json
path: annotations.json
+79 -54
View File
@@ -1,20 +1,21 @@
# Copyright 2022-2023, axodotdev
# This file was autogenerated by dist: https://opensource.axo.dev/cargo-dist/
#
# Copyright 2022-2024, axodotdev
# SPDX-License-Identifier: MIT or Apache-2.0
#
# CI that:
#
# * checks for a Git Tag that looks like a release
# * builds artifacts with cargo-dist (archives, installers, hashes)
# * builds artifacts with dist (archives, installers, hashes)
# * uploads those artifacts to temporary workflow zip
# * on success, uploads the artifacts to a Github Release
# * on success, uploads the artifacts to a GitHub Release
#
# Note that the Github Release will be created with a generated
# Note that the GitHub Release will be created with a generated
# title/body based on your changelogs.
name: Release
permissions:
contents: write
"contents": "write"
# This task will run whenever you push a git tag that looks like a version
# like "1.0.0", "v0.1.0-prerelease.1", "my-app/0.1.0", "releases/v1.0.0", etc.
@@ -23,30 +24,30 @@ permissions:
# must be a Cargo-style SemVer Version (must have at least major.minor.patch).
#
# If PACKAGE_NAME is specified, then the announcement will be for that
# package (erroring out if it doesn't have the given version or isn't cargo-dist-able).
# package (erroring out if it doesn't have the given version or isn't dist-able).
#
# If PACKAGE_NAME isn't specified, then the announcement will be for all
# (cargo-dist-able) packages in the workspace with that version (this mode is
# (dist-able) packages in the workspace with that version (this mode is
# intended for workspaces with only one dist-able package, or with all dist-able
# packages versioned/released in lockstep).
#
# If you push multiple tags at once, separate instances of this workflow will
# spin up, creating an independent announcement for each one. However Github
# spin up, creating an independent announcement for each one. However, GitHub
# will hard limit this to 3 tags per commit, as it will assume more tags is a
# mistake.
#
# If there's a prerelease-style suffix to the version, then the release(s)
# will be marked as a prerelease.
on:
pull_request:
push:
tags:
- '**[0-9]+.[0-9]+.[0-9]+*'
pull_request:
jobs:
# Run 'cargo dist plan' (or host) to determine what tasks we need to do
# Run 'dist plan' (or host) to determine what tasks we need to do
plan:
runs-on: ubuntu-latest
runs-on: "ubuntu-20.04"
outputs:
val: ${{ steps.plan.outputs.manifest }}
tag: ${{ !github.event.pull_request && github.ref_name || '' }}
@@ -58,11 +59,16 @@ jobs:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Install cargo-dist
- name: Install dist
# we specify bash to get pipefail; it guards against the `curl` command
# failing. otherwise `sh` won't catch that `curl` returned non-0
shell: bash
run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.12.0/cargo-dist-installer.sh | sh"
run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.28.0/cargo-dist-installer.sh | sh"
- name: Cache dist
uses: actions/upload-artifact@v4
with:
name: cargo-dist-cache
path: ~/.cargo/bin/dist
# sure would be cool if github gave us proper conditionals...
# so here's a doubly-nested ternary-via-truthiness to try to provide the best possible
# functionality based on whether this is a pull_request, and whether it's from a fork.
@@ -70,8 +76,8 @@ jobs:
# but also really annoying to build CI around when it needs secrets to work right.)
- id: plan
run: |
cargo dist ${{ (!github.event.pull_request && format('host --steps=create --tag={0}', github.ref_name)) || 'plan' }} --output-format=json > plan-dist-manifest.json
echo "cargo dist ran successfully"
dist ${{ (!github.event.pull_request && format('host --steps=create --tag={0}', github.ref_name)) || 'plan' }} --output-format=json > plan-dist-manifest.json
echo "dist ran successfully"
cat plan-dist-manifest.json
echo "manifest=$(jq -c "." plan-dist-manifest.json)" >> "$GITHUB_OUTPUT"
- name: "Upload dist-manifest.json"
@@ -89,28 +95,38 @@ jobs:
if: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix.include != null && (needs.plan.outputs.publishing == 'true' || fromJson(needs.plan.outputs.val).ci.github.pr_run_mode == 'upload') }}
strategy:
fail-fast: false
# Target platforms/runners are computed by cargo-dist in create-release.
# Target platforms/runners are computed by dist in create-release.
# Each member of the matrix has the following arguments:
#
# - runner: the github runner
# - dist-args: cli flags to pass to cargo dist
# - install-dist: expression to run to install cargo-dist on the runner
# - dist-args: cli flags to pass to dist
# - install-dist: expression to run to install dist on the runner
#
# Typically there will be:
# - 1 "global" task that builds universal installers
# - N "local" tasks that build each platform's binaries and platform-specific installers
matrix: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix }}
runs-on: ${{ matrix.runner }}
container: ${{ matrix.container && matrix.container.image || null }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BUILD_MANIFEST_NAME: target/distrib/${{ join(matrix.targets, '-') }}-dist-manifest.json
steps:
- name: enable windows longpaths
run: |
git config --global core.longpaths true
- uses: actions/checkout@v4
with:
submodules: recursive
- uses: swatinem/rust-cache@v2
- name: Install cargo-dist
run: ${{ matrix.install_dist }}
- name: Install Rust non-interactively if not already installed
if: ${{ matrix.container }}
run: |
if ! command -v cargo > /dev/null 2>&1; then
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
fi
- name: Install dist
run: ${{ matrix.install_dist.run }}
# Get the dist-manifest
- name: Fetch local artifacts
uses: actions/download-artifact@v4
@@ -124,8 +140,8 @@ jobs:
- name: Build artifacts
run: |
# Actually do builds and make zips and whatnot
cargo dist build ${{ needs.plan.outputs.tag-flag }} --print=linkage --output-format=json ${{ matrix.dist_args }} > dist-manifest.json
echo "cargo dist ran successfully"
dist build ${{ needs.plan.outputs.tag-flag }} --print=linkage --output-format=json ${{ matrix.dist_args }} > dist-manifest.json
echo "dist ran successfully"
- id: cargo-dist
name: Post-build
# We force bash here just because github makes it really hard to get values up
@@ -135,7 +151,7 @@ jobs:
run: |
# Parse out what we just built and upload it to scratch storage
echo "paths<<EOF" >> "$GITHUB_OUTPUT"
jq --raw-output ".artifacts[]?.path | select( . != null )" dist-manifest.json >> "$GITHUB_OUTPUT"
dist print-upload-files-from-manifest --manifest dist-manifest.json >> "$GITHUB_OUTPUT"
echo "EOF" >> "$GITHUB_OUTPUT"
cp dist-manifest.json "$BUILD_MANIFEST_NAME"
@@ -160,9 +176,12 @@ jobs:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Install cargo-dist
shell: bash
run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.12.0/cargo-dist-installer.sh | sh"
- name: Install cached dist
uses: actions/download-artifact@v4
with:
name: cargo-dist-cache
path: ~/.cargo/bin/
- run: chmod +x ~/.cargo/bin/dist
# Get all the local artifacts for the global tasks to use (for e.g. checksums)
- name: Fetch local artifacts
uses: actions/download-artifact@v4
@@ -173,12 +192,12 @@ jobs:
- id: cargo-dist
shell: bash
run: |
cargo dist build ${{ needs.plan.outputs.tag-flag }} --output-format=json "--artifacts=global" > dist-manifest.json
echo "cargo dist ran successfully"
dist build ${{ needs.plan.outputs.tag-flag }} --output-format=json "--artifacts=global" > dist-manifest.json
echo "dist ran successfully"
# Parse out what we just built and upload it to scratch storage
echo "paths<<EOF" >> "$GITHUB_OUTPUT"
jq --raw-output ".artifacts[]?.path | select( . != null )" dist-manifest.json >> "$GITHUB_OUTPUT"
jq --raw-output ".upload_files[]" dist-manifest.json >> "$GITHUB_OUTPUT"
echo "EOF" >> "$GITHUB_OUTPUT"
cp dist-manifest.json "$BUILD_MANIFEST_NAME"
@@ -206,8 +225,12 @@ jobs:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Install cargo-dist
run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.12.0/cargo-dist-installer.sh | sh"
- name: Install cached dist
uses: actions/download-artifact@v4
with:
name: cargo-dist-cache
path: ~/.cargo/bin/
- run: chmod +x ~/.cargo/bin/dist
# Fetch artifacts from scratch-storage
- name: Fetch artifacts
uses: actions/download-artifact@v4
@@ -215,11 +238,10 @@ jobs:
pattern: artifacts-*
path: target/distrib/
merge-multiple: true
# This is a harmless no-op for Github Releases, hosting for that happens in "announce"
- id: host
shell: bash
run: |
cargo dist host ${{ needs.plan.outputs.tag-flag }} --steps=upload --steps=release --output-format=json > dist-manifest.json
dist host ${{ needs.plan.outputs.tag-flag }} --steps=upload --steps=release --output-format=json > dist-manifest.json
echo "artifacts uploaded and released successfully"
cat dist-manifest.json
echo "manifest=$(jq -c "." dist-manifest.json)" >> "$GITHUB_OUTPUT"
@@ -229,8 +251,29 @@ jobs:
# Overwrite the previous copy
name: artifacts-dist-manifest
path: dist-manifest.json
# Create a GitHub Release while uploading all files to it
- name: "Download GitHub Artifacts"
uses: actions/download-artifact@v4
with:
pattern: artifacts-*
path: artifacts
merge-multiple: true
- name: Cleanup
run: |
# Remove the granular manifests
rm -f artifacts/*-dist-manifest.json
- name: Create GitHub Release
env:
PRERELEASE_FLAG: "${{ fromJson(steps.host.outputs.manifest).announcement_is_prerelease && '--prerelease' || '' }}"
ANNOUNCEMENT_TITLE: "${{ fromJson(steps.host.outputs.manifest).announcement_title }}"
ANNOUNCEMENT_BODY: "${{ fromJson(steps.host.outputs.manifest).announcement_github_body }}"
RELEASE_COMMIT: "${{ github.sha }}"
run: |
# Write and read notes from a file to avoid quoting breaking things
echo "$ANNOUNCEMENT_BODY" > $RUNNER_TEMP/notes.txt
gh release create "${{ needs.plan.outputs.tag }}" --target "$RELEASE_COMMIT" $PRERELEASE_FLAG --title "$ANNOUNCEMENT_TITLE" --notes-file "$RUNNER_TEMP/notes.txt" artifacts/*
# Create a Github Release while uploading all files to it
announce:
needs:
- plan
@@ -246,21 +289,3 @@ jobs:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: "Download Github Artifacts"
uses: actions/download-artifact@v4
with:
pattern: artifacts-*
path: artifacts
merge-multiple: true
- name: Cleanup
run: |
# Remove the granular manifests
rm -f artifacts/*-dist-manifest.json
- name: Create Github Release
uses: ncipollo/release-action@v1
with:
tag: ${{ needs.plan.outputs.tag }}
name: ${{ fromJson(needs.host.outputs.val).announcement_title }}
body: ${{ fromJson(needs.host.outputs.val).announcement_github_body }}
prerelease: ${{ fromJson(needs.host.outputs.val).announcement_is_prerelease }}
artifacts: "artifacts/*"
Generated
+344 -198
View File
File diff suppressed because it is too large Load Diff
+23 -24
View File
@@ -1,6 +1,6 @@
[package]
name = "findutils"
version = "0.6.0"
version = "0.8.0"
homepage = "https://github.com/uutils/findutils"
repository = "https://github.com/uutils/findutils"
edition = "2021"
@@ -10,24 +10,24 @@ description = "Rust implementation of GNU findutils"
authors = ["uutils developers"]
[dependencies]
chrono = "0.4.38"
chrono = "0.4.40"
clap = "4.5"
faccess = "0.2.4"
walkdir = "2.5"
regex = "1.10"
once_cell = "1.19"
regex = "1.11"
onig = { version = "6.4", default-features = false }
uucore = { version = "0.0.27", features = ["entries", "fs", "fsext", "mode"] }
uucore = { version = "0.0.30", features = ["entries", "fs", "fsext", "mode"] }
nix = { version = "0.29", features = ["fs", "user"] }
argmax = "0.3.1"
[dev-dependencies]
assert_cmd = "2"
filetime = "0.2"
nix = { version = "0.29", features = ["fs"] }
predicates = "3"
serial_test = "3.1"
serial_test = "3.2"
tempfile = "3"
pretty_assertions = "1.4.0"
pretty_assertions = "1.4.1"
[[bin]]
name = "find"
@@ -46,20 +46,19 @@ path = "src/testing/commandline/main.rs"
inherits = "release"
lto = "thin"
# Config for 'cargo dist'
[workspace.metadata.dist]
# The preferred cargo-dist version to use in CI (Cargo.toml SemVer syntax)
cargo-dist-version = "0.12.0"
# CI backends to support
ci = ["github"]
# The installers to generate for each app
installers = []
# Target platforms to build apps for (Rust target-triple syntax)
targets = [
"aarch64-apple-darwin",
"x86_64-apple-darwin",
"x86_64-unknown-linux-gnu",
"x86_64-pc-windows-msvc",
]
# Publish jobs to run in CI
pr-run-mode = "plan"
[lints.clippy]
multiple_crate_versions = "allow"
cargo_common_metadata = "allow"
uninlined_format_args = "allow"
missing_panics_doc = "allow"
use_self = "warn"
needless_pass_by_value = "warn"
semicolon_if_nothing_returned = "warn"
single_char_pattern = "warn"
explicit_iter_loop = "warn"
if_not_else = "warn"
manual_let_else = "warn"
# Disable for now, we have a few occurrences
# panic = "warn"
+19
View File
@@ -0,0 +1,19 @@
[workspace]
members = ["cargo:."]
# Config for 'dist'
[dist]
# The preferred dist version to use in CI (Cargo.toml SemVer syntax)
cargo-dist-version = "0.28.0"
# CI backends to support
ci = "github"
# The installers to generate for each app
installers = ["shell"]
# Target platforms to build apps for (Rust target-triple syntax)
targets = ["aarch64-apple-darwin", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu", "x86_64-pc-windows-msvc"]
# Which actions to run on pull requests
pr-run-mode = "plan"
# Path that installers should place binaries in
install-path = "CARGO_HOME"
# Whether to install an updater program
install-updater = false
+2 -3
View File
@@ -5,9 +5,8 @@
// https://opensource.org/licenses/MIT.
use faccess::PathExt;
use walkdir::DirEntry;
use super::{Matcher, MatcherIO};
use super::{Matcher, MatcherIO, WalkEntry};
/// Matcher for -{read,writ,execut}able.
pub enum AccessMatcher {
@@ -17,7 +16,7 @@ pub enum AccessMatcher {
}
impl Matcher for AccessMatcher {
fn matches(&self, file_info: &DirEntry, _: &mut MatcherIO) -> bool {
fn matches(&self, file_info: &WalkEntry, _: &mut MatcherIO) -> bool {
let path = file_info.path();
match self {
+10 -12
View File
@@ -7,32 +7,29 @@
* file that was distributed with this source code.
*/
use std::fs::{self, FileType};
use std::fs;
use std::io::{self, stderr, Write};
use std::path::Path;
use walkdir::DirEntry;
use super::{Matcher, MatcherIO};
use super::{Matcher, MatcherIO, WalkEntry};
pub struct DeleteMatcher;
impl DeleteMatcher {
pub fn new() -> Self {
DeleteMatcher
Self
}
fn delete(&self, file_path: &Path, file_type: FileType) -> io::Result<()> {
if file_type.is_dir() {
fs::remove_dir(file_path)
fn delete(&self, entry: &WalkEntry) -> io::Result<()> {
if entry.file_type().is_dir() && !entry.path_is_symlink() {
fs::remove_dir(entry.path())
} else {
fs::remove_file(file_path)
fs::remove_file(entry.path())
}
}
}
impl Matcher for DeleteMatcher {
fn matches(&self, file_info: &DirEntry, _: &mut MatcherIO) -> bool {
fn matches(&self, file_info: &WalkEntry, matcher_io: &mut MatcherIO) -> bool {
let path = file_info.path();
let path_str = path.to_string_lossy();
@@ -43,9 +40,10 @@ impl Matcher for DeleteMatcher {
return true;
}
match self.delete(path, file_info.file_type()) {
match self.delete(file_info) {
Ok(()) => true,
Err(e) => {
matcher_io.set_exit_code(1);
writeln!(&mut stderr(), "Failed to delete {path_str}: {e}").unwrap();
false
}
+4 -4
View File
@@ -9,18 +9,18 @@ use std::{
io::{stderr, Write},
};
use super::Matcher;
use super::{Matcher, MatcherIO, WalkEntry};
pub struct EmptyMatcher;
impl EmptyMatcher {
pub fn new() -> EmptyMatcher {
EmptyMatcher
pub fn new() -> Self {
Self
}
}
impl Matcher for EmptyMatcher {
fn matches(&self, file_info: &walkdir::DirEntry, _: &mut super::MatcherIO) -> bool {
fn matches(&self, file_info: &WalkEntry, _: &mut MatcherIO) -> bool {
if file_info.file_type().is_file() {
match file_info.metadata() {
Ok(meta) => meta.len() == 0,
+343
View File
@@ -0,0 +1,343 @@
//! Paths encountered during a walk.
use std::cell::OnceCell;
use std::error::Error;
use std::ffi::OsStr;
use std::fmt::{self, Display, Formatter};
use std::fs::{self, Metadata};
use std::io::{self, ErrorKind};
#[cfg(unix)]
use std::os::unix::fs::FileTypeExt;
use std::path::{Path, PathBuf};
use walkdir::DirEntry;
use super::Follow;
/// Wrapper for a directory entry.
#[derive(Debug)]
enum Entry {
/// Wraps an explicit path and depth.
Explicit(PathBuf, usize),
/// Wraps a WalkDir entry.
WalkDir(DirEntry),
}
/// File types.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum FileType {
Unknown,
Fifo,
CharDevice,
Directory,
BlockDevice,
Regular,
Symlink,
Socket,
}
impl FileType {
pub fn is_dir(self) -> bool {
self == Self::Directory
}
pub fn is_file(self) -> bool {
self == Self::Regular
}
pub fn is_symlink(self) -> bool {
self == Self::Symlink
}
}
impl From<fs::FileType> for FileType {
fn from(t: fs::FileType) -> Self {
if t.is_dir() {
return Self::Directory;
}
if t.is_file() {
return Self::Regular;
}
if t.is_symlink() {
return Self::Symlink;
}
#[cfg(unix)]
{
if t.is_fifo() {
return Self::Fifo;
}
if t.is_char_device() {
return Self::CharDevice;
}
if t.is_block_device() {
return Self::BlockDevice;
}
if t.is_socket() {
return Self::Socket;
}
}
Self::Unknown
}
}
/// An error encountered while walking a file system.
#[derive(Clone, Debug)]
pub struct WalkError {
/// The path that caused the error, if known.
path: Option<PathBuf>,
/// The depth below the root path, if known.
depth: Option<usize>,
/// The io::Error::raw_os_error(), if known.
raw: Option<i32>,
}
impl WalkError {
/// Get the path this error occurred on, if known.
pub fn path(&self) -> Option<&Path> {
self.path.as_deref()
}
/// Get the traversal depth when this error occurred, if known.
pub fn depth(&self) -> Option<usize> {
self.depth
}
/// Get the kind of I/O error.
pub fn kind(&self) -> ErrorKind {
io::Error::from(self).kind()
}
/// Check for ErrorKind::{NotFound,NotADirectory}.
pub fn is_not_found(&self) -> bool {
if self.kind() == ErrorKind::NotFound {
return true;
}
// NotADirectory is nightly-only
#[cfg(unix)]
{
if self.raw == Some(uucore::libc::ENOTDIR) {
return true;
}
}
false
}
/// Check for ErrorKind::FilesystemLoop.
pub fn is_loop(&self) -> bool {
#[cfg(unix)]
return self.raw == Some(uucore::libc::ELOOP);
#[cfg(not(unix))]
return false;
}
}
impl Display for WalkError {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
let ioe = io::Error::from(self);
if let Some(path) = &self.path {
write!(f, "{}: {}", path.display(), ioe)
} else {
write!(f, "{}", ioe)
}
}
}
impl Error for WalkError {}
impl From<io::Error> for WalkError {
fn from(e: io::Error) -> Self {
Self::from(&e)
}
}
impl From<&io::Error> for WalkError {
fn from(e: &io::Error) -> Self {
Self {
path: None,
depth: None,
raw: e.raw_os_error(),
}
}
}
impl From<walkdir::Error> for WalkError {
fn from(e: walkdir::Error) -> Self {
Self::from(&e)
}
}
impl From<&walkdir::Error> for WalkError {
fn from(e: &walkdir::Error) -> Self {
Self {
path: e.path().map(|p| p.to_owned()),
depth: Some(e.depth()),
raw: e.io_error().and_then(|e| e.raw_os_error()),
}
}
}
impl From<WalkError> for io::Error {
fn from(e: WalkError) -> Self {
Self::from(&e)
}
}
impl From<&WalkError> for io::Error {
fn from(e: &WalkError) -> Self {
e.raw
.map(Self::from_raw_os_error)
.unwrap_or_else(|| ErrorKind::Other.into())
}
}
/// A path encountered while walking a file system.
#[derive(Debug)]
pub struct WalkEntry {
/// The wrapped path/dirent.
inner: Entry,
/// Whether to follow symlinks.
follow: Follow,
/// Cached metadata.
meta: OnceCell<Result<Metadata, WalkError>>,
}
impl WalkEntry {
/// Create a new WalkEntry for a specific file.
pub fn new(path: impl Into<PathBuf>, depth: usize, follow: Follow) -> Self {
Self {
inner: Entry::Explicit(path.into(), depth),
follow,
meta: OnceCell::new(),
}
}
/// Convert a [walkdir::DirEntry] to a [WalkEntry]. Errors due to broken symbolic links will be
/// converted to valid entries, but other errors will be propagated.
pub fn from_walkdir(
result: walkdir::Result<DirEntry>,
follow: Follow,
) -> Result<Self, WalkError> {
let result = result.map_err(WalkError::from);
match result {
Ok(entry) => {
let ret = if entry.depth() == 0 && follow != Follow::Never {
// DirEntry::file_type() is wrong for root symlinks when follow_root_links is set
Self::new(entry.path(), 0, follow)
} else {
Self {
inner: Entry::WalkDir(entry),
follow,
meta: OnceCell::new(),
}
};
Ok(ret)
}
Err(e) if e.is_not_found() => {
// Detect broken symlinks and replace them with explicit entries
if let (Some(path), Some(depth)) = (e.path(), e.depth()) {
if let Ok(meta) = path.symlink_metadata() {
return Ok(Self {
inner: Entry::Explicit(path.into(), depth),
follow: Follow::Never,
meta: Ok(meta).into(),
});
}
}
Err(e)
}
Err(e) => Err(e),
}
}
/// Get the path to this entry.
pub fn path(&self) -> &Path {
match &self.inner {
Entry::Explicit(path, _) => path.as_path(),
Entry::WalkDir(ent) => ent.path(),
}
}
/// Get the path to this entry.
pub fn into_path(self) -> PathBuf {
match self.inner {
Entry::Explicit(path, _) => path,
Entry::WalkDir(ent) => ent.into_path(),
}
}
/// Get the name of this entry.
pub fn file_name(&self) -> &OsStr {
match &self.inner {
Entry::Explicit(path, _) => {
// Path::file_name() only works if the last component is normal
path.components()
.next_back()
.map(|c| c.as_os_str())
.unwrap_or_else(|| path.as_os_str())
}
Entry::WalkDir(ent) => ent.file_name(),
}
}
/// Get the depth of this entry below the root.
pub fn depth(&self) -> usize {
match &self.inner {
Entry::Explicit(_, depth) => *depth,
Entry::WalkDir(ent) => ent.depth(),
}
}
/// Get whether symbolic links are followed for this entry.
pub fn follow(&self) -> bool {
self.follow.follow_at_depth(self.depth())
}
/// Get the metadata on a cache miss.
fn get_metadata(&self) -> Result<Metadata, WalkError> {
self.follow.metadata_at_depth(self.path(), self.depth())
}
/// Get the [Metadata] for this entry, following symbolic links if appropriate.
/// Multiple calls to this function will cache and re-use the same [Metadata].
pub fn metadata(&self) -> Result<&Metadata, WalkError> {
let result = self.meta.get_or_init(|| match &self.inner {
Entry::Explicit(_, _) => Ok(self.get_metadata()?),
Entry::WalkDir(ent) => Ok(ent.metadata()?),
});
result.as_ref().map_err(|e| e.clone())
}
/// Get the file type of this entry.
pub fn file_type(&self) -> FileType {
match &self.inner {
Entry::Explicit(_, _) => self
.metadata()
.map(|m| m.file_type().into())
.unwrap_or(FileType::Unknown),
Entry::WalkDir(ent) => ent.file_type().into(),
}
}
/// Check whether this entry is a symbolic link, regardless of whether links
/// are being followed.
pub fn path_is_symlink(&self) -> bool {
match &self.inner {
Entry::Explicit(path, _) => {
if self.follow() {
path.symlink_metadata()
.is_ok_and(|m| m.file_type().is_symlink())
} else {
self.file_type().is_symlink()
}
}
Entry::WalkDir(ent) => ent.path_is_symlink(),
}
}
}
+123 -4
View File
@@ -4,14 +4,14 @@
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
use std::cell::RefCell;
use std::error::Error;
use std::ffi::OsString;
use std::io::{stderr, Write};
use std::path::Path;
use std::process::Command;
use walkdir::DirEntry;
use super::{Matcher, MatcherIO};
use super::{Matcher, MatcherIO, WalkEntry};
enum Arg {
FileArg(Vec<OsString>),
@@ -52,7 +52,7 @@ impl SingleExecMatcher {
}
impl Matcher for SingleExecMatcher {
fn matches(&self, file_info: &DirEntry, _: &mut MatcherIO) -> bool {
fn matches(&self, file_info: &WalkEntry, _: &mut MatcherIO) -> bool {
let mut command = Command::new(&self.executable);
let path_to_file = if self.exec_in_parent_dir {
if let Some(f) = file_info.path().file_name() {
@@ -67,7 +67,7 @@ impl Matcher for SingleExecMatcher {
for arg in &self.args {
match *arg {
Arg::LiteralArg(ref a) => command.arg(a.as_os_str()),
Arg::FileArg(ref parts) => command.arg(&parts.join(path_to_file.as_os_str())),
Arg::FileArg(ref parts) => command.arg(parts.join(path_to_file.as_os_str())),
};
}
if self.exec_in_parent_dir {
@@ -98,6 +98,125 @@ impl Matcher for SingleExecMatcher {
}
}
pub struct MultiExecMatcher {
executable: String,
args: Vec<OsString>,
exec_in_parent_dir: bool,
/// Command to build while matching.
command: RefCell<Option<argmax::Command>>,
}
impl MultiExecMatcher {
pub fn new(
executable: &str,
args: &[&str],
exec_in_parent_dir: bool,
) -> Result<Self, Box<dyn Error>> {
let transformed_args = args.iter().map(OsString::from).collect();
Ok(Self {
executable: executable.to_string(),
args: transformed_args,
exec_in_parent_dir,
command: RefCell::new(None),
})
}
fn new_command(&self) -> argmax::Command {
let mut command = argmax::Command::new(&self.executable);
command.try_args(&self.args).unwrap();
command
}
fn run_command(&self, command: &mut argmax::Command, matcher_io: &mut MatcherIO) {
match command.status() {
Ok(status) => {
if !status.success() {
matcher_io.set_exit_code(1);
}
}
Err(e) => {
writeln!(&mut stderr(), "Failed to run {}: {}", self.executable, e).unwrap();
matcher_io.set_exit_code(1);
}
}
}
}
impl Matcher for MultiExecMatcher {
fn matches(&self, file_info: &WalkEntry, matcher_io: &mut MatcherIO) -> bool {
let path_to_file = if self.exec_in_parent_dir {
if let Some(f) = file_info.path().file_name() {
Path::new(".").join(f)
} else {
Path::new(".").join(file_info.path())
}
} else {
file_info.path().to_path_buf()
};
let mut command = self.command.borrow_mut();
let command = command.get_or_insert_with(|| self.new_command());
// Build command, or dispatch it before when it is long enough.
if command.try_arg(&path_to_file).is_err() {
if self.exec_in_parent_dir {
match file_info.path().parent() {
None => {
// Root paths like "/" have no parent. Run them from the root to match GNU find.
command.current_dir(file_info.path());
}
Some(parent) if parent == Path::new("") => {
// Paths like "foo" have a parent of "". Avoid chdir("").
}
Some(parent) => {
command.current_dir(parent);
}
}
}
self.run_command(command, matcher_io);
// Reset command status.
*command = self.new_command();
if let Err(e) = command.try_arg(&path_to_file) {
writeln!(
&mut stderr(),
"Cannot fit a single argument {}: {}",
&path_to_file.to_string_lossy(),
e
)
.unwrap();
matcher_io.set_exit_code(1);
}
}
true
}
fn finished_dir(&self, dir: &Path, matcher_io: &mut MatcherIO) {
// Dispatch command for -execdir.
if self.exec_in_parent_dir {
let mut command = self.command.borrow_mut();
if let Some(mut command) = command.take() {
command.current_dir(Path::new(".").join(dir));
self.run_command(&mut command, matcher_io);
}
}
}
fn finished(&self, matcher_io: &mut MatcherIO) {
// Dispatch command for -exec.
if !self.exec_in_parent_dir {
let mut command = self.command.borrow_mut();
if let Some(mut command) = command.take() {
self.run_command(&mut command, matcher_io);
}
}
}
fn has_side_effects(&self) -> bool {
true
}
}
#[cfg(test)]
/// No tests here, because we need to call out to an external executable. See
/// `tests/exec_unit_tests.rs` instead.
+91 -35
View File
@@ -2,19 +2,21 @@
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
use super::{Matcher, MatcherIO, WalkEntry};
#[cfg(unix)]
use uucore::error::UResult;
use std::path::Path;
use std::{
error::Error,
io::{stderr, Write},
};
use super::Matcher;
/// The latest mapping from dev_id to fs_type, used for saving mount info reads
#[cfg(unix)]
pub struct Cache {
dev_id: String,
fs_type: String,
}
/// Get the filesystem type of a file.
/// 1. get the metadata of the file
/// 2. get the device ID of the metadata
/// 3. search the filesystem list
/// 3. search the cache, then the filesystem list
///
/// Returns an empty string when no file system list matches.
///
@@ -24,23 +26,42 @@ use super::Matcher;
///
/// This is only supported on Unix.
#[cfg(unix)]
pub fn get_file_system_type(path: &Path) -> Result<String, Box<dyn Error>> {
use std::{
cell::RefCell,
io::{stderr, Write},
path::Path,
};
#[cfg(unix)]
pub fn get_file_system_type(path: &Path, cache: &RefCell<Option<Cache>>) -> UResult<String> {
use std::os::unix::fs::MetadataExt;
let metadata = match path.metadata() {
// use symlink_metadata (lstat under the hood) instead of metadata (stat) to make sure that it
// does not return an error when there is a (broken) symlink; this is aligned with GNU find.
let metadata = match path.symlink_metadata() {
Ok(metadata) => metadata,
Err(err) => Err(err)?,
};
let dev_id = metadata.dev().to_string();
let fs_list = match uucore::fsext::read_fs_list() {
Ok(fs_list) => fs_list,
Err(err) => Err(err)?,
};
if let Some(cache) = cache.borrow().as_ref() {
if cache.dev_id == dev_id {
return Ok(cache.fs_type.clone());
}
}
let fs_list = uucore::fsext::read_fs_list()?;
let result = fs_list
.into_iter()
.find(|fs| fs.dev_id == dev_id)
.map_or_else(String::new, |fs| fs.fs_type);
// cache the latest query if not a match before
cache.replace(Some(Cache {
dev_id,
fs_type: result.clone(),
}));
Ok(result)
}
@@ -49,38 +70,49 @@ pub fn get_file_system_type(path: &Path) -> Result<String, Box<dyn Error>> {
///
/// This is only supported on Unix.
pub struct FileSystemMatcher {
#[cfg(unix)]
fs_text: String,
#[cfg(unix)]
cache: RefCell<Option<Cache>>,
}
impl FileSystemMatcher {
#[cfg(unix)]
pub fn new(fs_text: String) -> Self {
Self { fs_text }
Self {
fs_text,
cache: RefCell::new(None),
}
}
#[cfg(not(unix))]
pub fn new(_fs_text: String) -> Self {
Self {}
}
}
impl Matcher for FileSystemMatcher {
fn matches(&self, file_info: &walkdir::DirEntry, _: &mut super::MatcherIO) -> bool {
#[cfg(not(unix))]
{
false
}
#[cfg(unix)]
{
match get_file_system_type(file_info.path()) {
Ok(result) => result == self.fs_text,
Err(_) => {
writeln!(
&mut stderr(),
"Error getting filesystem type for {}",
file_info.path().to_string_lossy()
)
.unwrap();
#[cfg(unix)]
fn matches(&self, file_info: &WalkEntry, _: &mut MatcherIO) -> bool {
match get_file_system_type(file_info.path(), &self.cache) {
Ok(result) => result == self.fs_text,
Err(_) => {
writeln!(
&mut stderr(),
"Error getting filesystem type for {}",
file_info.path().to_string_lossy()
)
.unwrap();
false
}
false
}
}
}
#[cfg(not(unix))]
fn matches(&self, _file_info: &WalkEntry, _: &mut MatcherIO) -> bool {
false
}
}
#[cfg(test)]
@@ -89,9 +121,14 @@ mod tests {
#[cfg(unix)]
fn test_fs_matcher() {
use crate::find::{
matchers::{fs::get_file_system_type, tests::get_dir_entry_for, Matcher},
matchers::{
fs::{get_file_system_type, Cache},
tests::get_dir_entry_for,
Matcher,
},
tests::FakeDependencies,
};
use std::cell::RefCell;
use std::fs::File;
use tempfile::Builder;
@@ -105,7 +142,26 @@ mod tests {
let _ = File::create(foo_path).expect("create temp file");
let file_info = get_dir_entry_for(&temp_dir.path().to_string_lossy(), "foo");
let target_fs_type = get_file_system_type(file_info.path()).unwrap();
// create an empty cache for initial fs type lookup
let empty_cache = RefCell::new(None);
let target_fs_type = get_file_system_type(file_info.path(), &empty_cache).unwrap();
// should work with unmatched cache, and the cache should be set to the last query result
let unmatched_cache = RefCell::new(Some(Cache {
dev_id: "foo".to_string(),
fs_type: "bar".to_string(),
}));
let target_fs_type_unmatched_cache =
get_file_system_type(file_info.path(), &unmatched_cache).unwrap();
assert_eq!(
target_fs_type, target_fs_type_unmatched_cache,
"get_file_system_type should return correct result with unmatched cache"
);
assert_eq!(
unmatched_cache.borrow().as_ref().unwrap().fs_type,
target_fs_type,
"get_file_system_type should set the cache to the last query result"
);
// should match fs type
let matcher = super::FileSystemMatcher::new(target_fs_type.clone());
+31 -21
View File
@@ -112,7 +112,7 @@ fn extract_bracket_expr(pattern: &str) -> Option<(String, &str)> {
}
/// Converts a POSIX glob into a POSIX Basic Regular Expression
fn glob_to_regex(pattern: &str) -> String {
fn glob_to_regex(pattern: &str) -> Option<String> {
let mut regex = String::new();
let mut chars = pattern.chars();
@@ -130,9 +130,9 @@ fn glob_to_regex(pattern: &str) -> String {
// If pattern ends with an unescaped <backslash>, fnmatch() shall return a
// non-zero value (indicating either no match or an error).
//
// Most implementations return FNM_NOMATCH in this case, so return a regex that
// Most implementations return FNM_NOMATCH in this case, so create a pattern that
// never matches.
return "$.".to_string();
return None;
}
}
'[' => {
@@ -147,12 +147,12 @@ fn glob_to_regex(pattern: &str) -> String {
}
}
regex
Some(regex)
}
/// An fnmatch()-style glob matcher.
pub struct Pattern {
regex: Regex,
regex: Option<Regex>,
}
impl Pattern {
@@ -165,13 +165,13 @@ impl Pattern {
};
// As long as glob_to_regex() is correct, this should never fail
let regex = parse_bre(&glob_to_regex(pattern), options).unwrap();
let regex = glob_to_regex(pattern).map(|r| parse_bre(&r, options).unwrap());
Self { regex }
}
/// Test if this pattern matches a string.
pub fn matches(&self, string: &str) -> bool {
self.regex.is_match(string)
self.regex.as_ref().is_some_and(|r| r.is_match(string))
}
}
@@ -179,47 +179,52 @@ impl Pattern {
mod tests {
use super::*;
#[track_caller]
fn assert_glob_regex(glob: &str, regex: &str) {
assert_eq!(glob_to_regex(glob).as_deref(), Some(regex));
}
#[test]
fn literals() {
assert_eq!(glob_to_regex(r"foo.bar"), r"foo\.bar");
assert_glob_regex(r"foo.bar", r"foo\.bar");
}
#[test]
fn regex_special() {
assert_eq!(glob_to_regex(r"^foo.bar$"), r"\^foo\.bar\$");
assert_glob_regex(r"^foo.bar$", r"\^foo\.bar\$");
}
#[test]
fn wildcards() {
assert_eq!(glob_to_regex(r"foo?bar*baz"), r"foo.bar.*baz");
assert_glob_regex(r"foo?bar*baz", r"foo.bar.*baz");
}
#[test]
fn escapes() {
assert_eq!(glob_to_regex(r"fo\o\?bar\*baz\\"), r"foo?bar\*baz\\");
}
#[test]
fn incomplete_escape() {
assert_eq!(glob_to_regex(r"foo\"), r"$.");
assert_glob_regex(r"fo\o\?bar\*baz\\", r"foo?bar\*baz\\");
}
#[test]
fn valid_brackets() {
assert_eq!(glob_to_regex(r"foo[bar][!baz]"), r"foo[bar][^baz]");
assert_glob_regex(r"foo[bar][!baz]", r"foo[bar][^baz]");
}
#[test]
fn complex_brackets() {
assert_eq!(
glob_to_regex(r"[!]!.*[\[.].][=]=][:space:]-]"),
r"[^]!.*[\[.].][=]=][:space:]-]"
assert_glob_regex(
r"[!]!.*[\[.].][=]=][:space:]-]",
r"[^]!.*[\[.].][=]=][:space:]-]",
);
}
#[test]
fn invalid_brackets() {
assert_eq!(glob_to_regex(r"foo[bar[!baz"), r"foo\[bar\[!baz");
assert_glob_regex(r"foo[bar[!baz", r"foo\[bar\[!baz");
}
#[test]
fn incomplete_escape() {
assert_eq!(glob_to_regex(r"foo\"), None);
}
#[test]
@@ -235,4 +240,9 @@ mod tests {
assert!(!Pattern::new(r"foo*BAR", true).matches("BAR--foo"));
}
#[test]
fn incomplete_escape_matches() {
assert!(!Pattern::new(r"foo\", false).matches("\n"));
}
}
+27 -55
View File
@@ -3,7 +3,7 @@
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
use super::Matcher;
use super::{ComparableValue, Matcher, MatcherIO, WalkEntry};
#[cfg(unix)]
use nix::unistd::Group;
@@ -11,67 +11,43 @@ use nix::unistd::Group;
use std::os::unix::fs::MetadataExt;
pub struct GroupMatcher {
gid: Option<u32>,
gid: ComparableValue,
}
impl GroupMatcher {
#[cfg(unix)]
pub fn from_group_name(group: &str) -> GroupMatcher {
pub fn from_group_name(group: &str) -> Option<Self> {
// get gid from group name
let Ok(group) = Group::from_name(group) else {
return GroupMatcher { gid: None };
};
let Some(group) = group else {
// This if branch is to determine whether a certain group exists in the system.
// If a certain group does not exist in the system,
// the result will need to be returned according to
// the flag bit of whether to invert the result.
return GroupMatcher { gid: None };
};
GroupMatcher {
gid: Some(group.gid.as_raw()),
}
let group = Group::from_name(group).ok()??;
let gid = group.gid.as_raw();
Some(Self::from_gid(gid))
}
#[cfg(unix)]
pub fn from_gid(gid: u32) -> GroupMatcher {
GroupMatcher { gid: Some(gid) }
pub fn from_gid(gid: u32) -> Self {
Self::from_comparable(ComparableValue::EqualTo(gid as u64))
}
pub fn from_comparable(gid: ComparableValue) -> Self {
Self { gid }
}
#[cfg(windows)]
pub fn from_group_name(_group: &str) -> GroupMatcher {
GroupMatcher { gid: None }
}
#[cfg(windows)]
pub fn from_gid(_gid: u32) -> GroupMatcher {
GroupMatcher { gid: None }
}
pub fn gid(&self) -> &Option<u32> {
&self.gid
pub fn from_group_name(_group: &str) -> Option<Self> {
None
}
}
impl Matcher for GroupMatcher {
#[cfg(unix)]
fn matches(&self, file_info: &walkdir::DirEntry, _: &mut super::MatcherIO) -> bool {
let Ok(metadata) = file_info.path().metadata() else {
return false;
};
let file_gid = metadata.gid();
// When matching the -group parameter in find/matcher/mod.rs,
// it has been judged that the group does not exist and an error is returned.
// So use unwarp() directly here.
self.gid.unwrap() == file_gid
fn matches(&self, file_info: &WalkEntry, _: &mut MatcherIO) -> bool {
match file_info.metadata() {
Ok(metadata) => self.gid.matches(metadata.gid().into()),
Err(_) => false,
}
}
#[cfg(windows)]
fn matches(&self, _file_info: &walkdir::DirEntry, _: &mut super::MatcherIO) -> bool {
fn matches(&self, _file_info: &WalkEntry, _: &mut MatcherIO) -> bool {
// The user group acquisition function for Windows systems is not implemented in MetadataExt,
// so it is somewhat difficult to implement it. :(
false
@@ -82,14 +58,14 @@ pub struct NoGroupMatcher {}
impl Matcher for NoGroupMatcher {
#[cfg(unix)]
fn matches(&self, file_info: &walkdir::DirEntry, _: &mut super::MatcherIO) -> bool {
fn matches(&self, file_info: &WalkEntry, _: &mut MatcherIO) -> bool {
use nix::unistd::Gid;
if file_info.path().is_symlink() {
return false;
}
let Ok(metadata) = file_info.path().metadata() else {
let Ok(metadata) = file_info.metadata() else {
return true;
};
@@ -105,7 +81,7 @@ impl Matcher for NoGroupMatcher {
}
#[cfg(windows)]
fn matches(&self, _file_info: &walkdir::DirEntry, _: &mut super::MatcherIO) -> bool {
fn matches(&self, _file_info: &WalkEntry, _: &mut MatcherIO) -> bool {
false
}
}
@@ -130,13 +106,14 @@ mod tests {
let foo_path = temp_dir.path().join("foo");
let _ = File::create(foo_path).expect("create temp file");
let file_info = get_dir_entry_for(&temp_dir.path().to_string_lossy(), "foo");
let file_gid = file_info.path().metadata().unwrap().gid();
let file_gid = file_info.metadata().unwrap().gid();
let file_group = Group::from_gid(Gid::from_raw(file_gid))
.unwrap()
.unwrap()
.name;
let matcher = super::GroupMatcher::from_group_name(file_group.as_str());
let matcher =
super::GroupMatcher::from_group_name(file_group.as_str()).expect("group should exist");
assert!(
matcher.matches(&file_info, &mut matcher_io),
"group should match"
@@ -146,18 +123,13 @@ mod tests {
let time_string = Local::now().format("%Y%m%d%H%M%S").to_string();
let matcher = GroupMatcher::from_group_name(time_string.as_str());
assert!(
matcher.gid().is_none(),
matcher.is_none(),
"group name {} should not exist",
time_string
);
// Testing group id
let matcher = GroupMatcher::from_gid(file_gid);
assert!(
matcher.gid().is_some(),
"group id {} should exist",
file_gid
);
assert!(
matcher.matches(&file_info, &mut matcher_io),
"group id should match"
+4 -6
View File
@@ -7,12 +7,10 @@
use std::io::{stderr, Write};
use std::path::PathBuf;
use walkdir::DirEntry;
use super::glob::Pattern;
use super::{Matcher, MatcherIO};
use super::{Matcher, MatcherIO, WalkEntry};
fn read_link_target(file_info: &DirEntry) -> Option<PathBuf> {
fn read_link_target(file_info: &WalkEntry) -> Option<PathBuf> {
match file_info.path().read_link() {
Ok(target) => Some(target),
Err(err) => {
@@ -40,14 +38,14 @@ pub struct LinkNameMatcher {
}
impl LinkNameMatcher {
pub fn new(pattern_string: &str, caseless: bool) -> LinkNameMatcher {
pub fn new(pattern_string: &str, caseless: bool) -> Self {
let pattern = Pattern::new(pattern_string, caseless);
Self { pattern }
}
}
impl Matcher for LinkNameMatcher {
fn matches(&self, file_info: &DirEntry, _: &mut MatcherIO) -> bool {
fn matches(&self, file_info: &WalkEntry, _: &mut MatcherIO) -> bool {
if let Some(target) = read_link_target(file_info) {
self.pattern.matches(&target.to_string_lossy())
} else {
+25 -27
View File
@@ -11,9 +11,8 @@
//! to "-foo -o ( -bar -baz )", not "( -foo -o -bar ) -baz").
use std::error::Error;
use std::path::Path;
use walkdir::DirEntry;
use super::{Matcher, MatcherIO};
use super::{Matcher, MatcherIO, WalkEntry};
/// This matcher contains a collection of other matchers. A file only matches
/// if it matches ALL the contained sub-matchers. For sub-matchers that have
@@ -33,7 +32,7 @@ impl Matcher for AndMatcher {
/// Returns true if all sub-matchers return true. Short-circuiting does take
/// place. If the nth sub-matcher returns false, then we immediately return
/// and don't make any further calls.
fn matches(&self, dir_entry: &DirEntry, matcher_io: &mut MatcherIO) -> bool {
fn matches(&self, dir_entry: &WalkEntry, matcher_io: &mut MatcherIO) -> bool {
for matcher in &self.submatchers {
if !matcher.matches(dir_entry, matcher_io) {
return false;
@@ -52,15 +51,15 @@ impl Matcher for AndMatcher {
.any(super::Matcher::has_side_effects)
}
fn finished_dir(&self, dir: &Path) {
fn finished_dir(&self, dir: &Path, matcher_io: &mut MatcherIO) {
for m in &self.submatchers {
m.finished_dir(dir);
m.finished_dir(dir, matcher_io);
}
}
fn finished(&self) {
fn finished(&self, matcher_io: &mut MatcherIO) {
for m in &self.submatchers {
m.finished();
m.finished(matcher_io);
}
}
}
@@ -109,7 +108,7 @@ impl Matcher for OrMatcher {
/// Returns true if any sub-matcher returns true. Short-circuiting does take
/// place. If the nth sub-matcher returns true, then we immediately return
/// and don't make any further calls.
fn matches(&self, dir_entry: &DirEntry, matcher_io: &mut MatcherIO) -> bool {
fn matches(&self, dir_entry: &WalkEntry, matcher_io: &mut MatcherIO) -> bool {
for matcher in &self.submatchers {
if matcher.matches(dir_entry, matcher_io) {
return true;
@@ -128,15 +127,15 @@ impl Matcher for OrMatcher {
.any(super::Matcher::has_side_effects)
}
fn finished_dir(&self, dir: &Path) {
fn finished_dir(&self, dir: &Path, matcher_io: &mut MatcherIO) {
for m in &self.submatchers {
m.finished_dir(dir);
m.finished_dir(dir, matcher_io);
}
}
fn finished(&self) {
fn finished(&self, matcher_io: &mut MatcherIO) {
for m in &self.submatchers {
m.finished();
m.finished(matcher_io);
}
}
}
@@ -206,7 +205,7 @@ impl ListMatcher {
impl Matcher for ListMatcher {
/// Calls matches on all submatcher objects, with no short-circuiting.
/// Returns the result of the call to the final submatcher
fn matches(&self, dir_entry: &DirEntry, matcher_io: &mut MatcherIO) -> bool {
fn matches(&self, dir_entry: &WalkEntry, matcher_io: &mut MatcherIO) -> bool {
let mut rc = false;
for matcher in &self.submatchers {
rc = matcher.matches(dir_entry, matcher_io);
@@ -223,15 +222,15 @@ impl Matcher for ListMatcher {
.any(super::Matcher::has_side_effects)
}
fn finished_dir(&self, dir: &Path) {
fn finished_dir(&self, dir: &Path, matcher_io: &mut MatcherIO) {
for m in &self.submatchers {
m.finished_dir(dir);
m.finished_dir(dir, matcher_io);
}
}
fn finished(&self) {
fn finished(&self, matcher_io: &mut MatcherIO) {
for m in &self.submatchers {
m.finished();
m.finished(matcher_io);
}
}
}
@@ -311,7 +310,7 @@ impl ListMatcherBuilder {
pub struct TrueMatcher;
impl Matcher for TrueMatcher {
fn matches(&self, _dir_entry: &DirEntry, _: &mut MatcherIO) -> bool {
fn matches(&self, _dir_entry: &WalkEntry, _: &mut MatcherIO) -> bool {
true
}
}
@@ -320,7 +319,7 @@ impl Matcher for TrueMatcher {
pub struct FalseMatcher;
impl Matcher for FalseMatcher {
fn matches(&self, _dir_entry: &DirEntry, _: &mut MatcherIO) -> bool {
fn matches(&self, _dir_entry: &WalkEntry, _: &mut MatcherIO) -> bool {
false
}
}
@@ -339,7 +338,7 @@ impl NotMatcher {
}
impl Matcher for NotMatcher {
fn matches(&self, dir_entry: &DirEntry, matcher_io: &mut MatcherIO) -> bool {
fn matches(&self, dir_entry: &WalkEntry, matcher_io: &mut MatcherIO) -> bool {
!self.submatcher.matches(dir_entry, matcher_io)
}
@@ -347,17 +346,16 @@ impl Matcher for NotMatcher {
self.submatcher.has_side_effects()
}
fn finished_dir(&self, dir: &Path) {
self.submatcher.finished_dir(dir);
fn finished_dir(&self, dir: &Path, matcher_io: &mut MatcherIO) {
self.submatcher.finished_dir(dir, matcher_io);
}
fn finished(&self) {
self.submatcher.finished();
fn finished(&self, matcher_io: &mut MatcherIO) {
self.submatcher.finished(matcher_io);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::find::matchers::quit::QuitMatcher;
@@ -370,7 +368,7 @@ mod tests {
pub struct HasSideEffects;
impl Matcher for HasSideEffects {
fn matches(&self, _: &DirEntry, _: &mut MatcherIO) -> bool {
fn matches(&self, _: &WalkEntry, _: &mut MatcherIO) -> bool {
false
}
@@ -383,7 +381,7 @@ mod tests {
struct Counter(Rc<RefCell<u32>>);
impl Matcher for Counter {
fn matches(&self, _: &DirEntry, _: &mut MatcherIO) -> bool {
fn matches(&self, _: &WalkEntry, _: &mut MatcherIO) -> bool {
*self.0.borrow_mut() += 1;
true
}
+307
View File
@@ -0,0 +1,307 @@
// This file is part of the uutils findutils package.
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
use chrono::DateTime;
use std::{
fs::File,
io::{stderr, Write},
};
use super::{Matcher, MatcherIO, WalkEntry};
#[cfg(unix)]
fn format_permissions(mode: uucore::libc::mode_t) -> String {
let file_type = match mode & (uucore::libc::S_IFMT as uucore::libc::mode_t) {
uucore::libc::S_IFDIR => "d",
uucore::libc::S_IFREG => "-",
_ => "?",
};
// S_$$USR means "user permissions"
let user_perms = format!(
"{}{}{}",
if mode & uucore::libc::S_IRUSR != 0 {
"r"
} else {
"-"
},
if mode & uucore::libc::S_IWUSR != 0 {
"w"
} else {
"-"
},
if mode & uucore::libc::S_IXUSR != 0 {
"x"
} else {
"-"
}
);
// S_$$GRP means "group permissions"
let group_perms = format!(
"{}{}{}",
if mode & uucore::libc::S_IRGRP != 0 {
"r"
} else {
"-"
},
if mode & uucore::libc::S_IWGRP != 0 {
"w"
} else {
"-"
},
if mode & uucore::libc::S_IXGRP != 0 {
"x"
} else {
"-"
}
);
// S_$$OTH means "other permissions"
let other_perms = format!(
"{}{}{}",
if mode & uucore::libc::S_IROTH != 0 {
"r"
} else {
"-"
},
if mode & uucore::libc::S_IWOTH != 0 {
"w"
} else {
"-"
},
if mode & uucore::libc::S_IXOTH != 0 {
"x"
} else {
"-"
}
);
format!("{}{}{}{}", file_type, user_perms, group_perms, other_perms)
}
#[cfg(windows)]
fn format_permissions(file_attributes: u32) -> String {
let mut attributes = Vec::new();
// https://learn.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants
if file_attributes & 0x0001 != 0 {
attributes.push("read-only");
}
if file_attributes & 0x0002 != 0 {
attributes.push("hidden");
}
if file_attributes & 0x0004 != 0 {
attributes.push("system");
}
if file_attributes & 0x0020 != 0 {
attributes.push("archive");
}
if file_attributes & 0x0040 != 0 {
attributes.push("compressed");
}
if file_attributes & 0x0080 != 0 {
attributes.push("offline");
}
attributes.join(", ")
}
pub struct Ls {
output_file: Option<File>,
}
impl Ls {
pub fn new(output_file: Option<File>) -> Self {
Self { output_file }
}
#[cfg(unix)]
fn print(
&self,
file_info: &WalkEntry,
matcher_io: &mut MatcherIO,
mut out: impl Write,
print_error_message: bool,
) {
use nix::unistd::{Gid, Group, Uid, User};
use std::os::unix::fs::{MetadataExt, PermissionsExt};
let metadata = file_info.metadata().unwrap();
let inode_number = metadata.ino();
let number_of_blocks = {
let size = metadata.size();
let number_of_blocks = size / 1024;
let remainder = number_of_blocks % 4;
if remainder == 0 {
if number_of_blocks == 0 {
4
} else {
number_of_blocks
}
} else {
number_of_blocks + (4 - (remainder))
}
};
let permission =
{ format_permissions(metadata.permissions().mode() as uucore::libc::mode_t) };
let hard_links = metadata.nlink();
let user = {
let uid = metadata.uid();
User::from_uid(Uid::from_raw(uid)).unwrap().unwrap().name
};
let group = {
let gid = metadata.gid();
Group::from_gid(Gid::from_raw(gid)).unwrap().unwrap().name
};
let size = metadata.size();
let last_modified = {
let system_time = metadata.modified().unwrap();
let now_utc: DateTime<chrono::Utc> = system_time.into();
now_utc.format("%b %e %H:%M")
};
let path = file_info.path().to_string_lossy();
match writeln!(
out,
" {:<4} {:>6} {:<10} {:>3} {:<8} {:<8} {:>8} {} {}",
inode_number,
number_of_blocks,
permission,
hard_links,
user,
group,
size,
last_modified,
path,
) {
Ok(_) => {}
Err(e) => {
if print_error_message {
writeln!(
&mut stderr(),
"Error writing {:?} for {}",
file_info.path().to_string_lossy(),
e
)
.unwrap();
matcher_io.set_exit_code(1);
}
}
}
}
#[cfg(windows)]
fn print(
&self,
file_info: &WalkEntry,
matcher_io: &mut MatcherIO,
mut out: impl Write,
print_error_message: bool,
) {
use std::os::windows::fs::MetadataExt;
let metadata = file_info.metadata().unwrap();
let inode_number = 0;
let number_of_blocks = {
let size = metadata.file_size();
let number_of_blocks = size / 1024;
let remainder = number_of_blocks % 4;
if remainder == 0 {
if number_of_blocks == 0 {
4
} else {
number_of_blocks
}
} else {
number_of_blocks + (4 - (remainder))
}
};
let permission = { format_permissions(metadata.file_attributes()) };
let hard_links = 0;
let user = 0;
let group = 0;
let size = metadata.file_size();
let last_modified = {
let system_time = metadata.modified().unwrap();
let now_utc: DateTime<chrono::Utc> = system_time.into();
now_utc.format("%b %e %H:%M")
};
let path = file_info.path().to_string_lossy();
match write!(
out,
" {:<4} {:>6} {:<10} {:>3} {:<8} {:<8} {:>8} {} {}\n",
inode_number,
number_of_blocks,
permission,
hard_links,
user,
group,
size,
last_modified,
path,
) {
Ok(_) => {}
Err(e) => {
if print_error_message {
writeln!(
&mut stderr(),
"Error writing {:?} for {}",
file_info.path().to_string_lossy(),
e
)
.unwrap();
matcher_io.set_exit_code(1);
}
}
}
}
}
impl Matcher for Ls {
fn matches(&self, file_info: &WalkEntry, matcher_io: &mut MatcherIO) -> bool {
if let Some(file) = &self.output_file {
self.print(file_info, matcher_io, file, true);
} else {
self.print(
file_info,
matcher_io,
&mut *matcher_io.deps.get_output().borrow_mut(),
false,
);
}
true
}
fn has_side_effects(&self) -> bool {
true
}
}
#[cfg(test)]
mod tests {
#[test]
#[cfg(unix)]
fn test_format_permissions() {
use super::format_permissions;
let mode: uucore::libc::mode_t = 0o100644;
let expected = "-rw-r--r--";
assert_eq!(format_permissions(mode), expected);
let mode: uucore::libc::mode_t = 0o040755;
let expected = "drwxr-xr-x";
assert_eq!(format_permissions(mode), expected);
let mode: uucore::libc::mode_t = 0o100777;
let expected = "-rwxrwxrwx";
assert_eq!(format_permissions(mode), expected);
}
}
+472 -126
View File
File diff suppressed because it is too large Load Diff
+29 -5
View File
@@ -4,10 +4,8 @@
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
use walkdir::DirEntry;
use super::glob::Pattern;
use super::{Matcher, MatcherIO};
use super::{Matcher, MatcherIO, WalkEntry};
/// This matcher makes a comparison of the name against a shell wildcard
/// pattern. See `glob::Pattern` for details on the exact syntax.
@@ -23,8 +21,17 @@ impl NameMatcher {
}
impl Matcher for NameMatcher {
fn matches(&self, file_info: &DirEntry, _: &mut MatcherIO) -> bool {
fn matches(&self, file_info: &WalkEntry, _: &mut MatcherIO) -> bool {
let name = file_info.file_name().to_string_lossy();
#[cfg(unix)]
if name.len() > 1 && name.chars().all(|x| x == '/') {
self.pattern.matches("/")
} else {
self.pattern.matches(&name)
}
#[cfg(windows)]
self.pattern.matches(&name)
}
}
@@ -34,7 +41,6 @@ mod tests {
use super::*;
use crate::find::matchers::tests::get_dir_entry_for;
use crate::find::tests::FakeDependencies;
use std::io::ErrorKind;
#[cfg(unix)]
@@ -128,4 +134,22 @@ mod tests {
let deps = FakeDependencies::new();
assert!(matcher.matches(&link_f, &mut deps.new_matcher_io()));
}
#[test]
#[cfg(unix)]
fn slash_match_returns_true() {
let dir_to_match = get_dir_entry_for("///", "");
let matcher = NameMatcher::new("/", true);
let deps = FakeDependencies::new();
assert!(matcher.matches(&dir_to_match, &mut deps.new_matcher_io()));
}
#[test]
#[cfg(unix)]
fn only_one_slash() {
let dir_to_match = get_dir_entry_for("/", "");
let matcher = NameMatcher::new("/", false);
let deps = FakeDependencies::new();
assert!(matcher.matches(&dir_to_match, &mut deps.new_matcher_io()));
}
}
+2 -4
View File
@@ -4,10 +4,8 @@
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
use walkdir::DirEntry;
use super::glob::Pattern;
use super::{Matcher, MatcherIO};
use super::{Matcher, MatcherIO, WalkEntry};
/// This matcher makes a comparison of the path against a shell wildcard
/// pattern. See `glob::Pattern` for details on the exact syntax.
@@ -23,7 +21,7 @@ impl PathMatcher {
}
impl Matcher for PathMatcher {
fn matches(&self, file_info: &DirEntry, _: &mut MatcherIO) -> bool {
fn matches(&self, file_info: &WalkEntry, _: &mut MatcherIO) -> bool {
let path = file_info.path().to_string_lossy();
self.pattern.matches(&path)
}

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