diff --git a/src/uu/fold/src/fold.rs b/src/uu/fold/src/fold.rs index 8cf95b82a..fac0ff28e 100644 --- a/src/uu/fold/src/fold.rs +++ b/src/uu/fold/src/fold.rs @@ -14,6 +14,8 @@ use std::fs::File; use std::io::{stdin, BufRead, BufReader, Read}; use std::path::Path; +const TAB_WIDTH: usize = 8; + static SYNTAX: &str = "[OPTION]... [FILE]..."; static SUMMARY: &str = "Writes each file (or standard input if no files are given) to standard output whilst breaking long lines"; @@ -220,11 +222,14 @@ fn fold_file(mut file: BufReader, spaces: bool, width: usize) { match ch { '\t' => { - if col_count + 8 > width && !output.is_empty() { + let next_tab_stop = col_count + TAB_WIDTH - col_count % TAB_WIDTH; + + if next_tab_stop > width && !output.is_empty() { emit_output!(); } - col_count += 8; - last_space = Some(char_count); + + col_count = next_tab_stop; + last_space = if spaces { Some(char_count) } else { None }; } '\x08' => { // FIXME: does not match GNU's handling of backspace diff --git a/tests/by-util/test_fold.rs b/tests/by-util/test_fold.rs index 52e630e5b..cc92c8ff3 100644 --- a/tests/by-util/test_fold.rs +++ b/tests/by-util/test_fold.rs @@ -132,7 +132,7 @@ fn test_single_tab_should_not_add_extra_newline() { } #[test] -fn test_tab_counts_as_8_columns() { +fn test_initial_tab_counts_as_8_columns() { new_ucmd!() .arg("-w8") .pipe_in("\t1") @@ -140,6 +140,33 @@ fn test_tab_counts_as_8_columns() { .stdout_is("\t\n1"); } +#[test] +fn test_tab_should_advance_to_next_tab_stop() { + // tab advances the column count to the next tab stop, i.e. the width + // of the tab varies based on the leading text + new_ucmd!() + .args(&["-w8", "tab_stops.input"]) + .succeeds() + .stdout_is_fixture("tab_stops_w8.expected"); +} + +#[test] +fn test_all_tabs_should_advance_to_next_tab_stops() { + new_ucmd!() + .args(&["-w16", "tab_stops.input"]) + .succeeds() + .stdout_is_fixture("tab_stops_w16.expected"); +} + +#[test] +fn test_fold_before_tab_with_narrow_width() { + new_ucmd!() + .arg("-w7") + .pipe_in("a\t1") + .succeeds() + .stdout_is("a\n\t\n1"); +} + #[test] fn test_fold_at_word_boundary() { new_ucmd!() @@ -167,8 +194,35 @@ fn test_fold_at_word_boundary_preserve_final_newline() { .stdout_is("one \ntwo\n"); } +#[test] +fn test_fold_at_tab() { + new_ucmd!() + .arg("-w8") + .pipe_in("a\tbbb\n") + .succeeds() + .stdout_is("a\t\nbbb\n"); +} + +#[test] +fn test_fold_after_tab() { + new_ucmd!() + .arg("-w10") + .pipe_in("a\tbbb\n") + .succeeds() + .stdout_is("a\tbb\nb\n"); +} + #[test] fn test_fold_at_tab_as_word_boundary() { + new_ucmd!() + .args(&["-w8", "-s"]) + .pipe_in("a\tbbb\n") + .succeeds() + .stdout_is("a\t\nbbb\n"); +} + +#[test] +fn test_fold_after_tab_as_word_boundary() { new_ucmd!() .args(&["-w10", "-s"]) .pipe_in("a\tbbb\n") @@ -317,6 +371,15 @@ fn test_tab_counts_as_one_byte() { .stdout_is("1\t\n2\n"); } +#[test] +fn test_bytewise_fold_before_tab_with_narrow_width() { + new_ucmd!() + .args(&["-w7", "-b"]) + .pipe_in("a\t1") + .succeeds() + .stdout_is("a\t1"); +} + #[test] fn test_bytewise_fold_at_word_boundary_only_whitespace() { new_ucmd!() diff --git a/tests/fixtures/fold/tab_stops.input b/tests/fixtures/fold/tab_stops.input new file mode 100644 index 000000000..a96a378ea --- /dev/null +++ b/tests/fixtures/fold/tab_stops.input @@ -0,0 +1,11 @@ +1 +12 +123 +1234 +12345 +123456 +1234567 +12345678 +123456781 +12345678 2 +12345678 2 4 diff --git a/tests/fixtures/fold/tab_stops_w16.expected b/tests/fixtures/fold/tab_stops_w16.expected new file mode 100644 index 000000000..8122ac16d --- /dev/null +++ b/tests/fixtures/fold/tab_stops_w16.expected @@ -0,0 +1,13 @@ +1 +12 +123 +1234 +12345 +123456 +1234567 +12345678 +123456781 +12345678 +2 +12345678 +2 4 diff --git a/tests/fixtures/fold/tab_stops_w8.expected b/tests/fixtures/fold/tab_stops_w8.expected new file mode 100644 index 000000000..3173a4a22 --- /dev/null +++ b/tests/fixtures/fold/tab_stops_w8.expected @@ -0,0 +1,18 @@ +1 +12 +123 +1234 +12345 +123456 +1234567 +12345678 + +12345678 +1 +12345678 + +2 +12345678 + +2 +4