8 Commits

Author SHA1 Message Date
Jesse Rosenstock 795d29ab35 xargs: clarify -x and -t help text
The previous text implied -x only interacts with -L/-n and used
imprecise phrasing. The new text mirrors the POSIX spec: terminate
when a constructed command line would exceed the size limit rather
than reducing the argument count.

Clarify that -t prints each command to stderr, rather than just
"be verbose".
2026-06-10 15:00:22 +02:00
xtqqczze c619fbeb82 chore(deps): update rust crates 2026-06-10 14:51:52 +02:00
weili a70f5ccd7c find: -ls: don't panic on an unmapped owner uid/gid
`find <file> -ls` panicked (exit 101) when the file's owning uid (or gid) had
no `/etc/passwd` (`/etc/group`) entry: it resolved the name with
`User::from_uid(...).unwrap().unwrap()`, and the inner unwrap aborts on the
`Ok(None)` that an unmapped id returns. Unmapped owners are routine (extracted
archives, NFS, deleted accounts, container images).

Resolve the uid/gid via `uucore::entries::uid2usr`/`gid2grp`, falling back to
the numeric id when there is no entry, matching GNU find (which prints the
number). These helpers already serialize the non-thread-safe getpwuid/getgrgid
behind a mutex, so reuse them rather than reimplementing the lookup.

Add a -ls integration test that chowns a file to an unmapped id and checks the
numeric fallback; it skips when the process lacks the privilege to do so.
2026-06-10 07:45:53 +02:00
weili 5992901805 find: -printf: test octal escape before a multibyte char
Regression test for the panic fixed in #720/#723: a `\NNN` octal escape
immediately followed by a multibyte character (`\0€`) used to slice the
format string mid-char and abort. Assert `-printf` emits the escape byte
and the following character intact.
2026-06-10 07:45:25 +02:00
Sylvestre Ledru 782744f35c ci: build and lint the wasm32-wasip1 target
Add a job that installs the wasm32-wasip1 target plus the WASI SDK (so the
bundled C in onig_sys can be cross-compiled) and runs cargo build and
clippy -D warnings, so non-Unix compilation can't regress.
2026-06-10 07:45:04 +02:00
Sylvestre Ledru c100ac226f build: support compiling for non-Unix targets such as wasm
The platform-specific code was split into cfg(unix)/cfg(windows) arms, so
wasm32 targets (which are neither) failed to compile. Widen the generic
Windows stubs to cfg(not(unix)) and make the -ls printer and xargs size
limiters fall back to portable APIs when no platform-specific one exists.
2026-06-10 07:45:04 +02:00
dependabot[bot] 5bf236b891 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-06-10 07:15:21 +02:00
weili 001966f326 find: -name: don't panic on a malformed POSIX bracket class
A `-name` glob with a `[.`/`[=`/`[:` introducer whose closing delimiter is
missing — only a lone `]` (or the delimiter) before a multibyte char, e.g.
`'[[:]é'` — panicked (exit 101). `extract_bracket_expr` searched for *either*
the delimiter or `]` and then added a blind `+ 2`, which overshot into the
following character and sliced off a UTF-8 boundary.

Search for the actual two-byte closing sequence `<delim>]` instead. A valid
class (`[:alpha:]`) is unchanged; a malformed one returns None, so the caller
treats `[` literally and accepts the pattern like GNU find (exit 0). Reachable
via -name/-iname/-path/-ipath/-lname/-ilname. Adds a regression test.
2026-06-09 22:41:13 +02:00
12 changed files with 212 additions and 106 deletions
+23
View File
@@ -65,6 +65,29 @@ jobs:
- run: |
cargo clippy --all-targets -- -D warnings
wasm:
name: cargo build (wasm32-wasip1)
runs-on: ubuntu-latest
env:
WASI_SDK_VERSION: "33"
WASI_SDK_PATH: ${{ github.workspace }}/wasi-sdk
# onig_sys compiles bundled C, so it needs a C compiler with a WASI
# sysroot to cross-compile to wasm.
CC_wasm32_wasip1: ${{ github.workspace }}/wasi-sdk/bin/clang
AR_wasm32_wasip1: ${{ github.workspace }}/wasi-sdk/bin/llvm-ar
steps:
- uses: actions/checkout@v6
- name: Install wasm target
run: rustup target add wasm32-wasip1
- name: Install WASI SDK
run: |
curl -sSfL "https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-${WASI_SDK_VERSION}/wasi-sdk-${WASI_SDK_VERSION}.0-x86_64-linux.tar.gz" | tar xz
mv "wasi-sdk-${WASI_SDK_VERSION}.0-x86_64-linux" "${WASI_SDK_PATH}"
- name: Build
run: cargo build --target wasm32-wasip1
- name: Clippy
run: cargo clippy --target wasm32-wasip1 -- -D warnings
grcov:
name: Code coverage
runs-on: ${{ matrix.os }}
Generated
+78 -71
View File
@@ -119,9 +119,9 @@ dependencies = [
[[package]]
name = "autocfg"
version = "1.5.0"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
[[package]]
name = "bitflags"
@@ -131,9 +131,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.11.0"
version = "2.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8"
[[package]]
name = "bstr"
@@ -148,9 +148,9 @@ dependencies = [
[[package]]
name = "bumpalo"
version = "3.20.2"
version = "3.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb"
checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
[[package]]
name = "cast"
@@ -160,9 +160,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
[[package]]
name = "cc"
version = "1.2.60"
version = "1.2.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43c5703da9466b66a946814e1adf53ea2c90f10063b86290cc9eb67ce3478a20"
checksum = "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f"
dependencies = [
"find-msvc-tools",
"shlex",
@@ -422,9 +422,9 @@ checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8"
[[package]]
name = "displaydoc"
version = "0.2.5"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f"
dependencies = [
"proc-macro2",
"quote",
@@ -674,9 +674,9 @@ dependencies = [
[[package]]
name = "hashbrown"
version = "0.17.0"
version = "0.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51"
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
[[package]]
name = "heck"
@@ -727,7 +727,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
dependencies = [
"equivalent",
"hashbrown 0.17.0",
"hashbrown 0.17.1",
"serde",
"serde_core",
]
@@ -794,9 +794,9 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "jiff"
version = "0.2.23"
version = "0.2.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a3546dc96b6d42c5f24902af9e2538e82e39ad350b0c766eb3fbf2d8f3d8359"
checksum = "4603d3033e49e2b0e31229fcab20a5d40089c607d975cd9c80551dc69eed9102"
dependencies = [
"jiff-static",
"jiff-tzdb-platform",
@@ -804,14 +804,14 @@ dependencies = [
"portable-atomic",
"portable-atomic-util",
"serde_core",
"windows-sys 0.61.2",
"windows-link",
]
[[package]]
name = "jiff-static"
version = "0.2.23"
version = "0.2.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a8c8b344124222efd714b73bb41f8b5120b27a7cc1c75593a6ff768d9d05aa4"
checksum = "782d32378dddf207193ac91cefb848ad41abb58195c95168e1291227a0832b47"
dependencies = [
"proc-macro2",
"quote",
@@ -835,11 +835,12 @@ dependencies = [
[[package]]
name = "js-sys"
version = "0.3.94"
version = "0.3.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e04e2ef80ce82e13552136fabeef8a5ed1f985a96805761cbb9a2c34e7664d9"
checksum = "f2025f20d7a4fa7785846e7b63d10a76d3f1cee98ee5cb79ea59703f95e42162"
dependencies = [
"once_cell",
"cfg-if",
"futures-util",
"wasm-bindgen",
]
@@ -863,9 +864,9 @@ checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
[[package]]
name = "link-section"
version = "0.18.1"
version = "0.18.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "014e440054ce8170890229eeef5bcda955305e056ec713de40ed366944483f09"
checksum = "c2b1dd6fe32e55c0fc0ea9493aa57459ca3cf4ff3c857c7d0302290150da6e4f"
[[package]]
name = "linktime-proc-macro"
@@ -881,15 +882,15 @@ checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
[[package]]
name = "log"
version = "0.4.29"
version = "0.4.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a"
[[package]]
name = "memchr"
version = "2.8.0"
version = "2.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8"
[[package]]
name = "nix"
@@ -897,7 +898,7 @@ version = "0.30.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6"
dependencies = [
"bitflags 2.11.0",
"bitflags 2.13.0",
"cfg-if",
"cfg_aliases",
"libc",
@@ -909,7 +910,7 @@ version = "0.31.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d"
dependencies = [
"bitflags 2.11.0",
"bitflags 2.13.0",
"cfg-if",
"cfg_aliases",
"libc",
@@ -917,9 +918,9 @@ dependencies = [
[[package]]
name = "num-conv"
version = "0.2.1"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967"
checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441"
[[package]]
name = "num-traits"
@@ -957,7 +958,7 @@ version = "6.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0cc3cbf698f9438986c11a880c90a6d04b9de27575afd28bbf45b154b6c709e2"
dependencies = [
"bitflags 2.11.0",
"bitflags 2.13.0",
"libc",
"once_cell",
"onig_sys",
@@ -996,9 +997,9 @@ checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
[[package]]
name = "pkg-config"
version = "0.3.32"
version = "0.3.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e"
[[package]]
name = "plotters"
@@ -1036,9 +1037,9 @@ checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
[[package]]
name = "portable-atomic-util"
version = "0.2.6"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "091397be61a01d4be58e7841595bd4bfedb15f1cd54977d79b8271e94ed799a3"
checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618"
dependencies = [
"portable-atomic",
]
@@ -1142,9 +1143,9 @@ dependencies = [
[[package]]
name = "rand_core"
version = "0.10.0"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba"
checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69"
[[package]]
name = "rayon"
@@ -1168,9 +1169,9 @@ dependencies = [
[[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",
@@ -1191,9 +1192,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 = "relative-path"
@@ -1260,7 +1261,7 @@ version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
dependencies = [
"bitflags 2.11.0",
"bitflags 2.13.0",
"errno",
"libc",
"linux-raw-sys",
@@ -1326,9 +1327,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.149"
version = "1.0.150"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
dependencies = [
"itoa",
"memchr",
@@ -1339,9 +1340,9 @@ dependencies = [
[[package]]
name = "shlex"
version = "1.3.0"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
[[package]]
name = "slab"
@@ -1357,9 +1358,9 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]]
name = "socket2"
version = "0.6.3"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e"
checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51"
dependencies = [
"libc",
"windows-sys 0.61.2",
@@ -1658,11 +1659,11 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
name = "wasip2"
version = "1.0.2+wasi-0.2.9"
version = "1.0.3+wasi-0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5"
checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6"
dependencies = [
"wit-bindgen",
"wit-bindgen 0.57.1",
]
[[package]]
@@ -1671,14 +1672,14 @@ version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5"
dependencies = [
"wit-bindgen",
"wit-bindgen 0.51.0",
]
[[package]]
name = "wasm-bindgen"
version = "0.2.117"
version = "0.2.123"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0551fc1bb415591e3372d0bc4780db7e587d84e2a7e79da121051c5c4b89d0b0"
checksum = "a254a4b10c19a76f09a27640e7ffbf9bc30bf67e16a3bf28aaefa4920fe81563"
dependencies = [
"cfg-if",
"once_cell",
@@ -1689,9 +1690,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.117"
version = "0.2.123"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fbdf9a35adf44786aecd5ff89b4563a90325f9da0923236f6104e603c7e86be"
checksum = "24a40fc75b0ec6f3746ceb10d36f53a93dcd68a93b11b6445983945d79eba0dc"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -1699,9 +1700,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.117"
version = "0.2.123"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dca9693ef2bab6d4e6707234500350d8dad079eb508dca05530c85dc3a529ff2"
checksum = "908f34bd9b9ce3d4caf07b72dfab63d61504d156856c6bd3cd87fa350cf3985b"
dependencies = [
"bumpalo",
"proc-macro2",
@@ -1712,9 +1713,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.117"
version = "0.2.123"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39129a682a6d2d841b6c429d0c51e5cb0ed1a03829d8b3d1e69a011e62cb3d3b"
checksum = "7acbf7616c27b194bbb550bf77ed0c2c3e5b7fd1260a93082b95fb7f47959b92"
dependencies = [
"unicode-ident",
]
@@ -1747,7 +1748,7 @@ version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
dependencies = [
"bitflags 2.11.0",
"bitflags 2.13.0",
"hashbrown 0.15.5",
"indexmap",
"semver",
@@ -1755,9 +1756,9 @@ dependencies = [
[[package]]
name = "web-sys"
version = "0.3.94"
version = "0.3.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd70027e39b12f0849461e08ffc50b9cd7688d942c1c8e3c7b22273236b4dd0a"
checksum = "6e0871acf327f283dc6da28a1696cdc64fb355ba9f935d052021fa77f35cce69"
dependencies = [
"js-sys",
"wasm-bindgen",
@@ -2036,6 +2037,12 @@ dependencies = [
"wit-bindgen-rust-macro",
]
[[package]]
name = "wit-bindgen"
version = "0.57.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e"
[[package]]
name = "wit-bindgen-core"
version = "0.51.0"
@@ -2085,7 +2092,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
dependencies = [
"anyhow",
"bitflags 2.11.0",
"bitflags 2.13.0",
"indexmap",
"log",
"serde",
@@ -2133,18 +2140,18 @@ checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
[[package]]
name = "zerocopy"
version = "0.8.50"
version = "0.8.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b065d4f0e55f82fae73202e189638116a87c55ab6b8e6c2721e13dd9d854ad1"
checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.50"
version = "0.8.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b631b19d36a892ab55420c92dbc83ccd79274f25be714855d3074aa71cab639"
checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930"
dependencies = [
"proc-macro2",
"quote",
@@ -2153,9 +2160,9 @@ dependencies = [
[[package]]
name = "zerofrom"
version = "0.1.7"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df"
checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272"
[[package]]
name = "zerovec"
+14 -1
View File
@@ -83,7 +83,10 @@ fn extract_bracket_expr(pattern: &str) -> Option<(String, &str)> {
if matches!(delim, '.' | '=' | ':') {
let rest = chars.as_str();
let end = rest.find([delim, ']'])? + 2;
// Search for the two-byte closer `<delim>]` (e.g. `:]`);
// matching either byte alone let `+ 2` overshoot a char boundary.
let closer = format!("{delim}]");
let end = rest.find(closer.as_str())? + 2;
expr.push_str(&rest[..end]);
chars = rest[end..].chars();
}
@@ -222,6 +225,16 @@ mod tests {
assert_glob_regex(r"foo[bar[!baz", r"foo\[bar\[!baz");
}
#[test]
fn malformed_posix_class_with_multibyte_char() {
for pat in ["[[:]é", "[[:a]é", "[[.]é", "[[:é]", "[[=]😀"] {
assert!(
glob_to_regex(pat).is_some(),
"panicked or rejected: {pat:?}"
);
}
}
#[test]
fn incomplete_escape() {
assert_eq!(glob_to_regex(r"foo\"), None);
+7 -5
View File
@@ -11,6 +11,8 @@ use nix::unistd::Group;
use std::os::unix::fs::MetadataExt;
pub struct GroupMatcher {
// Only read on Unix; the non-Unix `matches` implementation is a stub.
#[cfg_attr(not(unix), allow(dead_code))]
gid: ComparableValue,
}
@@ -31,7 +33,7 @@ impl GroupMatcher {
Self { gid }
}
#[cfg(windows)]
#[cfg(not(unix))]
pub fn from_group_name(_group: &str) -> Option<Self> {
None
}
@@ -46,10 +48,10 @@ impl Matcher for GroupMatcher {
}
}
#[cfg(windows)]
#[cfg(not(unix))]
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. :(
// The user group acquisition function for non-Unix systems is not implemented in
// MetadataExt, so it is somewhat difficult to implement it. :(
false
}
}
@@ -80,7 +82,7 @@ impl Matcher for NoGroupMatcher {
false
}
#[cfg(windows)]
#[cfg(not(unix))]
fn matches(&self, _file_info: &WalkEntry, _: &mut MatcherIO) -> bool {
false
}
+21 -19
View File
@@ -126,7 +126,6 @@ impl Ls {
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();
@@ -150,14 +149,13 @@ impl Ls {
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
};
// Fall back to the numeric id when the uid/gid has no passwd/group entry
// (unmapped owner) — matching GNU find, which never crashes. uucore::entries
// serializes the non-thread-safe getpwuid/getgrgid behind a mutex.
let user =
uucore::entries::uid2usr(metadata.uid()).unwrap_or_else(|_| metadata.uid().to_string());
let group =
uucore::entries::gid2grp(metadata.gid()).unwrap_or_else(|_| metadata.gid().to_string());
let size = metadata.size();
let last_modified = {
let system_time = metadata.modified().unwrap();
@@ -195,7 +193,7 @@ impl Ls {
}
}
#[cfg(windows)]
#[cfg(not(unix))]
fn print(
&self,
file_info: &WalkEntry,
@@ -203,16 +201,15 @@ impl Ls {
mut out: impl Write,
print_error_message: bool,
) {
use std::os::windows::fs::MetadataExt;
// Non-Unix targets (Windows, wasm, ...) don't expose inode, owner,
// group or Unix permission bits, so those columns are left blank.
let metadata = file_info.metadata().unwrap();
let inode_number = 0;
let size = metadata.len();
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
@@ -220,14 +217,19 @@ impl Ls {
number_of_blocks
}
} else {
number_of_blocks + (4 - (remainder))
number_of_blocks + (4 - remainder)
}
};
let permission = { format_permissions(metadata.file_attributes()) };
#[cfg(windows)]
let permission = {
use std::os::windows::fs::MetadataExt;
format_permissions(metadata.file_attributes())
};
#[cfg(not(windows))]
let permission = "?---------";
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();
@@ -235,9 +237,9 @@ impl Ls {
};
let path = file_info.path().to_string_lossy();
match write!(
match writeln!(
out,
" {:<4} {:>6} {:<10} {:>3} {:<8} {:<8} {:>8} {} {}\n",
" {:<4} {:>6} {:<10} {:>3} {:<8} {:<8} {:>8} {} {}",
inode_number,
number_of_blocks,
permission,
+1 -1
View File
@@ -31,7 +31,7 @@ impl Matcher for NameMatcher {
self.pattern.matches(&name)
}
#[cfg(windows)]
#[cfg(not(unix))]
self.pattern.matches(&name)
}
}
+2 -2
View File
@@ -91,7 +91,7 @@ impl PermMatcher {
}
#[cfg(not(unix))]
pub fn new(_dummy_pattern: &str) -> Result<PermMatcher, Box<dyn Error>> {
pub fn new(_dummy_pattern: &str) -> Result<Self, Box<dyn Error>> {
Err(From::from(
"Permission matching is not available on this platform",
))
@@ -132,7 +132,7 @@ impl Matcher for PermMatcher {
"Permission matching not available on this platform!"
)
.unwrap();
return false;
false
}
}
+1
View File
@@ -17,6 +17,7 @@ use super::{FileType, Matcher, MatcherIO, WalkEntry, WalkError};
#[cfg(unix)]
use std::os::unix::prelude::MetadataExt;
#[cfg(unix)]
const STANDARD_BLOCK_SIZE: u64 = 512;
#[derive(Debug, PartialEq, Eq)]
+5 -3
View File
@@ -11,6 +11,8 @@ use nix::unistd::User;
use std::os::unix::fs::MetadataExt;
pub struct UserMatcher {
// Only read on Unix; the non-Unix `matches` implementation is a stub.
#[cfg_attr(not(unix), allow(dead_code))]
uid: ComparableValue,
}
@@ -31,7 +33,7 @@ impl UserMatcher {
Self { uid }
}
#[cfg(windows)]
#[cfg(not(unix))]
pub fn from_user_name(_user: &str) -> Option<Self> {
None
}
@@ -46,7 +48,7 @@ impl Matcher for UserMatcher {
}
}
#[cfg(windows)]
#[cfg(not(unix))]
fn matches(&self, _file_info: &WalkEntry, _: &mut MatcherIO) -> bool {
false
}
@@ -78,7 +80,7 @@ impl Matcher for NoUserMatcher {
false
}
#[cfg(windows)]
#[cfg(not(unix))]
fn matches(&self, _file_info: &WalkEntry, _: &mut MatcherIO) -> bool {
false
}
+3 -1
View File
@@ -9,7 +9,9 @@ pub mod matchers;
use matchers::{Follow, WalkEntry};
use std::cell::RefCell;
use std::error::Error;
use std::io::{self, stderr, stdout, BufRead, BufReader, IsTerminal, Write};
#[cfg(unix)]
use std::io::IsTerminal;
use std::io::{self, stderr, stdout, BufRead, BufReader, Write};
use std::path::PathBuf;
use std::rc::Rc;
use std::time::SystemTime;
+17 -3
View File
@@ -152,6 +152,12 @@ fn count_osstr_chars_for_exec(s: &OsStr) -> usize {
s.as_bytes().len() + 1
}
#[cfg(not(any(unix, windows)))]
fn count_osstr_chars_for_exec(s: &OsStr) -> usize {
// Include +1 for the null terminator.
s.as_encoded_bytes().len() + 1
}
#[derive(Clone)]
struct MaxCharsCommandSizeLimiter {
current_size: usize,
@@ -187,6 +193,14 @@ impl MaxCharsCommandSizeLimiter {
Self::new(arg_max - ARG_HEADROOM - env_size)
}
#[cfg(not(any(unix, windows)))]
fn new_system(_env: &HashMap<OsString, OsString>) -> Self {
// No portable way to query the system limit; fall back to the POSIX
// minimum guaranteed value for _POSIX_ARG_MAX.
const POSIX_ARG_MAX: usize = 4096;
Self::new(POSIX_ARG_MAX)
}
}
impl CommandSizeLimiter for MaxCharsCommandSizeLimiter {
@@ -918,8 +932,8 @@ fn do_xargs(args: &[&str]) -> Result<CommandResult, XargsError> {
.short('x')
.long(options::EXIT)
.help(
"Exit if the number of arguments allowed by -L or -n do not \
fit into the number of allowed characters",
"Terminate if a constructed command line would exceed the size \
limit (see -s) rather than reducing the argument count",
)
.action(ArgAction::SetTrue),
)
@@ -989,7 +1003,7 @@ fn do_xargs(args: &[&str]) -> Result<CommandResult, XargsError> {
Arg::new(options::VERBOSE)
.short('t')
.long(options::VERBOSE)
.help("Be verbose")
.help("Print each command to stderr before executing it")
.action(ArgAction::SetTrue),
)
.arg(
+40
View File
@@ -510,6 +510,15 @@ fn find_printf() {
);
}
#[test]
fn find_printf_octal_escape_before_multibyte_char() {
ucmd()
.args(&["./test_data/simple", "-maxdepth", "0", "-printf", "\\0€\\n"])
.succeeds()
.no_stderr()
.stdout_only("\0\n");
}
#[cfg(unix)]
#[test]
fn find_perm() {
@@ -1009,6 +1018,37 @@ fn find_ls() {
.no_stderr();
}
// Regression test for uutils/findutils#717: `-ls` used to abort (exit 101) when
// a file's owning uid/gid had no passwd/group entry; it must fall back to the
// numeric id like GNU find. Creating such a file needs privilege to chown to an
// unmapped id, so skip when that isn't available (e.g. non-root CI).
#[test]
#[cfg(unix)]
fn find_ls_unmapped_owner_renders_numeric_id() {
use nix::unistd::{chown, Gid, Uid};
let temp_dir = Builder::new().prefix("find_ls_unmapped").tempdir().unwrap();
let file = temp_dir.path().join("orphan");
File::create(&file).unwrap();
// A uid/gid essentially never present in /etc/passwd or /etc/group.
let unmapped = 4_000_000_000u32;
if chown(
&file,
Some(Uid::from_raw(unmapped)),
Some(Gid::from_raw(unmapped)),
)
.is_err()
{
return;
}
ucmd()
.args(&[file.to_str().unwrap(), "-ls"])
.succeeds()
.stdout_contains(unmapped.to_string());
}
#[test]
#[cfg(unix)]
fn find_slashes() {