#!/usr/bin/perl ## Copyright (c) 1998 Michael Zucchi, All Rights Reserved ## ## ## ## This software falls under the GNU Public License. Please read ## ## the COPYING file for more information ## # # 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: # kerneldoc [ -docbook | -html | -text | -man | -gnome | -bird ] # [ -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 -text -man -gnome or -bird. Default is man. # # -function funcname # If set, then only generate documentation for the given function(s). All # other functions are ignored. # # -nofunction funcname # If set, then only generate documentation for the other function(s). All # other functions are ignored. Cannot be used with -function together # (yes thats 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 ommitted, 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. # # All descriptions can be multiline, apart from the short function description. # # 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 # '@parameter' - name of a parameter # '%CONST' - name of a constant. # '|code|' - literal string # match expressions used to find embedded type information $type_constant = "\\\%(\\w+)"; $type_func = "(\\w+\\(\\))"; $type_param = "\\\@(\\w+)"; $type_struct = "\\\\\\\\\\\\amp;(\\w+)"; $type_env = "(\\\$\\w+)"; $type_code = "\\|(.*)\\|"; # Output conversion substitutions. # One for each output format # these work fairly well %highlights_html = ( $type_constant, "\$1", $type_func, "\$1", $type_struct, "\$1", $type_param, "\$1" ); $blankline_html = "

"; # sgml, docbook format %highlights_sgml = ( $type_constant, "\$1", $type_func, "\$1", $type_struct, "\$1", $type_env, "\$1", $type_param, "\$1" ); $blankline_sgml = "\n"; # gnome, docbook format %highlights_gnome = ( $type_constant, "\$1", $type_func, "\$1", $type_struct, "\$1", $type_env, "\$1", $type_param, "\$1" ); $blankline_gnome = "\n"; # bird documentation %highlights_bird = ( $type_constant, "\$1"); $blankline_bird = "

"; # these are pretty rough %highlights_man = ( $type_constant, "\\n.I \\\"\$1\\\"\\n", $type_func, "\\n.B \\\"\$1\\\"\\n", $type_struct, "\\n.I \\\"\$1\\\"\\n", $type_param."([\.\, ]*)\n?", "\\n.I \\\"\$1\$2\\\"\\n" ); $blankline_man = ""; # text-mode %highlights_text = ( $type_constant, "\$1", $type_func, "\$1", $type_struct, "\$1", $type_param, "\$1" ); $blankline_text = ""; sub usage { print "Usage: $0 [ -v ] [ -docbook | -html | -text | -man ]\n"; print " [ -function funcname [ -function funcname ...] ]\n"; print " [ -nofunction funcname [ -nofunction funcname ...] ]\n"; print " c source file(s) > outputfile\n"; exit 1; } # read arguments if ($#ARGV==-1) { usage(); } $verbose = 0; $output_mode = "man"; %highlights = %highlights_man; $blankline = $blankline_man; $modulename = "API Documentation"; $function_only = 0; while ($ARGV[0] =~ m/^-(.*)/) { $cmd = shift @ARGV; if ($cmd eq "-html") { $output_mode = "html"; %highlights = %highlights_html; $blankline = $blankline_html; } 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 = "sgml"; %highlights = %highlights_sgml; $blankline = $blankline_sgml; } elsif ($cmd eq "-gnome") { $output_mode = "gnome"; %highlights = %highlights_gnome; $blankline = $blankline_gnome; } elsif ($cmd eq "-bird") { $output_mode = "bird"; %highlights = %highlights_bird; $blankline = $blankline_bird; } elsif ($cmd eq "-module") { # not needed for sgml, 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(); } } # generate a sequence of code that will splice in highlighting information # using the s// operator. $dohighlight = ""; foreach $pattern (keys %highlights) { # print "scanning pattern $pattern ($highlights{$pattern})\n"; $dohighlight .= "\$contents =~ s:$pattern:$highlights{$pattern}:gs;\n"; } ## # dumps section contents to arrays/hashes intended for that purpose. # sub dump_section { 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; $parameters{$name} = $contents; } else { # print STDERR "other section '$name' = '$contents'\n"; $sections{$name} = $contents; push @sectionlist, $name; } } ## # output function # # parameters, a hash. # function => "function name" # parameterlist => @list of parameters # parameters => %parameter descriptions # sectionlist => @list of sections # sections => %descriont descriptions # sub output_highlight { my $contents = join "\n", @_; my $line; eval $dohighlight; foreach $line (split "\n", $contents) { if ($line eq ""){ print $lineprefix, $blankline; } else { $line =~ s/\\\\\\/\&/g; print $lineprefix, $line; } print "\n"; } } # output in html sub output_html { my %args = %{$_[0]}; my ($parameter, $section); my $count; print "

Function

\n"; print "".$args{'functiontype'}."\n"; print "".$args{'function'}."\n"; print "("; $count = 0; foreach $parameter (@{$args{'parameterlist'}}) { print "".$args{'parametertypes'}{$parameter}." ".$parameter."\n"; if ($count != $#{$args{'parameterlist'}}) { $count++; print ", "; } } print ")\n"; print "

Arguments

\n"; print "
\n"; foreach $parameter (@{$args{'parameterlist'}}) { print "
".$args{'parametertypes'}{$parameter}." ".$parameter."\n"; print "
"; output_highlight($args{'parameters'}{$parameter}); } print "
\n"; foreach $section (@{$args{'sectionlist'}}) { print "

$section

\n"; print "\n"; } print "
\n"; } # output in html sub output_intro_html { my %args = %{$_[0]}; my ($parameter, $section); my $count; foreach $section (@{$args{'sectionlist'}}) { print "

$section

\n"; print "\n"; } print "
\n"; } # output in sgml DocBook sub output_sgml { my %args = %{$_[0]}; my ($parameter, $section); my $count; my $id; $id = $args{'module'}."-".$args{'function'}; $id =~ s/[^A-Za-z0-9]/-/g; print "\n"; print "\n"; print "".$args{'function'}."\n"; print "\n"; print "\n"; print " ".$args{'function'}."\n"; print " \n"; print " ".$args{'purpose'}."\n"; print " \n"; print "\n"; print "\n"; print " Synopsis\n"; print " \n"; print " ".$args{'functiontype'}." "; print "".$args{'function'}." "; print "\n"; # print "\n"; # print " Synopsis\n"; # print " \n"; # print " ".$args{'functiontype'}." "; # print "".$args{'function'}." "; # print "\n"; $count = 0; if ($#{$args{'parameterlist'}} >= 0) { foreach $parameter (@{$args{'parameterlist'}}) { print " ".$args{'parametertypes'}{$parameter}; print " $parameter\n"; } } else { print " \n"; } print " \n"; print "\n"; # print "\n"; # print parameters print "\n Arguments\n"; # print "\nArguments\n"; if ($#{$args{'parameterlist'}} >= 0) { print " \n"; foreach $parameter (@{$args{'parameterlist'}}) { print " \n $parameter\n"; print " \n \n"; $lineprefix=" "; output_highlight($args{'parameters'}{$parameter}); print " \n \n \n"; } print " \n"; } else { print " \n None\n \n"; } print "\n"; # print out each section $lineprefix=" "; foreach $section (@{$args{'sectionlist'}}) { print "\n $section\n \n"; # print "\n$section\n"; if ($section =~ m/EXAMPLE/i) { print "\n"; } output_highlight($args{'sections'}{$section}); # print ""; if ($section =~ m/EXAMPLE/i) { print "\n"; } print " \n\n"; } print "\n\n"; } # output in sgml DocBook sub output_intro_sgml { my %args = %{$_[0]}; my ($parameter, $section); my $count; my $id; $id = $args{'module'}; $id =~ s/[^A-Za-z0-9]/-/g; # print out each section $lineprefix=" "; foreach $section (@{$args{'sectionlist'}}) { print "\n $section\n \n"; # print "\n$section\n"; if ($section =~ m/EXAMPLE/i) { print "\n"; } output_highlight($args{'sections'}{$section}); # print ""; if ($section =~ m/EXAMPLE/i) { print "\n"; } print " \n\n"; } print "\n\n"; } # output in sgml DocBook sub output_gnome { my %args = %{$_[0]}; my ($parameter, $section); my $count; my $id; $id = $args{'module'}."-".$args{'function'}; $id =~ s/[^A-Za-z0-9]/-/g; print "\n"; print " ".$args{'function'}."\n"; # print "\n"; # print " Synopsis\n"; print " \n"; print " ".$args{'functiontype'}." "; print "".$args{'function'}." "; print "\n"; $count = 0; if ($#{$args{'parameterlist'}} >= 0) { foreach $parameter (@{$args{'parameterlist'}}) { print " ".$args{'parametertypes'}{$parameter}; print " $parameter\n"; } } else { print " \n"; } print " \n"; # print "\n"; # print "\n"; # print parameters # print "\n Arguments\n"; # if ($#{$args{'parameterlist'}} >= 0) { # print " \n"; # foreach $parameter (@{$args{'parameterlist'}}) { # print " \n $parameter\n"; # print " \n \n"; # $lineprefix=" "; # output_highlight($args{'parameters'}{$parameter}); # print " \n \n \n"; # } # print " \n"; # } else { # print " \n None\n \n"; # } # print "\n"; # print "\n Arguments\n"; if ($#{$args{'parameterlist'}} >= 0) { print " \n"; print "\n"; print "\n"; print "\n"; print "\n"; foreach $parameter (@{$args{'parameterlist'}}) { print " $parameter\n"; print " \n"; $lineprefix=" "; output_highlight($args{'parameters'}{$parameter}); print " \n"; } print " \n"; } else { print " \n None\n \n"; } # print "\n"; # print out each section $lineprefix=" "; foreach $section (@{$args{'sectionlist'}}) { print "\n $section\n"; # print "\n$section\n"; if ($section =~ m/EXAMPLE/i) { print "\n"; } else { } print "\n"; output_highlight($args{'sections'}{$section}); # print ""; print "\n"; if ($section =~ m/EXAMPLE/i) { print "\n"; } else { } print " \n"; } print "\n\n"; } # output in birddoc sub output_bird { my %args = %{$_[0]}; my ($parameter, $section); my $count; print "

".$args{'functiontype'}."\n"; print "".$args{'function'}."\n"; print "("; $count = 0; my $ntyped = 0; foreach $parameter (@{$args{'parameterlist'}}) { if ($args{'parametertypes'}{$parameter} ne "") { print "".$args{'parametertypes'}{$parameter}." "; $ntyped++; } print "".$parameter.""; if ($count != $#{$args{'parameterlist'}}) { $count++; print ", "; } } print ")\n"; if ($ntyped) { print "Arguments\n"; print "

\n"; foreach $parameter (@{$args{'parameterlist'}}) { print "".$args{'parametertypes'}{$parameter}." ".$parameter."\n"; output_highlight($args{'parameters'}{$parameter}); } print "\n"; } foreach $section (@{$args{'sectionlist'}}) { print "$section\n"; print "

\n"; output_highlight($args{'sections'}{$section}); } print "\n"; } # output in birddoc sub output_intro_bird { my %args = %{$_[0]}; my ($parameter, $section); my $count; my $id; $id = $args{'module'}; $id =~ s/[^A-Za-z0-9]/-/g; # print out each section $lineprefix=" "; foreach $section (@{$args{'sectionlist'}}) { print "$section\n

\n"; output_highlight($args{'sections'}{$section}); } print "\n\n"; } ## # output in man sub output_man { my %args = %{$_[0]}; my ($parameter, $section); my $count; print ".TH \"$args{'module'}\" \"$args{'function'}\" \"25 May 1998\" \"API Manual\" LINUX\n"; print ".SH Function\n"; print ".I \"".$args{'functiontype'}."\"\n"; print ".B \"".$args{'function'}."\"\n"; print "(\n"; $count = 0; foreach $parameter (@{$args{'parameterlist'}}) { print ".I \"".$args{'parametertypes'}{$parameter}."\"\n.B \"".$parameter."\"\n"; if ($count != $#{$args{'parameterlist'}}) { $count++; print ",\n"; } } print ")\n"; print ".SH Arguments\n"; foreach $parameter (@{$args{'parameterlist'}}) { print ".IP \"".$args{'parametertypes'}{$parameter}." ".$parameter."\" 12\n"; output_highlight($args{'parameters'}{$parameter}); } foreach $section (@{$args{'sectionlist'}}) { print ".SH \"$section\"\n"; output_highlight($args{'sections'}{$section}); } } sub output_intro_man { my %args = %{$_[0]}; my ($parameter, $section); my $count; print ".TH \"$args{'module'}\" \"$args{'module'}\" \"25 May 1998\" \"API Manual\" LINUX\n"; foreach $section (@{$args{'sectionlist'}}) { print ".SH \"$section\"\n"; output_highlight($args{'sections'}{$section}); } } ## # output in text sub output_text { my %args = %{$_[0]}; my ($parameter, $section); print "Function = ".$args{'function'}."\n"; print " return type: ".$args{'functiontype'}."\n\n"; foreach $parameter (@{$args{'parameterlist'}}) { print " ".$args{'parametertypes'}{$parameter}." ".$parameter."\n"; print " -> ".$args{'parameters'}{$parameter}."\n"; } foreach $section (@{$args{'sectionlist'}}) { print " $section:\n"; print " -> "; output_highlight($args{'sections'}{$section}); } } sub output_intro_text { my %args = %{$_[0]}; my ($parameter, $section); foreach $section (@{$args{'sectionlist'}}) { print " $section:\n"; print " -> "; output_highlight($args{'sections'}{$section}); } } ## # generic output function - calls the right one based # on current output mode. sub output_function { # output_html(@_); eval "output_".$output_mode."(\@_);"; } ## # generic output function - calls the right one based # on current output mode. sub output_intro { # output_html(@_); eval "output_intro_".$output_mode."(\@_);"; } ## # takes a function prototype and spits out all the details # stored in the global arrays/hsahes. sub dump_function { my $prototype = shift @_; $prototype =~ s/^static+ //; $prototype =~ s/^extern+ //; $prototype =~ s/^inline+ //; $prototype =~ s/^__inline__+ //; 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*\(([^\)]*)\)/) { $return_type = $1; $function_name = $2; $args = $3; # print STDERR "ARGS = '$args'\n"; foreach $arg (split ',', $args) { # strip leading/trailing spaces $arg =~ s/^\s*//; $arg =~ s/\s*$//; # print STDERR "SCAN ARG: '$arg'\n"; @args = split('\s', $arg); # print STDERR " -> @args\n"; $param = pop @args; # print STDERR " -> @args\n"; if ($param =~ m/^(\*+)(.*)/) { $param = $2; push @args, $1; } $type = join " ", @args; if ($type eq "" && $param eq "...") { $type="..."; $param="..."; $parameters{"..."} = "variable arguments"; } if ($type eq "") { $type=""; $param="void"; $parameters{void} = "no arguments"; } if ($parameters{$param} eq "") { $parameters{$param} = "-- undescribed --"; print STDERR "Warning($lineno): Function parameter '$param' not described in '$function_name'\n"; } push @parameterlist, $param; $parametertypes{$param} = $type; # print STDERR "param = '$param', type = '$type'\n"; } } else { print STDERR "Error($lineno): cannot understand prototype: '$prototype'\n"; return; } if ($function_only==0 || ( $function_only == 1 && defined($function_table{$function_name})) || ( $function_only == 2 && !defined($function_table{$function_name}))) { output_function({'function' => $function_name, 'module' => $modulename, 'functiontype' => $return_type, 'parameterlist' => \@parameterlist, 'parameters' => \%parameters, 'parametertypes' => \%parametertypes, 'sectionlist' => \@sectionlist, 'sections' => \%sections, 'purpose' => $function_purpose }); } } ###################################################################### # main # states # 0 - normal code # 1 - looking for function name # 2 - scanning field start. # 3 - scanning prototype. $state = 0; $section = ""; $doc_special = "\@\%\$\&"; $doc_start = "^/\\*\\*\$"; $doc_end = "\\*/"; $doc_com = "\\s*\\*\\s*"; $doc_func = $doc_com."(\\w+):?"; $doc_sect = $doc_com."([".$doc_special."]?[\\w ]+):(.*)"; $doc_content = $doc_com."(.*)"; $doc_block = $doc_com."DOC:\\s*(.*)?"; %constants = (); %parameters = (); @parameterlist = (); %sections = (); @sectionlist = (); $contents = ""; $section_default = "Description"; # default section $section_intro = "Introduction"; $section = $section_default; $lineno = 0; foreach $file (@ARGV) { if (!open(IN,"<$file")) { print STDERR "Error: Cannot open file $file\n"; next; } while () { $lineno++; if ($state == 0) { if (/$doc_start/o) { $state = 1; # next line is always the function name } } 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_func/o) { $function = $1; $state = 2; if (/-(.*)/) { $function_purpose = $1; } else { $function_purpose = ""; } if ($verbose) { print STDERR "Info($lineno): Scanning doc for $function\n"; } } else { print STDERR "WARN($lineno): Cannot understand $_ on line $lineno", " - I thought it was a doc line\n"; $state = 0; } } elsif ($state == 2) { # look for head: lines, and include content if (/$doc_sect/o) { $newsection = $1; $newcontents = $2; if ($contents ne "") { $contents =~ s/\&/\\\\\\amp;/g; $contents =~ s/\/\\\\\\gt;/g; dump_section($section, $contents); $section = $section_default; } $contents = $newcontents; if ($contents ne "") { $contents .= "\n"; } $section = $newsection; } elsif (/$doc_end/) { if ($contents ne "") { $contents =~ s/\&/\\\\\\amp;/g; $contents =~ s/\/\\\\\\gt;/g; dump_section($section, $contents); $section = $section_default; $contents = ""; } # print STDERR "end of doc comment, looking for prototype\n"; $prototype = ""; $state = 3; } elsif (/$doc_content/) { # miguel-style comment kludge, look for blank lines after # @parameter line to signify start of description if ($1 eq "" && $section =~ m/^@/) { $contents =~ s/\&/\\\\\\amp;/g; $contents =~ s/\/\\\\\\gt;/g; dump_section($section, $contents); $section = $section_default; $contents = ""; } else { $contents .= $1."\n"; } } else { # i dont know - bad line? ignore. print STDERR "WARNING($lineno): bad line: $_"; } } elsif ($state == 3) { # scanning for function { (end of prototype) if (m#\s*/\*\s+MACDOC\s*#io) { # do nothing } elsif (/([^\{]*)/) { $prototype .= $1; } if (/\{/) { $prototype =~ s@/\*.*?\*/@@gos; # strip comments. $prototype =~ s@[\r\n]+@ @gos; # strip newlines/cr's. $prototype =~ s@^ +@@gos; # strip leading spaces dump_function($prototype); $function = ""; %constants = (); %parameters = (); %parametertypes = (); @parameterlist = (); %sections = (); @sectionlist = (); $prototype = ""; $state = 0; } } elsif ($state == 4) { # Documentation block if (/$doc_block/) { $contents =~ s/\&/\\\\\\amp;/g; $contents =~ s/\/\\\\\\gt;/g; dump_section($section, $contents); output_intro({'sectionlist' => \@sectionlist, 'sections' => \%sections }); $contents = ""; $function = ""; %constants = (); %parameters = (); %parametertypes = (); @parameterlist = (); %sections = (); @sectionlist = (); $prototype = ""; if ( $1 eq "" ) { $section = $section_intro; } else { $section = $1; } } elsif (/$doc_end/) { $contents =~ s/\&/\\\\\\amp;/g; $contents =~ s/\/\\\\\\gt;/g; dump_section($section, $contents); output_intro({'sectionlist' => \@sectionlist, 'sections' => \%sections }); $contents = ""; $function = ""; %constants = (); %parameters = (); %parametertypes = (); @parameterlist = (); %sections = (); @sectionlist = (); $prototype = ""; $state = 0; } elsif (/$doc_content/) { if ( $1 eq "" ) { $contents .= "\n"; } else { $contents .= $1 . "\n"; } } } } }