@@ -13,6 +13,7 @@ use std::{
1313 process,
1414} ;
1515
16+ use jiff:: Zoned ;
1617use clap:: { Arg , Command } ;
1718use clap_complete:: Shell ;
1819use clap_mangen:: Man ;
@@ -30,9 +31,12 @@ include!(concat!(env!("OUT_DIR"), "/uutils_map.rs"));
3031/// Post-process a generated manpage to fix mandoc lint issues
3132///
3233/// This function:
33- /// - Fixes the TH header by uppercasing command names and removing invalid date formats
34+ /// - Fixes the TH header by uppercasing command names and adding a proper date
3435/// - Removes trailing whitespace from all lines
3536/// - Fixes redundant .br paragraph macros that cause mandoc warnings
37+ /// - Removes .br before empty lines to avoid "br before sp" warnings
38+ /// - Removes .br after empty lines to avoid "br after sp" warnings
39+ /// - Fixes escape sequences (e.g., \\\\0 to \\0) to avoid "undefined escape" warnings
3640fn post_process_manpage ( manpage : String ) -> String {
3741 // Only match TH headers that have at least a command name on the same line
3842 // Use [ \t] instead of \s to avoid matching newlines
@@ -57,16 +61,21 @@ fn post_process_manpage(manpage: String) -> String {
5761 let line = lines[ i] . trim_end ( ) ;
5862
5963 if line == ".br" && !skip_indices. contains ( & i) {
60- // Check for consecutive .br macros
61- if i > 0 && lines[ i - 1 ] . trim_end ( ) == ".br" {
64+ // Check for .br followed by empty line
65+ if i + 1 < lines. len ( ) && lines[ i + 1 ] . trim ( ) . is_empty ( ) {
66+ // Remove the .br when it's followed by an empty line
67+ // This prevents "WARNING: skipping paragraph macro: br before sp"
6268 skip_indices. insert ( i) ;
69+
70+ // Also check if there's another .br after the empty line (common pattern)
71+ if i + 2 < lines. len ( ) && lines[ i + 2 ] . trim_end ( ) == ".br" {
72+ skip_indices. insert ( i + 2 ) ;
73+ }
6374 }
64- // Check for .br, empty line, .br pattern
65- else if i + 2 < lines. len ( )
66- && lines[ i + 1 ] . trim ( ) . is_empty ( )
67- && lines[ i + 2 ] . trim_end ( ) == ".br"
68- {
69- skip_indices. insert ( i + 2 ) ;
75+ // Check for .br preceded by empty line or another .br
76+ // This prevents "WARNING: skipping paragraph macro: br after sp" and consecutive .br
77+ else if i > 0 && ( lines[ i - 1 ] . trim ( ) . is_empty ( ) || lines[ i - 1 ] . trim_end ( ) == ".br" ) {
78+ skip_indices. insert ( i) ;
7079 }
7180 }
7281 }
@@ -825,4 +834,44 @@ mod tests {
825834 let result4 = post_process_manpage ( input4. to_string ( ) ) ;
826835 assert_eq ! ( result4, expected4) ;
827836 }
837+
838+ #[ test]
839+ fn test_post_process_manpage_removes_br_before_empty_line ( ) {
840+ // Test that .br is removed when followed by empty line (which becomes .sp)
841+ let input = ".TH TEST 1\n Some text\n .br\n \n More text\n " ;
842+ let expected = ".TH TEST 1 \" 2024-01-01\" \n Some text\n \n More text\n " ;
843+
844+ let result = post_process_manpage ( input. to_string ( ) ) ;
845+ assert_eq ! ( result, expected) ;
846+ }
847+
848+ #[ test]
849+ fn test_post_process_manpage_complex_br_before_empty ( ) {
850+ // Test multiple .br before empty line patterns
851+ let input = ".TH TEST 1\n Section 1\n .br\n \n Section 2\n .br\n \n Section 3\n " ;
852+ let expected = ".TH TEST 1 \" 2024-01-01\" \n Section 1\n \n Section 2\n \n Section 3\n " ;
853+
854+ let result = post_process_manpage ( input. to_string ( ) ) ;
855+ assert_eq ! ( result, expected) ;
856+ }
857+
858+ #[ test]
859+ fn test_post_process_manpage_removes_br_after_empty_line ( ) {
860+ // Test that .br is removed when preceded by empty line (which becomes .sp)
861+ let input = ".TH TEST 1\n Some text\n \n .br\n More text\n " ;
862+ let expected = ".TH TEST 1 \" 2024-01-01\" \n Some text\n \n More text\n " ;
863+
864+ let result = post_process_manpage ( input. to_string ( ) ) ;
865+ assert_eq ! ( result, expected) ;
866+ }
867+
868+ #[ test]
869+ fn test_post_process_manpage_fixes_escape_sequences ( ) {
870+ // Test that \\\\0 and \\0 are fixed to \e0 (literal backslash-zero)
871+ let input = ".TH TEST 1\n Text with \\ \\ \\ \\ 0 and \\ \\ 0 escape\n " ;
872+ let expected = ".TH TEST 1 \" 2024-01-01\" \n Text with \\ e0 and \\ e0 escape\n " ;
873+
874+ let result = post_process_manpage ( input. to_string ( ) ) ;
875+ assert_eq ! ( result, expected) ;
876+ }
828877}
0 commit comments