8000 printf: error on missing hexadecial escape value by jfinkels · Pull Request #7259 · uutils/coreutils · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

printf: error on missing hexadecial escape value #7259

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 2, 2025
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
0