Compare commits

...

87 Commits

Author SHA1 Message Date
Thayne McCombs cdea7f5633 Merge pull request #1943 from tmccombs/archive-script
build: Create archive script
2026-08-27 23:52:23 -06:00
Thayne McCombs fe0e455fee Merge pull request #2102 from tmccombs/changelog-update
chore: Update version in CHANGELOG
2026-08-26 09:54:02 -06:00
Thayne McCombs a89e394a0d chore: Update version in CHANGELOG 2026-08-26 02:12:47 -06:00
Thayne McCombs 4f81778774 Merge pull request #2101 from tmccombs/release-10.5.0
chore: Bump to version 10.5.0
2026-08-26 01:54:19 -06:00
Thayne McCombs 711ba7fe67 chore: Buimp to version 10.5.0 2026-08-26 01:27:33 -06:00
Thayne McCombs c276ac79f4 build: Create archive script
Move the code for creating the archive from the GHA yaml into a
dedicated script file.
2026-08-26 01:00:44 -06:00
Thayne McCombs ee20f426dd Merge pull request #2091 from sharkdp/dependabot/github_actions/actions/attest-4.2.1
build(deps): bump actions/attest from 4.2.0 to 4.2.1
2026-08-09 01:42:46 -06:00
Thayne McCombs 0b10bf2665 Merge pull request #2086 from sharkdp/dependabot/cargo/crossbeam-channel-0.5.16
build(deps): bump crossbeam-channel from 0.5.15 to 0.5.16
2026-08-09 01:37:12 -06:00
Thayne McCombs aaecbc0a58 Merge pull request #2085 from sharkdp/dependabot/cargo/ignore-0.4.31
build(deps): bump ignore from 0.4.26 to 0.4.31
2026-08-09 01:36:38 -06:00
Thayne McCombs cb4069ec55 Merge pull request #2087 from sharkdp/dependabot/cargo/globset-0.4.19
build(deps): bump globset from 0.4.18 to 0.4.19
2026-08-09 01:35:32 -06:00
Thayne McCombs 69a25907ab Merge pull request #2084 from sharkdp/dependabot/cargo/anyhow-1.0.104
build(deps): bump anyhow from 1.0.102 to 1.0.104
2026-08-09 01:35:00 -06:00
Thayne McCombs 868c3a34e2 Merge pull request #2083 from sharkdp/dependabot/cargo/libc-0.2.189
build(deps): bump libc from 0.2.186 to 0.2.189
2026-08-09 01:32:52 -06:00
Thayne McCombs ee195f4dd6 Merge pull request #2082 from nikolauspschuetz/fix-changed-time-overflow-panic
Fix panic on out-of-range '@' Unix timestamp in time filters
2026-08-09 01:32:07 -06:00
Thayne McCombs 0f1f9676dc Merge pull request #2068 from tmccombs/fix-clippy
fix: New clippy warnings
2026-08-06 10:54:59 -06:00
dependabot[bot] 2bb2e2b954 build(deps): bump actions/attest from 4.2.0 to 4.2.1
Bumps [actions/attest](https://github.com/actions/attest) from 4.2.0 to 4.2.1.
- [Release notes](https://github.com/actions/attest/releases)
- [Changelog](https://github.com/actions/attest/blob/main/RELEASE.md)
- [Commits](https://github.com/actions/attest/compare/f7c74d28b9d84cb8768d0b8ca14a4bac6ef463e6...508db95dd578ae2727ebd6217d5ba78e4fbda05d)

---
updated-dependencies:
- dependency-name: actions/attest
  dependency-version: 4.2.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-05 17:43:24 +00:00
dependabot[bot] a62a59dafe build(deps): bump globset from 0.4.18 to 0.4.19
Bumps [globset](https://github.com/BurntSushi/ripgrep) from 0.4.18 to 0.4.19.
- [Release notes](https://github.com/BurntSushi/ripgrep/releases)
- [Changelog](https://github.com/BurntSushi/ripgrep/blob/master/CHANGELOG.md)
- [Commits](https://github.com/BurntSushi/ripgrep/compare/globset-0.4.18...globset-0.4.19)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-01 17:42:57 +00:00
dependabot[bot] 7639bced74 build(deps): bump crossbeam-channel from 0.5.15 to 0.5.16
Bumps [crossbeam-channel](https://github.com/crossbeam-rs/crossbeam) from 0.5.15 to 0.5.16.
- [Release notes](https://github.com/crossbeam-rs/crossbeam/releases)
- [Changelog](https://github.com/crossbeam-rs/crossbeam/blob/main/CHANGELOG.md)
- [Commits](https://github.com/crossbeam-rs/crossbeam/compare/crossbeam-channel-0.5.15...crossbeam-channel-0.5.16)

---
updated-dependencies:
- dependency-name: crossbeam-channel
  dependency-version: 0.5.16
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-01 17:42:50 +00:00
dependabot[bot] 51e44c0626 build(deps): bump ignore from 0.4.26 to 0.4.31
Bumps [ignore](https://github.com/BurntSushi/ripgrep) from 0.4.26 to 0.4.31.
- [Release notes](https://github.com/BurntSushi/ripgrep/releases)
- [Changelog](https://github.com/BurntSushi/ripgrep/blob/master/CHANGELOG.md)
- [Commits](https://github.com/BurntSushi/ripgrep/compare/ignore-0.4.26...ignore-0.4.31)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-01 17:42:41 +00:00
dependabot[bot] 370174528f build(deps): bump anyhow from 1.0.102 to 1.0.104
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.102 to 1.0.104.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.102...1.0.104)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-01 17:42:31 +00:00
dependabot[bot] 30fd75b22b build(deps): bump libc from 0.2.186 to 0.2.189
Bumps [libc](https://github.com/rust-lang/libc) from 0.2.186 to 0.2.189.
- [Release notes](https://github.com/rust-lang/libc/releases)
- [Changelog](https://github.com/rust-lang/libc/blob/0.2.189/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/libc/compare/0.2.186...0.2.189)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-01 17:42:24 +00:00
Nikolaus Schuetz 5becb9d52c Reject out-of-range '@' Unix timestamp instead of panicking
Use UNIX_EPOCH.checked_add(...) so an out-of-range value returns None
and propagates through the existing ? like every other parse failure in
TimeFilter::from_str, rather than panicking via SystemTime's Add. See #2081.
2026-07-30 08:06:22 -07:00
Nikolaus Schuetz fb0426fed1 Add failing test for out-of-range '@' Unix timestamp
Demonstrates that a '@'-timestamp large enough to overflow SystemTime
panics ('overflow when adding duration to instant') instead of being
rejected like other unparseable time inputs. See #2081.
2026-07-30 08:06:07 -07:00
Thayne McCombs 41532d114e Merge pull request #2075 from cmelaro/fix-readme-example
Update README.md to resolve non-working example.
2026-07-27 09:41:31 -06:00
Chris Melaro 81efb5028c add -u flag to example so it works 2026-07-26 14:24:38 -04:00
Thayne McCombs ca51233d27 Merge pull request #2073 from sharkdp/dependabot/github_actions/actions/checkout-7.0.1
build(deps): bump actions/checkout from 7.0.0 to 7.0.1
2026-07-24 14:22:05 -06:00
Thayne McCombs 3381ab89d7 Merge pull request #2070 from sharkdp/dependabot/github_actions/actions/attest-4.2.0
build(deps): bump actions/attest from 4.1.1 to 4.2.0
2026-07-24 14:21:25 -06:00
dependabot[bot] 5095e95a95 build(deps): bump actions/checkout from 7.0.0 to 7.0.1
Bumps [actions/checkout](https://github.com/actions/checkout) from 7.0.0 to 7.0.1.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v7.0.0...v7.0.1)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-24 17:42:24 +00:00
dependabot[bot] 5a093fdfd6 build(deps): bump actions/attest from 4.1.1 to 4.2.0
Bumps [actions/attest](https://github.com/actions/attest) from 4.1.1 to 4.2.0.
- [Release notes](https://github.com/actions/attest/releases)
- [Changelog](https://github.com/actions/attest/blob/main/RELEASE.md)
- [Commits](https://github.com/actions/attest/compare/a1948c3f048ba23858d222213b7c278aabede763...f7c74d28b9d84cb8768d0b8ca14a4bac6ef463e6)

---
updated-dependencies:
- dependency-name: actions/attest
  dependency-version: 4.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-23 17:42:46 +00:00
Thayne McCombs acc64aab12 fix: New clippy warnings 2026-07-23 01:55:04 -06:00
Thayne McCombs eead1886cd Merge pull request #2066 from sharkdp/dependabot/github_actions/softprops/action-gh-release-3.0.2
build(deps): bump softprops/action-gh-release from 3.0.1 to 3.0.2
2026-07-23 01:47:36 -06:00
dependabot[bot] fed92120af build(deps): bump softprops/action-gh-release from 3.0.1 to 3.0.2
Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 3.0.1 to 3.0.2.
- [Release notes](https://github.com/softprops/action-gh-release/releases)
- [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md)
- [Commits](https://github.com/softprops/action-gh-release/compare/718ea10b132b3b2eba29c1007bb80653f286566b...3d0d9888cb7fd7b750713d6e236d1fcb99157228)

---
updated-dependencies:
- dependency-name: softprops/action-gh-release
  dependency-version: 3.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-20 17:42:43 +00:00
Thayne McCombs 1bfeea237a Merge pull request #2063 from kobihikri/docs-msrv
docs: update required Rust version in README to match Cargo.toml MSRV (1.77.2 -> 1.90.0)
2026-07-15 14:22:25 -06:00
Thayne McCombs 369a71a4c5 Merge pull request #2064 from WilliamLeony/fix-readme-bash-fence
Fix inconsistent code fence language in README.md
2026-07-15 14:19:06 -06:00
William Leony cd755e284c Fix inconsistent code fence language in README.md
The checksum example under 'Command execution' was missing the
'bash' language tag on its opening fence, unlike every other shell
example in the file, so it didn't get syntax highlighting.
2026-07-15 16:06:42 -03:00
Kobi Hikri 372584e760 docs: update required Rust version in README (1.77.2 -> 1.90.0, matching Cargo.toml rust-version) 2026-07-15 13:28:38 +03:00
Thayne McCombs 5a5852e15b Merge pull request #2052 from sharkdp/dependabot/github_actions/actions/attest-4.1.1
build(deps): bump actions/attest from 4.1.0 to 4.1.1
2026-07-07 02:00:22 -06:00
Thayne McCombs dda13cccf6 Merge pull request #2047 from sharkdp/dependabot/cargo/nix-0.31.3
build(deps): bump nix from 0.31.2 to 0.31.3
2026-07-06 07:19:29 -06:00
Thayne McCombs aa41f342b5 Merge pull request #2050 from sharkdp/dependabot/cargo/jiff-0.2.29
build(deps): bump jiff from 0.2.27 to 0.2.29
2026-07-04 23:39:02 -06:00
Thayne McCombs cffed27a2e Merge pull request #2049 from sharkdp/dependabot/cargo/regex-1.12.4
build(deps): bump regex from 1.12.3 to 1.12.4
2026-07-04 23:37:52 -06:00
Thayne McCombs 6b5d1ac9d6 Merge pull request #2048 from sharkdp/dependabot/cargo/ignore-0.4.26
build(deps): bump ignore from 0.4.25 to 0.4.26
2026-07-04 23:37:22 -06:00
dependabot[bot] 7d93f3adc7 build(deps): bump actions/attest from 4.1.0 to 4.1.1
Bumps [actions/attest](https://github.com/actions/attest) from 4.1.0 to 4.1.1.
- [Release notes](https://github.com/actions/attest/releases)
- [Changelog](https://github.com/actions/attest/blob/main/RELEASE.md)
- [Commits](https://github.com/actions/attest/compare/59d89421af93a897026c735860bf21b6eb4f7b26...a1948c3f048ba23858d222213b7c278aabede763)

---
updated-dependencies:
- dependency-name: actions/attest
  dependency-version: 4.1.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-03 17:42:31 +00:00
Thayne McCombs fb1486f210 Merge pull request #1905 from tmccombs/release-automation
Add a little more automation for the release
2026-07-01 13:27:06 -06:00
dependabot[bot] 0c9c101e21 build(deps): bump jiff from 0.2.27 to 0.2.29
Bumps [jiff](https://github.com/BurntSushi/jiff) from 0.2.27 to 0.2.29.
- [Release notes](https://github.com/BurntSushi/jiff/releases)
- [Changelog](https://github.com/BurntSushi/jiff/blob/master/CHANGELOG.md)
- [Commits](https://github.com/BurntSushi/jiff/commits/jiff-static-0.2.29)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-01 17:42:52 +00:00
dependabot[bot] 8ff051e801 build(deps): bump regex from 1.12.3 to 1.12.4
Bumps [regex](https://github.com/rust-lang/regex) from 1.12.3 to 1.12.4.
- [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.12.3...1.12.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-01 17:42:44 +00:00
dependabot[bot] daefe07614 build(deps): bump ignore from 0.4.25 to 0.4.26
Bumps [ignore](https://github.com/BurntSushi/ripgrep) from 0.4.25 to 0.4.26.
- [Release notes](https://github.com/BurntSushi/ripgrep/releases)
- [Changelog](https://github.com/BurntSushi/ripgrep/blob/master/CHANGELOG.md)
- [Commits](https://github.com/BurntSushi/ripgrep/compare/ignore-0.4.25...ignore-0.4.26)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-01 17:42:38 +00:00
dependabot[bot] cade0b5e76 build(deps): bump nix from 0.31.2 to 0.31.3
Bumps [nix](https://github.com/nix-rust/nix) from 0.31.2 to 0.31.3.
- [Changelog](https://github.com/nix-rust/nix/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nix-rust/nix/compare/v0.31.2...v0.31.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-01 17:42:31 +00:00
Thayne McCombs c73e5e58a4 Merge pull request #2045 from tmccombs/io-error-handling
refactor: Get io error from method
2026-07-01 09:03:44 -06:00
Thayne McCombs f898e95db5 refactor: Get io error from method
use `ignore::Error::io_error()` to inspect the `std::io::Error` insted
of pattern matching on `ignore::Error::Io`.

This avoids relying on the implementation of ignore and how it organizes
the error. In particular, this would allow us to handle if the IO error
was wrapped in a `WithDepth`, as would be the case if
BurntSushi/ripgrep#3458 was merged.

This also allows us to remove some duplicate code.
2026-07-01 00:55:42 -06:00
Thayne McCombs b12606438e Merge pull request #1976 from curious-rabbit/master
fix: sanitize escape sequence injection
2026-07-01 00:36:48 -06:00
Thayne McCombs 95dc6454d5 Merge pull request #2037 from parneetsingh022/tests/search-str-for-entry-edge-cases
Add tests for search_str_for_entry without base dir
2026-06-28 23:44:14 -06:00
Thayne McCombs 61c5399fa4 Merge pull request #2036 from greymoth-jp/fix/and-pattern-path-separator
Check --and patterns for path separators too (match #1975)
2026-06-28 23:42:55 -06:00
Thayne McCombs 126be98699 Merge pull request #2038 from sharkdp/dependabot/github_actions/softprops/action-gh-release-3.0.1
build(deps): bump softprops/action-gh-release from 3.0.0 to 3.0.1
2026-06-28 10:56:02 -06:00
Thayne McCombs 77629639ff Merge pull request #2040 from bonanza127/fix-readme-number-format
docs(README): use consistent thousands separator
2026-06-28 02:03:26 -06:00
bonanza127 3ae9c9567e docs(README): use consistent thousands separator
Change ~750.000 to ~750,000 for consistency with English number formatting.
2026-06-28 04:50:55 +09:00
dependabot[bot] 8965502e8a build(deps): bump softprops/action-gh-release from 3.0.0 to 3.0.1
Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 3.0.0 to 3.0.1.
- [Release notes](https://github.com/softprops/action-gh-release/releases)
- [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md)
- [Commits](https://github.com/softprops/action-gh-release/compare/b4309332981a82ec1c5618f44dd2e27cc8bfbfda...718ea10b132b3b2eba29c1007bb80653f286566b)

---
updated-dependencies:
- dependency-name: softprops/action-gh-release
  dependency-version: 3.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-26 17:42:37 +00:00
Parneet 86c1e7d30d Add tests for search_str_for_entry without base dir 2026-06-26 16:59:06 +05:30
greymoth-jp 6aac84e4ae fix: flag path separators in --and patterns, not just the primary pattern
The path-separator diagnostic (#1873, made unconditional for the primary
pattern in #1975) only inspected opts.pattern. --and patterns (opts.exprs)
are chained with the primary pattern and matched against the file name in
exactly the same way, so a path separator in any of them is the same silent
"no results" footgun. Factor the per-pattern check into a helper and apply
it to the primary pattern plus every --and pattern, mirroring how
ensure_use_hidden_option_for_leading_dot_pattern already iterates all
patterns. --full-path still suppresses the diagnostic. Adds a regression test.

See #1873.
2026-06-26 17:02:47 +09:00
Thayne McCombs a8a77d2162 Merge pull request #2034 from sharkdp/dependabot/github_actions/actions/checkout-7.0.0
build(deps): bump actions/checkout from 6.0.3 to 7.0.0
2026-06-25 01:13:26 -06:00
dependabot[bot] e8730e6ca3 build(deps): bump actions/checkout from 6.0.3 to 7.0.0
Bumps [actions/checkout](https://github.com/actions/checkout) from 6.0.3 to 7.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v6.0.3...v7.0.0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-24 17:42:30 +00:00
Thayne McCombs 3b3e328b5c Merge pull request #2032 from sharkdp/dependabot/github_actions/actions/checkout-6.0.3
build(deps): bump actions/checkout from 6.0.2 to 6.0.3
2026-06-18 13:36:42 -06:00
dependabot[bot] fccbe9b1ee build(deps): bump actions/checkout from 6.0.2 to 6.0.3
Bumps [actions/checkout](https://github.com/actions/checkout) from 6.0.2 to 6.0.3.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v6.0.2...v6.0.3)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 6.0.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-18 17:42:30 +00:00
Thayne McCombs b5ae476fb1 Merge pull request #2030 from sharkdp/dependabot/github_actions/actions/checkout-6.0.2
build(deps): bump actions/checkout from 6 to 6.0.2
2026-06-18 00:04:19 -06:00
Thayne McCombs 094446004d Merge pull request #2001 from leno23/docs/extension-help-1693
docs: clarify that -e/--extension matches all entry types
2026-06-18 00:03:42 -06:00
wuyangfan 1161471b7e docs: clarify extension filter scope 2026-06-17 10:56:33 +08:00
Thayne McCombs 25461e5ce1 Merge pull request #2029 from sharkdp/dependabot/cargo/clap_complete-4.6.5
build(deps): bump clap_complete from 4.6.2 to 4.6.5
2026-06-06 00:48:49 -06:00
Thayne McCombs f7d4d717c2 Merge pull request #2026 from sharkdp/dependabot/cargo/filetime-0.2.29
build(deps): bump filetime from 0.2.27 to 0.2.29
2026-06-04 10:30:46 -06:00
dependabot[bot] 21facf8832 build(deps): bump clap_complete from 4.6.2 to 4.6.5
Bumps [clap_complete](https://github.com/clap-rs/clap) from 4.6.2 to 4.6.5.
- [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.6.2...clap_complete-v4.6.5)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-04 08:10:24 +00:00
Thayne McCombs 8e80bbe721 Merge pull request #2027 from sharkdp/dependabot/cargo/tikv-jemallocator-0.7.0
build(deps): bump tikv-jemallocator from 0.6.1 to 0.7.0
2026-06-04 02:08:22 -06:00
Thayne McCombs b25ca81375 Merge pull request #2028 from sharkdp/dependabot/cargo/normpath-1.5.1
build(deps): bump normpath from 1.5.0 to 1.5.1
2026-06-04 02:07:11 -06:00
dependabot[bot] 5ac75050d1 build(deps): bump normpath from 1.5.0 to 1.5.1
Bumps [normpath](https://github.com/dylni/normpath) from 1.5.0 to 1.5.1.
- [Release notes](https://github.com/dylni/normpath/releases)
- [Commits](https://github.com/dylni/normpath/compare/1.5.0...1.5.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-04 07:15:00 +00:00
Thayne McCombs f72512ff43 Merge pull request #2025 from sharkdp/dependabot/cargo/jiff-0.2.27
build(deps): bump jiff from 0.2.24 to 0.2.27
2026-06-04 01:13:20 -06:00
dependabot[bot] 75527dd9c6 build(deps): bump actions/checkout from 6 to 6.0.2
Bumps [actions/checkout](https://github.com/actions/checkout) from 6 to 6.0.2.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v6...v6.0.2)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 6.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-03 01:50:25 +00:00
dependabot[bot] 86564cd00d build(deps): bump tikv-jemallocator from 0.6.1 to 0.7.0
Bumps [tikv-jemallocator](https://github.com/tikv/jemallocator) from 0.6.1 to 0.7.0.
- [Release notes](https://github.com/tikv/jemallocator/releases)
- [Changelog](https://github.com/tikv/jemallocator/blob/main/CHANGELOG.md)
- [Commits](https://github.com/tikv/jemallocator/compare/0.6.1...0.7.0)

---
updated-dependencies:
- dependency-name: tikv-jemallocator
  dependency-version: 0.7.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-03 01:49:36 +00:00
dependabot[bot] b955593ac6 build(deps): bump filetime from 0.2.27 to 0.2.29
Bumps [filetime](https://github.com/alexcrichton/filetime) from 0.2.27 to 0.2.29.
- [Commits](https://github.com/alexcrichton/filetime/compare/0.2.27...0.2.29)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-03 01:49:32 +00:00
dependabot[bot] 1d4fa16433 build(deps): bump jiff from 0.2.24 to 0.2.27
Bumps [jiff](https://github.com/BurntSushi/jiff) from 0.2.24 to 0.2.27.
- [Release notes](https://github.com/BurntSushi/jiff/releases)
- [Changelog](https://github.com/BurntSushi/jiff/blob/master/CHANGELOG.md)
- [Commits](https://github.com/BurntSushi/jiff/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-03 01:49:25 +00:00
curious-rabbit 66be8485bc fix format 2026-05-17 20:49:47 +02:00
curious-rabbit 7d18db23a7 improve patch 2026-05-17 20:35:00 +02:00
wuyangfan 9f938bea1e docs: drop accidental --full-path edits from extension PR
Scope this branch to -e/--extension only (#1693). Full-path help
clarifications remain in #1997.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-17 05:37:07 +08:00
wuyangfan e60582f838 docs: clarify that -e/--extension matches all entry types
Short help stays minimal; long help and man page explain that extension
filtering applies to directories as well unless --type restricts results.

Addresses maintainer feedback on #1922. Fixes #1693

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-17 04:47:24 +08:00
curious-rabbit c96398c1bb cleanup 2026-05-05 21:48:32 +02:00
curious-rabbit a00259bf79 optimize 2026-05-05 21:27:25 +02:00
curious-rabbit 9d536e1a40 formatting 2026-05-02 20:56:11 +02:00
curious-rabbit aeecb5d8c0 apply strict filter 2026-05-02 20:52:29 +02:00
curious-rabbit 825f308931 formatting 2026-04-27 12:56:06 +02:00
curious-rabbit 63a7d85d26 improve patch 2026-04-20 14:28:18 +02:00
curious-rabbit 92c54d37c0 fix: sanitize escape sequence injection 2026-04-19 13:11:47 +02:00
Thayne McCombs 160d00f8fe Add a little more automation for the release
Add an awk script for automatically updating the help
text in README.md
2026-03-09 01:52:08 -06:00
21 changed files with 625 additions and 188 deletions
+11 -43
View File
@@ -21,7 +21,7 @@ jobs:
name: Extract crate metadata
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7.0.1
with:
persist-credentials: false
- name: Extract crate information
@@ -46,7 +46,7 @@ jobs:
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt
- uses: actions/checkout@v6
- uses: actions/checkout@v7.0.1
with:
persist-credentials: false
- run: cargo fmt -- --check
@@ -58,7 +58,7 @@ jobs:
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- uses: actions/checkout@v6
- uses: actions/checkout@v7.0.1
with:
persist-credentials: false
- run: cargo clippy --all-targets --all-features -- -Dwarnings
@@ -69,7 +69,7 @@ jobs:
needs: crate_metadata
steps:
- name: Checkout source code
uses: actions/checkout@v6
uses: actions/checkout@v7.0.1
with:
persist-credentials: false
@@ -115,7 +115,7 @@ jobs:
name: ${{ needs.crate_metadata.outputs.name }}
steps:
- name: Checkout source code
uses: actions/checkout@v6
uses: actions/checkout@v7.0.1
with:
persist-credentials: false
@@ -205,44 +205,12 @@ jobs:
- name: Create tarball
id: package
shell: bash
run: bash scripts/create-archive.sh
env:
BIN_PATH: ${{ steps.bin.outputs.BIN_PATH }}
version: ${{ needs.crate_metadata.outputs.version }}
run: |
PKG_suffix=".tar.gz"
case ${target} in
*-pc-windows-*) PKG_suffix=".zip" ;;
esac
PKG_BASENAME=${name}-v${version}-${target}
PKG_NAME=${PKG_BASENAME}${PKG_suffix}
echo "PKG_NAME=${PKG_NAME}" >> $GITHUB_OUTPUT
PKG_STAGING="${CICD_INTERMEDIATES_DIR}/package"
ARCHIVE_DIR="${PKG_STAGING}/${PKG_BASENAME}/"
mkdir -p "${ARCHIVE_DIR}"
# Binary
cp "${BIN_PATH}" "$ARCHIVE_DIR"
# README, LICENSE and CHANGELOG files
cp "README.md" "LICENSE-MIT" "LICENSE-APACHE" "CHANGELOG.md" "$ARCHIVE_DIR"
# Man page
cp "doc/${name}.1" "$ARCHIVE_DIR"
# Autocompletion files
cp -r autocomplete "${ARCHIVE_DIR}"
# base compressed package
pushd "${PKG_STAGING}/" >/dev/null
case ${target} in
*-pc-windows-*) 7z -y a "${PKG_NAME}" "${PKG_BASENAME}"/* | tail -2 ;;
*) tar czf "${PKG_NAME}" "${PKG_BASENAME}"/* ;;
esac;
popd >/dev/null
# Let subsequent steps know where to find the compressed package
echo "PKG_PATH=${PKG_STAGING}/${PKG_NAME}" >> $GITHUB_OUTPUT
TARGET: ${{ env.target }}
ARCHIVE_NAME: ${{ env.name }}-v${{ needs.crate_metadata.outputs.version }}-${{ env.target }}
PKG_STAGING: "${{ env.CICD_INTERMEDIATES_DIR }}/package"
- name: Create Debian package
id: debian-package
@@ -279,7 +247,7 @@ jobs:
echo "IS_RELEASE=${IS_RELEASE}" >> $GITHUB_OUTPUT
- name: "Attest artifacts"
uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4
uses: actions/attest@508db95dd578ae2727ebd6217d5ba78e4fbda05d # v4
if: steps.is-release.outputs.IS_RELEASE
with:
subject-path: |
@@ -287,7 +255,7 @@ jobs:
${{ steps.debian-package.outputs.DPKG_PATH }}
- name: Publish archives and packages
uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0
uses: softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # v3.0.2
if: steps.is-release.outputs.IS_RELEASE
with:
files: |
+1
View File
@@ -1,3 +1,4 @@
target/
/autocomplete/
/package/
**/*.rs.bk
+15 -1
View File
@@ -1,13 +1,27 @@
# Unreleased
# Upcoming Release
## Features
-
## Bugfixes
-
# 10.5.0
## Features
- Add `--ignore-parent` option to override `--no-ignore-parent`, see #1958 (@tmchow)
- Add `--exact` option to match the entire filename exactly (literal, non-substring).
## Bugfixes
- Sanitize control characters and bidirectional override characters in filenames
when output goes to a terminal, to prevent terminal escape-sequence injection.
Also reject a placeholder as the executable for `--exec-batch`, while still
allowing it for `--exec`.
- Handle invalid working directories gracefully when using `--full-path`, see #1900 (@Xavrir).
- Fire the "search pattern contains a path separator" diagnostic for any pattern containing `/`, not just patterns that happen to name an existing directory. Preserves the legacy Windows behaviour that also flags native `\` separators when the pattern resolves to a real directory. See #1873.
- Also fire the "search pattern contains a path separator" diagnostic for `--and` patterns, not only the primary positional pattern. `--and` patterns are matched against the file name just like the primary pattern, so a path separator in them silently returned zero results. See #1873.
- Fix bug where passing "-" as a directory argument didn't actually search that directory, see #849 (@Sean-Kenneth-Doherty).
- Fix panic when `--changed-before`/`--changed-within` is given an out-of-range `@` Unix timestamp; the value is now rejected gracefully, see #2081 (@nikolauspschuetz).
# 10.4.2
Generated
+110 -63
View File
@@ -63,9 +63,9 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.102"
version = "1.0.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470"
[[package]]
name = "argmax"
@@ -156,9 +156,9 @@ dependencies = [
[[package]]
name = "clap_complete"
version = "4.6.2"
version = "4.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ff7a1dccbdd8b078c2bdebff47e404615151534d5043da397ec50286816f9cb"
checksum = "e0a7a9bfdb35811f9e59832f0f05975114d2251b415fb534108e6f34060fd772"
dependencies = [
"clap",
]
@@ -189,9 +189,9 @@ checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
[[package]]
name = "crossbeam-channel"
version = "0.5.15"
version = "0.5.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2"
checksum = "d85363c37faeca707aef026efa9f3b34d077bce547e48f770770625c6013679e"
dependencies = [
"crossbeam-utils",
]
@@ -228,10 +228,42 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0b1fab2ae45819af2d0731d60f2afe17227ebb1a1538a236da84c93e9a60162"
dependencies = [
"dispatch2",
"nix 0.31.2",
"nix 0.31.3",
"windows-sys 0.61.2",
]
[[package]]
name = "defmt"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6e524506490a1953d237cb87b1cfc1e46f88c18f10a22dfe0f507dc6bfc7f7f"
dependencies = [
"bitflags 1.3.2",
"defmt-macros",
]
[[package]]
name = "defmt-macros"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0a27770e9c8f719a79d8b638281f4d828f77d8fd61e0bd94451b9b85e576a0b"
dependencies = [
"defmt-parser",
"proc-macro-error2",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "defmt-parser"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e"
dependencies = [
"thiserror",
]
[[package]]
name = "diff"
version = "0.1.13"
@@ -295,7 +327,7 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
[[package]]
name = "fd-find"
version = "10.4.2"
version = "10.5.0"
dependencies = [
"aho-corasick",
"anyhow",
@@ -313,7 +345,7 @@ dependencies = [
"jiff",
"libc",
"lscolors",
"nix 0.31.2",
"nix 0.31.3",
"normpath",
"nu-ansi-term",
"regex",
@@ -325,13 +357,12 @@ dependencies = [
[[package]]
name = "filetime"
version = "0.2.27"
version = "0.2.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f98844151eee8917efc50bd9e8318cb963ae8b297431495d3f758616ea5c57db"
checksum = "5c287a33c7f0a620c38e641e7f60827713987b3c0f26e8ddc9462cc69cf75759"
dependencies = [
"cfg-if",
"libc",
"libredox",
]
[[package]]
@@ -361,9 +392,9 @@ dependencies = [
[[package]]
name = "globset"
version = "0.4.18"
version = "0.4.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3"
checksum = "e47d37d2ae4464254884b60ab7071be2b876a9c35b696bd018ddcc76847309cd"
dependencies = [
"aho-corasick",
"bstr",
@@ -401,9 +432,9 @@ checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
[[package]]
name = "ignore"
version = "0.4.25"
version = "0.4.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3d782a365a015e0f5c04902246139249abf769125006fbe7649e2ee88169b4a"
checksum = "7f8a7b8211e695a1d0cd91cace480d4d0bd57667ab10277cc412c5f7f4884f83"
dependencies = [
"crossbeam-deque",
"globset",
@@ -441,24 +472,25 @@ checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
[[package]]
name = "jiff"
version = "0.2.24"
version = "0.2.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f00b5dbd620d61dfdcb6007c9c1f6054ebd75319f163d886a9055cec1155073d"
checksum = "34f877a98676d2fb664698d74cc6a51ce6c484ce8c770f05d0108ec9090aeb46"
dependencies = [
"defmt",
"jiff-static",
"jiff-tzdb-platform",
"log",
"portable-atomic",
"portable-atomic-util",
"serde_core",
"windows-sys 0.61.2",
"windows-link",
]
[[package]]
name = "jiff-static"
version = "0.2.24"
version = "0.2.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e000de030ff8022ea1da3f466fbb0f3a809f5e51ed31f6dd931c35181ad8e6d7"
checksum = "0666b5ab5ecaca213fc2a85b8c0083d9004e84ee2d5f9a7e0017aaf50986f25f"
dependencies = [
"proc-macro2",
"quote",
@@ -488,21 +520,9 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
[[package]]
name = "libc"
version = "0.2.186"
version = "0.2.189"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
[[package]]
name = "libredox"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1744e39d1d6a9948f4f388969627434e31128196de472883b39f148769bfe30a"
dependencies = [
"bitflags 2.11.0",
"libc",
"plain",
"redox_syscall",
]
checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
[[package]]
name = "linux-raw-sys"
@@ -546,9 +566,9 @@ dependencies = [
[[package]]
name = "nix"
version = "0.31.2"
version = "0.31.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d6d0705320c1e6ba1d912b5e37cf18071b6c2e9b7fa8215a1e8a7651966f5d3"
checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d"
dependencies = [
"bitflags 2.11.0",
"cfg-if",
@@ -558,9 +578,9 @@ dependencies = [
[[package]]
name = "normpath"
version = "1.5.0"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf23ab2b905654b4cb177e30b629937b3868311d4e1cba859f899c041046e69b"
checksum = "b9985ef7269fa99f3b12437bb698381da2428743ab90f20393f399fa14cab21a"
dependencies = [
"windows-sys 0.61.2",
]
@@ -601,12 +621,6 @@ version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
[[package]]
name = "plain"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
[[package]]
name = "portable-atomic"
version = "1.13.1"
@@ -632,6 +646,28 @@ dependencies = [
"syn",
]
[[package]]
name = "proc-macro-error-attr2"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5"
dependencies = [
"proc-macro2",
"quote",
]
[[package]]
name = "proc-macro-error2"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802"
dependencies = [
"proc-macro-error-attr2",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "proc-macro2"
version = "1.0.106"
@@ -656,20 +692,11 @@ version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "redox_syscall"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ce70a74e890531977d37e532c34d45e9055d2409ed08ddba14529471ed0be16"
dependencies = [
"bitflags 2.11.0",
]
[[package]]
name = "regex"
version = "1.12.3"
version = "1.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276"
checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba"
dependencies = [
"aho-corasick",
"memchr",
@@ -690,9 +717,9 @@ dependencies = [
[[package]]
name = "regex-syntax"
version = "0.8.10"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
[[package]]
name = "rustix"
@@ -844,10 +871,30 @@ dependencies = [
]
[[package]]
name = "tikv-jemalloc-sys"
version = "0.6.1+5.3.0-1-ge13ca993e8ccb9ba9847cc330696e02839f328f7"
name = "thiserror"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd8aa5b2ab86a2cefa406d889139c162cbb230092f7d1d7cbc1716405d852a3b"
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tikv-jemalloc-sys"
version = "0.7.1+5.3.1-0-g81034ce1f1373e37dc865038e1bc8eeecf559ce8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a2825c78386b4ae0314074867860ba9577875de945f05992c38815cbec327f0"
dependencies = [
"cc",
"libc",
@@ -855,9 +902,9 @@ dependencies = [
[[package]]
name = "tikv-jemallocator"
version = "0.6.1"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0359b4327f954e0567e69fb191cf1436617748813819c94b8cd4a431422d053a"
checksum = "249f09e49ab1609436f34c776e84231bead18d6a955f119f939bdc1d847561bd"
dependencies = [
"libc",
"tikv-jemalloc-sys",
+6 -6
View File
@@ -16,7 +16,7 @@ license = "MIT OR Apache-2.0"
name = "fd-find"
readme = "README.md"
repository = "https://github.com/sharkdp/fd"
version = "10.4.2"
version = "10.5.0"
edition= "2024"
rust-version = "1.90.0"
@@ -41,11 +41,11 @@ ctrlc = "3.5"
globset = "0.4"
anyhow = "1.0"
etcetera = "0.11"
normpath = "1.1.1"
normpath = "1.5.1"
crossbeam-channel = "0.5.15"
clap_complete = {version = "4.6.2", optional = true}
clap_complete = {version = "4.6.5", optional = true}
faccess = "0.2.4"
jiff = "0.2.24"
jiff = "0.2.27"
[dependencies.clap]
version = "4.6.1"
@@ -68,7 +68,7 @@ libc = "0.2"
# This has to be kept in sync with src/main.rs where the allocator for
# the program is set.
[target.'cfg(all(not(windows), not(target_os = "android"), not(target_os = "macos"), not(target_os = "freebsd"), not(target_os = "openbsd"), not(target_os = "illumos"), not(all(target_env = "musl", target_pointer_width = "32")), not(target_arch = "riscv64")))'.dependencies]
tikv-jemallocator = {version = "0.6.0", optional = true}
tikv-jemallocator = {version = "0.7.0", optional = true}
[dev-dependencies]
diff = "0.1"
@@ -95,7 +95,7 @@ codegen-units = 1
use-jemalloc = ["tikv-jemallocator"]
completions = ["clap_complete"]
base = ["use-jemalloc"]
default = ["use-jemalloc", "completions"]
default = ["completions"]
[package.metadata.binstall]
pkg-url = "{ repo }/releases/download/v{ version }/{ name }-v{ version }-{ target }.{ archive-format }"
+13 -1
View File
@@ -1,9 +1,16 @@
PROFILE=release
PROFILE?=release
EXE=target/$(PROFILE)/fd
prefix=/usr/local
bindir=$(prefix)/bin
datadir=$(prefix)/share
exe_name=fd
ifdef VERSION
ARCHIVE_NAME = fd-v$(VERSION)
else
ARCHIVE_NAME = fd
endif
export ARCHIVE_NAME
archive_path=package/$(ARCHIVE_NAME).tar.gz
$(EXE): Cargo.toml src/**/*.rs
cargo build --profile $(PROFILE) --locked
@@ -41,6 +48,11 @@ autocomplete/fdfind.fish: contrib/completion/fdfind.fish
$(comp_dir)
cp $< $@
archive: $(archive_path)
$(archive_path): completions $(EXE)
bash scripts/create-archive.sh
install: $(EXE) completions
install -Dm755 $(EXE) $(DESTDIR)$(bindir)/fd
install -Dm644 autocomplete/fd.bash $(DESTDIR)/$(datadir)/bash-completion/completions/$(exe_name)
+5 -5
View File
@@ -147,7 +147,7 @@ By default, *fd* only matches the filename of each file. However, using the `--f
you can match against the full path.
```bash
> fd -p -g '**/.git/config'
> fd -u -p -g '**/.git/config'
> fd -p '.*/lesson-\d+/[a-z]+.(jpg|png)'
```
@@ -212,7 +212,7 @@ See below for more details on the placeholder syntax.
The terminal output of commands run from parallel threads using `-x` will not be interlaced or garbled,
so `fd -x` can be used to rudimentarily parallelize a task run over many files.
An example of this is calculating the checksum of each individual file within a directory.
```
``` bash
fd -tf -x md5sum > file_checksums.txt
```
@@ -328,7 +328,7 @@ Options:
-t, --type <filetype> Filter by type: file (f), directory (d/dir), symlink (l),
executable (x), empty (e), socket (s), pipe (p), char-device
(c), block-device (b)
-e, --extension <ext> Filter by file extension
-e, --extension <ext> Filter by extension
-S, --size <size> Limit results based on the size of files
--changed-within <date|dur> Filter by file modification time (newer than)
--changed-before <date|dur> Filter by file modification time (older than)
@@ -349,7 +349,7 @@ Note that options can be given after the pattern and/or path as well.
## Benchmark
Let's search my home folder for files that end in `[0-9].jpg`. It contains ~750.000
Let's search my home folder for files that end in `[0-9].jpg`. It contains ~750,000
subdirectories and about a 4 million files. For averaging and statistical analysis, I'm using
[hyperfine](https://github.com/sharkdp/hyperfine). The following benchmarks are performed
with a "warm"/pre-filled disk-cache (results for a "cold" disk-cache show the same trends).
@@ -723,7 +723,7 @@ With Rust's package manager [cargo](https://github.com/rust-lang/cargo), you can
```
cargo install fd-find
```
Note that rust version *1.77.2* or later is required.
Note that rust version *1.90.0* or later is required.
`make` is also needed for the build.
Vendored
+4 -2
View File
@@ -257,9 +257,11 @@ Examples:
.RE
.TP
.BI "\-e, \-\-extension " ext
Filter search results by file extension
Filter search results by extension
.IR ext .
This option can be used repeatedly to allow for multiple possible file extensions.
This option can be used repeatedly to allow for multiple possible extensions.
By default, extension filters apply to both files and directories. Use
\fB\-\-type\fR to restrict the search to specific types.
If you want to search for files without extension, you can use the regex '^[^.]+$'
as a normal search pattern.
+4 -1
View File
@@ -5,6 +5,9 @@ necessary changes for the upcoming release.
## Version bump
This section can be done by running `scripts/version-bump.sh` with the new version
as an argument.
- [ ] Create a new branch for the required changes for this release.
- [ ] Update version in `Cargo.toml`. Run `cargo build` to update `Cargo.lock`.
Make sure to `git add` the `Cargo.lock` changes as well.
@@ -21,7 +24,7 @@ necessary changes for the upcoming release.
new version).
- [ ] Review `-h`, `--help`, and the `man` page.
- [ ] Run `fd -h` and copy the output to the *"Command-line options"* section in
the README
the README. This can be done by running `gawk -i inplace -f scripts/update-help.awk README.md`.
- [ ] Push all changes and wait for CI to succeed (before continuing with the
next section).
- [ ] Optional: manually test the new features and command-line options described
+67
View File
@@ -0,0 +1,67 @@
#!/bin/bash
#
# Create a .tar.gz archive of the fd project for distribution
#
# Expected environment variables:
#
# TARGET: rust target to publish. Defaults to default rustc target
# BIN_PATH: path to fd binary
# PKG_STAGING: directory to use for staging files to include in the archive
# ARCHIVE_NAME: Name of output file, minus extension, defaults to "fd" with a version appended
# if VERSION is specified
# VERSION: version to publish
# GITHUB_OUTPUT: file to store github output variables in
if [[ -z "$TARGET" ]]; then
TARGET="$(rustc -vV | sed -n 's/host: //p')"
fi
PKG_suffix=".tar.gz"
EXE_suffix=""
case ${TARGET} in
*-pc-windows-*)
PKG_suffix=".zip"
EXE_suffix=".exe"
;;
esac
if [[ -z $BIN_PATH ]]; then
BIN_PATH=target/${PROFILE:-release}/fd
fi
if [[ -z "$ARCHIVE_NAME" ]]; then
ARCHIVE_NAME=fd
[[ -n "$VERSION" ]] && ARCHIVE_NAME+="-v$VERSION"
fi
PKG_NAME=${ARCHIVE_NAME}${PKG_suffix}
staging_dir="${PKG_STAGING:-package}"
ARCHIVE_DIR="${staging_dir}/${ARCHIVE_NAME}/"
mkdir -p "${ARCHIVE_DIR}"
# Binary
cp "${BIN_PATH}" "$ARCHIVE_DIR"
# README, LICENSE and CHANGELOG files
cp "README.md" "LICENSE-MIT" "LICENSE-APACHE" "CHANGELOG.md" "$ARCHIVE_DIR"
# Man page
cp "doc/fd.1" "$ARCHIVE_DIR"
# Autocompletion files
cp -r autocomplete "${ARCHIVE_DIR}"
# base compressed package
pushd "${staging_dir}/" >/dev/null
case ${TARGET} in
*-pc-windows-*) 7z -y a "${PKG_NAME}" "${ARCHIVE_NAME}"/* | tail -2 ;;
*) tar czf "${PKG_NAME}" "${ARCHIVE_NAME}"/* ;;
esac;
popd >/dev/null
if [[ -n "$GITHUB_OUTPUT" ]]; then
echo "PKG_NAME=${PKG_NAME}" >> $GITHUB_OUTPUT
# Let subsequent steps know where to find the compressed package
echo "PKG_PATH=${PKG_STAGING}/${PKG_NAME}" >> $GITHUB_OUTPUT
fi
+41
View File
@@ -0,0 +1,41 @@
#!/usr/bin/gawk -f
BEGIN {
inSection = 0
inBlock = 0
}
/Command-line options/ { inSection=1 }
inSection && /^```/ {
inBlock=1
inSection=0
# print the starting fence then move to next line
print
next
}
inBlock && /^```/ {
cmd="cargo run --release --quiet -- -h"
# Output the results of fd -h
u=0
while (cmd | getline line) {
# Skip everything before the usage line
if (line ~ /^Usage/) {
u=1;
}
if (u) {
print line
}
}
status = close(cmd)
if (status) {
print "failed to generate help output" > "/dev/stderr"
exit 1
}
inBlock = 0
}
!inBlock
+4 -3
View File
@@ -377,8 +377,9 @@ pub struct Opts {
)]
pub filetype: Option<Vec<FileType>>,
/// (Additionally) filter search results by their file extension. Multiple
/// allowable file extensions can be specified.
/// Filter results by extension. By default, this matches all entry types
/// (including directories whose names end with the extension). Use `--type`
/// to restrict the search to specific types. Multiple extensions can be specified.
///
/// If you want to search for files without extension,
/// you can use the regex '^[^.]+$' as a normal search pattern.
@@ -386,7 +387,7 @@ pub struct Opts {
long = "extension",
short = 'e',
value_name = "ext",
help = "Filter by file extension",
help = "Filter by extension",
long_help
)]
pub extensions: Option<Vec<String>>,
+36 -2
View File
@@ -54,11 +54,19 @@ impl DirEntry {
}
/// Returns the path as it should be presented to the user.
/// When stripping `./` would leave the path starting with `-`, keep the original
/// (with `./`) so downstream tools don't interpret it as an option.
pub fn stripped_path(&self, config: &Config) -> &Path {
let path = self.path();
if config.strip_cwd_prefix {
strip_current_dir(self.path())
let stripped = strip_current_dir(path);
if starts_with_dash(stripped) {
path
} else {
stripped
}
} else {
self.path()
path
}
}
@@ -101,6 +109,10 @@ impl DirEntry {
}
}
fn starts_with_dash(path: &Path) -> bool {
path.as_os_str().as_encoded_bytes().first() == Some(&b'-')
}
impl PartialEq for DirEntry {
#[inline]
fn eq(&self, other: &Self) -> bool {
@@ -153,3 +165,25 @@ impl Colorable for DirEntry {
self.metadata().cloned()
}
}
#[cfg(test)]
mod tests {
use super::starts_with_dash;
use std::path::Path;
#[test]
fn dash_prefixed_paths_detected() {
assert!(starts_with_dash(Path::new("-rf")));
assert!(starts_with_dash(Path::new("--delete")));
assert!(starts_with_dash(Path::new("-")));
}
#[test]
fn safe_paths_not_flagged() {
assert!(!starts_with_dash(Path::new("foo")));
assert!(!starts_with_dash(Path::new("./foo")));
assert!(!starts_with_dash(Path::new("sub/-rf")));
assert!(!starts_with_dash(Path::new("")));
assert!(!starts_with_dash(Path::new(" -rf")));
}
}
+7 -1
View File
@@ -1,3 +1,9 @@
use std::io::IsTerminal;
use crate::sanitize::maybe_sanitize;
pub fn print_error(msg: impl Into<String>) {
eprintln!("[fd error]: {}", msg.into());
let msg = msg.into();
let safe = maybe_sanitize(&msg, std::io::stderr().is_terminal());
eprintln!("[fd error]: {safe}");
}
+23 -10
View File
@@ -43,7 +43,7 @@ impl CommandSet {
mode: ExecutionMode::OneByOne,
commands: input
.into_iter()
.map(CommandTemplate::new)
.map(|args| CommandTemplate::new(args, ExecutionMode::OneByOne))
.collect::<Result<_>>()?,
})
}
@@ -59,13 +59,10 @@ impl CommandSet {
commands: input
.into_iter()
.map(|args| {
let cmd = CommandTemplate::new(args)?;
let cmd = CommandTemplate::new(args, ExecutionMode::Batch)?;
if cmd.number_of_tokens() > 1 {
bail!("Only one placeholder allowed for batch commands");
}
if cmd.args[0].has_tokens() {
bail!("First argument of exec-batch is expected to be a fixed executable");
}
Ok(cmd)
})
.collect::<Result<Vec<_>>>()?,
@@ -220,7 +217,7 @@ struct CommandTemplate {
}
impl CommandTemplate {
fn new<I, S>(input: I) -> Result<CommandTemplate>
fn new<I, S>(input: I, mode: ExecutionMode) -> Result<CommandTemplate>
where
I: IntoIterator<Item = S>,
S: AsRef<str>,
@@ -245,6 +242,11 @@ impl CommandTemplate {
bail!("No executable provided for --exec or --exec-batch");
}
// A placeholder as the executable is meaningful for `--exec` but never for `--exec-batch`.
if mode == ExecutionMode::Batch && args[0].has_tokens() {
bail!("First argument of --exec-batch must be a fixed executable, not a placeholder");
}
// If a placeholder token was not supplied, append one at the end of the command.
if !has_placeholder {
args.push(FormatTemplate::Tokens(vec![Token::Placeholder]));
@@ -365,7 +367,8 @@ mod tests {
#[test]
fn tokens_with_literal_braces() {
let template = CommandTemplate::new(vec!["{{}}", "{{", "{.}}"]).unwrap();
let template =
CommandTemplate::new(vec!["{{}}", "{{", "{.}}"], ExecutionMode::OneByOne).unwrap();
assert_eq!(
generate_str(&template, "foo"),
vec!["{}", "{", "{.}", "foo"]
@@ -374,8 +377,9 @@ mod tests {
#[test]
fn tokens_with_literal_braces_and_placeholder() {
let template = CommandTemplate::new(vec!["{{{},end}"]).unwrap();
assert_eq!(generate_str(&template, "foo"), vec!["{foo,end}"]);
let template =
CommandTemplate::new(vec!["echo", "{{{},end}"], ExecutionMode::OneByOne).unwrap();
assert_eq!(generate_str(&template, "foo"), vec!["echo", "{foo,end}"]);
}
#[test]
@@ -421,7 +425,9 @@ mod tests {
#[test]
fn template_no_args() {
assert!(CommandTemplate::new::<Vec<_>, &'static str>(vec![]).is_err());
assert!(
CommandTemplate::new::<Vec<_>, &'static str>(vec![], ExecutionMode::OneByOne).is_err()
);
}
#[test]
@@ -429,6 +435,13 @@ mod tests {
assert!(CommandSet::new(vec![vec!["echo"], vec![]]).is_err());
}
#[test]
fn placeholder_as_executable_rejected() {
assert!(CommandSet::new(vec![vec!["{}"]]).is_ok());
assert!(CommandSet::new(vec![vec!["{/}", "arg"]]).is_ok());
assert!(CommandSet::new_batch(vec![vec!["{}"]]).is_err());
}
#[test]
fn generate_custom_path_separator() {
let arg = FormatTemplate::Tokens(vec![Token::Placeholder]);
+9 -1
View File
@@ -42,7 +42,7 @@ impl TimeFilter {
)
} else {
let timestamp_secs: u64 = s.strip_prefix('@')?.parse().ok()?;
Some(UNIX_EPOCH + Duration::from_secs(timestamp_secs))
UNIX_EPOCH.checked_add(Duration::from_secs(timestamp_secs))
}
}
@@ -199,4 +199,12 @@ mod tests {
.applies_to(&t1s_later)
);
}
#[test]
fn out_of_range_unix_timestamp_is_rejected() {
// A '@' timestamp large enough to overflow SystemTime must return
// None rather than panicking.
assert!(TimeFilter::before(&format!("@{}", u64::MAX)).is_none());
assert!(TimeFilter::after(&format!("@{}", u64::MAX)).is_none());
}
}
+21 -7
View File
@@ -11,6 +11,7 @@ mod fmt;
mod hyperlink;
mod output;
mod regex_helper;
mod sanitize;
mod walk;
use std::env;
@@ -65,7 +66,7 @@ fn main() {
exit_code.exit();
}
Err(err) => {
eprintln!("[fd error]: {err:#}");
crate::error::print_error(format!("{err:#}"));
ExitCode::GeneralError.exit();
}
}
@@ -148,8 +149,10 @@ fn set_working_dir(opts: &Opts) -> Result<()> {
/// Detect if the user accidentally supplied a path instead of a search pattern.
///
/// Without `--full-path`, fd matches patterns against file names, so any pattern
/// containing a path separator can never match. Two cases are worth a friendly
/// error rather than silent "no results":
/// containing a path separator can never match. This applies to the primary
/// positional pattern *and* to every `--and` pattern, since all of them are
/// chained together and matched against the file name (see `run`). Two cases per
/// pattern are worth a friendly error rather than silent "no results":
///
/// 1. The pattern contains '/'. '/' is always a path separator (including on
/// Windows) and has no regex meaning, so flagging it is safe and catches the
@@ -168,11 +171,23 @@ fn ensure_search_pattern_is_not_a_path(opts: &Opts) -> Result<()> {
return Ok(());
}
// Check the primary pattern and every `--and` pattern. They are all matched
// against the file name (see `run`), so a path separator in any of them is
// the same silent "no results" footgun.
for pattern in std::iter::once(&opts.pattern).chain(opts.exprs.iter().flatten()) {
ensure_single_search_pattern_is_not_a_path(pattern)?;
}
Ok(())
}
/// Apply the path-separator diagnostic to a single pattern. See
/// [`ensure_search_pattern_is_not_a_path`] for the rationale of each case.
fn ensure_single_search_pattern_is_not_a_path(pattern: &str) -> Result<()> {
// Start with the cheap check: '/' is always a path separator, including on
// Windows, and has no regex meaning, so flagging it is safe and catches the
// Linux/macOS mistake of pasting a full path as the pattern.
#[cfg_attr(not(windows), allow(unused_mut))]
let mut should_warn = opts.pattern.contains('/');
let mut should_warn = pattern.contains('/');
// On Windows we additionally accept the native `\` separator, but only when
// the pattern actually resolves to an existing directory - `\` is also the
@@ -182,8 +197,7 @@ fn ensure_search_pattern_is_not_a_path(opts: &Opts) -> Result<()> {
#[cfg(windows)]
{
should_warn = should_warn
|| (opts.pattern.contains(std::path::MAIN_SEPARATOR)
&& Path::new(&opts.pattern).is_dir());
|| (pattern.contains(std::path::MAIN_SEPARATOR) && Path::new(pattern).is_dir());
}
if should_warn {
@@ -194,7 +208,7 @@ fn ensure_search_pattern_is_not_a_path(opts: &Opts) -> Result<()> {
fd . '{pattern}'\n\n\
Instead, if you want your pattern to match the full file path, use:\n\n \
fd --full-path '{pattern}'",
pattern = &opts.pattern,
pattern = pattern,
))
} else {
Ok(())
+14 -7
View File
@@ -7,6 +7,7 @@ use crate::config::Config;
use crate::dir_entry::DirEntry;
use crate::fmt::FormatTemplate;
use crate::hyperlink::PathUrl;
use crate::sanitize::maybe_sanitize;
fn replace_path_separator(path: &str, new_path_separator: &str) -> String {
path.replace(std::path::MAIN_SEPARATOR, new_path_separator)
@@ -76,7 +77,12 @@ fn print_entry_format<W: Write>(
config.path_separator.as_deref(),
);
// TODO: support writing raw bytes on unix?
write!(stdout, "{}", output.to_string_lossy())
let s = output.to_string_lossy();
write!(
stdout,
"{}",
maybe_sanitize(&s, config.interactive_terminal)
)
}
// TODO: this function is performance critical and can probably be optimized
@@ -86,7 +92,6 @@ fn print_entry_colorized<W: Write>(
config: &Config,
ls_colors: &LsColors,
) -> io::Result<()> {
// Split the path between the parent and the last component
let mut offset = 0;
let path = entry.stripped_path(config);
let path_str = path.to_string_lossy();
@@ -112,14 +117,16 @@ fn print_entry_colorized<W: Write>(
.style_for_indicator(Indicator::Directory)
.map(Style::to_nu_ansi_term_style)
.unwrap_or_default();
write!(stdout, "{}", style.paint(parent_str))?;
let safe_parent = maybe_sanitize(&parent_str, config.interactive_terminal);
write!(stdout, "{}", style.paint(safe_parent.as_ref()))?;
}
let style = entry
.style(ls_colors)
.map(Style::to_nu_ansi_term_style)
.unwrap_or_default();
write!(stdout, "{}", style.paint(&path_str[offset..]))?;
let safe_basename = maybe_sanitize(&path_str[offset..], config.interactive_terminal);
write!(stdout, "{}", style.paint(safe_basename.as_ref()))?;
print_trailing_slash(
stdout,
@@ -143,7 +150,8 @@ fn print_entry_uncolorized_base<W: Write>(
if let Some(ref separator) = config.path_separator {
*path_string.to_mut() = replace_path_separator(&path_string, separator);
}
write!(stdout, "{path_string}")?;
let safe = maybe_sanitize(&path_string, config.interactive_terminal);
write!(stdout, "{safe}")?;
print_trailing_slash(stdout, entry, config, None)
}
@@ -165,10 +173,9 @@ fn print_entry_uncolorized<W: Write>(
use std::os::unix::ffi::OsStrExt;
if config.interactive_terminal || config.path_separator.is_some() {
// Fall back to the base implementation
print_entry_uncolorized_base(stdout, entry, config)
} else {
// Print path as raw bytes, allowing invalid UTF-8 filenames to be passed to other processes
// Piped output: raw bytes so invalid UTF-8 filenames reach downstream tools intact.
stdout.write_all(entry.stripped_path(config).as_os_str().as_bytes())?;
print_trailing_slash(stdout, entry, config, None)
}
+168
View File
@@ -0,0 +1,168 @@
//! TTY-output sanitization to prevent terminal escape injection via filenames.
use std::borrow::Cow;
use std::fmt::Write;
/// True for any char that is neither printable nor permitted whitespace (only HT).
/// Covers C0/C1/DEL, bidi overrides, zero-width and format chars, and tag chars.
#[inline]
fn needs_escape(c: char) -> bool {
if c == '\t' {
return false;
}
c.is_control()
|| matches!(c,
'\u{00AD}' // soft hyphen (invisible)
| '\u{180E}' // Mongolian vowel separator
| '\u{200B}'..='\u{200F}' // zero-width + LRM/RLM
| '\u{202A}'..='\u{202E}' // bidi embedding/override
| '\u{2060}'..='\u{206F}' // word joiner, invisibles, deprecated formats
| '\u{FEFF}' // BOM / zero-width no-break space
| '\u{FFF9}'..='\u{FFFB}' // interlinear annotation
| '\u{E0000}'..='\u{E007F}' // language tags
)
}
/// Returns a `Cow<str>` borrowing `s` when no escaping is needed, otherwise an owned
/// escaped copy. Use this when an owned `&str`/`String` is required (e.g. ANSI paint).
pub fn sanitize_for_terminal(s: &str) -> Cow<'_, str> {
if !s.chars().any(needs_escape) {
return Cow::Borrowed(s);
}
let mut out = String::with_capacity(s.len());
for c in s.chars() {
if needs_escape(c) {
let v = c as u32;
if v <= 0xFF {
let _ = write!(out, "\\x{v:02X}");
} else {
let _ = write!(out, "\\u{{{v:04X}}}");
}
} else {
out.push(c);
}
}
Cow::Owned(out)
}
/// Sanitize for terminal output only; raw bytes pass through on pipes/files.
pub fn maybe_sanitize<'a>(s: &'a str, is_terminal: bool) -> Cow<'a, str> {
if is_terminal {
sanitize_for_terminal(s)
} else {
Cow::Borrowed(s)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn preserves_safe_content() {
for s in [
"hello.txt",
"résumé.pdf",
"文档.txt",
"🦀.rs",
"a\tb",
"a\u{FFFD}b",
] {
assert!(
matches!(sanitize_for_terminal(s), Cow::Borrowed(_)),
"{s:?}"
);
assert_eq!(sanitize_for_terminal(s), s);
}
}
#[test]
fn strips_osc52_clipboard_payload() {
let attack = "innocent\x1b]52;c;cHduZWQ=\x1b\\.txt";
let safe = sanitize_for_terminal(attack);
assert!(!safe.contains('\x1b'));
assert_eq!(safe, "innocent\\x1B]52;c;cHduZWQ=\\x1B\\.txt");
}
#[test]
fn strips_cr_output_forgery() {
assert_eq!(sanitize_for_terminal("A\rFAKE OUTPUT"), "A\\x0DFAKE OUTPUT");
}
#[test]
fn strips_osc8_hyperlink_injection() {
let attack = "phish\x1b]8;;https:evil.example\x1b\\phony.txt";
assert!(!sanitize_for_terminal(attack).contains('\x1b'));
}
#[test]
fn strips_del() {
assert_eq!(sanitize_for_terminal("a\x7fb"), "a\\x7Fb");
}
#[test]
fn strips_bel_and_null() {
assert_eq!(sanitize_for_terminal("a\x07b"), "a\\x07b");
assert_eq!(sanitize_for_terminal("a\0b"), "a\\x00b");
}
#[test]
fn strips_newline() {
assert_eq!(sanitize_for_terminal("a\nb"), "a\\x0Ab");
}
#[test]
fn escape_preserves_information() {
let s = "name\x1bX\x07Y.txt";
assert_eq!(sanitize_for_terminal(s), "name\\x1BX\\x07Y.txt");
}
#[test]
fn strips_c1_csi_and_osc_initiators() {
// U+009B is CSI, U+009D is OSC; dangerous on 8-bit-control terminals.
assert_eq!(sanitize_for_terminal("\u{9b}31m"), "\\x9B31m");
assert_eq!(
sanitize_for_terminal("\u{9d}0;pwned\u{9c}"),
"\\x9D0;pwned\\x9C"
);
}
#[test]
fn strips_bidi_overrides_and_zero_width() {
// Trojan-Source style RLO/LRO that flip rendered order of filename text.
assert_eq!(
sanitize_for_terminal("safe\u{202E}fil\u{202D}gnp.exe"),
"safe\\u{202E}fil\\u{202D}gnp.exe"
);
// Zero-width space and BOM are also format chars used to disguise filenames.
assert_eq!(sanitize_for_terminal("a\u{200B}b"), "a\\u{200B}b");
assert_eq!(sanitize_for_terminal("\u{FEFF}name"), "\\u{FEFF}name");
}
#[test]
fn keeps_legitimate_unicode_features() {
// Variation selectors (U+FE0F, U+E0100..) modify preceding glyphs in CJK/emoji
// and are legitimate in filenames. Private-use chars are used by icon fonts.
for s in [
"heart\u{2764}\u{FE0F}.txt",
"icon\u{E000}.cfg",
"cjk\u{6F22}\u{E0101}.txt",
] {
assert!(
matches!(sanitize_for_terminal(s), Cow::Borrowed(_)),
"{s:?}"
);
}
}
#[test]
fn maybe_sanitize_passthrough_when_not_terminal() {
let attack = "x\x1by";
// Pipe context: bytes pass through unchanged (zero-copy).
let out = maybe_sanitize(attack, false);
assert!(matches!(out, Cow::Borrowed(_)));
assert_eq!(out, attack);
// TTY context: escapes apply.
assert_eq!(maybe_sanitize(attack, true), "x\\x1By");
}
}
+26 -20
View File
@@ -487,26 +487,16 @@ impl WorkerState {
Err(ignore::Error::WithPath {
path,
err: inner_err,
}) => match inner_err.as_ref() {
ignore::Error::Io(io_error)
if io_error.kind() == io::ErrorKind::NotFound
&& path
.symlink_metadata()
.ok()
.is_some_and(|m| m.file_type().is_symlink()) =>
{
DirEntry::broken_symlink(path)
}
_ => {
return match tx.send(WorkerResult::Error(ignore::Error::WithPath {
path,
err: inner_err,
})) {
Ok(_) => WalkState::Continue,
Err(_) => WalkState::Quit,
};
}
},
}) if inner_err
.io_error()
.is_some_and(|io_error| io_error.kind() == io::ErrorKind::NotFound)
&& path
.symlink_metadata()
.ok()
.is_some_and(|m| m.file_type().is_symlink()) =>
{
DirEntry::broken_symlink(path)
}
Err(err) => {
return match tx.send(WorkerResult::Error(err)) {
Ok(_) => WalkState::Continue,
@@ -735,4 +725,20 @@ mod tests {
PathBuf::from("bar")
);
}
#[test]
fn search_str_no_base_dir_with_plain_relative_path() {
assert_eq!(
search_str_for_entry(Path::new("foo/bar"), None),
PathBuf::from("bar")
);
}
#[test]
fn search_str_no_base_dir_with_file_in_current_dir() {
assert_eq!(
search_str_for_entry(Path::new("foo"), None),
PathBuf::from("foo")
);
}
}
+40 -15
View File
@@ -417,6 +417,31 @@ fn test_pattern_with_forward_slash_allowed_with_full_path() {
);
}
/// `--and` patterns are matched against the file name exactly like the primary
/// pattern, so a path separator in any of them is the same silent "no results"
/// footgun and must trigger the same diagnostic. Regression for the sibling of
/// #1873 left unchecked by #1975 (only the positional pattern was validated).
#[test]
fn test_and_pattern_with_forward_slash_is_rejected() {
let te = TestEnv::new(DEFAULT_DIRS, DEFAULT_FILES);
// Clean primary pattern, but a `--and` pattern carrying a path separator.
te.assert_failure_with_error(
&["foo", "--and", "nonexistent/path"],
"[fd error]: The search pattern 'nonexistent/path' contains a path-separation character and will not lead to any search results.",
);
// `--full-path` is the explicit opt-in and must still suppress the
// diagnostic for `--and` patterns too (the invocation runs and matches
// instead of erroring on the path separator).
#[cfg(not(windows))]
te.assert_output(
&["--full-path", "one/two/c", "--and", "two/c"],
"one/two/c.foo
one/two/C.Foo2",
);
}
/// Explicit root path
#[test]
fn test_explicit_root_path() {
@@ -1249,7 +1274,7 @@ fn test_absolute_path() {
{abs_path}/one/two/three/d.foo
{abs_path}/one/two/three/directory_foo/
{abs_path}/symlink",
abs_path = &abs_path
abs_path = abs_path
),
);
@@ -1262,7 +1287,7 @@ fn test_absolute_path() {
{abs_path}/one/two/C.Foo2
{abs_path}/one/two/three/d.foo
{abs_path}/one/two/three/directory_foo/",
abs_path = &abs_path
abs_path = abs_path
),
);
}
@@ -1281,7 +1306,7 @@ fn test_implicit_absolute_path() {
{abs_path}/one/two/C.Foo2
{abs_path}/one/two/three/d.foo
{abs_path}/one/two/three/directory_foo/",
abs_path = &abs_path
abs_path = abs_path
),
);
}
@@ -1301,7 +1326,7 @@ fn test_normalized_absolute_path() {
{abs_path}/one/two/C.Foo2
{abs_path}/one/two/three/d.foo
{abs_path}/one/two/three/directory_foo/",
abs_path = &abs_path
abs_path = abs_path
),
);
}
@@ -1535,7 +1560,7 @@ fn test_symlink_as_root() {
{dir}/one/two/three/d.foo
{dir}/one/two/three/directory_foo/
{dir}/symlink",
dir = &parent_parent
dir = parent_parent
),
);
}
@@ -1555,7 +1580,7 @@ fn test_symlink_and_absolute_path() {
{abs_path}/{expected_path}/three/
{abs_path}/{expected_path}/three/d.foo
{abs_path}/{expected_path}/three/directory_foo/",
abs_path = &abs_path,
abs_path = abs_path,
expected_path = expected_path
),
);
@@ -1573,7 +1598,7 @@ fn test_symlink_as_absolute_root() {
{abs_path}/symlink/three/
{abs_path}/symlink/three/d.foo
{abs_path}/symlink/three/directory_foo/",
abs_path = &abs_path
abs_path = abs_path
),
);
}
@@ -1597,7 +1622,7 @@ fn test_symlink_and_full_path() {
"{abs_path}/{expected_path}/three/
{abs_path}/{expected_path}/three/d.foo
{abs_path}/{expected_path}/three/directory_foo/",
abs_path = &abs_path,
abs_path = abs_path,
expected_path = expected_path
),
);
@@ -1618,7 +1643,7 @@ fn test_symlink_and_full_path_abs_path() {
"{abs_path}/symlink/three/
{abs_path}/symlink/three/d.foo
{abs_path}/symlink/three/directory_foo/",
abs_path = &abs_path
abs_path = abs_path
),
);
}
@@ -1747,7 +1772,7 @@ fn test_exec() {
{abs_path}/one/two/c.foo
{abs_path}/one/two/three/d.foo
{abs_path}/one/two/three/directory_foo",
abs_path = &abs_path
abs_path = abs_path
),
);
@@ -1846,7 +1871,7 @@ fn test_exec_multi() {
test c.foo
test d.foo
test directory_foo",
abs_path = &abs_path
abs_path = abs_path
),
);
@@ -1902,7 +1927,7 @@ fn test_exec_batch() {
&["--absolute-path", "foo", "--exec-batch", "echo"],
&format!(
"{abs_path}/a.foo {abs_path}/one/b.foo {abs_path}/one/two/C.Foo2 {abs_path}/one/two/c.foo {abs_path}/one/two/three/d.foo {abs_path}/one/two/three/directory_foo",
abs_path = &abs_path
abs_path = abs_path
),
);
@@ -1956,7 +1981,7 @@ fn test_exec_batch() {
te.assert_failure_with_error(
&["foo", "--exec-batch", "echo {}"],
"error: First argument of exec-batch is expected to be a fixed executable\n\
"error: First argument of --exec-batch must be a fixed executable, not a placeholder\n\
\n\
Usage: fd [OPTIONS] [pattern] [path]...\n\
\n\
@@ -2514,7 +2539,7 @@ fn test_base_directory() {
// Ignore base directory when absolute path is used
let (te, abs_path) = get_test_env_with_abs_path(DEFAULT_DIRS, DEFAULT_FILES);
let abs_base_dir = &format!("{abs_path}/one/two/", abs_path = &abs_path);
let abs_base_dir = &format!("{abs_path}/one/two/", abs_path = abs_path);
te.assert_output(
&["--base-directory", abs_base_dir, "foo", &abs_path],
&format!(
@@ -2524,7 +2549,7 @@ fn test_base_directory() {
{abs_path}/one/two/C.Foo2
{abs_path}/one/two/three/d.foo
{abs_path}/one/two/three/directory_foo/",
abs_path = &abs_path
abs_path = abs_path
),
);
}