Read C language source or header FILEs, extract embedded documentation comments,
and print formatted documentation to standard output.
The documentation comments are identified by the "/**" opening comment mark.
See Documentation/doc-guide/kernel-doc.rst for the documentation comment syntax.
=cut
# more perldoc at the end of the file
## init lots of data
my $errors = 0;
my $warnings = 0;
my $anon_struct_union = 0;
# match expressions used to find embedded type information
my $type_constant = '\b``([^\`]+)``\b';
my $type_constant2 = '\%([-_*\w]+)';
my $type_func = '(\w+)\(\)';
my $type_param = '\@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)';
my $type_param_ref = '([\!~\*]?)\@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)';
my $type_fp_param = '\@(\w+)\(\)'; # Special RST handling for func ptr params
my $type_fp_param2 = '\@(\w+->\S+)\(\)'; # Special RST handling for structs with func ptr params
my $type_env = '(\$\w+)';
my $type_enum = '\&(enum\s*([_\w]+))';
my $type_struct = '\&(struct\s*([_\w]+))';
my $type_typedef = '\&(typedef\s*([_\w]+))';
my $type_union = '\&(union\s*([_\w]+))';
my $type_member = '\&([_\w]+)(\.|->)([_\w]+)';
my $type_fallback = '\&([_\w]+)';
my $type_member_func = $type_member . '\(\)';
# Output conversion substitutions. # One for each output format
# these are pretty rough
my @highlights_man = (
[$type_constant, "\$1"],
[$type_constant2, "\$1"],
[$type_func, "\\\\fB\$1\\\\fP"],
[$type_enum, "\\\\fI\$1\\\\fP"],
[$type_struct, "\\\\fI\$1\\\\fP"],
[$type_typedef, "\\\\fI\$1\\\\fP"],
[$type_union, "\\\\fI\$1\\\\fP"],
[$type_param, "\\\\fI\$1\\\\fP"],
[$type_param_ref, "\\\\fI\$1\$2\\\\fP"],
[$type_member, "\\\\fI\$1\$2\$3\\\\fP"],
[$type_fallback, "\\\\fI\$1\\\\fP"]
);
my $blankline_man = "";
# rst-mode
my @highlights_rst = (
[$type_constant, "``\$1``"],
[$type_constant2, "``\$1``"],
# Note: need to escape () to avoid func matching later
[$type_member_func, "\\:c\\:type\\:`\$1\$2\$3\\\\(\\\\) <\$1>`"],
[$type_member, "\\:c\\:type\\:`\$1\$2\$3 <\$1>`"],
[$type_fp_param, "**\$1\\\\(\\\\)**"],
[$type_fp_param2, "**\$1\\\\(\\\\)**"],
[$type_func, "\$1()"],
[$type_enum, "\\:c\\:type\\:`\$1 <\$2>`"],
[$type_struct, "\\:c\\:type\\:`\$1 <\$2>`"],
[$type_typedef, "\\:c\\:type\\:`\$1 <\$2>`"],
[$type_union, "\\:c\\:type\\:`\$1 <\$2>`"],
# in rst this can refer to any type
[$type_fallback, "\\:c\\:type\\:`\$1`"],
[$type_param_ref, "**\$1\$2**"]
);
my $blankline_rst = "\n";
my $verbose = 0;
my $Werror = 0;
my $Wreturn = 0;
my $Wshort_desc = 0;
my $output_mode = "rst";
my $output_preformatted = 0;
my $no_doc_sections = 0;
my $enable_lineno = 0;
my @highlights = @highlights_rst;
my $blankline = $blankline_rst;
my $modulename = "Kernel API";
use constant {
OUTPUT_ALL => 0, # output all symbols and doc sections
OUTPUT_INCLUDE => 1, # output only specified symbols
OUTPUT_EXPORTED => 2, # output exported symbols
OUTPUT_INTERNAL => 3, # output non-exported symbols
};
my $output_selection = OUTPUT_ALL;
my $show_not_found = 0; # No longer used
my @export_file_list;
my @build_time; if (defined($ENV{'KBUILD_BUILD_TIMESTAMP'}) &&
(my $seconds = `date -d "${ENV{'KBUILD_BUILD_TIMESTAMP'}}" +%s`) ne '') {
@build_time = gmtime($seconds);
} else {
@build_time = localtime;
}
# Essentially these are globals. # They probably want to be tidied up, made more localised or something. # CAVEAT EMPTOR! Some of the others I localised may not want to be, which # could cause "use of undefined value" or other bugs.
my ($function, %function_table, %parametertypes, $declaration_purpose);
my %nosymbol_table = ();
my $declaration_start_line;
my ($type, $declaration_name, $return_type);
my ($newsection, $newcontents, $prototype, $brcount);
if (defined($ENV{'KCFLAGS'})) {
my $kcflags = "$ENV{'KCFLAGS'}";
if ($kcflags =~ /(\s|^)-Werror(\s|$)/) {
$Werror = 1;
}
}
# reading this variable is for backwards compat just in case # someone was calling it with the variable from outside the # kernel's build system if (defined($ENV{'KDOC_WERROR'})) {
$Werror = "$ENV{'KDOC_WERROR'}";
} # other environment variables are converted to command-line # arguments in cmd_checkdoc in the build system
# Generated docbook code is inserted in a template at a point where # docbook v3.1 requires a non-zero sequence of RefEntry's; see: # https://www.oasis-open.org/docbook/documentation/reference/html/refentry.html # We keep track of number of generated entries and generate a dummy # if needs be to ensure the expanded template can be postprocessed # into html.
my $section_counter = 0;
my $lineprefix="";
# Parser states
use constant {
STATE_NORMAL => 0, # normal code
STATE_NAME => 1, # looking for function name
STATE_BODY_MAYBE => 2, # body - or maybe more description
STATE_BODY => 3, # the body of the comment
STATE_BODY_WITH_BLANK_LINE => 4, # the body, which has a blank line
STATE_PROTO => 5, # scanning prototype
STATE_DOCBLOCK => 6, # documentation block
STATE_INLINE => 7, # gathering doc outside main block
};
my $state;
my $leading_space;
# Inline documentation state
use constant {
STATE_INLINE_NA => 0, # not applicable ($state != STATE_INLINE)
STATE_INLINE_NAME => 1, # looking for member name (@foo:)
STATE_INLINE_TEXT => 2, # looking for member documentation
STATE_INLINE_END => 3, # done
STATE_INLINE_ERROR => 4, # error - Comment without header was found. # Spit a warning as it's not # proper kernel-doc and ignore the rest.
};
my $inline_doc_state;
#declaration types: can be # 'function', 'struct', 'union', 'enum', 'typedef'
my $decl_type;
# Name of the kernel-doc identifier for non-DOC markups
my $identifier;
my $doc_start = '^/\*\*\s*$'; # Allow whitespace at end of comment start.
my $doc_end = '\*/';
my $doc_com = '\s*\*\s*';
my $doc_com_body = '\s*\* ?';
my $doc_decl = $doc_com . '(\w+)'; # @params and a strictly limited set of supported section names # Specifically: # Match @word: # @...: # @{section-name}: # while trying to not match literal block starts like "example::" #
my $doc_sect = $doc_com . '\s*(\@[.\w]+|\@\.\.\.|description|context|returns?|notes?|examples?)\s*:([^:].*)?$';
my $doc_content = $doc_com_body . '(.*)';
my $doc_block = $doc_com . 'DOC:\s*(.*)?';
my $doc_inline_start = '^\s*/\*\*\s*$';
my $doc_inline_sect = '\s*\*\s*(@\s*[\w][\w\.]*\s*):(.*)';
my $doc_inline_end = '^\s*\*/\s*$';
my $doc_inline_oneline = '^\s*/\*\*\s*(@[\w\s]+):\s*(.*)\s*\*/\s*$';
my $export_symbol = '^\s*EXPORT_SYMBOL(_GPL)?\s*\(\s*(\w+)\s*\)\s*;';
my $export_symbol_ns = '^\s*EXPORT_SYMBOL_NS(_GPL)?\s*\(\s*(\w+)\s*,\s*"\S+"\)\s*;';
my $function_pointer = qr{([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)};
my $attribute = qr{__attribute__\s*\(\([a-z0-9,_\*\s\(\)]*\)\)}i;
my %parameterdescs;
my %parameterdesc_start_lines;
my @parameterlist;
my %sections;
my @sectionlist;
my %section_start_lines;
my $sectcheck;
my $struct_actual;
my $contents = "";
my $new_start_line = 0;
# the canonical section names. see also $doc_sect above.
my $section_default = "Description"; # default section
my $section_intro = "Introduction";
my $section = $section_default;
my $section_context = "Context";
my $section_return = "Return";
# get kernel version from env
sub get_kernel_version() {
my $version = 'unknown kernel version';
if (defined($ENV{'KERNELVERSION'})) {
$version = $ENV{'KERNELVERSION'};
}
return $version;
}
#
sub print_lineno {
my $lineno = shift; if ($enable_lineno && defined($lineno)) {
print ".. LINENO " . $lineno . "\n";
}
}
sub emit_warning {
my $location = shift;
my $msg = shift;
print STDERR "$location: warning: $msg";
++$warnings;
} ## # dumps section contents to arrays/hashes intended for that purpose. #
sub dump_section {
my $file = shift;
my $name = shift;
my $contents = join "\n", @_;
## # output function in man
sub output_function_man(%) {
my %args = %{$_[0]};
my ($parameter, $section);
my $count;
my $func_macro = $args{'func_macro'};
my $paramcount = $#{$args{'parameterlist'}}; # -1 is empty
# # This could use some work; it's used to output the DOC: sections, and # starts by putting out the name of the doc section itself, but that tends # to duplicate a header already in the template file. #
sub output_blockhead_rst(%) {
my %args = %{$_[0]};
my ($parameter, $section);
foreach $section (@{$args{'sectionlist'}}) {
next if (defined($nosymbol_table{$section}));
# # 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 {
my $input = join "\n",@_;
my $output = "";
my $line;
my $in_literal = 0;
my $litprefix;
my $block = "";
foreach $line (split "\n",$input) { # # If we're in a literal block, see if we should drop out # of it. Otherwise pass the line straight through unmunged. # 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 = ""
}
}
}
if ($block) {
$output .= highlight_block($block);
}
# # Put our descriptive text into a container (thus an HTML <div>) to help # set the function prototypes apart. #
$lineprefix = " "; if ($paramcount >= 0) {
print ".. container:: kernelindent\n\n";
print $lineprefix . "**Parameters**\n\n";
} foreach $parameter (@{$args{'parameterlist'}}) {
my $parameter_name = $parameter;
$parameter_name =~ s/\[.*//;
$type = $args{'parametertypes'}{$parameter};
if ($type ne "") {
print $lineprefix . "``$type``\n";
} else {
print $lineprefix . "``$parameter``\n";
}
## # generic output function for all types (function, struct/union, typedef, enum); # calls the generated, variable output_ function name based on # functype and output_mode
sub output_declaration {
no strict 'refs';
my $name = shift;
my $functype = shift;
my $func = "output_${functype}_$output_mode";
## # generic output function - calls the right one based on current output mode.
sub output_blockhead {
no strict 'refs';
my $func = "output_blockhead_" . $output_mode;
&$func(@_);
$section_counter++;
}
## # takes a declaration (struct, union, enum, typedef) and # invokes the right handler. NOT called for functions.
sub dump_declaration($$) {
no strict 'refs';
my ($prototype, $file) = @_;
my $func = "dump_" . $decl_type;
&$func(@_);
}
sub dump_union($$) {
dump_struct(@_);
}
sub dump_struct($$) {
my $x = shift;
my $file = shift;
my $decl_type;
my $members;
my $type = qr{struct|union}; # For capturing struct/union definition body, i.e. "{members*}qualifiers*"
my $qualifiers = qr{$attribute|__packed|__aligned|____cacheline_aligned_in_smp|____cacheline_aligned};
my $definition_body = qr{\{(.*)\}\s*$qualifiers*};
my $struct_members = qr{($type)([^\{\};]+)\{([^\{\}]*)\}([^\{\}\;]*)\;};
if ($members) { if ($identifier ne $declaration_name) {
emit_warning("${file}:$.", "expecting prototype for $decl_type $identifier. Prototype was for $decl_type $declaration_name instead\n");
return;
}
# Split nested struct/union elements as newer ones while ($members =~ m/$struct_members/) {
my $newmember;
my $maintype = $1;
my $ids = $4;
my $content = $3; foreach my $id(split /,/, $ids) {
$newmember .= "$maintype $id; ";
$id =~ s/[:\[].*//;
$id =~ s/^\s*\**(\S+)\s*/$1/; foreach my $arg (split /;/, $content) {
next if ($arg =~ m/^\s*$/); if ($arg =~ m/^([^\(]+\(\*?\s*)([\w\.]*)(\s*\).*)/) { # pointer-to-function
my $type = $1;
my $name = $2;
my $extra = $3;
next if (!$name); if ($id =~ m/^\s*$/) { # anonymous struct/union
$newmember .= "$type$name$extra; ";
} else {
$newmember .= "$type$id.$name$extra; ";
}
} else {
my $type;
my $names;
$arg =~ s/^\s+//;
$arg =~ s/\s+$//; # Handle bitmaps
$arg =~ s/:\s*\d+\s*//g; # Handle arrays
$arg =~ s/\[.*\]//g; # The type may have multiple words, # and multiple IDs can be defined, like: # const struct foo, *bar, foobar # So, we remove spaces when parsing the # names, in order to match just names # and commas for the names
$arg =~ s/\s*,\s*/,/g; if ($arg =~ m/(.*)\s+([\S+,]+)/) {
$type = $1;
$names = $2;
} else {
$newmember .= "$arg; ";
next;
} foreach my $name (split /,/, $names) {
$name =~ s/^\s*\**(\S+)\s*/$1/;
next if (($name =~ m/^\s*$/)); if ($id =~ m/^\s*$/) { # anonymous struct/union
$newmember .= "$type $name; ";
} else {
$newmember .= "$type $id.$name; ";
}
}
}
}
}
$members =~ s/$struct_members/$newmember/;
}
# Ignore other nested elements, like enums
$members =~ s/(\{[^\{\}]*\})//g;
foreach my $arg (split ',', $members) {
$arg =~ s/^\s*(\w+).*/$1/;
push @parameterlist, $arg; if (!$parameterdescs{$arg}) {
$parameterdescs{$arg} = $undescribed; if (show_warnings("enum", $declaration_name)) {
emit_warning("${file}:$.", "Enum value '$arg' not described in enum '$declaration_name'\n");
}
}
$_members{$arg} = 1;
}
while (my ($k, $v) = each %parameterdescs) { if (!exists($_members{$k})) { if (show_warnings("enum", $declaration_name)) {
emit_warning("${file}:$.", "Excess enum value '$k' description in '$declaration_name'\n");
}
}
}
my $typedef_type = qr { ((?:\s+[\w\*]+\b){0,7}\s+(?:\w+\b|\*+))\s* }x;
my $typedef_ident = qr { \*?\s*(\w\S+)\s* }x;
my $typedef_args = qr { \s*\((.*)\); }x;
my $typedef1 = qr { typedef$typedef_type\($typedef_ident\)$typedef_args }x;
my $typedef2 = qr { typedef$typedef_type$typedef_ident$typedef_args }x;
sub dump_typedef($$) {
my $x = shift;
my $file = shift;
$x =~ s@/\*.*?\*/@@gos; # strip comments.
# Parse function typedef prototypes if ($x =~ $typedef1 || $x =~ $typedef2) {
$return_type = $1;
$declaration_name = $2;
my $args = $3;
$return_type =~ s/^\s+//;
if ($identifier ne $declaration_name) {
emit_warning("${file}:$.", "expecting prototype for typedef $identifier. Prototype was for typedef $declaration_name instead\n");
return;
}
if ($x =~ /typedef.*\s+(\w+)\s*;/) {
$declaration_name = $1;
if ($identifier ne $declaration_name) {
emit_warning("${file}:$.", "expecting prototype for typedef $identifier. Prototype was for typedef $declaration_name instead\n");
return;
}
sub push_parameter($$$$$) {
my $param = shift;
my $type = shift;
my $org_arg = shift;
my $file = shift;
my $declaration_name = shift;
if (($anon_struct_union == 1) && ($type eq "") &&
($param eq "}")) {
return; # ignore the ending }; from anon. struct/union
}
$anon_struct_union = 0;
$param =~ s/[\[\)].*//;
if ($type eq "" && $param =~ /\.\.\.$/)
{ if (!$param =~ /\w\.\.\.$/) { # handles unnamed variable parameters
$param = "...";
} elsif ($param =~ /\w\.\.\.$/) { # for named variable parameters of the form `x...`, remove the dots
$param =~ s/\.\.\.$//;
} if (!defined $parameterdescs{$param} || $parameterdescs{$param} eq "") {
$parameterdescs{$param} = "variable arguments";
}
}
elsif ($type eq "" && ($param eq "" or $param eq "void"))
{
$param="void";
$parameterdescs{void} = "no arguments";
}
elsif ($type eq "" && ($param eq "struct" or $param eq "union")) # handle unnamed (anonymous) union or struct:
{
$type = $param;
$param = "{unnamed_" . $param . "}";
$parameterdescs{$param} = "anonymous\n";
$anon_struct_union = 1;
}
elsif ($param =~ "__cacheline_group" ) # handle cache group enforcing variables: they do not need be described in header files
{
return; # ignore __cacheline_group_begin and __cacheline_group_end
}
# warn if parameter has no description # (but ignore ones starting with # as these are not parameters # but inline preprocessor statements); # Note: It will also ignore void params and unnamed structs/unions if (!defined $parameterdescs{$param} && $param !~ /^#/) {
$parameterdescs{$param} = $undescribed;
if (show_warnings($type, $declaration_name) && $param !~ /\./) {
emit_warning("${file}:$.", "Function parameter or struct member '$param' not described in '$declaration_name'\n");
}
}
# strip spaces from $param so that it is one continuous string # on @parameterlist; # this fixes a problem where check_sections() cannot find # a parameter like "addr[6 + 2]" because it actually appears # as "addr[6", "+", "2]" on the parameter list; # but it's better to maintain the param string unchanged for output, # so just weaken the string compare in check_sections() to ignore # "[blah" in a parameter string; ###$param =~ s/\s*//g;
push @parameterlist, $param;
$org_arg =~ s/\s\s+/ /g;
$parametertypes{$param} = $org_arg;
}
sub check_sections($$$$$) {
my ($file, $decl_name, $decl_type, $sectcheck, $prmscheck) = @_;
my @sects = split ' ', $sectcheck;
my @prms = split ' ', $prmscheck;
my $err;
my ($px, $sx);
my $prm_clean; # strip trailing "[array size]" and/or beginning "*"
foreach $sx (0 .. $#sects) {
$err = 1; foreach $px (0 .. $#prms) {
$prm_clean = $prms[$px];
$prm_clean =~ s/\[.*\]//;
$prm_clean =~ s/$attribute//i; # ignore array size in a parameter string; # however, the original param string may contain # spaces, e.g.: addr[6 + 2] # and this appears in @prms as "addr[6" since the # parameter list is split at spaces; # hence just ignore "[..." for the sections check;
$prm_clean =~ s/\[.*//;
##$prm_clean =~ s/^\**//; if ($prm_clean eq $sects[$sx]) {
$err = 0;
last;
}
} if ($err) { if ($decl_type eq "function") {
emit_warning("${file}:$.", "Excess function parameter " . "'$sects[$sx]' " . "description in '$decl_name'\n");
} elsif (($decl_type eq "struct") or
($decl_type eq "union")) {
emit_warning("${file}:$.", "Excess $decl_type member " . "'$sects[$sx]' " . "description in '$decl_name'\n");
}
}
}
}
## # Checks the section describing the return value of a function.
sub check_return_section {
my $file = shift;
my $declaration_name = shift;
my $return_type = shift;
# Ignore an empty return type (It's a macro) # Ignore functions with a "void" return type. (But don't ignore "void *") if (($return_type eq "") || ($return_type =~ /void\s*\w*\s*$/)) {
return;
}
if (!defined($sections{$section_return}) ||
$sections{$section_return} eq "")
{
emit_warning("${file}:$.", "No description found for return value of " . "'$declaration_name'\n");
}
}
## # takes a function prototype and the name of the current file being # processed and spits out all the details stored in the global # arrays/hashes.
sub dump_function($$) {
my $prototype = shift;
my $file = shift;
my $func_macro = 0;
# Yes, this truly is vile. We are looking for: # 1. Return type (may be nothing if we're looking at a macro) # 2. Function name # 3. Function parameters. # # All the while we have to watch out for function pointer parameters # (which IIRC is what the two sections are for), C types (these # regexps don't even start to express all the possibilities), and # so on. # # If you mess with these regexps, it's a good idea to check that # the following functions' documentation still comes out right: # - parport_register_device (function pointer parameters) # - atomic_set (macro) # - pci_match_device, __copy_to_user (long return type)
my $name = qr{[a-zA-Z0-9_~:]+};
my $prototype_end1 = qr{[^\(]*};
my $prototype_end2 = qr{[^\{]*};
my $prototype_end = qr{\(($prototype_end1|$prototype_end2)\)};
my $type1 = qr{[\w\s]+};
my $type2 = qr{$type1\*+};
if ($define && $prototype =~ m/^()($name)\s+/) { # This is an object-like macro, it has no return type and no parameter # list. # Function-like macros are not allowed to have spaces between # declaration_name and opening parenthesis (notice the \s+).
$return_type = $1;
$declaration_name = $2;
$func_macro = 1;
} elsif ($prototype =~ m/^()($name)\s*$prototype_end/ ||
$prototype =~ m/^($type1)\s+($name)\s*$prototype_end/ ||
$prototype =~ m/^($type2+)\s*($name)\s*$prototype_end/) {
$return_type = $1;
$declaration_name = $2;
my $args = $3;
if ($identifier ne $declaration_name) {
emit_warning("${file}:$.", "expecting prototype for $identifier(). Prototype was for $declaration_name() instead\n");
return;
}
# This check emits a lot of warnings at the moment, because many # functions don't have a 'Return' doc section. So until the number # of warnings goes sufficiently down, the check is only performed in # -Wreturn mode. # TODO: always perform the check. if ($Wreturn && !$func_macro) {
check_return_section($file, $declaration_name, $return_type);
}
$prototype =~ s/SYSCALL_DEFINE.*\(/long sys_/; # fix return type & func name if ($prototype =~ m/long (sys_.*?),/) {
$prototype =~ s/,/\(/;
} elsif ($void) {
$prototype =~ s/\)/\(void\)/;
}
# now delete all of the odd-number commas in $prototype # so that arg types & arg names don't have a comma between them
my $count = 0;
my $len = length($prototype); if ($void) {
$len = 0; # skip the for-loop
} for (my $ix = 0; $ix < $len; $ix++) { if (substr($prototype, $ix, 1) eq ',') {
$count++; if ($count % 2 == 1) {
substr($prototype, $ix, 1) = ' ';
}
}
}
}
sub process_proto_function($$) {
my $x = shift;
my $file = shift;
$x =~ s@\/\/.*$@@gos; # strip C99-style comments to end of line
sub process_export_file($) {
my ($orig_file) = @_;
my $file = map_filename($orig_file);
if (!open(IN,"<$file")) {
print STDERR "Error: Cannot open file $file\n";
++$errors;
return;
}
while (<IN>) { if (/$export_symbol/) {
next if (defined($nosymbol_table{$2}));
$function_table{$2} = 1;
} if (/$export_symbol_ns/) {
next if (defined($nosymbol_table{$2}));
$function_table{$2} = 1;
}
}
close(IN);
}
# # Parsers for the various processing states. # # STATE_NORMAL: looking for the /** to begin everything. #
sub process_normal() { if (/$doc_start/o) {
$state = STATE_NAME; # next line is always the function name
$declaration_start_line = $. + 1;
}
}
# # STATE_NAME: Looking for the "name - description" line #
sub process_name($$) {
my $file = shift;
my $descr;
if ( $1 eq "" ) {
$section = $section_intro;
} else {
$section = $1;
}
} elsif (/$doc_decl/o) {
$identifier = $1;
my $is_kernel_comment = 0;
my $decl_start = qr{$doc_com}; # test for pointer declaration type, foo * bar() - desc
my $fn_type = qr{\w+\s*\*\s*};
my $parenthesis = qr{\(\w*\)};
my $decl_end = qr{[-:].*}; if (/^$decl_start([\w\s]+?)$parenthesis?\s*$decl_end?$/) {
$identifier = $1;
} if ($identifier =~ m/^(struct|union|enum|typedef)\b\s*(\S*)/) {
$decl_type = $1;
$identifier = $2;
$is_kernel_comment = 1;
} # Look for foo() or static void foo() - description; or misspelt # identifier
elsif (/^$decl_start$fn_type?(\w+)\s*$parenthesis?\s*$decl_end?$/ ||
/^$decl_start$fn_type?(\w+[^-:]*)$parenthesis?\s*$decl_end$/) {
$identifier = $1;
$decl_type = 'function';
$identifier =~ s/^define\s+//;
$is_kernel_comment = 1;
}
$identifier =~ s/\s+$//;
$state = STATE_BODY; # if there's no @param blocks need to set up default section # here
$contents = "";
$section = $section_default;
$new_start_line = $. + 1; if (/[-:](.*)/) { # strip leading/trailing/multiple spaces
$descr= $1;
$descr =~ s/^\s*//;
$descr =~ s/\s*$//;
$descr =~ s/\s+/ /g;
$declaration_purpose = $descr;
$state = STATE_BODY_MAYBE;
} else {
$declaration_purpose = "";
}
if (!$is_kernel_comment) {
emit_warning("${file}:$.", "This comment starts with '/**', but isn't a kernel-doc comment. Refer Documentation/doc-guide/kernel-doc.rst\n$_");
$state = STATE_NORMAL;
}
if (($declaration_purpose eq "") && $Wshort_desc) {
emit_warning("${file}:$.", "missing initial short description on line:\n$_");
}
if ($identifier eq "" && $decl_type ne "enum") {
emit_warning("${file}:$.", "wrong kernel-doc identifier on line:\n$_");
$state = STATE_NORMAL;
}
if ($verbose) {
print STDERR "${file}:$.: info: Scanning doc for $decl_type $identifier\n";
}
--> --------------------
--> maximum size reached
--> --------------------
¤ Dauer der Verarbeitung: 0.68 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.