Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 27 additions & 20 deletions src/uucore/src/lib/features/format/escape.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,43 +94,50 @@ fn parse_unicode(input: &mut &[u8], digits: u8) -> Option<char> {
char::from_u32(ret)
}

pub fn parse_escape_code(rest: &mut &[u8]) -> EscapedChar {
/// Represents an invalid escape sequence.
#[derive(Debug)]
pub struct EscapeError {}

/// Parse an escape sequence, like `\n` or `\xff`, etc.
pub fn parse_escape_code(rest: &mut &[u8]) -> Result<EscapedChar, EscapeError> {
if let [c, new_rest @ ..] = rest {
// This is for the \NNN syntax for octal sequences.
// Note that '0' is intentionally omitted because that
// would be the \0NNN syntax.
if let b'1'..=b'7' = c {
if let Some(parsed) = parse_code(rest, Base::Oct) {
return EscapedChar::Byte(parsed);
return Ok(EscapedChar::Byte(parsed));
}
}

*rest = new_rest;
match c {
b'\\' => EscapedChar::Byte(b'\\'),
b'"' => EscapedChar::Byte(b'"'),
b'a' => EscapedChar::Byte(b'\x07'),
b'b' => EscapedChar::Byte(b'\x08'),
b'c' => EscapedChar::End,
b'e' => EscapedChar::Byte(b'\x1b'),
b'f' => EscapedChar::Byte(b'\x0c'),
b'n' => EscapedChar::Byte(b'\n'),
b'r' => EscapedChar::Byte(b'\r'),
b't' => EscapedChar::Byte(b'\t'),
b'v' => EscapedChar::Byte(b'\x0b'),
b'\\' => Ok(EscapedChar::Byte(b'\\')),
b'"' => Ok(EscapedChar::Byte(b'"')),
b'a' => Ok(EscapedChar::Byte(b'\x07')),
b'b' => Ok(EscapedChar::Byte(b'\x08')),
b'c' => Ok(EscapedChar::End),
b'e' => Ok(EscapedChar::Byte(b'\x1b')),
b'f' => Ok(EscapedChar::Byte(b'\x0c')),
b'n' => Ok(EscapedChar::Byte(b'\n')),
b'r' => Ok(EscapedChar::Byte(b'\r')),
b't' => Ok(EscapedChar::Byte(b'\t')),
b'v' => Ok(EscapedChar::Byte(b'\x0b')),
b'x' => {
if let Some(c) = parse_code(rest, Base::Hex) {
EscapedChar::Byte(c)
Ok(EscapedChar::Byte(c))
} else {
EscapedChar::Backslash(b'x')
Err(EscapeError {})
}
}
b'0' => EscapedChar::Byte(parse_code(rest, Base::Oct).unwrap_or(b'\0')),
b'u' => EscapedChar::Char(parse_unicode(rest, 4).unwrap_or('\0')),
b'U' => EscapedChar::Char(parse_unicode(rest, 8).unwrap_or('\0')),
c => EscapedChar::Backslash(*c),
b'0' => Ok(EscapedChar::Byte(
parse_code(rest, Base::Oct).unwrap_or(b'\0'),
)),
b'u' => Ok(EscapedChar::Char(parse_unicode(rest, 4).unwrap_or('\0'))),
b'U' => Ok(EscapedChar::Char(parse_unicode(rest, 8).unwrap_or('\0'))),
c => Ok(EscapedChar::Backslash(*c)),
}
} else {
EscapedChar::Byte(b'\\')
Ok(EscapedChar::Byte(b'\\'))
}
}
10 changes: 8 additions & 2 deletions src/uucore/src/lib/features/format/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ pub enum FormatError {
InvalidPrecision(String),
/// The format specifier ends with a %, as in `%f%`.
EndsWithPercent(Vec<u8>),
/// The escape sequence `\x` appears without a literal hexadecimal value.
MissingHex,
}

impl Error for FormatError {}
Expand Down Expand Up @@ -105,6 +107,7 @@ impl Display for FormatError {
Self::IoError(_) => write!(f, "io error"),
Self::NoMoreArguments => write!(f, "no more arguments"),
Self::InvalidArgument(_) => write!(f, "invalid argument"),
Self::MissingHex => write!(f, "missing hexadecimal number in escape"),
}
}
}
Expand Down Expand Up @@ -181,7 +184,10 @@ pub fn parse_spec_and_escape(
}
[b'\\', rest @ ..] => {
current = rest;
Some(Ok(FormatItem::Char(parse_escape_code(&mut current))))
Some(match parse_escape_code(&mut current) {
Ok(c) => Ok(FormatItem::Char(c)),
Err(_) => Err(FormatError::MissingHex),
})
}
[c, rest @ ..] => {
current = rest;
Expand Down Expand Up @@ -224,7 +230,7 @@ pub fn parse_escape_only(fmt: &[u8]) -> impl Iterator<Item = EscapedChar> + '_ {
[] => None,
[b'\\', rest @ ..] => {
current = rest;
Some(parse_escape_code(&mut current))
Some(parse_escape_code(&mut current).unwrap_or(EscapedChar::Backslash(b'x')))
}
[c, rest @ ..] => {
current = rest;
Expand Down
9 changes: 9 additions & 0 deletions tests/by-util/test_printf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,15 @@ fn escaped_hex() {
new_ucmd!().args(&["\\x41"]).succeeds().stdout_only("A");
}

#[test]
fn test_missing_escaped_hex_value() {
new_ucmd!()
.arg(r"\x")
.fails()
.code_is(1)
.stderr_only("printf: missing hexadecimal number in escape\n");
}

#[test]
fn escaped_octal() {
new_ucmd!().args(&["\\101"]).succeeds().stdout_only("A");
Expand Down
Loading