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:
commit
fc4f39c582
3 changed files with 44 additions and 22 deletions
|
@ -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'\\'))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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");
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue