#!@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 (-e $MAIN_SGML_FILE) {
open(INPUT, "<$MAIN_SGML_FILE") || die "Can't open $MAIN_SGML_FILE";
$doctype_header = "";
while () {
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>/) {
$filename = $1;
if (! defined $templates{$filename}) {
if (&ReadTemplateFile ("$TMPL_DIR/$filename", 1)) {
&MergeSourceDocumentation;
$templates{$filename}=$.;
}
} else {
&LogWarning ($file, $., "Double $filename 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>/) {
if ($in_section) {
$section_includes = $1;
} else {
if (defined $DEFAULT_INCLUDES) {
&LogWarning ($file, $., "Default 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 .= " \n";
} else {
$book_top.="\n";
$book_bottom .= " &$section_id;\n";
}
if (defined ($SourceSymbolDocs{"$TMPL_DIR/$filename:Include"})) {
if ($section_includes) {
&LogWarning ($file, $., "Section 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 = <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>/) {
$KnownSymbols{"$TMPL_DIR/$1:Long_Description"} = 1;
$KnownSymbols{"$TMPL_DIR/$1:Short_Description"} = 1;
next;
} elsif (m/^(.*)<\/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 .= "Since $Since{$symbol}";
}
if (exists $StabilityLevel{$symbol}) {
$desc .= "Stability Level: $StabilityLevel{$symbol}";
}
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 .= <code search
edit documentation
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 .= <code search
edit documentation
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") . "$symbol";
my $desc;
my @fields = ParseMacroDeclaration($declaration, \&CreateValidSGML);
my $title = $symbol . (@fields ? "()" : "");
$desc = "\n$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 <$title3
\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 IndexIndex 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;
$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;
$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 "$1>";
}
}
# 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 . "" . $elem . ">";
}
#############################################################################
# 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 "$name" 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 "$name" $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%^$declaration_type>%) {
#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;
}
}