1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 03:27:44 +00:00

Merge pull request #7259 from jfinkels/printf-missing-hex-value

printf: error on missing hexadecial escape value
This commit is contained in:
Sylvestre Ledru 2025-02-02 22:28:38 +01:00 committed by GitHub
commit fc4f39c582
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 44 additions and 22 deletions

View file

@ -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'\\'))
}
}

View file

@ -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 {}
@ -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"),
}
}
}
@ -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;
@ -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;

View file

@ -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");