diff --git a/src/uu/ptx/src/ptx.rs b/src/uu/ptx/src/ptx.rs index 7749ecc998a..3f66857c24b 100644 --- a/src/uu/ptx/src/ptx.rs +++ b/src/uu/ptx/src/ptx.rs @@ -457,11 +457,11 @@ fn get_output_chunks( // https://github.com/MaiZure/coreutils-8.3/blob/master/src/ptx.c#L1234 let half_line_size = config.line_width / 2; let max_before_size = cmp::max(half_line_size as isize - config.gap_size as isize, 0) as usize; + + let keyword_len = keyword.chars().count(); + let trunc_len = config.trunc_str.chars().count(); let max_after_size = cmp::max( - half_line_size as isize - - (2 * config.trunc_str.len()) as isize - - keyword.len() as isize - - 1, + half_line_size as isize - (2 * trunc_len) as isize - keyword_len as isize - 1, 0, ) as usize; @@ -502,7 +502,7 @@ fn get_output_chunks( // and get the string let after_str: String = all_after[0..after_end].iter().collect(); after.push_str(&after_str); - assert!(max_after_size >= after.len()); + assert!(max_after_size >= after.chars().count()); // the tail chunk diff --git a/tests/by-util/test_ptx.rs b/tests/by-util/test_ptx.rs index 63ac3ca8fd4..826d1906a67 100644 --- a/tests/by-util/test_ptx.rs +++ b/tests/by-util/test_ptx.rs @@ -339,6 +339,18 @@ fn test_unicode_truncation_alignment() { .stdout_only(" / bar\n föö/\n"); } +#[test] +fn test_unicode_in_after_chunk_does_not_panic() { + // Regression test for a panic in get_output_chunks() when the computed + // max_after_size used byte lengths but the output was assembled as chars. + // The emoji is multibyte in UTF-8 and previously could trigger: + // `assertion failed: max_after_size >= after.len()`. + new_ucmd!() + .pipe_in("We've got +11 more G of 1.70. 🛠\n") + .succeeds() + .stdout_contains("We've got +11"); +} + #[test] fn test_duplicate_input_files() { new_ucmd!()