diff options
author | 依云 <lilydjwg@gmail.com> | 2024-01-03 16:46:59 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-01-03 08:46:59 +0000 |
commit | c37147d619a3d8d30897db8c5cb6dddb9276e310 (patch) | |
tree | 8d05c8e173a7441a946cae23469b1cf1ae86df6b | |
parent | 434e8238d8604dd1efcf601867b73ef17d912490 (diff) |
fix(import/zsh): zsh use a special format to escape some characters (#1490)
* fix(import/zsh): zsh use a special format to escape some characters
unescape those correctly rather than throw them away.
zsh side code:
https://github.com/zsh-users/zsh/blob/9f57ca4ac8ae071727b1d77cbb8c4c0d893b9099/Src/utils.c#L4889-L4900
* fix code style
-rw-r--r-- | atuin-client/src/import/zsh.rs | 46 |
1 files changed, 42 insertions, 4 deletions
diff --git a/atuin-client/src/import/zsh.rs b/atuin-client/src/import/zsh.rs index a4b03f2d..5bc8fc16 100644 --- a/atuin-client/src/import/zsh.rs +++ b/atuin-client/src/import/zsh.rs @@ -1,6 +1,7 @@ // import old shell history! // automatically hoover up all that we can find +use std::borrow::Cow; use std::path::PathBuf; use async_trait::async_trait; @@ -61,16 +62,16 @@ impl Importer for Zsh { let mut counter = 0; for b in unix_byte_lines(&self.bytes) { - let s = match std::str::from_utf8(b) { - Ok(s) => s, - Err(_) => continue, // we can skip past things like invalid utf8 + let s = match unmetafy(b) { + Some(s) => s, + _ => continue, // we can skip past things like invalid utf8 }; if let Some(s) = s.strip_suffix('\\') { line.push_str(s); line.push_str("\\\n"); } else { - line.push_str(s); + line.push_str(&s); let command = std::mem::take(&mut line); if let Some(command) = command.strip_prefix(": ") { @@ -116,6 +117,26 @@ fn parse_extended(line: &str, counter: i64) -> History { imported.build().into() } +fn unmetafy(line: &[u8]) -> Option<Cow<str>> { + if line.contains(&0x83) { + let mut s = Vec::with_capacity(line.len()); + let mut is_meta = false; + for ch in line { + if *ch == 0x83 { + is_meta = true; + } else if is_meta { + is_meta = false; + s.push(*ch ^ 32); + } else { + s.push(*ch) + } + } + String::from_utf8(s).ok().map(Cow::Owned) + } else { + std::str::from_utf8(line).ok().map(Cow::Borrowed) + } +} + #[cfg(test)] mod test { use itertools::assert_equal; @@ -188,4 +209,21 @@ cargo update ], ); } + + #[tokio::test] + async fn test_parse_metafied() { + let bytes = + b"echo \xe4\xbd\x83\x80\xe5\xa5\xbd\nls ~/\xe9\x83\xbf\xb3\xe4\xb9\x83\xb0\n".to_vec(); + + let mut zsh = Zsh { bytes }; + assert_eq!(zsh.entries().await.unwrap(), 2); + + let mut loader = TestLoader::default(); + zsh.load(&mut loader).await.unwrap(); + + assert_equal( + loader.buf.iter().map(|h| h.command.as_str()), + ["echo 你好", "ls ~/音乐"], + ); + } } |