Commit fcdf1df2 authored by Jonathan Corbet's avatar Jonathan Corbet

Merge branch 'kerneldoc2' into docs-next

So once upon a time I set out to fix the problem reported by Tobin wherein
a literal block within a kerneldoc comment would be corrupted in
processing.  On the way, though, I got annoyed at the way I have to learn
how kernel-doc works from the beginning every time I tear into it.

As a result, seven of the following eight patches just get rid of some dead
code and reorganize the rest - mostly turning the 500-line process_file()
function into something a bit more rational.  Sphinx output is unchanged
after these are applied.  Then, at the end, there's a tweak to stop messing
with literal blocks.

If anybody was unaware that I've not done any serious Perl since the
1990's, they will certainly understand that fact now.
parents 281a7af8 38476378
#!/usr/bin/env perl #!/usr/bin/env perl
# SPDX-License-Identifier: GPL-2.0
use warnings; use warnings;
use strict; use strict;
...@@ -328,13 +329,15 @@ my $lineprefix=""; ...@@ -328,13 +329,15 @@ my $lineprefix="";
use constant { use constant {
STATE_NORMAL => 0, # normal code STATE_NORMAL => 0, # normal code
STATE_NAME => 1, # looking for function name STATE_NAME => 1, # looking for function name
STATE_FIELD => 2, # scanning field start STATE_BODY_MAYBE => 2, # body - or maybe more description
STATE_PROTO => 3, # scanning prototype STATE_BODY => 3, # the body of the comment
STATE_DOCBLOCK => 4, # documentation block STATE_PROTO => 4, # scanning prototype
STATE_INLINE => 5, # gathering documentation outside main block STATE_DOCBLOCK => 5, # documentation block
STATE_INLINE => 6, # gathering documentation outside main block
}; };
my $state; my $state;
my $in_doc_sect; my $in_doc_sect;
my $leading_space;
# Inline documentation state # Inline documentation state
use constant { use constant {
...@@ -553,10 +556,9 @@ sub output_highlight { ...@@ -553,10 +556,9 @@ sub output_highlight {
} }
if ($line eq ""){ if ($line eq ""){
if (! $output_preformatted) { if (! $output_preformatted) {
print $lineprefix, local_unescape($blankline); print $lineprefix, $blankline;
} }
} else { } else {
$line =~ s/\\\\\\/\&/g;
if ($output_mode eq "man" && substr($line, 0, 1) eq ".") { if ($output_mode eq "man" && substr($line, 0, 1) eq ".") {
print "\\&$line"; print "\\&$line";
} else { } else {
...@@ -747,17 +749,73 @@ sub output_blockhead_rst(%) { ...@@ -747,17 +749,73 @@ sub output_blockhead_rst(%) {
} }
} }
#
# Apply the RST highlights to a sub-block of text.
#
sub highlight_block($) {
# The dohighlight kludge requires the text be called $contents
my $contents = shift;
eval $dohighlight;
die $@ if $@;
return $contents;
}
#
# Regexes used only here.
#
my $sphinx_literal = '^[^.].*::$';
my $sphinx_cblock = '^\.\.\ +code-block::';
sub output_highlight_rst { sub output_highlight_rst {
my $contents = join "\n",@_; my $input = join "\n",@_;
my $output = "";
my $line; my $line;
my $in_literal = 0;
my $litprefix;
my $block = "";
# undo the evil effects of xml_escape() earlier foreach $line (split "\n",$input) {
$contents = xml_unescape($contents); #
# If we're in a literal block, see if we should drop out
eval $dohighlight; # of it. Otherwise pass the line straight through unmunged.
die $@ if $@; #
if ($in_literal) {
if (! ($line =~ /^\s*$/)) {
#
# If this is the first non-blank line in a literal
# block we need to figure out what the proper indent is.
#
if ($litprefix eq "") {
$line =~ /^(\s*)/;
$litprefix = '^' . $1;
$output .= $line . "\n";
} elsif (! ($line =~ /$litprefix/)) {
$in_literal = 0;
} else {
$output .= $line . "\n";
}
} else {
$output .= $line . "\n";
}
}
#
# Not in a literal block (or just dropped out)
#
if (! $in_literal) {
$block .= $line . "\n";
if (($line =~ /$sphinx_literal/) || ($line =~ /$sphinx_cblock/)) {
$in_literal = 1;
$litprefix = "";
$output .= highlight_block($block);
$block = ""
}
}
}
foreach $line (split "\n", $contents) { if ($block) {
$output .= highlight_block($block);
}
foreach $line (split "\n", $output) {
print $lineprefix . $line . "\n"; print $lineprefix . $line . "\n";
} }
} }
...@@ -1422,8 +1480,6 @@ sub push_parameter($$$$) { ...@@ -1422,8 +1480,6 @@ sub push_parameter($$$$) {
} }
} }
$param = xml_escape($param);
# strip spaces from $param so that it is one continuous string # strip spaces from $param so that it is one continuous string
# on @parameterlist; # on @parameterlist;
# this fixes a problem where check_sections() cannot find # this fixes a problem where check_sections() cannot find
...@@ -1748,47 +1804,6 @@ sub process_proto_type($$) { ...@@ -1748,47 +1804,6 @@ sub process_proto_type($$) {
} }
} }
# xml_escape: replace <, >, and & in the text stream;
#
# however, formatting controls that are generated internally/locally in the
# kernel-doc script are not escaped here; instead, they begin life like
# $blankline_html (4 of '\' followed by a mnemonic + ':'), then these strings
# are converted to their mnemonic-expected output, without the 4 * '\' & ':',
# just before actual output; (this is done by local_unescape())
sub xml_escape($) {
my $text = shift;
if ($output_mode eq "man") {
return $text;
}
$text =~ s/\&/\\\\\\amp;/g;
$text =~ s/\</\\\\\\lt;/g;
$text =~ s/\>/\\\\\\gt;/g;
return $text;
}
# xml_unescape: reverse the effects of xml_escape
sub xml_unescape($) {
my $text = shift;
if ($output_mode eq "man") {
return $text;
}
$text =~ s/\\\\\\amp;/\&/g;
$text =~ s/\\\\\\lt;/</g;
$text =~ s/\\\\\\gt;/>/g;
return $text;
}
# convert local escape strings to html
# local escape strings look like: '\\\\menmonic:' (that's 4 backslashes)
sub local_unescape($) {
my $text = shift;
if ($output_mode eq "man") {
return $text;
}
$text =~ s/\\\\\\\\lt:/</g;
$text =~ s/\\\\\\\\gt:/>/g;
return $text;
}
sub map_filename($) { sub map_filename($) {
my $file; my $file;
...@@ -1826,40 +1841,27 @@ sub process_export_file($) { ...@@ -1826,40 +1841,27 @@ sub process_export_file($) {
close(IN); close(IN);
} }
sub process_file($) { #
my $file; # Parsers for the various processing states.
my $identifier; #
my $func; # STATE_NORMAL: looking for the /** to begin everything.
my $descr; #
my $in_purpose = 0; sub process_normal() {
my $initial_section_counter = $section_counter;
my ($orig_file) = @_;
my $leading_space;
$file = map_filename($orig_file);
if (!open(IN,"<$file")) {
print STDERR "Error: Cannot open file $file\n";
++$errors;
return;
}
$. = 1;
$section_counter = 0;
while (<IN>) {
while (s/\\\s*$//) {
$_ .= <IN>;
}
# Replace tabs by spaces
while ($_ =~ s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e) {};
if ($state == STATE_NORMAL) {
if (/$doc_start/o) { if (/$doc_start/o) {
$state = STATE_NAME; # next line is always the function name $state = STATE_NAME; # next line is always the function name
$in_doc_sect = 0; $in_doc_sect = 0;
$declaration_start_line = $. + 1; $declaration_start_line = $. + 1;
} }
} elsif ($state == STATE_NAME) {# this line is the function name (always) }
#
# STATE_NAME: Looking for the "name - description" line
#
sub process_name($$) {
my $file = shift;
my $identifier;
my $descr;
if (/$doc_block/o) { if (/$doc_block/o) {
$state = STATE_DOCBLOCK; $state = STATE_DOCBLOCK;
$contents = ""; $contents = "";
...@@ -1877,7 +1879,7 @@ sub process_file($) { ...@@ -1877,7 +1879,7 @@ sub process_file($) {
$identifier = $1; $identifier = $1;
} }
$state = STATE_FIELD; $state = STATE_BODY;
# if there's no @param blocks need to set up default section # if there's no @param blocks need to set up default section
# here # here
$contents = ""; $contents = "";
...@@ -1889,8 +1891,8 @@ sub process_file($) { ...@@ -1889,8 +1891,8 @@ sub process_file($) {
$descr =~ s/^\s*//; $descr =~ s/^\s*//;
$descr =~ s/\s*$//; $descr =~ s/\s*$//;
$descr =~ s/\s+/ /g; $descr =~ s/\s+/ /g;
$declaration_purpose = xml_escape($descr); $declaration_purpose = $descr;
$in_purpose = 1; $state = STATE_BODY_MAYBE;
} else { } else {
$declaration_purpose = ""; $declaration_purpose = "";
} }
...@@ -1922,7 +1924,15 @@ sub process_file($) { ...@@ -1922,7 +1924,15 @@ sub process_file($) {
++$warnings; ++$warnings;
$state = STATE_NORMAL; $state = STATE_NORMAL;
} }
} elsif ($state == STATE_FIELD) { # look for head: lines, and include content }
#
# STATE_BODY and STATE_BODY_MAYBE: the bulk of a kerneldoc comment.
#
sub process_body($$) {
my $file = shift;
if (/$doc_sect/i) { # case insensitive for supported section names if (/$doc_sect/i) { # case insensitive for supported section names
$newsection = $1; $newsection = $1;
$newcontents = $2; $newcontents = $2;
...@@ -1944,12 +1954,12 @@ sub process_file($) { ...@@ -1944,12 +1954,12 @@ sub process_file($) {
print STDERR "${file}:$.: warning: contents before sections\n"; print STDERR "${file}:$.: warning: contents before sections\n";
++$warnings; ++$warnings;
} }
dump_section($file, $section, xml_escape($contents)); dump_section($file, $section, $contents);
$section = $section_default; $section = $section_default;
} }
$in_doc_sect = 1; $in_doc_sect = 1;
$in_purpose = 0; $state = STATE_BODY;
$contents = $newcontents; $contents = $newcontents;
$new_start_line = $.; $new_start_line = $.;
while (substr($contents, 0, 1) eq " ") { while (substr($contents, 0, 1) eq " ") {
...@@ -1962,7 +1972,7 @@ sub process_file($) { ...@@ -1962,7 +1972,7 @@ sub process_file($) {
$leading_space = undef; $leading_space = undef;
} elsif (/$doc_end/) { } elsif (/$doc_end/) {
if (($contents ne "") && ($contents ne "\n")) { if (($contents ne "") && ($contents ne "\n")) {
dump_section($file, $section, xml_escape($contents)); dump_section($file, $section, $contents);
$section = $section_default; $section = $section_default;
$contents = ""; $contents = "";
} }
...@@ -1975,24 +1985,23 @@ sub process_file($) { ...@@ -1975,24 +1985,23 @@ sub process_file($) {
$prototype = ""; $prototype = "";
$state = STATE_PROTO; $state = STATE_PROTO;
$brcount = 0; $brcount = 0;
# print STDERR "end of doc comment, looking for prototype\n";
} elsif (/$doc_content/) { } elsif (/$doc_content/) {
# miguel-style comment kludge, look for blank lines after # miguel-style comment kludge, look for blank lines after
# @parameter line to signify start of description # @parameter line to signify start of description
if ($1 eq "") { if ($1 eq "") {
if ($section =~ m/^@/ || $section eq $section_context) { if ($section =~ m/^@/ || $section eq $section_context) {
dump_section($file, $section, xml_escape($contents)); dump_section($file, $section, $contents);
$section = $section_default; $section = $section_default;
$contents = ""; $contents = "";
$new_start_line = $.; $new_start_line = $.;
} else { } else {
$contents .= "\n"; $contents .= "\n";
} }
$in_purpose = 0; $state = STATE_BODY;
} elsif ($in_purpose == 1) { } elsif ($state == STATE_BODY_MAYBE) {
# Continued declaration purpose # Continued declaration purpose
chomp($declaration_purpose); chomp($declaration_purpose);
$declaration_purpose .= " " . xml_escape($1); $declaration_purpose .= " " . $1;
$declaration_purpose =~ s/\s+/ /g; $declaration_purpose =~ s/\s+/ /g;
} else { } else {
my $cont = $1; my $cont = $1;
...@@ -2004,7 +2013,6 @@ sub process_file($) { ...@@ -2004,7 +2013,6 @@ sub process_file($) {
$leading_space = ""; $leading_space = "";
} }
} }
$cont =~ s/^$leading_space//; $cont =~ s/^$leading_space//;
} }
$contents .= $cont . "\n"; $contents .= $cont . "\n";
...@@ -2014,7 +2022,67 @@ sub process_file($) { ...@@ -2014,7 +2022,67 @@ sub process_file($) {
print STDERR "${file}:$.: warning: bad line: $_"; print STDERR "${file}:$.: warning: bad line: $_";
++$warnings; ++$warnings;
} }
} elsif ($state == STATE_INLINE) { # scanning for inline parameters }
#
# STATE_PROTO: reading a function/whatever prototype.
#
sub process_proto($$) {
my $file = shift;
if (/$doc_inline_oneline/) {
$section = $1;
$contents = $2;
if ($contents ne "") {
$contents .= "\n";
dump_section($file, $section, $contents);
$section = $section_default;
$contents = "";
}
} elsif (/$doc_inline_start/) {
$state = STATE_INLINE;
$inline_doc_state = STATE_INLINE_NAME;
} elsif ($decl_type eq 'function') {
process_proto_function($_, $file);
} else {
process_proto_type($_, $file);
}
}
#
# STATE_DOCBLOCK: within a DOC: block.
#
sub process_docblock($$) {
my $file = shift;
if (/$doc_end/) {
dump_doc_section($file, $section, $contents);
$section = $section_default;
$contents = "";
$function = "";
%parameterdescs = ();
%parametertypes = ();
@parameterlist = ();
%sections = ();
@sectionlist = ();
$prototype = "";
$state = STATE_NORMAL;
} elsif (/$doc_content/) {
if ( $1 eq "" ) {
$contents .= $blankline;
} else {
$contents .= $1 . "\n";
}
}
}
#
# STATE_INLINE: docbook comments within a prototype.
#
sub process_inline($$) {
my $file = shift;
# First line (state 1) needs to be a @parameter # First line (state 1) needs to be a @parameter
if ($inline_doc_state == STATE_INLINE_NAME && /$doc_inline_sect/o) { if ($inline_doc_state == STATE_INLINE_NAME && /$doc_inline_sect/o) {
$section = $1; $section = $1;
...@@ -2030,7 +2098,7 @@ sub process_file($) { ...@@ -2030,7 +2098,7 @@ sub process_file($) {
# Documentation block end */ # Documentation block end */
} elsif (/$doc_inline_end/) { } elsif (/$doc_inline_end/) {
if (($contents ne "") && ($contents ne "\n")) { if (($contents ne "") && ($contents ne "\n")) {
dump_section($file, $section, xml_escape($contents)); dump_section($file, $section, $contents);
$section = $section_default; $section = $section_default;
$contents = ""; $contents = "";
} }
...@@ -2051,52 +2119,48 @@ sub process_file($) { ...@@ -2051,52 +2119,48 @@ sub process_file($) {
++$warnings; ++$warnings;
} }
} }
} elsif ($state == STATE_PROTO) { # scanning for function '{' (end of prototype) }
if (/$doc_inline_oneline/) {
$section = $1;
$contents = $2; sub process_file($) {
if ($contents ne "") { my $file;
$contents .= "\n"; my $initial_section_counter = $section_counter;
dump_section($file, $section, xml_escape($contents)); my ($orig_file) = @_;
$section = $section_default;
$contents = ""; $file = map_filename($orig_file);
if (!open(IN,"<$file")) {
print STDERR "Error: Cannot open file $file\n";
++$errors;
return;
} }
} elsif (/$doc_inline_start/) {
$state = STATE_INLINE; $. = 1;
$inline_doc_state = STATE_INLINE_NAME;
} elsif ($decl_type eq 'function') { $section_counter = 0;
process_proto_function($_, $file); while (<IN>) {
} else { while (s/\\\s*$//) {
process_proto_type($_, $file); $_ .= <IN>;
} }
# Replace tabs by spaces
while ($_ =~ s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e) {};
# Hand this line to the appropriate state handler
if ($state == STATE_NORMAL) {
process_normal();
} elsif ($state == STATE_NAME) {
process_name($file, $_);
} elsif ($state == STATE_BODY || $state == STATE_BODY_MAYBE) {
process_body($file, $_);
} elsif ($state == STATE_INLINE) { # scanning for inline parameters
process_inline($file, $_);
} elsif ($state == STATE_PROTO) {
process_proto($file, $_);
} elsif ($state == STATE_DOCBLOCK) { } elsif ($state == STATE_DOCBLOCK) {
if (/$doc_end/) process_docblock($file, $_);
{
dump_doc_section($file, $section, xml_escape($contents));
$section = $section_default;
$contents = "";
$function = "";
%parameterdescs = ();
%parametertypes = ();
@parameterlist = ();
%sections = ();
@sectionlist = ();
$prototype = "";
$state = STATE_NORMAL;
}
elsif (/$doc_content/)
{
if ( $1 eq "" )
{
$contents .= $blankline;
}
else
{
$contents .= $1 . "\n";
}
}
} }
} }
# Make sure we got something interesting.
if ($initial_section_counter == $section_counter) { if ($initial_section_counter == $section_counter) {
if ($output_mode ne "none") { if ($output_mode ne "none") {
print STDERR "${file}:1: warning: no structured comments found\n"; print STDERR "${file}:1: warning: no structured comments found\n";
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment