aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/parse/mod.rs24
-rw-r--r--src/parse/test.rs10
2 files changed, 33 insertions, 1 deletions
diff --git a/src/parse/mod.rs b/src/parse/mod.rs
index 4c19c96..f08426e 100644
--- a/src/parse/mod.rs
+++ b/src/parse/mod.rs
@@ -454,7 +454,7 @@ impl Parse for ExpString {
let mut delim = b.peek();
if delim == b'\'' || delim == b'"' {
b.adv();
- } else if is_symbol(delim) && delim != b'$' {
+ } else if is_symbol(delim) && delim != b'$' && delim != b'\\' {
return if already_parsed {
Ok(Self { parts })
} else {
@@ -475,10 +475,30 @@ impl Parse for ExpString {
b'r' => b'\r',
b't' => b'\t',
b'e' => 0x1b, // escape
+ b'x' => {
+ // parse two hex digits
+ b.adv();
+ if b.buf.len() < 2 {
+ Err(ParseError::Eof)?;
+ }
+ let x1 = b.peek();
+ b.adv();
+ let x2 = b.peek();
+
+ if !x1.is_ascii_hexdigit() || !x2.is_ascii_hexdigit() {
+ Err(ParseError::NotHexDigit)?;
+ }
+
+ let x1 = (x1 as char).to_digit(16).unwrap_or(0);
+ let x2 = (x2 as char).to_digit(16).unwrap_or(0);
+
+ ((x1 << 4) | x2) as u8
+ }
_ => x,
};
add_char(p, x);
escaping = false;
+ already_parsed = true;
b.adv();
continue;
}
@@ -650,6 +670,8 @@ pub enum ParseError {
NotAFunDecl,
NotAVarAssign,
+
+ NotHexDigit,
}
type Result<T> = std::result::Result<T, ParseError>;
diff --git a/src/parse/test.rs b/src/parse/test.rs
index 92124b0..4ae6341 100644
--- a/src/parse/test.rs
+++ b/src/parse/test.rs
@@ -109,3 +109,13 @@ fn escape_carriage_return() {
fn escape_tab() {
parse_test(parse(b"\"\\t\""), pipes([cmd([estr(b"\t")])]));
}
+
+#[test]
+fn escape_hex_1() {
+ parse_test(parse(b"\\x41"), pipes([cmd([estr(b"A")])]));
+}
+
+#[test]
+fn escape_hex_2() {
+ parse_test(parse(b"\\x0a"), pipes([cmd([estr(b"\n")])]));
+}