Skip to content

Commit bbca2ff

Browse files
RenjiSannaryalaadi
andcommitted
printf: fix escape octal parsing
Co-authored-by: aryal <141743392+aryalaadi@users.noreply.github.com>
1 parent cd1e764 commit bbca2ff

File tree

4 files changed

+47
-21
lines changed

4 files changed

+47
-21
lines changed

src/uu/echo/src/echo.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use std::env;
99
use std::ffi::{OsStr, OsString};
1010
use std::io::{self, StdoutLock, Write};
1111
use uucore::error::{UResult, USimpleError};
12-
use uucore::format::{parse_escape_only, EscapedChar, FormatChar};
12+
use uucore::format::{parse_escape_only, EscapedChar, FormatChar, OctalParsing};
1313
use uucore::{format_usage, help_about, help_section, help_usage};
1414

1515
const ABOUT: &str = help_about!("echo.md");
@@ -135,7 +135,7 @@ fn execute(
135135
}
136136

137137
if escaped {
138-
for item in parse_escape_only(bytes) {
138+
for item in parse_escape_only(bytes, OctalParsing::ThreeDigits) {
139139
match item {
140140
EscapedChar::End => return Ok(()),
141141
c => c.write(&mut *stdout_lock)?,

src/uucore/src/lib/features/format/escape.rs

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,24 +17,37 @@ pub enum EscapedChar {
1717
End,
1818
}
1919

20-
#[repr(u8)]
20+
#[derive(Clone, Copy, Default)]
21+
pub enum OctalParsing {
22+
#[default]
23+
TwoDigits = 2,
24+
ThreeDigits = 3,
25+
}
26+
2127
#[derive(Clone, Copy)]
2228
enum Base {
23-
Oct = 8,
24-
Hex = 16,
29+
Oct(OctalParsing),
30+
Hex,
2531
}
2632

2733
impl Base {
34+
fn as_base(&self) -> u8 {
35+
match self {
36+
Base::Oct(_) => 8,
37+
Base::Hex => 16,
38+
}
39+
}
40+
2841
fn max_digits(&self) -> u8 {
2942
match self {
30-
Self::Oct => 3,
43+
Self::Oct(parsing) => *parsing as u8,
3144
Self::Hex => 2,
3245
}
3346
}
3447

3548
fn convert_digit(&self, c: u8) -> Option<u8> {
3649
match self {
37-
Self::Oct => {
50+
Self::Oct(_) => {
3851
if matches!(c, b'0'..=b'7') {
3952
Some(c - b'0')
4053
} else {
@@ -68,7 +81,7 @@ fn parse_code(input: &mut &[u8], base: Base) -> Option<u8> {
6881
let Some(n) = base.convert_digit(*c) else {
6982
break;
7083
};
71-
ret = ret.wrapping_mul(base as u8).wrapping_add(n);
84+
ret = ret.wrapping_mul(base.as_base()).wrapping_add(n);
7285
*input = rest;
7386
}
7487

@@ -87,7 +100,9 @@ fn parse_unicode(input: &mut &[u8], digits: u8) -> Option<char> {
87100
for _ in 1..digits {
88101
let (c, rest) = input.split_first()?;
89102
let n = Base::Hex.convert_digit(*c)?;
90-
ret = ret.wrapping_mul(Base::Hex as u32).wrapping_add(n as u32);
103+
ret = ret
104+
.wrapping_mul(Base::Hex.as_base() as u32)
105+
.wrapping_add(n as u32);
91106
*input = rest;
92107
}
93108

@@ -99,13 +114,16 @@ fn parse_unicode(input: &mut &[u8], digits: u8) -> Option<char> {
99114
pub struct EscapeError {}
100115

101116
/// Parse an escape sequence, like `\n` or `\xff`, etc.
102-
pub fn parse_escape_code(rest: &mut &[u8]) -> Result<EscapedChar, EscapeError> {
117+
pub fn parse_escape_code(
118+
rest: &mut &[u8],
119+
zero_octal_parsing: OctalParsing,
120+
) -> Result<EscapedChar, EscapeError> {
103121
if let [c, new_rest @ ..] = rest {
104122
// This is for the \NNN syntax for octal sequences.
105123
// Note that '0' is intentionally omitted because that
106124
// would be the \0NNN syntax.
107125
if let b'1'..=b'7' = c {
108-
if let Some(parsed) = parse_code(rest, Base::Oct) {
126+
if let Some(parsed) = parse_code(rest, Base::Oct(OctalParsing::ThreeDigits)) {
109127
return Ok(EscapedChar::Byte(parsed));
110128
}
111129
}
@@ -131,7 +149,7 @@ pub fn parse_escape_code(rest: &mut &[u8]) -> Result<EscapedChar, EscapeError> {
131149
}
132150
}
133151
b'0' => Ok(EscapedChar::Byte(
134-
parse_code(rest, Base::Oct).unwrap_or(b'\0'),
152+
parse_code(rest, Base::Oct(zero_octal_parsing)).unwrap_or(b'\0'),
135153
)),
136154
b'u' => Ok(EscapedChar::Char(parse_unicode(rest, 4).unwrap_or('\0'))),
137155
b'U' => Ok(EscapedChar::Char(parse_unicode(rest, 8).unwrap_or('\0'))),

src/uucore/src/lib/features/format/mod.rs

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ use os_display::Quotable;
5151
use crate::error::UError;
5252

5353
pub use self::{
54-
escape::{parse_escape_code, EscapedChar},
54+
escape::{parse_escape_code, EscapedChar, OctalParsing},
5555
num_format::Formatter,
5656
};
5757

@@ -184,10 +184,12 @@ pub fn parse_spec_and_escape(
184184
}
185185
[b'\\', rest @ ..] => {
186186
current = rest;
187-
Some(match parse_escape_code(&mut current) {
188-
Ok(c) => Ok(FormatItem::Char(c)),
189-
Err(_) => Err(FormatError::MissingHex),
190-
})
187+
Some(
188+
match parse_escape_code(&mut current, OctalParsing::default()) {
189+
Ok(c) => Ok(FormatItem::Char(c)),
190+
Err(_) => Err(FormatError::MissingHex),
191+
},
192+
)
191193
}
192194
[c, rest @ ..] => {
193195
current = rest;
@@ -224,13 +226,19 @@ pub fn parse_spec_only(
224226
}
225227

226228
/// Parse a format string containing escape sequences
227-
pub fn parse_escape_only(fmt: &[u8]) -> impl Iterator<Item = EscapedChar> + '_ {
229+
pub fn parse_escape_only(
230+
fmt: &[u8],
231+
zero_octal_parsing: OctalParsing,
232+
) -> impl Iterator<Item = EscapedChar> + '_ {
228233
let mut current = fmt;
229234
std::iter::from_fn(move || match current {
230235
[] => None,
231236
[b'\\', rest @ ..] => {
232237
current = rest;
233-
Some(parse_escape_code(&mut current).unwrap_or(EscapedChar::Backslash(b'x')))
238+
Some(
239+
parse_escape_code(&mut current, zero_octal_parsing)
240+
.unwrap_or(EscapedChar::Backslash(b'x')),
241+
)
234242
}
235243
[c, rest @ ..] => {
236244
current = rest;

src/uucore/src/lib/features/format/spec.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use super::{
1212
self, Case, FloatVariant, ForceDecimal, Formatter, NumberAlignment, PositiveSign, Prefix,
1313
UnsignedIntVariant,
1414
},
15-
parse_escape_only, ArgumentIter, FormatChar, FormatError,
15+
parse_escape_only, ArgumentIter, FormatChar, FormatError, OctalParsing,
1616
};
1717
use std::{io::Write, ops::ControlFlow};
1818

@@ -348,7 +348,7 @@ impl Spec {
348348
Self::EscapedString => {
349349
let s = args.get_str();
350350
let mut parsed = Vec::new();
351-
for c in parse_escape_only(s.as_bytes()) {
351+
for c in parse_escape_only(s.as_bytes(), OctalParsing::default()) {
352352
match c.write(&mut parsed)? {
353353
ControlFlow::Continue(()) => {}
354354
ControlFlow::Break(()) => {

0 commit comments

Comments
 (0)