### 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/^fastcall +//; $prototype =~ s/^asmlinkage +//; $prototype =~ s/^inline +//; $prototype =~ s/^__inline__ +//; $prototype =~ s/^#define +//; #ak added $prototype =~ s/__attribute__ \(\([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*\(([^\{]*)\)/) { $return_type = $1; $declaration_name = $2; my $args = $3; create_parameterlist($args, ',', $file); } else { print STDERR "Error(${file}:$.): cannot understand prototype: '$prototype'\n"; ++$errors; return; } 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 process_file($); # 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);} if ($filelist) { open(FLIST,"<$filelist") or die "Can't open file list $filelist"; while(<FLIST>) { chop; process_file($_); }} foreach (@ARGV) { chomp; process_file($_);}if ($verbose && $errors) { print STDERR "$errors errors\n";}if ($verbose && $warnings) { print STDERR "$warnings warnings\n";} exit($errors); sub reset_state { $function = ""; %constants = (); %parameterdescs = (); %parametertypes = (); @parameterlist = (); %sections = (); @sectionlist = (); $prototype = ""; $state = 0;} sub process_state3_function($$) { my $x = shift; my $file = shift; if ($x =~ m#\s*/\*\s+MACDOC\s*#io || ($x =~ /^#/ && $x !~ /^#define/)) { # do nothing } elsif ($x =~ /([^\{]*)/) { $prototype .= $1; } if (($x =~ /\{/) || ($x =~ /\#define/) || ($x =~ /;/)) { $prototype =~ s@/\*.*?\*/@@gos; # strip comments. $prototype =~ s@[\r\n]+@ @gos; # strip newlines/cr's. $prototype =~ s@^\s+@@gos; # strip leading spaces 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 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; } }} # replace <, >, and &sub xml_escape($) { my $text = shift; $text =~ s/\&/\\\\\\amp;/g; $text =~ s/\</\\\\\\lt;/g; $text =~ s/\>/\\\\\\gt;/g; return $text;} sub process_file($) { my $file; my $identifier; my $func; 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; } $section_counter = 0; while (<IN>) { 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_decl/o) { $identifier = $1; if (/\s*([\w\s]+?)\s*-/) { $identifier = $1; } $state = 2; if (/-(.*)/) { $declaration_purpose = xml_escape($1); } else { $declaration_purpose = ""; } 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 "") { dump_section($section, xml_escape($contents)); $section = $section_default; } $contents = $newcontents; if ($contents ne "") { $contents .= "\n"; } $section = $newsection; } elsif (/$doc_end/) { if ($contents ne "") { dump_section($section, xml_escape($contents)); $section = $section_default; $contents = ""; } $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 "" && ($section =~ m/^@/ || $section eq $section_context)) { dump_section($section, xml_escape($contents)); $section = $section_default; $contents = ""; } 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_section($section, $contents); output_intro({'sectionlist' => \@sectionlist, 'sections' => \%sections }); $contents = ""; $function = ""; %constants = (); %parameterdescs = (); %parametertypes = (); @parameterlist = (); %sections = (); @sectionlist = (); $prototype = ""; if ( $1 eq "" ) { $section = $section_intro; } else { $section = $1; } } elsif (/$doc_end/) { dump_section($section, $contents); output_intro({'sectionlist' => \@sectionlist, 'sections' => \%sections }); $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 ($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"; } }}Lines 1501–1883 of 1883. Previous lines
## # 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/^fastcall +//; $prototype =~ s/^asmlinkage +//; $prototype =~ s/^inline +//; $prototype =~ s/^__inline__ +//; $prototype =~ s/^#define +//; #ak added $prototype =~ s/__attribute__ \(\([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*\(([^\{]*)\)/) { $return_type = $1; $declaration_name = $2; my $args = $3; create_parameterlist($args, ',', $file); } else { print STDERR "Error(${file}:$.): cannot understand prototype: '$prototype'\n"; ++$errors; return; } 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 process_file($); # 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); } if ($filelist) { open(FLIST,"<$filelist") or die "Can't open file list $filelist"; while(<FLIST>) { chop; process_file($_); } } foreach (@ARGV) { chomp; process_file($_); } if ($verbose && $errors) { print STDERR "$errors errors\n"; } if ($verbose && $warnings) { print STDERR "$warnings warnings\n"; } exit($errors); sub reset_state { $function = ""; %constants = (); %parameterdescs = (); %parametertypes = (); @parameterlist = (); %sections = (); @sectionlist = (); $prototype = ""; $state = 0; } sub process_state3_function($$) { my $x = shift; my $file = shift; if ($x =~ m#\s*/\*\s+MACDOC\s*#io || ($x =~ /^#/ && $x !~ /^#define/)) { # do nothing } elsif ($x =~ /([^\{]*)/) { $prototype .= $1; } if (($x =~ /\{/) || ($x =~ /\#define/) || ($x =~ /;/)) { $prototype =~ s@/\*.*?\*/@@gos; # strip comments. $prototype =~ s@[\r\n]+@ @gos; # strip newlines/cr's. $prototype =~ s@^\s+@@gos; # strip leading spaces 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 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; } } } # replace <, >, and & sub xml_escape($) { my $text = shift; $text =~ s/\&/\\\\\\amp;/g; $text =~ s/\</\\\\\\lt;/g; $text =~ s/\>/\\\\\\gt;/g; return $text; } sub process_file($) { my $file; my $identifier; my $func; 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; } $section_counter = 0; while (<IN>) { 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_decl/o) { $identifier = $1; if (/\s*([\w\s]+?)\s*-/) { $identifier = $1; } $state = 2; if (/-(.*)/) { $declaration_purpose = xml_escape($1); } else { $declaration_purpose = ""; } 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 "") { dump_section($section, xml_escape($contents)); $section = $section_default; } $contents = $newcontents; if ($contents ne "") { $contents .= "\n"; } $section = $newsection; } elsif (/$doc_end/) { if ($contents ne "") { dump_section($section, xml_escape($contents)); $section = $section_default; $contents = ""; } $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 "" && ($section =~ m/^@/ || $section eq $section_context)) { dump_section($section, xml_escape($contents)); $section = $section_default; $contents = ""; } 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_section($section, $contents); output_intro({'sectionlist' => \@sectionlist, 'sections' => \%sections }); $contents = ""; $function = ""; %constants = (); %parameterdescs = (); %parametertypes = (); @parameterlist = (); %sections = (); @sectionlist = (); $prototype = ""; if ( $1 eq "" ) { $section = $section_intro; } else { $section = $1; } } elsif (/$doc_end/) { dump_section($section, $contents); output_intro({'sectionlist' => \@sectionlist, 'sections' => \%sections }); $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 ($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"; } } }