@@ -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,10 +31,13 @@ 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- fn post_process_manpage ( manpage : String ) -> String {
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
40+ fn post_process_manpage ( manpage : String , date : Option < & str > ) -> 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
4043 // Use a date format that satisfies mandoc (YYYY-MM-DD)
@@ -61,16 +64,22 @@ fn post_process_manpage(manpage: String) -> String {
6164 let line = lines[ i] . trim_end ( ) ;
6265
6366 if line == ".br" && !skip_indices. contains ( & i) {
64- // Check for consecutive .br macros
65- if i > 0 && lines[ i - 1 ] . trim_end ( ) == ".br" {
67+ // Check for .br followed by empty line
68+ if i + 1 < lines. len ( ) && lines[ i + 1 ] . trim ( ) . is_empty ( ) {
69+ // Remove the .br when it's followed by an empty line
70+ // This prevents "WARNING: skipping paragraph macro: br before sp"
6671 skip_indices. insert ( i) ;
72+
73+ // Also check if there's another .br after the empty line (common pattern)
74+ if i + 2 < lines. len ( ) && lines[ i + 2 ] . trim_end ( ) == ".br" {
75+ skip_indices. insert ( i + 2 ) ;
76+ }
6777 }
68- // Check for .br, empty line, .br pattern
69- else if i + 2 < lines. len ( )
70- && lines[ i + 1 ] . trim ( ) . is_empty ( )
71- && lines[ i + 2 ] . trim_end ( ) == ".br"
78+ // Check for .br preceded by empty line or another .br
79+ // This prevents "WARNING: skipping paragraph macro: br after sp" and consecutive .br
80+ else if i > 0 && ( lines[ i - 1 ] . trim ( ) . is_empty ( ) || lines[ i - 1 ] . trim_end ( ) == ".br" )
7281 {
73- skip_indices. insert ( i + 2 ) ;
82+ skip_indices. insert ( i) ;
7483 }
7584 }
7685 }
@@ -829,4 +838,44 @@ mod tests {
829838 let result4 = post_process_manpage ( input4. to_string ( ) , Some ( "2024-01-01" ) ) ;
830839 assert_eq ! ( result4, expected4) ;
831840 }
841+
842+ #[ test]
843+ fn test_post_process_manpage_removes_br_before_empty_line ( ) {
844+ // Test that .br is removed when followed by empty line (which becomes .sp)
845+ let input = ".TH TEST 1\n Some text\n .br\n \n More text\n " ;
846+ let expected = ".TH TEST 1 \" 2024-01-01\" \n Some text\n \n More text\n " ;
847+
848+ let result = post_process_manpage ( input. to_string ( ) , Some ( "2024-01-01" ) ) ;
849+ assert_eq ! ( result, expected) ;
850+ }
851+
852+ #[ test]
853+ fn test_post_process_manpage_complex_br_before_empty ( ) {
854+ // Test multiple .br before empty line patterns
855+ let input = ".TH TEST 1\n Section 1\n .br\n \n Section 2\n .br\n \n Section 3\n " ;
856+ let expected = ".TH TEST 1 \" 2024-01-01\" \n Section 1\n \n Section 2\n \n Section 3\n " ;
857+
858+ let result = post_process_manpage ( input. to_string ( ) , Some ( "2024-01-01" ) ) ;
859+ assert_eq ! ( result, expected) ;
860+ }
861+
862+ #[ test]
863+ fn test_post_process_manpage_removes_br_after_empty_line ( ) {
864+ // Test that .br is removed when preceded by empty line (which becomes .sp)
865+ let input = ".TH TEST 1\n Some text\n \n .br\n More text\n " ;
866+ let expected = ".TH TEST 1 \" 2024-01-01\" \n Some text\n \n More text\n " ;
867+
868+ let result = post_process_manpage ( input. to_string ( ) , Some ( "2024-01-01" ) ) ;
869+ assert_eq ! ( result, expected) ;
870+ }
871+
872+ #[ test]
873+ fn test_post_process_manpage_fixes_escape_sequences ( ) {
874+ // Test that \\\\0 and \\0 are fixed to \e0 (literal backslash-zero)
875+ let input = ".TH TEST 1\n Text with \\ \\ \\ \\ 0 and \\ \\ 0 escape\n " ;
876+ let expected = ".TH TEST 1 \" 2024-01-01\" \n Text with \\ e0 and \\ e0 escape\n " ;
877+
878+ let result = post_process_manpage ( input. to_string ( ) , Some ( "2024-01-01" ) ) ;
879+ assert_eq ! ( result, expected) ;
880+ }
832881}
0 commit comments