diff --git a/Cargo.lock b/Cargo.lock index 8665535..a588b72 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -47,7 +47,7 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" dependencies = [ - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -57,9 +57,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" dependencies = [ "anstyle", - "windows-sys", + "windows-sys 0.52.0", ] +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + [[package]] name = "autocfg" version = "1.3.0" @@ -72,6 +78,19 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +[[package]] +name = "brush-parser" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98bbbd29d06a11b0819fd28f076633bdb6e9943777fe0ed61201b283e7523f20" +dependencies = [ + "indenter", + "peg", + "thiserror", + "tracing", + "utf8-chars", +] + [[package]] name = "build-env" version = "0.3.1" @@ -90,12 +109,39 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "colorchoice" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + [[package]] name = "dlib" version = "0.5.2" @@ -111,6 +157,12 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + [[package]] name = "env_filter" version = "0.1.2" @@ -141,7 +193,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", ] [[package]] @@ -150,12 +213,27 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + [[package]] name = "is_terminal_polyfill" version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "libc" version = "0.2.155" @@ -169,7 +247,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags", + "libc", ] [[package]] @@ -210,6 +298,63 @@ dependencies = [ "autocfg", ] +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "peg" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "295283b02df346d1ef66052a757869b2876ac29a6bb0ac3f5f7cd44aebe40e8f" +dependencies = [ + "peg-macros", + "peg-runtime", +] + +[[package]] +name = "peg-macros" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdad6a1d9cf116a059582ce415d5f5566aabcd4008646779dab7fdc2a9a9d426" +dependencies = [ + "peg-runtime", + "proc-macro2", + "quote", +] + +[[package]] +name = "peg-runtime" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3aeb8f54c078314c2065ee649a7241f46b9d8e418e1a9581ba0546657d7aa3a" + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + [[package]] name = "pkg-config" version = "0.3.30" @@ -243,6 +388,17 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "redox_users" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + [[package]] name = "regex" version = "1.10.6" @@ -282,7 +438,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -301,26 +457,107 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" name = "swayidle-rs" version = "0.1.0" dependencies = [ + "brush-parser", + "dirs", "env_logger", + "itertools", "libc", "libsystemd-sys", "log", + "nix", "wayland-client", "wayland-sys", ] +[[package]] +name = "syn" +version = "2.0.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fceb41e3d546d0bd83421d3409b1460cc7444cd389341a4c880fe7a042cb3d7" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "utf8-chars" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb8b618e8c34f77c00710bbe6adaff80f3c856296be49241071e1aa08df45cec" +dependencies = [ + "arrayvec", +] + [[package]] name = "utf8parse" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "wayland-backend" version = "0.3.6" @@ -372,13 +609,37 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] @@ -387,28 +648,46 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -421,24 +700,48 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" diff --git a/Cargo.toml b/Cargo.toml index ef95fe1..cbc542d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,9 +6,13 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +brush-parser = "0.2.5" +dirs = "5.0.1" env_logger = "0.11.5" +itertools = "0.13.0" libc = "0.2.155" libsystemd-sys = "0.9.3" log = "0.4.22" +nix = { version = "0.29.0", features = ["fs"] } wayland-client = { version = "0.31.5", features = ["log"] } wayland-sys = { version = "0.31.4", features = ["client", "server"] } diff --git a/src/expand.rs b/src/expand.rs new file mode 100644 index 0000000..62d13f6 --- /dev/null +++ b/src/expand.rs @@ -0,0 +1,228 @@ +use brush_parser::word::WordPiece; +use itertools::Itertools; + +fn expand_backslash_escapes(s: &str) -> Result<(Vec, bool), ()> { + let mut result: Vec = vec![]; + let mut it = s.chars(); + while let Some(c) = it.next() { + if c != '\\' { + // Not a backslash, add and move on. + result.append(c.to_string().into_bytes().as_mut()); + continue; + } + + match it.next() { + Some('a') => result.push(b'\x07'), + Some('b') => result.push(b'\x08'), + Some('c') => { + if let Some(_next_next) = it.next() { + // "control character in ANSI C quotes" + return Err(()); + } else { + result.push(b'\\'); + result.push(b'c'); + } + } + Some('e' | 'E') => result.push(b'\x1b'), + Some('f') => result.push(b'\x0c'), + Some('n') => result.push(b'\n'), + Some('r') => result.push(b'\r'), + Some('t') => result.push(b'\t'), + Some('v') => result.push(b'\x0b'), + Some('\\') => result.push(b'\\'), + Some('\'') => result.push(b'\''), + Some('\"') => result.push(b'\"'), + Some('?') => result.push(b'?'), + Some('0') => { + // Consume 0-3 valid octal chars + let mut taken_so_far = 0; + let mut octal_chars: String = it + .take_while_ref(|c| { + if taken_so_far < 3 && matches!(*c, '0'..='7') { + taken_so_far += 1; + true + } else { + false + } + }) + .collect(); + + if octal_chars.is_empty() { + octal_chars.push('0'); + } + + let value = u8::from_str_radix(octal_chars.as_str(), 8).map_err(|_| ())?; + result.push(value); + } + Some('x') => { + // Consume 1-2 valid hex chars + let mut taken_so_far = 0; + let hex_chars: String = it + .take_while_ref(|c| { + if taken_so_far < 2 && c.is_ascii_hexdigit() { + taken_so_far += 1; + true + } else { + false + } + }) + .collect(); + + if hex_chars.is_empty() { + result.push(b'\\'); + result.append(c.to_string().into_bytes().as_mut()); + } else { + let value = u8::from_str_radix(hex_chars.as_str(), 16).map_err(|_| ())?; + result.push(value); + } + } + Some('u') => { + // Consume 1-4 hex digits + let mut taken_so_far = 0; + let hex_chars: String = it + .take_while_ref(|c| { + if taken_so_far < 4 && c.is_ascii_hexdigit() { + taken_so_far += 1; + true + } else { + false + } + }) + .collect(); + + if hex_chars.is_empty() { + result.push(b'\\'); + result.append(c.to_string().into_bytes().as_mut()); + } else { + let value = u16::from_str_radix(hex_chars.as_str(), 16).map_err(|_| ())?; + + if let Some(decoded) = char::from_u32(u32::from(value)) { + result.append(decoded.to_string().into_bytes().as_mut()); + } else { + result.push(b'\\'); + result.append(c.to_string().into_bytes().as_mut()); + } + } + } + Some('U') => { + // Consume 1-8 hex digits + let mut taken_so_far = 0; + let hex_chars: String = it + .take_while_ref(|c| { + if taken_so_far < 8 && c.is_ascii_hexdigit() { + taken_so_far += 1; + true + } else { + false + } + }) + .collect(); + + if hex_chars.is_empty() { + result.push(b'\\'); + result.append(c.to_string().into_bytes().as_mut()); + } else { + let value = u32::from_str_radix(hex_chars.as_str(), 16).map_err(|_| ())?; + + if let Some(decoded) = char::from_u32(value) { + result.append(decoded.to_string().into_bytes().as_mut()); + } else { + result.push(b'\\'); + result.append(c.to_string().into_bytes().as_mut()); + } + } + } + Some(c) => { + // Not a valid escape sequence. + result.push(b'\\'); + result.append(c.to_string().into_bytes().as_mut()); + } + None => { + // Trailing backslash. + result.push(b'\\'); + } + } + } + + Ok((result, true)) +} + +fn expand_tilde_expression(prefix: &str) -> Result { + if !prefix.is_empty() { + // "expansion: complex tilde expression" + return Err(()); + } + + if let Some(home_dir) = dirs::home_dir() { + Ok(home_dir.to_string_lossy().to_string()) + } else { + Err(()) + } +} + +fn expand_word_piece(word_piece: WordPiece) -> Result<(Vec, bool), ()> { + let mut cat = true; + let expansion: Vec = match word_piece { + brush_parser::word::WordPiece::Text(s) => vec![s], + brush_parser::word::WordPiece::SingleQuotedText(s) => vec![s], + brush_parser::word::WordPiece::AnsiCQuotedText(s) => { + let (expanded, _) = expand_backslash_escapes(s.as_str())?; + vec![String::from_utf8_lossy(expanded.as_slice()).into_owned()] + } + brush_parser::word::WordPiece::DoubleQuotedSequence(pieces) => { + let mut fields: Vec = vec![]; + + let pieces_is_empty = pieces.is_empty(); + + for piece in pieces { + let (this_fields, concatenate) = expand_word_piece(piece)?; + + if concatenate { + fields.push(this_fields.join(" ")); + } else { + fields.extend(this_fields); + }; + } + + // If there were no pieces, then make sure we yield a single field containing an + // empty, unsplittable string. + if pieces_is_empty { + fields.push(String::new()); + } + + cat = false; + fields + } + brush_parser::word::WordPiece::TildePrefix(prefix) => { + vec![expand_tilde_expression(prefix.as_str())?] + } + brush_parser::word::WordPiece::ParameterExpansion(_) => return Err(()), + brush_parser::word::WordPiece::CommandSubstitution(_) => return Err(()), + brush_parser::word::WordPiece::EscapeSequence(s) => { + let expanded = s.strip_prefix('\\').unwrap(); + vec![expanded.to_owned()] + } + brush_parser::word::WordPiece::ArithmeticExpression(_) => return Err(()), + }; + + Ok((expansion, cat)) +} +pub fn wordexp(cmd: &str) -> Result, ()> { + let frags = brush_parser::word::parse( + cmd, + &brush_parser::ParserOptions { + enable_extended_globbing: false, + posix_mode: false, + sh_mode: false, + tilde_expansion: true, + }, + ) + .map_err(|_| ())?; + Ok(frags + .into_iter() + .map(|x| Ok(expand_word_piece(x)?.0)) + .collect::, _>>()? + .into_iter() + .flatten() + .collect()) +} diff --git a/src/main.rs b/src/main.rs index d5198c2..481ea86 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,9 +8,11 @@ use std::{ borrow::Cow, env, ffi::{CStr, CString}, - mem, - mem::MaybeUninit, + fs::File, + io::{BufRead, BufReader}, + mem::{self, MaybeUninit}, os::raw::c_void, + path::Path, process, ptr, }; @@ -18,9 +20,8 @@ use ext_idle_notify_v1_protocol::{ ext_idle_notification_v1_interface, ext_idle_notifier_v1_interface, }; use libc::{ - __errno_location, abort, access, calloc, close, execvp, exit, fclose, fopen, fork, free, - getenv, getline, getopt, getpid, printf, sigemptyset, signal, sigprocmask, sigset_t, size_t, - sprintf, strcmp, strdup, strlen, strncmp, strstr, strtoul, waitpid, + __errno_location, abort, calloc, close, execvp, exit, fork, getenv, getopt, getpid, + sigemptyset, signal, sigprocmask, sigset_t, size_t, sprintf, strcmp, strdup, strstr, waitpid, }; use libsystemd_sys::bus::{ sd_bus, sd_bus_call_method, sd_bus_default_system, sd_bus_error, sd_bus_error_free, @@ -29,6 +30,7 @@ use libsystemd_sys::bus::{ sd_bus_message_read, sd_bus_message_read_basic, sd_bus_message_skip, sd_bus_message_unref, sd_bus_process, sd_bus_slot, }; +use nix::unistd::AccessFlags; use wayland_client::protocol::__interfaces::{wl_registry_interface, wl_seat_interface}; use wayland_sys::{ client::{ @@ -44,6 +46,7 @@ use wayland_sys::{ }, }; +mod expand; mod ext_idle_notify_v1_protocol; #[repr(C)] @@ -76,12 +79,6 @@ extern "C" { flags: u32, _: ... ) -> *mut wl_proxy; - fn wordexp( - __words: *const libc::c_char, - __pwordexp: *mut wordexp_t, - __flags: libc::c_int, - ) -> libc::c_int; - fn wordfree(__wordexp: *mut wordexp_t); fn sd_bus_match_signal( bus_0: *mut sd_bus, ret: *mut *mut sd_bus_slot, @@ -119,13 +116,6 @@ const WL_EVENT_WRITABLE: u32 = 2; const WL_EVENT_READABLE: u32 = 1; #[derive(Copy, Clone)] #[repr(C)] -struct wordexp_t { - we_wordc: size_t, - we_wordv: *mut *mut libc::c_char, - we_offs: size_t, -} -#[derive(Copy, Clone)] -#[repr(C)] struct ext_idle_notification_v1_listener { idled: Option ()>, resumed: Option ()>, @@ -170,7 +160,7 @@ struct seat { capabilities: u32, } #[inline] -unsafe extern "C" fn wl_display_get_registry(wl_display: *mut wl_display) -> *mut wl_registry { +unsafe fn wl_display_get_registry(wl_display: *mut wl_display) -> *mut wl_registry { wl_proxy_marshal_flags( wl_display.cast(), 1, @@ -182,7 +172,7 @@ unsafe extern "C" fn wl_display_get_registry(wl_display: *mut wl_display) -> *mu .cast() } #[inline] -unsafe extern "C" fn wl_registry_add_listener( +unsafe fn wl_registry_add_listener( wl_registry: *mut wl_registry, listener: *mut wl_registry_listener, data: *mut libc::c_void, @@ -190,7 +180,7 @@ unsafe extern "C" fn wl_registry_add_listener( wl_proxy_add_listener(wl_registry.cast(), listener.cast(), data) } #[inline] -unsafe extern "C" fn wl_registry_bind( +unsafe fn wl_registry_bind( wl_registry: *mut wl_registry, name: u32, interface: *const wl_interface, @@ -210,7 +200,7 @@ unsafe extern "C" fn wl_registry_bind( .cast() } #[inline] -unsafe extern "C" fn wl_seat_add_listener( +unsafe fn wl_seat_add_listener( wl_seat: *mut wl_seat, listener: *const wl_seat_listener, data: *mut libc::c_void, @@ -218,7 +208,7 @@ unsafe extern "C" fn wl_seat_add_listener( wl_proxy_add_listener(wl_seat.cast(), listener.cast_mut().cast(), data) } #[inline] -unsafe extern "C" fn ext_idle_notifier_v1_get_idle_notification( +unsafe fn ext_idle_notifier_v1_get_idle_notification( ext_idle_notifier_v1: *mut ext_idle_notifier_v1, timeout: u32, seat_0: *mut wl_seat, @@ -236,7 +226,7 @@ unsafe extern "C" fn ext_idle_notifier_v1_get_idle_notification( .cast() } #[inline] -unsafe extern "C" fn ext_idle_notification_v1_add_listener( +unsafe fn ext_idle_notification_v1_add_listener( ext_idle_notification_v1: *mut ext_idle_notification_v1, listener: *const ext_idle_notification_v1_listener, data: *mut libc::c_void, @@ -248,7 +238,7 @@ unsafe extern "C" fn ext_idle_notification_v1_add_listener( ) } #[inline] -unsafe extern "C" fn ext_idle_notification_v1_destroy( +unsafe fn ext_idle_notification_v1_destroy( ext_idle_notification_v1: *mut ext_idle_notification_v1, ) { wl_proxy_marshal_flags( @@ -261,7 +251,6 @@ unsafe extern "C" fn ext_idle_notification_v1_destroy( } static mut idle_notifier: *mut ext_idle_notifier_v1 = ptr::null_mut(); static mut seat: *mut wl_seat = ptr::null_mut(); -#[no_mangle] static mut state: swayidle_state = swayidle_state { display: ptr::null_mut(), event_loop: ptr::null_mut(), @@ -282,8 +271,7 @@ static mut state: swayidle_state = swayidle_state { timeouts_enabled: false, wait: false, }; -#[no_mangle] -unsafe extern "C" fn swayidle_log_init(verbosity: log_importance) { +unsafe fn swayidle_log_init(verbosity: log_importance) { env_logger::builder() .filter( None, @@ -368,7 +356,7 @@ impl swayidle_state { } }; } - unsafe extern "C" fn connect_to_bus(&self) { + unsafe fn connect_to_bus(&self) { let ret = sd_bus_default_system(ptr::addr_of_mut!(bus)); if ret < 0 { *__errno_location() = -ret; @@ -444,7 +432,7 @@ impl swayidle_state { &mut self, argc: usize, argv: *mut *mut libc::c_char, - config_path: *mut *mut libc::c_char, + config_path: &mut Option, ) -> libc::c_int { let mut c: libc::c_int; loop { @@ -452,55 +440,65 @@ impl swayidle_state { if c == -1 { break; } - match c { - 67 => { - free((*config_path).cast()); - *config_path = strdup(optarg); + match c as u8 { + b'C' => { + *config_path = Some(read_str(optarg).into()); } - 100 => { + b'd' => { swayidle_log_init(LOG_DEBUG); } - 119 => { + b'w' => { self.wait = 1 != 0; } - 83 => { + b'S' => { self.seat_name = read_str2(optarg); } - 104 | 63 => { - printf(b"Usage: %s [OPTIONS]\n\0".as_ptr().cast(), *argv); - printf(b" -h\tthis help menu\n\0".as_ptr().cast()); - printf(b" -C\tpath to config file\n\0".as_ptr().cast()); - printf(b" -d\tdebug\n\0".as_ptr().cast()); - printf(b" -w\twait for command to finish\n\0".as_ptr().cast()); - printf(b" -S\tpick the seat to work with\n\0".as_ptr().cast()); + b'h' | b'?' => { + println!("Usage: {} [OPTIONS]", read_str(*argv)); + println!(" -h\tthis help menu"); + println!(" -C\tpath to config file"); + println!(" -d\tdebug"); + println!(" -w\twait for command to finish"); + println!(" -S\tpick the seat to work with"); return 1; } _ => return 1, } } - let mut i = optind as usize; - while i < argc { - if strcmp(b"timeout\0".as_ptr().cast(), *argv.add(i)) == 0 { - log::debug!("Got timeout"); - i += self.parse_timeout(argc - i, &mut *argv.add(i)); - } else if strcmp(b"before-sleep\0".as_ptr().cast(), *argv.add(i)) == 0 { - log::debug!("Got before-sleep"); - i += self.parse_sleep(argc - i, &mut *argv.add(i)); - } else if strcmp(b"after-resume\0".as_ptr().cast(), *argv.add(i)) == 0 { - log::debug!("Got after-resume"); - i += self.parse_resume(argc - i, &mut *argv.add(i)); - } else if strcmp(b"lock\0".as_ptr().cast(), *argv.add(i)) == 0 { - log::debug!("Got lock"); - i += self.parse_lock(argc - i, &mut *argv.add(i)); - } else if strcmp(b"unlock\0".as_ptr().cast(), *argv.add(i)) == 0 { - log::debug!("Got unlock"); - i += self.parse_unlock(argc - i, &mut *argv.add(i)); - } else if strcmp(b"idlehint\0".as_ptr().cast(), *argv.add(i)) == 0 { - log::debug!("Got idlehint"); - i += self.parse_idlehint(argc - i, &mut *argv.add(i)); - } else { - log::error!("Unsupported command '{}'", read_str(*argv.add(i))); - return 1; + let args = (optind as usize..argc) + .map(|i| read_str(*argv.add(i)).into_owned()) + .collect::>(); + let mut i = 0; + while i < args.len() { + match args[i].as_str() { + "timeout" => { + log::debug!("Got timeout"); + i += self.parse_timeout(&args[i..]); + } + "before-sleep" => { + log::debug!("Got before-sleep"); + i += self.parse_sleep(&args[i..]); + } + "after-resume" => { + log::debug!("Got after-resume"); + i += self.parse_resume(&args[i..]); + } + "lock" => { + log::debug!("Got lock"); + i += self.parse_lock(&args[i..]); + } + "unlock" => { + log::debug!("Got unlock"); + i += self.parse_unlock(&args[i..]); + } + "idlehint" => { + log::debug!("Got idlehint"); + i += self.parse_idlehint(&args[i..]); + } + _ => { + log::error!("Unsupported command '{}'", read_str(*argv.add(i))); + return 1; + } } } 0 @@ -594,7 +592,7 @@ impl swayidle_state { cmd = (*cmd).link.next.cast(); } } - unsafe extern "C" fn disable_timeouts(&mut self) { + unsafe fn disable_timeouts(&mut self) { if !self.timeouts_enabled { return; } @@ -610,20 +608,20 @@ impl swayidle_state { set_idle_hint(false); } } - unsafe fn parse_timeout(&mut self, argc: usize, argv: *mut *mut libc::c_char) -> usize { - if argc < 3 { + unsafe fn parse_timeout(&mut self, args: &[String]) -> usize { + if args.len() < 3 { log::error!( "Too few parameters to timeout command. Usage: timeout " ); exit(-1); } - let cmd = build_timeout_cmd(argv); + let cmd = build_timeout_cmd(args); log::debug!("Register idle timeout at {} ms", (*cmd).timeout,); log::debug!("Setup idle"); - (*cmd).idle_cmd = parse_command(argc - 2, &mut *argv.offset(2)); - let result = if argc >= 5 && strcmp(b"resume\0".as_ptr().cast(), *argv.offset(3)) == 0 { + (*cmd).idle_cmd = parse_command(&args[2..]); + let result = if args.len() >= 5 && args[3] == "resume" { log::debug!("Setup resume"); - (*cmd).resume_cmd = parse_command(argc - 4, &mut *argv.offset(4)); + (*cmd).resume_cmd = parse_command(&args[4..]); 5 } else { 3 @@ -631,123 +629,111 @@ impl swayidle_state { wl_list_insert(&mut self.timeout_cmds, &mut (*cmd).link); result } - unsafe fn parse_sleep(&mut self, argc: usize, argv: *mut *mut libc::c_char) -> usize { - if argc < 2 { + unsafe fn parse_sleep(&mut self, args: &[String]) -> usize { + if args.len() < 2 { log::error!( "Too few parameters to before-sleep command. Usage: before-sleep " ); exit(-1); } - self.before_sleep_cmd = parse_command(argc - 1, &mut *argv.offset(1)); + self.before_sleep_cmd = parse_command(&args[1..]); if let Some(before_sleep_cmd) = &self.before_sleep_cmd { log::debug!("Setup sleep lock: {before_sleep_cmd}"); } 2 } - unsafe fn parse_resume(&mut self, argc: usize, argv: *mut *mut libc::c_char) -> usize { - if argc < 2 { + unsafe fn parse_resume(&mut self, args: &[String]) -> usize { + if args.len() < 2 { log::error!( "Too few parameters to after-resume command. Usage: after-resume " ); exit(-1); } - self.after_resume_cmd = parse_command(argc - 1, &mut *argv.offset(1)); + self.after_resume_cmd = parse_command(&args[1..]); if let Some(after_resume_cmd) = &self.after_resume_cmd { log::debug!("Setup resume hook: {after_resume_cmd}"); } 2 } - unsafe fn parse_lock(&mut self, argc: usize, argv: *mut *mut libc::c_char) -> usize { - if argc < 2 { + unsafe fn parse_lock(&mut self, args: &[String]) -> usize { + if args.len() < 2 { log::error!("Too few parameters to lock command. Usage: lock "); exit(-1); } - self.logind_lock_cmd = parse_command(argc - 1, &mut *argv.offset(1)); + self.logind_lock_cmd = parse_command(&args[1..]); if let Some(logind_lock_cmd) = &self.logind_lock_cmd { log::debug!("Setup lock hook: {logind_lock_cmd}"); } 2 } - unsafe fn parse_unlock(&mut self, argc: usize, argv: *mut *mut libc::c_char) -> usize { - if argc < 2 { + unsafe fn parse_unlock(&mut self, args: &[String]) -> usize { + if args.len() < 2 { log::error!("Too few parameters to unlock command. Usage: unlock "); exit(-1); } - self.logind_unlock_cmd = parse_command(argc - 1, &mut *argv.offset(1)); + self.logind_unlock_cmd = parse_command(&args[1..]); if let Some(logind_unlock_cmd) = &self.logind_unlock_cmd { log::debug!("Setup unlock hook: {logind_unlock_cmd}"); } 2 } - unsafe fn parse_idlehint(&mut self, argc: usize, argv: *mut *mut libc::c_char) -> usize { + unsafe fn parse_idlehint(&mut self, args: &[String]) -> usize { if self.logind_idlehint { log::error!("Cannot add multiple idlehint events"); exit(-1); } - if argc < 2 { + if args.len() < 2 { log::error!("Too few parameters to idlehint command. Usage: idlehint "); exit(-1); } - let cmd: *mut swayidle_timeout_cmd = build_timeout_cmd(argv); + let cmd: *mut swayidle_timeout_cmd = build_timeout_cmd(args); (*cmd).idlehint = true; log::debug!("Register idlehint timeout at {} ms", (*cmd).timeout,); wl_list_insert(&mut self.timeout_cmds, &mut (*cmd).link); self.logind_idlehint = true; 2 } - unsafe extern "C" fn load_config(&mut self, config_path: *const libc::c_char) -> i32 { - let f = fopen(config_path, b"r\0".as_ptr().cast()); - if f.is_null() { + unsafe fn load_config(&mut self, config_path: &Path) -> i32 { + let Ok(file) = File::open(config_path) else { return -2; - } + }; + let mut file = BufReader::new(file); let mut lineno: size_t = 0; - let mut line = ptr::null_mut(); - let mut n = 0; - let mut nread; + let mut line = String::new(); loop { - nread = getline(&mut line, &mut n, f); - if nread == -1 { + line.clear(); + let Ok(nread) = file.read_line(&mut line) else { + break; + }; + if nread == 0 { break; } lineno = lineno.wrapping_add(1); - if *line.offset(nread - 1) as u8 == b'\n' { - *line.offset(nread - 1) = 0; - } - if strlen(line) == 0 || *line as u8 == b'#' { + let line = line.strip_suffix('\n').unwrap_or(&line); + if line.is_empty() || line.starts_with('#') { continue; } - let mut i = 0; - while *line.add(i) as u8 != b'\0' && *line.add(i) as u8 != b' ' { - i = i.wrapping_add(1); - } - let mut p = wordexp_t { - we_wordc: 0, - we_wordv: ptr::null_mut(), - we_offs: 0, - }; - wordexp(line, &mut p, 0); - if strncmp(b"timeout\0".as_ptr().cast(), line, i) == 0 { - self.parse_timeout(p.we_wordc, p.we_wordv); - } else if strncmp(b"before-sleep\0".as_ptr().cast(), line, i) == 0 { - self.parse_sleep(p.we_wordc, p.we_wordv); - } else if strncmp(b"after-resume\0".as_ptr().cast(), line, i) == 0 { - self.parse_resume(p.we_wordc, p.we_wordv); - } else if strncmp(b"lock\0".as_ptr().cast(), line, i) == 0 { - self.parse_lock(p.we_wordc, p.we_wordv); - } else if strncmp(b"unlock\0".as_ptr().cast(), line, i) == 0 { - self.parse_unlock(p.we_wordc, p.we_wordv); - } else if strncmp(b"idlehint\0".as_ptr().cast(), line, i) == 0 { - self.parse_idlehint(p.we_wordc, p.we_wordv); + let (cmd, args) = if let Some((a, b)) = line.split_once(' ') { + (a, Some(b)) } else { - *line.add(i) = 0; - log::error!("Unexpected keyword {} in line {lineno}", read_str(line)); - free(line.cast::()); - return -22; - } - wordfree(&mut p); + (line, None) + }; + let args = args + .map(|args| expand::wordexp(args).unwrap()) + .unwrap_or_default(); + match cmd { + "timeout" => self.parse_timeout(&args), + "before-sleep" => self.parse_sleep(&args), + "after-resume" => self.parse_resume(&args), + "lock" => self.parse_lock(&args), + "unlock" => self.parse_unlock(&args), + "idlehint" => self.parse_idlehint(&args), + _ => { + log::error!("Unexpected keyword {cmd} in line {lineno}"); + return -22; + } + }; } - free(line.cast()); - fclose(f); 0 } } @@ -773,7 +759,7 @@ unsafe fn read_str(ptr: *const libc::c_char) -> Cow<'static, str> { static mut sleep_lock_fd: libc::c_int = -1; static mut bus: *mut sd_bus = ptr::null_mut(); static mut session_name: *mut libc::c_char = ptr::null_mut(); -unsafe extern "C" fn acquire_inhibitor_lock( +unsafe fn acquire_inhibitor_lock( type_0: *const libc::c_char, mode: *const libc::c_char, fd: *mut libc::c_int, @@ -835,13 +821,13 @@ unsafe extern "C" fn acquire_inhibitor_lock( sd_bus_error_free(&mut error); sd_bus_message_unref(msg); } -unsafe extern "C" fn release_inhibitor_lock(fd: libc::c_int) { +unsafe fn release_inhibitor_lock(fd: libc::c_int) { if fd >= 0 { log::debug!("Releasing inhibitor lock {}", fd); close(fd); } } -unsafe extern "C" fn set_idle_hint(hint: bool) { +unsafe fn set_idle_hint(hint: bool) { log::debug!("SetIdleHint {}", i32::from(hint)); let mut msg = ptr::null_mut(); let mut error = sd_bus_error { @@ -869,7 +855,7 @@ unsafe extern "C" fn set_idle_hint(hint: bool) { sd_bus_error_free(&mut error); sd_bus_message_unref(msg); } -unsafe extern "C" fn get_logind_idle_inhibit() -> bool { +unsafe fn get_logind_idle_inhibit() -> bool { let mut locks: *const libc::c_char = ptr::null(); let res: bool; let mut reply = ptr::null_mut(); @@ -1032,7 +1018,7 @@ unsafe extern "C" fn dbus_event( } count } -unsafe extern "C" fn set_session() { +unsafe fn set_session() { let mut is_error = false; let mut msg = ptr::null_mut(); let mut error = sd_bus_error { @@ -1089,7 +1075,7 @@ unsafe extern "C" fn set_session() { sd_bus_error_free(&mut error); sd_bus_message_unref(msg); } -unsafe extern "C" fn setup_sleep_listener() { +unsafe fn setup_sleep_listener() { let ret = sd_bus_match_signal( bus, ptr::null_mut(), @@ -1114,7 +1100,7 @@ unsafe extern "C" fn setup_sleep_listener() { ptr::addr_of_mut!(sleep_lock_fd), ); } -unsafe extern "C" fn setup_lock_listener() { +unsafe fn setup_lock_listener() { let ret = sd_bus_match_signal( bus, ptr::null_mut(), @@ -1133,7 +1119,7 @@ unsafe extern "C" fn setup_lock_listener() { ); } } -unsafe extern "C" fn setup_unlock_listener() { +unsafe fn setup_unlock_listener() { let ret = sd_bus_match_signal( bus, ptr::null_mut(), @@ -1152,7 +1138,7 @@ unsafe extern "C" fn setup_unlock_listener() { ); } } -unsafe extern "C" fn setup_property_changed_listener() { +unsafe fn setup_property_changed_listener() { let ret = sd_bus_match_signal( bus, ptr::null_mut(), @@ -1210,13 +1196,13 @@ static mut registry_listener: wl_registry_listener = wl_registry_listener { global: Some(handle_global), global_remove: Some(handle_global_remove), }; -unsafe extern "C" fn destroy_cmd_timer(cmd: *mut swayidle_timeout_cmd) { +unsafe fn destroy_cmd_timer(cmd: *mut swayidle_timeout_cmd) { if !(*cmd).idle_notification.is_null() { ext_idle_notification_v1_destroy((*cmd).idle_notification); (*cmd).idle_notification = ptr::null_mut(); } } -unsafe extern "C" fn register_timeout(cmd: *mut swayidle_timeout_cmd, timeout: libc::c_int) { +unsafe fn register_timeout(cmd: *mut swayidle_timeout_cmd, timeout: libc::c_int) { destroy_cmd_timer(cmd); if timeout < 0 { log::debug!("Not registering idle timeout"); @@ -1263,30 +1249,24 @@ static mut idle_notification_listener: ext_idle_notification_v1_listener = idled: Some(handle_idled), resumed: Some(handle_resumed), }; -unsafe fn parse_command(argc: usize, argv: *mut *mut libc::c_char) -> Option { - if argc < 1 { - log::error!("Missing command"); - return None; - } - let Some(ret) = read_str2(*argv) else { +unsafe fn parse_command(args: &[String]) -> Option { + let Some(ret) = args.first() else { log::error!("Missing command"); return None; }; log::debug!("Command: {ret}"); - Some(ret) + Some(ret.clone()) } -unsafe extern "C" fn build_timeout_cmd(argv: *mut *mut libc::c_char) -> *mut swayidle_timeout_cmd { +unsafe fn build_timeout_cmd(args: &[String]) -> *mut swayidle_timeout_cmd { *__errno_location() = 0; - let mut endptr = ptr::null_mut(); - let seconds = strtoul(*argv.offset(1), &mut endptr, 10) as libc::c_int; - if *__errno_location() != 0 || *endptr as u8 != b'\0' { + let Ok(seconds) = args[1].parse::() else { log::error!( "Invalid {} parameter '{}', it should be a numeric value representing seconds", - read_str(*argv), - read_str(*argv.offset(1)), + args[0], + args[1], ); exit(-1); - } + }; Box::into_raw(Box::new(swayidle_timeout_cmd { idlehint: false, resume_pending: false, @@ -1311,69 +1291,54 @@ unsafe extern "C" fn display_event( ) -> libc::c_int { state.display_event(mask) } -unsafe extern "C" fn get_config_path() -> *mut libc::c_char { - static mut config_paths: [*const libc::c_char; 3] = [ - b"$XDG_CONFIG_HOME/swayidle/config\0" - .as_ptr() - .cast::(), - b"$HOME/.swayidle/config\0".as_ptr().cast::(), - b"/usr/local/etc/swayidle/config\0" - .as_ptr() - .cast::(), +unsafe fn get_config_path() -> Option { + static mut config_paths: [&str; 3] = [ + "$XDG_CONFIG_HOME/swayidle/config", + "$HOME/.swayidle/config", + "/usr/local/etc/swayidle/config", ]; let config_home = getenv(b"XDG_CONFIG_HOME\0".as_ptr().cast::()); if config_home.is_null() || *config_home as u8 == b'\n' { - config_paths[0] = b"$HOME/.config/swayidle/config\0" - .as_ptr() - .cast::(); + config_paths[0] = "$HOME/.config/swayidle/config"; } - let mut p = wordexp_t { - we_wordc: 0, - we_wordv: ptr::null_mut(), - we_offs: 0, - }; let mut path; let mut i = 0; while i < (mem::size_of::<[*mut libc::c_char; 3]>()) .wrapping_div(mem::size_of::<*mut libc::c_char>()) { - if wordexp(config_paths[i], &mut p, 0) == 0 { - path = strdup(*p.we_wordv); - wordfree(&mut p); - if !path.is_null() && access(path, 4) == 0 { - return path; + if let Ok(res) = expand::wordexp(config_paths[i]) { + path = &res[0]; + if !path.is_empty() && nix::unistd::access(path.as_str(), AccessFlags::R_OK).is_ok() { + return Some(path.clone()); } - free(path.cast()); } i = i.wrapping_add(1); } - ptr::null_mut() + None } unsafe fn main_0(argc: usize, argv: *mut *mut libc::c_char) -> libc::c_int { state.init(); - let mut config_path = ptr::null_mut(); + let mut config_path = None; if state.parse_args(argc, argv, &mut config_path) != 0 { - free(config_path.cast()); return -1; } - if config_path.is_null() { + if config_path.is_none() { config_path = get_config_path(); } - let config_load = if config_path.is_null() { - -2 + let config_load = if let Some(ref config_path) = config_path { + state.load_config(Path::new(&config_path)) } else { - state.load_config(config_path) + -2 }; if config_load == -2 { log::debug!("No config file found."); } else if config_load == -22 { - log::error!("Config file {} has errors, exiting.", read_str(config_path)); + log::error!("Config file {} has errors, exiting.", config_path.unwrap()); exit(-1); } else { - log::debug!("Loaded config at {}", read_str(config_path)); + log::debug!("Loaded config at {}", config_path.unwrap()); } - free(config_path.cast()); state.event_loop = wl_event_loop_create(); wl_event_loop_add_signal(state.event_loop, 2, handle_signal, ptr::null_mut()); wl_event_loop_add_signal(state.event_loop, 15, handle_signal, ptr::null_mut());