2540 lines · 67.7 KB · Text
RawDownload
#!/usr/bin/env perl# SPDX-License-Identifier: GPL-2.0 use warnings;use strict; ## Copyright (c) 1998 Michael Zucchi, All Rights Reserved        #### Copyright (C) 2000, 1  Tim Waugh <twaugh@redhat.com>          #### Copyright (C) 2001  Simon Huggins                             #### Copyright (C) 2005-2012  Randy Dunlap                         #### Copyright (C) 2012  Dan Luedtke                               #### 								 #### #define enhancements by Armin Kuster <akuster@mvista.com>	 #### Copyright (c) 2000 MontaVista Software, Inc.			 #### Copyright (C) 2022 Tomasz Warniełło (POD) use Pod::Usage qw/pod2usage/; =head1 NAME kernel-doc - Print formatted kernel documentation to stdout =head1 SYNOPSIS  kernel-doc [-h] [-v] [-Werror] [-Wall] [-Wreturn] [-Wshort-desc[ription]] [-Wcontents-before-sections]   [ -man |     -rst [-sphinx-version VERSION] [-enable-lineno] |     -none   ]   [     -export |     -internal |     [-function NAME] ... |     [-nosymbol NAME] ...   ]   [-no-doc-sections]   [-export-file FILE] ...   FILE ... Run `kernel-doc -h` for details. =head1 DESCRIPTION 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 informationmy $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 paramsmy $type_fp_param2 = '\@(\w+->\S+)\(\)';  # Special RST handling for structs with func ptr paramsmy $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 roughmy @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-modemy @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"; # read argumentsif ($#ARGV == -1) {	pod2usage(		-message => "No arguments!\n",		-exitval => 1,		-verbose => 99,		-sections => 'SYNOPSIS',		-output => \*STDERR,	);} my $kernelversion;my ($sphinx_major, $sphinx_minor, $sphinx_patch); my $dohighlight = ""; my $verbose = 0;my $Werror = 0;my $Wreturn = 0;my $Wshort_desc = 0;my $Wcontents_before_sections = 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;} my $man_date = ('January', 'February', 'March', 'April', 'May', 'June',		'July', 'August', 'September', 'October',		'November', 'December')[$build_time[4]] .  " " . ($build_time[5]+1900); # 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, %source_map); if (defined($ENV{'KBUILD_VERBOSE'}) && $ENV{'KBUILD_VERBOSE'} =~ '1') {	$verbose = 1;} 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 systemif (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 statesuse 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 $in_doc_sect;my $leading_space; # Inline documentation stateuse 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 markupsmy $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*\w+\)\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 sectionmy $section_intro = "Introduction";my $section = $section_default;my $section_context = "Context";my $section_return = "Return"; my $undescribed = "-- undescribed --"; reset_state(); while ($ARGV[0] =~ m/^--?(.*)/) {    my $cmd = $1;    shift @ARGV;    if ($cmd eq "man") {	$output_mode = "man";	@highlights = @highlights_man;	$blankline = $blankline_man;    } elsif ($cmd eq "rst") {	$output_mode = "rst";	@highlights = @highlights_rst;	$blankline = $blankline_rst;    } elsif ($cmd eq "none") {	$output_mode = "none";    } elsif ($cmd eq "module") { # not needed for XML, inherits from calling document	$modulename = shift @ARGV;    } elsif ($cmd eq "function") { # to only output specific functions	$output_selection = OUTPUT_INCLUDE;	$function = shift @ARGV;	$function_table{$function} = 1;    } elsif ($cmd eq "nosymbol") { # Exclude specific symbols	my $symbol = shift @ARGV;	$nosymbol_table{$symbol} = 1;    } elsif ($cmd eq "export") { # only exported symbols	$output_selection = OUTPUT_EXPORTED;	%function_table = ();    } elsif ($cmd eq "internal") { # only non-exported symbols	$output_selection = OUTPUT_INTERNAL;	%function_table = ();    } elsif ($cmd eq "export-file") {	my $file = shift @ARGV;	push(@export_file_list, $file);    } elsif ($cmd eq "v") {	$verbose = 1;    } elsif ($cmd eq "Werror") {	$Werror = 1;    } elsif ($cmd eq "Wreturn") {	$Wreturn = 1;    } elsif ($cmd eq "Wshort-desc" or $cmd eq "Wshort-description") {	$Wshort_desc = 1;    } elsif ($cmd eq "Wcontents-before-sections") {	$Wcontents_before_sections = 1;    } elsif ($cmd eq "Wall") {        $Wreturn = 1;        $Wshort_desc = 1;        $Wcontents_before_sections = 1;    } elsif (($cmd eq "h") || ($cmd eq "help")) {		pod2usage(-exitval => 0, -verbose => 2);    } elsif ($cmd eq 'no-doc-sections') {	    $no_doc_sections = 1;    } elsif ($cmd eq 'enable-lineno') {	    $enable_lineno = 1;    } elsif ($cmd eq 'show-not-found') {	$show_not_found = 1;  # A no-op but don't fail    } elsif ($cmd eq "sphinx-version") {	my $ver_string = shift @ARGV;	if ($ver_string =~ m/^(\d+)(\.\d+)?(\.\d+)?/) {	    $sphinx_major = $1;	    if (defined($2)) {		$sphinx_minor = substr($2,1);	    } else {		$sphinx_minor = 0;	    }	    if (defined($3)) {		$sphinx_patch = substr($3,1)	    } else {		$sphinx_patch = 0;	    }	} else {	    die "Sphinx version should either major.minor or major.minor.patch format\n";	}    } else {	# Unknown argument	pod2usage(	    -message => "Argument unknown!\n",	    -exitval => 1,	    -verbose => 99,	    -sections => 'SYNOPSIS',	    -output => \*STDERR,	    );    }    if ($#ARGV < 0){	pod2usage(	    -message => "FILE argument missing\n",	    -exitval => 1,	    -verbose => 99,	    -sections => 'SYNOPSIS',	    -output => \*STDERR,	    );    }} # continue execution near EOF; # The C domain dialect changed on Sphinx 3. So, we need to check the# version in order to produce the right tags.sub findprog($){	foreach(split(/:/, $ENV{PATH})) {		return "$_/$_[0]" if(-x "$_/$_[0]");	}} sub get_sphinx_version(){	my $ver; 	my $cmd = "sphinx-build";	if (!findprog($cmd)) {		my $cmd = "sphinx-build3";		if (!findprog($cmd)) {			$sphinx_major = 1;			$sphinx_minor = 2;			$sphinx_patch = 0;			printf STDERR "Warning: Sphinx version not found. Using default (Sphinx version %d.%d.%d)\n",			       $sphinx_major, $sphinx_minor, $sphinx_patch;			return;		}	} 	open IN, "$cmd --version 2>&1 |";	while (<IN>) {		if (m/^\s*sphinx-build\s+([\d]+)\.([\d\.]+)(\+\/[\da-f]+)?$/) {			$sphinx_major = $1;			$sphinx_minor = $2;			$sphinx_patch = $3;			last;		}		# Sphinx 1.2.x uses a different format		if (m/^\s*Sphinx.*\s+([\d]+)\.([\d\.]+)$/) {			$sphinx_major = $1;			$sphinx_minor = $2;			$sphinx_patch = $3;			last;		}	}	close IN;} # get kernel version from envsub 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", @_;     if ($name =~ m/$type_param/) {	$name = $1;	$parameterdescs{$name} = $contents;	$sectcheck = $sectcheck . $name . " ";        $parameterdesc_start_lines{$name} = $new_start_line;        $new_start_line = 0;    } elsif ($name eq "@\.\.\.") {	$name = "...";	$parameterdescs{$name} = $contents;	$sectcheck = $sectcheck . $name . " ";        $parameterdesc_start_lines{$name} = $new_start_line;        $new_start_line = 0;    } else {	if (defined($sections{$name}) && ($sections{$name} ne "")) {	    # Only warn on user specified duplicate section names.	    if ($name ne $section_default) {		emit_warning("${file}:$.", "duplicate section name '$name'\n");	    }	    $sections{$name} .= $contents;	} else {	    $sections{$name} = $contents;	    push @sectionlist, $name;            $section_start_lines{$name} = $new_start_line;            $new_start_line = 0;	}    }} ### dump DOC: section after checking that it should go out#sub dump_doc_section {    my $file = shift;    my $name = shift;    my $contents = join "\n", @_;     if ($no_doc_sections) {

Lines 1–500 of 2540.