@@ -17,8 +17,8 @@ use clap::{Arg, Command};
1717use clap_complete:: Shell ;
1818use clap_mangen:: Man ;
1919use fluent_syntax:: ast:: { Entry , Message , Pattern } ;
20- use jiff:: Zoned ;
2120use fluent_syntax:: parser;
21+ use jiff:: Zoned ;
2222use regex:: Regex ;
2323use textwrap:: { fill, indent, termwidth} ;
2424use zip:: ZipArchive ;
@@ -31,9 +31,12 @@ include!(concat!(env!("OUT_DIR"), "/uutils_map.rs"));
3131/// Post-process a generated manpage to fix mandoc lint issues
3232///
3333/// This function:
34- /// - 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
3535/// - Removes trailing whitespace from all lines
3636/// - 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
3740fn post_process_manpage ( manpage : String ) -> String {
3841 // Only match TH headers that have at least a command name on the same line
3942 // Use [ \t] instead of \s to avoid matching newlines
@@ -58,16 +61,22 @@ fn post_process_manpage(manpage: String) -> String {
5861 let line = lines[ i] . trim_end ( ) ;
5962
6063 if line == ".br" && !skip_indices. contains ( & i) {
61- // Check for consecutive .br macros
62- 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"
6368 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+ }
6474 }
65- // Check for .br, empty line, .br pattern
66- else if i + 2 < lines. len ( )
67- && lines[ i + 1 ] . trim ( ) . is_empty ( )
68- && lines[ i + 2 ] . trim_end ( ) == ".br"
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" )
6978 {
70- skip_indices. insert ( i + 2 ) ;
79+ skip_indices. insert ( i) ;
7180 }
7281 }
7382 }
@@ -826,4 +835,44 @@ mod tests {
826835 let result4 = post_process_manpage ( input4. to_string ( ) ) ;
827836 assert_eq ! ( result4, expected4) ;
828837 }
838+
839+ #[ test]
840+ fn test_post_process_manpage_removes_br_before_empty_line ( ) {
841+ // Test that .br is removed when followed by empty line (which becomes .sp)
842+ let input = ".TH TEST 1\n Some text\n .br\n \n More text\n " ;
843+ let expected = ".TH TEST 1 \" 2024-01-01\" \n Some text\n \n More text\n " ;
844+
845+ let result = post_process_manpage ( input. to_string ( ) ) ;
846+ assert_eq ! ( result, expected) ;
847+ }
848+
849+ #[ test]
850+ fn test_post_process_manpage_complex_br_before_empty ( ) {
851+ // Test multiple .br before empty line patterns
852+ let input = ".TH TEST 1\n Section 1\n .br\n \n Section 2\n .br\n \n Section 3\n " ;
853+ let expected = ".TH TEST 1 \" 2024-01-01\" \n Section 1\n \n Section 2\n \n Section 3\n " ;
854+
855+ let result = post_process_manpage ( input. to_string ( ) ) ;
856+ assert_eq ! ( result, expected) ;
857+ }
858+
859+ #[ test]
860+ fn test_post_process_manpage_removes_br_after_empty_line ( ) {
861+ // Test that .br is removed when preceded by empty line (which becomes .sp)
862+ let input = ".TH TEST 1\n Some text\n \n .br\n More text\n " ;
863+ let expected = ".TH TEST 1 \" 2024-01-01\" \n Some text\n \n More text\n " ;
864+
865+ let result = post_process_manpage ( input. to_string ( ) ) ;
866+ assert_eq ! ( result, expected) ;
867+ }
868+
869+ #[ test]
870+ fn test_post_process_manpage_fixes_escape_sequences ( ) {
871+ // Test that \\\\0 and \\0 are fixed to \e0 (literal backslash-zero)
872+ let input = ".TH TEST 1\n Text with \\ \\ \\ \\ 0 and \\ \\ 0 escape\n " ;
873+ let expected = ".TH TEST 1 \" 2024-01-01\" \n Text with \\ e0 and \\ e0 escape\n " ;
874+
875+ let result = post_process_manpage ( input. to_string ( ) ) ;
876+ assert_eq ! ( result, expected) ;
877+ }
829878}
0 commit comments