From 346415e1d2a2ff5b2a1859b7ad0473d1b34c5790 Mon Sep 17 00:00:00 2001 From: Justin Tracey Date: Wed, 5 Jan 2022 23:26:12 -0500 Subject: [PATCH] join: add support for -z option --- src/uu/join/src/join.rs | 42 +++++++++++++++++++++++++++++---- tests/by-util/test_join.rs | 10 ++++++++ tests/fixtures/join/z.expected | Bin 0 -> 12 bytes 3 files changed, 48 insertions(+), 4 deletions(-) create mode 100644 tests/fixtures/join/z.expected diff --git a/src/uu/join/src/join.rs b/src/uu/join/src/join.rs index 0c881f20d..a338a22c4 100644 --- a/src/uu/join/src/join.rs +++ b/src/uu/join/src/join.rs @@ -25,6 +25,13 @@ enum FileNum { File2, } +#[repr(u8)] +#[derive(Copy, Clone)] +enum LineEnding { + Nul = 0, + Newline = b'\n', +} + #[derive(Copy, Clone)] enum Sep { Char(u8), @@ -46,6 +53,7 @@ struct Settings { print_unpaired2: bool, print_joined: bool, ignore_case: bool, + line_ending: LineEnding, separator: Sep, autoformat: bool, format: Vec, @@ -63,6 +71,7 @@ impl Default for Settings { print_unpaired2: false, print_joined: true, ignore_case: false, + line_ending: LineEnding::Newline, separator: Sep::Whitespaces, autoformat: false, format: vec![], @@ -75,14 +84,21 @@ impl Default for Settings { /// Output representation. struct Repr<'a> { + line_ending: LineEnding, separator: u8, format: &'a [Spec], empty: &'a [u8], } impl<'a> Repr<'a> { - fn new(separator: u8, format: &'a [Spec], empty: &'a [u8]) -> Repr<'a> { + fn new( + line_ending: LineEnding, + separator: u8, + format: &'a [Spec], + empty: &'a [u8], + ) -> Repr<'a> { Repr { + line_ending, separator, format, empty, @@ -133,6 +149,10 @@ impl<'a> Repr<'a> { } Ok(()) } + + fn print_line_ending(&self) -> Result<(), std::io::Error> { + stdout().write_all(&[self.line_ending as u8]) + } } /// Input processing parameters. @@ -260,6 +280,7 @@ impl<'a> State<'a> { name: &'a str, stdin: &'a Stdin, key: usize, + line_ending: LineEnding, print_unpaired: bool, ) -> State<'a> { let f = if name == "-" { @@ -276,7 +297,7 @@ impl<'a> State<'a> { file_name: name, file_num, print_unpaired, - lines: f.split(b'\n'), + lines: f.split(line_ending as u8), seq: Vec::new(), line_num: 0, has_failed: false, @@ -351,7 +372,7 @@ impl<'a> State<'a> { repr.print_fields(line2, other.key)?; } - stdout().write_all(&[b'\n'])?; + repr.print_line_ending()?; } } @@ -461,7 +482,7 @@ impl<'a> State<'a> { repr.print_fields(line, self.key)?; } - stdout().write_all(&[b'\n']) + repr.print_line_ending() } fn print_first_line(&self, repr: &Repr) -> Result<(), std::io::Error> { @@ -540,6 +561,10 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { settings.headers = true; } + if matches.is_present("z") { + settings.line_ending = LineEnding::Nul; + } + let file1 = matches.value_of("file1").unwrap(); let file2 = matches.value_of("file2").unwrap(); @@ -646,6 +671,12 @@ FILENUM is 1 or 2, corresponding to FILE1 or FILE2", "treat the first line in each file as field headers, \ print them without trying to pair them", )) + .arg( + Arg::with_name("z") + .short("z") + .long("zero-terminated") + .help("line delimiter is NUL, not newline"), + ) .arg( Arg::with_name("file1") .required(true) @@ -668,6 +699,7 @@ fn exec(file1: &str, file2: &str, settings: Settings) -> Result<(), std::io::Err file1, &stdin, settings.key1, + settings.line_ending, settings.print_unpaired1, ); @@ -676,6 +708,7 @@ fn exec(file1: &str, file2: &str, settings: Settings) -> Result<(), std::io::Err file2, &stdin, settings.key2, + settings.line_ending, settings.print_unpaired2, ); @@ -705,6 +738,7 @@ fn exec(file1: &str, file2: &str, settings: Settings) -> Result<(), std::io::Err }; let repr = Repr::new( + settings.line_ending, match settings.separator { Sep::Char(sep) => sep, _ => b' ', diff --git a/tests/by-util/test_join.rs b/tests/by-util/test_join.rs index be25b9390..4b2d1bbba 100644 --- a/tests/by-util/test_join.rs +++ b/tests/by-util/test_join.rs @@ -346,3 +346,13 @@ fn non_unicode() { .succeeds() .stdout_only_fixture("non-unicode.expected"); } + +#[test] +fn null_line_endings() { + new_ucmd!() + .arg("-z") + .arg("non-unicode_1.bin") + .arg("non-unicode_2.bin") + .succeeds() + .stdout_only_fixture("z.expected"); +} diff --git a/tests/fixtures/join/z.expected b/tests/fixtures/join/z.expected new file mode 100644 index 0000000000000000000000000000000000000000..ed85bb05393f487f3b5e9f736d0611d140a046f3 GIT binary patch literal 12 TcmYdPSgx>~Aw?loA&mh57>)y9 literal 0 HcmV?d00001