From f2be808970f054ab73d90dffb5b70aed41e71191 Mon Sep 17 00:00:00 2001 From: kirbylife Date: Thu, 23 Jul 2026 00:44:43 -0600 Subject: [PATCH] Initial commit --- .gitignore | 1 + Cargo.lock | 1668 +++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 7 + src/main.rs | 3 + sshch.py | 811 +++++++++++++++++++++++++ 5 files changed, 2490 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/main.rs create mode 100644 sshch.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..40da4f0 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1668 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "anyhow" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" + +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] + +[[package]] +name = "atomic" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89cbf775b137e9b968e67227ef7f775587cde3fd31b0d8599dbd0f598a48340" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "by_address" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64fa3c856b712db6612c019f14756e64e4bcea13337a6b33b696333a9eaa2d06" + +[[package]] +name = "bytemuck" +version = "1.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95832e849adfb21180ccb6826a99da14e5d266ae5c2e668e1602cf234f153797" + +[[package]] +name = "castaway" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a" +dependencies = [ + "rustversion", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" + +[[package]] +name = "compact_str" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dfdd1c2274d9aa354115b09dc9a901d6c5576818cdf70d14cae2bdb47df00ab" +dependencies = [ + "castaway", + "cfg-if", + "itoa", + "rustversion", + "ryu", + "static_assertions", +] + +[[package]] +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + +[[package]] +name = "crossterm" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" +dependencies = [ + "bitflags 2.13.1", + "crossterm_winapi", + "derive_more", + "document-features", + "mio", + "parking_lot", + "rustix", + "signal-hook", + "signal-hook-mio", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" +dependencies = [ + "winapi", +] + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "csscolorparser" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb2a7d3066da2de787b7f032c736763eb7ae5d355f81a68bab2675a96008b0bf" +dependencies = [ + "lab", + "phf", +] + +[[package]] +name = "darling" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" +dependencies = [ + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.119", +] + +[[package]] +name = "darling_macro" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "deltae" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5729f5117e208430e437df2f4843f5e5952997175992d1414f94c57d61e270b4" + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" + +[[package]] +name = "derive_more" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.119", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "document-features" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", +] + +[[package]] +name = "either" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "euclid" +version = "0.22.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1a05365e3b1c6d1650318537c7460c6923f1abdd272ad6842baa2b509957a06" +dependencies = [ + "num-traits", +] + +[[package]] +name = "fancy-regex" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b95f7c0680e4142284cf8b22c14a476e87d61b004a3a0861872b32ef7ead40a2" +dependencies = [ + "bit-set", + "regex", +] + +[[package]] +name = "fast-srgb8" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd2e7510819d6fbf51a5545c8f922716ecfb14df168a3242f7d33e0239efe6a1" + +[[package]] +name = "filedescriptor" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e40758ed24c9b2eeb76c35fb0aebc66c626084edd827e07e1552279814c6682d" +dependencies = [ + "libc", + "thiserror 1.0.69", + "winapi", +] + +[[package]] +name = "finl_unicode" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9844ddc3a6e533d62bba727eb6c28b5d360921d5175e9ff0f1e621a5c590a4d5" + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "futures-core" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7" + +[[package]] +name = "futures-task" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109" + +[[package]] +name = "futures-util" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi 5.3.0", + "wasip2", +] + +[[package]] +name = "getrandom" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" +dependencies = [ + "cfg-if", + "libc", + "r-efi 6.0.0", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "indoc" +version = "2.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" +dependencies = [ + "rustversion", +] + +[[package]] +name = "instability" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eb2d60ef19920a3a9193c3e371f726ec1dafc045dac788d0fb3704272458971" +dependencies = [ + "darling", + "indoc", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "js-sys" +version = "0.3.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "kasuari" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bde5057d6143cc94e861d90f591b9303d6716c6b9602309150bd068853c10899" +dependencies = [ + "hashbrown 0.16.1", + "portable-atomic", + "thiserror 2.0.19", +] + +[[package]] +name = "lab" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf36173d4167ed999940f804952e6b08197cae5ad5d572eb4db150ce8ad5d58f" + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.189" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" + +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + +[[package]] +name = "line-clipping" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f50e8f47623268b5407192d26876c4d7f89d686ca130fdc53bced4814cd29f8" +dependencies = [ + "bitflags 2.13.1", +] + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "litrs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" + +[[package]] +name = "lru" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b6180140927ee907000b0aa540091f6ea512ead4447c92b8fc35bc72788a5a6" +dependencies = [ + "hashbrown 0.17.1", +] + +[[package]] +name = "mac_address" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0aeb26bf5e836cc1c341c8106051b573f1766dfa05aa87f0b98be5e51b02303" +dependencies = [ + "nix", + "winapi", +] + +[[package]] +name = "memchr" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" + +[[package]] +name = "memmem" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a64a92489e2744ce060c349162be1c5f33c6969234104dbd99ddb5feb08b8c15" + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "mio" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d65c71f1ce40ab09135ce117d742b9f8a19ff91a41a8b57ed50bc2de59c427" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys", +] + +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags 2.13.1", + "cfg-if", + "cfg_aliases", + "libc", + "memoffset", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-conv" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ + "libc", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "ordered-float" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bb71e1b3fa6ca1c61f383464aaf2bb0e2f8e772a1f01d486832464de363b951" +dependencies = [ + "num-traits", +] + +[[package]] +name = "palette" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cbf71184cc5ecc2e4e1baccdb21026c20e5fc3dcf63028a086131b3ab00b6e6" +dependencies = [ + "approx", + "fast-srgb8", + "libm", + "palette_derive", +] + +[[package]] +name = "palette_derive" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5030daf005bface118c096f510ffb781fc28f9ab6a32ab224d8631be6851d30" +dependencies = [ + "by_address", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "pest" +version = "2.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7df728be843c7070fab6ab7c328c4e9e9d78e23bf749c0669c86ee7ebfa050a2" +dependencies = [ + "memchr", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e2dd6fc3b26b3462ee188aac870f5a41d398f1cd5e2408d16531bd71c9591fd" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a7a9205cfb6f596a9e8b689c0a15f9ceb7a1aafae7aaf788150ac65b29975b6" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "pest_meta" +version = "2.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85abd351c0de1e8384fc791a0737111a350394937e92b956b743dac12429f57c" +dependencies = [ + "pest", +] + +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_macros", + "phf_shared", +] + +[[package]] +name = "phf_codegen" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" +dependencies = [ + "phf_generator", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +dependencies = [ + "phf_shared", + "rand", +] + +[[package]] +name = "phf_macros" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "portable-atomic" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d20d5497ef88037a52ff98267d066e7f11fcc5e99bbfbd58a42336193aacec3" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "proc-macro2" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rand" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22f6172bdec972074665ed81ed53b71da00bfc44b65a753cfde883ec4c702a1a" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "ratatui" +version = "0.30.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3274ba0a2c5e1bcad2a2005d20f4dc59dad26b2eb0940fb094500dba4099d57d" +dependencies = [ + "instability", + "ratatui-core", + "ratatui-crossterm", + "ratatui-macros", + "ratatui-termina", + "ratatui-termwiz", + "ratatui-widgets", + "serde", +] + +[[package]] +name = "ratatui-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbb175c433c8e28a809d1f5773a2ae96e68c0ce40db865cbab1020bf33ae479c" +dependencies = [ + "bitflags 2.13.1", + "compact_str", + "critical-section", + "hashbrown 0.17.1", + "itertools", + "kasuari", + "lru", + "palette", + "serde", + "strum", + "thiserror 2.0.19", + "unicode-segmentation", + "unicode-truncate", + "unicode-width", +] + +[[package]] +name = "ratatui-crossterm" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567584a3b0e6a8203c23de40b4861497266725eb5363dbfd18a1edd603cca9f0" +dependencies = [ + "cfg-if", + "crossterm", + "instability", + "ratatui-core", +] + +[[package]] +name = "ratatui-macros" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed7dc68daa7498a43e4d68e0eb078427e10c38fbcfbb1e42d955f1fa2140d814" +dependencies = [ + "ratatui-core", + "ratatui-widgets", +] + +[[package]] +name = "ratatui-termina" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0bf912d9e66f057a759d92e386a280ea886b352ab757d6ac4d653c7ed2c43c2" +dependencies = [ + "instability", + "ratatui-core", + "termina", +] + +[[package]] +name = "ratatui-termwiz" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf03e0380b7744054d6cb74224fe3adf062a029754933f575ca1e3b4c2ce977" +dependencies = [ + "ratatui-core", + "termwiz", +] + +[[package]] +name = "ratatui-widgets" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66e3d19bcc9130ca376277d93b60767ff121ace3be06f5f95f81dd68956407d1" +dependencies = [ + "bitflags 2.13.1", + "hashbrown 0.17.1", + "indoc", + "instability", + "itertools", + "line-clipping", + "ratatui-core", + "serde", + "strum", + "time", + "unicode-segmentation", + "unicode-width", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags 2.13.1", +] + +[[package]] +name = "regex" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fcfdb36bda0c880c5931cdc7a2bcdc8ba4556847b9d912bca70bc94708711ad" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags 2.13.1", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "rustversion" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.3", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "signal-hook" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-mio" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc" +dependencies = [ + "libc", + "mio", + "signal-hook", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "siphasher" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" + +[[package]] +name = "sshmngrs" +version = "0.1.0" +dependencies = [ + "ratatui", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9628de9b8791db39ceda2b119bbe13134770b56c138ec1d3af810d045c04f9bd" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab85eea0270ee17587ed4156089e10b9e6880ee688791d45a905f5b1ca36f664" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "termina" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9048a889effe34a5cddee0af7f53285198b16dca3be510858d38dfdb3e62a04e" +dependencies = [ + "bitflags 2.13.1", + "parking_lot", + "rustix", + "signal-hook", + "windows-sys", +] + +[[package]] +name = "terminfo" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4ea810f0692f9f51b382fff5893887bb4580f5fa246fde546e0b13e7fcee662" +dependencies = [ + "fnv", + "nom", + "phf", + "phf_codegen", +] + +[[package]] +name = "termios" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "411c5bf740737c7918b8b1fe232dca4dc9f8e754b8ad5e20966814001ed0ac6b" +dependencies = [ + "libc", +] + +[[package]] +name = "termwiz" +version = "0.23.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4676b37242ccbd1aabf56edb093a4827dc49086c0ffd764a5705899e0f35f8f7" +dependencies = [ + "anyhow", + "base64", + "bitflags 2.13.1", + "fancy-regex", + "filedescriptor", + "finl_unicode", + "fixedbitset", + "hex", + "lazy_static", + "libc", + "log", + "memmem", + "nix", + "num-derive", + "num-traits", + "ordered-float", + "pest", + "pest_derive", + "phf", + "sha2", + "signal-hook", + "siphasher", + "terminfo", + "termios", + "thiserror 1.0.69", + "ucd-trie", + "unicode-segmentation", + "vtparse", + "wezterm-bidi", + "wezterm-blob-leases", + "wezterm-color-types", + "wezterm-dynamic", + "wezterm-input-types", + "winapi", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09a43598840e33d5b0331f38c5e30d13bb11c11210a4b58f0d9b18a5a5eefcd9" +dependencies = [ + "thiserror-impl 2.0.19", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43cbfe0cf76104d42a574802844187e84a305e531ed54455f11fbde0f10541cd" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.3", +] + +[[package]] +name = "time" +version = "0.3.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e1d5e639ff6bab73cb6885cc7e7b1de96c3f32c68ec55f3952614bec1092244" +dependencies = [ + "deranged", + "libc", + "num-conv", + "num_threads", + "powerfmt", + "serde_core", + "time-core", +] + +[[package]] +name = "time-core" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" + +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-segmentation" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" + +[[package]] +name = "unicode-truncate" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b380a1238663e5f8a691f9039c73e1cdae598a30e9855f541d29b08b53e9a5" +dependencies = [ + "itertools", + "unicode-segmentation", + "unicode-width", +] + +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "uuid" +version = "1.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf3923a6f5c4c6382e0b653c4117f48d631ea17f38ed86e2a828e6f7412f5239" +dependencies = [ + "atomic", + "getrandom 0.4.3", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "vtparse" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d9b2acfb050df409c972a37d3b8e08cdea3bddb0c09db9d53137e504cfabed0" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.4+wasi-0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 2.0.119", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wezterm-bidi" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0a6e355560527dd2d1cf7890652f4f09bb3433b6aadade4c9b5ed76de5f3ec" +dependencies = [ + "log", + "wezterm-dynamic", +] + +[[package]] +name = "wezterm-blob-leases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "692daff6d93d94e29e4114544ef6d5c942a7ed998b37abdc19b17136ea428eb7" +dependencies = [ + "getrandom 0.3.4", + "mac_address", + "sha2", + "thiserror 1.0.69", + "uuid", +] + +[[package]] +name = "wezterm-color-types" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7de81ef35c9010270d63772bebef2f2d6d1f2d20a983d27505ac850b8c4b4296" +dependencies = [ + "csscolorparser", + "deltae", + "lazy_static", + "wezterm-dynamic", +] + +[[package]] +name = "wezterm-dynamic" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f2ab60e120fd6eaa68d9567f3226e876684639d22a4219b313ff69ec0ccd5ac" +dependencies = [ + "log", + "ordered-float", + "strsim", + "thiserror 1.0.69", + "wezterm-dynamic-derive", +] + +[[package]] +name = "wezterm-dynamic-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c0cf2d539c645b448eaffec9ec494b8b19bd5077d9e58cb1ae7efece8d575b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "wezterm-input-types" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7012add459f951456ec9d6c7e6fc340b1ce15d6fc9629f8c42853412c029e57e" +dependencies = [ + "bitflags 1.3.2", + "euclid", + "lazy_static", + "serde", + "wezterm-dynamic", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..3b74985 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "sshmngrs" +version = "0.1.0" +edition = "2024" + +[dependencies] +ratatui = "0.30.2" diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..e7a11a9 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} diff --git a/sshch.py b/sshch.py new file mode 100644 index 0000000..08ac83a --- /dev/null +++ b/sshch.py @@ -0,0 +1,811 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from __future__ import division +from __future__ import print_function +try: + import configparser +except ImportError: + import ConfigParser as configparser # Python 2.x import + +from os import path +from sys import argv +from math import ceil +from getpass import getpass +from optparse import OptionParser +import subprocess +import base64 +import time +import curses +from curses import textpad, panel + +# https://github.com/zlaxy/sshch +version = "1.0" +# path to conf file, default: ~/.config/sshch.conf +conf_file = path.expanduser("~") + '/.config/sshch.conf' +# expand groups by default +expand_default = True + + +def AddNewAlias(alias): + if not conf.has_section(alias): + conf.add_section(alias) + conf.write(open(conf_file, "w")) + return True + else: + return "error: '" + alias + "' alias or group already exists" + + +def SetAliasString(alias, string): + conf.set(alias, "exec_string", string) + conf.write(open(conf_file, "w")) + + +def SetGroupString(alias, string): + conf.set(alias, "group", string) + conf.write(open(conf_file, "w")) + + +def SetPassword(alias, string): + if string == "" or string == b'': + conf.remove_option(alias, "password") + else: + string = string.encode() + string = base64.b64encode(base64.b16encode( + base64.b32encode(string))) + string = string.decode('utf-8') + conf.set(alias, "password", string) + conf.write(open(conf_file, "w")) + + +def RemoveAliases(aliases): + for alias in aliases: + conf.remove_section(alias) + conf.write(open(conf_file, "w")) + + +def ConnectAlias(alias, command=False): + exec_string = "" + if conf.has_option(alias, "password"): + password = base64.b32decode(base64.b16decode( + base64.b64decode(conf.get(alias, "password")))) + exec_string = 'sshpass -p "' + password.decode('utf-8') + '" ' + if conf.has_option(alias, "exec_string"): + exec_string = exec_string + conf.get(alias, "exec_string") + if command: + exec_string = exec_string + " " + command + subprocess.Popen(exec_string, shell=True).communicate()[0] + + +def HoldConnection(alias): + groups = [] + connectaliases = [] + for a in conf.sections(): + if conf.has_option(a, "group"): + groups.append(a) + if alias in groups: + print("Can't hold connection with group.") + else: + print("Connecting to " + alias + ". Press CTRL+C to cancel.") + time.sleep(1) + while True: + ConnectAlias(alias) + time.sleep(5) + + +def CMDAdd(alias): + alias = alias.split()[0].strip() + result = AddNewAlias(alias) + if result == True: + prompt_add = ("".join(["Enter connection string for new alias ", + "(example: ssh user@somehost.com):\n"])) + string = "" + while string == "": + string = input(prompt_add) + SetAliasString(alias, string) + else: + print(result) + + +def CMDGroup(group): + group = group.split()[0].strip() + result = AddNewAlias(group) + if result == True: + prompt_add = ("".join(["Enter aliases for new group ", + "(example: alias1 alias2):\n"])) + string = "" + while string == "": + string = input(prompt_add) + SetGroupString(group, string) + else: + print(result) + + +def CMDEdit(alias): + if conf.has_section(alias): + if conf.has_option(alias, "exec_string"): + prompt_edit = ("".join(["Enter connection string for existing alias ", + "(example: ssh user@somehost.com):\n"])) + string = "" + while string == "": + string = input(prompt_edit) + SetAliasString(alias, string) + elif conf.has_option(alias, "group"): + prompt_edit = ("".join(["Enter aliases for existing group ", + "(example: alias1 alias2):\n"])) + string = "" + while string == "": + string = input(prompt_edit) + SetGroupString(alias, string) + else: + print("error: '" + alias + "' is not correct alias or group") + else: + print("error: '" + alias + "' alias or group does not exists") + + +def CMDPassword(alias): + groups = [] + connectaliases = [] + for a in conf.sections(): + if conf.has_option(a, "group"): + groups.append(a) + if alias in groups: + print("Can't set password for group.") + else: + if conf.has_section(alias): + prompt_pass = ("[UNSAFE] Enter password for sshpass (Ctrl+C - cancel, blank - clear password):\n") + string = "" + string = getpass(prompt_pass) + SetPassword(alias, string) + else: + print("error: '" + alias + "' alias does not exists") + + +def CMDRemove(alias): + if conf.has_section(alias): + prompt_remove = ("Type 'yes' if you sure to remove '" + alias + "' alias or group: ") + string = input(prompt_remove) + if string == "yes": + RemoveAliases([alias]) + else: + print("'" + alias + "' alias or group was not deleted.") + else: + print("error: '" + alias + "' alias or group does not exists.") + + +def CMDConnect(aliases, command=False): + groups = [] + connectaliases = [] + for a in conf.sections(): + if conf.has_option(a, "group"): + groups.append(a) + for alias in aliases: + if alias in groups: + group_aliases = conf.get(alias, "group").split() + for ga in group_aliases: + if not ga in connectaliases: + connectaliases.append(ga) + else: + if not alias in connectaliases: + connectaliases.append(alias) + for alias in connectaliases: + if conf.has_section(alias): + print("Connecting to " + alias + "...") + ConnectAlias(alias, command) + print("... " + alias + " session finished.") + else: + print("error: '" + alias + "' alias does not exists") + + +def CMDList(option, opt, value, parser): + print(' '.join(str(p) for p in conf.sections())) + + +def GetTreeList(strings=True, expandlist=True): + aliases = [] + groups = [] + resultalias = [] + resultstring = [] + for a in conf.sections(): + if conf.has_option(a, "group"): + groups.append(a) + elif conf.has_option(a, "exec_string"): + aliases.append(a) + for g in groups: + resultalias.append(g) + resultstring.append(">> "+g) + group_aliases = conf.get(g, "group").split() + for ga in group_aliases: + if expandlist == False: + pass + elif expandlist == True or g in expandlist: + if conf.has_option(ga, "exec_string"): + resultalias.append(ga+" "+g) + result = "".join([" ", str(ga), " (", (conf.get(ga, "exec_string") if + conf.has_option(ga, "exec_string") else ""), ")", + (" [password]" if conf.has_option(ga, "password") else "")]) + resultstring.append(result) + try: + aliases.remove(ga) + except ValueError: + pass + for a in aliases: + resultalias.append(a) + result = "".join([str(a), " (", (conf.get(a, "exec_string") if + conf.has_option(a, "exec_string") else ""), ")", + (" [password]" if conf.has_option(a, "password") else "")]) + resultstring.append(result) + if strings: + return resultstring; + else: + return resultalias; + +def CMDFullList(option, opt, value, parser): + for p in GetTreeList(): + print (p) + + +def CursesConnect(screen, aliases, command=False): + curses.endwin() + groups = [] + connectaliases = [] + for a in conf.sections(): + if conf.has_option(a, "group"): + groups.append(a) + for alias in aliases: + if alias in groups: + group_aliases = conf.get(alias, "group").split() + for ga in group_aliases: + if not ga in connectaliases: + connectaliases.append(ga) + else: + if not alias in connectaliases: + connectaliases.append(alias) + for alias in connectaliases: + print("Connecting to " + alias + "...") + ConnectAlias(alias, command) + print("... " + alias + " session finished.") + print("Press 'enter' to continue.") + screen.getch() + + +def CursesExit(error=False): + curses.endwin() + if error: + print(error) + exit() + + +def CursesTextpadConfirm(value): + if value == 10: + value = 7 + return value + + +def CursesTextpad(screen, h, w, y, x, title="", value="", + text_colorpair=0, deco_colorpair=0): + new_window = curses.newwin(h + 3, w + 2, y - 1, x - 1) + title_window = new_window.subwin(1, w, y, x) + title_window.addnstr(0, 0, title, w, text_colorpair) + title_window.refresh() + sub_window = new_window.subwin(h, w, y + 1, x) + textbox_field = textpad.Textbox(sub_window, insert_mode=True) + new_window.attron(deco_colorpair) + new_window.box() + new_window.attroff(deco_colorpair) + new_window.refresh() + sub_window.addnstr(0, 0, value, h * w, text_colorpair) + sub_window.attron(text_colorpair) + return textbox_field + + +def CursesPanel(screen, h, w, y, x, text, + text_colorpair=0, deco_colorpair=0, confirm=0): + new_window = curses.newwin(h, w, y, x) + new_window.erase() + new_window.attron(deco_colorpair) + new_window.box() + new_window.attroff(deco_colorpair) + sub_window = new_window.subwin(h - 2, w - 2, y + 1, x + 1) + sub_window.insstr(0, 0, text) + panel = curses.panel.new_panel(new_window) + curses.panel.update_panels() + screen.refresh() + if confirm == "password": + hidden_password = "" + keych = "" + position = 2 + while True: + keych = screen.getch() + if keych == ord("\n"): + break + if keych == 27: + hidden_password = "" + break + if keych == curses.KEY_BACKSPACE: + if position > 2: + if position == len(hidden_password) + 2: + sub_window.addstr(1, position - 1, " ", + text_colorpair) + sub_window.refresh() + position = position - 1 + hidden_password = hidden_password[0:-1] + if keych > 31 and keych < 127: + hidden_password += curses.keyname(keych).decode('utf-8') + sub_window.addstr(1, position, "*", text_colorpair) + if position < w - 4: + position += 1 + sub_window.refresh() + return hidden_password + elif confirm == "remove": + keych = screen.getch() + if keych == ord("y") or keych == ord("Y"): + return "confirm" + return False + else: + screen.getch() + + +def CMDOptions(): + class FormatedParser(OptionParser): + + def format_epilog(self, formatter): + return self.epilog + usage = "usage: %prog [options] [aliases]" + progname = path.basename(__file__) + epilog = ("".join(["Examples:\n ", + progname, " existingalias\n ", + progname, " -a newremoteserver\n ", + progname, " --edit=newremoteserver -p newremoteserver\n ", + progname, ' -c "ls -l" newremoteserver\n ', + progname, " -c reboot existingalias newremoteserver\n", + "Examples of connection string:\n ", + "ssh user@somehost.com\n ", + "ssh gates@8.8.8.8 -p 667\n ", + "ssh root@somehost.com -t tmux a\n", + "Also, you can edit config file manually: ", conf_file, "\n"])) + opts = FormatedParser(usage=usage, version="%prog " + version, + epilog=epilog) + opts.add_option('-l', '--list', action="callback", + callback=CMDList, help="show list of all existing aliases") + opts.add_option('-f', '--fulllist', action="callback", + callback=CMDFullList, help=("show list of all existing " + + "aliases with connection strings")) + opts.add_option('-a', '--add', action="store", type="string", + dest="add", metavar="alias", default=False, + help="add new alias for connection string") + opts.add_option('-g', '--group', action="store", type="string", + dest="group", metavar="group", default=False, + help="add new group for aliases") + opts.add_option('-c', '--command', action="store", type="string", + dest="command", metavar="command", default=False, + help="execute command for aliases or group") + opts.add_option('-k', '--keep', action="store", type="string", + dest="keep", metavar="alias", default=False, + help="hold connection with specified alias") + opts.add_option('-e', '--edit', action="store", type="string", + dest='edit', metavar="alias", default=False, + help="edit existing alias or group") + opts.add_option('-p', '--password', action="store", type="string", + dest='password', metavar="alias", default=False, + help="set and store password for sshpass [UNSAFE]") + opts.add_option('-r', '--remove', action="store", type="string", + dest='remove', metavar="alias", default=False, + help="remove existing alias or group") + options, alias = opts.parse_args() + if options.add: + CMDAdd(options.add) + if options.group: + CMDGroup(options.group) + if options.edit: + CMDEdit(options.edit) + if options.password: + CMDPassword(options.password) + if options.remove: + CMDRemove(options.remove) + if options.keep: + HoldConnection(options.keep) + if alias: + CMDConnect(alias, options.command) + +# curses template from: https://stackoverflow.com/a/30828805/6224462 + + +def CursesMain(): + help_screen = ("".join([" Press:\n", + " 'z'/'x' or arrows - navigation\n", + " 'a'/'F2' - add new alias (without spaces)\n", + " 'g'/'F5' - add new group (spaces will be stripped)\n", + " 'e'/'F4' - edit existing alias/group\n", + " 'p'/'F6' - set alias's password for sshpass [UNSAFE]\n", + " 'space'/'insert' - select\n", + " 'r'/'F8' - remove selected alias/aliases\n", + " 'c'/'F3' - execute specific command with selected alias/aliases\n", + " 'k'/'F7' - hold connection with selected alias\n", + " 'enter'/'F9' - connect to selected alias/aliases,\n", + " expand/collapse group\n", + " 'q'/'F10' - quit\n", + " Run program with '--help' option to view command line help.\n", + " Also, you can edit config file manually:\n", + " ", conf_file])) + if expand_default == True: + groups = [] + for a in conf.sections(): + if conf.has_option(a, "group"): + groups.append(a) + expanded = groups + strings = GetTreeList(False, expanded) + stringsfull = GetTreeList(True, expanded) + elif expand_default == False: + groups = [] + expanded = groups + strings = GetTreeList(False, expanded) + stringsfull = GetTreeList(True, expanded) + row_num = len(strings) + selected_strings = [" " for i in range(0, row_num + 1)] + screen = curses.initscr() + curses.noecho() + curses.cbreak() + curses.start_color() + screen.keypad(1) + curses.init_pair(1, curses.COLOR_BLACK, curses.COLOR_CYAN) + highlight_text = curses.color_pair(1) + normal_text = curses.A_NORMAL + height, width = screen.getmaxyx() + screen.border(0) + curses.curs_set(0) + max_row = height - 5 + screen.addnstr(1, 2, "sshch " + version + ", press 'h' for help", + width - 4) + box = curses.newwin(max_row + 2, width - 2, 2, 1) + box.box() + pages = int(ceil(row_num / max_row)) + position = 1 + page = 1 + for i in range(1, max_row + 1): + if row_num == 0: + box.addnstr(1, 1, "There aren't any aliases yet. Press 'a' to add new one.", + width - 6, highlight_text) + else: + exec_string = ["[", selected_strings[i], "] ", stringsfull[i - 1]] + if (i == position): + box.addnstr(i, 2, "".join(exec_string), width - 6, highlight_text) + else: + box.addnstr(i, 2, "".join(exec_string), width - 6, normal_text) + if i == row_num: + break + screen.refresh() + box.refresh() + key_pressed = screen.getch() + while True: + if key_pressed == ord('q') or key_pressed == ord( + 'Q') or key_pressed == ( + 27) or key_pressed == curses.KEY_F10: + CursesExit() + if key_pressed == ord('h') or key_pressed == ord( + 'H') or key_pressed == curses.KEY_F1: + CursesPanel(screen, height - 4, width - 6, 2, 3, + help_screen, normal_text, highlight_text) + if key_pressed == ord('a') or key_pressed == ord( + 'A') or key_pressed == curses.KEY_F2: + curses.curs_set(1) + new_alias_textpad = CursesTextpad(screen, 1, width - 8, + (height // 2) - 1, 4, "Enter new alias:", "", + normal_text, highlight_text) + add_alias = new_alias_textpad.edit(CursesTextpadConfirm) + add_alias = add_alias.split()[0].strip() + if not add_alias == "": + add_result = AddNewAlias(add_alias) + if add_result == True: + add_string = "" + while add_string == "": + string_textpad = CursesTextpad(screen, 3, + width - 8, (height // 2) - 1, 4, + "Enter full execution string:", + "ssh ", normal_text, highlight_text) + add_string = string_textpad.edit( + CursesTextpadConfirm) + SetAliasString(add_alias, + add_string.replace("\n", "").rstrip()) + strings = GetTreeList(False, expanded) + stringsfull = GetTreeList(True, expanded) + row_num = len(strings) + selected_strings.append(" ") + pages = int(ceil(row_num / max_row)) + box.refresh() + else: + curses.curs_set(0) + CursesPanel(screen, 3, + width - 6, (height // 2) - 1, 3, add_result, + normal_text, highlight_text) + curses.curs_set(0) + if key_pressed == ord('g') or key_pressed == ord( + 'G') or key_pressed == curses.KEY_F5: + curses.curs_set(1) + new_group_textpad = CursesTextpad(screen, 1, width - 8, + (height // 2) - 1, 4, "Enter group name (without spaces):", "", + normal_text, highlight_text) + add_group = new_group_textpad.edit(CursesTextpadConfirm) + add_group = add_group.split()[0].strip() + if not add_group == "": + add_result = AddNewAlias(add_group) + if add_result == True: + add_string = "" + while add_string.rstrip() == "": + string_textpad = CursesTextpad(screen, 3, + width - 8, (height // 2) - 1, 4, + "Enter aliases for new group (example: alias1 alias2):", + "", normal_text, highlight_text) + add_string = string_textpad.edit( + CursesTextpadConfirm) + SetGroupString(add_group, + add_string.replace("\n", "").rstrip()) + expanded.append(add_group) + strings = GetTreeList(False, expanded) + stringsfull = GetTreeList(True, expanded) + row_num = len(strings) + selected_strings = [" " for i in range(0, row_num + 1)] + pages = int(ceil(row_num / max_row)) + box.refresh() + else: + curses.curs_set(0) + CursesPanel(screen, 3, + width - 6, (height // 2) - 1, 3, add_result, + normal_text, highlight_text) + curses.curs_set(0) + if (key_pressed == ord('e') or key_pressed == ord( + 'E') or key_pressed == curses.KEY_F4) and row_num != 0: + edit_string = "" + groups = [] + for a in conf.sections(): + if conf.has_option(a, "group"): + groups.append(a) + curses.curs_set(1) + if strings[position - 1] in groups: + while edit_string.rstrip() == "": + string_textpad = CursesTextpad(screen, 3, width - 8, + (height // 2) - 1, 4, "Enter new aliases for existing group:", + (conf.get(strings[position - 1], + "group") if conf.has_option(strings[position - 1], + "group") else ""), + normal_text, highlight_text) + edit_string = string_textpad.edit(CursesTextpadConfirm) + SetGroupString(strings[position - 1], + edit_string.replace("\n", "").rstrip()) + strings = GetTreeList(False, expanded) + stringsfull = GetTreeList(True, expanded) + row_num = len(strings) + selected_strings.append(" ") + pages = int(ceil(row_num / max_row)) + box.refresh() + else: + while edit_string.rstrip() == "": + string_textpad = CursesTextpad(screen, 3, width - 8, + (height // 2) - 1, 4, "Enter new execution string:", + (conf.get(strings[position - 1].split()[0].strip(), + "exec_string") if conf.has_option(strings[position - 1].split()[0].strip(), + "exec_string") else ""), + normal_text, highlight_text) + edit_string = string_textpad.edit(CursesTextpadConfirm) + SetAliasString(strings[position - 1].split()[0].strip(), + edit_string.replace("\n", "").rstrip()) + strings = GetTreeList(False, expanded) + stringsfull = GetTreeList(True, expanded) + curses.curs_set(0) + if (key_pressed == ord('p') or key_pressed == ord( + 'P') or key_pressed == curses.KEY_F6) and row_num != 0: + groups = [] + for a in conf.sections(): + if conf.has_option(a, "group"): + groups.append(a) + if not strings[position - 1].split()[0].strip() in groups: + set_password = "" + set_password = CursesPanel(screen, 4, width - 6, + (height // 2) - 1, 3, + " Enter user password for sshpass and press 'enter':\n>", + normal_text, highlight_text, "password") + SetPassword(strings[position - 1].split()[0].strip(), set_password) + strings = GetTreeList(False, expanded) + stringsfull = GetTreeList(True, expanded) + if (key_pressed == ord('r') or key_pressed == ord( + 'R') or key_pressed == curses.KEY_F8 or key_pressed == ( + curses.KEY_DC)) and row_num != 0: + selected = [] + for i in range(1, row_num + 1): + if selected_strings[i] == "*": + selected.append(strings[i - 1]) + if len(selected) > 0: + remove_confirm = ( + "".join(["Are you sure to remove ", + str(len(selected)), " selected aliases? (y/N)"])) + else: + remove_confirm = ("".join(["Are you sure to remove '", + strings[position - 1].split()[0].strip(), "' alias? (y/N)"])) + selected.append(strings[position - 1].split()[0].strip()) + remove_result = CursesPanel(screen, 4, width - 6, + (height // 2) - 1, 3, remove_confirm, normal_text, + highlight_text, "remove") + if remove_result == "confirm": + RemoveAliases(selected) + strings = GetTreeList(False, expanded) + stringsfull = GetTreeList(True, expanded) + row_num = len(strings) + selected_strings = [" " for i in range(0, row_num + 1)] + pages = int(ceil(row_num / max_row)) + position = 1 + page = 1 + box.refresh() + if (key_pressed == ord('c') or key_pressed == ord( + 'C') or key_pressed == curses.KEY_F3) and row_num != 0: + selected = [] + for i in range(1, row_num + 1): + if selected_strings[i] == "*": + selected.append(strings[i - 1]) + if not len(selected) > 0: + selected.append(strings[position - 1].split()[0].strip()) + curses.curs_set(1) + command_textpad = CursesTextpad(screen, 3, width - 8, + (height // 2) - 1, 4, + "".join([ + "Enter specific command to execute with selected ", + "alias/aliases:"] + ), "", normal_text, highlight_text) + command_string = command_textpad.edit(CursesTextpadConfirm) + CursesConnect(screen, selected, + command_string.replace("\n", "").rstrip()) + curses.curs_set(0) + if (key_pressed == ord('k') or key_pressed == ord('K') or + key_pressed == (curses.KEY_F7)) and row_num != 0: + groups = [] + for a in conf.sections(): + if conf.has_option(a, "group"): + groups.append(a) + if not strings[position - 1] in groups: + curses.endwin() + HoldConnection(strings[position - 1].split()[0].strip()) + if (key_pressed == ord("\n") or key_pressed == ( + curses.KEY_F9)) and row_num != 0: + groups = [] + for a in conf.sections(): + if conf.has_option(a, "group"): + groups.append(a) + if strings[position - 1] in groups: + if strings[position - 1] in expanded: + expanded.remove(strings[position - 1]) + else: + expanded.append(strings[position - 1]) + strings = GetTreeList(False, expanded) + stringsfull = GetTreeList(True, expanded) + row_num = len(strings) + selected_strings.append(" ") + pages = int(ceil(row_num / max_row)) + box.refresh() + else: + selected = [] + for i in range(1, row_num + 1): + if selected_strings[i] == "*": + selected.append(strings[i - 1]) + if not len(selected) > 0: + selected.append(strings[position - 1].split()[0].strip()) + CursesConnect(screen, selected) + selected_strings = [" " for i in range(0, row_num + 1)] + if (key_pressed == 32 or key_pressed == ( + curses.KEY_IC)) and row_num != 0: + if selected_strings[position] == ' ': + selected_strings[position] = '*' + else: + selected_strings[position] = ' ' + if page == 1: + if position < i: + position = position + 1 + else: + if pages > 1: + page = page + 1 + position = 1 + (max_row * (page - 1)) + elif page == pages: + if position < row_num: + position = position + 1 + else: + if position < max_row + (max_row * (page - 1)): + position = position + 1 + else: + page = page + 1 + position = 1 + (max_row * (page - 1)) + if key_pressed == curses.KEY_DOWN or key_pressed == ord( + 'x') or key_pressed == ord('X'): + if page == 1: + if position < i: + position = position + 1 + else: + if pages > 1: + page = page + 1 + position = 1 + (max_row * (page - 1)) + elif page == pages: + if position < row_num: + position = position + 1 + else: + if position < max_row + (max_row * (page - 1)): + position = position + 1 + else: + page = page + 1 + position = 1 + (max_row * (page - 1)) + if key_pressed == curses.KEY_UP or key_pressed == ord( + 'z') or key_pressed == ord('Z'): + if page == 1: + if position > 1: + position = position - 1 + else: + if position > (1 + (max_row * (page - 1))): + position = position - 1 + else: + page = page - 1 + position = max_row + (max_row * (page - 1)) + if key_pressed == curses.KEY_LEFT or (key_pressed == + curses.KEY_PPAGE): + if page > 1: + page = page - 1 + position = 1 + (max_row * (page - 1)) + if key_pressed == curses.KEY_RIGHT or (key_pressed == + curses.KEY_NPAGE): + if page < pages: + page = page + 1 + position = (1 + (max_row * (page - 1))) + if key_pressed == curses.KEY_HOME: + page = 1 + position = 1 + if key_pressed == curses.KEY_END: + page = pages + position = row_num + box.erase() + screen.border(0) + box.border(0) + for i in range(1 + (max_row * (page - 1)), max_row + 1 + + (max_row * (page - 1))): + if row_num == 0: + box.addnstr(1, 1, "There aren't any aliases yet. Press 'a' to add new one.", + width - 6, highlight_text) + else: + exec_string = ["[", selected_strings[i], "] ", stringsfull[i - 1]] + if (i + (max_row * (page - 1)) == (position + (max_row * (page - 1)))): + box.addnstr(i - (max_row * (page - 1)), 2, "".join( + exec_string), width - 6, highlight_text) + else: + box.addnstr(i - (max_row * (page - 1)), 2, "".join( + exec_string), width - 6, normal_text) + if i == row_num: + break + screen.refresh() + box.refresh() + key_pressed = screen.getch() + CursesExit() + + +if __name__ == "__main__": + try: + input = raw_input # Fix for Python 2.x + except NameError: + pass + + conf = configparser.RawConfigParser() + if not path.exists(conf_file): + open(conf_file, 'w') + conf.read(conf_file) + if len(argv) > 1: + try: + CMDOptions() + except KeyboardInterrupt: + exit() + except configparser.Error: + print("Error: can't parse your config file, please check it manually or make new one") + exit() + else: + try: + CursesMain() + except KeyboardInterrupt: + CursesExit() + except configparser.NoOptionError: + CursesExit("".join(["Error: can't parse your config file, please ", + "check it manually or make new one"])) + except curses.error: + CursesExit("".join(["Error: can't show some curses element, maybe ", + "your terminal is too small"]))