diff --git a/CHANGELOG.md b/CHANGELOG.md index 46b6306..6a75dce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,10 +4,15 @@ - Header view: new view to inspect ELF and PE executables. - Added a command to change the view (same as pressing `Tab`). Example: `:set view hex` (possible values are `text`, `hex`, and `header`). It can be used in `~/.dz6init` to set the default view when dz6 is loaded. -- Added support for comments / lines starting with `#` in `~/.dz6init`. +- initfile comments: lines starting with `#` in `~/.dz6init` are ignored. +- `set wrapscan` is now disabled by default. `set ws` is an alias. +- `set ignorecase` or `set ic` enables case-insensitive search for ASCII strings. +- `set smartscan` or `set scs` enables smart scan (needs `ignorecase` enabled). In this mode, `ignorecase` is disabled in case the search term contains a capital letter. +- Hex search now supports patterns. -## Breaking changes: -- `Tab` cycles through available views now (`Enter` no longer does that). +### Breaking changes: + +- `Tab` cycles through available views now. `Enter` no longer does that. - To search over the string list, press `/` (it was `f` before). ## dz6 v0.7.1 diff --git a/Cargo.lock b/Cargo.lock index d8aaabf..5b78ef8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "addr2line" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59317f77929f0e679d39364702289274de2f0f0b22cbf50b2b8cff2169a0b27a" +dependencies = [ + "gimli 0.33.0", +] + [[package]] name = "aho-corasick" version = "1.1.5" @@ -26,6 +35,17 @@ dependencies = [ "libc", ] +[[package]] +name = "annotate-snippets" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f211a51805bc641f3ad5b7664c77d2547af685cc33b4cd8d31964027a46f13f1" +dependencies = [ + "anstyle", + "memchr", + "unicode-width", +] + [[package]] name = "anstream" version = "1.0.0" @@ -91,6 +111,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" + [[package]] name = "arboard" version = "3.6.1" @@ -108,6 +134,23 @@ dependencies = [ "x11rb", ] +[[package]] +name = "ascii_tree" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca6c635b3aa665c649ad1415f1573c85957dfa47690ec27aebe7ec17efe3c643" + +[[package]] +name = "async-trait" +version = "0.1.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82f6aeea286b8eb4dd3431a1be1b59d290ace00f5bfd8e2a159bc2a05e2c1667" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + [[package]] name = "atomic" version = "0.6.1" @@ -129,6 +172,32 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" + +[[package]] +name = "bincode" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36eaf5d7b090263e8150820482d5d93cd964a81e4019913c972f4edcc6edb740" +dependencies = [ + "bincode_derive", + "serde", + "unty", +] + +[[package]] +name = "bincode_derive" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf95709a440f45e986983918d0e8a1f30a9b1df04918fc828670606804ac3c09" +dependencies = [ + "virtue", +] + [[package]] name = "bit-set" version = "0.5.3" @@ -155,6 +224,22 @@ name = "bitflags" version = "2.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06" +dependencies = [ + "serde_core", +] + +[[package]] +name = "bitvec" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddcec3d12c579d40898fe0a9a358a803c23e9c52ca3c425707f81c9436211837" +dependencies = [ + "funty", + "radium", + "serde", + "tap", + "wyz", +] [[package]] name = "block-buffer" @@ -165,11 +250,25 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bstr" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bb31b46c14244e20ee9984b11bf5c992b91fb6939fea616e3512c8baecdbe5f" +dependencies = [ + "memchr", + "regex-automata", + "serde_core", +] + [[package]] name = "bumpalo" version = "3.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" +dependencies = [ + "allocator-api2", +] [[package]] name = "by_address" @@ -194,9 +293,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.4.7" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54413ede23c2daf518f35156dfde027feb2374004d63bd497f983c8db9c0e313" +checksum = "f360145194ee8e21db5ee7f3fcd4fe52210864c75c985dae33218202c8bbe040" dependencies = [ "find-msvc-tools", "shlex", @@ -287,6 +386,15 @@ dependencies = [ "error-code", ] +[[package]] +name = "cobs" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" +dependencies = [ + "thiserror 2.0.21", +] + [[package]] name = "colorchoice" version = "1.0.5" @@ -328,6 +436,21 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f8f80099a98041a3d1622845c271458a2d73e688351bf3cb999266764b81d48" +[[package]] +name = "countme" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7704b5fdd17b18ae31c4c1da5a2e0305a2bf17b5249300a9ee9ed7b72114c636" + +[[package]] +name = "cpp_demangle" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2bb79cb74d735044c972aae58ed0aaa9a837e85b01106a54c39e42e97f62253" +dependencies = [ + "cfg-if", +] + [[package]] name = "cpufeatures" version = "0.2.17" @@ -346,12 +469,188 @@ dependencies = [ "libc", ] +[[package]] +name = "cranelift-assembler-x64" +version = "0.132.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d521bdbc6098937af83ef4ab6d5c07398126bc71878f7ef4ea9499977978ef5" +dependencies = [ + "cranelift-assembler-x64-meta", +] + +[[package]] +name = "cranelift-assembler-x64-meta" +version = "0.132.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dde0b83164d4a497860af4236271178bc640512b067101e0853e4f74eaa4df5" +dependencies = [ + "cranelift-srcgen", +] + +[[package]] +name = "cranelift-bforest" +version = "0.132.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0111d110b72b4efad69a372e29e21628652fd0bcab66967e5c8350ab679affd5" +dependencies = [ + "cranelift-entity", + "wasmtime-internal-core", +] + +[[package]] +name = "cranelift-bitset" +version = "0.132.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf01ecc92fc5499789d79c3b817299d5a8ddd828d31dcf0f9cc3cc66f38dbb36" +dependencies = [ + "serde", + "serde_derive", + "wasmtime-internal-core", +] + +[[package]] +name = "cranelift-codegen" +version = "0.132.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cd2563bead0090c3879a7ff7327f7550c9d59bb5d05ecc8cd7886977aa3125a" +dependencies = [ + "bumpalo", + "cranelift-assembler-x64", + "cranelift-bforest", + "cranelift-bitset", + "cranelift-codegen-meta", + "cranelift-codegen-shared", + "cranelift-control", + "cranelift-entity", + "cranelift-isle", + "gimli 0.33.0", + "hashbrown 0.17.1", + "libm", + "log", + "pulley-interpreter", + "regalloc2", + "rustc-hash 2.1.3", + "serde", + "smallvec", + "target-lexicon", + "wasmtime-internal-core", +] + +[[package]] +name = "cranelift-codegen-meta" +version = "0.132.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9640d250d26f9381a73dc7f9862b27928d4809119aec73be0e556612e67b6a99" +dependencies = [ + "cranelift-assembler-x64-meta", + "cranelift-codegen-shared", + "cranelift-srcgen", + "heck", + "pulley-interpreter", +] + +[[package]] +name = "cranelift-codegen-shared" +version = "0.132.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a07f156b90efc94371ddb3536f76e4ab671ad2e093bfa5511e10198cadbb0c47" + +[[package]] +name = "cranelift-control" +version = "0.132.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75a76fd9dd37dcbc3d2d2e00abe3281a7e88adc035fba3b8114cc69981576ec" +dependencies = [ + "arbitrary", +] + +[[package]] +name = "cranelift-entity" +version = "0.132.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eea22522144ba08c7e7ef94bfc25d474ef016c2771974b8ab1986734ac85965" +dependencies = [ + "cranelift-bitset", + "serde", + "serde_derive", + "wasmtime-internal-core", +] + +[[package]] +name = "cranelift-frontend" +version = "0.132.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efa2826c80dff1d93b19b3cfbcaf9fae44c78d739a98d146dd5bd89c51e14367" +dependencies = [ + "cranelift-codegen", + "log", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cranelift-isle" +version = "0.132.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e238a69b95c5415456f22189494a12db94f313bb72b6ec9cc88ddf2f1056e28e" + +[[package]] +name = "cranelift-native" +version = "0.132.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eceb0ebd8d6aef6bb287e8d532a164b0db1c0062961211e9c22a91f67521c098" +dependencies = [ + "cranelift-codegen", + "libc", + "target-lexicon", +] + +[[package]] +name = "cranelift-srcgen" +version = "0.132.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "004643f39a7bec553de5263d650db30e5b9caec1d5cbe065fd732b7ec9ae40d0" + +[[package]] +name = "crc32fast" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01a7799fd6b852db0e61728dde9a204c423b44d689dbd432522543614b490e78" +dependencies = [ + "cfg-if", +] + [[package]] name = "critical-section" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" +[[package]] +name = "crossbeam-deque" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622f3fc73690be383c7214310406f28a90e6edeadc3cea882f9d71e495b9711a" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc74980687109a3b14c72fd458107bf0baa1da1a1a805e178d15501ba9b86d9d" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31eee39dddec8330830986fcd7625edb5a24ec90ea038215273bbc3adb08ac6" + [[package]] name = "crossterm" version = "0.29.0" @@ -399,6 +698,12 @@ dependencies = [ "phf", ] +[[package]] +name = "daachorse" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd10668980c9e7ba8aa2e616207d9ec52f7db66ebb47db857ed1f3c342530dad" + [[package]] name = "darling" version = "0.24.1" @@ -546,6 +851,7 @@ dependencies = [ "shell-words", "toml", "tui-input", + "yara-x", ] [[package]] @@ -554,15 +860,26 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34" +[[package]] +name = "embedded-io" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" + +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + [[package]] name = "encoding_rs" -version = "0.8.41" +version = "0.8.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5ef0006ac9ab233c38522f5ae99cae3625151de8f706cacee1cba4b8e2832a" +checksum = "8e985e0451871ad22fb8d2b6b076e2028a502a0d3950998c2c5c0a4f9b5d9679" dependencies = [ "cfg-if", "core_detect", - "multiversion", "multiversion_no_op", "rustversion", "scopeguard", @@ -606,6 +923,12 @@ version = "13.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25929004897f2bbab309121a60400d36992f6d911d09baa6c172f6cc55706601" +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + [[package]] name = "fancy-regex" version = "0.11.0" @@ -629,9 +952,9 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef25905e51abafe4dcea6c15fec58c57b601cdbd0ee53d22ea1d3016c587d39b" +checksum = "aedcfb3409746eddb02b9e19ebda1c3394f759a152e48ee875a0844d1b955484" [[package]] name = "finl_unicode" @@ -657,6 +980,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + [[package]] name = "futures-core" version = "0.3.34" @@ -708,8 +1037,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] @@ -736,6 +1067,53 @@ dependencies = [ "rand_core 0.10.1", ] +[[package]] +name = "gimli" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" +dependencies = [ + "fallible-iterator", + "indexmap", + "stable_deref_trait", +] + +[[package]] +name = "gimli" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf7f043f89559805f8c7cacc432749b2fa0d0a0a9ee46ce47164ed5ba7f126c" +dependencies = [ + "fnv", + "hashbrown 0.16.1", + "indexmap", + "stable_deref_trait", +] + +[[package]] +name = "globset" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07c34a9410465b45bd9787443bc7370f37735bad04b0f0cd57ff1a3186c98988" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "globwalk" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757" +dependencies = [ + "bitflags 2.13.2", + "ignore", + "walkdir", +] + [[package]] name = "goblin" version = "0.10.7" @@ -747,6 +1125,12 @@ dependencies = [ "scroll", ] +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + [[package]] name = "hashbrown" version = "0.16.1" @@ -756,6 +1140,8 @@ dependencies = [ "allocator-api2", "equivalent", "foldhash", + "serde", + "serde_core", ] [[package]] @@ -767,6 +1153,8 @@ dependencies = [ "allocator-api2", "equivalent", "foldhash", + "serde", + "serde_core", ] [[package]] @@ -805,12 +1193,34 @@ dependencies = [ "cc", ] +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + [[package]] name = "ident_case" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +[[package]] +name = "ignore" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b69833ed729dc5aa7d19541d96d6cf8e9137194207a04916d658e43168402f" +dependencies = [ + "crossbeam-deque", + "globset", + "log", + "memchr", + "regex-automata", + "same-file", + "walkdir", + "winapi-util", +] + [[package]] name = "indexmap" version = "2.14.2" @@ -819,6 +1229,8 @@ checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855" dependencies = [ "equivalent", "hashbrown 0.17.1", + "serde", + "serde_core", ] [[package]] @@ -832,9 +1244,9 @@ dependencies = [ [[package]] name = "instability" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bf84e73fa6f27f299dec58e13223cf70db80da872eb921d4f6138342a0eabc8" +checksum = "4c3b5acc1e2fd9375041a388da33d1eb8aed5f7a8c0dd3543e3ea2805adfbe20" dependencies = [ "darling", "indoc", @@ -844,16 +1256,40 @@ dependencies = [ ] [[package]] -name = "is_terminal_polyfill" -version = "1.70.2" +name = "intaglio" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" +checksum = "8eca9188c1b20836bb561bc09bb2f54e9ca99b271031b5f3ff183a00c08c98c8" [[package]] -name = "itertools" -version = "0.14.0" +name = "inventory" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +checksum = "a4f0c30c76f2f4ccee3fe55a2435f691ca00c0e4bd87abe4f4a851b1d4dac39b" +dependencies = [ + "rustversion", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b4baf93f58d4425749ca49a51c50ebab072c5df6994d08fed93541c331481dc" dependencies = [ "either", ] @@ -866,9 +1302,9 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "js-sys" -version = "0.3.105" +version = "0.3.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce57d20d1ea864ce2ac172ab472d409214f4fd359f0b2a2775abdf522e2af99e" +checksum = "7883d941dae510fb2d978fc3fe018c71c9e2892fd38854de3e8b92c2e5ad9cc5" dependencies = [ "cfg-if", "futures-util", @@ -883,7 +1319,7 @@ checksum = "bde5057d6143cc94e861d90f591b9303d6716c6b9602309150bd068853c10899" dependencies = [ "hashbrown 0.16.1", "portable-atomic", - "thiserror 2.0.20", + "thiserror 2.0.21", ] [[package]] @@ -898,6 +1334,18 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[package]] +name = "leb128" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c83bff1d572d6b9aeef67ddfc8448e4a3737909cb28e81f97c791b9018703e52" + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + [[package]] name = "libc" version = "0.2.189" @@ -912,9 +1360,9 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libredox" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6480ccc157a1389bb2e4891b24751b0f798ba640d22386f23143fbcc89da195a" +checksum = "61ff90caf6077a803a240f62fdbe88645a890bbca49ef8174c3cb0404362171d" dependencies = [ "libc", ] @@ -955,11 +1403,43 @@ version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" +[[package]] +name = "logos" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb2c55a318a87600ea870ff8c2012148b44bf18b74fad48d0f835c38c7d07c5f" +dependencies = [ + "logos-derive", +] + +[[package]] +name = "logos-codegen" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58b3ffaa284e1350d017a57d04ada118c4583cf260c8fb01e0fe28a2e9cf8970" +dependencies = [ + "fnv", + "proc-macro2", + "quote", + "regex-automata", + "regex-syntax", + "syn 2.0.119", +] + +[[package]] +name = "logos-derive" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52d3a9855747c17eaf4383823f135220716ab49bea5fbea7dd42cc9a92f8aa31" +dependencies = [ + "logos-codegen", +] + [[package]] name = "lru" -version = "0.18.4" +version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff9840bcc50b71349309900da0ce7279aa336ae71d73250b07998932c7d97c25" +checksum = "ef9ac18847474e638e3702b76c65d4eb93428471a74778ef0f1be711717f89b5" dependencies = [ "hashbrown 0.17.1", ] @@ -974,12 +1454,30 @@ dependencies = [ "winapi", ] +[[package]] +name = "mach2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d640282b302c0bb0a2a8e0233ead9035e3bed871f0b7e81fe4a1ec829765db44" +dependencies = [ + "libc", +] + [[package]] name = "memchr" version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" +[[package]] +name = "memfd" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57804b2c9b69967f1536a56f86297e367a33b19e98852ed624b84551cdbc0d90" +dependencies = [ + "rustix", +] + [[package]] name = "memmap2" version = "0.7.1" @@ -989,6 +1487,15 @@ dependencies = [ "libc", ] +[[package]] +name = "memmap2" +version = "0.9.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1219ed1b7f229ee7104d281dd01d6802fe28bb6e95d292942c4daacdeb798c0" +dependencies = [ + "libc", +] + [[package]] name = "memmem" version = "0.1.1" @@ -1032,32 +1539,11 @@ dependencies = [ "cfg-if", "libc", "log", - "memmap2", + "memmap2 0.7.1", "parking_lot", "thiserror 1.0.69", ] -[[package]] -name = "multiversion" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ca4bea16ffc3f443cf7d866912118196bfef4c6a1556ca00f9f9b00bb43f7c" -dependencies = [ - "multiversion-macros", -] - -[[package]] -name = "multiversion-macros" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d416831a7317ef4b08bee00b69cbbb9c8763da7959a7026244d6266869f9c83" -dependencies = [ - "proc-macro2", - "quote", - "rustversion", - "syn 3.0.6", -] - [[package]] name = "multiversion_no_op" version = "1.0.0" @@ -1195,6 +1681,18 @@ dependencies = [ "objc2-core-foundation", ] +[[package]] +name = "object" +version = "0.39.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e5a6c098c7a3b6547378093f5cc30bc54fd361ce711e05293a5cc589562739b" +dependencies = [ + "crc32fast", + "hashbrown 0.17.1", + "indexmap", + "memchr", +] + [[package]] name = "once_cell" version = "1.21.4" @@ -1280,9 +1778,9 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pest" -version = "2.9.1" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d45aeb61b4bf818e12d4205f2466f8c4748f85f4fce0146d1c03d69d753f0ad" +checksum = "45d3aca230fad2e6f6317ca0a72724338c4960cb97168a85cdee66df4a9a21a8" dependencies = [ "memchr", "ucd-trie", @@ -1290,9 +1788,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.9.1" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89cc5a242e25ed4e7704d0be240f2cfbe20a8c27e7e252d94835be93d92dc39f" +checksum = "284b60557f2c4a2e72ad3f2d34d42685a2fa4a6a61d0d2a10c0ae2a5e916c2cf" dependencies = [ "pest", "pest_generator", @@ -1300,9 +1798,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.9.1" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7abf21475cc3820fe4b2ca2dc2142902f67a02189f3b5b3a229f4febc01a43e5" +checksum = "1d9d1f08a115309ee99268cf85e5228e0e56aa9caf8841ec12866b6be07c3109" dependencies = [ "pest", "pest_meta", @@ -1313,9 +1811,9 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.9.1" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adba4db388f687393c18c51348d44a41d870ca9df71a2c98172ea3035dc6936e" +checksum = "ed93ba1a9ffcca32130a5188701c81c0c49cf00d4b7c5007d5148951d743adcb" dependencies = [ "pest", ] @@ -1390,6 +1888,18 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85" +[[package]] +name = "postcard" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24" +dependencies = [ + "cobs", + "embedded-io 0.4.0", + "embedded-io 0.6.1", + "serde", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -1415,6 +1925,49 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "protobuf" +version = "3.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d65a1d4ddae7d8b5de68153b48f6aa3bba8cb002b243dbdbc55a5afbc98f99f4" +dependencies = [ + "once_cell", + "protobuf-support", + "thiserror 1.0.69", +] + +[[package]] +name = "protobuf-support" +version = "3.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e36c2f31e0a47f9280fb347ef5e461ffcd2c52dd520d8e216b52f93b0b0d7d6" +dependencies = [ + "thiserror 1.0.69", +] + +[[package]] +name = "pulley-interpreter" +version = "45.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b04adf8f93264685f2c70a3edb8f828c791e71b22659253319c33f29d1165aef" +dependencies = [ + "cranelift-bitset", + "log", + "pulley-macros", + "wasmtime-internal-core", +] + +[[package]] +name = "pulley-macros" +version = "45.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c120a6af1a574a42efcd9df2ad27cb78bc04118c5e0c69e90599f17301a1c7a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + [[package]] name = "quote" version = "1.0.47" @@ -1436,6 +1989,12 @@ version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + [[package]] name = "rand" version = "0.8.8" @@ -1494,13 +2053,13 @@ dependencies = [ "compact_str", "critical-section", "hashbrown 0.17.1", - "itertools", + "itertools 0.14.0", "kasuari", "lru", "palette", "serde", "strum", - "thiserror 2.0.20", + "thiserror 2.0.21", "unicode-segmentation", "unicode-truncate", "unicode-width", @@ -1559,7 +2118,7 @@ dependencies = [ "hashbrown 0.17.1", "indoc", "instability", - "itertools", + "itertools 0.14.0", "line-clipping", "ratatui-core", "serde", @@ -1589,6 +2148,20 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "regalloc2" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "757712e8e61590d6d4f5d563483755538b5aa13467837a3b41cd9832509a7f85" +dependencies = [ + "allocator-api2", + "bumpalo", + "hashbrown 0.17.1", + "log", + "rustc-hash 2.1.3", + "smallvec", +] + [[package]] name = "regex" version = "1.13.1" @@ -1618,6 +2191,37 @@ version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" +[[package]] +name = "rowan" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14b574c58582fa59fa43a2feb6608b8744184659f08a2e0117e4b8224d95ed61" +dependencies = [ + "countme", + "hashbrown 0.14.5", + "memoffset", + "rustc-hash 1.1.0", + "text-size", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b74b56ffa8bb2830709a538c2cbcae9aa062db0d2a42563bfb09bdaae44020eb" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-hash" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" + [[package]] name = "rustc_version" version = "0.4.1" @@ -1652,6 +2256,15 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -1714,6 +2327,20 @@ dependencies = [ "syn 3.0.6", ] +[[package]] +name = "serde_json" +version = "1.0.151" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" +dependencies = [ + "indexmap", + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + [[package]] name = "serde_spanned" version = "1.1.1" @@ -1785,9 +2412,9 @@ checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" [[package]] name = "siphasher" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" +checksum = "33f4fe9184a62d842c9ef383018f3306d8ba224fd9d836f56d7288308847c256" [[package]] name = "slab" @@ -1797,9 +2424,18 @@ checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "smallvec" -version = "1.16.1" +version = "1.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9395f0f0eee849a9b707b2f06bb92a6a422090e2123bb2ef8e87a0e61892a8e" +dependencies = [ + "serde", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba467056f1b547ed52077911161fc86985becbc60e8e1857c8a144dab0def891" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "static_assertions" @@ -1867,6 +2503,27 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "target-lexicon" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca" + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + [[package]] name = "termina" version = "0.3.3" @@ -1908,7 +2565,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4676b37242ccbd1aabf56edb093a4827dc49086c0ffd764a5705899e0f35f8f7" dependencies = [ "anyhow", - "base64", + "base64 0.22.1", "bitflags 2.13.2", "fancy-regex", "filedescriptor", @@ -1943,6 +2600,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "text-size" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f18aa187839b2bdb1ad2fa35ead8c4c2976b64e4363c386d45ac0f7ee85c9233" + [[package]] name = "thiserror" version = "1.0.69" @@ -1954,11 +2617,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.20" +version = "2.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f" +checksum = "09e52cb86a36cede5cb101bf8908837b3e4c6e5e59fe7fd85c23fb56200d189e" dependencies = [ - "thiserror-impl 2.0.20", + "thiserror-impl 2.0.21", ] [[package]] @@ -1974,9 +2637,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.20" +version = "2.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" +checksum = "fe5197923287db20a58125f0bc85c062f7f2c892de97b18c356f9efb14b28524" dependencies = [ "proc-macro2", "quote", @@ -2045,9 +2708,9 @@ checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2" [[package]] name = "tui-input" -version = "0.15.4" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eb7c7ddaef6dcc58fae905d0f89a3df49ef77e93822df7447a5bb0babc9ece3" +checksum = "a4fa0cb7933d19f91eb3109781bf7297d5e4cba57e857d97a29b9465d3383f56" dependencies = [ "ratatui", "unicode-segmentation", @@ -2084,7 +2747,7 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16b380a1238663e5f8a691f9039c73e1cdae598a30e9855f541d29b08b53e9a5" dependencies = [ - "itertools", + "itertools 0.14.0", "unicode-segmentation", "unicode-width", ] @@ -2095,6 +2758,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" +[[package]] +name = "unty" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae" + [[package]] name = "utf8parse" version = "0.2.2" @@ -2119,6 +2788,12 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "virtue" +version = "0.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "051eb1abcf10076295e815102942cc58f9d5e3b4560e46e53c21e8ff6f3af7b1" + [[package]] name = "vtparse" version = "0.6.2" @@ -2128,6 +2803,44 @@ dependencies = [ "utf8parse", ] +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "walrus" +version = "0.26.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25b63a2bc6e4acb4cf69080068a531bad4530791098230e972ffcb64fd1dc266" +dependencies = [ + "anyhow", + "gimli 0.32.3", + "id-arena", + "leb128", + "log", + "walrus-macro", + "wasm-encoder 0.245.1", + "wasmparser 0.245.1", +] + +[[package]] +name = "walrus-macro" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a9b0525d7ea6e5f906aca581a172e5c91b4c595290dfa8ad4a2bc9ffef33b44" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.119", +] + [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" @@ -2145,9 +2858,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.128" +version = "0.2.129" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aecb87a33d3b0c5e3b7aa46336eaf486cffafbd281b195e4c8b80d50df2351bf" +checksum = "9bb54f33acc68fd454578d9820b0bde1a1a3d17aa17bb7b6595806d02886d409" dependencies = [ "cfg-if", "once_cell", @@ -2158,9 +2871,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.128" +version = "0.2.129" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a690d511e3c1a8b3a55e33511e3c2c00c78415cd23650f32b808627f5696b9ed" +checksum = "2e29d0c35b16e224a7eeb5cd2d25e3e1968fbd65604117b44d3b789d00ee8535" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2168,9 +2881,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.128" +version = "0.2.129" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "411e4887f0071ef2d2164a9d5fdf2d20efbef78fccd3a78b0c10a1dc5295e48a" +checksum = "6f501a8bc3719dba86ef8ae4728879c08001bea749eb1333ac5b91e040e2a6b7" dependencies = [ "bumpalo", "proc-macro2", @@ -2181,13 +2894,235 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.128" +version = "0.2.129" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81941cd78d0c92026c33e5e01312845a4cb1e9af3407f9134b100dd03144103e" +checksum = "23f0c9c52aa7cd7d77769a4cfe2a9adb1b331f489a41d912ce14513d5ab995c6" dependencies = [ "unicode-ident", ] +[[package]] +name = "wasm-encoder" +version = "0.245.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9dca005e69bf015e45577e415b9af8c67e8ee3c0e38b5b0add5aa92581ed5c" +dependencies = [ + "leb128fmt", + "wasmparser 0.245.1", +] + +[[package]] +name = "wasm-encoder" +version = "0.248.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac92cf547bc18d27ecc521015c08c353b4f18b84ab388bb6d1b6b682c620d9b6" +dependencies = [ + "leb128fmt", + "wasmparser 0.248.0", +] + +[[package]] +name = "wasmparser" +version = "0.245.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f08c9adee0428b7bddf3890fc27e015ac4b761cc608c822667102b8bfd6995e" +dependencies = [ + "bitflags 2.13.2", + "hashbrown 0.16.1", + "indexmap", + "semver", + "serde", +] + +[[package]] +name = "wasmparser" +version = "0.248.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa4439c5eee9df71ee0c6efb37f63b1fcb1fec38f85f5142c54e7ed05d33091a" +dependencies = [ + "bitflags 2.13.2", + "hashbrown 0.17.1", + "indexmap", + "semver", + "serde", +] + +[[package]] +name = "wasmprinter" +version = "0.248.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b264a5410b008d4d199a92bf536eae703cbd614482fc1ec53831cf19e1c183" +dependencies = [ + "anyhow", + "termcolor", + "wasmparser 0.248.0", +] + +[[package]] +name = "wasmtime" +version = "45.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a83ef84ac8bab735c89e27b0268b0586099fbe81281ae45be2b8e4f407b2daa" +dependencies = [ + "addr2line", + "async-trait", + "bitflags 2.13.2", + "bumpalo", + "cc", + "cfg-if", + "libc", + "log", + "mach2", + "memfd", + "object", + "once_cell", + "postcard", + "pulley-interpreter", + "rustix", + "serde", + "serde_derive", + "smallvec", + "target-lexicon", + "wasmparser 0.248.0", + "wasmtime-environ", + "wasmtime-internal-core", + "wasmtime-internal-cranelift", + "wasmtime-internal-fiber", + "wasmtime-internal-jit-debug", + "wasmtime-internal-jit-icache-coherence", + "wasmtime-internal-unwinder", + "wasmtime-internal-versioned-export-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "wasmtime-environ" +version = "45.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439686d3deb38525c86b88bbc90f7ba669f70c8edc7d6b35147c738bbef5ff76" +dependencies = [ + "anyhow", + "cpp_demangle", + "cranelift-bforest", + "cranelift-bitset", + "cranelift-entity", + "gimli 0.33.0", + "hashbrown 0.17.1", + "indexmap", + "log", + "object", + "postcard", + "rustc-demangle", + "serde", + "serde_derive", + "sha2", + "smallvec", + "target-lexicon", + "wasm-encoder 0.248.0", + "wasmparser 0.248.0", + "wasmprinter", + "wasmtime-internal-core", +] + +[[package]] +name = "wasmtime-internal-core" +version = "45.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3073c03f97f871fe6400e68621c863b93ba79296f8e570285231952d23fcc804" +dependencies = [ + "hashbrown 0.17.1", + "libm", + "serde", +] + +[[package]] +name = "wasmtime-internal-cranelift" +version = "45.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a15981261d361f9c93b42929f446b4009840853ceea181ad6e17730f4016a0b" +dependencies = [ + "cfg-if", + "cranelift-codegen", + "cranelift-control", + "cranelift-entity", + "cranelift-frontend", + "cranelift-native", + "gimli 0.33.0", + "itertools 0.14.0", + "log", + "object", + "pulley-interpreter", + "smallvec", + "target-lexicon", + "thiserror 2.0.21", + "wasmparser 0.248.0", + "wasmtime-environ", + "wasmtime-internal-core", + "wasmtime-internal-unwinder", + "wasmtime-internal-versioned-export-macros", +] + +[[package]] +name = "wasmtime-internal-fiber" +version = "45.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33aed9d91d54c9f861ba7e3c3130bded8a7a63e01fe58c3a9a36b45d6ee0fb57" +dependencies = [ + "cc", + "cfg-if", + "libc", + "rustix", + "wasmtime-environ", + "wasmtime-internal-versioned-export-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "wasmtime-internal-jit-debug" +version = "45.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "944942118aab67e7b4febfe88a65d0af4cfd10af8ed0e79fd6cf39d75359ab7e" +dependencies = [ + "cc", + "wasmtime-internal-versioned-export-macros", +] + +[[package]] +name = "wasmtime-internal-jit-icache-coherence" +version = "45.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37dee05e8c35759826f6b926cd949c51f771b6a58619d8e60c63c3f3d3e5e59b" +dependencies = [ + "cfg-if", + "libc", + "wasmtime-internal-core", + "windows-sys 0.61.2", +] + +[[package]] +name = "wasmtime-internal-unwinder" +version = "45.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a83f7ebe911a6f1605ff0d3a678472ddb1fcc57c3e2f6bae5dd4d170b625b3ed" +dependencies = [ + "cfg-if", + "cranelift-codegen", + "log", + "object", + "wasmtime-environ", +] + +[[package]] +name = "wasmtime-internal-versioned-export-macros" +version = "45.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75980644456dc003238766254b0e5f002704630237ccd0728747991fe2739d7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + [[package]] name = "wezterm-bidi" version = "0.2.3" @@ -2276,6 +3211,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -2436,6 +3380,15 @@ version = "0.57.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + [[package]] name = "x11rb" version = "0.13.2" @@ -2458,3 +3411,82 @@ name = "yansi" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + +[[package]] +name = "yara-x" +version = "1.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "015b06cc60800856d04c3115ef6b7c6cbb95f59f20a9df4358dd2f85f3d5b391" +dependencies = [ + "annotate-snippets", + "anyhow", + "base64 0.23.1", + "bincode", + "bitflags 2.13.2", + "bitvec", + "bstr", + "daachorse", + "getrandom 0.2.17", + "globwalk", + "hex", + "indexmap", + "intaglio", + "inventory", + "itertools 0.15.0", + "js-sys", + "memchr", + "memmap2 0.9.11", + "num-derive", + "num-traits", + "protobuf", + "regex", + "regex-automata", + "regex-syntax", + "rustc-hash 2.1.3", + "serde", + "serde_json", + "smallvec", + "strum_macros", + "thiserror 2.0.21", + "walrus", + "wasm-bindgen", + "wasmtime", + "yara-x-macros", + "yara-x-parser", +] + +[[package]] +name = "yara-x-macros" +version = "1.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a120badca58e15c6cebaff85386c23b75c31f78c06ccb68d24bd7a27293563ed" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "yara-x-parser" +version = "1.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20ace34cd6497968bfcb513c7bc2c3712b559e3fd1332f81ae49593da88cc5c" +dependencies = [ + "ascii_tree", + "bitflags 2.13.2", + "bstr", + "indexmap", + "itertools 0.15.0", + "logos", + "num-traits", + "rowan", + "rustc-hash 2.1.3", + "serde", +] + +[[package]] +name = "zmij" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" diff --git a/Cargo.toml b/Cargo.toml index a15a3d6..149ed66 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ encoding_rs = "0.8.35" evalexpr = "13.1.*" goblin = "0.10.5" hex = "0.4.3" -memchr = "2.8.0" +memchr = "2.8.3" mmap-io = "1.0.0" rand = "0.10.1" ratatui = "0.30.0" @@ -29,6 +29,7 @@ serde = { version = "1.0.228", features = ["derive"] } shell-words = "1.1.0" toml = "1.0.1" tui-input = "0.15.0" +yara-x = { version = "1.20.0", default-features = false } # The profile that 'dist' will build with [profile.dist] diff --git a/README.md b/README.md index ee77896..8773812 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Fast Vim-inspired hex editor for the terminal - Edit in hex or ASCII - String list with regex filtering - Multiple smart ways to navigate through a file -- Find strings and hex bytes +- Find strings and hex patterns - Add comments and bookmarks - Mark blocks with colors - PE/ELF headers parsing (WIP -- not released yet) @@ -100,25 +100,29 @@ Once you load a file in **dz6**, you can use the commands below. #### Commands -| Command | Action | Parameters | Tips/Examples | -| ---------------- | ---------------------------------------------------------------- | ------------------------- | ------------------------------------------------------------------------------------------------- | -| `` | Go to offset | | hex default; `t` suffix = decimal; `+` prefix = incremental jump; `-` prefix = decremental jump | -| `cmt` | Add `` to `` | `` `` | `cmt 1000 "my comment"` (comment at offset 0x1000; offset obeys the same rules above) | -| `sel` | Select `` bytes from `` | `` `` | `sel 40 10t` (select 10 bytes from offset 0x40) | -| `set byteline` | Set the number of bytes per line | `` or `auto` | `set byteline 8` (default is 16; `auto` enables automatic setting based on screen width) | -| `set ctrlchar` | Set the character shown in the ASCII dump for non-graphic values | `` | `set ctrlchar " "` would set a blankspace (default: `.`) | -| `set db` | Turn on database file saving/loading (default) | | A database file with a `.dz6` extension will be used to store bookmarks and comments for the file | -| `set nodb` | Turn off database file saving/loading | | | -| `set dimzero` | Dim (gray out) null bytes only (default) | | | -| `set dimctrl` | Dim all control characters | | All non-graphic characters will be dimmed | -| `set nodim` | Turn off byte dimming | | | -| `set theme` | Set the theme | `dark` or `light` | `set theme light` (default: `dark`) | -| `set view` | Changes the current view | `text`, `hex` or `header` | `set view header` (default: `hex`) | -| `set wrapscan` | Enable search results wrap | | | -| `set nowrapscan` | Disable search results wrap | | | -| `w` | Write changes to file | | | -| `wq` or `x` | Write changes to file and quit | | | -| `q` | Quit without saving changes | | In replace mode, `T` (truncate) is an exception because it modifies the file immediately. | +| Command | Action | Parameters | Tips/Examples | +| ------------------ | ---------------------------------------------------------------- | ------------------------- | ----------------------------------------------------------------------------------------------------------- | +| `` | Go to offset | | hex default; `t` suffix = decimal; `+` prefix = incremental jump; `-` prefix = decremental jump | +| `cmt` | Add `` to `` | `` `` | `cmt 1000 "my comment"` (comment at offset 0x1000; offset obeys the same rules above) | +| `sel` | Select `` bytes from `` | `` `` | `sel 40 10t` (select 10 bytes from offset 0x40) | +| `set byteline` | Set the number of bytes per line | `` or `auto` | `set byteline 8` (default is 16; `auto` enables automatic setting based on screen width) | +| `set ctrlchar` | Set the character shown in the ASCII dump for non-graphic values | `` | `set ctrlchar " "` would set a blankspace (default: `.`) | +| `set db` | Turn on database file saving/loading (default) | | A database file with a `.dz6` extension will be used to store bookmarks and comments for the file | +| `set nodb` | Turn off database file saving/loading | | | +| `set dimzero` | Dim (gray out) null bytes only (default) | | | +| `set dimctrl` | Dim all control characters | | All non-graphic characters will be dimmed | +| `set nodim` | Turn off byte dimming | | | +| `set theme` | Set the theme | `dark` or `light` | `set theme light` (default: `dark`) | +| `set view` | Changes the current view | `text`, `hex` or `header` | `set view header` (default: `hex`) | +| `set wrapscan` | Enable search results wrap | | `set ws` also works | +| `set nowrapscan` | Disable search results wrap | | `set nows` also works | +| `set ignorecase` | Enable case-insensitive search (ASCII only) | | `set ic` also works | +| `set noignorecase` | Disable case-insensitive search | | `set noic` also works | +| `set smartcase` | Enable smart case search | | if the search term contains a capital letter and `ignorecase` is enabled, disable it. `set scs` also works. | +| `set nosmartcase` | Disable smart case search | | `set noscs` also works | +| `w` | Write changes to file | | | +| `wq` or `x` | Write changes to file and quit | | | +| `q` | Quit without saving changes | | In replace mode, `T` (truncate) is an exception because it modifies the file immediately. | > If you need permanent settings, create a `$HOME/.dz6init` file containing any of the commands above, one per line. dz6 will load that at startup. Lines starting with `#` are ignored. @@ -144,9 +148,9 @@ This is the main view you would expect from a hex editor. It displays file offse | `Ctrl+x` | Decrement byte under the cursor | | | `v` | Enter [select mode](#hex-selection-mode) | | | `u` | Undo the last change made to the buffer | Use it _before_ writing to the file (`:w`) | -| `/` | Search (forward) | Search the entire file. `Tab` cycles between ASCII and hex search | +| `/` | Enter [search mode](#hex-search-mode) (forward) | Search the entire file. `Tab` cycles between UTF-8 and hex search | | `n` | Search next (forward) | | -| `?` | Search (backward) | Search the entire file. `Tab` cycles between ASCII and hex search | +| `?` | Search (backward) | Search the entire file. `Tab` cycles between UTF-8 and hex search | | `N` | Search next (backward) | | | `s` | Open [Strings](#strings) window | | | `Backspace` | Go to the previously visited offset | This is useful after a Go to command, for example | @@ -172,6 +176,25 @@ This is the main view you would expect from a hex editor. It displays file offse | `Alt+m` | Mark a block with a random color | `Alt+m` again to pick another color. `[` and `]` to navigate to block boundaries | | `Esc` | Go back to normal mode | | +#### Hex search mode + +This mode has two sub-modes: UTF-8 and HEX. The first is straightforward: search for UTF-8 strings, +but if `ignorecase` is enabled, it will only work with the ASCII character set. + +The HEX sub-mode supports [YARA-X patterns](https://virustotal.github.io/yara-x/docs/writing_rules/hex-patterns/): + +`f0f0ca` or `F0F0CA` match `f0f0ca` only (no pattern matching in this case). + +`b0 ?? ca` - `??` means "any byte", so this would match `b0b0ca`, `b0ffca`, `b042ca`... + +`b? b? ca` - a single `?` means "any nibble". This would match `b0b0ca` and `babaca` for example. + +`ba (b0|ba) ca` matches `bab0bca` or `babaca`. + +`f0 [1-3] ca` matches a sequence starting with 0xf0, followed by 1, 2, or 3 bytes of any value, then followed by 0xca. + +`b0 ~b0 ca` negation, meaning 0xb0 followed by anything but 0xb0, followed 0xca. + #### Hex replace mode | Key | Action | Tips | diff --git a/src/app.rs b/src/app.rs index 74ac231..8198a61 100644 --- a/src/app.rs +++ b/src/app.rs @@ -104,7 +104,9 @@ impl App { hex_mode_non_graphic_char: '.', maximum_strings_to_show: 3000, minimum_string_length: 4, - search_wrap: true, + search_wrapscan: false, + search_ignorecase: false, + search_smartcase: false, theme: DARK, // hex_mode_dword_separator: '-', // text_mode_tab_spaces: 4, diff --git a/src/commands.rs b/src/commands.rs index 99030fd..9575a1c 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -235,13 +235,33 @@ pub fn parse_command(app: &mut App, cmdline: &str) { } } } - // saarch wrap - "wrapscan" => { - app.config.search_wrap = true; + // search wrap + "wrapscan" | "ws" => { + app.config.search_wrapscan = true; app.dialog_renderer = None; } - "nowrapscan" => { - app.config.search_wrap = false; + "nowrapscan" | "nows" => { + app.config.search_wrapscan = false; + app.dialog_renderer = None; + } + // search ignorecase + "ignorecase" | "ic" => { + app.config.search_ignorecase = true; + app.dialog_renderer = None; + } + "noignorecase" | "noic" => { + app.config.search_ignorecase = false; + app.dialog_renderer = None; + } + // search smartcase + // if ignorecase == true and needle contains a capital letter, + // then ignorecase will be set to false + "smartcase" | "scs" => { + app.config.search_smartcase = true; + app.dialog_renderer = None; + } + "nosmartcase" | "noscs" => { + app.config.search_smartcase = false; app.dialog_renderer = None; } // view diff --git a/src/config.rs b/src/config.rs index 72d6846..09a4156 100644 --- a/src/config.rs +++ b/src/config.rs @@ -13,7 +13,9 @@ pub struct Config { pub hex_mode_non_graphic_char: char, pub maximum_strings_to_show: usize, pub minimum_string_length: usize, - pub search_wrap: bool, + pub search_wrapscan: bool, + pub search_ignorecase: bool, + pub search_smartcase: bool, pub theme: Theme, // pub hex_mode_dword_separator: char, // pub text_mode_tab_spaces: usize, diff --git a/src/hex/events.rs b/src/hex/events.rs index 85c5c22..f292387 100644 --- a/src/hex/events.rs +++ b/src/hex/events.rs @@ -38,19 +38,20 @@ pub fn hex_mode_events(app: &mut App, key: KeyEvent) -> Result { && !app.hex_view.search.input_text.value().is_empty() { let needle = app.hex_view.search.input_text.value().to_string(); - ofs = hex::search::search(app, needle); + + ofs = hex::search::search(app, &needle, true); } else if app.hex_view.search.mode == hex::search::SearchMode::Hex && !app.hex_view.search.input_hex.value().is_empty() { let hex_string = app.hex_view.search.input_hex.value().to_string(); - if let Some(by) = hex::search::hex_string_to_u8(&hex_string) { - ofs = hex::search::search(app, &by) - } + ofs = hex::search::search(app, &hex_string, true) } if let Some(ofs) = ofs { app.goto(ofs); + } else { + beep!(); } } } diff --git a/src/hex/search.rs b/src/hex/search.rs index 04f71df..c285a44 100644 --- a/src/hex/search.rs +++ b/src/hex/search.rs @@ -1,11 +1,13 @@ use crate::widgets::{Message, MessageType}; use crate::{app::App, editor::UIState}; + use ratatui::Frame; use ratatui::crossterm::event::{Event, KeyCode}; use ratatui::widgets::Paragraph; use std::io::Result; use tui_input::Input; use tui_input::backend::crossterm::EventHandler; +use yara_x; #[derive(Default, Debug)] pub struct Search { @@ -41,7 +43,7 @@ pub enum SearchDirection { Backward, } -pub fn hex_string_to_u8(hex_string: &str) -> Option> { +fn hex_string_to_u8(hex_string: &str) -> Option> { if hex_string.is_empty() || !hex_string.len().is_multiple_of(2) { return None; } @@ -49,7 +51,147 @@ pub fn hex_string_to_u8(hex_string: &str) -> Option> { Some(bytes) } -pub fn search>(app: &mut App, needle: T) -> Option { +/// returns the fist occurrence of needle, ignoring case (ASCII only for now) +/// so, it won't work with utf-8 characters for example +fn find_nocase(haystack: &[u8], needle: &[u8]) -> Option { + // get first needle char and return early if we can't / needle.len() == 0 + let first = needle.first()?; + let needle_len = needle.len(); + + // we can't search for a needle bigger than the haystack + if needle_len > haystack.len() { + return None; + } + + if first.is_ascii_alphabetic() { + // if needle's first letter is alphabetic, we use memchr2_iter to find both variants + for pos in memchr::memchr2_iter( + first.to_ascii_lowercase(), + first.to_ascii_uppercase(), + haystack, + ) { + let mut matches = 1; // number of matching characters + + for c in needle.iter().skip(1) { + if !c.eq_ignore_ascii_case(haystack.get(pos + matches)?) { + break; + } + + matches += 1; + } + + if matches == needle_len { + return Some(pos); + } + } + } else { + // otherwise we call memchr_iter, which is faster + for pos in memchr::memchr_iter(*first, haystack) { + // same logic + let mut matches = 1; + + for c in needle.iter().skip(1) { + if !c.eq_ignore_ascii_case(haystack.get(pos + matches)?) { + break; + } + + matches += 1; + } + + if matches == needle_len { + return Some(pos); + } + } + } + + None +} + +fn rfind_nocase(haystack: &[u8], needle: &[u8]) -> Option { + // get first needle char and return early if we can't / needle.len() == 0 + let first = needle.first()?; + let needle_len = needle.len(); + + // we can't search for a needle bigger than the haystack + if needle_len > haystack.len() { + return None; + } + + if first.is_ascii_alphabetic() { + // if needle's first letter is alphabetic, we use memchr2_iter to find both variants + for pos in memchr::memrchr2_iter( + first.to_ascii_lowercase(), + first.to_ascii_uppercase(), + haystack, + ) { + let mut matches = 1; // number of matching characters + + for c in needle.iter().skip(1) { + // continue early if neddle can't fit at current pos to the end of haystack + if haystack.len() - pos + matches < needle_len { + continue; + } + + // if let Some(v) = haystack.get(pos + matches) { + if !c.eq_ignore_ascii_case(haystack.get(pos + matches)?) { + break; + } + + matches += 1; + // } + } + + if matches == needle_len { + return Some(pos); + } + } + } else { + // otherwise we call memchr_iter, which is faster + for pos in memchr::memrchr_iter(*first, haystack) { + // same logic + let mut matches = 1; + + for c in needle.iter().skip(1) { + if haystack.len() - pos + matches < needle_len { + continue; + } + + if !c.eq_ignore_ascii_case(haystack.get(pos + matches)?) { + break; + } + + matches += 1; + } + + if matches == needle_len { + return Some(pos); + } + } + } + + None +} + +// main search entrypoint +// it will decide whether to call search_literal or search_pattern +pub fn search(app: &mut App, needle: &str, next: bool) -> Option { + if app.hex_view.search.mode == SearchMode::Hex { + // SearchMode::Hex will use search_pattern() if it contains patterns + let pattern_chars = ['?', '[', '(']; + + if needle.contains(|c| pattern_chars.contains(&c)) { + return search_pattern(app, needle, false); + // otherwise we use literal search, which is faster + } else if let Some(n) = hex_string_to_u8(needle) { + return search_literal(app, n, next); + } + } + // SearchMode::Utf8 + search_literal(app, needle, next) +} + +// search literal strings with memchr +pub fn search_literal>(app: &mut App, needle: T, next: bool) -> Option { let text = needle.as_ref(); let filesize = app.file_info.size; let buffer = app.file_info.get_buffer(); @@ -59,16 +201,40 @@ pub fn search>(app: &mut App, needle: T) -> Option { } let ofs = if app.hex_view.search.direction == SearchDirection::Forward { - let start = app.hex_view.offset.checked_add(1)?; + let start = if next { + app.hex_view.offset.checked_add(1)? + } else { + app.hex_view.offset + }; + + let contais_capital_letter = text.iter().any(|b| b.is_ascii_uppercase()); + let smart_search = contais_capital_letter && app.config.search_smartcase; + if start < filesize { - memchr::memmem::find(buffer.get(start..)?, text).map(|pos| start + pos) + if app.config.search_ignorecase && !smart_search { + find_nocase(buffer.get(start..)?, text).map(|pos| start + pos) + } else { + memchr::memmem::find(buffer.get(start..)?, text).map(|pos| start + pos) + } } else { None } } else { - let end = app.hex_view.offset; - if end > 0 { - memchr::memmem::rfind(buffer.get(..end)?, text) + let start = if next { + app.hex_view.offset.checked_sub(1)? + } else { + app.hex_view.offset + }; + + let contais_capital_letter = text.iter().any(|b| b.is_ascii_uppercase()); + let smart_search = contais_capital_letter && app.config.search_smartcase; + + if start < filesize && start != 0 { + if app.config.search_ignorecase && !smart_search { + rfind_nocase(buffer.get(..start)?, text) + } else { + memchr::memmem::rfind(buffer.get(..start)?, text) + } } else { None } @@ -79,7 +245,7 @@ pub fn search>(app: &mut App, needle: T) -> Option { } // ofs is None, check wrap setting - if app.config.search_wrap { + if app.config.search_wrapscan { let ofs = if app.hex_view.search.direction == SearchDirection::Forward { memchr::memmem::find(buffer, text) } else { @@ -95,6 +261,93 @@ pub fn search>(app: &mut App, needle: T) -> Option { None } +// search patterns with yara-x +pub fn search_pattern(app: &mut App, pattern: &str, nocase: bool) -> Option { + if pattern.is_empty() { + return None; + } + + let string_decl = match app.hex_view.search.mode { + SearchMode::Utf8 => format!( + "$a = \"{}\" {}", + pattern, + if nocase { "nocase" } else { "" } + ), + SearchMode::Hex => format!("$a = {{{pattern}}}"), + }; + + let rule = format!( + r#" + rule dz6 {{ + strings: + {string_decl} + condition: + $a + }}"# + ); + + let rules = yara_x::compile(&*rule).ok()?; + let mut scanner = yara_x::Scanner::new(&rules); + let buffer = app.file_info.get_buffer(); + + if app.hex_view.search.direction == SearchDirection::Forward { + let start = app.hex_view.offset.checked_add(1)?; + let slice = buffer.get(start..)?; + + // enable fast scan to return the first result only + scanner.fast_scan(true); + + let result = scanner.scan(slice).unwrap(); + + // if we're at the end, the rule won't match... + if let Some(rule) = result.matching_rules().next() { + // na segunda vez n tem matching rule + + let pattern = rule.patterns().next()?; + + if let Some(r#match) = pattern.matches().next() { + // the search result is an offset from start, so we add start to it + return r#match.range().start.checked_add(start); + } + // ...so we check if wrapscan is set to try again from the beginning + } else if app.config.search_wrapscan { + let start = 0; + let slice = buffer.get(start..)?; + let result = scanner.scan(slice).unwrap(); + let rule = result.matching_rules().next()?; + let pattern = rule.patterns().next()?; + let r#match = pattern.matches().next()?; + + return Some(r#match.range().start); + } + } else { + // backward search + // this works, but fast_scan is not set, thus the scanner will + // find all matches so we can get the last one + let slice = buffer.get(..app.hex_view.offset)?; + let result = scanner.scan(slice).unwrap(); + + if let Some(rule) = result.matching_rules().next() { + let pattern = rule.patterns().next()?; + + // get the last match + if let Some(r#match) = pattern.matches().last() { + return Some(r#match.range().start); + } + } else if app.config.search_wrapscan { + let slice = buffer.get(0..)?; + let result = scanner.scan(slice).unwrap(); + let rule = result.matching_rules().next()?; + let pattern = rule.patterns().next()?; + let r#match = pattern.matches().last()?; + + return Some(r#match.range().start); + } + } + + None +} + // string // hex pub fn dialog_search_draw(app: &mut App, frame: &mut Frame) { @@ -154,37 +407,20 @@ pub fn dialog_search_events(app: &mut App, event: &Event) -> Result { } }, KeyCode::Enter => { - match app.hex_view.search.mode { - SearchMode::Utf8 => { - let text = app.hex_view.search.input_text.value().to_string(); - app.state = UIState::Normal; - - if text.is_empty() { - app.dialog_renderer = None; - return Ok(false); - } - - if let Some(ofs) = search(app, &text) { - app.goto(ofs); - app.dialog_renderer = None; - } else { - app.dialog_renderer = Some(dialog_search_error_draw); - } - } - SearchMode::Hex => { - let hex_string = app.hex_view.search.input_hex.value().to_string(); - - if let Some(bytes) = hex_string_to_u8(&hex_string) { - if let Some(ofs) = search(app, &bytes) { - app.goto(ofs); - app.dialog_renderer = None; - } else { - app.dialog_renderer = Some(dialog_search_error_draw); - } - app.state = UIState::Normal; - } - } + let needle = match app.hex_view.search.mode { + SearchMode::Utf8 => app.hex_view.search.input_text.value().to_string(), + SearchMode::Hex => app.hex_view.search.input_hex.value().to_string(), }; + + if let Some(ofs) = search(app, &needle, false) { + app.goto(ofs); + app.dialog_renderer = None; + } else { + app.dialog_renderer = Some(dialog_search_error_draw); + crate::beep!(); + } + // TODO: draw "Searching..." + app.state = UIState::Normal; } KeyCode::Tab => { app.hex_view.search.mode.next(); @@ -194,7 +430,10 @@ pub fn dialog_search_events(app: &mut App, event: &Event) -> Result { match app.hex_view.search.mode { SearchMode::Utf8 => app.hex_view.search.input_text.handle_event(event), SearchMode::Hex => { - if c.is_ascii_hexdigit() { + // as per https://virustotal.github.io/yara-x/docs/writing_rules/hex-patterns/ + let allowed = [' ', '?', '[', '-', ']', 'x', 'X', '~', '(', ')', '|']; + + if c.is_ascii_hexdigit() || allowed.contains(&c) { app.hex_view.search.input_hex.handle_event(event) } else { None @@ -218,3 +457,57 @@ pub fn dialog_search_error_draw(app: &mut App, frame: &mut Frame) { dialog.kind = MessageType::Error; dialog.render(app, frame); } + +#[cfg(test)] +mod tests { + use super::*; + use pretty_assertions::assert_eq; + + #[test] + fn test_find_nocase() { + // found + for needle in [ + b"opengl", b"OpenGL", b"OpengL", b"opengL", b"Opengl", b"OPENGL", + ] { + let r = find_nocase(b"I think OpenGL is nice", needle); + assert_eq!(r, Some(8)); + } + for needle in [b"i think", b"I think", b"i Think", b"I thinK"] { + let r = find_nocase(b"I think OpenGL is nice", needle); + assert_eq!(r, Some(0)); + } + for needle in [b"ce", b"cE", b"Ce", b"CE"] { + let r = find_nocase(b"I think OpenGL is nice", needle); + assert_eq!(r, Some(20)); + } + for needle in [b"x", b"X"] { + let r = find_nocase(b"I think OpenGL is nicX", needle); + assert_eq!(r, Some(21)); + } + + // not found + for needle in [ + b"Aopengl", b"OpeanGL", b"OpengaL", b"ope-ngL", b"Open2gl", b"OPExNGL", + ] { + let r = find_nocase(b"I think OpenGL is nice", needle); + assert_eq!(r, None); + } + for needle in [b"i t?hink", b"I thi]nk", b"i Thin/k", b"I |thinK"] { + let r = find_nocase(b"I think OpenGL is nice", needle); + assert_eq!(r, None); + } + for needle in [b"c]e", b"c,E", b"Ce0", b"CE-"] { + let r = find_nocase(b"I think OpenGL is nice", needle); + assert_eq!(r, None); + } + for needle in [b"*", b"?"] { + let r = find_nocase(b"I think OpenGL is nicX", needle); + assert_eq!(r, None); + } + + assert_eq!( + find_nocase(b"\x00\x80\xCD\x6F\x00\x00\xCE\x6F\x00\x80", b"opengl"), + None + ); + } +}