diff --git a/src/uniq/uniq.rs b/src/uniq/uniq.rs index 0c1200a25..aa1951613 100644 --- a/src/uniq/uniq.rs +++ b/src/uniq/uniq.rs @@ -34,6 +34,7 @@ struct Uniq { slice_start: Option, slice_stop: Option, ignore_case: bool, + zero_terminated: bool, } impl Uniq { @@ -41,9 +42,10 @@ impl Uniq { let mut lines: Vec = vec!(); let mut first_line_printed = false; let delimiters = &self.delimiters[..]; + let line_terminator = self.get_line_terminator(); - for io_line in reader.lines() { - let line = crash_if_err!(1, io_line); + for io_line in reader.split(line_terminator) { + let line = String::from_utf8(crash_if_err!(1, io_line)).unwrap(); if !lines.is_empty() && self.cmp_key(&lines[0]) != self.cmp_key(&line) { let print_delimiter = delimiters == "prepend" || (delimiters == "separate" && first_line_printed); first_line_printed |= self.print_lines(writer, &lines, print_delimiter); @@ -80,6 +82,14 @@ impl Uniq { } } + fn get_line_terminator(&self) -> u8 { + if self.zero_terminated { + 0 + } else { + '\n' as u8 + } + } + fn cmp_key(&self, line: &str) -> String { let fields_to_check = &self.skip_fields(line); let len = fields_to_check.len(); @@ -116,8 +126,10 @@ impl Uniq { } fn print_line(&self, writer: &mut BufWriter, line: &str, count: usize, print_delimiter: bool) { + let line_terminator = self.get_line_terminator(); + if print_delimiter { - crash_if_err!(1, writer.write_all(&['\n' as u8])); + crash_if_err!(1, writer.write_all(&[line_terminator])); } crash_if_err!(1, if self.show_counts { @@ -125,7 +137,7 @@ impl Uniq { } else { writer.write_all(line.as_bytes()) }); - crash_if_err!(1, writer.write_all("\n".as_bytes())); + crash_if_err!(1, writer.write_all(&[line_terminator])); } } @@ -153,6 +165,7 @@ pub fn uumain(args: Vec) -> i32 { opts.optopt("w", "check-chars", "compare no more than N characters in lines", "N"); opts.optflag("i", "ignore-case", "ignore differences in case when comparing"); opts.optflag("u", "unique", "only print unique lines"); + opts.optflag("z", "zero-terminated", "end lines with 0 byte, not newline"); opts.optflag("h", "help", "display this help and exit"); opts.optflag("V", "version", "output version information and exit"); @@ -202,6 +215,7 @@ pub fn uumain(args: Vec) -> i32 { slice_start: opt_parsed("skip-chars", &matches), slice_stop: opt_parsed("check-chars", &matches), ignore_case: matches.opt_present("ignore-case"), + zero_terminated: matches.opt_present("zero-terminated"), }; uniq.print_uniq(&mut open_input_file(in_file_name), &mut open_output_file(out_file_name)); diff --git a/tests/fixtures/uniq/sorted-zero-terminated.expected b/tests/fixtures/uniq/sorted-zero-terminated.expected new file mode 100644 index 000000000..94081d364 Binary files /dev/null and b/tests/fixtures/uniq/sorted-zero-terminated.expected differ diff --git a/tests/fixtures/uniq/sorted-zero-terminated.txt b/tests/fixtures/uniq/sorted-zero-terminated.txt new file mode 100644 index 000000000..588f7b076 Binary files /dev/null and b/tests/fixtures/uniq/sorted-zero-terminated.txt differ diff --git a/tests/test_uniq.rs b/tests/test_uniq.rs index 5a13cfe4d..f7cf42d45 100644 --- a/tests/test_uniq.rs +++ b/tests/test_uniq.rs @@ -8,6 +8,7 @@ fn new_ucmd() -> UCommand { static INPUT: &'static str = "sorted.txt"; static SKIP_CHARS: &'static str = "skip-chars.txt"; static SKIP_FIELDS: &'static str = "skip-fields.txt"; +static SORTED_ZERO_TERMINATED: &'static str = "sorted-zero-terminated.txt"; #[test] fn test_stdin_default() { @@ -92,3 +93,10 @@ fn test_stdin_repeated_only() { .args(&["-d"]).pipe_in_fixture(INPUT) .run().stdout_is_fixture("sorted-repeated-only.expected"); } + +#[test] +fn test_stdin_zero_terminated() { + new_ucmd() + .args(&["-z"]).pipe_in_fixture(SORTED_ZERO_TERMINATED) + .run().stdout_is_fixture("sorted-zero-terminated.expected"); +} \ No newline at end of file