From 03b065002bb59c82780ec5874da233dc7593ca88 Mon Sep 17 00:00:00 2001 From: Peter Grayson Date: Sun, 4 Feb 2024 09:31:26 -0500 Subject: [PATCH] chore: update gix-command to 0.3.4 This version of gix-command gains the ability to parse shebang lines on Windows. This replaces StGit code that did the same, but with perhaps more robustness. Refs: #407 --- Cargo.lock | 4 +- Cargo.toml | 2 +- src/patch/edit/interactive.rs | 121 +++++----------------------------- 3 files changed, 18 insertions(+), 109 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 855281e7..57304147 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -381,9 +381,9 @@ dependencies = [ [[package]] name = "gix-command" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce1ffc7db3fb50b7dae6ecd937a3527cb725f444614df2ad8988d81806f13f09" +checksum = "c82b5e9494e61983e61049bbd15fe0fa6b70672dd236362bdb5b2b50fc428f10" dependencies = [ "bstr", "gix-path", diff --git a/Cargo.toml b/Cargo.toml index ed0d5e75..606fad39 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,7 @@ ctrlc = "3.4" encoding_rs = "0.8" flate2 = "1" gix = { version = "0.58", default-features = false, features = ["revision"] } -gix-command = "0.3" +gix-command = "0.3.4" indexmap = "2.1" is-terminal = "0.4" nom = { version = "7", default_features = false, features = ["std"] } diff --git a/src/patch/edit/interactive.rs b/src/patch/edit/interactive.rs index eedc88db..a07d29ce 100644 --- a/src/patch/edit/interactive.rs +++ b/src/patch/edit/interactive.rs @@ -3,14 +3,14 @@ //! Functions for conducting interactive patch edit session. use std::{ - ffi::{OsStr, OsString}, + ffi::OsString, fs::File, - io::{BufWriter, Read, Write}, + io::{BufWriter, Write}, path::Path, }; use anyhow::{anyhow, Context, Result}; -use bstr::{BString, ByteSlice}; +use bstr::BString; use super::description::{EditablePatchDescription, EditedPatchDescription}; @@ -89,7 +89,17 @@ pub(crate) fn call_editor>( stderr.flush()?; } - let result = run_editor(&editor, path.as_ref()); + let status_result = gix_command::prepare(&editor) + .arg(path.as_ref()) + .with_shell_allow_argument_splitting() + .stdout(std::process::Stdio::inherit()) + .spawn() + .with_context(|| format!("running editor: {}", editor.to_string_lossy())) + .and_then(|mut child| { + child + .wait() + .with_context(|| format!("waiting editor: {}", editor.to_string_lossy())) + }); if use_advice && !is_dumb { let mut stderr = std::io::stderr(); @@ -97,7 +107,7 @@ pub(crate) fn call_editor>( stderr.flush()?; } - let status = result?; + let status = status_result?; if !status.success() { return Err(anyhow!( @@ -112,32 +122,6 @@ pub(crate) fn call_editor>( Ok(buf) } -fn run_editor>(editor: &OsStr, path: P) -> Result { - let prep = gix_command::prepare(editor) - .arg(path.as_ref()) - .with_shell_allow_argument_splitting() - .stdout(std::process::Stdio::inherit()); - - let mut command = if cfg!(windows) && !prep.use_shell { - if let Some(interpreter) = parse_interpreter(&prep.command) { - let mut cmd = std::process::Command::new(interpreter); - cmd.arg(prep.command).arg(path.as_ref()); - cmd - } else { - std::process::Command::from(prep) - } - } else { - std::process::Command::from(prep) - }; - let mut child = command - .spawn() - .with_context(|| format!("running editor: {}", editor.to_string_lossy()))?; - - child - .wait() - .with_context(|| format!("waiting editor: {}", editor.to_string_lossy())) -} - /// Determine user's editor of choice based on config and environment. fn get_editor(config: &gix::config::Snapshot) -> Result { let editor = if let Some(editor) = std::env::var_os("GIT_EDITOR") { @@ -163,78 +147,3 @@ fn get_editor(config: &gix::config::Snapshot) -> Result { }; Ok(editor) } - -fn parse_interpreter(command: &OsStr) -> Option { - let command_path = Path::new(command); - if command_path.extension().and_then(|ext| ext.to_str()) == Some("exe") { - return None; - } - - let mut buffer = [0; 128]; - if let Some(n) = std::fs::File::open(command_path) - .ok() - .and_then(|mut file| file.read(&mut buffer).ok()) - { - parse_shebang(&buffer[..n]) - .and_then(|bytes| bytes.to_os_str().ok()) - .map(|osstr| osstr.to_os_string()) - } else { - None - } -} - -fn parse_shebang(buffer: &[u8]) -> Option<&[u8]> { - buffer - .as_bstr() - .lines() - .next() - .and_then(|line| line.strip_prefix(b"#!")) - .and_then(|shebang| { - shebang.rfind_byteset(b"/\\").map(|index| { - if let Some(space_index) = shebang[index..].find_byte(b' ') { - &shebang[..index + space_index] - } else { - shebang - } - }) - }) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn plain_shebang() { - assert_eq!(parse_shebang(b"#!/bin/sh\nsome stuff").unwrap(), b"/bin/sh"); - } - - #[test] - fn shebang_with_options() { - assert_eq!( - parse_shebang(b"#!/bin/sh -i -o -u\nsome stuff").unwrap(), - b"/bin/sh" - ); - } - - #[test] - fn shebang_with_backslashes() { - assert_eq!( - parse_shebang(b"#!C:\\Program Files\\Imashell.exe\nsome stuff").unwrap(), - b"C:\\Program Files\\Imashell.exe" - ); - } - - #[test] - fn shebang_with_trailing_space() { - assert_eq!( - parse_shebang(b"#!/bin/sh \nsome stuff").unwrap(), - b"/bin/sh" - ); - } - - #[test] - fn not_a_shebang() { - assert!(parse_shebang(b"/bin/sh\nsome stuff").is_none()); - } -}