From a390f5716e66a67d4f8f464d5e4c331f18cec521 Mon Sep 17 00:00:00 2001 From: krizej Date: Tue, 8 Apr 2025 20:16:43 +0200 Subject: [PATCH] docs: improve man page generation --- build-scripts/wikiheaders.pl | 77 +++++++++++++++++++++++++++++------- 1 file changed, 62 insertions(+), 15 deletions(-) diff --git a/build-scripts/wikiheaders.pl b/build-scripts/wikiheaders.pl index 98591493b..019304e9b 100755 --- a/build-scripts/wikiheaders.pl +++ b/build-scripts/wikiheaders.pl @@ -424,7 +424,10 @@ sub dewikify_chunk { $str .= "\n```$codelang\n$code\n```\n"; } } elsif ($dewikify_mode eq 'manpage') { - $str =~ s/\./\\[char46]/gms; # make sure these can't become control codes. + # make sure these can't become part of roff syntax. + $str =~ s/\./\\[char46]/gms; + $str =~ s/"/\\(dq/gms; + if ($wikitype eq 'mediawiki') { # Dump obvious wikilinks. if (defined $apiprefixregex) { @@ -449,33 +452,52 @@ sub dewikify_chunk { # bullets $str =~ s/^\* /\n\\\(bu /gm; } elsif ($wikitype eq 'md') { + # bullets + $str =~ s/^\- /\n\\(bu /gm; + # merge paragraphs + $str =~ s/^[ \t]+//gm; + $str =~ s/([^\-\n])\n([^\-\n])/$1 $2/g; + $str =~ s/\n\n/\n.PP\n/g; + # Dump obvious wikilinks. if (defined $apiprefixregex) { - $str =~ s/\[(\`?$apiprefixregex[a-zA-Z0-9_]+\`?)\]\($apiprefixregex[a-zA-Z0-9_]+\)/\n.BR $1\n/gms; + my $apr = $apiprefixregex; + if(!($apr =~ /\A\(.*\)\Z/s)) { + # we're relying on the apiprefixregex having a capturing group. + $apr = "(" . $apr . ")"; + } + $str =~ s/(\S*?)\[\`?($apr[a-zA-Z0-9_]+)\`?\]\($apr[a-zA-Z0-9_]+\)(\S*)\s*/\n.BR "" "$1" "$2" "$5"\n/gm; + # handle cases like "[x](x), [y](y), [z](z)" being separated. + while($str =~ s/(\.BR[^\n]*)\n\n\.BR/$1\n.BR/gm) {} } # links $str =~ s/\[(.*?)]\((https?\:\/\/.*?)\)/\n.URL "$2" "$1"\n/g; # is also popular. :/ - $str =~ s/\s*\`(.*?)\`\s*/\n.BR $1\n/gms; + $str =~ s/\s*(\S*?)\`([^\n]*?)\`(\S*)\s*/\n.BR "" "$1" "$2" "$3"\n/gms; # bold+italic (this looks bad, just make it bold). - $str =~ s/\s*\*\*\*(.*?)\*\*\*\s*/\n.B $1\n/gms; + $str =~ s/\s*(\S*?)\*\*\*([^\n]*?)\*\*\*(\S*)\s*/\n.BR "" "$1" "$2" "$3"\n/gms; # bold - $str =~ s/\s*\*\*(.*?)\*\*\s*/\n.B $1\n/gms; + $str =~ s/\s*(\S*?)\*\*([^\n]*?)\*\*(\S*)\s*/\n.BR "" "$1" "$2" "$3"\n/gms; # italic - $str =~ s/\s*\*(.*?)\*\s*/\n.I $1\n/gms; - - # bullets - $str =~ s/^\- /\n\\\(bu /gm; + $str =~ s/\s*(\S*?)\*([^\n]*?)\*(\S*)\s*/\n.IR "" "$1" "$2" "$3"\n/gms; } + # cleanup unnecessary quotes + $str =~ s/(\.[IB]R?)(.*?) ""\n/$1$2\n/gm; + $str =~ s/(\.[IB]R?) "" ""(.*?)\n/$1$2\n/gm; + $str =~ s/"(\S+)"/$1/gm; + # cleanup unnecessary whitespace + $str =~ s/ +\n/\n/gm; + if (defined $code) { $code =~ s/\A\n+//gms; $code =~ s/\n+\Z//gms; + $code =~ s/\\/\\(rs/gms; if ($dewikify_manpage_code_indent) { $str .= "\n.IP\n" } else { @@ -580,7 +602,7 @@ sub dewikify { $retval .= dewikify_chunk($wikitype, $1, $2, $3); } } elsif ($wikitype eq 'md') { - while ($str =~ s/\A(.*?)\n```(.*?)\n(.*?)\n```\n//ms) { + while ($str =~ s/\A(.*?)\n?```(.*?)\n(.*?)\n```\n//ms) { $retval .= dewikify_chunk($wikitype, $1, $2, $3); } } @@ -2765,7 +2787,6 @@ __EOF__ my $wikitype = $wikitypes{$sym}; my $sectionsref = $wikisyms{$sym}; my $remarks = $sectionsref->{'Remarks'}; - my $params = $sectionsref->{'Function Parameters'}; my $returns = $sectionsref->{'Return Value'}; my $version = $sectionsref->{'Version'}; my $threadsafety = $sectionsref->{'Thread Safety'}; @@ -2773,6 +2794,23 @@ __EOF__ my $examples = $sectionsref->{'Code Examples'}; my $deprecated = $sectionsref->{'Deprecated'}; my $headerfile = $manpageheaderfiletext; + + my $params = undef; + + if ($symtype == -1) { # category documentation block. + # nothing to be done here. + } elsif (($symtype == 1) || (($symtype == 5))) { # we'll assume a typedef (5) with a \param is a function pointer typedef. + $params = $sectionsref->{'Function Parameters'}; + } elsif ($symtype == 2) { + $params = $sectionsref->{'Macro Parameters'}; + } elsif ($symtype == 3) { + $params = $sectionsref->{'Fields'}; + } elsif ($symtype == 4) { + $params = $sectionsref->{'Values'}; + } else { + die("Unexpected symtype $symtype"); + } + $headerfile =~ s/\%fname\%/$headersymslocation{$sym}/g; $headerfile .= "\n"; @@ -2839,18 +2877,22 @@ __EOF__ $str .= dewikify($wikitype, $deprecated) . "\n"; } + my $incfile = $mainincludefname; if (defined $headerfile) { - $str .= ".SH HEADER FILE\n"; - $str .= dewikify($wikitype, $headerfile) . "\n"; + if($headerfile =~ /Defined in (.*)/) { + $incfile = $1; + } } $str .= ".SH SYNOPSIS\n"; $str .= ".nf\n"; - $str .= ".B #include \\(dq$mainincludefname\\(dq\n"; + $str .= ".B #include <$incfile>\n"; $str .= ".PP\n"; my @decllines = split /\n/, $decl; foreach (@decllines) { + $_ =~ s/\\/\\(rs/g; # fix multiline macro defs + $_ =~ s/"/\\(dq/g; $str .= ".BI \"$_\n"; } $str .= ".fi\n"; @@ -2938,8 +2980,11 @@ __EOF__ } if (defined $returns) { + # Chop datatype in parentheses off the front. + if(!($returns =~ s/\A\([^\[]*\[[^\]]*\]\([^\)]*\)[^\)]*\) //ms)) { + $returns =~ s/\A\([^\)]*\) //ms; + } $returns = dewikify($wikitype, $returns); - $returns =~ s/\A\(.*?\)\s*//; # Chop datatype in parentheses off the front. $str .= ".SH RETURN VALUE\n"; $str .= "$returns\n"; } @@ -2975,6 +3020,8 @@ __EOF__ s/\A\/*//; s/\A\.BR\s+//; # dewikify added this, but we want to handle it. s/\A\.I\s+//; # dewikify added this, but we want to handle it. + s/\A\.PP\s*//; # dewikify added this, but we want to handle it. + s/\\\(bu//; # dewikify added this, but we want to handle it. s/\A\s*[\:\*\-]\s*//; s/\A\s+//; s/\s+\Z//;