diff --git a/debian/patches/fixup-tr-implement-translate-and-squeeze-s-mode.patch b/debian/patches/fixup-tr-implement-translate-and-squeeze-s-mode.patch new file mode 100644 index 0000000000000000000000000000000000000000..e21f1adef8144b7a3d4762e5b26e52aa349dccc0 --- /dev/null +++ b/debian/patches/fixup-tr-implement-translate-and-squeeze-s-mode.patch @@ -0,0 +1,130 @@ +From 5674d093275ea9ddc3be9ffbb3627eb5d6cae3a1 Mon Sep 17 00:00:00 2001 +From: Jeffrey Finkelstein <jeffrey.finkelstein@protonmail.com> +Date: Sat, 1 May 2021 13:01:55 -0400 +Subject: [PATCH] fixup! tr: implement translate and squeeze (-s) mode +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: 8bit + +--- + src/uu/tr/src/tr.rs | 89 +++++++++++++++++++++------------------------ + 1 file changed, 42 insertions(+), 47 deletions(-) + +diff --git a/src/uu/tr/src/tr.rs b/src/uu/tr/src/tr.rs +index 09c4304a..e04737a4 100644 +--- a/src/uu/tr/src/tr.rs ++++ b/src/uu/tr/src/tr.rs +@@ -151,6 +151,35 @@ impl SymbolTranslator for TranslateOperation { + } + } + ++struct TranslateAndSqueezeOperation { ++ translate: TranslateOperation, ++ squeeze: SqueezeOperation, ++} ++ ++impl TranslateAndSqueezeOperation { ++ fn new( ++ set1: ExpandSet, ++ set2: &mut ExpandSet, ++ set2_: ExpandSet, ++ truncate: bool, ++ complement: bool, ++ ) -> TranslateAndSqueezeOperation { ++ TranslateAndSqueezeOperation { ++ translate: TranslateOperation::new(set1, set2, truncate), ++ squeeze: SqueezeOperation::new(set2_, complement), ++ } ++ } ++} ++ ++impl SymbolTranslator for TranslateAndSqueezeOperation { ++ fn translate(&self, c: char, prev_c: char) -> Option<char> { ++ // `unwrap()` will never panic because `Translate.translate()` ++ // always returns `Some`. ++ self.squeeze ++ .translate(self.translate.translate(c, 0 as char).unwrap(), prev_c) ++ } ++} ++ + fn translate_input<T: SymbolTranslator>( + input: &mut dyn BufRead, + output: &mut dyn Write, +@@ -168,8 +197,11 @@ fn translate_input<T: SymbolTranslator>( + // isolation to make borrow checker happy + let filtered = buf.chars().filter_map(|c| { + let res = translator.translate(c, prev_c); ++ // Set `prev_c` to the post-translate character. This ++ // allows the squeeze operation to correctly function ++ // after the translate operation. + if res.is_some() { +- prev_c = c; ++ prev_c = res.unwrap(); + } + res + }); +@@ -282,53 +314,16 @@ pub fn uumain(args: impl uucore::Args) -> i32 { + let op = SqueezeOperation::new(set1, complement_flag); + translate_input(&mut locked_stdin, &mut buffered_stdout, op); + } else { +- // Define a closure that computes the translation using a hash map. +- // +- // The `unwrap()` should never panic because the +- // `TranslateOperation.translate()` method always returns +- // `Some`. + let mut set2 = ExpandSet::new(sets[1].as_ref()); +- let translator = TranslateOperation::new(set1, &mut set2, truncate_flag); +- let translate = |c| translator.translate(c, 0 as char).unwrap(); +- +- // Prepare some variables to be used for the closure that +- // computes the squeeze operation. +- // +- // The `squeeze()` closure needs to be defined anew for +- // each line of input, but these variables do not change +- // while reading the input so they can be defined before +- // the `while` loop. +- let set2 = ExpandSet::new(sets[1].as_ref()); +- let squeezer = SqueezeOperation::new(set2, complement_flag); +- +- // Prepare some memory to read each line of the input (`buf`) and to write +- let mut buf = String::with_capacity(BUFFER_LEN + 4); +- +- // Loop over each line of stdin. +- while let Ok(length) = locked_stdin.read_line(&mut buf) { +- if length == 0 { +- break; +- } +- +- // Define a closure that computes the squeeze operation. +- // +- // We keep track of the previously seen character on +- // each call to `squeeze()`, but we need to reset the +- // `prev_c` variable at the beginning of each line of +- // the input. That's why we define the closure inside +- // the `while` loop. +- let mut prev_c = 0 as char; +- let squeeze = |c| { +- let result = squeezer.translate(c, prev_c); +- prev_c = c; +- result +- }; +- +- // First translate, then squeeze each character of the input line. +- let filtered: String = buf.chars().map(translate).filter_map(squeeze).collect(); +- buf.clear(); +- buffered_stdout.write_all(filtered.as_bytes()).unwrap(); +- } ++ let set2_ = ExpandSet::new(sets[1].as_ref()); ++ let op = TranslateAndSqueezeOperation::new( ++ set1, ++ &mut set2, ++ set2_, ++ complement_flag, ++ truncate_flag, ++ ); ++ translate_input(&mut locked_stdin, &mut buffered_stdout, op); + } + } else { + let mut set2 = ExpandSet::new(sets[1].as_ref()); +-- +2.25.1 + diff --git a/debian/patches/series b/debian/patches/series index ca4378e7c6c1c6a1f4b256c2bb8f75286c70a717..43ce5d25ba0035be5983d503b768365a54e04662 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -16,6 +16,12 @@ Sort-Various-fixes-and-performance-improvements.patch sort-implement-k-and-t-support.patch cp-improve-symlink-handling.patch cp-move-symlink-check-to-the-right-place.patch +tr-implement-complement-separately-from-delete-or-sq.patch +tr-fix-complement-if-set2-is-range.patch +tr-implement-translate-and-squeeze-s-mode.patch +fixup-tr-implement-translate-and-squeeze-s-mode.patch +tr-fix-merge-conflict.patch +tr-fix-squeeze-and-complement-with-two-sets-2485.patch # Apertis patches sort-fix-dependencies-versions.patch diff --git a/debian/patches/tr-fix-complement-if-set2-is-range.patch b/debian/patches/tr-fix-complement-if-set2-is-range.patch new file mode 100644 index 0000000000000000000000000000000000000000..e409d9bea4eeaa8d8d60e951bfa6f9ac401451ee --- /dev/null +++ b/debian/patches/tr-fix-complement-if-set2-is-range.patch @@ -0,0 +1,59 @@ +From 34c22dc3ad359ebdd08ebbfcf6d3651c433de2df Mon Sep 17 00:00:00 2001 +From: Jan Scheer <jhscheer@users.noreply.github.com> +Date: Sun, 2 May 2021 12:15:16 +0200 +Subject: [PATCH] tr: fix complement if set2 is range +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: 8bit + +--- + src/uu/tr/src/tr.rs | 2 +- + tests/by-util/test_tr.rs | 20 ++++++++++++++++++++ + 2 files changed, 21 insertions(+), 1 deletion(-) + +diff --git a/src/uu/tr/src/tr.rs b/src/uu/tr/src/tr.rs +index b1143b4b..68543229 100644 +--- a/src/uu/tr/src/tr.rs ++++ b/src/uu/tr/src/tr.rs +@@ -149,7 +149,7 @@ impl TranslateOperation { + TranslateOperation { + translate_map: map, + complement, +- s2_last: s2_prev, ++ s2_last: set2.last().unwrap_or(s2_prev), + } + } + } +diff --git a/tests/by-util/test_tr.rs b/tests/by-util/test_tr.rs +index 064fbf77..f840dee6 100644 +--- a/tests/by-util/test_tr.rs ++++ b/tests/by-util/test_tr.rs +@@ -86,6 +86,26 @@ fn test_complement3() { + .stdout_is("3he3ca33a3d33he3ba3"); + } + ++#[test] ++fn test_complement4() { ++ // $ echo -n '0x1y2z3' | tr -c '0-@' '*-~' ++ // 0~1~2~3 ++ new_ucmd!() ++ .args(&["-c", "0-@", "*-~"]) ++ .pipe_in("0x1y2z3") ++ .run() ++ .stdout_is("0~1~2~3"); ++ ++ // TODO: fix this ++ // $ echo '0x1y2z3' | tr -c '\0-@' '*-~' ++ // 0a1b2c3 ++ // new_ucmd!() ++ // .args(&["-c", "\\0-@", "*-~"]) ++ // .pipe_in("0x1y2z3") ++ // .run() ++ // .stdout_is("0a1b2c3"); ++} ++ + #[test] + fn test_squeeze() { + new_ucmd!() +-- +2.25.1 + diff --git a/debian/patches/tr-fix-merge-conflict.patch b/debian/patches/tr-fix-merge-conflict.patch new file mode 100644 index 0000000000000000000000000000000000000000..43f2dc1f7ddd33adfe878c345a8eba5d2d3fff93 --- /dev/null +++ b/debian/patches/tr-fix-merge-conflict.patch @@ -0,0 +1,27 @@ +From 000bd73edceae756be09693d4f7db9ed5899210a Mon Sep 17 00:00:00 2001 +From: Jan Scheer <jhscheer@users.noreply.github.com> +Date: Sun, 2 May 2021 12:39:25 +0200 +Subject: [PATCH] tr: fix merge conflict +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: 8bit + +--- + src/uu/tr/src/tr.rs | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/uu/tr/src/tr.rs b/src/uu/tr/src/tr.rs +index 5e6c41a8..dcb64a12 100644 +--- a/src/uu/tr/src/tr.rs ++++ b/src/uu/tr/src/tr.rs +@@ -182,7 +182,7 @@ impl TranslateAndSqueezeOperation { + complement: bool, + ) -> TranslateAndSqueezeOperation { + TranslateAndSqueezeOperation { +- translate: TranslateOperation::new(set1, set2, truncate), ++ translate: TranslateOperation::new(set1, set2, truncate, complement), + squeeze: SqueezeOperation::new(set2_, complement), + } + } +-- +2.25.1 + diff --git a/debian/patches/tr-fix-squeeze-and-complement-with-two-sets-2485.patch b/debian/patches/tr-fix-squeeze-and-complement-with-two-sets-2485.patch new file mode 100644 index 0000000000000000000000000000000000000000..23f5378a4342219b55b17d0c7f8ccb1249296a48 --- /dev/null +++ b/debian/patches/tr-fix-squeeze-and-complement-with-two-sets-2485.patch @@ -0,0 +1,80 @@ +From 30b80d0c82634451d855865125c99a54f98aad19 Mon Sep 17 00:00:00 2001 +From: Jan Scheer <jhscheer@users.noreply.github.com> +Date: Fri, 9 Jul 2021 22:55:00 +0200 +Subject: [PATCH] tr: fix squeeze and complement with two sets (#2485) +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: 8bit + +--- + src/uu/tr/src/tr.rs | 26 ++++++++------------------ + tests/by-util/test_tr.rs | 9 +++++++++ + 2 files changed, 17 insertions(+), 18 deletions(-) + +diff --git a/src/uu/tr/src/tr.rs b/src/uu/tr/src/tr.rs +index 28ce70c2..2abc652f 100644 +--- a/src/uu/tr/src/tr.rs ++++ b/src/uu/tr/src/tr.rs +@@ -173,16 +173,14 @@ struct TranslateAndSqueezeOperation { + } + + impl TranslateAndSqueezeOperation { +- fn new( +- set1: ExpandSet, +- set2: &mut ExpandSet, +- set2_: ExpandSet, +- truncate: bool, +- complement: bool, +- ) -> TranslateAndSqueezeOperation { ++ fn new(sets: Vec<String>, truncate: bool, complement: bool) -> TranslateAndSqueezeOperation { ++ let set1 = ExpandSet::new(sets[0].as_ref()); ++ let set1_ = ExpandSet::new(sets[0].as_ref()); ++ let mut set2 = ExpandSet::new(sets[1].as_ref()); ++ let set2_ = ExpandSet::new(sets[1].as_ref()); + TranslateAndSqueezeOperation { +- translate: TranslateOperation::new(set1, set2, truncate, complement), +- squeeze: SqueezeOperation::new(set2_, complement), ++ translate: TranslateOperation::new(set1, &mut set2, truncate, complement), ++ squeeze: SqueezeOperation::new(if complement { set1_ } else { set2_ }, complement), + } + } + } +@@ -302,15 +300,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { + let op = SqueezeOperation::new(set1, complement_flag); + translate_input(&mut locked_stdin, &mut buffered_stdout, op); + } else { +- let mut set2 = ExpandSet::new(sets[1].as_ref()); +- let set2_ = ExpandSet::new(sets[1].as_ref()); +- let op = TranslateAndSqueezeOperation::new( +- set1, +- &mut set2, +- set2_, +- complement_flag, +- truncate_flag, +- ); ++ let op = TranslateAndSqueezeOperation::new(sets, truncate_flag, complement_flag); + translate_input(&mut locked_stdin, &mut buffered_stdout, op); + } + } else { +diff --git a/tests/by-util/test_tr.rs b/tests/by-util/test_tr.rs +index 936af2ca..07e3a843 100644 +--- a/tests/by-util/test_tr.rs ++++ b/tests/by-util/test_tr.rs +@@ -127,6 +127,15 @@ fn test_squeeze_complement() { + .stdout_is("aaBcDcc"); + } + ++#[test] ++fn test_squeeze_complement_two_sets() { ++ new_ucmd!() ++ .args(&["-sc", "a", "_"]) ++ .pipe_in("test a aa with 3 ___ spaaaces +++") // spell-checker:disable-line ++ .run() ++ .stdout_is("_a_aa_aaa_"); ++} ++ + #[test] + fn test_translate_and_squeeze() { + new_ucmd!() +-- +2.25.1 + diff --git a/debian/patches/tr-implement-complement-separately-from-delete-or-sq.patch b/debian/patches/tr-implement-complement-separately-from-delete-or-sq.patch new file mode 100644 index 0000000000000000000000000000000000000000..5eb4aa307b126772b0b3031add86a32cfd927489 --- /dev/null +++ b/debian/patches/tr-implement-complement-separately-from-delete-or-sq.patch @@ -0,0 +1,128 @@ +From 117e84eed3adabb54fa852030712848f08c319f1 Mon Sep 17 00:00:00 2001 +From: Jan Scheer <jhscheer@users.noreply.github.com> +Date: Sat, 1 May 2021 18:46:13 +0200 +Subject: [PATCH] tr: implement complement separately from delete or squeeze + (#2147) +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: 8bit + +--- + src/uu/tr/src/tr.rs | 34 ++++++++++++++++++++++++---------- + tests/by-util/test_tr.rs | 27 +++++++++++++++++++++++++++ + 2 files changed, 51 insertions(+), 10 deletions(-) + +diff --git a/src/uu/tr/src/tr.rs b/src/uu/tr/src/tr.rs +index 6c1c0746..f8fabd69 100644 +--- a/src/uu/tr/src/tr.rs ++++ b/src/uu/tr/src/tr.rs +@@ -125,10 +125,17 @@ impl SymbolTranslator for DeleteAndSqueezeOperation { + + struct TranslateOperation { + translate_map: FnvHashMap<usize, char>, ++ complement: bool, ++ s2_last: char, + } + + impl TranslateOperation { +- fn new(set1: ExpandSet, set2: &mut ExpandSet, truncate: bool) -> TranslateOperation { ++ fn new( ++ set1: ExpandSet, ++ set2: &mut ExpandSet, ++ truncate: bool, ++ complement: bool, ++ ) -> TranslateOperation { + let mut map = FnvHashMap::default(); + let mut s2_prev = '_'; + for i in set1 { +@@ -141,13 +148,25 @@ impl TranslateOperation { + map.insert(i as usize, s2_prev); + } + } +- TranslateOperation { translate_map: map } ++ TranslateOperation { ++ translate_map: map, ++ complement, ++ s2_last: s2_prev, ++ } + } + } + + impl SymbolTranslator for TranslateOperation { + fn translate(&self, c: char, _prev_c: char) -> Option<char> { +- Some(*self.translate_map.get(&(c as usize)).unwrap_or(&c)) ++ if self.complement { ++ Some(if self.translate_map.contains_key(&(c as usize)) { ++ c ++ } else { ++ self.s2_last ++ }) ++ } else { ++ Some(*self.translate_map.get(&(c as usize)).unwrap_or(&c)) ++ } + } + } + +@@ -256,11 +275,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 { + return 1; + } + +- if complement_flag && !delete_flag && !squeeze_flag { +- show_error!("-c is only supported with -d or -s"); +- return 1; +- } +- + let stdin = stdin(); + let mut locked_stdin = stdin.lock(); + let stdout = stdout(); +@@ -282,8 +296,8 @@ pub fn uumain(args: impl uucore::Args) -> i32 { + translate_input(&mut locked_stdin, &mut buffered_stdout, op); + } else { + let mut set2 = ExpandSet::new(sets[1].as_ref()); +- let op = TranslateOperation::new(set1, &mut set2, truncate_flag); +- translate_input(&mut locked_stdin, &mut buffered_stdout, op) ++ let op = TranslateOperation::new(set1, &mut set2, truncate_flag, complement_flag); ++ translate_input(&mut locked_stdin, &mut buffered_stdout, op); + } + + 0 +diff --git a/tests/by-util/test_tr.rs b/tests/by-util/test_tr.rs +index 630c305c..637c9b10 100644 +--- a/tests/by-util/test_tr.rs ++++ b/tests/by-util/test_tr.rs +@@ -45,6 +45,33 @@ fn test_delete_complement() { + .stdout_is("ac"); + } + ++#[test] ++fn test_complement1() { ++ new_ucmd!() ++ .args(&["-c", "a", "X"]) ++ .pipe_in("ab") ++ .run() ++ .stdout_is("aX"); ++} ++ ++#[test] ++fn test_complement2() { ++ new_ucmd!() ++ .args(&["-c", "0-9", "x"]) ++ .pipe_in("Phone: 01234 567890") ++ .run() ++ .stdout_is("xxxxxxx01234x567890"); ++} ++ ++#[test] ++fn test_complement3() { ++ new_ucmd!() ++ .args(&["-c", "abcdefgh", "123"]) ++ .pipe_in("the cat and the bat") ++ .run() ++ .stdout_is("3he3ca33a3d33he3ba3"); ++} ++ + #[test] + fn test_squeeze() { + new_ucmd!() +-- +2.25.1 + diff --git a/debian/patches/tr-implement-translate-and-squeeze-s-mode.patch b/debian/patches/tr-implement-translate-and-squeeze-s-mode.patch new file mode 100644 index 0000000000000000000000000000000000000000..26fceff1903a60c1ef302f05efd9ebf396fb507c --- /dev/null +++ b/debian/patches/tr-implement-translate-and-squeeze-s-mode.patch @@ -0,0 +1,115 @@ +From 0f3bc237393adef485182656b0edc986575c592d Mon Sep 17 00:00:00 2001 +From: Jeffrey Finkelstein <jeffrey.finkelstein@protonmail.com> +Date: Thu, 29 Apr 2021 23:13:20 -0400 +Subject: [PATCH] tr: implement translate and squeeze (-s) mode +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: 8bit + +Add translate and squeeze mode to the `tr` program. For example: + + $ printf xx | tr -s x y + y + +Fixes #2141. +--- + src/uu/tr/src/tr.rs | 54 ++++++++++++++++++++++++++++++++++++++-- + tests/by-util/test_tr.rs | 18 ++++++++++++++ + 2 files changed, 70 insertions(+), 2 deletions(-) + +diff --git a/src/uu/tr/src/tr.rs b/src/uu/tr/src/tr.rs +index 6c1c0746..09c4304a 100644 +--- a/src/uu/tr/src/tr.rs ++++ b/src/uu/tr/src/tr.rs +@@ -278,8 +278,58 @@ pub fn uumain(args: impl uucore::Args) -> i32 { + translate_input(&mut locked_stdin, &mut buffered_stdout, op); + } + } else if squeeze_flag { +- let op = SqueezeOperation::new(set1, complement_flag); +- translate_input(&mut locked_stdin, &mut buffered_stdout, op); ++ if sets.len() < 2 { ++ let op = SqueezeOperation::new(set1, complement_flag); ++ translate_input(&mut locked_stdin, &mut buffered_stdout, op); ++ } else { ++ // Define a closure that computes the translation using a hash map. ++ // ++ // The `unwrap()` should never panic because the ++ // `TranslateOperation.translate()` method always returns ++ // `Some`. ++ let mut set2 = ExpandSet::new(sets[1].as_ref()); ++ let translator = TranslateOperation::new(set1, &mut set2, truncate_flag); ++ let translate = |c| translator.translate(c, 0 as char).unwrap(); ++ ++ // Prepare some variables to be used for the closure that ++ // computes the squeeze operation. ++ // ++ // The `squeeze()` closure needs to be defined anew for ++ // each line of input, but these variables do not change ++ // while reading the input so they can be defined before ++ // the `while` loop. ++ let set2 = ExpandSet::new(sets[1].as_ref()); ++ let squeezer = SqueezeOperation::new(set2, complement_flag); ++ ++ // Prepare some memory to read each line of the input (`buf`) and to write ++ let mut buf = String::with_capacity(BUFFER_LEN + 4); ++ ++ // Loop over each line of stdin. ++ while let Ok(length) = locked_stdin.read_line(&mut buf) { ++ if length == 0 { ++ break; ++ } ++ ++ // Define a closure that computes the squeeze operation. ++ // ++ // We keep track of the previously seen character on ++ // each call to `squeeze()`, but we need to reset the ++ // `prev_c` variable at the beginning of each line of ++ // the input. That's why we define the closure inside ++ // the `while` loop. ++ let mut prev_c = 0 as char; ++ let squeeze = |c| { ++ let result = squeezer.translate(c, prev_c); ++ prev_c = c; ++ result ++ }; ++ ++ // First translate, then squeeze each character of the input line. ++ let filtered: String = buf.chars().map(translate).filter_map(squeeze).collect(); ++ buf.clear(); ++ buffered_stdout.write_all(filtered.as_bytes()).unwrap(); ++ } ++ } + } else { + let mut set2 = ExpandSet::new(sets[1].as_ref()); + let op = TranslateOperation::new(set1, &mut set2, truncate_flag, complement_flag); +diff --git a/tests/by-util/test_tr.rs b/tests/by-util/test_tr.rs +index 630c305c..5d044a18 100644 +--- a/tests/by-util/test_tr.rs ++++ b/tests/by-util/test_tr.rs +@@ -63,6 +63,24 @@ fn test_squeeze_complement() { + .stdout_is("aaBcDcc"); + } + ++#[test] ++fn test_translate_and_squeeze() { ++ new_ucmd!() ++ .args(&["-s", "x", "y"]) ++ .pipe_in("xx") ++ .run() ++ .stdout_is("y"); ++} ++ ++#[test] ++fn test_translate_and_squeeze_multiple_lines() { ++ new_ucmd!() ++ .args(&["-s", "x", "y"]) ++ .pipe_in("xxaax\nxaaxx") ++ .run() ++ .stdout_is("yaay\nyaay"); ++} ++ + #[test] + fn test_delete_and_squeeze() { + new_ucmd!() +-- +2.25.1 +