aboutsummaryrefslogtreecommitdiff
path: root/src/platform/macos.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/platform/macos.rs')
-rw-r--r--src/platform/macos.rs35
1 files changed, 35 insertions, 0 deletions
diff --git a/src/platform/macos.rs b/src/platform/macos.rs
index df957652..3770a3be 100644
--- a/src/platform/macos.rs
+++ b/src/platform/macos.rs
@@ -16,6 +16,30 @@ const PROC_PGRP_ONLY: u32 = 2;
const SERVER_NOFILE_LIMIT_TARGET: libc::rlim_t = 8192;
const CF_STRING_ENCODING_UTF8: u32 = 0x0800_0100;
+pub(crate) fn scrollback_editor_argv(path: &Path) -> std::io::Result<Vec<String>> {
+ let quoted_path = shell_quote(&path.display().to_string());
+ let command = format!(
+ r#"scrollback_file={quoted_path}; eval "${{EDITOR:-vi}} \"\$scrollback_file\""; status=$?; rm -f "$scrollback_file"; exit $status"#
+ );
+ Ok(vec!["/bin/sh".to_string(), "-c".to_string(), command])
+}
+
+fn shell_quote(value: &str) -> String {
+ if !value.is_empty()
+ && value.chars().all(|ch| {
+ ch.is_ascii_alphanumeric()
+ || matches!(
+ ch,
+ '@' | '%' | '_' | '+' | '=' | ':' | ',' | '.' | '/' | '-'
+ )
+ })
+ {
+ return value.to_string();
+ }
+
+ format!("'{}'", value.replace('\'', "'\\''"))
+}
+
#[repr(C)]
struct TisInputSource {
_private: [u8; 0],
@@ -1117,4 +1141,15 @@ printf '%s\n' "$@" > "$HERDR_NOTIFY_ARGS"
"-e\non run argv\n-e\ndisplay notification (item 2 of argv) with title (item 1 of argv)\n-e\nend run\ntitle\nbody\n"
);
}
+
+ #[test]
+ fn scrollback_editor_argv_preserves_unix_editor_shell_semantics() {
+ let path = std::path::Path::new("/tmp/herdr scrollback.txt");
+ let argv = scrollback_editor_argv(path).unwrap();
+
+ assert_eq!(argv[0], "/bin/sh");
+ assert_eq!(argv[1], "-c");
+ assert!(argv[2].contains("EDITOR:-vi"));
+ assert!(argv[2].contains("/tmp/herdr scrollback.txt"));
+ }
}