Skip to content
Snippets Groups Projects
kernel-doc 72.1 KiB
Newer Older
  • Learn to ignore specific revisions
  • #!/usr/bin/perl -w
    
    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.			 ##
    ## 								 ##
    ## This software falls under the GNU General Public License.     ##
    ## Please read the COPYING file for more information             ##
    
    # 18/01/2001 - 	Cleanups
    # 		Functions prototyped as foo(void) same as foo()
    # 		Stop eval'ing where we don't need to.
    # -- huggie@earth.li
    
    # 27/06/2001 -  Allowed whitespace after initial "/**" and
    #               allowed comments before function declarations.
    # -- Christian Kreibich <ck@whoop.org>
    
    # Still to do:
    # 	- add perldoc documentation
    # 	- Look more closely at some of the scarier bits :)
    
    # 26/05/2001 - 	Support for separate source and object trees.
    #		Return error code.
    # 		Keith Owens <kaos@ocs.com.au>
    
    # 23/09/2001 - Added support for typedefs, structs, enums and unions
    #              Support for Context section; can be terminated using empty line
    #              Small fixes (like spaces vs. \s in regex)
    # -- Tim Jansen <tim@tjansen.de>
    
    # 25/07/2012 - Added support for HTML5
    # -- Dan Luedtke <mail@danrl.de>
    
    #
    # This will read a 'c' file and scan for embedded comments in the
    # style of gnome comments (+minor extensions - see below).
    #
    
    # Note: This only supports 'c'.
    
    # usage:
    # kernel-doc [ -docbook | -html | -html5 | -text | -man | -list ]
    #            [ -no-doc-sections ]
    #            [ -function funcname [ -function funcname ...] ]
    #            c file(s)s > outputfile
    # or
    #            [ -nofunction funcname [ -function funcname ...] ]
    #            c file(s)s > outputfile
    #
    #  Set output format using one of -docbook -html -html5 -text or -man.
    #  Default is man.
    #  The -list format is for internal use by docproc.
    #
    #  -no-doc-sections
    #	Do not output DOC: sections
    #
    #  -function funcname
    #	If set, then only generate documentation for the given function(s) or
    #	DOC: section titles.  All other functions and DOC: sections are ignored.
    #
    #  -nofunction funcname
    #	If set, then only generate documentation for the other function(s)/DOC:
    #	sections. Cannot be used together with -function (yes, that's a bug --
    #	perl hackers can fix it 8))
    #
    #  c files - list of 'c' files to process
    #
    #  All output goes to stdout, with errors to stderr.
    
    #
    # format of comments.
    # In the following table, (...)? signifies optional structure.
    #                         (...)* signifies 0 or more structure elements
    # /**
    #  * function_name(:)? (- short description)?
    # (* @parameterx: (description of parameter x)?)*
    # (* a blank line)?
    #  * (Description:)? (Description of function)?
    #  * (section header: (section description)? )*
    #  (*)?*/
    #
    # So .. the trivial example would be:
    #
    # /**
    #  * my_function
    #  */
    #
    # If the Description: header tag is omitted, then there must be a blank line
    # after the last parameter specification.
    # e.g.
    # /**
    #  * my_function - does my stuff
    #  * @my_arg: its mine damnit
    #  *
    #  * Does my stuff explained.
    #  */
    #
    #  or, could also use:
    # /**
    #  * my_function - does my stuff
    #  * @my_arg: its mine damnit
    #  * Description: Does my stuff explained.
    #  */
    # etc.
    #
    # Besides functions you can also write documentation for structs, unions,
    # enums and typedefs. Instead of the function name you must write the name
    # of the declaration;  the struct/union/enum/typedef must always precede
    # the name. Nesting of declarations is not supported.
    # Use the argument mechanism to document members or constants.
    # e.g.
    # /**
    #  * struct my_struct - short description
    #  * @a: first member
    #  * @b: second member
    #  *
    #  * Longer description
    #  */
    # struct my_struct {
    #     int a;
    #     int b;
    # /* private: */
    #     int c;
    # };
    #
    # All descriptions can be multiline, except the short function description.
    #
    # You can also add additional sections. When documenting kernel functions you
    # should document the "Context:" of the function, e.g. whether the functions
    # can be called form interrupts. Unlike other sections you can end it with an
    # empty line.
    
    # A non-void function should have a "Return:" section describing the return
    # value(s).
    
    # Example-sections should contain the string EXAMPLE so that they are marked
    # appropriately in DocBook.
    #
    # Example:
    # /**
    #  * user_function - function that can only be called in user context
    #  * @a: some argument
    #  * Context: !in_interrupt()
    #  *
    #  * Some description
    #  * Example:
    #  *    user_function(22);
    #  */
    # ...
    #
    #
    # All descriptive text is further processed, scanning for the following special
    # patterns, which are highlighted appropriately.
    #
    # 'funcname()' - function
    # '$ENVVAR' - environmental variable
    # '&struct_name' - name of a structure (up to two words including 'struct')
    # '@parameter' - name of a parameter
    # '%CONST' - name of a constant.
    
    ## 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 = '\%([-_\w]+)';
    my $type_func = '(\w+)\(\)';
    my $type_param = '\@(\w+)';
    my $type_struct = '\&((struct\s*)*[_\w]+)';
    my $type_struct_xml = '\\&amp;((struct\s*)*[_\w]+)';
    my $type_env = '(\$\w+)';
    
    # Output conversion substitutions.
    #  One for each output format
    
    # these work fairly well
    my %highlights_html = ( $type_constant, "<i>\$1</i>",
    			$type_func, "<b>\$1</b>",
    			$type_struct_xml, "<i>\$1</i>",
    			$type_env, "<b><i>\$1</i></b>",
    			$type_param, "<tt><b>\$1</b></tt>" );
    my $local_lt = "\\\\\\\\lt:";
    my $local_gt = "\\\\\\\\gt:";
    my $blankline_html = $local_lt . "p" . $local_gt;	# was "<p>"
    
    # html version 5
    my %highlights_html5 = ( $type_constant, "<span class=\"const\">\$1</span>",
    			$type_func, "<span class=\"func\">\$1</span>",
    			$type_struct_xml, "<span class=\"struct\">\$1</span>",
    			$type_env, "<span class=\"env\">\$1</span>",
    			$type_param, "<span class=\"param\">\$1</span>" );
    my $blankline_html5 = $local_lt . "br /" . $local_gt;
    
    # XML, docbook format
    my %highlights_xml = ( "([^=])\\\"([^\\\"<]+)\\\"", "\$1<quote>\$2</quote>",
    			$type_constant, "<constant>\$1</constant>",
    			$type_func, "<function>\$1</function>",
    			$type_struct_xml, "<structname>\$1</structname>",
    			$type_env, "<envar>\$1</envar>",
    			$type_param, "<parameter>\$1</parameter>" );
    my $blankline_xml = $local_lt . "/para" . $local_gt . $local_lt . "para" . $local_gt . "\n";
    
    # gnome, docbook format
    my %highlights_gnome = ( $type_constant, "<replaceable class=\"option\">\$1</replaceable>",
    			 $type_func, "<function>\$1</function>",
    			 $type_struct, "<structname>\$1</structname>",
    			 $type_env, "<envar>\$1</envar>",
    			 $type_param, "<parameter>\$1</parameter>" );
    my $blankline_gnome = "</para><para>\n";
    
    # these are pretty rough
    my %highlights_man = ( $type_constant, "\$1",
    		       $type_func, "\\\\fB\$1\\\\fP",
    		       $type_struct, "\\\\fI\$1\\\\fP",
    		       $type_param, "\\\\fI\$1\\\\fP" );
    my $blankline_man = "";
    
    # text-mode
    my %highlights_text = ( $type_constant, "\$1",
    			$type_func, "\$1",
    			$type_struct, "\$1",
    			$type_param, "\$1" );
    my $blankline_text = "";
    
    # list mode
    my %highlights_list = ( $type_constant, "\$1",
    			$type_func, "\$1",
    			$type_struct, "\$1",
    			$type_param, "\$1" );
    my $blankline_list = "";
    
    # read arguments
    if ($#ARGV == -1) {
        usage();
    }
    
    my $kernelversion;
    my $dohighlight = "";
    
    my $verbose = 0;
    my $output_mode = "man";
    
    my $output_preformatted = 0;
    
    my $no_doc_sections = 0;
    my %highlights = %highlights_man;
    my $blankline = $blankline_man;
    my $modulename = "Bootloader API";
    my $function_only = 0;
    my $man_date = ('January', 'February', 'March', 'April', 'May', 'June',
    		'July', 'August', 'September', 'October',
    		'November', 'December')[(localtime)[4]] .
      " " . ((localtime)[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 ($type, $declaration_name, $return_type);
    my ($newsection, $newcontents, $prototype, $brcount, %source_map);
    
    if (defined($ENV{'KBUILD_VERBOSE'})) {
    	$verbose = "$ENV{'KBUILD_VERBOSE'}";
    }
    
    # Generated docbook code is inserted in a template at a point where
    # docbook v3.1 requires a non-zero sequence of RefEntry's; see:
    # http://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="";
    
    # states
    # 0 - normal code
    # 1 - looking for function name
    # 2 - scanning field start.
    # 3 - scanning prototype.
    # 4 - documentation block
    my $state;
    my $in_doc_sect;
    
    #declaration types: can be
    # 'function', 'struct', 'union', 'enum', 'typedef'
    my $decl_type;
    
    my $doc_special = "\@\%\$\&";
    
    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+)';
    my $doc_sect = $doc_com . '([' . $doc_special . ']?[\w\s]+):(.*)';
    
    my $doc_content = $doc_com_body . '(.*)';
    
    my $doc_block = $doc_com . 'DOC:\s*(.*)?';
    
    my %constants;
    my %parameterdescs;
    my @parameterlist;
    my %sections;
    my @sectionlist;
    my $sectcheck;
    my $struct_actual;
    
    my $contents = "";
    my $section_default = "Description";	# default section
    my $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 = shift @ARGV;
        if ($cmd eq "-html") {
    	$output_mode = "html";
    	%highlights = %highlights_html;
    	$blankline = $blankline_html;
        } elsif ($cmd eq "-html5") {
    	$output_mode = "html5";
    	%highlights = %highlights_html5;
    	$blankline = $blankline_html5;
        } elsif ($cmd eq "-man") {
    	$output_mode = "man";
    	%highlights = %highlights_man;
    	$blankline = $blankline_man;
        } elsif ($cmd eq "-text") {
    	$output_mode = "text";
    	%highlights = %highlights_text;
    	$blankline = $blankline_text;
        } elsif ($cmd eq "-docbook") {
    	$output_mode = "xml";
    	%highlights = %highlights_xml;
    	$blankline = $blankline_xml;
        } elsif ($cmd eq "-list") {
    	$output_mode = "list";
    	%highlights = %highlights_list;
    	$blankline = $blankline_list;
        } elsif ($cmd eq "-gnome") {
    	$output_mode = "gnome";
    	%highlights = %highlights_gnome;
    	$blankline = $blankline_gnome;
        } elsif ($cmd eq "-module") { # not needed for XML, inherits from calling document
    	$modulename = shift @ARGV;
        } elsif ($cmd eq "-function") { # to only output specific functions
    	$function_only = 1;
    	$function = shift @ARGV;
    	$function_table{$function} = 1;
        } elsif ($cmd eq "-nofunction") { # to only output specific functions
    	$function_only = 2;
    	$function = shift @ARGV;
    	$function_table{$function} = 1;
        } elsif ($cmd eq "-v") {
    	$verbose = 1;
        } elsif (($cmd eq "-h") || ($cmd eq "--help")) {
    	usage();
        } elsif ($cmd eq '-no-doc-sections') {
    	    $no_doc_sections = 1;
    
        } elsif ($cmd eq '-show-not-found') {
    	$show_not_found = 1;
    
        }
    }
    
    # continue execution near EOF;
    
    sub usage {
        print "Usage: $0 [ -docbook | -html | -html5 | -text | -man | -list ]\n";
        print "         [ -no-doc-sections ]\n";
        print "         [ -function funcname [ -function funcname ...] ]\n";
        print "         [ -nofunction funcname [ -nofunction funcname ...] ]\n";
        print "         [ -v ]\n";
        print "         c source file(s) > outputfile\n";
        print "         -v : verbose output, more warnings & other info listed\n";
        exit 1;
    }
    
    # get kernel version from env
    sub get_kernel_version() {
        my $version = 'unknown kernel version';
    
    
        if (defined($ENV{'UBOOTVERSION'})) {
    	$version = $ENV{'UBOOTVERSION'};
    
        }
        return $version;
    }
    
    ##
    # 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_constant/) {
    	$name = $1;
    #	print STDERR "constant section '$1' = '$contents'\n";
    	$constants{$name} = $contents;
        } elsif ($name =~ m/$type_param/) {
    #	print STDERR "parameter def '$1' = '$contents'\n";
    	$name = $1;
    	$parameterdescs{$name} = $contents;
    	$sectcheck = $sectcheck . $name . " ";
        } elsif ($name eq "@\.\.\.") {
    #	print STDERR "parameter def '...' = '$contents'\n";
    	$name = "...";
    	$parameterdescs{$name} = $contents;
    	$sectcheck = $sectcheck . $name . " ";
        } else {
    #	print STDERR "other section '$name' = '$contents'\n";
    	if (defined($sections{$name}) && ($sections{$name} ne "")) {
    		print STDERR "Error(${file}:$.): duplicate section name '$name'\n";
    		++$errors;
    	}
    	$sections{$name} = $contents;
    	push @sectionlist, $name;
        }
    }
    
    ##
    # 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) {
    
        }
    
        if (($function_only == 0) ||
    	( $function_only == 1 && defined($function_table{$name})) ||
    	( $function_only == 2 && !defined($function_table{$name})))
        {
    	dump_section($file, $name, $contents);
    	output_blockhead({'sectionlist' => \@sectionlist,
    			  'sections' => \%sections,
    			  'module' => $modulename,
    			  'content-only' => ($function_only != 0), });
        }
    }
    
    ##
    # output function
    #
    # parameterdescs, a hash.
    #  function => "function name"
    #  parameterlist => @list of parameters
    #  parameterdescs => %parameter descriptions
    #  sectionlist => @list of sections
    #  sections => %section descriptions
    #
    
    sub output_highlight {
        my $contents = join "\n",@_;
        my $line;
    
    #   DEBUG
    #   if (!defined $contents) {
    #	use Carp;
    #	confess "output_highlight got called with no args?\n";
    #   }
    
        if ($output_mode eq "html" || $output_mode eq "html5" ||
    	$output_mode eq "xml") {
    	$contents = local_unescape($contents);
    	# convert data read & converted thru xml_escape() into &xyz; format:
    	$contents =~ s/\\\\\\/\&/g;
        }
    #   print STDERR "contents b4:$contents\n";
        eval $dohighlight;
        die $@ if $@;
    #   print STDERR "contents af:$contents\n";
    
    #   strip whitespaces when generating html5
        if ($output_mode eq "html5") {
    	$contents =~ s/^\s+//;
    	$contents =~ s/\s+$//;
        }
        foreach $line (split "\n", $contents) {
    
    	if (! $output_preformatted) {
    	    $line =~ s/^\s*//;
    	}
    
    	if ($line eq ""){
    
    	    if (! $output_preformatted) {
    		print $lineprefix, local_unescape($blankline);
    	    }
    
    503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917
    	} else {
    	    $line =~ s/\\\\\\/\&/g;
    	    if ($output_mode eq "man" && substr($line, 0, 1) eq ".") {
    		print "\\&$line";
    	    } else {
    		print $lineprefix, $line;
    	    }
    	}
    	print "\n";
        }
    }
    
    # output sections in html
    sub output_section_html(%) {
        my %args = %{$_[0]};
        my $section;
    
        foreach $section (@{$args{'sectionlist'}}) {
    	print "<h3>$section</h3>\n";
    	print "<blockquote>\n";
    	output_highlight($args{'sections'}{$section});
    	print "</blockquote>\n";
        }
    }
    
    # output enum in html
    sub output_enum_html(%) {
        my %args = %{$_[0]};
        my ($parameter);
        my $count;
        print "<h2>enum " . $args{'enum'} . "</h2>\n";
    
        print "<b>enum " . $args{'enum'} . "</b> {<br>\n";
        $count = 0;
        foreach $parameter (@{$args{'parameterlist'}}) {
    	print " <b>" . $parameter . "</b>";
    	if ($count != $#{$args{'parameterlist'}}) {
    	    $count++;
    	    print ",\n";
    	}
    	print "<br>";
        }
        print "};<br>\n";
    
        print "<h3>Constants</h3>\n";
        print "<dl>\n";
        foreach $parameter (@{$args{'parameterlist'}}) {
    	print "<dt><b>" . $parameter . "</b>\n";
    	print "<dd>";
    	output_highlight($args{'parameterdescs'}{$parameter});
        }
        print "</dl>\n";
        output_section_html(@_);
        print "<hr>\n";
    }
    
    # output typedef in html
    sub output_typedef_html(%) {
        my %args = %{$_[0]};
        my ($parameter);
        my $count;
        print "<h2>typedef " . $args{'typedef'} . "</h2>\n";
    
        print "<b>typedef " . $args{'typedef'} . "</b>\n";
        output_section_html(@_);
        print "<hr>\n";
    }
    
    # output struct in html
    sub output_struct_html(%) {
        my %args = %{$_[0]};
        my ($parameter);
    
        print "<h2>" . $args{'type'} . " " . $args{'struct'} . " - " . $args{'purpose'} . "</h2>\n";
        print "<b>" . $args{'type'} . " " . $args{'struct'} . "</b> {<br>\n";
        foreach $parameter (@{$args{'parameterlist'}}) {
    	if ($parameter =~ /^#/) {
    		print "$parameter<br>\n";
    		next;
    	}
    	my $parameter_name = $parameter;
    	$parameter_name =~ s/\[.*//;
    
    	($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next;
    	$type = $args{'parametertypes'}{$parameter};
    	if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) {
    	    # pointer-to-function
    	    print "&nbsp; &nbsp; <i>$1</i><b>$parameter</b>) <i>($2)</i>;<br>\n";
    	} elsif ($type =~ m/^(.*?)\s*(:.*)/) {
    	    # bitfield
    	    print "&nbsp; &nbsp; <i>$1</i> <b>$parameter</b>$2;<br>\n";
    	} else {
    	    print "&nbsp; &nbsp; <i>$type</i> <b>$parameter</b>;<br>\n";
    	}
        }
        print "};<br>\n";
    
        print "<h3>Members</h3>\n";
        print "<dl>\n";
        foreach $parameter (@{$args{'parameterlist'}}) {
    	($parameter =~ /^#/) && next;
    
    	my $parameter_name = $parameter;
    	$parameter_name =~ s/\[.*//;
    
    	($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next;
    	print "<dt><b>" . $parameter . "</b>\n";
    	print "<dd>";
    	output_highlight($args{'parameterdescs'}{$parameter_name});
        }
        print "</dl>\n";
        output_section_html(@_);
        print "<hr>\n";
    }
    
    # output function in html
    sub output_function_html(%) {
        my %args = %{$_[0]};
        my ($parameter, $section);
        my $count;
    
        print "<h2>" . $args{'function'} . " - " . $args{'purpose'} . "</h2>\n";
        print "<i>" . $args{'functiontype'} . "</i>\n";
        print "<b>" . $args{'function'} . "</b>\n";
        print "(";
        $count = 0;
        foreach $parameter (@{$args{'parameterlist'}}) {
    	$type = $args{'parametertypes'}{$parameter};
    	if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) {
    	    # pointer-to-function
    	    print "<i>$1</i><b>$parameter</b>) <i>($2)</i>";
    	} else {
    	    print "<i>" . $type . "</i> <b>" . $parameter . "</b>";
    	}
    	if ($count != $#{$args{'parameterlist'}}) {
    	    $count++;
    	    print ",\n";
    	}
        }
        print ")\n";
    
        print "<h3>Arguments</h3>\n";
        print "<dl>\n";
        foreach $parameter (@{$args{'parameterlist'}}) {
    	my $parameter_name = $parameter;
    	$parameter_name =~ s/\[.*//;
    
    	($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next;
    	print "<dt><b>" . $parameter . "</b>\n";
    	print "<dd>";
    	output_highlight($args{'parameterdescs'}{$parameter_name});
        }
        print "</dl>\n";
        output_section_html(@_);
        print "<hr>\n";
    }
    
    # output DOC: block header in html
    sub output_blockhead_html(%) {
        my %args = %{$_[0]};
        my ($parameter, $section);
        my $count;
    
        foreach $section (@{$args{'sectionlist'}}) {
    	print "<h3>$section</h3>\n";
    	print "<ul>\n";
    	output_highlight($args{'sections'}{$section});
    	print "</ul>\n";
        }
        print "<hr>\n";
    }
    
    # output sections in html5
    sub output_section_html5(%) {
        my %args = %{$_[0]};
        my $section;
    
        foreach $section (@{$args{'sectionlist'}}) {
    	print "<section>\n";
    	print "<h1>$section</h1>\n";
    	print "<p>\n";
    	output_highlight($args{'sections'}{$section});
    	print "</p>\n";
    	print "</section>\n";
        }
    }
    
    # output enum in html5
    sub output_enum_html5(%) {
        my %args = %{$_[0]};
        my ($parameter);
        my $count;
        my $html5id;
    
        $html5id = $args{'enum'};
        $html5id =~ s/[^a-zA-Z0-9\-]+/_/g;
        print "<article class=\"enum\" id=\"enum:". $html5id . "\">";
        print "<h1>enum " . $args{'enum'} . "</h1>\n";
        print "<ol class=\"code\">\n";
        print "<li>";
        print "<span class=\"keyword\">enum</span> ";
        print "<span class=\"identifier\">" . $args{'enum'} . "</span> {";
        print "</li>\n";
        $count = 0;
        foreach $parameter (@{$args{'parameterlist'}}) {
    	print "<li class=\"indent\">";
    	print "<span class=\"param\">" . $parameter . "</span>";
    	if ($count != $#{$args{'parameterlist'}}) {
    	    $count++;
    	    print ",";
    	}
    	print "</li>\n";
        }
        print "<li>};</li>\n";
        print "</ol>\n";
    
        print "<section>\n";
        print "<h1>Constants</h1>\n";
        print "<dl>\n";
        foreach $parameter (@{$args{'parameterlist'}}) {
    	print "<dt>" . $parameter . "</dt>\n";
    	print "<dd>";
    	output_highlight($args{'parameterdescs'}{$parameter});
    	print "</dd>\n";
        }
        print "</dl>\n";
        print "</section>\n";
        output_section_html5(@_);
        print "</article>\n";
    }
    
    # output typedef in html5
    sub output_typedef_html5(%) {
        my %args = %{$_[0]};
        my ($parameter);
        my $count;
        my $html5id;
    
        $html5id = $args{'typedef'};
        $html5id =~ s/[^a-zA-Z0-9\-]+/_/g;
        print "<article class=\"typedef\" id=\"typedef:" . $html5id . "\">\n";
        print "<h1>typedef " . $args{'typedef'} . "</h1>\n";
    
        print "<ol class=\"code\">\n";
        print "<li>";
        print "<span class=\"keyword\">typedef</span> ";
        print "<span class=\"identifier\">" . $args{'typedef'} . "</span>";
        print "</li>\n";
        print "</ol>\n";
        output_section_html5(@_);
        print "</article>\n";
    }
    
    # output struct in html5
    sub output_struct_html5(%) {
        my %args = %{$_[0]};
        my ($parameter);
        my $html5id;
    
        $html5id = $args{'struct'};
        $html5id =~ s/[^a-zA-Z0-9\-]+/_/g;
        print "<article class=\"struct\" id=\"struct:" . $html5id . "\">\n";
        print "<hgroup>\n";
        print "<h1>" . $args{'type'} . " " . $args{'struct'} . "</h1>";
        print "<h2>". $args{'purpose'} . "</h2>\n";
        print "</hgroup>\n";
        print "<ol class=\"code\">\n";
        print "<li>";
        print "<span class=\"type\">" . $args{'type'} . "</span> ";
        print "<span class=\"identifier\">" . $args{'struct'} . "</span> {";
        print "</li>\n";
        foreach $parameter (@{$args{'parameterlist'}}) {
    	print "<li class=\"indent\">";
    	if ($parameter =~ /^#/) {
    		print "<span class=\"param\">" . $parameter ."</span>\n";
    		print "</li>\n";
    		next;
    	}
    	my $parameter_name = $parameter;
    	$parameter_name =~ s/\[.*//;
    
    	($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next;
    	$type = $args{'parametertypes'}{$parameter};
    	if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) {
    	    # pointer-to-function
    	    print "<span class=\"type\">$1</span> ";
    	    print "<span class=\"param\">$parameter</span>";
    	    print "<span class=\"type\">)</span> ";
    	    print "(<span class=\"args\">$2</span>);";
    	} elsif ($type =~ m/^(.*?)\s*(:.*)/) {
    	    # bitfield
    	    print "<span class=\"type\">$1</span> ";
    	    print "<span class=\"param\">$parameter</span>";
    	    print "<span class=\"bits\">$2</span>;";
    	} else {
    	    print "<span class=\"type\">$type</span> ";
    	    print "<span class=\"param\">$parameter</span>;";
    	}
    	print "</li>\n";
        }
        print "<li>};</li>\n";
        print "</ol>\n";
    
        print "<section>\n";
        print "<h1>Members</h1>\n";
        print "<dl>\n";
        foreach $parameter (@{$args{'parameterlist'}}) {
    	($parameter =~ /^#/) && next;
    
    	my $parameter_name = $parameter;
    	$parameter_name =~ s/\[.*//;
    
    	($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next;
    	print "<dt>" . $parameter . "</dt>\n";
    	print "<dd>";
    	output_highlight($args{'parameterdescs'}{$parameter_name});
    	print "</dd>\n";
        }
        print "</dl>\n";
        print "</section>\n";
        output_section_html5(@_);
        print "</article>\n";
    }
    
    # output function in html5
    sub output_function_html5(%) {
        my %args = %{$_[0]};
        my ($parameter, $section);
        my $count;
        my $html5id;
    
        $html5id = $args{'function'};
        $html5id =~ s/[^a-zA-Z0-9\-]+/_/g;
        print "<article class=\"function\" id=\"func:". $html5id . "\">\n";
        print "<hgroup>\n";
        print "<h1>" . $args{'function'} . "</h1>";
        print "<h2>" . $args{'purpose'} . "</h2>\n";
        print "</hgroup>\n";
        print "<ol class=\"code\">\n";
        print "<li>";
        print "<span class=\"type\">" . $args{'functiontype'} . "</span> ";
        print "<span class=\"identifier\">" . $args{'function'} . "</span> (";
        print "</li>";
        $count = 0;
        foreach $parameter (@{$args{'parameterlist'}}) {
    	print "<li class=\"indent\">";
    	$type = $args{'parametertypes'}{$parameter};
    	if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) {
    	    # pointer-to-function
    	    print "<span class=\"type\">$1</span> ";
    	    print "<span class=\"param\">$parameter</span>";
    	    print "<span class=\"type\">)</span> ";
    	    print "(<span class=\"args\">$2</span>)";
    	} else {
    	    print "<span class=\"type\">$type</span> ";
    	    print "<span class=\"param\">$parameter</span>";
    	}
    	if ($count != $#{$args{'parameterlist'}}) {
    	    $count++;
    	    print ",";
    	}
    	print "</li>\n";
        }
        print "<li>)</li>\n";
        print "</ol>\n";
    
        print "<section>\n";
        print "<h1>Arguments</h1>\n";
        print "<p>\n";
        print "<dl>\n";
        foreach $parameter (@{$args{'parameterlist'}}) {
    	my $parameter_name = $parameter;
    	$parameter_name =~ s/\[.*//;
    
    	($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next;
    	print "<dt>" . $parameter . "</dt>\n";
    	print "<dd>";
    	output_highlight($args{'parameterdescs'}{$parameter_name});
    	print "</dd>\n";
        }
        print "</dl>\n";
        print "</section>\n";
        output_section_html5(@_);
        print "</article>\n";
    }
    
    # output DOC: block header in html5
    sub output_blockhead_html5(%) {
        my %args = %{$_[0]};
        my ($parameter, $section);
        my $count;
        my $html5id;
    
        foreach $section (@{$args{'sectionlist'}}) {
    	$html5id = $section;
    	$html5id =~ s/[^a-zA-Z0-9\-]+/_/g;
    	print "<article class=\"doc\" id=\"doc:". $html5id . "\">\n";
    	print "<h1>$section</h1>\n";
    	print "<p>\n";
    	output_highlight($args{'sections'}{$section});
    	print "</p>\n";
        }
        print "</article>\n";
    }
    
    sub output_section_xml(%) {
        my %args = %{$_[0]};
        my $section;
        # print out each section
        $lineprefix="   ";
        foreach $section (@{$args{'sectionlist'}}) {
    	print "<refsect1>\n";
    	print "<title>$section</title>\n";
    	if ($section =~ m/EXAMPLE/i) {
    	    print "<informalexample><programlisting>\n";
    
    	} else {
    	    print "<para>\n";
    	}
    	output_highlight($args{'sections'}{$section});
    
    	if ($section =~ m/EXAMPLE/i) {
    	    print "</programlisting></informalexample>\n";
    	} else {
    	    print "</para>\n";
    	}
    	print "</refsect1>\n";
        }
    }
    
    # output function in XML DocBook
    sub output_function_xml(%) {
        my %args = %{$_[0]};
        my ($parameter, $section);
        my $count;
        my $id;
    
        $id = "API-" . $args{'function'};
        $id =~ s/[^A-Za-z0-9]/-/g;
    
        print "<refentry id=\"$id\">\n";
        print "<refentryinfo>\n";
        print " <title>U-BOOT</title>\n";
        print " <productname>Bootloader Hackers Manual</productname>\n";
        print " <date>$man_date</date>\n";
        print "</refentryinfo>\n";
        print "<refmeta>\n";
        print " <refentrytitle><phrase>" . $args{'function'} . "</phrase></refentrytitle>\n";
        print " <manvolnum>9</manvolnum>\n";
        print " <refmiscinfo class=\"version\">" . $kernelversion . "</refmiscinfo>\n";
        print "</refmeta>\n";
        print "<refnamediv>\n";
        print " <refname>" . $args{'function'} . "</refname>\n";
        print " <refpurpose>\n";
        print "  ";
        output_highlight ($args{'purpose'});
        print " </refpurpose>\n";
        print "</refnamediv>\n";
    
        print "<refsynopsisdiv>\n";
        print " <title>Synopsis</title>\n";
        print "  <funcsynopsis><funcprototype>\n";
        print "   <funcdef>" . $args{'functiontype'} . " ";
        print "<function>" . $args{'function'} . " </function></funcdef>\n";
    
        $count = 0;
        if ($#{$args{'parameterlist'}} >= 0) {
    	foreach $parameter (@{$args{'parameterlist'}}) {
    	    $type = $args{'parametertypes'}{$parameter};
    	    if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) {
    		# pointer-to-function
    		print "   <paramdef>$1<parameter>$parameter</parameter>)\n";
    		print "     <funcparams>$2</funcparams></paramdef>\n";
    	    } else {
    		print "   <paramdef>" . $type;
    		print " <parameter>$parameter</parameter></paramdef>\n";
    	    }
    	}
        } else {
    	print "  <void/>\n";
        }
        print "  </funcprototype></funcsynopsis>\n";
        print "</refsynopsisdiv>\n";
    
        # print parameters
        print "<refsect1>\n <title>Arguments</title>\n";
        if ($#{$args{'parameterlist'}} >= 0) {
    	print " <variablelist>\n";
    	foreach $parameter (@{$args{'parameterlist'}}) {
    	    my $parameter_name = $parameter;
    	    $parameter_name =~ s/\[.*//;
    
    	    print "  <varlistentry>\n   <term><parameter>$parameter</parameter></term>\n";
    	    print "   <listitem>\n    <para>\n";
    	    $lineprefix="     ";
    	    output_highlight($args{'parameterdescs'}{$parameter_name});
    	    print "    </para>\n   </listitem>\n  </varlistentry>\n";
    	}