Skip to content

Commit a2af5d6

Browse files
authored
Merge pull request #7320 from RenjiSann/peron/tr-fix-multiple-chars-in-eq
tr: Fix GNU behavior deviation
2 parents 99d4fbd + 989b6ba commit a2af5d6

File tree

2 files changed

+44
-11
lines changed

2 files changed

+44
-11
lines changed

src/uu/tr/src/operation.rs

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@
88
use crate::unicode_table;
99
use nom::{
1010
branch::alt,
11-
bytes::complete::{tag, take, take_till},
11+
bytes::complete::{tag, take, take_till, take_until},
1212
character::complete::one_of,
1313
combinator::{map, map_opt, peek, recognize, value},
1414
multi::{many0, many_m_n},
15-
sequence::{delimited, preceded, separated_pair},
15+
sequence::{delimited, preceded, separated_pair, terminated},
1616
IResult, Parser,
1717
};
1818
use std::{
@@ -39,6 +39,7 @@ pub enum BadSequence {
3939
Set1LongerSet2EndsInClass,
4040
ComplementMoreThanOneUniqueInSet2,
4141
BackwardsRange { end: u32, start: u32 },
42+
MultipleCharInEquivalence(String),
4243
}
4344

4445
impl Display for BadSequence {
@@ -89,6 +90,10 @@ impl Display for BadSequence {
8990
end_or_start_to_string(end)
9091
)
9192
}
93+
Self::MultipleCharInEquivalence(s) => write!(
94+
f,
95+
"{s}: equivalence class operand must be a single character"
96+
),
9297
}
9398
}
9499
}
@@ -492,18 +497,37 @@ impl Sequence {
492497
}
493498

494499
fn parse_char_equal(input: &[u8]) -> IResult<&[u8], Result<Self, BadSequence>> {
495-
delimited(
500+
preceded(
496501
tag("[="),
497-
alt((
498-
value(
499-
Err(BadSequence::MissingEquivalentClassChar),
500-
peek(tag("=]")),
501-
),
502-
map(Self::parse_backslash_or_char, |c| Ok(Self::Char(c))),
503-
)),
504-
tag("=]"),
502+
(
503+
alt((
504+
value(Err(()), peek(tag("=]"))),
505+
map(Self::parse_backslash_or_char, Ok),
506+
)),
507+
map(terminated(take_until("=]"), tag("=]")), |v: &[u8]| {
508+
if v.is_empty() {
509+
Ok(())
510+
} else {
511+
Err(v)
512+
}
513+
}),
514+
),
505515
)
506516
.parse(input)
517+
.map(|(l, (a, b))| {
518+
(
519+
l,
520+
match (a, b) {
521+
(Err(()), _) => Err(BadSequence::MissingEquivalentClassChar),
522+
(Ok(c), Ok(())) => Ok(Self::Char(c)),
523+
(Ok(c), Err(v)) => Err(BadSequence::MultipleCharInEquivalence(format!(
524+
"{}{}",
525+
String::from_utf8_lossy(&[c]).into_owned(),
526+
String::from_utf8_lossy(v).into_owned()
527+
))),
528+
},
529+
)
530+
})
507531
}
508532
}
509533

tests/by-util/test_tr.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1166,6 +1166,15 @@ fn check_against_gnu_tr_tests_empty_eq() {
11661166
.stderr_is("tr: missing equivalence class character '[==]'\n");
11671167
}
11681168

1169+
#[test]
1170+
fn check_too_many_chars_in_eq() {
1171+
new_ucmd!()
1172+
.args(&["-d", "[=aa=]"])
1173+
.pipe_in("")
1174+
.fails()
1175+
.stderr_contains("aa: equivalence class operand must be a single character\n");
1176+
}
1177+
11691178
#[test]
11701179
fn check_against_gnu_tr_tests_empty_cc() {
11711180
// ['empty-cc', qw('[::]' x), {IN=>''}, {OUT=>''}, {EXIT=>1},

0 commit comments

Comments
 (0)