Skip to content
Snippets Groups Projects
kernel-doc 71.7 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	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__\s*\(\([a-z,_\*\s\(\)]*\)\)//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") {
    				print STDERR "Warning(${file}:$.): " .
    					"Excess function parameter " .
    					"'$sects[$sx]' " .
    					"description in '$decl_name'\n";
    				++$warnings;
    			} else {
    				if ($nested !~ m/\Q$sects[$sx]\E/) {
    				    print STDERR "Warning(${file}:$.): " .
    					"Excess struct/union/enum/typedef member " .
    					"'$sects[$sx]' " .
    					"description in '$decl_name'\n";
    				    ++$warnings;
    				}
    			}
    		}
    	}
    }
    
    
    ##
    # 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 "") {
                    print STDERR "Warning(${file}:$.): " .
                            "No description found for return value of " .
                            "'$declaration_name'\n";
                    ++$warnings;
            }
    }
    
    
    ##
    # 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;
    
        $prototype =~ s/^static +//;
        $prototype =~ s/^extern +//;
        $prototype =~ s/^asmlinkage +//;
        $prototype =~ s/^inline +//;
        $prototype =~ s/^__inline__ +//;
        $prototype =~ s/^__inline +//;
        $prototype =~ s/^__always_inline +//;
        $prototype =~ s/^noinline +//;
        $prototype =~ s/__init +//;
        $prototype =~ s/__init_or_module +//;
        $prototype =~ s/__must_check +//;
        $prototype =~ s/__weak +//;
        $prototype =~ s/^#\s*define\s+//; #ak added
        $prototype =~ s/__attribute__\s*\(\([a-z,]*\)\)//;
    
        # 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)
    
        if ($prototype =~ m/^()([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ ||
    	$prototype =~ m/^(\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ ||
    	$prototype =~ m/^(\w+\s*\*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ ||
    	$prototype =~ m/^(\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ ||
    	$prototype =~ m/^(\w+\s+\w+\s*\*+)\s*([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ ||
    	$prototype =~ m/^(\w+\s+\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ ||
    	$prototype =~ m/^(\w+\s+\w+\s+\w+\s*\*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ ||
    	$prototype =~ m/^()([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ ||
    	$prototype =~ m/^(\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ ||
    	$prototype =~ m/^(\w+\s*\*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ ||
    	$prototype =~ m/^(\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ ||
    	$prototype =~ m/^(\w+\s+\w+\s*\*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ ||
    	$prototype =~ m/^(\w+\s+\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ ||
    	$prototype =~ m/^(\w+\s+\w+\s+\w+\s*\*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ ||
    	$prototype =~ m/^(\w+\s+\w+\s+\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ ||
    	$prototype =~ m/^(\w+\s+\w+\s+\w+\s+\w+\s*\*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ ||
    	$prototype =~ m/^(\w+\s+\w+\s*\*\s*\w+\s*\*\s*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/)  {
    	$return_type = $1;
    	$declaration_name = $2;
    	my $args = $3;
    
    	create_parameterlist($args, ',', $file);
        } else {
    
    	print STDERR "Warning(${file}:$.): cannot understand function prototype: '$prototype'\n";
    
    	return;
        }
    
    	my $prms = join " ", @parameterlist;
    	check_sections($file, $declaration_name, "function", $sectcheck, $prms, "");
    
    
            # 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
            # verbose mode.
            # TODO: always perform the check.
            if ($verbose) {
                    check_return_section($file, $declaration_name, $return_type);
            }
    
    
        output_declaration($declaration_name,
    		       'function',
    		       {'function' => $declaration_name,
    			'module' => $modulename,
    			'functiontype' => $return_type,
    			'parameterlist' => \@parameterlist,
    			'parameterdescs' => \%parameterdescs,
    			'parametertypes' => \%parametertypes,
    			'sectionlist' => \@sectionlist,
    			'sections' => \%sections,
    			'purpose' => $declaration_purpose
    		       });
    }
    
    sub reset_state {
        $function = "";
        %constants = ();
        %parameterdescs = ();
        %parametertypes = ();
        @parameterlist = ();
        %sections = ();
        @sectionlist = ();
        $sectcheck = "";
        $struct_actual = "";
        $prototype = "";
    
        $state = 0;
    }
    
    sub tracepoint_munge($) {
    	my $file = shift;
    	my $tracepointname = 0;
    	my $tracepointargs = 0;
    
    	if ($prototype =~ m/TRACE_EVENT\((.*?),/) {
    		$tracepointname = $1;
    	}
    	if ($prototype =~ m/DEFINE_SINGLE_EVENT\((.*?),/) {
    		$tracepointname = $1;
    	}
    	if ($prototype =~ m/DEFINE_EVENT\((.*?),(.*?),/) {
    		$tracepointname = $2;
    	}
    	$tracepointname =~ s/^\s+//; #strip leading whitespace
    	if ($prototype =~ m/TP_PROTO\((.*?)\)/) {
    		$tracepointargs = $1;
    	}
    	if (($tracepointname eq 0) || ($tracepointargs eq 0)) {
    		print STDERR "Warning(${file}:$.): Unrecognized tracepoint format: \n".
    			     "$prototype\n";
    	} else {
    		$prototype = "static inline void trace_$tracepointname($tracepointargs)";
    	}
    }
    
    sub syscall_munge() {
    	my $void = 0;
    
    	$prototype =~ s@[\r\n\t]+@ @gos; # strip newlines/CR's/tabs
    ##	if ($prototype =~ m/SYSCALL_DEFINE0\s*\(\s*(a-zA-Z0-9_)*\s*\)/) {
    	if ($prototype =~ m/SYSCALL_DEFINE0/) {
    		$void = 1;
    ##		$prototype = "long sys_$1(void)";
    	}
    
    	$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_state3_function($$) {
        my $x = shift;
        my $file = shift;
    
        $x =~ s@\/\/.*$@@gos; # strip C99-style comments to end of line
    
        if ($x =~ m#\s*/\*\s+MACDOC\s*#io || ($x =~ /^#/ && $x !~ /^#\s*define/)) {
    	# do nothing
        }
        elsif ($x =~ /([^\{]*)/) {
    	$prototype .= $1;
        }
    
        if (($x =~ /\{/) || ($x =~ /\#\s*define/) || ($x =~ /;/)) {
    	$prototype =~ s@/\*.*?\*/@@gos;	# strip comments.
    	$prototype =~ s@[\r\n]+@ @gos; # strip newlines/cr's.
    	$prototype =~ s@^\s+@@gos; # strip leading spaces
    	if ($prototype =~ /SYSCALL_DEFINE/) {
    		syscall_munge();
    	}
    	if ($prototype =~ /TRACE_EVENT/ || $prototype =~ /DEFINE_EVENT/ ||
    	    $prototype =~ /DEFINE_SINGLE_EVENT/)
    	{
    		tracepoint_munge($file);
    	}
    	dump_function($prototype, $file);
    	reset_state();
        }
    }
    
    sub process_state3_type($$) {
        my $x = shift;
        my $file = shift;
    
        $x =~ s@[\r\n]+@ @gos; # strip newlines/cr's.
        $x =~ s@^\s+@@gos; # strip leading spaces
        $x =~ s@\s+$@@gos; # strip trailing spaces
        $x =~ s@\/\/.*$@@gos; # strip C99-style comments to end of line
    
        if ($x =~ /^#/) {
    	# To distinguish preprocessor directive from regular declaration later.
    	$x .= ";";
        }
    
        while (1) {
    	if ( $x =~ /([^{};]*)([{};])(.*)/ ) {
    	    $prototype .= $1 . $2;
    	    ($2 eq '{') && $brcount++;
    	    ($2 eq '}') && $brcount--;
    	    if (($2 eq ';') && ($brcount == 0)) {
    		dump_declaration($prototype, $file);
    		reset_state();
    		last;
    	    }
    	    $x = $3;
    	} else {
    	    $prototype .= $x;
    	    last;
    	}
        }
    }
    
    # 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 "text") || ($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 "text") || ($output_mode eq "man")) {
    		return $text;
    	}
    	$text =~ s/\\\\\\\\lt:/</g;
    	$text =~ s/\\\\\\\\gt:/>/g;
    	return $text;
    }
    
    sub process_file($) {
        my $file;
        my $identifier;
        my $func;
        my $descr;
        my $in_purpose = 0;
        my $initial_section_counter = $section_counter;
    
        if (defined($ENV{'SRCTREE'})) {
    	$file = "$ENV{'SRCTREE'}" . "/" . "@_";
        }
        else {
    	$file = "@_";
        }
        if (defined($source_map{$file})) {
    	$file = $source_map{$file};
        }
    
        if (!open(IN,"<$file")) {
    	print STDERR "Error: Cannot open file $file\n";
    	++$errors;
    	return;
        }
    
        $. = 1;
    
        $section_counter = 0;
        while (<IN>) {
    
    	if ($state == 0) {
    	    if (/$doc_start/o) {
    		$state = 1;		# next line is always the function name
    		$in_doc_sect = 0;
    	    }
    	} elsif ($state == 1) {	# this line is the function name (always)
    	    if (/$doc_block/o) {
    		$state = 4;
    		$contents = "";
    		if ( $1 eq "" ) {
    			$section = $section_intro;
    		} else {
    			$section = $1;
    		}
    	    }
    	    elsif (/$doc_decl/o) {
    		$identifier = $1;
    		if (/\s*([\w\s]+?)\s*-/) {
    		    $identifier = $1;
    		}
    
    		$state = 2;
    		if (/-(.*)/) {
    		    # strip leading/trailing/multiple spaces
    		    $descr= $1;
    		    $descr =~ s/^\s*//;
    		    $descr =~ s/\s*$//;
    
    		    $declaration_purpose = xml_escape($descr);
    		    $in_purpose = 1;
    		} else {
    		    $declaration_purpose = "";
    		}
    
    		if (($declaration_purpose eq "") && $verbose) {
    			print STDERR "Warning(${file}:$.): missing initial short description on line:\n";
    			print STDERR $_;
    			++$warnings;
    		}
    
    		if ($identifier =~ m/^struct/) {
    		    $decl_type = 'struct';
    		} elsif ($identifier =~ m/^union/) {
    		    $decl_type = 'union';
    		} elsif ($identifier =~ m/^enum/) {
    		    $decl_type = 'enum';
    		} elsif ($identifier =~ m/^typedef/) {
    		    $decl_type = 'typedef';
    		} else {
    		    $decl_type = 'function';
    		}
    
    		if ($verbose) {
    		    print STDERR "Info(${file}:$.): Scanning doc for $identifier\n";
    		}
    	    } else {
    		print STDERR "Warning(${file}:$.): Cannot understand $_ on line $.",
    		" - I thought it was a doc line\n";
    		++$warnings;
    		$state = 0;
    	    }
    	} elsif ($state == 2) {	# look for head: lines, and include content
    	    if (/$doc_sect/o) {
    		$newsection = $1;
    		$newcontents = $2;
    
    		if (($contents ne "") && ($contents ne "\n")) {
    		    if (!$in_doc_sect && $verbose) {
    			print STDERR "Warning(${file}:$.): contents before sections\n";
    			++$warnings;
    		    }
    		    dump_section($file, $section, xml_escape($contents));
    		    $section = $section_default;
    		}
    
    		$in_doc_sect = 1;
    		$in_purpose = 0;
    		$contents = $newcontents;
    		if ($contents ne "") {
    		    while ((substr($contents, 0, 1) eq " ") ||
    			substr($contents, 0, 1) eq "\t") {
    			    $contents = substr($contents, 1);
    		    }
    		    $contents .= "\n";
    		}
    		$section = $newsection;
    	    } elsif (/$doc_end/) {
    
    		if (($contents ne "") && ($contents ne "\n")) {
    		    dump_section($file, $section, xml_escape($contents));
    		    $section = $section_default;
    		    $contents = "";
    		}
    		# look for doc_com + <text> + doc_end:
    		if ($_ =~ m'\s*\*\s*[a-zA-Z_0-9:\.]+\*/') {
    		    print STDERR "Warning(${file}:$.): suspicious ending line: $_";
    		    ++$warnings;
    		}
    
    		$prototype = "";
    		$state = 3;
    		$brcount = 0;
    #		print STDERR "end of doc comment, looking for prototype\n";
    	    } elsif (/$doc_content/) {
    		# miguel-style comment kludge, look for blank lines after
    		# @parameter line to signify start of description
    		if ($1 eq "") {
    		    if ($section =~ m/^@/ || $section eq $section_context) {
    			dump_section($file, $section, xml_escape($contents));
    			$section = $section_default;
    			$contents = "";
    		    } else {
    			$contents .= "\n";
    		    }
    		    $in_purpose = 0;
    		} elsif ($in_purpose == 1) {
    		    # Continued declaration purpose
    		    chomp($declaration_purpose);
    		    $declaration_purpose .= " " . xml_escape($1);
    
    		    $declaration_purpose =~ s/\s+/ /g;
    
    		} else {
    		    $contents .= $1 . "\n";
    		}
    	    } else {
    		# i dont know - bad line?  ignore.
    		print STDERR "Warning(${file}:$.): bad line: $_";
    		++$warnings;
    	    }
    	} elsif ($state == 3) {	# scanning for function '{' (end of prototype)
    	    if ($decl_type eq 'function') {
    		process_state3_function($_, $file);
    	    } else {
    		process_state3_type($_, $file);
    	    }
    	} elsif ($state == 4) {
    		# Documentation block
    		if (/$doc_block/) {
    			dump_doc_section($file, $section, xml_escape($contents));
    			$contents = "";
    			$function = "";
    			%constants = ();
    			%parameterdescs = ();
    			%parametertypes = ();
    			@parameterlist = ();
    			%sections = ();
    			@sectionlist = ();
    			$prototype = "";
    			if ( $1 eq "" ) {
    				$section = $section_intro;
    			} else {
    				$section = $1;
    			}
    		}
    		elsif (/$doc_end/)
    		{
    			dump_doc_section($file, $section, xml_escape($contents));
    			$contents = "";
    			$function = "";
    			%constants = ();
    			%parameterdescs = ();
    			%parametertypes = ();
    			@parameterlist = ();
    			%sections = ();
    			@sectionlist = ();
    			$prototype = "";
    			$state = 0;
    		}
    		elsif (/$doc_content/)
    		{
    			if ( $1 eq "" )
    			{
    				$contents .= $blankline;
    			}
    			else
    			{
    				$contents .= $1 . "\n";
    			}
    		}
    	}
        }
        if ($initial_section_counter == $section_counter) {
    	print STDERR "Warning(${file}): no structured comments found\n";
    
    	if (($function_only == 1) && ($show_not_found == 1)) {
    	    print STDERR "    Was looking for '$_'.\n" for keys %function_table;
    	}
    
    	if ($output_mode eq "xml") {
    	    # The template wants at least one RefEntry here; make one.
    	    print "<refentry>\n";
    	    print " <refnamediv>\n";
    	    print "  <refname>\n";
    	    print "   ${file}\n";
    	    print "  </refname>\n";
    	    print "  <refpurpose>\n";
    	    print "   Document generation inconsistency\n";
    	    print "  </refpurpose>\n";
    	    print " </refnamediv>\n";
    	    print " <refsect1>\n";
    	    print "  <title>\n";
    	    print "   Oops\n";
    	    print "  </title>\n";
    	    print "  <warning>\n";
    	    print "   <para>\n";
    	    print "    The template for this document tried to insert\n";
    	    print "    the structured comment from the file\n";
    	    print "    <filename>${file}</filename> at this point,\n";
    	    print "    but none was found.\n";
    	    print "    This dummy section is inserted to allow\n";
    	    print "    generation to continue.\n";
    	    print "   </para>\n";
    	    print "  </warning>\n";
    	    print " </refsect1>\n";
    	    print "</refentry>\n";
    	}
        }
    }
    
    
    $kernelversion = get_kernel_version();
    
    # generate a sequence of code that will splice in highlighting information
    # using the s// operator.
    foreach my $pattern (keys %highlights) {
    #   print STDERR "scanning pattern:$pattern, highlight:($highlights{$pattern})\n";
        $dohighlight .=  "\$contents =~ s:$pattern:$highlights{$pattern}:gs;\n";
    }
    
    # Read the file that maps relative names to absolute names for
    # separate source and object directories and for shadow trees.
    if (open(SOURCE_MAP, "<.tmp_filelist.txt")) {
    	my ($relname, $absname);
    	while(<SOURCE_MAP>) {
    		chop();
    		($relname, $absname) = (split())[0..1];
    		$relname =~ s:^/+::;
    		$source_map{$relname} = $absname;
    	}
    	close(SOURCE_MAP);
    }
    
    foreach (@ARGV) {
        chomp;
        process_file($_);
    }
    if ($verbose && $errors) {
      print STDERR "$errors errors\n";
    }
    if ($verbose && $warnings) {
      print STDERR "$warnings warnings\n";
    }
    
    exit($errors);