#!@PERL@ -w # -*- cperl -*- # # gtk-doc - GTK DocBook documentation generator. # Copyright (C) 1998 Damon Chaplin # 2007,2008,2009 Stefan Kost # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # ############################################################################# # Script : gtkdoc-mkdb # Description : This creates the DocBook files from the edited templates. ############################################################################# use strict; use Getopt::Long; push @INC, '@PACKAGE_DATA_DIR@'; require "gtkdoc-common.pl"; # Options # name of documentation module my $MODULE; my $TMPL_DIR; my $SGML_OUTPUT_DIR; my @SOURCE_DIRS; my $SOURCE_SUFFIXES = ""; my $IGNORE_FILES = ""; my $PRINT_VERSION; my $PRINT_HELP; my $MAIN_SGML_FILE; my $EXPAND_CONTENT_FILES = ""; my $INLINE_MARKUP_MODE; my $DEFAULT_STABILITY; my $DEFAULT_INCLUDES; my $OUTPUT_FORMAT; my $NAME_SPACE = ""; my $OUTPUT_ALL_SYMBOLS; my $OUTPUT_SYMBOLS_WITHOUT_SINCE; my %optctl = ('module' => \$MODULE, 'source-dir' => \@SOURCE_DIRS, 'source-suffixes' => \$SOURCE_SUFFIXES, 'ignore-files' => \$IGNORE_FILES, 'output-dir' => \$SGML_OUTPUT_DIR, 'tmpl-dir' => \$TMPL_DIR, 'version' => \$PRINT_VERSION, 'help' => \$PRINT_HELP, 'main-sgml-file' => \$MAIN_SGML_FILE, 'expand-content-files' => \$EXPAND_CONTENT_FILES, 'sgml-mode' => \$INLINE_MARKUP_MODE, 'xml-mode' => \$INLINE_MARKUP_MODE, 'default-stability' => \$DEFAULT_STABILITY, 'default-includes' => \$DEFAULT_INCLUDES, 'output-format' => \$OUTPUT_FORMAT, 'name-space' => \$NAME_SPACE, 'outputallsymbols' => \$OUTPUT_ALL_SYMBOLS, 'outputsymbolswithoutsince' => \$OUTPUT_SYMBOLS_WITHOUT_SINCE ); GetOptions(\%optctl, "module=s", "source-dir:s", "source-suffixes:s", "ignore-files:s", "output-dir:s", "tmpl-dir:s", "version", "outputallsymbols", "outputsymbolswithoutsince", "expand-content-files:s", "main-sgml-file:s", "extra-db-files:s", "help", "sgml-mode", "xml-mode", "default-stability:s", "default-includes:s", "output-format:s", "name-space:s"); if ($PRINT_VERSION) { print "@VERSION@\n"; exit 0; } if (!$MODULE) { $PRINT_HELP = 1; } if ($DEFAULT_STABILITY && $DEFAULT_STABILITY ne "Stable" && $DEFAULT_STABILITY ne "Private" && $DEFAULT_STABILITY ne "Unstable") { $PRINT_HELP = 1; } if ($PRINT_HELP) { print <) { if (/^\s*<(book|chapter|article)/) { # check that the top-level tag or the doctype decl contain the xinclude namespace decl if (($_ !~ m/http:\/\/www.w3.org\/200[13]\/XInclude/) && ($doctype_header !~ m/http:\/\/www.w3.org\/200[13]\/XInclude/m)) { $doctype_header = ""; } last; } $doctype_header .= $_; } close(INPUT); $doctype_header =~ s/##g; } else { $doctype_header = "\n" . "\n" . "]>\n"; } } else { if (!$MAIN_SGML_FILE) { $MAIN_SGML_FILE = "${MODULE}-docs.sgml"; } $empty_element_end = ">"; $doctype_header = ""; } my $ROOT_DIR = "."; # All the files are written in subdirectories beneath here. $TMPL_DIR = $TMPL_DIR ? $TMPL_DIR : "$ROOT_DIR/tmpl"; # This is where we put all the DocBook output. $SGML_OUTPUT_DIR = $SGML_OUTPUT_DIR ? $SGML_OUTPUT_DIR : "$ROOT_DIR/$OUTPUT_FORMAT"; # This file contains the object hierarchy. my $OBJECT_TREE_FILE = "$ROOT_DIR/$MODULE.hierarchy"; # This file contains the interfaces. my $INTERFACES_FILE = "$ROOT_DIR/$MODULE.interfaces"; # This file contains the prerequisites. my $PREREQUISITES_FILE = "$ROOT_DIR/$MODULE.prerequisites"; # This file contains signal arguments and names. my $SIGNALS_FILE = "$ROOT_DIR/$MODULE.signals"; # The file containing Arg information. my $ARGS_FILE = "$ROOT_DIR/$MODULE.args"; # These global arrays store information on signals. Each signal has an entry # in each of these arrays at the same index, like a multi-dimensional array. my @SignalObjects; # The GtkObject which emits the signal. my @SignalNames; # The signal name. my @SignalReturns; # The return type. my @SignalFlags; # Flags for the signal my @SignalPrototypes; # The rest of the prototype of the signal handler. # These global arrays store information on Args. Each Arg has an entry # in each of these arrays at the same index, like a multi-dimensional array. my @ArgObjects; # The GtkObject which has the Arg. my @ArgNames; # The Arg name. my @ArgTypes; # The Arg type - gint, GtkArrowType etc. my @ArgFlags; # How the Arg can be used - readable/writable etc. my @ArgNicks; # The nickname of the Arg. my @ArgBlurbs; # Docstring of the Arg. my @ArgDefaults; # Default value of the Arg. my @ArgRanges; # The range of the Arg type # These global hashes store declaration info keyed on a symbol name. my %Declarations; my %DeclarationTypes; my %DeclarationConditional; my %DeclarationOutput; my %Deprecated; my %Since; my %StabilityLevel; my %StructHasTypedef; # These global hashes store the existing documentation. my %SymbolDocs; my %SymbolTypes; my %SymbolParams; my %SymbolSourceFile; my %SymbolSourceLine; # These global hashes store documentation scanned from the source files. my %SourceSymbolDocs; my %SourceSymbolParams; my %SourceSymbolSourceFile; my %SourceSymbolSourceLine; # all documentation goes in here, so we can do coverage analysis my %AllSymbols; my %AllIncompleteSymbols; my %AllUnusedSymbols; my %AllDocumentedSymbols; # Undeclared yet documented symbols my %UndeclaredSymbols; # These global arrays store GObject, subclasses and the hierarchy. my @Objects; my @ObjectLevels; my %Interfaces; my %Prerequisites; # holds the symbols which are mentioned in $MODULE-sections.txt and in which # section they are defined my %KnownSymbols; my %SymbolSection; my %SymbolSectionId; # collects index entries my %IndexEntriesFull; my %IndexEntriesSince; my %IndexEntriesDeprecated; # Standard C preprocessor directives, which we ignore for '#' abbreviations. my %PreProcessorDirectives; $PreProcessorDirectives{"assert"} = 1; $PreProcessorDirectives{"define"} = 1; $PreProcessorDirectives{"elif"} = 1; $PreProcessorDirectives{"else"} = 1; $PreProcessorDirectives{"endif"} = 1; $PreProcessorDirectives{"error"} = 1; $PreProcessorDirectives{"if"} = 1; $PreProcessorDirectives{"ifdef"} = 1; $PreProcessorDirectives{"ifndef"} = 1; $PreProcessorDirectives{"include"} = 1; $PreProcessorDirectives{"line"} = 1; $PreProcessorDirectives{"pragma"} = 1; $PreProcessorDirectives{"unassert"} = 1; $PreProcessorDirectives{"undef"} = 1; $PreProcessorDirectives{"warning"} = 1; # remember used annotation (to write minimal glossary) my %AnnotationsUsed; # the annotations are defined at: # http://live.gnome.org/GObjectIntrospection/Annotations my %AnnotationDefinition = ( 'allow-none' => "NULL is ok, both for passing and for returning.", 'array' => "Parameter points to an array of items.", 'closure' => "This parameter is a 'user_data', for callbacks; many bindings can pass NULL here.", 'default' => "Default parameter value (for in case the shadows-to function has less parameters).", 'element-type' => "Generics and defining elements of containers and arrays.", 'error-domains' => "Typed errors. Similar to throws in Java.", 'in' => "Parameter for input. Default is transfer none.", 'inout' => "Parameter for input and for returning results. Default is transfer full.", 'in-out' => "Parameter for input and for returning results. Default is transfer full.", 'not-error' => "A GError parameter is not to be handled like a normal GError.", 'out' => "Parameter for returning results. Default is transfer full.", 'transfer container' => "Free data container after the code is done.", 'transfer full' => "Free data after the code is done.", 'transfer none' => "Don't free data after the code is done.", 'scope call' => "The callback is valid only during the call to the method.", 'scope async' => "The callback is valid until first called.", 'scope notified' => "The callback is valid until the GDestroyNotify argument is called.", 'type' => "Override the parsed C type with given type" ); # Create the root DocBook output directory if it doens't exist. if (! -e $SGML_OUTPUT_DIR) { mkdir ("$SGML_OUTPUT_DIR", 0777) || die "Can't create directory: $SGML_OUTPUT_DIR"; } # Function and other declaration output settings. my $RETURN_TYPE_FIELD_WIDTH = 20; my $SYMBOL_FIELD_WIDTH = 36; my $SIGNAL_FIELD_WIDTH = 16; my $PARAM_FIELD_COUNT = 2; &ReadKnownSymbols ("$ROOT_DIR/$MODULE-sections.txt"); &ReadSignalsFile ($SIGNALS_FILE); &ReadArgsFile ($ARGS_FILE); &ReadObjectHierarchy; &ReadInterfaces; &ReadPrerequisites; &ReadDeclarationsFile ("$ROOT_DIR/$MODULE-decl.txt", 0); if (-f "$ROOT_DIR/$MODULE-overrides.txt") { &ReadDeclarationsFile ("$ROOT_DIR/$MODULE-overrides.txt", 1); } for my $dir (@SOURCE_DIRS) { &ReadSourceDocumentation ($dir); } my $changed = &OutputSGML ("$ROOT_DIR/$MODULE-sections.txt"); # If any of the DocBook SGML files have changed, update the timestamp file (so # it can be used for Makefile dependencies). if ($changed || ! -e "$ROOT_DIR/sgml.stamp") { # try to detect the common prefix # GtkWidget, GTK_WIDGET, gtk_widget -> gtk if ($NAME_SPACE eq "") { $NAME_SPACE=""; my $pos=0; my $ratio=0.0; do { my %prefix; my $letter=""; foreach my $symbol (keys(%IndexEntriesFull)) { if(($NAME_SPACE eq "") || $symbol =~ /^$NAME_SPACE/i) { if (length($symbol)>$pos) { $letter=substr($symbol,$pos,1); # stop prefix scanning if ($letter eq "_") { # stop on "_" last; } # Should we also stop on a uppercase char, if last was lowercase # GtkWidget, if we have the 'W' and had the 't' before # or should we count upper and lowercase, and stop one 2nd uppercase, if we already had a lowercase # GtkWidget, the 'W' would be the 2nd uppercase and with 't','k' we had lowercase chars before # need to recound each time as this is per symbol $prefix{uc($letter)}++; } } } if ($letter ne "" && $letter ne "_") { my $maxletter=""; my $maxsymbols=0; foreach $letter (keys(%prefix)) { #print "$letter: $prefix{$letter}.\n"; if ($prefix{$letter}>$maxsymbols) { $maxletter=$letter; $maxsymbols=$prefix{$letter}; } } $ratio = scalar(keys(%IndexEntriesFull)) / $prefix{$maxletter}; #print "most symbols start with $maxletter, that is ". (100 * $ratio) ." %\n"; if ($ratio > 0.9) { # do another round $NAME_SPACE .= $maxletter; } $pos++; } else { $ratio=0.0; } } while ($ratio > 0.9); #print "most symbols start with $NAME_SPACE\n"; } &OutputIndexFull; &OutputDeprecatedIndex; &OutputSinceIndexes; &OutputAnnotationGlossary; open (TIMESTAMP, ">$ROOT_DIR/sgml.stamp") || die "Can't create $ROOT_DIR/sgml.stamp: $!"; print (TIMESTAMP "timestamp"); close (TIMESTAMP); } ############################################################################# # Function : OutputObjectList # Description : This outputs the alphabetical list of objects, in a columned # table. # FIXME: Currently this also outputs ancestor objects # which may not actually be in this module. # Arguments : none ############################################################################# sub OutputObjectList { my $cols = 3; # FIXME: use $OUTPUT_FORMAT # my $old_object_index = "$SGML_OUTPUT_DIR/object_index.$OUTPUT_FORMAT"; my $old_object_index = "$SGML_OUTPUT_DIR/object_index.sgml"; my $new_object_index = "$SGML_OUTPUT_DIR/object_index.new"; open (OUTPUT, ">$new_object_index") || die "Can't create $new_object_index: $!"; if ($OUTPUT_FORMAT eq "xml") { my $header = $doctype_header; $header =~ s/ EOF my $count = 0; my $object; foreach $object (sort (@Objects)) { my $xref = &MakeXRef ($object); if ($count % $cols == 0) { print (OUTPUT "\n"); } print (OUTPUT "$xref\n"); if ($count % $cols == ($cols - 1)) { print (OUTPUT "\n"); } $count++; } if ($count == 0) { # emit an empty row, since empty tables are invalid print (OUTPUT " \n"); } else { if ($count % $cols > 0) { print (OUTPUT "\n"); } } print (OUTPUT < EOF close (OUTPUT); &UpdateFileIfChanged ($old_object_index, $new_object_index, 0); } ############################################################################# # Function : OutputSGML # Description : This collects the output for each section of the docs, and # outputs each file when the end of the section is found. # Arguments : $file - the $MODULE-sections.txt file which contains all of # the functions/macros/structs etc. being documented, organised # into sections and subsections. ############################################################################# sub OutputSGML { my ($file) = @_; #print "Reading: $file\n"; open (INPUT, $file) || die "Can't open $file: $!"; my $filename = ""; my $book_top = ""; my $book_bottom = ""; my $includes = (defined $DEFAULT_INCLUDES) ? $DEFAULT_INCLUDES : ""; my $section_includes = ""; my $in_section = 0; my $title = ""; my $section_id = ""; my $subsection = ""; my $synopsis; my $details; my $num_symbols; my $changed = 0; my $signals_synop = ""; my $signals_desc = ""; my $args_synop = ""; my $child_args_synop = ""; my $style_args_synop = ""; my $args_desc = ""; my $child_args_desc = ""; my $style_args_desc = ""; my $hierarchy = ""; my $interfaces = ""; my $implementations = ""; my $prerequisites = ""; my $derived = ""; my @file_objects = (); my %templates = (); my %symbol_def_line = (); # merge the source docs, in case there are no templates &MergeSourceDocumentation; while () { if (m/^#/) { next; } elsif (m/^
/) { $synopsis = ""; $details = ""; $num_symbols = 0; $in_section = 1; @file_objects = (); %symbol_def_line = (); } elsif (m/^/i) { $synopsis .= "\n"; $subsection = $1; } elsif (m/^/) { } elsif (m/^(.*)<\/TITLE>/) { $title = $1; #print "Section: $title\n"; # We don't want warnings if object & class structs aren't used. $DeclarationOutput{$title} = 1; $DeclarationOutput{"${title}Class"} = 1; $DeclarationOutput{"${title}Iface"} = 1; $DeclarationOutput{"${title}Interface"} = 1; } elsif (m/^<FILE>(.*)<\/FILE>/) { $filename = $1; if (! defined $templates{$filename}) { if (&ReadTemplateFile ("$TMPL_DIR/$filename", 1)) { &MergeSourceDocumentation; $templates{$filename}=$.; } } else { &LogWarning ($file, $., "Double <FILE>$filename</FILE> entry. ". "Previous occurrence on line ".$templates{$filename}."."); } if (($title eq "") and (defined $SourceSymbolDocs{"$TMPL_DIR/$filename:Title"})) { $title = $SourceSymbolDocs{"$TMPL_DIR/$filename:Title"}; # Remove trailing blanks $title =~ s/\s+$//; } } elsif (m/^<INCLUDE>(.*)<\/INCLUDE>/) { if ($in_section) { $section_includes = $1; } else { if (defined $DEFAULT_INCLUDES) { &LogWarning ($file, $., "Default <INCLUDE> being overridden by command line option."); } else { $includes = $1; } } } elsif (m/^<\/SECTION>/) { #print "End of section: $title\n"; if ($num_symbols > 0) { # collect documents if ($OUTPUT_FORMAT eq "xml") { $book_bottom .= " <xi:include href=\"xml/$filename.xml\"/>\n"; } else { $book_top.="<!ENTITY $section_id SYSTEM \"sgml/$filename.sgml\">\n"; $book_bottom .= " &$section_id;\n"; } if (defined ($SourceSymbolDocs{"$TMPL_DIR/$filename:Include"})) { if ($section_includes) { &LogWarning ($file, $., "Section <INCLUDE> being overridden by inline comments."); } $section_includes = $SourceSymbolDocs{"$TMPL_DIR/$filename:Include"}; } if ($section_includes eq "") { $section_includes = $includes; } $signals_synop =~ s/^\n*//g; $signals_synop =~ s/\n+$/\n/g; if ($signals_synop ne '') { $signals_synop = <<EOF; <refsect1 id="$section_id.signals" role="signal_proto"> <title role="signal_proto.title">Signals ${signals_synop} EOF $signals_desc =~ s/^\n*//g; $signals_desc =~ s/\n+$/\n/g; $signals_desc =~ s/(\s|\n)+$//ms; $signals_desc = < Signal Details $signals_desc EOF } $args_synop =~ s/^\n*//g; $args_synop =~ s/\n+$/\n/g; if ($args_synop ne '') { $args_synop = < Properties ${args_synop} EOF $args_desc =~ s/^\n*//g; $args_desc =~ s/\n+$/\n/g; $args_desc =~ s/(\s|\n)+$//ms; $args_desc = < Property Details $args_desc EOF } $child_args_synop =~ s/^\n*//g; $child_args_synop =~ s/\n+$/\n/g; if ($child_args_synop ne '') { $args_synop .= < Child Properties ${child_args_synop} EOF $child_args_desc =~ s/^\n*//g; $child_args_desc =~ s/\n+$/\n/g; $child_args_desc =~ s/(\s|\n)+$//ms; $args_desc .= < Child Property Details $child_args_desc EOF } $style_args_synop =~ s/^\n*//g; $style_args_synop =~ s/\n+$/\n/g; if ($style_args_synop ne '') { $args_synop .= < Style Properties ${style_args_synop} EOF $style_args_desc =~ s/^\n*//g; $style_args_desc =~ s/\n+$/\n/g; $style_args_desc =~ s/(\s|\n)+$//ms; $args_desc .= < Style Property Details $style_args_desc EOF } $hierarchy =~ s/^\n*//g; $hierarchy =~ s/\n+$/\n/g; $hierarchy =~ s/(\s|\n)+$//ms; if ($hierarchy ne "") { $hierarchy = < Object Hierarchy $hierarchy EOF } $interfaces =~ s/^\n*//g; $interfaces =~ s/\n+$/\n/g; $interfaces =~ s/(\s|\n)+$//ms; if ($interfaces ne "") { $interfaces = < Implemented Interfaces $interfaces EOF } $implementations =~ s/^\n*//g; $implementations =~ s/\n+$/\n/g; $implementations =~ s/(\s|\n)+$//ms; if ($implementations ne "") { $implementations = < Known Implementations $implementations EOF } $prerequisites =~ s/^\n*//g; $prerequisites =~ s/\n+$/\n/g; $prerequisites =~ s/(\s|\n)+$//ms; if ($prerequisites ne "") { $prerequisites = < Prerequisites $prerequisites EOF } $derived =~ s/^\n*//g; $derived =~ s/\n+$/\n/g; $derived =~ s/(\s|\n)+$//ms; if ($derived ne "") { $derived = < Known Derived Interfaces $derived EOF } $synopsis =~ s/^\n*//g; $synopsis =~ s/\n+$/\n/g; my $file_changed = &OutputSGMLFile ($filename, $title, $section_id, $section_includes, \$synopsis, \$details, \$signals_synop, \$signals_desc, \$args_synop, \$args_desc, \$hierarchy, \$interfaces, \$implementations, \$prerequisites, \$derived, \@file_objects); if ($file_changed) { $changed = 1; } } $title = ""; $section_id = ""; $subsection = ""; $in_section = 0; $section_includes = ""; $signals_synop = ""; $signals_desc = ""; $args_synop = ""; $child_args_synop = ""; $style_args_synop = ""; $args_desc = ""; $child_args_desc = ""; $style_args_desc = ""; $hierarchy = ""; $interfaces = ""; $implementations = ""; $prerequisites = ""; $derived = ""; } elsif (m/^(\S+)/) { my $symbol = $1; #print " Symbol: $symbol\n"; # check for duplicate entries if (! defined $symbol_def_line{$symbol}) { my $declaration = $Declarations{$symbol}; if (defined ($declaration)) { # We don't want standard macros/functions of GObjects, # or private declarations. if ($subsection ne "Standard" && $subsection ne "Private") { if (&CheckIsObject ($symbol)) { push @file_objects, $symbol; } my ($synop, $desc) = &OutputDeclaration ($symbol, $declaration); my ($sig_synop, $sig_desc) = &GetSignals ($symbol); my ($arg_synop, $child_arg_synop, $style_arg_synop, $arg_desc, $child_arg_desc, $style_arg_desc) = &GetArgs ($symbol); my $hier = &GetHierarchy ($symbol); my $ifaces = &GetInterfaces ($symbol); my $impls = &GetImplementations ($symbol); my $prereqs = &GetPrerequisites ($symbol); my $der = &GetDerived ($symbol); $synopsis .= $synop; $details .= $desc; $signals_synop .= $sig_synop; $signals_desc .= $sig_desc; $args_synop .= $arg_synop; $child_args_synop .= $child_arg_synop; $style_args_synop .= $style_arg_synop; $args_desc .= $arg_desc; $child_args_desc .= $child_arg_desc; $style_args_desc .= $style_arg_desc; $hierarchy .= $hier; $interfaces .= $ifaces; $implementations .= $impls; $prerequisites .= $prereqs; $derived .= $der; } # Note that the declaration has been output. $DeclarationOutput{$symbol} = 1; } elsif ($subsection ne "Standard" && $subsection ne "Private") { $UndeclaredSymbols{$symbol} = 1; &LogWarning ($file, $., "No declaration found for $symbol."); } $num_symbols++; $symbol_def_line{$symbol}=$.; if ($section_id eq "") { if($title eq "" && $filename eq "") { &LogWarning ($file, $., "Section has no title and no file."); } # FIXME: one of those would be enough # filename should be an internal detail for gtk-doc if ($title eq "") { $title = $filename; } elsif ($filename eq "") { $filename = $title; } $filename =~ s/\s/_/g; $section_id = $SourceSymbolDocs{"$TMPL_DIR/$filename:Section_Id"}; if (defined ($section_id) && $section_id !~ m/^\s*$/) { # Remove trailing blanks and use as is $section_id =~ s/\s+$//; } elsif (&CheckIsObject ($title)) { # GObjects use their class name as the ID. $section_id = &CreateValidSGMLID ($title); } else { $section_id = &CreateValidSGMLID ("$MODULE-$title"); } } $SymbolSection{$symbol}=$title; $SymbolSectionId{$symbol}=$section_id; } else { &LogWarning ($file, $., "Double symbol entry for $symbol. ". "Previous occurrence on line ".$symbol_def_line{$symbol}."."); } } } close (INPUT); &OutputMissingDocumentation; &OutputUndeclaredSymbols; &OutputUnusedSymbols; if ($OUTPUT_ALL_SYMBOLS) { &OutputAllSymbols; } if ($OUTPUT_SYMBOLS_WITHOUT_SINCE) { &OutputSymbolsWithoutSince; } for $filename (split (' ', $EXPAND_CONTENT_FILES)) { my $file_changed = &OutputExtraFile ($filename); if ($file_changed) { $changed = 1; } } &OutputBook ($book_top, $book_bottom); return $changed; } ############################################################################# # Function : OutputIndex # Description : This writes an indexlist that can be included into the main- # document into an tag. ############################################################################# sub OutputIndex { my ($basename, $apiindexref ) = @_; my %apiindex = %{$apiindexref}; my $old_index = "$SGML_OUTPUT_DIR/$basename.xml"; my $new_index = "$SGML_OUTPUT_DIR/$basename.new"; my $lastletter = " "; my $divopen = 0; my $symbol; my $short_symbol; open (OUTPUT, ">$new_index") || die "Can't create $new_index"; my $header = $doctype_header; $header =~ s/\n"); #print "generate $basename index (".%apiindex." entries)\n"; # do a case insensitive sort while chopping off the prefix foreach my $hash ( sort { $$a{criteria} cmp $$b{criteria} } map { my $x = uc($_); $x =~ s/^$NAME_SPACE\_?(.*)/$1/i; { criteria => $x, original => $_, short => $1 } } keys %apiindex) { $symbol = $$hash{original}; if (defined($$hash{short})) { $short_symbol = $$hash{short}; } else { $short_symbol = $symbol; } # generate a short symbol description my $symbol_desc = ""; my $symbol_section = ""; my $symbol_section_id = ""; my $symbol_type = ""; if (defined($DeclarationTypes{$symbol})) { $symbol_type = lc($DeclarationTypes{$symbol}); } if ($symbol_type eq "") { #print "trying symbol $symbol\n"; if ($symbol =~ m/(.*)::(.*)/) { my $oname = $1; my $osym = $2; my $i; #print " trying object signal ${oname}:$osym in ".$#SignalNames." signals\n"; for ($i = 0; $i <= $#SignalNames; $i++) { if ($SignalNames[$i] eq $osym) { $symbol_type = "object signal"; if (defined($SymbolSection{$oname})) { $symbol_section = $SymbolSection{$oname}; $symbol_section_id = $SymbolSectionId{$oname}; } last; } } } elsif ($symbol =~ m/(.*):(.*)/) { my $oname = $1; my $osym = $2; my $i; #print " trying object property ${oname}::$osym in ".$#ArgNames." properties\n"; for ($i = 0; $i <= $#ArgNames; $i++) { #print " ".$ArgNames[$i]."\n"; if ($ArgNames[$i] eq $osym) { $symbol_type = "object property"; if (defined($SymbolSection{$oname})) { $symbol_section = $SymbolSection{$oname}; $symbol_section_id = $SymbolSectionId{$oname}; } last; } } } } else { if (defined($SymbolSection{$symbol})) { $symbol_section = $SymbolSection{$symbol}; $symbol_section_id = $SymbolSectionId{$symbol}; } } if ($symbol_type ne "") { $symbol_desc=", $symbol_type"; if ($symbol_section ne "") { $symbol_desc.=" in $symbol_section"; #$symbol_desc.=" in ". &ExpandAbbreviations($symbol, "#$symbol_section"); } } my $curletter = uc(substr($short_symbol,0,1)); my $id = $apiindex{$symbol}; #print " add symbol $symbol with $id to index in section $curletter\n"; if ($curletter ne $lastletter) { $lastletter = $curletter; if ($divopen == 1) { print (OUTPUT "\n"); } print (OUTPUT "$curletter\n"); $divopen = 1; } print (OUTPUT <$symbol$symbol_desc EOF } if ($divopen == 1) { print (OUTPUT "\n"); } print (OUTPUT "\n"); close (OUTPUT); &UpdateFileIfChanged ($old_index, $new_index, 0); } ############################################################################# # Function : OutputIndexFull # Description : This writes the full api indexlist that can be included into the # main document into an tag. ############################################################################# sub OutputIndexFull { &OutputIndex ("api-index-full", \%IndexEntriesFull); } ############################################################################# # Function : OutputDeprecatedIndex # Description : This writes the deprecated api indexlist that can be included # into the main document into an tag. ############################################################################# sub OutputDeprecatedIndex { &OutputIndex ("api-index-deprecated", \%IndexEntriesDeprecated); } ############################################################################# # Function : OutputSinceIndexes # Description : This writes the 'since' api indexlists that can be included into # the main document into an tag. ############################################################################# sub OutputSinceIndexes { my @sinces = keys %{{ map { $_ => 1 } values %Since }}; foreach my $version (@sinces) { #print "Since : [$version]\n"; # TODO make filtered hash #my %index = grep { $Since{$_} eq $version } %IndexEntriesSince; my %index = map { $_ => $IndexEntriesSince{$_} } grep { $Since{$_} eq $version } keys %IndexEntriesSince; &OutputIndex ("api-index-$version", \%index); } } ############################################################################# # Function : OutputAnnotationGlossary # Description : This writes a glossary of the used annotation terms into a # separate glossary file that can be included into the main # document. ############################################################################# sub OutputAnnotationGlossary { my $old_glossary = "$SGML_OUTPUT_DIR/annotation-glossary.xml"; my $new_glossary = "$SGML_OUTPUT_DIR/annotation-glossary.new"; my $lastletter = " "; my $divopen = 0; # if there are no annotations used return return if (! keys(%AnnotationsUsed)); # add acronyms that are referenced from acronym text rerun: foreach my $annotation (keys(%AnnotationsUsed)) { if(defined($AnnotationDefinition{$annotation})) { if($AnnotationDefinition{$annotation} =~ m/([\w ]+)<\/acronym>/) { if (!exists($AnnotationsUsed{$1})) { $AnnotationsUsed{$1} = 1; goto rerun; } } } } open (OUTPUT, ">$new_glossary") || die "Can't create $new_glossary"; my $header = $doctype_header; $header =~ s/ Annotation Glossary EOF foreach my $annotation (keys(%AnnotationsUsed)) { if(defined($AnnotationDefinition{$annotation})) { my $def = $AnnotationDefinition{$annotation}; my $curletter = uc(substr($annotation,0,1)); if ($curletter ne $lastletter) { $lastletter = $curletter; if ($divopen == 1) { print (OUTPUT "\n"); } print (OUTPUT "$curletter\n"); $divopen = 1; } print (OUTPUT < $annotation $def EOF } } if ($divopen == 1) { print (OUTPUT "\n"); } print (OUTPUT "\n"); close (OUTPUT); &UpdateFileIfChanged ($old_glossary, $new_glossary, 0); } ############################################################################# # Function : ReadKnownSymbols # Description : This collects the names of non-private symbols from the # $MODULE-sections.txt file. # Arguments : $file - the $MODULE-sections.txt file which contains all of # the functions/macros/structs etc. being documented, organised # into sections and subsections. ############################################################################# sub ReadKnownSymbols { my ($file) = @_; my $subsection = ""; #print "Reading: $file\n"; open (INPUT, $file) || die "Can't open $file: $!"; while () { if (m/^#/) { next; } elsif (m/^
/) { $subsection = ""; } elsif (m/^/i) { $subsection = $1; } elsif (m/^/) { next; } elsif (m/^(.*)<\/TITLE>/) { next; } elsif (m/^<FILE>(.*)<\/FILE>/) { $KnownSymbols{"$TMPL_DIR/$1:Long_Description"} = 1; $KnownSymbols{"$TMPL_DIR/$1:Short_Description"} = 1; next; } elsif (m/^<INCLUDE>(.*)<\/INCLUDE>/) { next; } elsif (m/^<\/SECTION>/) { next; } elsif (m/^(\S+)/) { my $symbol = $1; if ($subsection ne "Standard" && $subsection ne "Private") { $KnownSymbols{$symbol} = 1; } else { $KnownSymbols{$symbol} = 0; } } } close (INPUT); } ############################################################################# # Function : OutputDeclaration # Description : Returns the synopsis and detailed description DocBook # describing one function/macro etc. # Arguments : $symbol - the name of the function/macro begin described. # $declaration - the declaration of the function/macro. ############################################################################# sub OutputDeclaration { my ($symbol, $declaration) = @_; my $type = $DeclarationTypes {$symbol}; if ($type eq 'MACRO') { return &OutputMacro ($symbol, $declaration); } elsif ($type eq 'TYPEDEF') { return &OutputTypedef ($symbol, $declaration); } elsif ($type eq 'STRUCT') { return &OutputStruct ($symbol, $declaration); } elsif ($type eq 'ENUM') { return &OutputEnum ($symbol, $declaration); } elsif ($type eq 'UNION') { return &OutputUnion ($symbol, $declaration); } elsif ($type eq 'VARIABLE') { return &OutputVariable ($symbol, $declaration); } elsif ($type eq 'FUNCTION') { return &OutputFunction ($symbol, $declaration, $type); } elsif ($type eq 'USER_FUNCTION') { return &OutputFunction ($symbol, $declaration, $type); } else { die "Unknown symbol type"; } } ############################################################################# # Function : OutputSymbolTraits # Description : Returns the Since and StabilityLevel paragraphs for a symbol. # Arguments : $symbol - the name of the function/macro begin described. ############################################################################# sub OutputSymbolTraits { my ($symbol) = @_; my $desc = ""; if (exists $Since{$symbol}) { $desc .= "<para role=\"since\">Since $Since{$symbol}</para>"; } if (exists $StabilityLevel{$symbol}) { $desc .= "<para role=\"stability\">Stability Level: $StabilityLevel{$symbol}</para>"; } return $desc; } ############################################################################# # Function : Outpu{Symbol,Section}ExtraLinks # Description : Returns extralinks for the symbol (if enabled). # Arguments : $symbol - the name of the function/macro begin described. ############################################################################# sub uri_escape { my $text = $_[0]; return undef unless defined $text; # Build a char to hex map my %escapes = (); for (0..255) { $escapes{chr($_)} = sprintf("%%%02X", $_); } # Default unsafe characters. RFC 2732 ^(uric - reserved) $text =~ s/([^A-Za-z0-9\-_.!~*'()])/$escapes{$1}/g; return $text; } sub OutputSymbolExtraLinks { my ($symbol) = @_; my $desc = ""; if (0) { # NEW FEATURE: needs configurability my $sstr = &uri_escape($symbol); my $mstr = &uri_escape($MODULE); $desc .= <<EOF; <ulink role="extralinks" url="http://www.google.com/codesearch?q=$sstr">code search</ulink> <ulink role="extralinks" url="http://library.gnome.org/edit?module=$mstr&symbol=$sstr">edit documentation</ulink> EOF } return $desc; } sub OutputSectionExtraLinks { my ($symbol,$docsymbol) = @_; my $desc = ""; if (0) { # NEW FEATURE: needs configurability my $sstr = &uri_escape($symbol); my $mstr = &uri_escape($MODULE); my $dsstr = &uri_escape($docsymbol); $desc .= <<EOF; <ulink role="extralinks" url="http://www.google.com/codesearch?q=$sstr">code search</ulink> <ulink role="extralinks" url="http://library.gnome.org/edit?module=$mstr&symbol=$dsstr">edit documentation</ulink> EOF } return $desc; } ############################################################################# # Function : OutputMacro # Description : Returns the synopsis and detailed description of a macro. # Arguments : $symbol - the macro. # $declaration - the declaration of the macro. ############################################################################# sub OutputMacro { my ($symbol, $declaration) = @_; my $id = &CreateValidSGMLID ($symbol); my $condition = &MakeConditionDescription ($symbol); my $synop = &MakeReturnField("#define") . "<link linkend=\"$id\">$symbol</link>"; my $desc; my @fields = ParseMacroDeclaration($declaration, \&CreateValidSGML); my $title = $symbol . (@fields ? "()" : ""); $desc = "<refsect2 id=\"$id\" role=\"macro\"$condition>\n<title>$title\n"; $desc .= MakeIndexterms($symbol, $id); $desc .= "\n"; $desc .= OutputSymbolExtraLinks($symbol); if (@fields) { if (length ($symbol) < $SYMBOL_FIELD_WIDTH) { $synop .= (' ' x ($SYMBOL_FIELD_WIDTH - length ($symbol))); } $synop .= "("; for (my $i = 1; $i <= $#fields; $i += 2) { my $field_name = $fields[$i]; if ($i == 1) { $synop .= "$field_name"; } else { $synop .= ",\n" . (' ' x ($SYMBOL_FIELD_WIDTH + $RETURN_TYPE_FIELD_WIDTH)) . " $field_name"; } } $synop .= ")"; } $synop .= "\n"; # Don't output the macro definition if is is a conditional macro or it # looks like a function, i.e. starts with "g_" or "_?gnome_", or it is # longer than 2 lines, otherwise we get lots of complicated macros like # g_assert. if (!defined ($DeclarationConditional{$symbol}) && ($symbol !~ m/^g_/) && ($symbol !~ m/^_?gnome_/) && (($declaration =~ tr/\n//) < 2)) { my $decl_out = &CreateValidSGML ($declaration); $desc .= "$decl_out\n"; } else { $desc .= "" . &MakeReturnField("#define") . "$symbol"; if ($declaration =~ m/^\s*#\s*define\s+\w+(\([^\)]*\))/) { my $args = $1; my $pad = ' ' x ($RETURN_TYPE_FIELD_WIDTH - length ("#define ")); # Align each line so that if should all line up OK. $args =~ s/\n/\n$pad/gm; $desc .= &CreateValidSGML ($args); } $desc .= "\n"; } $desc .= &MakeDeprecationNote($symbol); my $parameters = &OutputParamDescriptions ("MACRO", $symbol, @fields); my $parameters_output = 0; if (defined ($SymbolDocs{$symbol})) { my $symbol_docs = &ExpandAbbreviations($symbol, $SymbolDocs{$symbol}); # Try to insert the parameter table at the author's desired position. # Otherwise we need to tag it onto the end. if ($symbol_docs =~ s//$parameters/) { $parameters_output = 1; } $desc .= $symbol_docs; } if ($parameters_output == 0) { $desc .= $parameters; } $desc .= OutputSymbolTraits ($symbol); $desc .= "\n"; return ($synop, $desc); } ############################################################################# # Function : OutputTypedef # Description : Returns the synopsis and detailed description of a typedef. # Arguments : $symbol - the typedef. # $declaration - the declaration of the typedef, # e.g. 'typedef unsigned int guint;' ############################################################################# sub OutputTypedef { my ($symbol, $declaration) = @_; my $id = &CreateValidSGMLID ($symbol); my $condition = &MakeConditionDescription ($symbol); my $synop = &MakeReturnField("typedef") . "$symbol;\n"; my $desc = "\n$symbol\n"; $desc .= MakeIndexterms($symbol, $id); $desc .= "\n"; $desc .= OutputSymbolExtraLinks($symbol); if (!defined ($DeclarationConditional{$symbol})) { my $decl_out = &CreateValidSGML ($declaration); $desc .= "$decl_out\n"; } $desc .= &MakeDeprecationNote($symbol); if (defined ($SymbolDocs{$symbol})) { $desc .= &ExpandAbbreviations($symbol, $SymbolDocs{$symbol}); } $desc .= OutputSymbolTraits ($symbol); $desc .= "\n"; return ($synop, $desc); } ############################################################################# # Function : OutputStruct # Description : Returns the synopsis and detailed description of a struct. # We check if it is a object struct, and if so we only output # parts of it that are noted as public fields. # We also use a different SGML ID for object structs, since the # original ID is used for the entire RefEntry. # Arguments : $symbol - the struct. # $declaration - the declaration of the struct. ############################################################################# sub OutputStruct { my ($symbol, $declaration) = @_; my $is_object_struct = 0; my $default_to_public = 1; if (&CheckIsObject ($symbol)) { #print "Found object struct: $symbol\n"; $is_object_struct = 1; $default_to_public = 0; } my $id; my $condition; if ($is_object_struct) { $id = &CreateValidSGMLID ($symbol . "_struct"); $condition = &MakeConditionDescription ($symbol . "_struct"); } else { $id = &CreateValidSGMLID ($symbol); $condition = &MakeConditionDescription ($symbol); } # Determine if it is a simple struct or it also has a typedef. my $has_typedef = 0; if ($StructHasTypedef{$symbol} || $declaration =~ m/^\s*typedef\s+/) { $has_typedef = 1; } my $synop; my $desc; if ($has_typedef) { # For structs with typedefs we just output the struct name. $synop = &MakeReturnField("") . "$symbol;\n"; $desc = "\n$symbol\n"; } else { $synop = &MakeReturnField("struct") . "$symbol;\n"; $desc = "\nstruct $symbol\n"; } $desc .= MakeIndexterms($symbol, $id); $desc .= "\n"; $desc .= OutputSymbolExtraLinks($symbol); # Form a pretty-printed, private-data-removed form of the declaration my $decl_out = ""; if ($declaration =~ m/^\s*$/) { #print "Found opaque struct: $symbol\n"; $decl_out = "typedef struct _$symbol $symbol;"; } elsif ($declaration =~ m/^\s*struct\s+\w+\s*;\s*$/) { #print "Found opaque struct: $symbol\n"; $decl_out = "struct $symbol;"; } else { my $public = $default_to_public; my $new_declaration = ""; my $decl_line; my $decl = $declaration; if ($decl =~ m/^\s*(typedef\s+)?struct\s*\w*\s*(?:\/\*.*\*\/)?\s*{(.*)}\s*\w*\s*;\s*$/s) { my $struct_contents = $2; foreach $decl_line (split (/\n/, $struct_contents)) { #print "Struct line: $decl_line\n"; if ($decl_line =~ m%/\*\s*<\s*public\s*>\s*\*/%) { $public = 1; } elsif ($decl_line =~ m%/\*\s*<\s*(private|protected)\s*>\s*\*/%) { $public = 0; } elsif ($public) { $new_declaration .= $decl_line . "\n"; } } if ($new_declaration) { # Strip any blank lines off the ends. $new_declaration =~ s/^\s*\n//; $new_declaration =~ s/\n\s*$/\n/; if ($has_typedef) { $decl_out = "typedef struct {\n" . $new_declaration . "} $symbol;\n"; } else { $decl_out = "struct $symbol {\n" . $new_declaration . "};\n"; } } } else { &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol), "Couldn't parse struct:\n$declaration"); } # If we couldn't parse the struct or it was all private, output an # empty struct declaration. if ($decl_out eq "") { if ($has_typedef) { $decl_out = "typedef struct _$symbol $symbol;"; } else { $decl_out = "struct $symbol;"; } } } $decl_out = &CreateValidSGML ($decl_out); $desc .= "$decl_out\n"; $desc .= &MakeDeprecationNote($symbol); if (defined ($SymbolDocs{$symbol})) { $desc .= &ExpandAbbreviations($symbol, $SymbolDocs{$symbol}); } # Create a table of fields and descriptions # FIXME: Inserting  's into the produced type declarations here would # improve the output in most situations ... except for function # members of structs! my @fields = ParseStructDeclaration($declaration, !$default_to_public, 0, \&MakeXRef, sub { "$_[0]"; }); my $params = $SymbolParams{$symbol}; # If no parameters are filled in, we don't generate the description # table, for backwards compatibility my $found = 0; if (defined $params) { for (my $i = 1; $i <= $#$params; $i += $PARAM_FIELD_COUNT) { if ($params->[$i] =~ /\S/) { $found = 1; last; } } } if ($found) { my %field_descrs = @$params; my $missing_parameters = ""; my $unused_parameters = ""; $desc .= "\n"; while (@fields) { my $field_name = shift @fields; my $text = shift @fields; my $field_descr = $field_descrs{$field_name}; my $param_annotations = ""; $desc .= "$text\n"; if (defined $field_descr) { ($field_descr,$param_annotations) = &ExpandAnnotation($symbol, $field_descr); $field_descr = &ExpandAbbreviations($symbol, $field_descr); $field_descr .= $param_annotations; # trim $field_descr =~ s/^(\s|\n)+//msg; $field_descr =~ s/(\s|\n)+$//msg; $desc .= "$field_descr\n"; delete $field_descrs{$field_name}; } else { &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol), "Field description for $symbol"."::"."$field_name is missing in source code comment block."); if ($missing_parameters ne "") { $missing_parameters .= ", ".$field_name; } else { $missing_parameters = $field_name; } $desc .= "\n"; } $desc .= "\n"; } $desc .= ""; foreach my $field_name (keys %field_descrs) { &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol), "Field description for $symbol"."::"."$field_name is not used from source code comment block."); if ($unused_parameters ne "") { $unused_parameters .= ", ".$field_name; } else { $unused_parameters = $field_name; } } # remember missing/unused parameters (needed in tmpl-free build) if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) { $AllIncompleteSymbols{$symbol}=$missing_parameters; } if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) { $AllUnusedSymbols{$symbol}=$unused_parameters; } } else { if (scalar(@fields) > 0) { if (! exists ($AllIncompleteSymbols{$symbol})) { $AllIncompleteSymbols{$symbol}=""; &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol), "Field descriptions for $symbol are missing in source code comment block."); } } } $desc .= OutputSymbolTraits ($symbol); $desc .= "\n"; return ($synop, $desc); } ############################################################################# # Function : OutputUnion # Description : Returns the synopsis and detailed description of a union. # Arguments : $symbol - the union. # $declaration - the declaration of the union. ############################################################################# sub OutputUnion { my ($symbol, $declaration) = @_; my $id = &CreateValidSGMLID ($symbol); my $condition = &MakeConditionDescription ($symbol); # Determine if it is a simple struct or it also has a typedef. my $has_typedef = 0; if ($StructHasTypedef{$symbol} || $declaration =~ m/^\s*typedef\s+/) { $has_typedef = 1; } my $synop; my $desc; if ($has_typedef) { # For unions with typedefs we just output the union name. $synop = &MakeReturnField("") . "$symbol;\n"; $desc = "\n$symbol\n"; } else { $synop = &MakeReturnField("union") . "$symbol;\n"; $desc = "\nunion $symbol\n"; } $desc .= MakeIndexterms($symbol, $id); $desc .= "\n"; $desc .= OutputSymbolExtraLinks($symbol); # FIXME: we do more for structs my $decl_out = &CreateValidSGML ($declaration); $desc .= "$decl_out\n"; $desc .= &MakeDeprecationNote($symbol); if (defined ($SymbolDocs{$symbol})) { $desc .= &ExpandAbbreviations($symbol, $SymbolDocs{$symbol}); } # Create a table of fields and descriptions # FIXME: Inserting  's into the produced type declarations here would # improve the output in most situations ... except for function # members of structs! my @fields = ParseStructDeclaration($declaration, 0, 0, \&MakeXRef, sub { "$_[0]"; }); my $params = $SymbolParams{$symbol}; # If no parameters are filled in, we don't generate the description # table, for backwards compatibility my $found = 0; if (defined $params) { for (my $i = 1; $i <= $#$params; $i += $PARAM_FIELD_COUNT) { if ($params->[$i] =~ /\S/) { $found = 1; last; } } } if ($found) { my %field_descrs = @$params; my $missing_parameters = ""; my $unused_parameters = ""; $desc .= "\n"; while (@fields) { my $field_name = shift @fields; my $text = shift @fields; my $field_descr = $field_descrs{$field_name}; my $param_annotations = ""; $desc .= "$text\n"; if (defined $field_descr) { ($field_descr,$param_annotations) = &ExpandAnnotation($symbol, $field_descr); $field_descr = &ExpandAbbreviations($symbol, $field_descr); $field_descr .= $param_annotations; # trim $field_descr =~ s/^(\s|\n)+//msg; $field_descr =~ s/(\s|\n)+$//msg; $desc .= "$field_descr\n"; delete $field_descrs{$field_name}; } else { &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol), "Field description for $symbol"."::"."$field_name is missing in source code comment block."); if ($missing_parameters ne "") { $missing_parameters .= ", ".$field_name; } else { $missing_parameters = $field_name; } $desc .= "\n"; } $desc .= "\n"; } $desc .= ""; foreach my $field_name (keys %field_descrs) { &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol), "Field description for $symbol"."::"."$field_name is not used from source code comment block."); if ($unused_parameters ne "") { $unused_parameters .= ", ".$field_name; } else { $unused_parameters = $field_name; } } # remember missing/unused parameters (needed in tmpl-free build) if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) { $AllIncompleteSymbols{$symbol}=$missing_parameters; } if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) { $AllUnusedSymbols{$symbol}=$unused_parameters; } } else { if (scalar(@fields) > 0) { if (! exists ($AllIncompleteSymbols{$symbol})) { $AllIncompleteSymbols{$symbol}=""; &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol), "Field descriptions for $symbol are missing in source code comment block."); } } } $desc .= OutputSymbolTraits ($symbol); $desc .= "\n"; return ($synop, $desc); } ############################################################################# # Function : OutputEnum # Description : Returns the synopsis and detailed description of a enum. # Arguments : $symbol - the enum. # $declaration - the declaration of the enum. ############################################################################# sub OutputEnum { my ($symbol, $declaration) = @_; my $id = &CreateValidSGMLID ($symbol); my $condition = &MakeConditionDescription ($symbol); my $synop = &MakeReturnField("enum") . "$symbol;\n"; my $desc = "\nenum $symbol\n"; $desc .= MakeIndexterms($symbol, $id); $desc .= "\n"; $desc .= OutputSymbolExtraLinks($symbol); my $decl_out = &CreateValidSGML ($declaration); $desc .= "$decl_out\n"; $desc .= &MakeDeprecationNote($symbol); if (defined ($SymbolDocs{$symbol})) { $desc .= &ExpandAbbreviations($symbol, $SymbolDocs{$symbol}); } # Create a table of fields and descriptions my @fields = ParseEnumDeclaration($declaration); my $params = $SymbolParams{$symbol}; # If no parameters are filled in, we don't generate the description # table, for backwards compatibility my $found = 0; if (defined $params) { for (my $i = 1; $i <= $#$params; $i += $PARAM_FIELD_COUNT) { if ($params->[$i] =~ /\S/) { $found = 1; last; } } } if ($found) { my %field_descrs = @$params; my $missing_parameters = ""; my $unused_parameters = ""; $desc .= "\n"; for my $field_name (@fields) { my $field_descr = $field_descrs{$field_name}; my $param_annotations = ""; $id = &CreateValidSGMLID ($field_name); $condition = &MakeConditionDescription ($field_name); $desc .= "\n$field_name\n"; if (defined $field_descr) { ($field_descr,$param_annotations) = &ExpandAnnotation($symbol, $field_descr); $field_descr = &ExpandAbbreviations($symbol, $field_descr); $desc .= "$field_descr$param_annotations\n"; delete $field_descrs{$field_name}; } else { &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol), "Value description for $symbol"."::"."$field_name is missing in source code comment block."); if ($missing_parameters ne "") { $missing_parameters .= ", ".$field_name; } else { $missing_parameters = $field_name; } $desc .= "\n"; } $desc .= "\n"; } $desc .= ""; foreach my $field_name (keys %field_descrs) { &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol), "Value description for $symbol"."::"."$field_name is not used from source code comment block."); if ($unused_parameters ne "") { $unused_parameters .= ", ".$field_name; } else { $unused_parameters = $field_name; } } # remember missing/unused parameters (needed in tmpl-free build) if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) { $AllIncompleteSymbols{$symbol}=$missing_parameters; } if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) { $AllUnusedSymbols{$symbol}=$unused_parameters; } } else { if (scalar(@fields) > 0) { if (! exists ($AllIncompleteSymbols{$symbol})) { $AllIncompleteSymbols{$symbol}=""; &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol), "Value descriptions for $symbol are missing in source code comment block."); } } } $desc .= OutputSymbolTraits ($symbol); $desc .= "\n"; return ($synop, $desc); } ############################################################################# # Function : OutputVariable # Description : Returns the synopsis and detailed description of a variable. # Arguments : $symbol - the extern'ed variable. # $declaration - the declaration of the variable. ############################################################################# sub OutputVariable { my ($symbol, $declaration) = @_; my $id = &CreateValidSGMLID ($symbol); my $condition = &MakeConditionDescription ($symbol); my $synop; if ($declaration =~ m/^\s*extern\s+((const\s+|signed\s+|unsigned\s+)*\w+)(\s+\*+|\*+|\s)(\s*)([A-Za-z]\w*)\s*;/) { my $mod = defined ($1) ? $1 : ""; my $ptr = defined ($3) ? $3 : ""; my $space = defined ($4) ? $4 : ""; $synop = &MakeReturnField("extern") . "$mod$ptr$space$symbol;\n"; } else { $synop = &MakeReturnField("extern") . "$symbol;\n"; } my $desc = "\n$symbol\n"; $desc .= MakeIndexterms($symbol, $id); $desc .= "\n"; $desc .= OutputSymbolExtraLinks($symbol); my $decl_out = &CreateValidSGML ($declaration); $desc .= "$decl_out\n"; $desc .= &MakeDeprecationNote($symbol); if (defined ($SymbolDocs{$symbol})) { $desc .= &ExpandAbbreviations($symbol, $SymbolDocs{$symbol}); } $desc .= OutputSymbolTraits ($symbol); $desc .= "\n"; return ($synop, $desc); } ############################################################################# # Function : OutputFunction # Description : Returns the synopsis and detailed description of a function. # Arguments : $symbol - the function. # $declaration - the declaration of the function. ############################################################################# sub OutputFunction { my ($symbol, $declaration, $symbol_type) = @_; my $id = &CreateValidSGMLID ($symbol); my $condition = &MakeConditionDescription ($symbol); # Take out the return type $1 $3 $4 $declaration =~ s/\s*((const\s+|G_CONST_RETURN\s+|signed\s+|unsigned\s+|long\s+|short\s+|struct\s+|enum\s+)*)(\w+)(\s*\**\s*(const|G_CONST_RETURN)?\s*\**\s*(restrict)?\s*)<\/RETURNS>\n//; my $type_modifier = defined($1) ? $1 : ""; my $type = $3; my $pointer = $4; #print "$symbol pointer is $pointer\n"; my $xref = &MakeXRef ($type, &tagify($type, "returnvalue")); my $start = ""; #if ($symbol_type eq 'USER_FUNCTION') { # $start = "typedef "; #} # We output const rather than G_CONST_RETURN. $type_modifier =~ s/G_CONST_RETURN/const/g; $pointer =~ s/G_CONST_RETURN/const/g; $pointer =~ s/^\s+/ /g; my $ret_type_len = length ($start) + length ($type_modifier) + length ($pointer) + length ($type); my $ret_type_output; my $symbol_len; if ($ret_type_len < $RETURN_TYPE_FIELD_WIDTH) { $ret_type_output = "$start$type_modifier$xref$pointer" . (' ' x ($RETURN_TYPE_FIELD_WIDTH - $ret_type_len)); $symbol_len = 0; } else { #$ret_type_output = "$start$type_modifier$xref$pointer\n" . (' ' x $RETURN_TYPE_FIELD_WIDTH); $ret_type_output = "$start$type_modifier$xref$pointer "; $symbol_len = $ret_type_len + 1 - $RETURN_TYPE_FIELD_WIDTH; } $symbol_len += length ($symbol); my $char1 = my $char2 = my $char3 = ""; if ($symbol_type eq 'USER_FUNCTION') { $symbol_len += 3; $char1 = "("; $char2 = "*"; $char3 = ")"; } my ($symbol_output, $symbol_desc_output); if ($symbol_len < $SYMBOL_FIELD_WIDTH) { $symbol_output = "$char1$char2$symbol$char3" . (' ' x ($SYMBOL_FIELD_WIDTH - $symbol_len)); $symbol_desc_output = "$char1$char2$symbol$char3" . (' ' x ($SYMBOL_FIELD_WIDTH - $symbol_len)); } else { $symbol_output = "$char1$char2$symbol$char3\n" . (' ' x ($SYMBOL_FIELD_WIDTH + $RETURN_TYPE_FIELD_WIDTH)); $symbol_desc_output = "$char1$char2$symbol$char3\n" . (' ' x ($SYMBOL_FIELD_WIDTH + $RETURN_TYPE_FIELD_WIDTH)); } my $synop = $ret_type_output . $symbol_output . '('; my $desc = "\n${symbol} ()\n"; $desc .= MakeIndexterms($symbol, $id); $desc .= "\n"; $desc .= OutputSymbolExtraLinks($symbol); $desc .= "${ret_type_output}$symbol_desc_output("; my @fields = ParseFunctionDeclaration($declaration, \&MakeXRef, sub { &tagify($_[0],"parameter"); }); for (my $i = 1; $i <= $#fields; $i += 2) { my $field_name = $fields[$i]; if ($field_name eq "Varargs") { $field_name = "..."; } if ($i == 1) { $synop .= "$field_name"; $desc .= "$field_name"; } else { $synop .= ",\n" . (' ' x ($SYMBOL_FIELD_WIDTH + $RETURN_TYPE_FIELD_WIDTH)) . " $field_name"; $desc .= ",\n" . (' ' x ($SYMBOL_FIELD_WIDTH + $RETURN_TYPE_FIELD_WIDTH)) . " $field_name"; } } $synop .= ");\n"; $desc .= ");\n"; $desc .= &MakeDeprecationNote($symbol); my $parameters = &OutputParamDescriptions ("FUNCTION", $symbol, @fields); my $parameters_output = 0; if (defined ($SymbolDocs{$symbol})) { my $symbol_docs = &ExpandAbbreviations($symbol, $SymbolDocs{$symbol}); # Try to insert the parameter table at the author's desired position. # Otherwise we need to tag it onto the end. # FIXME: document that in the user manual and make it useable for other # types too if ($symbol_docs =~ s//$parameters/) { $parameters_output = 1; } $desc .= $symbol_docs; } if ($parameters_output == 0) { $desc .= $parameters; } $desc .= OutputSymbolTraits ($symbol); $desc .= "\n"; return ($synop, $desc); } ############################################################################# # Function : OutputParamDescriptions # Description : Returns the DocBook output describing the parameters of a # function, macro or signal handler. # Arguments : $symbol_type - 'FUNCTION', 'MACRO' or 'SIGNAL'. Signal # handlers have an implicit user_data parameter last. # $symbol - the name of the function/macro being described. # @fields - parsed fields from the declaration, used to determine # undocumented/unused entries ############################################################################# sub OutputParamDescriptions { my ($symbol_type, $symbol, @fields) = @_; my $output = ""; my $params = $SymbolParams{$symbol}; my $num_params = 0; my %field_descrs = (); if (@fields) { %field_descrs = @fields; delete $field_descrs{"void"}; delete $field_descrs{"Returns"}; } if (defined $params) { my $returns = ""; my $params_desc = ""; my $missing_parameters = ""; my $unused_parameters = ""; my $j; for ($j = 0; $j <= $#$params; $j += $PARAM_FIELD_COUNT) { my $param_name = $$params[$j]; my $param_desc = $$params[$j + 1]; my $param_annotations = ""; ($param_desc,$param_annotations) = & ExpandAnnotation($symbol, $param_desc); $param_desc = &ExpandAbbreviations($symbol, $param_desc); $param_desc .= $param_annotations; # trim $param_desc =~ s/^(\s|\n)+//msg; $param_desc =~ s/(\s|\n)+$//msg; if ($param_name eq "Returns") { $returns = "$param_desc"; } elsif ($param_name eq "void") { #print "!!!! void in params for $symbol?\n"; } else { if (@fields) { if (!defined $field_descrs{$param_name}) { &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol), "Parameter description for $symbol"."::"."$param_name is not used from source code comment block."); if ($unused_parameters ne "") { $unused_parameters .= ", ".$param_name; } else { $unused_parameters = $param_name; } } else { delete $field_descrs{$param_name}; } } if ($param_name eq "Varargs") { $param_name = "..."; } if($param_desc ne "") { $params_desc .= "$param_name :\n$param_desc\n"; $num_params++; } } } foreach my $param_name (keys %field_descrs) { &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol), "Parameter description for $symbol"."::"."$param_name is missing in source code comment block."); if ($missing_parameters ne "") { $missing_parameters .= ", ".$param_name; } else { $missing_parameters = $param_name; } } # Signals have an implicit user_data parameter which we describe. if ($symbol_type eq "SIGNAL") { $params_desc .= "user_data :\nuser data set when the signal handler was connected.\n"; } # Start a table if we need one. if ($params_desc || $returns) { $output .= "\n"; if ($params_desc ne "") { #$output .= "Parameters:\n"; $output .= $params_desc; } # Output the returns info last if ($returns) { $output .= "Returns :$returns\n"; } # Finish the table. $output .= ""; } # remember missing/unused parameters (needed in tmpl-free build) if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) { $AllIncompleteSymbols{$symbol}=$missing_parameters; } if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) { $AllUnusedSymbols{$symbol}=$unused_parameters; } } if (($num_params == 0) && @fields && (scalar(keys(%field_descrs)) > 0)) { if (! exists ($AllIncompleteSymbols{$symbol})) { $AllIncompleteSymbols{$symbol}=""; } } return $output; } ############################################################################# # Function : ParseStabilityLevel # Description : Parses a stability level and outputs a warning if it isn't # valid. # Arguments : $stability - the stability text. # $file, $line - context for error message # $message - description of where the level is from, to use in # any error message. # Returns : The parsed stability level string. ############################################################################# sub ParseStabilityLevel { my ($stability, $file, $line, $message) = @_; $stability =~ s/^\s*//; $stability =~ s/\s*$//; if ($stability =~ m/^stable$/i) { $stability = "Stable"; } elsif ($stability =~ m/^unstable$/i) { $stability = "Unstable"; } elsif ($stability =~ m/^private$/i) { $stability = "Private"; } else { &LogWarning ($file, $line, "$message is $stability.". "It should be one of these: Stable, Unstable, or Private."); } return $stability; } ############################################################################# # Function : OutputSGMLFile # Description : Outputs the final DocBook file for one section. # Arguments : $file - the name of the file. # $title - the title from the $MODULE-sections.txt file, which # will be overridden by the title in the template file. # $section_id - the SGML id to use for the toplevel tag. # $includes - comma-separates list of include files added at top # of synopsis, with '<' '>' around them (if not already enclosed in ""). # $synopsis - reference to the DocBook for the Synopsis part. # $details - reference to the DocBook for the Details part. # $signal_synop - reference to the DocBook for the Signal Synopsis part # $signal_desc - reference to the DocBook for the Signal Description part # $args_synop - reference to the DocBook for the Arg Synopsis part # $args_desc - reference to the DocBook for the Arg Description part # $hierarchy - reference to the DocBook for the Object Hierarchy part # $interfaces - reference to the DocBook for the Interfaces part # $implementations - reference to the DocBook for the Known Implementations part # $prerequisites - reference to the DocBook for the Prerequisites part # $derived - reference to the DocBook for the Derived Interfaces part # $file_objects - reference to an array of objects in this file ############################################################################# sub OutputSGMLFile { my ($file, $title, $section_id, $includes, $synopsis, $details, $signals_synop, $signals_desc, $args_synop, $args_desc, $hierarchy, $interfaces, $implementations, $prerequisites, $derived, $file_objects) = @_; #print "Output sgml for file $file with title '$title'\n"; # The edited title overrides the one from the sections file. my $new_title = $SymbolDocs{"$TMPL_DIR/$file:Title"}; if (defined ($new_title) && $new_title !~ m/^\s*$/) { $title = $new_title; #print "Found title: $title\n"; } my $short_desc = $SymbolDocs{"$TMPL_DIR/$file:Short_Description"}; if (!defined ($short_desc) || $short_desc =~ m/^\s*$/) { $short_desc = ""; } else { $short_desc = &ExpandAbbreviations("$title:Short_description", $short_desc); #print "Found short_desc: $short_desc"; } my $long_desc = $SymbolDocs{"$TMPL_DIR/$file:Long_Description"}; if (!defined ($long_desc) || $long_desc =~ m/^\s*$/) { $long_desc = ""; } else { $long_desc = &ExpandAbbreviations("$title:Long_description", $long_desc); #print "Found long_desc: $long_desc"; } my $see_also = $SymbolDocs{"$TMPL_DIR/$file:See_Also"}; if (!defined ($see_also) || $see_also =~ m%^\s*()?\s*()?\s*$%) { $see_also = ""; } else { $see_also = &ExpandAbbreviations("$title:See_Also", $see_also); #print "Found see_also: $see_also"; } if ($see_also) { $see_also = "\nSee Also\n$see_also\n\n"; } my $stability = $SymbolDocs{"$TMPL_DIR/$file:Stability_Level"}; if (!defined ($stability) || $stability =~ m/^\s*$/) { $stability = ""; } else { $stability = &ParseStabilityLevel($stability, $file, $., "Section stability level"); #print "Found stability: $stability"; } if ($stability) { $stability = "\nStability Level\n$stability, unless otherwise indicated\n\n"; } elsif ($DEFAULT_STABILITY) { $stability = "\nStability Level\n$DEFAULT_STABILITY, unless otherwise indicated\n\n"; } my $image = $SymbolDocs{"$TMPL_DIR/$file:Image"}; if (!defined ($image) || $image =~ m/^\s*$/) { $image = ""; } else { $image =~ s/^\s*//; $image =~ s/\s*$//; my $format; if ($image =~ /jpe?g$/i) { $format = "format='JPEG'"; } elsif ($image =~ /png$/i) { $format = "format='PNG'"; } elsif ($image =~ /svg$/i) { $format = "format='SVG'"; } else { $format = ""; } $image = " \n" } my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = gmtime (time); my $month = (qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec))[$mon]; $year += 1900; my $include_output = ""; my $include; foreach $include (split (/,/, $includes)) { if ($include =~ m/^\".+\"$/) { $include_output .= "#include ${include}\n"; } else { $include =~ s/^\s+|\s+$//gs; $include_output .= "#include <${include}>\n"; } } if ($include_output ne '') { $include_output = "\n$include_output\n"; } my $extralinks = OutputSectionExtraLinks($title,"Section:$file"); my $old_sgml_file = "$SGML_OUTPUT_DIR/$file.$OUTPUT_FORMAT"; my $new_sgml_file = "$SGML_OUTPUT_DIR/$file.$OUTPUT_FORMAT.new"; open (OUTPUT, ">$new_sgml_file") || die "Can't create $new_sgml_file: $!"; my $object_anchors = ""; foreach my $object (@$file_objects) { next if ($object eq $section_id); my $id = CreateValidSGMLID($object); #print "Debug: Adding anchor for $object\n"; $object_anchors .= "" if ($OUTPUT_FORMAT eq "xml") { print OUTPUT $doctype_header; } print OUTPUT < $title 3 \U$MODULE\E Library $image $title $short_desc $stability Synopsis $object_anchors $include_output$${synopsis} $$hierarchy$$prerequisites$$derived$$interfaces$$implementations$$args_synop$$signals_synop Description $extralinks$long_desc Details $$details $$args_desc$$signals_desc$see_also EOF close (OUTPUT); return &UpdateFileIfChanged ($old_sgml_file, $new_sgml_file, 0); } ############################################################################# # Function : OutputExtraFile # Description : Copies an "extra" DocBook file into the output directory, # expanding abbreviations # Arguments : $file - the source file. ############################################################################# sub OutputExtraFile { my ($file) = @_; my $basename; ($basename = $file) =~ s!^.*/!!; my $old_sgml_file = "$SGML_OUTPUT_DIR/$basename"; my $new_sgml_file = "$SGML_OUTPUT_DIR/$basename.new"; my $contents; open(EXTRA_FILE, "<$file") || die "Can't open $file"; { local $/; $contents = ; } open (OUTPUT, ">$new_sgml_file") || die "Can't create $new_sgml_file: $!"; print OUTPUT &ExpandAbbreviations ("$basename file", $contents); close (OUTPUT); return &UpdateFileIfChanged ($old_sgml_file, $new_sgml_file, 0); } ############################################################################# # Function : OutputBook # Description : Outputs the SGML entities that need to be included into the # main SGML file for the module. # Arguments : $book_top - the declarations of the entities, which are added # at the top of the main SGML file. # $book_bottom - the references to the entities, which are # added in the main SGML file at the desired position. ############################################################################# sub OutputBook { my ($book_top, $book_bottom) = @_; my $old_file = "$SGML_OUTPUT_DIR/$MODULE-doc.top"; my $new_file = "$SGML_OUTPUT_DIR/$MODULE-doc.top.new"; open (OUTPUT, ">$new_file") || die "Can't create $new_file: $!"; print OUTPUT $book_top; close (OUTPUT); &UpdateFileIfChanged ($old_file, $new_file, 0); $old_file = "$SGML_OUTPUT_DIR/$MODULE-doc.bottom"; $new_file = "$SGML_OUTPUT_DIR/$MODULE-doc.bottom.new"; open (OUTPUT, ">$new_file") || die "Can't create $new_file: $!"; print OUTPUT $book_bottom; close (OUTPUT); &UpdateFileIfChanged ($old_file, $new_file, 0); # If the main SGML/XML file hasn't been created yet, we create it here. # The user can tweak it later. if ($MAIN_SGML_FILE && ! -e $MAIN_SGML_FILE) { open (OUTPUT, ">$MAIN_SGML_FILE") || die "Can't create $MAIN_SGML_FILE: $!"; if ($OUTPUT_FORMAT eq "xml") { print OUTPUT < ]> EOF } else { print OUTPUT < EOF } print OUTPUT < $MODULE Reference Manual for $MODULE [VERSION]. The latest version of this documentation can be found on-line at http://[SERVER]/$MODULE/. [Insert title here] $book_bottom EOF if (-e $OBJECT_TREE_FILE) { print OUTPUT < Object Hierarchy EOF } print OUTPUT < API Index Index of deprecated API EOF close (OUTPUT); } } ############################################################################# # Function : CreateValidSGML # Description : This turns any chars which are used in SGML into entities, # e.g. '<' into '<' # Arguments : $text - the text to turn into proper SGML. ############################################################################# sub CreateValidSGML { my ($text) = @_; $text =~ s/&/&/g; # Do this first, or the others get messed up. $text =~ s//>/g; # browers render single tabs inconsistently $text =~ s/([^\s])\t([^\s])/$1 $2/g; return $text; } ############################################################################# # Function : ConvertSGMLChars # Description : This is used for text in source code comment blocks, to turn # chars which are used in SGML into entities, e.g. '<' into # '<'. Depending on $INLINE_MARKUP_MODE, this is done # unconditionally or only if the character doesn't seem to be # part of an SGML construct (tag or entity reference). # Arguments : $text - the text to turn into proper SGML. ############################################################################# sub ConvertSGMLChars { my ($symbol, $text) = @_; if ($INLINE_MARKUP_MODE) { # For the XML/SGML mode only convert to entities outside CDATA sections. return &ModifyXMLElements ($text, $symbol, "]*>", \&ConvertSGMLCharsEndTag, \&ConvertSGMLCharsCallback); } else { # For the simple non-sgml mode, convert to entities everywhere. $text =~ s/&/&/g; # Do this first, or the others get messed up. $text =~ s//>/g; return $text; } } sub ConvertSGMLCharsEndTag { if ($_[0] eq ""; } else { return ""; } } sub ConvertSGMLCharsCallback { my ($text, $symbol, $tag) = @_; if ($tag =~ m/^ specially here. return &ModifyXMLElements ($text, $symbol, "/>/g; # Handle "#include " $text =~ s/#include(\s+)<([^>]+)>/#include$1<$2>/g; } return $text; } sub ConvertSGMLCharsCallback2 { my ($text, $symbol, $tag) = @_; # If we're not in CDATA convert to entities. # We could handle differently, though I'm not sure it helps. if ($tag eq "") { # replace only if its not a tag $text =~ s/&(?![a-zA-Z#]+;)/&/g; # Do this first, or the others get messed up. $text =~ s/<(?![a-zA-Z\/!])/</g; $text =~ s/(?/>/g; # Handle "#include " $text =~ s/#include(\s+)<([^>]+)>/#include$1<$2>/g; } return $text; } ############################################################################# # Function : ExpandAnnotation # Description : This turns annotations into acronym tags. # Arguments : $symbol - the symbol being documented, for error messages. # $text - the text to expand. ############################################################################# sub ExpandAnnotation { my ($symbol, $param_desc) = @_; my $param_annotations = ""; # look for annotations at the start of the comment part if ($param_desc =~ m%^\s*\((.*?)\):%) { my @annotations; my $annotation; $param_desc = $'; @annotations = split(/\)\s*\(/,$1); foreach $annotation (@annotations) { # need to search for the longest key-match in %AnnotationDefinition my $match_length=0; my $match_annotation=""; my $annotationdef; foreach $annotationdef (keys %AnnotationDefinition) { if ($annotation =~ m/^$annotationdef/) { if (length($annotationdef)>$match_length) { $match_length=length($annotationdef); $match_annotation=$annotationdef; } } } my $annotation_extra = ""; if ($match_annotation ne "") { if ($annotation =~ m%$match_annotation\s+(.*)%) { $annotation_extra = " $1"; } $AnnotationsUsed{$match_annotation} = 1; $param_annotations .= "[$match_annotation$annotation_extra]"; } else { &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol), "unknown annotation \"$annotation\" in documentation for $symbol."); $param_annotations .= "[$annotation]"; } } chomp($param_desc); $param_desc =~ m/^(.*?)\.*\s*$/s; $param_desc = "$1. "; } if ($param_annotations ne "") { $param_annotations = "$param_annotations"; } return ($param_desc, $param_annotations); } ############################################################################# # Function : ExpandAbbreviations # Description : This turns the abbreviations function(), macro(), @param, # %constant, and #symbol into appropriate DocBook markup. # CDATA sections and parts are skipped. # Arguments : $symbol - the symbol being documented, for error messages. # $text - the text to expand. ############################################################################# sub ExpandAbbreviations { my ($symbol, $text) = @_; # Convert "|[" and "]|" into the start and end of program listing examples. # FIXME: we like to have a way to specify parameters e.g. language="c" $text =~ s%\|\[%%g; $text =~ s%\]\|%%g; # TODO: check for a xml comment after |[ and pick the language attribute from # that # keep CDATA unmodified, preserve ulink tags (ideally we preseve all tags # as such) return &ModifyXMLElements ($text, $symbol, "]*>|]*>|"; } elsif ($start_tag eq ""; } elsif ($start_tag =~ m/<(\w+)/) { return ""; } } # Called inside or outside each CDATA or section. sub ExpandAbbreviationsCallback { my ($text, $symbol, $tag) = @_; if ($tag =~ m/^ sections, so we expand # any gtk-doc abbreviations. # Convert 'function()' or 'macro()'. # if there is abc_*_def() we don't want to make a link to _def() # FIXME: also handle abc(....) : but that would need to be done recursively :/ $text =~ s/([^\*.\w])(\w+)\s*\(\)/$1.&MakeXRef($2, &tagify($2 . "()", "function"));/eg; # handle #Object.func() $text =~ s/(\A|[^\\])#([\w\-:\.]+[\w]+)\s*\(\)/$1.&MakeXRef($2, &tagify($2 . "()", "function"));/eg; # Convert '@param', but not '\@param'. $text =~ s/(\A|[^\\])\@(\w+((\.|->)\w+)*)/$1$2<\/parameter>/g; $text =~ s/\\\@/\@/g; # Convert '%constant', but not '\%constant'. # Also allow negative numbers, e.g. %-1. $text =~ s/(\A|[^\\])\%(-?\w+)/$1.&MakeXRef($2, &tagify($2, "literal"));/eg; $text =~ s/\\\%/\%/g; # Convert '#symbol', but not '\#symbol'. $text =~ s/(\A|[^\\])#([\w\-:\.]+[\w]+)/$1.&MakeHashXRef($2, "type");/eg; $text =~ s/\\#/#/g; # Expand urls # FIXME: should we skip urls that are already tagged? (e.g. http://...) # this is apparently also called for markup and not just for plain text # disable for now. #$text =~ s%(http|https|ftp)://(.*?)((?:\s|,|\)|\]|\<|\.\s))%$2$3%g; # TODO: optionally check all words from $text against internal symbols and # warn if those could be xreffed, but miss a %,# or () } return $text; } # This is called inside a sub ExpandAbbreviationsCallback2 { my ($text, $symbol, $tag) = @_; if ($tag eq "") { # We are inside a but outside any CDATA sections, # so we expand any gtk-doc abbreviations. # FIXME: why is this different from &ExpandAbbreviationsCallback(), # why not just call it $text =~ s/#(\w+)/&MakeHashXRef($1, "");/eg; } return $text; } sub MakeHashXRef { my ($symbol, $tag) = @_;; my $text = $symbol; # Check for things like '#include', '#define', and skip them. if ($PreProcessorDirectives{$symbol}) { return "#$symbol"; } # Get rid of any special '-struct' suffix. $text =~ s/-struct$//; # If the symbol is in the form "Object::signal", then change the symbol to # "Object-signal" and use "signal" as the text. if ($symbol =~ s/::/-/) { $text = "\"$'\""; } # If the symbol is in the form "Object:property", then change the symbol to # "Object--property" and use "property" as the text. if ($symbol =~ s/:/--/) { $text = "\"$'\""; } if ($tag ne "") { $text = tagify ($text, $tag); } return &MakeXRef($symbol, $text); } ############################################################################# # Function : ModifyXMLElements # Description : Looks for given XML element tags within the text, and calls # the callback on pieces of text inside & outside those elements. # Used for special handling of text inside things like CDATA # and . # Arguments : $text - the text. # $symbol - the symbol currently being documented (only used for # error messages). # $start_tag_regexp - the regular expression to match start tags. # e.g. "]*>" to match # CDATA sections or programlisting elements. # $end_tag_func - function which is passed the matched start tag # and should return the appropriate end tag string. # $callback - callback called with each part of the text. It is # called with a piece of text, the symbol being # documented, and the matched start tag or "" if the text # is outside the XML elements being matched. ############################################################################# sub ModifyXMLElements { my ($text, $symbol, $start_tag_regexp, $end_tag_func, $callback) = @_; my ($before_tag, $start_tag, $end_tag_regexp, $end_tag); my $result = ""; while ($text =~ m/$start_tag_regexp/s) { $before_tag = $`; # Prematch for last successful match string $start_tag = $&; # Last successful match $text = $'; # Postmatch for last successful match string $result .= &$callback ($before_tag, $symbol, ""); $result .= $start_tag; # get the mathing end-tag for current tag $end_tag_regexp = &$end_tag_func ($start_tag); if ($text =~ m/$end_tag_regexp/s) { $before_tag = $`; $end_tag = $&; $text = $'; $result .= &$callback ($before_tag, $symbol, $start_tag); $result .= $end_tag; } else { &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol), "Can't find tag end: $end_tag_regexp in docs for: $symbol."); # Just assume it is all inside the tag. $result .= &$callback ($text, $symbol, $start_tag); $text = ""; } } # Handle any remaining text outside the tags. $result .= &$callback ($text, $symbol, ""); return $result; } sub noop { return $_[0]; } # Adds a tag around some text. # e.g tagify("Text", "literal") => "Text". sub tagify { my ($text, $elem) = @_; return "<" . $elem . ">" . $text . ""; } ############################################################################# # Function : MakeXRef # Description : This returns a cross-reference link to the given symbol. # Though it doesn't try to do this for a few standard C types # that it knows won't be in the documentation. # Arguments : $symbol - the symbol to try to create a XRef to. # $text - text text to put inside the XRef, defaults to $symbol ############################################################################# sub MakeXRef { my ($symbol, $text) = ($_[0], $_[1]); $symbol =~ s/^\s+//; $symbol =~ s/\s+$//; if (!defined($text)) { $text = $symbol; # Get rid of special '-struct' suffix. $text =~ s/-struct$//; } if ($symbol =~ m/ /) { return "$text"; } #print "Getting type link for $symbol -> $text\n"; my $symbol_id = &CreateValidSGMLID ($symbol); return "$text"; } ############################################################################# # Function : MakeIndexterms # Description : This returns a indexterm elements for the given symbol # Arguments : $symbol - the symbol to create indexterms for ############################################################################# sub MakeIndexterms { my ($symbol, $id) = @_; my $terms = ""; my $sortas = ""; # make the index useful, by ommiting the namespace when sorting if ($NAME_SPACE ne "") { if ($symbol =~ m/^$NAME_SPACE\_?(.*)/i) { $sortas=" sortas=\"$1\""; } } if (exists $Deprecated{$symbol}) { $terms .= "$symbol"; $IndexEntriesDeprecated{$symbol}=$id; $IndexEntriesFull{$symbol}=$id; } if (exists $Since{$symbol}) { my $since = $Since{$symbol}; $since =~ s/^\s+//; $since =~ s/\s+$//; if ($since ne "") { $terms .= "$symbol"; } $IndexEntriesSince{$symbol}=$id; $IndexEntriesFull{$symbol}=$id; } if ($terms eq "") { $terms .= "$symbol"; $IndexEntriesFull{$symbol}=$id; } return $terms; } ############################################################################# # Function : MakeDeprecationNote # Description : This returns a deprecation warning for the given symbol. # Arguments : $symbol - the symbol to try to create a warning for. ############################################################################# sub MakeDeprecationNote { my ($symbol) = $_[0]; my $desc = ""; my $note = ""; if (exists $Deprecated{$symbol}) { $desc .= ""; if ($Deprecated{$symbol} =~ /^\s*([0-9\.]+)\s*:/) { $desc .= "$symbol has been deprecated since version $1 and should not be used in newly-written code."; } else { $desc .= "$symbol is deprecated and should not be used in newly-written code."; } if ($Deprecated{$symbol} ne "") { $note = &ExpandAbbreviations($symbol, $Deprecated{$symbol}); $note =~ s/^\s*([0-9\.]+)\s*:\s*//; $note =~ s/^\s+//; $note =~ s/\s+$//; $note =~ s%\n{2,}%\n\n\n%g; $desc .= " " . $note; } $desc .= "\n"; } return $desc; } ############################################################################# # Function : MakeConditionDescription # Description : This returns a sumary of conditions for the given symbol. # Arguments : $symbol - the symbol to try to create the sumary. ############################################################################# sub MakeConditionDescription { my ($symbol) = $_[0]; my $desc = ""; if (exists $Deprecated{$symbol}) { if ($desc ne "") { $desc .= "|"; } if ($Deprecated{$symbol} =~ /^\s*(.*?)\s*$/) { $desc .= "deprecated:$1"; } else { $desc .= "deprecated"; } } if (exists $Since{$symbol}) { if ($desc ne "") { $desc .= "|"; } if ($Since{$symbol} =~ /^\s*(.*?)\s*$/) { $desc .= "since:$1"; } else { $desc .= "since"; } } if (exists $StabilityLevel{$symbol}) { if ($desc ne "") { $desc .= "|"; } $desc .= "stability:".$StabilityLevel{$symbol}; } if ($desc ne "") { $desc=" condition=\"".$desc."\""; #print "condition for '$symbol' = '$desc'\n"; } return $desc; } ############################################################################# # Function : GetHierarchy # Description : Returns the DocBook output describing the ancestors and # immediate children of a GObject subclass. It uses the # global @Objects and @ObjectLevels arrays to walk the tree. # Arguments : $object - the GtkObject subclass. ############################################################################# sub GetHierarchy { my ($object) = @_; # Find object in the objects array. my $found = 0; my @children = (); my $i; my $level; my $j; for ($i = 0; $i < @Objects; $i++) { if ($found) { if ($ObjectLevels[$i] <= $level) { last; } elsif ($ObjectLevels[$i] == $level + 1) { push (@children, $Objects[$i]); } } elsif ($Objects[$i] eq $object) { $found = 1; $j = $i; $level = $ObjectLevels[$i]; } } if (!$found) { return ""; } # Walk up the hierarchy, pushing ancestors onto the ancestors array. my @ancestors = (); push (@ancestors, $object); #print "Level: $level\n"; while ($level > 1) { $j--; if ($ObjectLevels[$j] < $level) { push (@ancestors, $Objects[$j]); $level = $ObjectLevels[$j]; #print "Level: $level\n"; } } # Output the ancestors list, indented and with links. my $hierarchy = "\n"; $level = 0; for ($i = $#ancestors; $i >= 0; $i--) { my $link_text; # Don't add a link to the current object, i.e. when i == 0. if ($i > 0) { my $ancestor_id = &CreateValidSGMLID ($ancestors[$i]); $link_text = "$ancestors[$i]"; } else { $link_text = "$ancestors[$i]"; } if ($level == 0) { $hierarchy .= " $link_text\n"; } else { # $hierarchy .= ' ' x ($level * 6 - 3) . "|\n"; $hierarchy .= ' ' x ($level * 6 - 3) . "+----$link_text\n"; } $level++; } for ($i = 0; $i <= $#children; $i++) { my $id = &CreateValidSGMLID ($children[$i]); my $link_text = "$children[$i]"; $hierarchy .= ' ' x ($level * 6 - 3) . "+----$link_text\n"; } $hierarchy .= "\n"; return $hierarchy; } ############################################################################# # Function : GetInterfaces # Description : Returns the DocBook output describing the interfaces # implemented by a class. It uses the global %Interfaces hash. # Arguments : $object - the GtkObject subclass. ############################################################################# sub GetInterfaces { my ($object) = @_; my $text = ""; my $i; # Find object in the objects array. if (exists($Interfaces{$object})) { my @ifaces = split(' ', $Interfaces{$object}); $text = < $object implements EOF for ($i = 0; $i <= $#ifaces; $i++) { my $id = &CreateValidSGMLID ($ifaces[$i]); $text .= " $ifaces[$i]"; if ($i < $#ifaces - 1) { $text .= ', '; } elsif ($i < $#ifaces) { $text .= ' and '; } else { $text .= '.'; } } $text .= < EOF } return $text; } ############################################################################# # Function : GetImplementations # Description : Returns the DocBook output describing the implementations # of an interface. It uses the global %Interfaces hash. # Arguments : $object - the GtkObject subclass. ############################################################################# sub GetImplementations { my ($object) = @_; my @impls = (); my $text = ""; my $i; foreach my $key (keys %Interfaces) { if ($Interfaces{$key} =~ /\b$object\b/) { push (@impls, $key); } } if ($#impls >= 0) { @impls = sort @impls; $text = < $object is implemented by EOF for ($i = 0; $i <= $#impls; $i++) { my $id = &CreateValidSGMLID ($impls[$i]); $text .= " $impls[$i]"; if ($i < $#impls - 1) { $text .= ', '; } elsif ($i < $#impls) { $text .= ' and '; } else { $text .= '.'; } } $text .= < EOF } return $text; } ############################################################################# # Function : GetPrerequisites # Description : Returns the DocBook output describing the prerequisites # of an interface. It uses the global %Prerequisites hash. # Arguments : $iface - the interface. ############################################################################# sub GetPrerequisites { my ($iface) = @_; my $text = ""; my $i; if (exists($Prerequisites{$iface})) { $text = < $iface requires EOF my @prereqs = split(' ', $Prerequisites{$iface}); for ($i = 0; $i <= $#prereqs; $i++) { my $id = &CreateValidSGMLID ($prereqs[$i]); $text .= " $prereqs[$i]"; if ($i < $#prereqs - 1) { $text .= ', '; } elsif ($i < $#prereqs) { $text .= ' and '; } else { $text .= '.'; } } $text .= < EOF } return $text; } ############################################################################# # Function : GetDerived # Description : Returns the DocBook output describing the derived interfaces # of an interface. It uses the global %Prerequisites hash. # Arguments : $iface - the interface. ############################################################################# sub GetDerived { my ($iface) = @_; my $text = ""; my $i; my @derived = (); foreach my $key (keys %Prerequisites) { if ($Prerequisites{$key} =~ /\b$iface\b/) { push (@derived, $key); } } if ($#derived >= 0) { @derived = sort @derived; $text = < $iface is required by EOF for ($i = 0; $i <= $#derived; $i++) { my $id = &CreateValidSGMLID ($derived[$i]); $text .= " $derived[$i]"; if ($i < $#derived - 1) { $text .= ', '; } elsif ($i < $#derived) { $text .= ' and '; } else { $text .= '.'; } } $text .= < EOF } return $text; } ############################################################################# # Function : GetSignals # Description : Returns the synopsis and detailed description DocBook output # for the signal handlers of a given GtkObject subclass. # Arguments : $object - the GtkObject subclass, e.g. 'GtkButton'. ############################################################################# sub GetSignals { my ($object) = @_; my $synop = ""; my $desc = ""; my $i; for ($i = 0; $i <= $#SignalObjects; $i++) { if ($SignalObjects[$i] eq $object) { #print "Found signal: $SignalNames[$i]\n"; my $name = $SignalNames[$i]; my $symbol = "${object}::${name}"; my $id = &CreateValidSGMLID ("$object-$name"); my $pad = ' ' x (46 - length($name)); $synop .= " "$name"$pad "; $desc .= "The <literal>"$name"</literal> signal\n"; $desc .= MakeIndexterms($symbol, $id); $desc .= "\n"; $desc .= OutputSymbolExtraLinks($symbol); $desc .= ""; $SignalReturns[$i] =~ m/\s*(const\s+)?(\w+)\s*(\**)/; my $type_modifier = defined($1) ? $1 : ""; my $type = $2; my $pointer = $3; my $xref = &MakeXRef ($type, &tagify($type, "returnvalue")); my $ret_type_len = length ($type_modifier) + length ($pointer) + length ($type); my $ret_type_output = "$type_modifier$xref$pointer" . (' ' x ($RETURN_TYPE_FIELD_WIDTH - $ret_type_len)); $desc .= "${ret_type_output}user_function " . &MakeReturnField("") . " ("; my $sourceparams = $SourceSymbolParams{$symbol}; my @params = split ("\n", $SignalPrototypes[$i]); my $j; my $l; my $type_len = length("gpointer"); my $name_len = length("user_data"); # do two passes, the first one is to calculate padding for ($l = 0; $l < 2; $l++) { for ($j = 0; $j <= $#params; $j++) { # allow alphanumerics, '_', '[' & ']' in param names if ($params[$j] =~ m/^\s*(\w+)\s*(\**)\s*([\w\[\]]+)\s*$/) { $type = $1; $pointer = $2; if (defined($sourceparams)) { $name = $$sourceparams[$PARAM_FIELD_COUNT * $j]; } else { $name = $3; } if (!defined($name)) { $name = "arg$j"; } if ($l == 0) { if (length($type) + length($pointer) > $type_len) { $type_len = length($type) + length($pointer); } if (length($name) > $name_len) { $name_len = length($name); } } else { $xref = &MakeXRef ($type, &tagify($type, "type")); $pad = ' ' x ($type_len - length($type) - length($pointer)); $desc .= "$xref$pad $pointer$name,\n"; $desc .= (' ' x ($SYMBOL_FIELD_WIDTH + $RETURN_TYPE_FIELD_WIDTH)); } } else { &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol), "Can't parse arg: $params[$j]\nArgs:$SignalPrototypes[$i]"); } } } $xref = &MakeXRef ("gpointer", &tagify("gpointer", "type")); $pad = ' ' x ($type_len - length("gpointer")); $desc .= "$xref$pad user_data)"; my $flags = $SignalFlags[$i]; my $flags_string = ""; if (defined ($flags)) { if ($flags =~ m/f/) { $flags_string = "Run First"; } elsif ($flags =~ m/l/) { $flags_string = "Run Last"; } elsif ($flags =~ m/c/) { $flags_string = "Cleanup"; $flags_string = "Cleanup"; } if ($flags =~ m/r/) { if ($flags_string) { $flags_string .= " / "; } $flags_string = "No Recursion"; } if ($flags =~ m/d/) { if ($flags_string) { $flags_string .= " / "; } $flags_string = "Has Details"; } if ($flags =~ m/a/) { if ($flags_string) { $flags_string .= " / "; } $flags_string = "Action"; } if ($flags =~ m/h/) { if ($flags_string) { $flags_string .= " / "; } $flags_string = "No Hooks"; } } if ($flags_string) { $synop .= ": $flags_string\n"; $pad = ' ' x (5 + $name_len - length("user_data")); $desc .= "$pad : $flags_string\n"; } else { $synop .= "\n"; $desc .= "\n"; } $desc .= &MakeDeprecationNote($symbol); my $parameters = &OutputParamDescriptions ("SIGNAL", $symbol); my $parameters_output = 0; $AllSymbols{$symbol} = 1; if (defined ($SymbolDocs{$symbol})) { my $symbol_docs = &ExpandAbbreviations($symbol, $SymbolDocs{$symbol}); # Try to insert the parameter table at the author's desired # position. Otherwise we need to tag it onto the end. if ($symbol_docs =~ s//$parameters/) { $parameters_output = 1; } $desc .= $symbol_docs; if (!IsEmptyDoc($SymbolDocs{$symbol})) { $AllDocumentedSymbols{$symbol} = 1; } } if ($parameters_output == 0) { $desc .= $parameters; } $desc .= OutputSymbolTraits ($symbol); $desc .= ""; } } return ($synop, $desc); } ############################################################################# # Function : GetArgs # Description : Returns the synopsis and detailed description DocBook output # for the Args of a given GtkObject subclass. # Arguments : $object - the GtkObject subclass, e.g. 'GtkButton'. ############################################################################# sub GetArgs { my ($object) = @_; my $synop = ""; my $desc = ""; my $child_synop = ""; my $child_desc = ""; my $style_synop = ""; my $style_desc = ""; my $i; for ($i = 0; $i <= $#ArgObjects; $i++) { if ($ArgObjects[$i] eq $object) { #print "Found arg: $ArgNames[$i]\n"; my $name = $ArgNames[$i]; my $flags = $ArgFlags[$i]; my $flags_string = ""; my $kind = ""; my $id_sep = ""; if ($flags =~ m/c/) { $kind = "child property"; $id_sep = "c-"; } elsif ($flags =~ m/s/) { $kind = "style property"; $id_sep = "s-"; } else { $kind = "property"; } # Remember only one colon so we don't clash with signals. my $symbol = "${object}:${name}"; # use two dashes and ev. an extra separator here for the same reason. my $id = &CreateValidSGMLID ("$object--$id_sep$name"); my $type = $ArgTypes[$i]; my $type_output; my $range = $ArgRanges[$i]; my $range_output = CreateValidSGML ($range); my $default = $ArgDefaults[$i]; my $default_output = CreateValidSGML ($default); if ($type eq "GtkString") { $type = "char*"; } if ($type eq "GtkSignal") { $type = "GtkSignalFunc, gpointer"; $type_output = &MakeXRef ("GtkSignalFunc") . ", " . &MakeXRef ("gpointer"); } elsif ($type =~ m/^(\w+)\*$/) { $type_output = &MakeXRef ($1, &tagify($1, "type")) . "*"; } else { $type_output = &MakeXRef ($type, &tagify($type, "type")); } if ($flags =~ m/r/) { $flags_string = "Read"; } if ($flags =~ m/w/) { if ($flags_string) { $flags_string .= " / "; } $flags_string .= "Write"; } if ($flags =~ m/x/) { if ($flags_string) { $flags_string .= " / "; } $flags_string .= "Construct"; } if ($flags =~ m/X/) { if ($flags_string) { $flags_string .= " / "; } $flags_string .= "Construct Only"; } $AllSymbols{$symbol} = 1; my $blurb; if (defined($SymbolDocs{$symbol}) && !IsEmptyDoc($SymbolDocs{$symbol})) { $blurb = &ExpandAbbreviations($symbol, $SymbolDocs{$symbol}); #print ".. [$SymbolDocs{$symbol}][$blurb]\n"; $AllDocumentedSymbols{$symbol} = 1; } else { if (!($ArgBlurbs[$i] eq "")) { $AllDocumentedSymbols{$symbol} = 1; } else { # FIXME: print a warning? #print ".. no description\n"; } $blurb = "" . &CreateValidSGML ($ArgBlurbs[$i]) . ""; } my $pad1 = " " x (24 - length ($name)); my $pad2 = " " x (20 - length ($type)); my $arg_synop = " "$name"$pad1 $type_output $pad2 : $flags_string\n"; my $arg_desc = "The <literal>"$name"</literal> $kind\n"; $arg_desc .= MakeIndexterms($symbol, $id); $arg_desc .= "\n"; $arg_desc .= OutputSymbolExtraLinks($symbol); $arg_desc .= " "$name"$pad1 $type_output $pad2 : $flags_string\n"; $arg_desc .= &MakeDeprecationNote($symbol); $arg_desc .= $blurb; if ($range ne "") { $arg_desc .= "Allowed values: $range_output\n"; } if ($default ne "") { $arg_desc .= "Default value: $default_output\n"; } $arg_desc .= OutputSymbolTraits ($symbol); $arg_desc .= "\n"; if ($flags =~ m/c/) { $child_synop .= $arg_synop; $child_desc .= $arg_desc; } elsif ($flags =~ m/s/) { $style_synop .= $arg_synop; $style_desc .= $arg_desc; } else { $synop .= $arg_synop; $desc .= $arg_desc; } } } return ($synop, $child_synop, $style_synop, $desc, $child_desc, $style_desc); } ############################################################################# # Function : ReadSourceDocumentation # Description : This reads in the documentation embedded in comment blocks # in the source code (for Gnome). # # Parameter descriptions override any in the template files. # Function descriptions are placed before any description from # the template files. # # It recursively descends the source directory looking for .c # files and scans them looking for specially-formatted comment # blocks. # # Arguments : $source_dir - the directory to scan. #############m############################################################### sub ReadSourceDocumentation { my ($source_dir) = @_; my ($file, $dir, @suffix_list, $suffix); #print "Scanning source directory: $source_dir\n"; # This array holds any subdirectories found. my (@subdirs) = (); @suffix_list = split (/,/, $SOURCE_SUFFIXES); opendir (SRCDIR, $source_dir) || die "Can't open source directory $source_dir: $!"; foreach $file (readdir (SRCDIR)) { if ($file =~ /^\./) { next; } elsif (-d "$source_dir/$file") { push (@subdirs, $file); } elsif (@suffix_list) { foreach $suffix (@suffix_list) { if ($file =~ m/\.\Q${suffix}\E$/) { &ScanSourceFile ("$source_dir/$file"); } } } elsif ($file =~ m/\.[ch]$/) { &ScanSourceFile ("$source_dir/$file"); } } closedir (SRCDIR); # Now recursively scan the subdirectories. foreach $dir (@subdirs) { next if ($IGNORE_FILES =~ m/(\s|^)\Q${dir}\E(\s|$)/); &ReadSourceDocumentation ("$source_dir/$dir"); } } ############################################################################# # Function : ScanSourceFile # Description : Scans one source file looking for specially-formatted comment # blocks. Later &MergeSourceDocumentation is used to merge any # documentation found with the documentation already read in # from the template files. # # Arguments : $file - the file to scan. ############################################################################# sub ScanSourceFile { my ($file) = @_; my $basename; if ($file =~ m/^.*[\/\\]([^\/\\]*)$/) { $basename = $1; } else { &LogWarning ($file, 1, "Can't find basename for this filename."); $basename = $file; } # Check if the basename is in the list of files to ignore. if ($IGNORE_FILES =~ m/(\s|^)\Q${basename}\E(\s|$)/) { return; } #print "DEBUG: Scanning $file\n"; open (SRCFILE, $file) || die "Can't open $file: $!"; my $in_comment_block = 0; my $symbol; my ($in_description, $in_return, $in_since, $in_stability, $in_deprecated); my ($description, $return_desc, $return_start, $return_style); my ($since_desc, $stability_desc, $deprecated_desc); my $current_param; my $ignore_broken_returns; my @params; while () { # Look for the start of a comment block. if (!$in_comment_block) { if (m%^\s*/\*.*\*/%) { #one-line comment - not gtkdoc } elsif (m%^\s*/\*\*\s%) { #print "Found comment block start\n"; $in_comment_block = 1; # Reset all the symbol data. $symbol = ""; $in_description = 0; $in_return = 0; $in_since = 0; $in_deprecated = 0; $in_stability = 0; $description = ""; $return_desc = ""; $return_style = ""; $since_desc = ""; $deprecated_desc = ""; $stability_desc = ""; $current_param = -1; $ignore_broken_returns = 0; @params = (); } next; } # We're in a comment block. Check if we've found the end of it. if (m%^\s*\*+/%) { if (!$symbol) { # maybe its not even meant to be a gtk-doc comment? &LogWarning ($file, $., "Symbol name not found at the start of the comment block."); } else { # Add the return value description onto the end of the params. if ($return_desc) { push (@params, "Returns"); push (@params, $return_desc); if ($return_style eq 'broken') { &LogWarning ($file, $., "Free-form return value description in $symbol. Use `Returns:' to avoid ambiguities."); } } # Convert special SGML characters $description = &ConvertSGMLChars ($symbol, $description); my $k; for ($k = 1; $k <= $#params; $k += $PARAM_FIELD_COUNT) { $params[$k] = &ConvertSGMLChars ($symbol, $params[$k]); } # Handle Section docs if ($symbol =~ m/SECTION:\s*(.*)/) { my $real_symbol=$1; my $key; if (scalar %KnownSymbols) { if ((! defined($KnownSymbols{"$TMPL_DIR/$real_symbol:Long_Description"})) || $KnownSymbols{"$TMPL_DIR/$real_symbol:Long_Description"} != 1) { &LogWarning ($file, $., "Section $real_symbol is not defined in the $MODULE-section.txt file."); } } #print "SECTION DOCS found in source for : '$real_symbol'\n"; $ignore_broken_returns = 1; for ($k = 0; $k <= $#params; $k += $PARAM_FIELD_COUNT) { #print " '".$params[$k]."'\n"; $params[$k] = "\L$params[$k]"; undef $key; if ($params[$k] eq "short_description") { $key = "$TMPL_DIR/$real_symbol:Short_Description"; } elsif ($params[$k] eq "see_also") { $key = "$TMPL_DIR/$real_symbol:See_Also"; } elsif ($params[$k] eq "title") { $key = "$TMPL_DIR/$real_symbol:Title"; } elsif ($params[$k] eq "stability") { $key = "$TMPL_DIR/$real_symbol:Stability_Level"; } elsif ($params[$k] eq "section_id") { $key = "$TMPL_DIR/$real_symbol:Section_Id"; } elsif ($params[$k] eq "include") { $key = "$TMPL_DIR/$real_symbol:Include"; } elsif ($params[$k] eq "image") { $key = "$TMPL_DIR/$real_symbol:Image"; } if (defined($key)) { $SourceSymbolDocs{$key}=$params[$k+1]; $SourceSymbolSourceFile{$key} = $file; $SourceSymbolSourceLine{$key} = $.; } } $SourceSymbolDocs{"$TMPL_DIR/$real_symbol:Long_Description"}=$description; $SourceSymbolSourceFile{"$TMPL_DIR/$real_symbol:Long_Description"} = $file; $SourceSymbolSourceLine{"$TMPL_DIR/$real_symbol:Long_Description"} = $.; #$SourceSymbolTypes{$symbol} = "SECTION"; } else { #print "SYMBOL DOCS found in source for : '$symbol' ",length($description), "\n"; $SourceSymbolDocs{$symbol} = $description; $SourceSymbolParams{$symbol} = [ @params ]; # FIXME $SourceSymbolTypes{$symbol} = "STRUCT,SIGNAL,ARG,FUNCTION,MACRO"; #if (defined $DeclarationTypes{$symbol}) { # $SourceSymbolTypes{$symbol} = $DeclarationTypes{$symbol} #} $SourceSymbolSourceFile{$symbol} = $file; $SourceSymbolSourceLine{$symbol} = $.; } if ($since_desc) { ($since_desc, my @extra_lines) = split ("\n", $since_desc); $since_desc =~ s/^\s+//; $since_desc =~ s/\s+$//; #print "Since($symbol) : [$since_desc]\n"; $Since{$symbol} = &ConvertSGMLChars ($symbol, $since_desc); if(scalar @extra_lines) { &LogWarning ($file, $., "multi-line since docs found"); } } if ($stability_desc) { $stability_desc = &ParseStabilityLevel($stability_desc, $file, $., "Stability level for $symbol"); $StabilityLevel{$symbol} = &ConvertSGMLChars ($symbol, $stability_desc); } if ($deprecated_desc) { if (exists $Deprecated{$symbol}) { } else { # don't warn for signals and properties #if ($symbol !~ m/::?(.*)/) { if (defined $DeclarationTypes{$symbol}) { &LogWarning ($file, $., "$symbol is deprecated in the inline comments, but no deprecation guards were found around the declaration.". " (See the --deprecated-guards option for gtkdoc-scan.)"); } } $Deprecated{$symbol} = &ConvertSGMLChars ($symbol, $deprecated_desc); } } $in_comment_block = 0; next; } # Get rid of ' * ' at start of every line in the comment block. s%^\s*\*\s?%%; # But make sure we don't get rid of the newline at the end. if (!$_) { $_ = "\n"; } #print "DEBUG: scanning :$_"; # If we haven't found the symbol name yet, look for it. if (!$symbol) { if (m%^\s*(SECTION:\s*\S+)%) { $symbol = $1; #print "SECTION DOCS found in source for : '$symbol'\n"; $ignore_broken_returns = 1; } elsif (m%^\s*([\w:-]*\w)\s*:?\s*(\([a-z ]+\)\s*)*$%) { $symbol = $1; #print "SYMBOL DOCS found in source for : '$symbol'\n"; } next; } # If we're in the return value description, add it to the end. if ($in_return) { # If we find another valid returns line, we assume that the first # one was really part of the description. if (m/^\s*(returns:|return\s+value:)/i) { if ($return_style eq 'broken') { $description .= $return_start . $return_desc; } $return_start = $1; if ($return_style eq 'sane') { &LogWarning ($file, $., "Multiple Returns for $symbol."); } $return_style = 'sane'; $ignore_broken_returns = 1; $return_desc = $'; } elsif (!$ignore_broken_returns && m/^\s*(returns\b\s*)/i) { $description .= $return_start . $return_desc; $return_start = $1; $return_style = 'broken'; $return_desc = $'; } elsif (m%^\s*since:%i) { $since_desc = $'; $in_since = 1; $in_return = 0; } elsif (m%^\s*stability:%i) { $stability_desc = $'; $in_stability = 1; $in_return = 0; } elsif (m%^\s*deprecated:%i) { $deprecated_desc = $'; $in_deprecated = 1; $in_return = 0; } else { $return_desc .= $_; } next; } if ($in_since) { if (m/^\s*(returns:|return\s+value:)/i) { if ($return_style eq 'broken') { $description .= $return_start . $return_desc; } $return_start = $1; if ($return_style eq 'sane') { &LogWarning ($file, $., "Multiple Returns for $symbol."); } $return_style = 'sane'; $ignore_broken_returns = 1; $return_desc = $'; $in_return = 1; $in_since = 0; } elsif (!$ignore_broken_returns && m/^\s*(returns\b\s*)/i) { $return_start = $1; $return_style = 'broken'; $return_desc = $'; $in_return = 1; $in_since = 0; } elsif (m%^\s*deprecated:%i) { $deprecated_desc = $'; $in_deprecated = 1; $in_since = 0; } elsif (m%^\s*stability:%i) { $stability_desc = $'; $in_stability = 1; $in_since = 0; } else { $since_desc .= $_; } next; } if ($in_stability) { if (m/^\s*(returns:|return\s+value:)/i) { if ($return_style eq 'broken') { $description .= $return_start . $return_desc; } $return_start = $1; if ($return_style eq 'sane') { &LogWarning ($file, $., "Multiple Returns for $symbol."); } $return_style = 'sane'; $ignore_broken_returns = 1; $return_desc = $'; $in_return = 1; $in_stability = 0; } elsif (!$ignore_broken_returns && m/^\s*(returns\b\s*)/i) { $return_start = $1; $return_style = 'broken'; $return_desc = $'; $in_return = 1; $in_stability = 0; } elsif (m%^\s*deprecated:%i) { $deprecated_desc = $'; $in_deprecated = 1; $in_stability = 0; } elsif (m%^\s*since:%i) { $since_desc = $'; $in_since = 1; $in_stability = 0; } else { $stability_desc .= $_; } next; } if ($in_deprecated) { if (m/^\s*(returns:|return\s+value:)/i) { if ($return_style eq 'broken') { $description .= $return_start . $return_desc; } $return_start = $1; if ($return_style eq 'sane') { &LogWarning ($file, $., "Multiple Returns for $symbol."); } $return_style = 'sane'; $ignore_broken_returns = 1; $return_desc = $'; $in_return = 1; $in_deprecated = 0; } elsif (!$ignore_broken_returns && m/^\s*(returns\b\s*)/i) { $return_start = $1; $return_style = 'broken'; $return_desc = $'; $in_return = 1; $in_deprecated = 0; } elsif (m%^\s*since:%i) { $since_desc = $'; $in_since = 1; $in_deprecated = 0; } elsif (m%^\s*stability:%i) { $stability_desc = $'; $in_stability = 1; $in_deprecated = 0; } else { $deprecated_desc .= $_; } next; } # If we're in the description part, check for the 'Returns:' line. # If that isn't found, add the text to the end. if ($in_description) { # Get rid of 'Description:' s%^\s*Description:%%; if (m/^\s*(returns:|return\s+value:)/i) { if ($return_style eq 'broken') { $description .= $return_start . $return_desc; } $return_start = $1; if ($return_style eq 'sane') { &LogWarning ($file, $., "Multiple Returns for $symbol."); } $return_style = 'sane'; $ignore_broken_returns = 1; $return_desc = $'; $in_return = 1; next; } elsif (!$ignore_broken_returns && m/^\s*(returns\b\s*)/i) { $return_start = $1; $return_style = 'broken'; $return_desc = $'; $in_return = 1; next; } elsif (m%^\s*since:%i) { $since_desc = $'; $in_since = 1; next; } elsif (m%^\s*deprecated:%i) { $deprecated_desc = $'; $in_deprecated = 1; next; } elsif (m%^\s*stability:%i) { $stability_desc = $'; $in_stability = 1; next; } $description .= $_; next; } # We must be in the parameters. Check for the empty line below them. if (m%^\s*$%) { $in_description = 1; next; } # Look for a parameter name. if (m%^\s*@(\S+)\s*:\s*%) { my $param_name = $1; my $param_desc = $'; #print "Found parameter: $param_name\n"; # Allow '...' as the Varargs parameter. if ($param_name eq "...") { $param_name = "Varargs"; } if ("\L$param_name" eq "returns") { $return_style = 'sane'; $ignore_broken_returns = 1; } push (@params, $param_name); push (@params, $param_desc); $current_param += $PARAM_FIELD_COUNT; next; } # We must be in the middle of a parameter description, so add it on # to the last element in @params. if ($current_param == -1) { &LogWarning ($file, $., "Parsing comment block file : parameter expected."); } else { $params[$#params] .= $_; } } close (SRCFILE); } ############################################################################# # Function : OutputMissingDocumentation # Description : Outputs report of documentation coverage to a file # # Arguments : none ############################################################################# sub OutputMissingDocumentation { my $old_undocumented_file = "$ROOT_DIR/$MODULE-undocumented.txt"; my $new_undocumented_file = "$ROOT_DIR/$MODULE-undocumented.new"; my $n_documented = 0; my $n_incomplete = 0; my $total = 0; my $symbol; my $percent; my $msg; my $buffer = ""; my $buffer_deprecated = ""; my $buffer_descriptions = ""; open(UNDOCUMENTED, ">$new_undocumented_file") || die "Can't create $new_undocumented_file"; foreach $symbol (sort (keys (%AllSymbols))) { # FIXME: should we print LogWarnings for undocumented stuff? # DEBUG #my $ssfile = &GetSymbolSourceFile($symbol); #my $ssline = &GetSymbolSourceLine($symbol); #my $location = "defined at " . (defined($ssfile)?$ssfile:"?") . ":" . (defined($ssline)?$ssline:"0") . "\n"; # DEBUG if ($symbol !~ /:(Title|Long_Description|Short_Description|See_Also|Stability_Level|Include|Section_Id|Image)/) { $total++; if (exists ($AllDocumentedSymbols{$symbol})) { $n_documented++; if (exists ($AllIncompleteSymbols{$symbol})) { $n_incomplete++; $buffer .= $symbol . " (" . $AllIncompleteSymbols{$symbol} . ")\n"; #$buffer .= "\t0: ".$location; } } elsif (exists $Deprecated{$symbol}) { if (exists ($AllIncompleteSymbols{$symbol})) { $n_incomplete++; $buffer_deprecated .= $symbol . " (" . $AllIncompleteSymbols{$symbol} . ")\n"; #$buffer .= "\t1a: ".$location; } else { $buffer_deprecated .= $symbol . "\n"; #$buffer .= "\t1b: ".$location; } } else { if (exists ($AllIncompleteSymbols{$symbol})) { $n_incomplete++; $buffer .= $symbol . " (" . $AllIncompleteSymbols{$symbol} . ")\n"; #$buffer .= "\t2a: ".$location; } else { $buffer .= $symbol . "\n"; #$buffer .= "\t2b: ".$location; } } } elsif ($symbol =~ /:(Long_Description|Short_Description)/) { $total++; #my $len1=(exists($SymbolDocs{$symbol}))?length($SymbolDocs{$symbol}):-1; #my $len2=(exists($AllDocumentedSymbols{$symbol}))?length($AllDocumentedSymbols{$symbol}):-1; #print "%%%% $symbol : $len1,$len2\n"; if (((exists ($SymbolDocs{$symbol})) && (length ($SymbolDocs{$symbol}) > 0)) || ((exists ($AllDocumentedSymbols{$symbol})) && (length ($AllDocumentedSymbols{$symbol}) > 0))) { $n_documented++; } else { # cut off the leading namespace ($TMPL_DIR) $symbol =~ m/^.*\/(.*)$/; $buffer_descriptions .= $1 . "\n"; } } } $buffer .= "\n" . $buffer_deprecated . "\n" . $buffer_descriptions; if ($total == 0) { $percent = 100; } else { $percent = ($n_documented / $total) * 100.0; } printf UNDOCUMENTED "%.0f%% symbol docs coverage.\n", $percent; print UNDOCUMENTED "$n_documented symbols documented.\n"; print UNDOCUMENTED "$n_incomplete symbols incomplete.\n"; print UNDOCUMENTED ($total - $n_documented) . " not documented.\n\n\n"; print UNDOCUMENTED $buffer; close (UNDOCUMENTED); return &UpdateFileIfChanged ($old_undocumented_file, $new_undocumented_file, 0); printf "%.0f%% symbol docs coverage", $percent; print "($n_documented symbols documented, $n_incomplete symbols incomplete, " . ($total - $n_documented) . " not documented)\n"; print "See $MODULE-undocumented.txt for a list of missing docs.\nThe doc coverage percentage doesn't include intro sections.\n"; } ############################################################################# # Function : OutputUndeclaredSymbols # Description : Outputs symbols that are listed in the section file, but not # declaration is found in the sources # # Arguments : none ############################################################################# sub OutputUndeclaredSymbols { my $old_undeclared_file = "$ROOT_DIR/$MODULE-undeclared.txt"; my $new_undeclared_file = "$ROOT_DIR/$MODULE-undeclared.new"; open(UNDECLARED, ">$new_undeclared_file") || die "Can't create $new_undeclared_file"; if (%UndeclaredSymbols) { print UNDECLARED (join("\n", sort keys %UndeclaredSymbols)); print UNDECLARED "\n"; print "See $MODULE-undeclared.txt for the list of undeclared symbols.\n" } close(UNDECLARED); return &UpdateFileIfChanged ($old_undeclared_file, $new_undeclared_file, 0); } ############################################################################# # Function : OutputUnusedSymbols # Description : Outputs symbols that are documented in comments, but not # declared in the sources # # Arguments : none ############################################################################# sub OutputUnusedSymbols { my $num_unused = 0; my $old_unused_file = "$ROOT_DIR/$MODULE-unused.txt"; my $new_unused_file = "$ROOT_DIR/$MODULE-unused.new"; open (UNUSED, ">$new_unused_file") || die "Can't open $new_unused_file"; my ($symbol); foreach $symbol (sort keys (%Declarations)) { if (!defined ($DeclarationOutput{$symbol})) { print (UNUSED "$symbol\n"); $num_unused++; } } foreach $symbol (sort (keys (%AllUnusedSymbols))) { print (UNUSED "$symbol(" . $AllUnusedSymbols{$symbol} . ")\n"); $num_unused++; } close (UNUSED); if ($num_unused != 0) { &LogWarning ($old_unused_file, 1, "$num_unused unused declarations.". "They should be added to $MODULE-sections.txt in the appropriate place."); } return &UpdateFileIfChanged ($old_unused_file, $new_unused_file, 0); } ############################################################################# # Function : OutputAllSymbols # Description : Outputs list of all symbols to a file # # Arguments : none ############################################################################# sub OutputAllSymbols { my $n_documented = 0; my $total = 0; my $symbol; my $percent; my $msg; open (SYMBOLS, ">$ROOT_DIR/$MODULE-symbols.txt") || die "Can't create $ROOT_DIR/$MODULE-symbols.txt: $!"; foreach $symbol (sort (keys (%AllSymbols))) { print SYMBOLS $symbol . "\n"; } close (SYMBOLS); } ############################################################################# # Function : OutputSymbolsWithoutSince # Description : Outputs list of all symbols without a since tag to a file # # Arguments : none ############################################################################# sub OutputSymbolsWithoutSince { my $n_documented = 0; my $total = 0; my $symbol; my $percent; my $msg; open (SYMBOLS, ">$ROOT_DIR/$MODULE-nosince.txt") || die "Can't create $ROOT_DIR/$MODULE-nosince.txt: $!"; foreach $symbol (sort (keys (%SourceSymbolDocs))) { if (!defined $Since{$symbol}) { print SYMBOLS $symbol . "\n"; } } close (SYMBOLS); } ############################################################################# # Function : MergeSourceDocumentation # Description : This merges documentation read from a source file into the # documentation read in from a template file. # # Parameter descriptions override any in the template files. # Function descriptions are placed before any description from # the template files. # # Arguments : none ############################################################################# sub MergeSourceDocumentation { my $symbol; my @Symbols; if (scalar %SymbolDocs) { @Symbols=keys (%SymbolDocs); #print "num existing entries: ".(scalar @Symbols)."\n"; #print " ",$Symbols[0], " ",$Symbols[1],"\n"; } else { # filter scanned declarations, with what we suppress from -sections.txt my %tmp = (); foreach $symbol (keys (%Declarations)) { if (defined($KnownSymbols{$symbol}) && $KnownSymbols{$symbol} == 1) { $tmp{$symbol}=1; } } # , add the rest from -sections.txt foreach $symbol (keys (%KnownSymbols)) { if ($KnownSymbols{$symbol} == 1) { $tmp{$symbol}=1; } } # and add whats found in the source foreach $symbol (keys (%SourceSymbolDocs)) { $tmp{$symbol}=1; } @Symbols = keys (%tmp); #print "num source entries: ".(scalar @Symbols)."\n"; } foreach $symbol (@Symbols) { $AllSymbols{$symbol} = 1; my $have_tmpl_docs = 0; ## see if the symbol is documented in template my $tmpl_doc = defined ($SymbolDocs{$symbol}) ? $SymbolDocs{$symbol} : ""; my $check_tmpl_doc =$tmpl_doc; # remove all xml-tags and whitespaces $check_tmpl_doc =~ s/<.*?>//g; $check_tmpl_doc =~ s/\s//g; # anything left ? if ($check_tmpl_doc ne "") { $have_tmpl_docs = 1; #print "## [$check_tmpl_doc]\n"; } else { # if the docs have just an empty para, don't merge that. $check_tmpl_doc = $tmpl_doc; $check_tmpl_doc =~ s/(\s|\n)//msg; if ($check_tmpl_doc eq "") { $tmpl_doc = ""; } } if (exists ($SourceSymbolDocs{$symbol})) { my $type = $DeclarationTypes {$symbol}; #print "merging [$symbol] from source\n"; my $item = "Parameter"; if (defined ($type)) { if ($type eq 'STRUCT') { $item = "Field"; } elsif ($type eq 'ENUM') { $item = "Value"; } elsif ($type eq 'UNION') { $item = "Field"; } } else { $type="SIGNAL"; } my $src_doc = $SourceSymbolDocs{$symbol}; # remove leading and training whitespaces $src_doc =~ s/^\s+//; $src_doc =~ s/\s+$//; # Don't output warnings for overridden titles as titles are # automatically generated in the -sections.txt file, and thus they # are often overridden. if ($have_tmpl_docs && $symbol !~ m/:Title$/) { # check if content is different if ($tmpl_doc ne $src_doc) { #print "[$tmpl_doc] [$src_doc]\n"; &LogWarning ($SourceSymbolSourceFile{$symbol}, $SourceSymbolSourceLine{$symbol}, "Documentation in template ".$SymbolSourceFile{$symbol}.":".$SymbolSourceLine{$symbol}." for $symbol being overridden by inline comments."); } } if ($src_doc ne "") { $AllDocumentedSymbols{$symbol} = 1; } # Convert with any blank lines around it to # a followed by followed by . $src_doc =~ s%\n+\s*\s*\n+%\n\n\n\n%g; # Do not add to nothing, it breaks missing docs checks. my $src_doc_para = ""; if ($src_doc ne "") { # If there is a blank line, finish the paragraph and start another. $src_doc = &ConvertBlankLines ($src_doc, $symbol); $src_doc_para = "\n$src_doc\n"; ## fixup xml markup # FIXME: this is questionable, as we can't make assumtions on the content really #$src_doc_para =~ s%^\n(\n%%gms; #$src_doc_para =~ s%()\n$%$1%gms; #print "$symbol : [$src_doc][$src_doc_para]\n"; } if ($symbol =~ m/$TMPL_DIR\/.+:Long_Description/) { $SymbolDocs{$symbol} = "$src_doc_para$tmpl_doc"; } elsif ($symbol =~ m/$TMPL_DIR\/.+:.+/) { # For the title/summary/see also section docs we don't want to # add any tags. $SymbolDocs{$symbol} = "$src_doc" } else { $SymbolDocs{$symbol} = "$src_doc_para$tmpl_doc"; } # merge parameters if ($symbol =~ m/.*::.*/) { # For signals we prefer the param names from the source docs, # since the ones from the templates are likely to contain the # artificial argn names which are generated by gtkdoc-scangobj. $SymbolParams{$symbol} = $SourceSymbolParams{$symbol}; # FIXME: we need to check for empty docs here as well! } else { # The templates contain the definitive parameter names and order, # so we will not change that. We only override the actual text. my $tmpl_params = $SymbolParams{$symbol}; if (!defined ($tmpl_params)) { #print "No merge needed for $symbol\n"; $SymbolParams{$symbol} = $SourceSymbolParams{$symbol}; # FIXME: we still like to get the number of params and merge # 1) we would noticed that params have been removed/renamed # 2) we would catch undocumented params # params are not (yet) exported in -decl.txt so that we # could easily grab them :/ } else { my $params = $SourceSymbolParams{$symbol}; my $j; #print "Merge needed for $symbol, tmpl_params: ",$#$tmpl_params,", source_params: ",$#$params," \n"; for ($j = 0; $j <= $#$tmpl_params; $j += $PARAM_FIELD_COUNT) { my $tmpl_param_name = $$tmpl_params[$j]; # Allow '...' as the Varargs parameter. if ($tmpl_param_name eq "...") { $tmpl_param_name = "Varargs"; } # Try to find the param in the source comment documentation. my $found = 0; my $k; #print " try merge param $tmpl_param_name\n"; for ($k = 0; $k <= $#$params; $k += $PARAM_FIELD_COUNT) { my $param_name = $$params[$k]; my $param_desc = $$params[$k + 1]; #print " test param $param_name\n"; # We accept changes in case, since the Gnome source # docs contain a lot of these. if ("\L$param_name" eq "\L$tmpl_param_name") { $found = 1; # Override the description. $$tmpl_params[$j + 1] = $param_desc; # Set the name to "" to mark it as used. $$params[$k] = ""; last; } } # If it looks like the parameters are there, but not # in the right place, try to explain a bit better. if ((!$found) && ($src_doc =~ m/\@$tmpl_param_name:/)) { &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol), "Parameters for $symbol must start on the line immediately after the function or macro name."); } } # Now we output a warning if parameters have been described which # do not exist. for ($j = 0; $j <= $#$params; $j += $PARAM_FIELD_COUNT) { my $param_name = $$params[$j]; if ($param_name) { # the template builder cannot detect if a macro returns # a result or not if(($type eq "MACRO") && ($param_name eq "Returns")) { # FIXME: do we need to add it then to tmpl_params[] ? my $num=$#$tmpl_params; #print " adding Returns: to macro docs for $symbol.\n"; $$tmpl_params[$num+1]="Returns"; $$tmpl_params[$num+2]=$$params[$j+1]; next; } &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol), "$item described in source code comment block but does not exist. $type: $symbol $item: $param_name."); } } } } } else { if ($have_tmpl_docs) { $AllDocumentedSymbols{$symbol} = 1; #print "merging [$symbol] from template\n"; } else { #print "[$symbol] undocumented\n"; } } # if this symbol is documented, check if docs are complete $check_tmpl_doc = defined ($SymbolDocs{$symbol}) ? $SymbolDocs{$symbol} : ""; # remove all xml-tags and whitespaces $check_tmpl_doc =~ s/<.*?>//g; $check_tmpl_doc =~ s/\s//g; if ($check_tmpl_doc ne "") { my $tmpl_params = $SymbolParams{$symbol}; if (defined ($tmpl_params)) { my $type = $DeclarationTypes {$symbol}; my $item = "Parameter"; if (defined ($type)) { if ($type eq 'STRUCT') { $item = "Field"; } elsif ($type eq 'ENUM') { $item = "Value"; } elsif ($type eq 'UNION') { $item = "Field"; } } else { $type="SIGNAL"; } #print "Check param docs for $symbol, tmpl_params: ",$#$tmpl_params," entries, type=$type\n"; if ($#$tmpl_params > 0) { my $j; for ($j = 0; $j <= $#$tmpl_params; $j += $PARAM_FIELD_COUNT) { # Output a warning if the parameter is empty and # remember for stats. my $tmpl_param_name = $$tmpl_params[$j]; my $tmpl_param_desc = $$tmpl_params[$j + 1]; if ($tmpl_param_name ne "void" && $tmpl_param_desc !~ m/\S/) { if (exists ($AllIncompleteSymbols{$symbol})) { $AllIncompleteSymbols{$symbol}.=", ".$tmpl_param_name; } else { $AllIncompleteSymbols{$symbol}=$tmpl_param_name; } &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol), "$item description for $symbol"."::"."$tmpl_param_name is missing in source code comment block."); } } } else { if ($#$tmpl_params == 0) { $AllIncompleteSymbols{$symbol}=""; &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol), "$item descriptions for $symbol are missing in source code comment block."); } # $#$tmpl_params==-1 means we don't know about parameters # this unfortunately does not tell if there should be some } } } } #print "num doc entries: ".(scalar %SymbolDocs)."\n"; } ############################################################################# # Function : IsEmptyDoc # Description : Check if a doc-string is empty. Its also regarded as empty if # it only consist of whitespace or e.g. FIXME. # Arguments : the doc-string ############################################################################# sub IsEmptyDoc { my ($doc) = @_; if ($doc =~ /^\s*$/) { return 1; } if ($doc =~ /^\s*\s*(FIXME)?\s*<\/para>\s*$/) { return 1; } return 0; } # This converts blank lines to "", but only outside CDATA and # tags. sub ConvertBlankLines { return &ModifyXMLElements ($_[0], $_[1], "]*>|\\|\\[", \&ConvertBlankLinesEndTag, \&ConvertBlankLinesCallback); } sub ConvertBlankLinesEndTag { if ($_[0] eq ""; } elsif ($_[0] eq "|[") { return "]\\|"; } else { return ""; } } sub ConvertBlankLinesCallback { my ($text, $symbol, $tag) = @_; # If we're not in CDATA or a we convert blank lines so # they start a new . if ($tag eq "") { $text =~ s%\n{2,}%\n\n\n%g; } return $text; } ############################################################################# # LIBRARY FUNCTIONS - These functions are used in both gtkdoc-mkdb and # gtkdoc-mktmpl and should eventually be moved to a # separate library. ############################################################################# ############################################################################# # Function : ReadDeclarationsFile # Description : This reads in a file containing the function/macro/enum etc. # declarations. # # Note that in some cases there are several declarations with # the same name, e.g. for conditional macros. In this case we # set a flag in the %DeclarationConditional hash so the # declaration is not shown in the docs. # # If a macro and a function have the same name, e.g. for # gtk_object_ref, the function declaration takes precedence. # # Some opaque structs are just declared with 'typedef struct # _name name;' in which case the declaration may be empty. # The structure may have been found later in the header, so # that overrides the empty declaration. # # Arguments : $file - the declarations file to read # $override - if declarations in this file should override # any current declaration. ############################################################################# sub ReadDeclarationsFile { my ($file, $override) = @_; if ($override == 0) { %Declarations = (); %DeclarationTypes = (); %DeclarationConditional = (); %DeclarationOutput = (); } open (INPUT, $file) || die "Can't open $file: $!"; my $declaration_type = ""; my $declaration_name; my $declaration; my $is_deprecated = 0; while () { if (!$declaration_type) { if (m/^<([^>]+)>/) { $declaration_type = $1; $declaration_name = ""; #print "Found declaration: $declaration_type\n"; $declaration = ""; } } else { if (m%^(.*)%) { $declaration_name = $1; } elsif (m%^%) { $is_deprecated = 1; } elsif (m%^%) { #print "Found end of declaration: $declaration_name\n"; # Check that the declaration has a name if ($declaration_name eq "") { print "ERROR: $declaration_type has no name $file:$.\n"; } # If the declaration is an empty typedef struct _XXX XXX # set the flag to indicate the struct has a typedef. if ($declaration_type eq 'STRUCT' && $declaration =~ m/^\s*$/) { #print "Struct has typedef: $declaration_name\n"; $StructHasTypedef{$declaration_name} = 1; } # Check if the symbol is already defined. if (defined ($Declarations{$declaration_name}) && $override == 0) { # Function declarations take precedence. if ($DeclarationTypes{$declaration_name} eq 'FUNCTION') { # Ignore it. } elsif ($declaration_type eq 'FUNCTION') { if ($is_deprecated) { $Deprecated{$declaration_name} = ""; } $Declarations{$declaration_name} = $declaration; $DeclarationTypes{$declaration_name} = $declaration_type; } elsif ($DeclarationTypes{$declaration_name} eq $declaration_type) { # If the existing declaration is empty, or is just a # forward declaration of a struct, override it. if ($declaration_type eq 'STRUCT') { if ($Declarations{$declaration_name} =~ m/^\s*(struct\s+\w+\s*;)?\s*$/) { if ($is_deprecated) { $Deprecated{$declaration_name} = ""; } $Declarations{$declaration_name} = $declaration; } elsif ($declaration =~ m/^\s*(struct\s+\w+\s*;)?\s*$/) { # Ignore an empty or forward declaration. } else { &LogWarning ($file, $., "Structure $declaration_name has multiple definitions."); } } else { # set flag in %DeclarationConditional hash for # multiply defined macros/typedefs. $DeclarationConditional{$declaration_name} = 1; } } else { &LogWarning ($file, $., "$declaration_name has multiple definitions."); } } else { if ($is_deprecated) { $Deprecated{$declaration_name} = ""; } $Declarations{$declaration_name} = $declaration; $DeclarationTypes{$declaration_name} = $declaration_type; } $declaration_type = ""; $is_deprecated = 0; } else { $declaration .= $_; } } } close (INPUT); } ############################################################################# # Function : ReadSignalsFile # Description : This reads in an existing file which contains information on # all GTK signals. It creates the arrays @SignalNames and # @SignalPrototypes containing info on the signals. The first # line of the SignalPrototype is the return type of the signal # handler. The remaining lines are the parameters passed to it. # The last parameter, "gpointer user_data" is always the same # so is not included. # Arguments : $file - the file containing the signal handler prototype # information. ############################################################################# sub ReadSignalsFile { my ($file) = @_; my $in_signal = 0; my $signal_object; my $signal_name; my $signal_returns; my $signal_flags; my $signal_prototype; # Reset the signal info. @SignalObjects = (); @SignalNames = (); @SignalReturns = (); @SignalFlags = (); @SignalPrototypes = (); if (! -f $file) { return; } if (!open (INPUT, $file)) { warn "Can't open $file - skipping signals\n"; return; } while () { if (!$in_signal) { if (m/^/) { $in_signal = 1; $signal_object = ""; $signal_name = ""; $signal_returns = ""; $signal_prototype = ""; } } else { if (m/^(.*)<\/NAME>/) { $signal_name = $1; if ($signal_name =~ m/^(.*)::(.*)$/) { $signal_object = $1; ($signal_name = $2) =~ s/_/-/g; #print "Found signal: $signal_name\n"; } else { &LogWarning ($file, $., "Invalid signal name: $signal_name."); } } elsif (m/^(.*)<\/RETURNS>/) { $signal_returns = $1; } elsif (m/^(.*)<\/FLAGS>/) { $signal_flags = $1; } elsif (m%^%) { #print "Found end of signal: ${signal_object}::${signal_name}\nReturns: ${signal_returns}\n${signal_prototype}"; push (@SignalObjects, $signal_object); push (@SignalNames, $signal_name); push (@SignalReturns, $signal_returns); push (@SignalFlags, $signal_flags); push (@SignalPrototypes, $signal_prototype); $in_signal = 0; } else { $signal_prototype .= $_; } } } close (INPUT); } ############################################################################# # Function : ReadTemplateFile # Description : This reads in the manually-edited documentation file # corresponding to the file currently being created, so we can # insert the documentation at the appropriate places. # It outputs %SymbolTypes, %SymbolDocs and %SymbolParams, which # is a hash of arrays. # NOTE: This function is duplicated in gtkdoc-mktmpl (but # slightly different). # Arguments : $docsfile - the template file to read in. # $skip_unused_params - 1 if the unused parameters should be # skipped. ############################################################################# sub ReadTemplateFile { my ($docsfile, $skip_unused_params) = @_; my $template = "$docsfile.sgml"; if (! -f $template) { #print "File doesn't exist: $template\n"; return 0; } #print "Reading $template\n"; # start with empty hashes, we merge the source comment for each file # afterwards %SymbolDocs = (); %SymbolTypes = (); %SymbolParams = (); my $current_type = ""; # Type of symbol being read. my $current_symbol = ""; # Name of symbol being read. my $symbol_doc = ""; # Description of symbol being read. my @params; # Parameter names and descriptions of current # function/macro/function typedef. my $current_param = -1; # Index of parameter currently being read. # Note that the param array contains pairs # of param name & description. my $in_unused_params = 0; # True if we are reading in the unused params. my $in_deprecated = 0; my $in_since = 0; my $in_stability = 0; open (DOCS, "$template") || die "Can't open $template: $!"; while () { if (m/^/) { my $type = $1; my $symbol = $2; if ($symbol eq "Title" || $symbol eq "Short_Description" || $symbol eq "Long_Description" || $symbol eq "See_Also" || $symbol eq "Stability_Level" || $symbol eq "Include" || $symbol eq "Image") { $symbol = $docsfile . ":" . $symbol; } #print "Found symbol: $symbol\n"; # Remember file and line for the symbol $SymbolSourceFile{$symbol} = $template; $SymbolSourceLine{$symbol} = $.; # Store previous symbol, but remove any trailing blank lines. if ($current_symbol ne "") { $symbol_doc =~ s/\s+$//; $SymbolTypes{$current_symbol} = $current_type; $SymbolDocs{$current_symbol} = $symbol_doc; # Check that the stability level is valid. if ($StabilityLevel{$current_symbol}) { $StabilityLevel{$current_symbol} = &ParseStabilityLevel($StabilityLevel{$current_symbol}, $template, $., "Stability level for $current_symbol"); } if ($current_param >= 0) { $SymbolParams{$current_symbol} = [ @params ]; } else { # Delete any existing params in case we are overriding a # previously read template. delete $SymbolParams{$current_symbol}; } } $current_type = $type; $current_symbol = $symbol; $current_param = -1; $in_unused_params = 0; $in_deprecated = 0; $in_since = 0; $in_stability = 0; $symbol_doc = ""; @params = (); } elsif (m/^/) { #print "DEBUG: Found unused parameters\n"; $in_unused_params = 1; next; } elsif ($in_unused_params && $skip_unused_params) { # When outputting the DocBook we skip unused parameters. #print "DEBUG: Skipping unused param: $_"; next; } else { # Check if param found. Need to handle "..." and "format...". if (s/^\@([\w\.]+):\040?//) { my $param_name = $1; my $param_desc = $_; # Allow variations of 'Returns' if ($param_name =~ m/^[Rr]eturns?$/) { $param_name = "Returns"; } # strip trailing whitespaces and blank lines s/\s+\n$/\n/m; s/\n+$/\n/sm; #print "Found param for symbol $current_symbol : '$param_name'= '$_'"; if ($param_name eq "Deprecated") { $in_deprecated = 1; $Deprecated{$current_symbol} = $_; } elsif ($param_name eq "Since") { $in_since = 1; chomp; $Since{$current_symbol} = $_; } elsif ($param_name eq "Stability") { $in_stability = 1; $StabilityLevel{$current_symbol} = $_; } else { push (@params, $param_name); push (@params, $param_desc); $current_param += $PARAM_FIELD_COUNT; } } else { # strip trailing whitespaces and blank lines s/\s+\n$/\n/m; s/\n+$/\n/sm; if (!m/^\s+$/) { if ($in_deprecated) { $Deprecated{$current_symbol} .= $_; } elsif ($in_since) { &LogWarning ($template, $., "multi-line since docs found"); #$Since{$current_symbol} .= $_; } elsif ($in_stability) { $StabilityLevel{$current_symbol} .= $_; } elsif ($current_param >= 0) { $params[$current_param] .= $_; } else { $symbol_doc .= $_; } } } } } # Remember to finish the current symbol doccs. if ($current_symbol ne "") { $symbol_doc =~ s/\s+$//; $SymbolTypes{$current_symbol} = $current_type; $SymbolDocs{$current_symbol} = $symbol_doc; # Check that the stability level is valid. if ($StabilityLevel{$current_symbol}) { $StabilityLevel{$current_symbol} = &ParseStabilityLevel($StabilityLevel{$current_symbol}, $template, $., "Stability level for $current_symbol"); } if ($current_param >= 0) { $SymbolParams{$current_symbol} = [ @params ]; } else { # Delete any existing params in case we are overriding a # previously read template. delete $SymbolParams{$current_symbol}; } } close (DOCS); return 1; } ############################################################################# # Function : ReadObjectHierarchy # Description : This reads in the $MODULE-hierarchy.txt file containing all # the GtkObject subclasses described in this module (and their # ancestors). # It places them in the @Objects array, and places their level # in the object hierarchy in the @ObjectLevels array, at the # same index. GtkObject, the root object, has a level of 1. # # FIXME: the version in gtkdoc-mkdb also generates tree_index.sgml # as it goes along, this should be split out into a separate # function. # # Arguments : none ############################################################################# sub ReadObjectHierarchy { @Objects = (); @ObjectLevels = (); if (! -f $OBJECT_TREE_FILE) { return; } if (!open (INPUT, $OBJECT_TREE_FILE)) { warn "Can't open $OBJECT_TREE_FILE - skipping object tree\n"; return; } # FIXME: use $OUTPUT_FORMAT # my $old_tree_index = "$SGML_OUTPUT_DIR/tree_index.$OUTPUT_FORMAT"; my $old_tree_index = "$SGML_OUTPUT_DIR/tree_index.sgml"; my $new_tree_index = "$SGML_OUTPUT_DIR/tree_index.new"; open (OUTPUT, ">$new_tree_index") || die "Can't create $new_tree_index: $!"; if ($OUTPUT_FORMAT eq "xml") { my $tree_header = $doctype_header; $tree_header =~ s/\n"); # Only emit objects if they are supposed to be documented, or if # they have documented children. To implement this, we maintain a # stack of pending objects which will be emitted if a documented # child turns up. my @pending_objects = (); my @pending_levels = (); while () { if (m/\S+/) { my $object = $&; my $level = (length($`)) / 2 + 1; my $xref = ""; while (($#pending_levels >= 0) && ($pending_levels[$#pending_levels] >= $level)) { my $pobject = pop(@pending_objects); my $plevel = pop(@pending_levels); } push (@pending_objects, $object); push (@pending_levels, $level); if (exists($KnownSymbols{$object}) && $KnownSymbols{$object} == 1) { while ($#pending_levels >= 0) { $object = shift @pending_objects; $level = shift @pending_levels; $xref = &MakeXRef ($object); print (OUTPUT ' ' x ($level * 4), "$xref\n"); push (@Objects, $object); push (@ObjectLevels, $level); } } #else { # LogWarning ($OBJECT_TREE_FILE, $., "unknown type $object"); #} } } print (OUTPUT "\n"); close (INPUT); close (OUTPUT); &UpdateFileIfChanged ($old_tree_index, $new_tree_index, 0); &OutputObjectList; } ############################################################################# # Function : ReadInterfaces # Description : This reads in the $MODULE.interfaces file. # # Arguments : none ############################################################################# sub ReadInterfaces { %Interfaces = (); if (! -f $INTERFACES_FILE) { return; } if (!open (INPUT, $INTERFACES_FILE)) { warn "Can't open $INTERFACES_FILE - skipping interfaces\n"; return; } while () { chomp; my ($object, @ifaces) = split; if (exists($KnownSymbols{$object}) && $KnownSymbols{$object} == 1) { my @knownIfaces = (); # filter out private interfaces, but leave foreign interfaces foreach my $iface (@ifaces) { if (!exists($KnownSymbols{$iface}) || $KnownSymbols{$iface} == 1) { push (@knownIfaces, $iface); } } $Interfaces{$object} = join(' ', @knownIfaces); } } close (INPUT); } ############################################################################# # Function : ReadPrerequisites # Description : This reads in the $MODULE.prerequisites file. # # Arguments : none ############################################################################# sub ReadPrerequisites { %Prerequisites = (); if (! -f $PREREQUISITES_FILE) { return; } if (!open (INPUT, $PREREQUISITES_FILE)) { warn "Can't open $PREREQUISITES_FILE - skipping prerequisites\n"; return; } while () { chomp; my ($iface, @prereqs) = split; if (exists($KnownSymbols{$iface}) && $KnownSymbols{$iface} == 1) { my @knownPrereqs = (); # filter out private prerequisites, but leave foreign prerequisites foreach my $prereq (@prereqs) { if (!exists($KnownSymbols{$prereq}) || $KnownSymbols{$prereq} == 1) { push (@knownPrereqs, $prereq); } } $Prerequisites{$iface} = join(' ', @knownPrereqs); } } close (INPUT); } ############################################################################# # Function : ReadArgsFile # Description : This reads in an existing file which contains information on # all GTK args. It creates the arrays @ArgObjects, @ArgNames, # @ArgTypes, @ArgFlags, @ArgNicks and @ArgBlurbs containing info # on the args. # Arguments : $file - the file containing the arg information. ############################################################################# sub ReadArgsFile { my ($file) = @_; my $in_arg = 0; my $arg_object; my $arg_name; my $arg_type; my $arg_flags; my $arg_nick; my $arg_blurb; my $arg_default; my $arg_range; # Reset the args info. @ArgObjects = (); @ArgNames = (); @ArgTypes = (); @ArgFlags = (); @ArgNicks = (); @ArgBlurbs = (); @ArgDefaults = (); @ArgRanges = (); if (! -f $file) { return; } if (!open (INPUT, $file)) { warn "Can't open $file - skipping args\n"; return; } while () { if (!$in_arg) { if (m/^/) { $in_arg = 1; $arg_object = ""; $arg_name = ""; $arg_type = ""; $arg_flags = ""; $arg_nick = ""; $arg_blurb = ""; $arg_default = ""; $arg_range = ""; } } else { if (m/^(.*)<\/NAME>/) { $arg_name = $1; if ($arg_name =~ m/^(.*)::(.*)$/) { $arg_object = $1; ($arg_name = $2) =~ s/_/-/g; #print "Found arg: $arg_name\n"; } else { &LogWarning ($file, $., "Invalid argument name: $arg_name"); } } elsif (m/^(.*)<\/TYPE>/) { $arg_type = $1; } elsif (m/^(.*)<\/RANGE>/) { $arg_range = $1; } elsif (m/^(.*)<\/FLAGS>/) { $arg_flags = $1; } elsif (m/^(.*)<\/NICK>/) { $arg_nick = $1; } elsif (m/^(.*)<\/BLURB>/) { $arg_blurb = $1; if ($arg_blurb eq "(null)") { $arg_blurb = ""; &LogWarning ($file, $., "Property ${arg_object}:${arg_name} has no documentation."); } } elsif (m/^(.*)<\/DEFAULT>/) { $arg_default = $1; } elsif (m%^%) { #print "Found end of arg: ${arg_object}::${arg_name}\n${arg_type} : ${arg_flags}\n"; push (@ArgObjects, $arg_object); push (@ArgNames, $arg_name); push (@ArgTypes, $arg_type); push (@ArgRanges, $arg_range); push (@ArgFlags, $arg_flags); push (@ArgNicks, $arg_nick); push (@ArgBlurbs, $arg_blurb); push (@ArgDefaults, $arg_default); $in_arg = 0; } } } close (INPUT); } ############################################################################# # Function : CheckIsObject # Description : Returns 1 if the given name is a GObject or a subclass. # It uses the global @Objects array. # Note that the @Objects array only contains classes in the # current module and their ancestors - not all GObject classes. # Arguments : $name - the name to check. ############################################################################# sub CheckIsObject { my ($name) = @_; my $object; foreach $object (@Objects) { if ($object eq $name) { return 1; } } return 0; } ############################################################################# # Function : MakeReturnField # Description : Pads a string to $RETURN_TYPE_FIELD_WIDTH. # Arguments : $str - the string to pad. ############################################################################# sub MakeReturnField { my ($str) = @_; return $str . (' ' x ($RETURN_TYPE_FIELD_WIDTH - length ($str))); } ############################################################################# # Function : GetSymbolSourceFile # Description : Get the filename where the symbol docs where taken from. # Arguments : $symbol - the symbol name ############################################################################# sub GetSymbolSourceFile { my ($symbol) = @_; if (defined($SourceSymbolSourceFile{$symbol})) { return $SourceSymbolSourceFile{$symbol}; } elsif (defined($SymbolSourceFile{$symbol})) { return $SymbolSourceFile{$symbol}; } else { return ""; } } ############################################################################# # Function : GetSymbolSourceLine # Description : Get the file line where the symbol docs where taken from. # Arguments : $symbol - the symbol name ############################################################################# sub GetSymbolSourceLine { my ($symbol) = @_; if (defined($SourceSymbolSourceLine{$symbol})) { return $SourceSymbolSourceLine{$symbol}; } elsif (defined($SymbolSourceLine{$symbol})) { return $SymbolSourceLine{$symbol}; } else { return 0; } }