mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 11:37: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)
|
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 {
|
if let [c, new_rest @ ..] = rest {
|
||||||
// This is for the \NNN syntax for octal sequences.
|
// This is for the \NNN syntax for octal sequences.
|
||||||
// Note that '0' is intentionally omitted because that
|
// Note that '0' is intentionally omitted because that
|
||||||
// would be the \0NNN syntax.
|
// would be the \0NNN syntax.
|
||||||
if let b'1'..=b'7' = c {
|
if let b'1'..=b'7' = c {
|
||||||
if let Some(parsed) = parse_code(rest, Base::Oct) {
|
if let Some(parsed) = parse_code(rest, Base::Oct) {
|
||||||
return EscapedChar::Byte(parsed);
|
return Ok(EscapedChar::Byte(parsed));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*rest = new_rest;
|
*rest = new_rest;
|
||||||
match c {
|
match c {
|
||||||
b'\\' => EscapedChar::Byte(b'\\'),
|
b'\\' => Ok(EscapedChar::Byte(b'\\')),
|
||||||
b'"' => EscapedChar::Byte(b'"'),
|
b'"' => Ok(EscapedChar::Byte(b'"')),
|
||||||
b'a' => EscapedChar::Byte(b'\x07'),
|
b'a' => Ok(EscapedChar::Byte(b'\x07')),
|
||||||
b'b' => EscapedChar::Byte(b'\x08'),
|
b'b' => Ok(EscapedChar::Byte(b'\x08')),
|
||||||
b'c' => EscapedChar::End,
|
b'c' => Ok(EscapedChar::End),
|
||||||
b'e' => EscapedChar::Byte(b'\x1b'),
|
b'e' => Ok(EscapedChar::Byte(b'\x1b')),
|
||||||
b'f' => EscapedChar::Byte(b'\x0c'),
|
b'f' => Ok(EscapedChar::Byte(b'\x0c')),
|
||||||
b'n' => EscapedChar::Byte(b'\n'),
|
b'n' => Ok(EscapedChar::Byte(b'\n')),
|
||||||
b'r' => EscapedChar::Byte(b'\r'),
|
b'r' => Ok(EscapedChar::Byte(b'\r')),
|
||||||
b't' => EscapedChar::Byte(b'\t'),
|
b't' => Ok(EscapedChar::Byte(b'\t')),
|
||||||
b'v' => EscapedChar::Byte(b'\x0b'),
|
b'v' => Ok(EscapedChar::Byte(b'\x0b')),
|
||||||
b'x' => {
|
b'x' => {
|
||||||
if let Some(c) = parse_code(rest, Base::Hex) {
|
if let Some(c) = parse_code(rest, Base::Hex) {
|
||||||
EscapedChar::Byte(c)
|
Ok(EscapedChar::Byte(c))
|
||||||
} else {
|
} else {
|
||||||
EscapedChar::Backslash(b'x')
|
Err(EscapeError {})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
b'0' => EscapedChar::Byte(parse_code(rest, Base::Oct).unwrap_or(b'\0')),
|
b'0' => Ok(EscapedChar::Byte(
|
||||||
b'u' => EscapedChar::Char(parse_unicode(rest, 4).unwrap_or('\0')),
|
parse_code(rest, Base::Oct).unwrap_or(b'\0'),
|
||||||
b'U' => EscapedChar::Char(parse_unicode(rest, 8).unwrap_or('\0')),
|
)),
|
||||||
c => EscapedChar::Backslash(*c),
|
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 {
|
} else {
|
||||||
EscapedChar::Byte(b'\\')
|
Ok(EscapedChar::Byte(b'\\'))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,6 +67,8 @@ pub enum FormatError {
|
||||||
InvalidPrecision(String),
|
InvalidPrecision(String),
|
||||||
/// The format specifier ends with a %, as in `%f%`.
|
/// The format specifier ends with a %, as in `%f%`.
|
||||||
EndsWithPercent(Vec<u8>),
|
EndsWithPercent(Vec<u8>),
|
||||||
|
/// The escape sequence `\x` appears without a literal hexadecimal value.
|
||||||
|
MissingHex,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Error for FormatError {}
|
impl Error for FormatError {}
|
||||||
|
@ -105,6 +107,7 @@ impl Display for FormatError {
|
||||||
Self::IoError(_) => write!(f, "io error"),
|
Self::IoError(_) => write!(f, "io error"),
|
||||||
Self::NoMoreArguments => write!(f, "no more arguments"),
|
Self::NoMoreArguments => write!(f, "no more arguments"),
|
||||||
Self::InvalidArgument(_) => write!(f, "invalid argument"),
|
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 @ ..] => {
|
[b'\\', rest @ ..] => {
|
||||||
current = 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 @ ..] => {
|
[c, rest @ ..] => {
|
||||||
current = rest;
|
current = rest;
|
||||||
|
@ -224,7 +230,7 @@ pub fn parse_escape_only(fmt: &[u8]) -> impl Iterator<Item = EscapedChar> + '_ {
|
||||||
[] => None,
|
[] => None,
|
||||||
[b'\\', rest @ ..] => {
|
[b'\\', rest @ ..] => {
|
||||||
current = rest;
|
current = rest;
|
||||||
Some(parse_escape_code(&mut current))
|
Some(parse_escape_code(&mut current).unwrap_or(EscapedChar::Backslash(b'x')))
|
||||||
}
|
}
|
||||||
[c, rest @ ..] => {
|
[c, rest @ ..] => {
|
||||||
current = rest;
|
current = rest;
|
||||||
|
|
|
@ -46,6 +46,15 @@ fn escaped_hex() {
|
||||||
new_ucmd!().args(&["\\x41"]).succeeds().stdout_only("A");
|
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]
|
#[test]
|
||||||
fn escaped_octal() {
|
fn escaped_octal() {
|
||||||
new_ucmd!().args(&["\\101"]).succeeds().stdout_only("A");
|
new_ucmd!().args(&["\\101"]).succeeds().stdout_only("A");
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue