From 81996915dfc0506f6c9fb6d4660b9fa6d866bea0 Mon Sep 17 00:00:00 2001 From: Erik Vesteraas Date: Mon, 23 Jan 2017 03:34:47 +0100 Subject: [PATCH 1/2] cat: fix for numbered lines w/ no trailing newline Make at_line_start persist between printing each file. This fixes an issue when numbering lines in the output and one of the input files does not have a trailing newline. --- src/cat/cat.rs | 26 ++++++++++++-------------- tests/fixtures/cat/nonewline.txt | 1 + tests/test_cat.rs | 9 +++++++++ 3 files changed, 22 insertions(+), 14 deletions(-) create mode 100644 tests/fixtures/cat/nonewline.txt diff --git a/src/cat/cat.rs b/src/cat/cat.rs index 48587ffe3..41b0e7547 100644 --- a/src/cat/cat.rs +++ b/src/cat/cat.rs @@ -304,9 +304,10 @@ fn write_fast(files: Vec) -> CatResult<()> { fn write_lines(files: Vec, options: &OutputOptions) -> CatResult<()> { let mut line_counter: usize = 1; let mut error_count = 0; + let mut at_line_start = true; for file in files { - let written = write_file_lines(&file[..], options, line_counter); + let written = write_file_lines(&file[..], options, line_counter, &mut at_line_start); line_counter += match written { Ok(lines) => lines, Err(error) => { @@ -326,16 +327,13 @@ fn write_lines(files: Vec, options: &OutputOptions) -> CatResult<()> { /// Outputs file and returns result with the number of lines to stdout /// from `file`. If line numbering is enabled, then start output /// numbering at `line_number`. -/// -/// # Arguments -/// -/// * `files` - There is no short circuit when encountiner an error -/// reading a file in this vector -fn write_file_lines(file: &str, options: &OutputOptions, line_number: usize) -> CatResult { +fn write_file_lines(file: &str, + options: &OutputOptions, + line_number: usize, + at_line_start: &mut bool) -> CatResult { let mut handle = open(&file[..])?; let mut in_buf = [0; 1024 * 31]; let mut writer = BufWriter::with_capacity(1024 * 64, stdout()); - let mut at_line_start = true; let mut one_blank_kept = false; let mut lines = 0; @@ -349,9 +347,9 @@ fn write_file_lines(file: &str, options: &OutputOptions, line_number: usize) -> while pos < n { // skip empty line_number enumerating them if needed if in_buf[pos] == '\n' as u8 { - if !at_line_start || ! options.squeeze_blank || !one_blank_kept { + if !*at_line_start || ! options.squeeze_blank || !one_blank_kept { one_blank_kept = true; - if at_line_start && options.number == NumberingMode::NumberAll { + if *at_line_start && options.number == NumberingMode::NumberAll { write!(&mut writer, "{0:6}\t", line_number + lines)?; lines += 1; } @@ -360,12 +358,12 @@ fn write_file_lines(file: &str, options: &OutputOptions, line_number: usize) -> writer.flush().context(&file[..])?; } } - at_line_start = true; + *at_line_start = true; pos += 1; continue; } one_blank_kept = false; - if at_line_start && options.number != NumberingMode::NumberNone { + if *at_line_start && options.number != NumberingMode::NumberNone { write!(&mut writer, "{0:6}\t", line_number + lines)?; lines += 1; } @@ -380,7 +378,7 @@ fn write_file_lines(file: &str, options: &OutputOptions, line_number: usize) -> }; // end of buffer? if offset == 0 { - at_line_start = false; + *at_line_start = false; break; } // print suitable end of line @@ -388,7 +386,7 @@ fn write_file_lines(file: &str, options: &OutputOptions, line_number: usize) -> if handle.is_interactive { writer.flush()?; } - at_line_start = true; + *at_line_start = true; pos += offset; } } diff --git a/tests/fixtures/cat/nonewline.txt b/tests/fixtures/cat/nonewline.txt new file mode 100644 index 000000000..320b95eaf --- /dev/null +++ b/tests/fixtures/cat/nonewline.txt @@ -0,0 +1 @@ +text without a trailing newline \ No newline at end of file diff --git a/tests/test_cat.rs b/tests/test_cat.rs index c476bb2c0..027bfddd1 100644 --- a/tests/test_cat.rs +++ b/tests/test_cat.rs @@ -22,6 +22,15 @@ fn test_output_multi_files_print_all_chars() { pM-qM-rM-sM-tM-uM-vM-wM-xM-yM-zM-{M-|M-}M-~M-^?"); } +#[test] +fn test_numbered_lines_no_trailing_newline() { + new_ucmd!() + .args(&["nonewline.txt", "alpha.txt", "-n"]) + .succeeds() + .stdout_only(" 1\ttext without a trailing newlineabcde\n 2\tfghij\n \ + 3\tklmno\n 4\tpqrst\n 5\tuvwxyz\n"); +} + #[test] fn test_stdin_show_nonprinting() { for same_param in vec!["-v", "--show-nonprinting"] { From 21d9152cfefda1c6918a99750c66871c261cbe17 Mon Sep 17 00:00:00 2001 From: Erik Vesteraas Date: Mon, 23 Jan 2017 11:07:47 +0100 Subject: [PATCH 2/2] cat: Collect output state into a struct --- src/cat/cat.rs | 59 +++++++++++++++++++++++++------------------------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/src/cat/cat.rs b/src/cat/cat.rs index 41b0e7547..9b0a763f0 100644 --- a/src/cat/cat.rs +++ b/src/cat/cat.rs @@ -292,6 +292,14 @@ fn write_fast(files: Vec) -> CatResult<()> { } } +/// State that persists between output of each file +struct OutputState { + /// The current line number + line_number: usize, + + /// Whether the output cursor is at the beginning of a new line + at_line_start: bool, +} /// Writes files to stdout with `options` as configuration. Returns /// `Ok(())` if no errors were encountered, or an error with the @@ -302,19 +310,16 @@ fn write_fast(files: Vec) -> CatResult<()> { /// * `files` - There is no short circuit when encountiner an error /// reading a file in this vector fn write_lines(files: Vec, options: &OutputOptions) -> CatResult<()> { - let mut line_counter: usize = 1; let mut error_count = 0; - let mut at_line_start = true; + let mut state = OutputState { + line_number: 1, + at_line_start: true, + }; for file in files { - let written = write_file_lines(&file[..], options, line_counter, &mut at_line_start); - line_counter += match written { - Ok(lines) => lines, - Err(error) => { - writeln!(&mut stderr(), "{}", error).context(&file[..])?; - error_count += 1; - 0 - } + if let Err(error) = write_file_lines(&file, options, &mut state) { + writeln!(&mut stderr(), "{}", error).context(&file[..])?; + error_count += 1; } } @@ -324,20 +329,16 @@ fn write_lines(files: Vec, options: &OutputOptions) -> CatResult<()> { } } -/// Outputs file and returns result with the number of lines to stdout -/// from `file`. If line numbering is enabled, then start output -/// numbering at `line_number`. +/// Outputs file contents to stdout in a linewise fashion, +/// propagating any errors that might occur. fn write_file_lines(file: &str, options: &OutputOptions, - line_number: usize, - at_line_start: &mut bool) -> CatResult { - let mut handle = open(&file[..])?; + state: &mut OutputState) -> CatResult<()> { + let mut handle = open(file)?; let mut in_buf = [0; 1024 * 31]; let mut writer = BufWriter::with_capacity(1024 * 64, stdout()); let mut one_blank_kept = false; - let mut lines = 0; - while let Ok(n) = handle.reader.read(&mut in_buf) { if n == 0 { break; @@ -347,25 +348,25 @@ fn write_file_lines(file: &str, while pos < n { // skip empty line_number enumerating them if needed if in_buf[pos] == '\n' as u8 { - if !*at_line_start || ! options.squeeze_blank || !one_blank_kept { + if !state.at_line_start || !options.squeeze_blank || !one_blank_kept { one_blank_kept = true; - if *at_line_start && options.number == NumberingMode::NumberAll { - write!(&mut writer, "{0:6}\t", line_number + lines)?; - lines += 1; + if state.at_line_start && options.number == NumberingMode::NumberAll { + write!(&mut writer, "{0:6}\t", state.line_number)?; + state.line_number += 1; } writer.write_all(options.end_of_line.as_bytes())?; if handle.is_interactive { writer.flush().context(&file[..])?; } } - *at_line_start = true; + state.at_line_start = true; pos += 1; continue; } one_blank_kept = false; - if *at_line_start && options.number != NumberingMode::NumberNone { - write!(&mut writer, "{0:6}\t", line_number + lines)?; - lines += 1; + if state.at_line_start && options.number != NumberingMode::NumberNone { + write!(&mut writer, "{0:6}\t", state.line_number)?; + state.line_number += 1; } // print to end of line or end of buffer @@ -378,7 +379,7 @@ fn write_file_lines(file: &str, }; // end of buffer? if offset == 0 { - *at_line_start = false; + state.at_line_start = false; break; } // print suitable end of line @@ -386,12 +387,12 @@ fn write_file_lines(file: &str, if handle.is_interactive { writer.flush()?; } - *at_line_start = true; + state.at_line_start = true; pos += offset; } } - Ok(lines) + Ok(()) } // write***_to_end methods