Skip to content

Commit 045c269

Browse files
committed
uucore: format: Separate function to parse numbers starting with a quote
After this, we can use a common extract_value function.
1 parent 076a305 commit 045c269

File tree

1 file changed

+69
-33
lines changed

1 file changed

+69
-33
lines changed

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

Lines changed: 69 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -115,42 +115,74 @@ impl<'a> FormatArguments<'a> {
115115
}
116116
}
117117

118-
fn get_num<T>(os: &OsStr) -> T
118+
// Parse an OsStr that we know to start with a '/"
119+
fn parse_quote_start<T>(os: &OsStr) -> Result<T, ExtendedParserError<T>>
119120
where
120121
T: ExtendedParser + From<u8> + From<u32> + Default,
121122
{
122-
// FIXME: Remove unwrap
123-
let s = os_str_as_bytes(os).unwrap();
124-
// Check if the string begins with a quote, and is therefore a literal
125-
if let Some((&first, bytes)) = s.split_first() {
126-
if (first == b'"' || first == b'\'') && !bytes.is_empty() {
127-
let (val, len) = if let Some(c) = bytes
128-
.utf8_chunks()
129-
.next()
130-
.expect("bytes should not be empty")
131-
.valid()
132-
.chars()
133-
.next()
134-
{
135-
// Valid UTF-8 character, cast the codepoint to u32 then T
136-
// (largest unicode codepoint is only 3 bytes, so this is safe)
137-
((c as u32).into(), c.len_utf8())
138-
} else {
139-
// Not a valid UTF-8 character, use the first byte
140-
(bytes[0].into(), 1)
141-
};
142-
// Emit a warning if there are additional characters
143-
if bytes.len() > len {
144-
show_warning!(
145-
"{}: character(s) following character constant have been ignored",
146-
String::from_utf8_lossy(&bytes[len..])
147-
);
148-
}
149-
return val;
123+
// If this fails (this can only happens on Windows), then just
124+
// return NotNumeric.
125+
let s = match os_str_as_bytes(os) {
126+
Ok(s) => s,
127+
Err(_) => return Err(ExtendedParserError::NotNumeric),
128+
};
129+
130+
let bytes = match s.split_first() {
131+
Some((b'"', bytes)) | Some((b'\'', bytes)) => bytes,
132+
_ => {
133+
// This really can't happen, the string we are given must start with '/".
134+
debug_assert!(false);
135+
return Err(ExtendedParserError::NotNumeric);
150136
}
137+
};
138+
139+
if bytes.is_empty() {
140+
return Err(ExtendedParserError::NotNumeric);
151141
}
142+
143+
let (val, len) = if let Some(c) = bytes
144+
.utf8_chunks()
145+
.next()
146+
.expect("bytes should not be empty")
147+
.valid()
148+
.chars()
149+
.next()
150+
{
151+
// Valid UTF-8 character, cast the codepoint to u32 then T
152+
// (largest unicode codepoint is only 3 bytes, so this is safe)
153+
((c as u32).into(), c.len_utf8())
154+
} else {
155+
// Not a valid UTF-8 character, use the first byte
156+
(bytes[0].into(), 1)
157+
};
158+
// Emit a warning if there are additional characters
159+
if bytes.len() > len {
160+
return Err(ExtendedParserError::PartialMatch(
161+
val,
162+
String::from_utf8_lossy(&bytes[len..]).to_string(),
163+
));
164+
}
165+
166+
Ok(val)
167+
}
168+
169+
fn get_num<T>(os: &OsStr) -> T
170+
where
171+
T: ExtendedParser + From<u8> + From<u32> + Default,
172+
{
152173
let s = os.to_string_lossy();
153-
extract_value(T::extended_parse(&s), &s)
174+
let first = s.as_bytes().first().copied();
175+
176+
let quote_start = first == Some(b'"') || first == Some(b'\'');
177+
let parsed = if quote_start {
178+
// The string begins with a quote
179+
Self::parse_quote_start(os)
180+
} else {
181+
T::extended_parse(&s)
182+
};
183+
184+
// Get the best possible value, even if parsed was an error.
185+
extract_value(parsed, &s, quote_start)
154186
}
155187

156188
fn get_at_relative_position(&mut self, pos: NonZero<usize>) -> Option<&'a FormatArgument> {
@@ -172,7 +204,11 @@ impl<'a> FormatArguments<'a> {
172204
}
173205
}
174206

175-
fn extract_value<T: Default>(p: Result<T, ExtendedParserError<T>>, input: &str) -> T {
207+
fn extract_value<T: Default>(
208+
p: Result<T, ExtendedParserError<T>>,
209+
input: &str,
210+
quote_start: bool,
211+
) -> T {
176212
match p {
177213
Ok(v) => v,
178214
Err(e) => {
@@ -192,8 +228,8 @@ fn extract_value<T: Default>(p: Result<T, ExtendedParserError<T>>, input: &str)
192228
Default::default()
193229
}
194230
ExtendedParserError::PartialMatch(v, rest) => {
195-
let bytes = input.as_encoded_bytes();
196-
if !bytes.is_empty() && (bytes[0] == b'\'' || bytes[0] == b'"') {
231+
if quote_start {
232+
set_exit_code(0);
197233
show_warning!(
198234
"{rest}: character(s) following character constant have been ignored"
199235
);

0 commit comments

Comments
 (0)