├── .gitignore ├── Cargo.toml ├── src └── main.rs └── Cargo.lock /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "i3-tmux-integration" 3 | version = "0.1.0" 4 | authors = ["roblabla "] 5 | 6 | [dependencies] 7 | pty = "*" 8 | libc = "*" 9 | termion = "*" 10 | twoway = "*" 11 | chan = "*" 12 | log = "*" 13 | fern = "*" 14 | i3ipc = "*" 15 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate pty; 2 | extern crate libc; 3 | extern crate termion; 4 | extern crate twoway; 5 | #[macro_use(chan_select)] 6 | extern crate chan; 7 | #[macro_use] 8 | extern crate log; 9 | extern crate fern; 10 | extern crate i3ipc; 11 | 12 | use i3ipc::I3Connection; 13 | use std::env; 14 | use std::ptr; 15 | use std::io::{self, Read, Write}; 16 | use std::ffi::CString; 17 | use std::thread; 18 | use termion::raw::IntoRawMode; 19 | use std::os::unix::ffi::OsStringExt; 20 | use pty::fork::*; 21 | 22 | const TMUX_DEC : [u8; 7]= [0o33, 'P' as u8, '1' as u8, '0' as u8, '0' as u8, '0' as u8, 'p' as u8]; 23 | 24 | #[derive(Debug)] 25 | enum InputModes { 26 | LookingForTmuxDec(I3Connection), 27 | TmuxWaiting(I3Connection, String), 28 | TmuxCommandBlock(I3Connection, String) 29 | } 30 | 31 | impl InputModes { 32 | fn handle_input(mut self, bytes: &[u8]) -> Self { 33 | match self { 34 | InputModes::LookingForTmuxDec(mut ipc) => match twoway::find_bytes(bytes, &TMUX_DEC) { 35 | Some(x) => { 36 | std::io::stdout().write(&bytes[..x]).unwrap(); 37 | print_tmux_msg(); 38 | // TODO: Make sure it creates a new workspace 39 | ipc.command("workspace tmux").unwrap(); 40 | self = InputModes::TmuxWaiting(ipc, "tmux".into()); 41 | self.handle_input(&bytes[x + TMUX_DEC.len()..]) 42 | }, 43 | None => { 44 | std::io::stdout().write(bytes).unwrap(); 45 | std::io::stdout().flush().unwrap(); 46 | InputModes::LookingForTmuxDec(ipc) 47 | } 48 | }, 49 | InputModes::TmuxWaiting(ipc, workspace) => { 50 | info!("Command : {}", std::str::from_utf8(bytes).unwrap_or("BROKEN_UTF8")); 51 | let mut size = 0usize; 52 | for line in bytes.split(|&e| e == '\n' as u8) { 53 | size += line.len() + 1; 54 | let (cmd, args) = line.split_at(line.iter().position(|&e| e == ' ' as u8).unwrap_or(line.len().saturating_sub(1))); 55 | match std::str::from_utf8(cmd).unwrap() { 56 | "%begin" => (), 57 | "%exit" => { 58 | self = InputModes::LookingForTmuxDec(ipc); 59 | return self.handle_input(&bytes[size..]); 60 | }, 61 | "%layout-change" => (), 62 | "%output" => (), 63 | "%session-changed" => (), 64 | "%session-renamed" => (), 65 | "%sessions-changed" => (), 66 | "%unlinked-window-add" => (), 67 | "%window-add" => (), 68 | "%window-close" => (), 69 | "%window-renamed" => (), 70 | cmd => { 71 | error!("Unknown command \"{}\"", cmd); 72 | } 73 | } 74 | } 75 | InputModes::TmuxWaiting(ipc, workspace) 76 | }, 77 | InputModes::TmuxCommandBlock(_, _) => { 78 | self 79 | } 80 | } 81 | } 82 | } 83 | 84 | fn print_tmux_msg() { 85 | println!("** tmux mode started **\r"); 86 | println!("Command Menu\r"); 87 | println!("----------------------------\r"); 88 | println!("esc Detach cleanly.\r"); 89 | println!(" X Force-quit tmux mode.\r"); 90 | println!(" L Toggle logging.\r"); 91 | println!(" C Run tmux command.\r"); 92 | } 93 | 94 | // TODO: Figure out safe, correct way to send a slice via chan. 95 | fn readers<'a, 'b>(mut pty_master : Master, pid: libc::pid_t) -> (chan::Receiver<([u8;4096], usize)>, chan::Receiver<([u8;4096], usize)>, chan::Receiver<()>) { 96 | let (tx1, rx1) = chan::sync(0); // TODO: might want to make this a sync channel instead of rdv 97 | let (tx2, rx2) = chan::sync(0); 98 | let (tx3, rx3) = chan::sync(0); 99 | thread::spawn(move || { 100 | let mut bytes = [0u8; 4096]; 101 | loop { 102 | let read = match pty_master.read(&mut bytes) { 103 | Ok(read) => read, 104 | Err(_) => { 105 | error!("Got an error reading from stdin"); 106 | break 107 | } 108 | }; 109 | tx1.send((bytes, read)); 110 | } 111 | }); 112 | thread::spawn(move || { 113 | let mut bytes = [0u8; 4096]; 114 | loop { 115 | let read = match io::stdin().read(&mut bytes) { 116 | Ok(read) => read, 117 | Err(_) => { 118 | info!("Got an error reading from stdout"); 119 | break 120 | } 121 | }; 122 | tx2.send((bytes, read)); 123 | } 124 | }); 125 | thread::spawn(move || { 126 | unsafe { libc::waitpid(pid, &mut 0, 0) }; 127 | tx3.send(()); 128 | }); 129 | return (rx1, rx2, rx3); 130 | } 131 | 132 | fn main() { 133 | let logger_config = fern::DispatchConfig { 134 | format: Box::new(|msg, _, _| { 135 | format!("{}", msg) 136 | }), 137 | output: vec![fern::OutputConfig::file("output.log")], 138 | level: log::LogLevelFilter::Trace 139 | }; 140 | fern::init_global_logger(logger_config, log::LogLevelFilter::Trace).unwrap(); 141 | 142 | let ipc = match I3Connection::connect() { 143 | Ok(ipc) => ipc, 144 | Err(err) => { 145 | write!(std::io::stderr(), "Error connecting to i3 IPC! {}\n", err).unwrap(); 146 | let args : Vec = env::args_os().skip(1).map(|e| CString::new(e.into_vec()).unwrap()).collect(); 147 | let mut args_ptrs : Vec<_> = args.iter().map(|e| e.as_ptr()).collect(); 148 | args_ptrs.push(ptr::null()); 149 | let args_ptr = args_ptrs.as_ptr(); 150 | let cmd = args_ptrs[0]; 151 | unsafe { libc::execvp(cmd, args_ptr) }; 152 | unreachable!(); 153 | } 154 | }; 155 | 156 | let mut fork = Fork::from_ptmx().unwrap(); 157 | if let Fork::Parent(pid, ref mut master) = fork { 158 | let mut raw_stdout = std::io::stdout().into_raw_mode().unwrap(); 159 | let (input, output, close) = readers(master.clone(), pid); 160 | let mut input_mode = InputModes::LookingForTmuxDec(ipc); 161 | loop { 162 | chan_select! { 163 | input.recv() -> val => { 164 | let (bytes, read) = match val { 165 | Some((bytes, read)) => (bytes, read), 166 | None => break 167 | }; 168 | input_mode = input_mode.handle_input(&bytes[..read]); 169 | }, 170 | output.recv() -> val => { 171 | let (bytes, read) = match val { 172 | Some((bytes, read)) => (bytes, read), 173 | None => break 174 | }; 175 | if let InputModes::LookingForTmuxDec(_) = input_mode { 176 | master.write(&bytes[..read]).unwrap(); 177 | master.flush().unwrap(); 178 | } else { if bytes.iter().find(|e| **e == 27).is_some() { 179 | info!("Detaching"); 180 | master.write("detach\r\n".as_ref()).unwrap(); 181 | master.flush().unwrap(); 182 | } else { 183 | info!("{:?}", &bytes[..read]); 184 | }} 185 | }, 186 | close.recv() -> _ => { 187 | break 188 | } 189 | } 190 | } 191 | } else { 192 | let cmd = CString::new(env::args_os().nth(1).unwrap().into_vec()).unwrap(); 193 | let args = [cmd.as_ptr(), ptr::null()].as_mut_ptr(); 194 | unsafe { libc::execvp(cmd.as_ptr(), args) }; 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | [root] 2 | name = "i3-tmux-integration" 3 | version = "0.1.0" 4 | dependencies = [ 5 | "chan 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", 6 | "fern 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", 7 | "i3ipc 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 8 | "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", 9 | "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 10 | "pty 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 11 | "termion 1.1.3 (registry+https://github.com/rust-lang/crates.io-index)", 12 | "twoway 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", 13 | ] 14 | 15 | [[package]] 16 | name = "byteorder" 17 | version = "0.3.13" 18 | source = "registry+https://github.com/rust-lang/crates.io-index" 19 | 20 | [[package]] 21 | name = "chan" 22 | version = "0.1.18" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | dependencies = [ 25 | "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", 26 | ] 27 | 28 | [[package]] 29 | name = "debug-builders" 30 | version = "0.1.0" 31 | source = "registry+https://github.com/rust-lang/crates.io-index" 32 | 33 | [[package]] 34 | name = "errno" 35 | version = "0.1.8" 36 | source = "registry+https://github.com/rust-lang/crates.io-index" 37 | dependencies = [ 38 | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 39 | "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", 40 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 41 | ] 42 | 43 | [[package]] 44 | name = "fern" 45 | version = "0.3.5" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | dependencies = [ 48 | "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 49 | ] 50 | 51 | [[package]] 52 | name = "i3ipc" 53 | version = "0.4.2" 54 | source = "registry+https://github.com/rust-lang/crates.io-index" 55 | dependencies = [ 56 | "byteorder 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", 57 | "serde 0.6.15 (registry+https://github.com/rust-lang/crates.io-index)", 58 | "serde_json 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", 59 | "unix_socket 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", 60 | ] 61 | 62 | [[package]] 63 | name = "kernel32-sys" 64 | version = "0.2.2" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | dependencies = [ 67 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 68 | "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 69 | ] 70 | 71 | [[package]] 72 | name = "libc" 73 | version = "0.1.12" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | 76 | [[package]] 77 | name = "libc" 78 | version = "0.2.17" 79 | source = "registry+https://github.com/rust-lang/crates.io-index" 80 | 81 | [[package]] 82 | name = "log" 83 | version = "0.3.6" 84 | source = "registry+https://github.com/rust-lang/crates.io-index" 85 | 86 | [[package]] 87 | name = "memchr" 88 | version = "0.1.11" 89 | source = "registry+https://github.com/rust-lang/crates.io-index" 90 | dependencies = [ 91 | "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", 92 | ] 93 | 94 | [[package]] 95 | name = "num" 96 | version = "0.1.36" 97 | source = "registry+https://github.com/rust-lang/crates.io-index" 98 | dependencies = [ 99 | "num-bigint 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", 100 | "num-complex 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", 101 | "num-integer 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", 102 | "num-iter 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", 103 | "num-rational 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", 104 | "num-traits 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", 105 | ] 106 | 107 | [[package]] 108 | name = "num-bigint" 109 | version = "0.1.35" 110 | source = "registry+https://github.com/rust-lang/crates.io-index" 111 | dependencies = [ 112 | "num-integer 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", 113 | "num-traits 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", 114 | "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", 115 | "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", 116 | ] 117 | 118 | [[package]] 119 | name = "num-complex" 120 | version = "0.1.35" 121 | source = "registry+https://github.com/rust-lang/crates.io-index" 122 | dependencies = [ 123 | "num-traits 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", 124 | "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", 125 | ] 126 | 127 | [[package]] 128 | name = "num-integer" 129 | version = "0.1.32" 130 | source = "registry+https://github.com/rust-lang/crates.io-index" 131 | dependencies = [ 132 | "num-traits 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", 133 | ] 134 | 135 | [[package]] 136 | name = "num-iter" 137 | version = "0.1.32" 138 | source = "registry+https://github.com/rust-lang/crates.io-index" 139 | dependencies = [ 140 | "num-integer 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", 141 | "num-traits 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", 142 | ] 143 | 144 | [[package]] 145 | name = "num-rational" 146 | version = "0.1.35" 147 | source = "registry+https://github.com/rust-lang/crates.io-index" 148 | dependencies = [ 149 | "num-bigint 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", 150 | "num-integer 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", 151 | "num-traits 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", 152 | "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", 153 | ] 154 | 155 | [[package]] 156 | name = "num-traits" 157 | version = "0.1.36" 158 | source = "registry+https://github.com/rust-lang/crates.io-index" 159 | 160 | [[package]] 161 | name = "pty" 162 | version = "0.2.0" 163 | source = "registry+https://github.com/rust-lang/crates.io-index" 164 | dependencies = [ 165 | "chan 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", 166 | "errno 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", 167 | "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", 168 | ] 169 | 170 | [[package]] 171 | name = "rand" 172 | version = "0.3.14" 173 | source = "registry+https://github.com/rust-lang/crates.io-index" 174 | dependencies = [ 175 | "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", 176 | ] 177 | 178 | [[package]] 179 | name = "rustc-serialize" 180 | version = "0.3.19" 181 | source = "registry+https://github.com/rust-lang/crates.io-index" 182 | 183 | [[package]] 184 | name = "serde" 185 | version = "0.6.15" 186 | source = "registry+https://github.com/rust-lang/crates.io-index" 187 | dependencies = [ 188 | "num 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", 189 | ] 190 | 191 | [[package]] 192 | name = "serde_json" 193 | version = "0.6.1" 194 | source = "registry+https://github.com/rust-lang/crates.io-index" 195 | dependencies = [ 196 | "num 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", 197 | "serde 0.6.15 (registry+https://github.com/rust-lang/crates.io-index)", 198 | ] 199 | 200 | [[package]] 201 | name = "termion" 202 | version = "1.1.3" 203 | source = "registry+https://github.com/rust-lang/crates.io-index" 204 | dependencies = [ 205 | "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", 206 | ] 207 | 208 | [[package]] 209 | name = "twoway" 210 | version = "0.1.3" 211 | source = "registry+https://github.com/rust-lang/crates.io-index" 212 | dependencies = [ 213 | "memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", 214 | ] 215 | 216 | [[package]] 217 | name = "unix_socket" 218 | version = "0.4.6" 219 | source = "registry+https://github.com/rust-lang/crates.io-index" 220 | dependencies = [ 221 | "debug-builders 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 222 | "libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", 223 | ] 224 | 225 | [[package]] 226 | name = "winapi" 227 | version = "0.2.8" 228 | source = "registry+https://github.com/rust-lang/crates.io-index" 229 | 230 | [[package]] 231 | name = "winapi-build" 232 | version = "0.1.1" 233 | source = "registry+https://github.com/rust-lang/crates.io-index" 234 | 235 | [metadata] 236 | "checksum byteorder 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)" = "29b2aa490a8f546381308d68fc79e6bd753cd3ad839f7a7172897f1feedfa175" 237 | "checksum chan 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "82b22acfef7960fd8f829bc50749273be637cbd76b9d4cc20497666cc3a33329" 238 | "checksum debug-builders 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0f5d8e3d14cabcb2a8a59d7147289173c6ada77a0bc526f6b85078f941c0cf12" 239 | "checksum errno 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "1e2b2decb0484e15560df3210cf0d78654bb0864b2c138977c07e377a1bae0e2" 240 | "checksum fern 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "4d2f58d053ad7791bfaad58a3f3541fe2d2aecc564dd82aee7f92fa402c054b2" 241 | "checksum i3ipc 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bb717ff1dcb9d8950806b7a757b51befc6218471f6350c8bd57ca643fd2b1072" 242 | "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" 243 | "checksum libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "e32a70cf75e5846d53a673923498228bbec6a8624708a9ea5645f075d6276122" 244 | "checksum libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)" = "044d1360593a78f5c8e5e710beccdc24ab71d1f01bc19a29bcacdba22e8475d8" 245 | "checksum log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ab83497bf8bf4ed2a74259c1c802351fcd67a65baa86394b6ba73c36f4838054" 246 | "checksum memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d8b629fb514376c675b98c1421e80b151d3817ac42d7c667717d282761418d20" 247 | "checksum num 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)" = "bde7c03b09e7c6a301ee81f6ddf66d7a28ec305699e3d3b056d2fc56470e3120" 248 | "checksum num-bigint 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "88b14378471f7c2adc5262f05b4701ef53e8da376453a8d8fee48e51db745e49" 249 | "checksum num-complex 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "f0c78e054dd19c3fd03419ade63fa661e9c49bb890ce3beb4eee5b7baf93f92f" 250 | "checksum num-integer 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "fb24d9bfb3f222010df27995441ded1e954f8f69cd35021f6bef02ca9552fb92" 251 | "checksum num-iter 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "287a1c9969a847055e1122ec0ea7a5c5d6f72aad97934e131c83d5c08ab4e45c" 252 | "checksum num-rational 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "54ff603b8334a72fbb27fe66948aac0abaaa40231b3cecd189e76162f6f38aaf" 253 | "checksum num-traits 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)" = "a16a42856a256b39c6d3484f097f6713e14feacd9bfb02290917904fae46c81c" 254 | "checksum pty 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c276ec5bcbc0e0618f582c90eb4ab10f6a494fbeb97f27f0d2668994a690e6c4" 255 | "checksum rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "2791d88c6defac799c3f20d74f094ca33b9332612d9aef9078519c82e4fe04a5" 256 | "checksum rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)" = "6159e4e6e559c81bd706afe9c8fd68f547d3e851ce12e76b1de7914bab61691b" 257 | "checksum serde 0.6.15 (registry+https://github.com/rust-lang/crates.io-index)" = "c97b18e9e53de541f11e497357d6c5eaeb39f0cb9c8734e274abe4935f6991fa" 258 | "checksum serde_json 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b5aaee47e038bf9552d30380d3973fff2593ee0a76d81ad4c581f267cdcadf36" 259 | "checksum termion 1.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d94a5aea537a27dd9412585d7d77f2c382a2361f2b6a7cf0a6a56ea04aa5b71a" 260 | "checksum twoway 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e267e178055eb3b081224bbef62d4f508ae3c9f000b6ae6ccdb04a0d9c34b77f" 261 | "checksum unix_socket 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "d570eba7deb3197c04599645dd776ca8b7b9c3c623be7491c61d50ed5895f13d" 262 | "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" 263 | "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" 264 | --------------------------------------------------------------------------------