diff --git a/crates/forms/src/af.rs b/crates/forms/src/af.rs index c5b81719a..5c1ebeea2 100644 --- a/crates/forms/src/af.rs +++ b/crates/forms/src/af.rs @@ -196,6 +196,18 @@ impl Parser<'_> { b'n' => out.push(b'\n'), b'r' => out.push(b'\r'), b't' => out.push(b'\t'), + b'b' => out.push(0x08), + b'f' => out.push(0x0C), + b'v' => out.push(0x0B), + b'0' => out.push(0), + b'x' => { + let digits = self.s.get(self.i..self.i.checked_add(2)?)?; + let code = hex4(&[b'0', b'0', digits[0], digits[1]])?; + self.i += 2; + let ch = char::from_u32(u32::from(code)).unwrap_or('\u{FFFD}'); + let mut encoded = [0u8; 4]; + out.extend_from_slice(ch.encode_utf8(&mut encoded).as_bytes()); + } b'u' => { let code = hex4(self.s.get(self.i..self.i.checked_add(4)?)?)?; self.i += 4; @@ -1111,6 +1123,15 @@ pub fn calculate(c: &Calculate, values: &dyn Fn(&str) -> Vec) -> Option< mod tests { use super::*; + #[test] + fn javascript_escapes_are_decoded() { + assert_eq!( + parse_format("AFNumber_Format(2, 0, 0, 0, \"\\x24\", true);"), + Some(Format::Number { decimals: 2, sep: 0, neg: 0, currency: "$".into(), prepend: true }) + ); + assert_eq!(parse_format("AFDate_FormatEx(\"\\b\\f\\v\\0\\q\");"), Some(Format::Date("\u{8}\u{c}\u{b}\0q".into()))); + } + #[test] fn scripts_are_recognised_and_written_back() { assert_eq!(