Added British English translation by Jen Ockwell
[gtk-doc.git] / gtkdoc-mkdb.in
blob3c4cfb862375704d771bf872e5bf2a45f8e7e3c7
1 #!@PERL@ -w
2 # -*- cperl -*-
4 # gtk-doc - GTK DocBook documentation generator.
5 # Copyright (C) 1998  Damon Chaplin
6 #               2007,2008  Stefan Kost
8 # This program is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 2 of the License, or
11 # (at your option) any later version.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program; if not, write to the Free Software
20 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 #############################################################################
24 # Script      : gtkdoc-mkdb
25 # Description : This creates the DocBook files from the edited templates.
27 #               NOTE: When creating SGML IDS, we append ":CAPS" to all
28 #               all-caps identifiers to prevent name clashes. (It basically
29 #               never is the case that mixed-case identifiers would collide.)
30 #               See the CreateValidSGMLID function.
31 #############################################################################
33 use strict;
34 use Getopt::Long;
36 unshift @INC, '@PACKAGE_DATA_DIR@';
37 require "gtkdoc-common.pl";
39 # Options
41 # name of documentation module
42 my $MODULE;
43 my $TMPL_DIR;
44 my $SGML_OUTPUT_DIR;
45 my @SOURCE_DIRS;
46 my $SOURCE_SUFFIXES = "";
47 my $IGNORE_FILES = "";
48 my $PRINT_VERSION;
49 my $PRINT_HELP;
50 my $OUTPUT_ALL_SYMBOLS;
51 my $MAIN_SGML_FILE;
52 my $EXPAND_CONTENT_FILES = "";
53 my $SGML_MODE;
54 my $DEFAULT_STABILITY;
55 my $DEFAULT_INCLUDES;
56 my $OUTPUT_FORMAT;
57 my $NAME_SPACE = "";
59 my %optctl = ('module' => \$MODULE,
60               'source-dir' => \@SOURCE_DIRS,
61               'source-suffixes' => \$SOURCE_SUFFIXES,
62               'ignore-files' => \$IGNORE_FILES,
63               'output-dir' => \$SGML_OUTPUT_DIR,
64               'tmpl-dir' => \$TMPL_DIR,
65               'version' => \$PRINT_VERSION,
66               'help' => \$PRINT_HELP,
67               'main-sgml-file' => \$MAIN_SGML_FILE,
68               'expand-content-files' => \$EXPAND_CONTENT_FILES,
69               'outputallsymbols' => \$OUTPUT_ALL_SYMBOLS,
70               'sgml-mode' => \$SGML_MODE,
71               'default-stability' => \$DEFAULT_STABILITY,
72               'default-includes' => \$DEFAULT_INCLUDES,
73               'output-format' => \$OUTPUT_FORMAT,
74               'name-space' => \$NAME_SPACE);
75 GetOptions(\%optctl, "module=s", "source-dir:s", "source-suffixes:s", 
76     "ignore-files:s", "output-dir:s", "tmpl-dir:s", "version", "outputallsymbols",
77      "expand-content-files:s", "main-sgml-file:s", "extra-db-files:s", "help",
78      "sgml-mode", "default-stability:s", "default-includes:s", "output-format:s",
79      "name-space:s");
81 if ($PRINT_VERSION) {
82     print "@VERSION@\n";
83     exit 0;
86 if (!$MODULE) {
87     $PRINT_HELP = 1;
90 if ($DEFAULT_STABILITY && $DEFAULT_STABILITY ne "Stable"
91     && $DEFAULT_STABILITY ne "Private" && $DEFAULT_STABILITY ne "Unstable") {
92     $PRINT_HELP = 1;
95 if ($PRINT_HELP) {
96     print <<EOF;
97 gtkdoc-mkdb version @VERSION@ - generate docbook files
99 --module=MODULE_NAME       Name of the doc module being parsed
100 --source-dir=DIRNAME       Directories which contain inline reference material
101 --source-suffixes=SUFFIXES Suffixes of source files to scan, comma-separated
102 --ignore-files=FILES       Files or directories which should not be scanned
103                            May be used more than once for multiple directories
104 --output-dir=DIRNAME       Directory to put the generated DocBook files in
105 --tmpl-dir=DIRNAME         Directory in which template files may be found
106 --main-sgml-file=FILE      File containing the toplevel DocBook file.
107 --expand-content-files=FILES Extra DocBook files to expand abbreviations in.
108 --output-format=FORMAT     Format to use for the generated docbook, XML or SGML.
109 --sgml-mode                Allow DocBook markup in inline documentation.
110 --default-stability=LEVEL  Specify default stability Level. Valid values are
111                            Stable, Unstable, or Private.
112 --default-includes=FILENAMES Specify default includes for section Synopsis
113 --name-space=NS            Omit namespace in index.
114 --version                  Print the version of this program
115 --help                     Print this help
117     exit 0;
120 my ($empty_element_end, $doctype_header);
122 if (lc($OUTPUT_FORMAT) eq "xml") {
123     if (!$MAIN_SGML_FILE) {
124         # backwards compatibility
125         if (-e "${MODULE}-docs.sgml") {
126             $MAIN_SGML_FILE="${MODULE}-docs.sgml";
127         } else {
128             $MAIN_SGML_FILE="${MODULE}-docs.xml";
129         }
130     }
131     $OUTPUT_FORMAT = "xml";
132     $empty_element_end = "/>";
134     if (-e $MAIN_SGML_FILE) {
135         open(INPUT, "<$MAIN_SGML_FILE") || die "Can't open $MAIN_SGML_FILE";
136         $doctype_header = "";
137         while (<INPUT>) {
138             if (/^\s*<(book|chapter|article)/) {
139                 if ($_ !~ m/http:\/\/www.w3.org\/200[13]\/XInclude/) {
140                     $doctype_header = "";
141                 }
142                 last;
143             }
144             $doctype_header .= $_;
145         }
146         close(INPUT);
147     } else {
148         $doctype_header =
149 "<?xml version=\"1.0\"?>\n" .
150 "<!DOCTYPE book PUBLIC \"-//OASIS//DTD DocBook XML V4.3//EN\"\n" .
151 "               \"http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd\">\n";
152     }
153     $doctype_header =~ s/<!DOCTYPE \w+/<!DOCTYPE refentry/;
154 } else {
155     if (!$MAIN_SGML_FILE) {
156         $MAIN_SGML_FILE="${MODULE}-docs.sgml";
157     }
158     $OUTPUT_FORMAT = "sgml";
159     $doctype_header = "";
160     $empty_element_end = ">";
163 my $ROOT_DIR = ".";
165 # All the files are written in subdirectories beneath here.
166 $TMPL_DIR = $TMPL_DIR ? $TMPL_DIR : "$ROOT_DIR/tmpl";
168 # This is where we put all the DocBook output.
169 $SGML_OUTPUT_DIR = $SGML_OUTPUT_DIR ? $SGML_OUTPUT_DIR : "$ROOT_DIR/$OUTPUT_FORMAT";
171 # This file contains the object hierarchy.
172 my $OBJECT_TREE_FILE = "$ROOT_DIR/$MODULE.hierarchy";
174 # This file contains the interfaces.
175 my $INTERFACES_FILE = "$ROOT_DIR/$MODULE.interfaces";
177 # This file contains the prerequisites.
178 my $PREREQUISITES_FILE = "$ROOT_DIR/$MODULE.prerequisites";
180 # This file contains signal arguments and names.
181 my $SIGNALS_FILE = "$ROOT_DIR/$MODULE.signals";
183 # The file containing Arg information.
184 my $ARGS_FILE = "$ROOT_DIR/$MODULE.args";
186 # These global arrays store information on signals. Each signal has an entry
187 # in each of these arrays at the same index, like a multi-dimensional array.
188 my @SignalObjects;      # The GtkObject which emits the signal.
189 my @SignalNames;        # The signal name.
190 my @SignalReturns;      # The return type.
191 my @SignalFlags;        # Flags for the signal
192 my @SignalPrototypes;   # The rest of the prototype of the signal handler.
194 # These global arrays store information on Args. Each Arg has an entry
195 # in each of these arrays at the same index, like a multi-dimensional array.
196 my @ArgObjects;         # The GtkObject which has the Arg.
197 my @ArgNames;           # The Arg name.
198 my @ArgTypes;           # The Arg type - gint, GtkArrowType etc.
199 my @ArgFlags;           # How the Arg can be used - readable/writable etc.
200 my @ArgNicks;           # The nickname of the Arg.
201 my @ArgBlurbs;          # Docstring of the Arg.
202 my @ArgDefaults;        # Default value of the Arg.
203 my @ArgRanges;          # The range of the Arg type
204 # These global hashes store declaration info keyed on a symbol name.
205 my %Declarations;
206 my %DeclarationTypes;
207 my %DeclarationConditional;
208 my %DeclarationOutput;
209 my %Deprecated;
210 my %Since;
211 my %StabilityLevel;
212 my %StructHasTypedef;
214 # These global hashes store the existing documentation.
215 my %SymbolDocs;
216 my %SymbolTypes;
217 my %SymbolParams;
218 my %SymbolSourceFile;
219 my %SymbolSourceLine;
221 # These global hashes store documentation scanned from the source files.
222 my %SourceSymbolDocs;
223 my %SourceSymbolParams;
224 my %SourceSymbolSourceFile;
225 my %SourceSymbolSourceLine;
227 # all documentation goes in here, so we can do coverage analysis
228 my %AllSymbols;
229 my %AllIncompleteSymbols;
230 my %AllDocumentedSymbols;
232 # Undeclared yet documented symbols
233 my %UndeclaredSymbols;
235 # These global arrays store GObject, subclasses and the hierarchy.
236 my @Objects;
237 my @ObjectLevels;
239 my %Interfaces;
240 my %Prerequisites;
242 # holds the symbols which are mentioned in $MODULE-sections.txt
243 my %KnownSymbols;
245 # collects index entries
246 my %IndexEntriesFull;
247 my %IndexEntriesSince;
248 my %IndexEntriesDeprecated;
250 # Standard C preprocessor directives, which we ignore for '#' abbreviations.
251 my %PreProcessorDirectives;
252 $PreProcessorDirectives{"assert"} = 1;
253 $PreProcessorDirectives{"define"} = 1;
254 $PreProcessorDirectives{"elif"} = 1;
255 $PreProcessorDirectives{"else"} = 1;
256 $PreProcessorDirectives{"endif"} = 1;
257 $PreProcessorDirectives{"error"} = 1;
258 $PreProcessorDirectives{"if"} = 1;
259 $PreProcessorDirectives{"ifdef"} = 1;
260 $PreProcessorDirectives{"ifndef"} = 1;
261 $PreProcessorDirectives{"include"} = 1;
262 $PreProcessorDirectives{"line"} = 1;
263 $PreProcessorDirectives{"pragma"} = 1;
264 $PreProcessorDirectives{"unassert"} = 1;
265 $PreProcessorDirectives{"undef"} = 1;
266 $PreProcessorDirectives{"warning"} = 1;
268 # remember used annotation (to write minimal glossary)
269 my %AnnotationsUsed;
271 my %AnnotationDefinition = (
272     'allow-none' => "NULL is ok, both for passing and for returning.",
273     'array' => "Parameter points to an array of items.",
274     'default' => "Default parameter value (for in case the <acronym>shadows</acronym>-to function has less parameters).",
275     'element-type' => "Generics and defining elements of containers and arrays.",
276     'error-domains' => "Typed errors. Similar to throws in Java.",
277     'in' => "Parameter for input. Default is <acronym>transfer none</acronym>.",
278     'inout' => "Parameter for input and for returning results. Default is <acronym>transfer full</acronym>.",
279     'in-out' => "Parameter for input and for returning results. Default is <acronym>transfer full</acronym>.",
280     'not-error' => "A GError parameter is not to be handled like a normal GError.",
281     'out' => "Parameter for returning results. Default is <acronym>transfer full</acronym>.",
282     'transfer container' => "Free data container after the code is done.",
283     'transfer full' => "Free data after the code is done.",
284     'transfer none' => "Don't free data after the code is done."
287 # Create the root DocBook output directory if it doens't exist.
288 if (! -e $SGML_OUTPUT_DIR) {
289     mkdir ("$SGML_OUTPUT_DIR", 0777)
290         || die "Can't create directory: $SGML_OUTPUT_DIR";
293 # Function and other declaration output settings.
294 my $RETURN_TYPE_FIELD_WIDTH = 20;
295 my $SYMBOL_FIELD_WIDTH = 36;
296 my $SIGNAL_FIELD_WIDTH = 16;
297 my $PARAM_FIELD_COUNT = 2;
299 &ReadKnownSymbols ("$ROOT_DIR/$MODULE-sections.txt");
300 &ReadSignalsFile ($SIGNALS_FILE);
301 &ReadArgsFile ($ARGS_FILE);
302 &ReadObjectHierarchy;
303 &ReadInterfaces;
304 &ReadPrerequisites;
306 &ReadDeclarationsFile ("$ROOT_DIR/$MODULE-decl.txt", 0);
307 if (-f "$ROOT_DIR/$MODULE-overrides.txt") {
308     &ReadDeclarationsFile ("$ROOT_DIR/$MODULE-overrides.txt", 1);
311 for my $dir (@SOURCE_DIRS) {
312     &ReadSourceDocumentation ($dir);
315 my $changed = &OutputSGML ("$ROOT_DIR/$MODULE-sections.txt");
317 # If any of the DocBook SGML files have changed, update the timestamp file (so
318 # it can be used for Makefile dependencies).
319 if ($changed || ! -e "$ROOT_DIR/sgml.stamp") {
321     # try to detect the common prefix
322     # GtkWidget, GTK_WIDGET, gtk_widget -> gtk
323     if ($NAME_SPACE eq "") {
324         $NAME_SPACE="";
325         my $pos=0;
326         my $ratio=0.0;
327         do {
328             my %prefix;
329             my $letter="";
330             foreach my $symbol (keys(%IndexEntriesFull)) {
331                 if(($NAME_SPACE eq "") || $symbol =~ /^$NAME_SPACE/i) {
332                     if (length($symbol)>$pos) {
333                         $letter=substr($symbol,$pos,1);
334                         # stop prefix scanning
335                         if ($letter eq "_") {
336                             # stop on "_"
337                             last;
338                         }
339                         # Should we also stop on a uppercase char, if last was lowercase
340                         #   GtkWidget, if we have the 'W' and had the 't' before
341                         # or should we count upper and lowercase, and stop one 2nd uppercase, if we already had a lowercase
342                         #   GtkWidget, the 'W' would be the 2nd uppercase and with 't','k' we had lowercase chars before
343                         # need to recound each time as this is per symbol
344                         $prefix{uc($letter)}++;
345                     }
346                 }
347             }
348             if ($letter ne "" && $letter ne "_") {
349                 my $maxletter="";
350                 my $maxsymbols=0;
351                 foreach $letter (keys(%prefix)) {
352                     #print "$letter: $prefix{$letter}.\n";
353                     if ($prefix{$letter}>$maxsymbols) {
354                         $maxletter=$letter;
355                         $maxsymbols=$prefix{$letter};
356                     }
357                 }
358                 $ratio = scalar(keys(%IndexEntriesFull)) / $prefix{$maxletter};
359                 #print "most symbols start with $maxletter, that is ". (100 * $ratio) ." %\n";
360                 if ($ratio > 0.9) {
361                     # do another round
362                     $NAME_SPACE .= $maxletter;
363                 }
364                 $pos++;
365             }
366             else {
367                 $ratio=0.0;
368             }
369         } while ($ratio > 0.9);
370         #print "most symbols start with $NAME_SPACE\n";
371     }
373     &OutputIndexFull;
374     &OutputDeprecatedIndex;
375     &OutputSinceIndexes;
376     &OutputAnnotationGlossary;
378     open (TIMESTAMP, ">$ROOT_DIR/sgml.stamp")
379         || die "Can't create $ROOT_DIR/sgml.stamp: $!";
380     print (TIMESTAMP "timestamp");
381     close (TIMESTAMP);
384 #############################################################################
385 # Function    : OutputObjectList
386 # Description : This outputs the alphabetical list of objects, in a columned
387 #               table.
388 #               FIXME: Currently this also outputs ancestor objects
389 #               which may not actually be in this module.
390 # Arguments   : none
391 #############################################################################
393 sub OutputObjectList {
394     my $cols = 3;
395     
396     # FIXME: use $OUTPUT_FORMAT
397     # my $old_object_index = "$SGML_OUTPUT_DIR/object_index.$OUTPUT_FORMAT";
398     my $old_object_index = "$SGML_OUTPUT_DIR/object_index.sgml";
399     my $new_object_index = "$SGML_OUTPUT_DIR/object_index.new";
401     open (OUTPUT, ">$new_object_index")
402         || die "Can't create $new_object_index: $!";
404     if (lc($OUTPUT_FORMAT) eq "xml") {
405         my $header = $doctype_header;
407         $header =~ s/<!DOCTYPE \w+/<!DOCTYPE informaltable/;
408         print (OUTPUT "$header");
409     }
411     print (OUTPUT <<EOF);
412 <informaltable pgwide="1" frame="none">
413 <tgroup cols="$cols">
414 <colspec colwidth="1*"${empty_element_end}
415 <colspec colwidth="1*"${empty_element_end}
416 <colspec colwidth="1*"${empty_element_end}
417 <tbody>
420     my $count = 0;
421     my $object;
422     foreach $object (sort (@Objects)) {
423         my $xref = &MakeXRef ($object);
424         if ($count % $cols == 0) { print (OUTPUT "<row>\n"); }
425         print (OUTPUT "<entry>$xref</entry>\n");
426         if ($count % $cols == ($cols - 1)) { print (OUTPUT "</row>\n"); }
427         $count++;
428     }
429     if ($count == 0) {
430         # emit an empty row, since empty tables are invalid
431         print (OUTPUT "<row><entry> </entry></row>\n");
432     }
433     else {
434         print (OUTPUT "</row>\n");
435     }
437     print (OUTPUT <<EOF);
438 </tbody></tgroup></informaltable>
440     close (OUTPUT);
442     &UpdateFileIfChanged ($old_object_index, $new_object_index, 0);
446 #############################################################################
447 # Function    : OutputSGML
448 # Description : This collects the output for each section of the docs, and
449 #               outputs each file when the end of the section is found.
450 # Arguments   : $file - the $MODULE-sections.txt file which contains all of
451 #               the functions/macros/structs etc. being documented, organised
452 #               into sections and subsections.
453 #############################################################################
455 sub OutputSGML {
456     my ($file) = @_;
458     #print "Reading: $file\n";
459     open (INPUT, $file)
460         || die "Can't open $file: $!";
461     my $filename = "";
462     my $book_top = "";
463     my $book_bottom = "";
464     my $includes = (defined $DEFAULT_INCLUDES) ? $DEFAULT_INCLUDES : "";
465     my $section_includes = "";
466     my $in_section = 0;
467     my $title = "";
468     my $subsection = "";
469     my $synopsis;
470     my $details;
471     my $num_symbols;
472     my $changed = 0;
473     my $signals_synop = "";
474     my $signals_desc = "";
475     my $args_synop = "";
476     my $child_args_synop = "";
477     my $style_args_synop = "";
478     my $args_desc = "";
479     my $child_args_desc = "";
480     my $style_args_desc = "";
481     my $hierarchy = "";
482     my $interfaces = "";
483     my $implementations = "";
484     my $prerequisites = "";
485     my $derived = "";
486     my @file_objects = ();
487     my %templates = ();
488     my %symbols = ();
490     # merge the source docs, in case there are no templates
491     &MergeSourceDocumentation;
493     while (<INPUT>) {
494         if (m/^#/) {
495             next;
497         } elsif (m/^<SECTION>/) {
498             $synopsis = "";
499             $details = "";
500             $num_symbols = 0;
501             $in_section = 1;
502             @file_objects = ();
503             %symbols = ();
505         } elsif (m/^<SUBSECTION\s*(.*)>/i) {
506             $synopsis .= "\n";
507             $subsection = $1;
509         } elsif (m/^<SUBSECTION>/) {
511         } elsif (m/^<TITLE>(.*)<\/TITLE>/) {
512             $title = $1;
513             #print "Section: $title\n";
515             # We don't want warnings if object & class structs aren't used.
516             $DeclarationOutput{$title} = 1;
517             $DeclarationOutput{"${title}Class"} = 1;
518             $DeclarationOutput{"${title}Iface"} = 1;
519             $DeclarationOutput{"${title}Interface"} = 1;
521         } elsif (m/^<FILE>(.*)<\/FILE>/) {
522             $filename = $1;
523             if (! defined $templates{$filename}) {
524                if (&ReadTemplateFile ("$TMPL_DIR/$filename", 1)) {
525                    &MergeSourceDocumentation;
526                    $templates{$filename}=$.;
527                }
528             } else {
529                 &LogWarning ($file, $., "Double <FILE>$filename</FILE> entry. ".
530                     "Previous occurrence on line ".$templates{$filename}.".");
531             }
533         } elsif (m/^<INCLUDE>(.*)<\/INCLUDE>/) {
534             if ($in_section) {
535                 $section_includes = $1;
536             } else {
537                 if (defined $DEFAULT_INCLUDES) {
538                     &LogWarning ($file, $., "Default <INCLUDE> being overridden by command line option.");
539                 }
540                 else {
541                     $includes = $1;
542                 }
543             }
545         } elsif (m/^<\/SECTION>/) {
546             if($title eq "" && $filename eq "") {
547                 &LogWarning ($file, $., "Section has no title and no file.");
548             }
549             # FIXME: one of those would be enough
550             if ($title eq "") {
551                 $title = $filename;
552             }
553             if ($filename eq "") {
554                 $filename = $title;
555             }
556             #print "End of section: $title\n";
558             $filename =~ s/\s/_/g;
560             my $section_id = $SourceSymbolDocs{"$TMPL_DIR/$filename:Section_Id"};
561             if (defined ($section_id) && $section_id !~ m/^\s*$/) {
562                 # Do nothing. Use section_id as it is.
563             } elsif (&CheckIsObject ($title)) {
564                 # GtkObjects use their class name as the ID.
565                 $section_id = &CreateValidSGMLID ($title);
566             } else {
567                 $section_id = &CreateValidSGMLID ("$MODULE-$title");
568             }
570             if ($num_symbols > 0) {
571                 # collect documents
572                 if (lc($OUTPUT_FORMAT) eq "xml") {
573                     $book_bottom .= "    <xi:include href=\"xml/$filename.xml\"/>\n";
574                 } else {
575                     $book_top.="<!ENTITY $section_id SYSTEM \"sgml/$filename.sgml\">\n";
576                     $book_bottom .= "    &$section_id;\n";
577                 }
579                 if (defined ($SourceSymbolDocs{"$TMPL_DIR/$filename:Include"})) {
580                     if ($section_includes) {
581                         &LogWarning ($file, $., "Section <INCLUDE> being overridden by inline comments.");
582                     }
583                     $section_includes = $SourceSymbolDocs{"$TMPL_DIR/$filename:Include"};
584                 }
585                 if ($section_includes eq "") {
586                     $section_includes = $includes;
587                 }
589                  $signals_synop =~ s/^\n*//g;
590                  $signals_synop =~ s/\n+$/\n/g;
591                 if ($signals_synop ne '') {
592                     $signals_synop = <<EOF;
593 <refsect1 id="$section_id.signals" role="signal_proto">
594 <title role="signal_proto.title">Signals</title>
595 <synopsis>
596 ${signals_synop}</synopsis>
597 </refsect1>
599                      $signals_desc =~ s/^\n*//g;
600                      $signals_desc =~ s/\n+$/\n/g;
601                      $signals_desc =~ s/(\s|\n)+$//ms;
602                     $signals_desc  = <<EOF;
603 <refsect1 id="$section_id.signal-details" role="signals">
604 <title role="signals.title">Signal Details</title>
605 $signals_desc
606 </refsect1>
608                 }
610                  $args_synop =~ s/^\n*//g;
611                  $args_synop =~ s/\n+$/\n/g;
612                 if ($args_synop ne '') {
613                     $args_synop = <<EOF;
614 <refsect1 id="$section_id.properties" role="properties">
615 <title role="properties.title">Properties</title>
616 <synopsis>
617 ${args_synop}</synopsis>
618 </refsect1>
620                      $args_desc =~ s/^\n*//g;
621                      $args_desc =~ s/\n+$/\n/g;
622                      $args_desc =~ s/(\s|\n)+$//ms;
623                     $args_desc  = <<EOF;
624 <refsect1 id="$section_id.property-details" role="property_details">
625 <title role="property_details.title">Property Details</title>
626 $args_desc
627 </refsect1>
629                 }
631                  $child_args_synop =~ s/^\n*//g;
632                  $child_args_synop =~ s/\n+$/\n/g;
633                 if ($child_args_synop ne '') {
634                     $args_synop .= <<EOF;
635 <refsect1 id="$section_id.child-properties" role="child_properties">
636 <title role="child_properties.title">Child Properties</title>
637 <synopsis>
638 ${child_args_synop}</synopsis>
639 </refsect1>
641                      $child_args_desc =~ s/^\n*//g;
642                      $child_args_desc =~ s/\n+$/\n/g;
643                      $child_args_desc =~ s/(\s|\n)+$//ms;
644                     $args_desc .= <<EOF;
645 <refsect1 id="$section_id.child-property-details" role="child_property_details">
646 <title role="child_property_details.title">Child Property Details</title>
647 $child_args_desc
648 </refsect1>
650                 }
652                  $style_args_synop =~ s/^\n*//g;
653                  $style_args_synop =~ s/\n+$/\n/g;
654                 if ($style_args_synop ne '') {
655                     $args_synop .= <<EOF;
656 <refsect1 id="$section_id.style-properties" role="style_properties">
657 <title role="style_properties.title">Style Properties</title>
658 <synopsis>
659 ${style_args_synop}</synopsis>
660 </refsect1>
662                      $style_args_desc =~ s/^\n*//g;
663                      $style_args_desc =~ s/\n+$/\n/g;
664                      $style_args_desc =~ s/(\s|\n)+$//ms;
665                     $args_desc .= <<EOF;
666 <refsect1 id="$section_id.style-property-details" role="style_properties_details">
667 <title role="style_properties_details.title">Style Property Details</title>
668 $style_args_desc
669 </refsect1>
671                 }
673                  $hierarchy =~ s/^\n*//g;
674                  $hierarchy =~ s/\n+$/\n/g;
675                  $hierarchy =~ s/(\s|\n)+$//ms;
676                 if ($hierarchy ne "") {
677                     $hierarchy = <<EOF;
678 <refsect1 id="$section_id.object-hierarchy" role="object_hierarchy">
679 <title role="object_hierarchy.title">Object Hierarchy</title>
680 $hierarchy
681 </refsect1>
683                 }
685                  $interfaces =~ s/^\n*//g;
686                  $interfaces =~ s/\n+$/\n/g;
687                  $interfaces =~ s/(\s|\n)+$//ms;
688                 if ($interfaces ne "") {
689                     $interfaces = <<EOF;
690 <refsect1 id="$section_id.implemented-interfaces" role="impl_interfaces">
691 <title role="impl_interfaces.title">Implemented Interfaces</title>
692 $interfaces
693 </refsect1>
695                 }
697                  $implementations =~ s/^\n*//g;
698                  $implementations =~ s/\n+$/\n/g;
699                  $implementations =~ s/(\s|\n)+$//ms;
700                 if ($implementations ne "") {
701                     $implementations = <<EOF;
702 <refsect1 id="$section_id.implementations" role="implementations">
703 <title role="implementations.title">Known Implementations</title>
704 $implementations
705 </refsect1>
707                 }
709                  $prerequisites =~ s/^\n*//g;
710                  $prerequisites =~ s/\n+$/\n/g;
711                  $prerequisites =~ s/(\s|\n)+$//ms;
712                 if ($prerequisites ne "") {
713                     $prerequisites = <<EOF;
714 <refsect1 id="$section_id.prerequisites" role="prerequisites">
715 <title role="prerequisites.title">Prerequisites</title>
716 $prerequisites
717 </refsect1>
719                 }
721                  $derived =~ s/^\n*//g;
722                  $derived =~ s/\n+$/\n/g;
723                  $derived =~ s/(\s|\n)+$//ms;
724                 if ($derived ne "") {
725                     $derived = <<EOF;
726 <refsect1 id="$section_id.derived-interfaces" role="derived_interfaces">
727 <title role="derived_interfaces.title">Known Derived Interfaces</title>
728 $derived
729 </refsect1>
731                 }
733                 $synopsis =~ s/^\n*//g;
734                 $synopsis =~ s/\n+$/\n/g;
735                 my $file_changed = &OutputSGMLFile ($filename, $title, $section_id,
736                                                     $section_includes,
737                                                     \$synopsis, \$details,
738                                                     \$signals_synop, \$signals_desc,
739                                                     \$args_synop, \$args_desc,
740                                                     \$hierarchy, \$interfaces,
741                                                     \$implementations,
742                                                     \$prerequisites, \$derived,
743                                                     \@file_objects);
744                 if ($file_changed) {
745                     $changed = 1;
746                 }
747             }
748             $title = "";
749             $subsection = "";
750             $in_section = 0;
751             $section_includes = "";
752             $signals_synop = "";
753             $signals_desc = "";
754             $args_synop = "";
755             $child_args_synop = "";
756             $style_args_synop = "";
757             $args_desc = "";
758             $child_args_desc = "";
759             $style_args_desc = "";
760             $hierarchy = "";
761             $interfaces = "";
762             $implementations = "";
763             $prerequisites = "";
764             $derived = "";
766         } elsif (m/^(\S+)/) {
767             my $symbol = $1;
768             #print "  Symbol: $symbol\n";
770             # FIXME: check for duplicate entries
771             if (! defined $symbols{$symbol}) {
772                 my $declaration = $Declarations{$symbol};
773                 if (defined ($declaration)) {
774                     # We don't want standard macros/functions of GtkObjects,
775                     # or private declarations.
776                     if ($subsection ne "Standard" && $subsection ne "Private") {
777                         if (&CheckIsObject ($symbol)) {
778                             push @file_objects, $symbol;
779                         }
780                         my ($synop, $desc) = &OutputDeclaration ($symbol,
781                                                                  $declaration);
782                         my ($sig_synop, $sig_desc) = &GetSignals ($symbol);
783                         my ($arg_synop, $child_arg_synop, $style_arg_synop,
784                             $arg_desc, $child_arg_desc, $style_arg_desc) = &GetArgs ($symbol);
785                         my $hier = &GetHierarchy ($symbol);
786                         my $ifaces = &GetInterfaces ($symbol);
787                         my $impls = &GetImplementations ($symbol);
788                         my $prereqs = &GetPrerequisites ($symbol);
789                         my $der = &GetDerived ($symbol);
790                         $synopsis .= $synop;
791                         $details .= $desc;
792                         $signals_synop .= $sig_synop;
793                         $signals_desc .= $sig_desc;
794                         $args_synop .= $arg_synop;
795                         $child_args_synop .= $child_arg_synop;
796                         $style_args_synop .= $style_arg_synop;
797                         $args_desc .= $arg_desc;
798                         $child_args_desc .= $child_arg_desc;
799                         $style_args_desc .= $style_arg_desc;
800                         $hierarchy .= $hier;
801                         $interfaces .= $ifaces;
802                         $implementations .= $impls;
803                         $prerequisites .= $prereqs;
804                         $derived .= $der;
805                     }
806     
807                     # Note that the declaration has been output.
808                     $DeclarationOutput{$symbol} = 1;
809                 } elsif ($subsection ne "Standard" && $subsection ne "Private") {
810                     $UndeclaredSymbols{$symbol} = 1;
811                     &LogWarning ($file, $., "No declaration found for $symbol.");
812                 }
813                 $num_symbols++;
814                 $symbols{$symbol}=$.;
815             }
816             else {
817                 &LogWarning ($file, $., "Double symbol entry for $symbol. ".
818                     "Previous occurrence on line ".$symbols{$symbol}.".");
819             }
820         }
821     }
822     close (INPUT);
824     &OutputMissingDocumentation;
825     &OutputUndeclaredSymbols;
827     if ($OUTPUT_ALL_SYMBOLS) {
828         &OutputAllSymbols;
829     }
831     for $filename (split (' ', $EXPAND_CONTENT_FILES)) {
832         my $file_changed = &OutputExtraFile ($filename);
833         if ($file_changed) {
834             $changed = 1;
835         }
836     }
838     &OutputBook ($book_top, $book_bottom);
840     return $changed;
843 #############################################################################
844 # Function    : OutputIndex
845 # Description : This writes an indexlist that can be included into the main-
846 #               document into an <index> tag.
847 #############################################################################
849 sub OutputIndex {
850     my ($basename, $apiindexref ) = @_;
851     my %apiindex = %{$apiindexref};
852     my $old_index = "$SGML_OUTPUT_DIR/$basename.xml";
853     my $new_index = "$SGML_OUTPUT_DIR/$basename.new";
854     my $lastletter = " ";
855     my $divopen = 0;
856     my $symbol;
857     my $short_symbol;
859     open (OUTPUT, ">$new_index")
860         || die "Can't create $new_index";
862     my $header = $doctype_header;
863     $header =~ s/<!DOCTYPE \w+/<!DOCTYPE indexdiv/;
865     print (OUTPUT "$header<indexdiv>\n");
867     #print "generate $basename index (".%apiindex." entries)\n";
868     
869     # do a case insensitive sort while chopping off the prefix
870     foreach my $hash (
871         sort { $$a{criteria} cmp $$b{criteria} }
872         map { my $x = uc($_); $x =~ s/^$NAME_SPACE\_?(.*)/$1/i; { criteria => $x, original => $_, short => $1 } } 
873         keys %apiindex) {
875         $symbol = $$hash{original};
876         if(defined($$hash{short})) {
877             $short_symbol = $$hash{short};
878         } else {
879             $short_symbol = $symbol;
880         }
882         my $curletter = uc(substr($short_symbol,0,1));
883         my $id = $apiindex{$symbol};
885         #print "  add symbol $symbol with $id to index in section $curletter\n";
887         if ($curletter ne $lastletter) {
888             $lastletter = $curletter;
890             if ($divopen == 1) {
891                 print (OUTPUT "</indexdiv>\n");
892             }
893             print (OUTPUT "<indexdiv><title>$curletter</title>\n");
894             $divopen = 1;
895         }
897         print (OUTPUT <<EOF);
898 <indexentry><primaryie linkends="$id"><link linkend="$id">$symbol</link></primaryie></indexentry>
900     }
902     if ($divopen == 1) {
903         print (OUTPUT "</indexdiv>\n");
904     }
905     print (OUTPUT "</indexdiv>\n");
906     close (OUTPUT);
908     &UpdateFileIfChanged ($old_index, $new_index, 0);
912 #############################################################################
913 # Function    : OutputIndexFull
914 # Description : This writes the full api indexlist that can be included into the
915 #               main document into an <index> tag.
916 #############################################################################
918 sub OutputIndexFull {
919     &OutputIndex ("api-index-full", \%IndexEntriesFull);
923 #############################################################################
924 # Function    : OutputDeprecatedIndex
925 # Description : This writes the deprecated api indexlist that can be included
926 #               into the main document into an <index> tag.
927 #############################################################################
929 sub OutputDeprecatedIndex {
930     &OutputIndex ("api-index-deprecated", \%IndexEntriesDeprecated);
934 #############################################################################
935 # Function    : OutputSinceIndexes
936 # Description : This writes the 'since' api indexlists that can be included into
937 #               the main document into an <index> tag.
938 #############################################################################
940 sub OutputSinceIndexes {
941     my @sinces = keys %{{ map { $_ => 1 } values %Since }};
943     foreach my $version (@sinces) {
944         #print "Since : [$version]\n";
945         # todo make filtered hash
946         #my %index = grep { $Since{$_} eq $version } %IndexEntriesSince;
947         my %index = map { $_ => $IndexEntriesSince{$_} } grep { $Since{$_} eq $version } keys %IndexEntriesSince;
949         &OutputIndex ("api-index-$version", \%index);
950     }
953 #############################################################################
954 # Function    : OutputSinceIndexes
955 # Description : This writes the 'since' api indexlists that can be included into
956 #               the main document into an <index> tag.
957 #############################################################################
959 sub OutputAnnotationGlossary {
960     my $old_glossary = "$SGML_OUTPUT_DIR/annotation-glossary.xml";
961     my $new_glossary = "$SGML_OUTPUT_DIR/annotation-glossary.new";
962     my $lastletter = " ";
963     my $divopen = 0;
965     # TODO: if there are no annotations used return
966     return if (! keys(%AnnotationsUsed));
968     open (OUTPUT, ">$new_glossary")
969         || die "Can't create $new_glossary";
971     my $header = $doctype_header;
972     $header =~ s/<!DOCTYPE \w+/<!DOCTYPE glossary/;
974     print (OUTPUT  <<EOF);
975 $header
976 <glossary id="annotation-glossary">
977   <title>Annotation Glossary</title>
980     foreach my $annotation (keys(%AnnotationsUsed)) {
981         my $def = $AnnotationDefinition{$annotation};
982         my $curletter = uc(substr($annotation,0,1));
984         if ($curletter ne $lastletter) {
985             $lastletter = $curletter;
986       
987             if ($divopen == 1) {
988                 print (OUTPUT "</glossdiv>\n");
989             }
990             print (OUTPUT "<glossdiv><title>$curletter</title>\n");
991             $divopen = 1;
992         }
993         print (OUTPUT <<EOF);
994     <glossentry>
995       <glossterm><anchor id="annotation-glossterm-$annotation"/>$annotation</glossterm>
996       <glossdef>
997         <para>$def</para>
998       </glossdef>
999     </glossentry>
1001     }
1003     if ($divopen == 1) {
1004         print (OUTPUT "</glossdiv>\n");
1005     }
1006     print (OUTPUT "</glossary>\n");
1007     close (OUTPUT);
1009     &UpdateFileIfChanged ($old_glossary, $new_glossary, 0);
1012 #############################################################################
1013 # Function    : ReadKnownSymbols
1014 # Description : This collects the names of non-private symbols from the
1015 #               $MODULE-sections.txt file.
1016 # Arguments   : $file - the $MODULE-sections.txt file which contains all of
1017 #               the functions/macros/structs etc. being documented, organised
1018 #               into sections and subsections.
1019 #############################################################################
1021 sub ReadKnownSymbols {
1022     my ($file) = @_;
1024     my $subsection = "";
1026     #print "Reading: $file\n";
1027     open (INPUT, $file)
1028         || die "Can't open $file: $!";
1030     while (<INPUT>) {
1031         if (m/^#/) {
1032             next;
1034         } elsif (m/^<SECTION>/) {
1035             $subsection = "";
1037         } elsif (m/^<SUBSECTION\s*(.*)>/i) {
1038             $subsection = $1;
1040         } elsif (m/^<SUBSECTION>/) {
1041             next;
1043         } elsif (m/^<TITLE>(.*)<\/TITLE>/) {
1044             next;
1046         } elsif (m/^<FILE>(.*)<\/FILE>/) {
1047             $KnownSymbols{"$TMPL_DIR/$1:Long_Description"} = 1;
1048             $KnownSymbols{"$TMPL_DIR/$1:Short_Description"} = 1;
1049             next;
1051         } elsif (m/^<INCLUDE>(.*)<\/INCLUDE>/) {
1052             next;
1054         } elsif (m/^<\/SECTION>/) {
1055             next;
1057         } elsif (m/^(\S+)/) {
1058             my $symbol = $1;
1060             if ($subsection ne "Standard" && $subsection ne "Private") {
1061                 $KnownSymbols{$symbol} = 1;
1062             }
1063             else {
1064                 $KnownSymbols{$symbol} = 0;
1065             }
1066         }
1067     }
1068     close (INPUT);
1072 #############################################################################
1073 # Function    : OutputDeclaration
1074 # Description : Returns the synopsis and detailed description DocBook
1075 #               describing one function/macro etc.
1076 # Arguments   : $symbol - the name of the function/macro begin described.
1077 #               $declaration - the declaration of the function/macro.
1078 #############################################################################
1080 sub OutputDeclaration {
1081     my ($symbol, $declaration) = @_;
1083     my $type = $DeclarationTypes {$symbol};
1084     if ($type eq 'MACRO') {
1085         return &OutputMacro ($symbol, $declaration);
1086     } elsif ($type eq 'TYPEDEF') {
1087         return &OutputTypedef ($symbol, $declaration);
1088     } elsif ($type eq 'STRUCT') {
1089         return &OutputStruct ($symbol, $declaration);
1090     } elsif ($type eq 'ENUM') {
1091         return &OutputEnum ($symbol, $declaration);
1092     } elsif ($type eq 'UNION') {
1093         return &OutputUnion ($symbol, $declaration);
1094     } elsif ($type eq 'VARIABLE') {
1095         return &OutputVariable ($symbol, $declaration);
1097     } elsif ($type eq 'FUNCTION') {
1098         return &OutputFunction ($symbol, $declaration, $type);
1099     } elsif ($type eq 'USER_FUNCTION') {
1100         return &OutputFunction ($symbol, $declaration, $type);
1101     } else {
1102         die "Unknown symbol type";
1103     }
1107 #############################################################################
1108 # Function    : OutputSymbolTraits
1109 # Description : Returns the Since and StabilityLevel paragraphs for a symbol.
1110 # Arguments   : $symbol - the name of the function/macro begin described.
1111 #############################################################################
1113 sub OutputSymbolTraits {
1114     my ($symbol) = @_;
1115     my $desc = "";
1117     if (exists $Since{$symbol}) {
1118         $desc .= "<para role=\"since\">Since $Since{$symbol}</para>";
1119     }
1120     if (exists $StabilityLevel{$symbol}) {
1121         $desc .= "<para role=\"stability\">Stability Level: $StabilityLevel{$symbol}</para>";
1122     }
1123     return $desc;
1126 #############################################################################
1127 # Function    : Outpu{Symbol,Section}ExtraLinks
1128 # Description : Returns extralinks for the symbol (if enabled).
1129 # Arguments   : $symbol - the name of the function/macro begin described.
1130 #############################################################################
1132 sub uri_escape {
1133     my $text = $_[0];
1134     return undef unless defined $text;
1136     # Build a char to hex map
1137     my %escapes = ();
1138     for (0..255) {
1139             $escapes{chr($_)} = sprintf("%%%02X", $_);
1140     }
1142     # Default unsafe characters.  RFC 2732 ^(uric - reserved)
1143     $text =~ s/([^A-Za-z0-9\-_.!~*'()])/$escapes{$1}/g;
1145     return $text;
1148 sub OutputSymbolExtraLinks {
1149     my ($symbol) = @_;
1150     my $desc = "";
1152     if (0) { # NEW FEATURE: needs configurability
1153     my $sstr = &uri_escape($symbol);
1154     my $mstr = &uri_escape($MODULE);
1155     $desc .= <<EOF;
1156 <ulink role="extralinks" url="http://www.google.com/codesearch?q=$sstr">code search</ulink>
1157 <ulink role="extralinks" url="http://library.gnome.org/edit?module=$mstr&amp;symbol=$sstr">edit documentation</ulink>
1159     }
1160     return $desc;
1163 sub OutputSectionExtraLinks {
1164     my ($symbol,$docsymbol) = @_;
1165     my $desc = "";
1167     if (0) { # NEW FEATURE: needs configurability
1168     my $sstr = &uri_escape($symbol);
1169     my $mstr = &uri_escape($MODULE);
1170     my $dsstr = &uri_escape($docsymbol);
1171     $desc .= <<EOF;
1172 <ulink role="extralinks" url="http://www.google.com/codesearch?q=$sstr">code search</ulink>
1173 <ulink role="extralinks" url="http://library.gnome.org/edit?module=$mstr&amp;symbol=$dsstr">edit documentation</ulink>
1175     }
1176     return $desc;
1180 #############################################################################
1181 # Function    : OutputMacro
1182 # Description : Returns the synopsis and detailed description of a macro.
1183 # Arguments   : $symbol - the macro.
1184 #               $declaration - the declaration of the macro.
1185 #############################################################################
1187 sub OutputMacro {
1188     my ($symbol, $declaration) = @_;
1189     my $id = &CreateValidSGMLID ($symbol);
1190     my $condition = &MakeConditionDescription ($symbol);
1191     my $synop = &MakeReturnField("#define") . "<link linkend=\"$id\">$symbol</link>";
1192     my $desc;
1193     my $padding = "";
1194     my $args = "";
1195     my $pad;
1197     if ($declaration =~ m/^\s*#\s*define(\s+)\w+(\([^\)]*\))/) {
1198         $padding = $1;
1199         $args = $2;
1201         if (length ($symbol) < $SYMBOL_FIELD_WIDTH) {
1202             $synop .= (' ' x ($SYMBOL_FIELD_WIDTH - length ($symbol)));
1203         }
1205         # Try to align all the lines of args correctly.
1206         $pad = ' ' x ($RETURN_TYPE_FIELD_WIDTH + $SYMBOL_FIELD_WIDTH + 1);
1207         my $args_padded = $args;
1208         $args_padded =~ s/ *\\\n */\n$pad/gm;
1209         $synop .= &CreateValidSGML ($args_padded);
1210     }
1211     $synop .= "\n";
1213     my $title = $symbol . ($args ? "()" : "");
1214     $desc = "<refsect2 id=\"$id\" role=\"macro\"$condition>\n<title>$title</title>\n";
1215     $desc .= MakeIndexterms($symbol, $id);
1216     $desc .= "\n";
1217     $desc .= OutputSymbolExtraLinks($symbol);
1219     # Don't output the macro definition if is is a conditional macro or it
1220     # looks like a function, i.e. starts with "g_" or "_?gnome_", or it is
1221     # longer than 2 lines, otherwise we get lots of complicated macros like
1222     # g_assert.
1223     if (!defined ($DeclarationConditional{$symbol}) && ($symbol !~ m/^g_/)
1224         && ($symbol !~ m/^_?gnome_/) && (($declaration =~ tr/\n//) < 2)) {
1225         my $decl_out = &CreateValidSGML ($declaration);
1226         $desc .= "<programlisting>$decl_out</programlisting>\n";
1227     } else {
1228         $desc .= "<programlisting>" . &MakeReturnField("#define") . "$symbol";
1229         # Align each line so that if should all line up OK.
1230         $pad = ' ' x ($RETURN_TYPE_FIELD_WIDTH - length ("#define "));
1231         $args =~ s/\n/\n$pad/gm;
1232         $desc .= &CreateValidSGML ($args);
1233         $desc .= "</programlisting>\n";
1234     }
1236     $desc .= &MakeDeprecationNote($symbol);
1238     my $parameters = &OutputParamDescriptions ("MACRO", $symbol);
1239     my $parameters_output = 0;
1241     if (defined ($SymbolDocs{$symbol})) {
1242         my $symbol_docs = &ExpandAbbreviations($symbol, $SymbolDocs{$symbol});
1244         # Try to insert the parameter table at the author's desired position.
1245         # Otherwise we need to tag it onto the end.
1246         if ($symbol_docs =~ s/<!--PARAMETERS-->/$parameters/) {
1247           $parameters_output = 1;
1248         }
1249         $desc .= $symbol_docs;
1250     }
1252     if ($parameters_output == 0) {
1253         $desc .= $parameters;
1254     }
1256     $desc .= OutputSymbolTraits ($symbol);
1257     $desc .= "</refsect2>\n";
1258     return ($synop, $desc);
1262 #############################################################################
1263 # Function    : OutputTypedef
1264 # Description : Returns the synopsis and detailed description of a typedef.
1265 # Arguments   : $symbol - the typedef.
1266 #               $declaration - the declaration of the typedef,
1267 #                 e.g. 'typedef unsigned int guint;'
1268 #############################################################################
1270 sub OutputTypedef {
1271     my ($symbol, $declaration) = @_;
1272     my $id = &CreateValidSGMLID ($symbol);
1273     my $condition = &MakeConditionDescription ($symbol);
1274     my $synop = &MakeReturnField("typedef") . "<link linkend=\"$id\">$symbol</link>;\n";
1275     my $desc = "<refsect2 id=\"$id\" role=\"typedef\"$condition>\n<title>$symbol</title>\n";
1277     $desc .= MakeIndexterms($symbol, $id);
1278     $desc .= "\n";
1279     $desc .= OutputSymbolExtraLinks($symbol);
1281     if (!defined ($DeclarationConditional{$symbol})) {
1282         my $decl_out = &CreateValidSGML ($declaration);
1283         $desc .= "<programlisting>$decl_out</programlisting>\n";
1284     }
1286     $desc .= &MakeDeprecationNote($symbol);
1288     if (defined ($SymbolDocs{$symbol})) {
1289         $desc .= &ExpandAbbreviations($symbol, $SymbolDocs{$symbol});
1290     }
1291     $desc .= OutputSymbolTraits ($symbol);
1292     $desc .= "</refsect2>\n";
1293     return ($synop, $desc);
1297 #############################################################################
1298 # Function    : OutputStruct
1299 # Description : Returns the synopsis and detailed description of a struct.
1300 #               We check if it is a widget struct, and if so we only output
1301 #               parts of it that are noted as public fields.
1302 #               We also use a different SGML ID for widget structs, since the
1303 #               original ID is used for the entire RefEntry.
1304 # Arguments   : $symbol - the struct.
1305 #               $declaration - the declaration of the struct.
1306 #############################################################################
1308 sub OutputStruct {
1309     my ($symbol, $declaration) = @_;
1311     my $is_widget_struct = 0;
1312     my $default_to_public = 1;
1313     if (&CheckIsObject ($symbol)) {
1314     #print "Found widget struct: $symbol\n";
1315     $is_widget_struct = 1;
1316         $default_to_public = 0;
1317     }
1319     my $id;
1320     my $condition;
1321     if ($is_widget_struct) {
1322         $id = &CreateValidSGMLID ($symbol . "_struct");
1323         $condition = &MakeConditionDescription ($symbol . "_struct");
1324     } else {
1325         $id = &CreateValidSGMLID ($symbol);
1326         $condition = &MakeConditionDescription ($symbol);
1327     }
1329     # Determine if it is a simple struct or it also has a typedef.
1330     my $has_typedef = 0;
1331     if ($StructHasTypedef{$symbol} || $declaration =~ m/^\s*typedef\s+/) {
1332       $has_typedef = 1;
1333     }
1335     my $synop;
1336     my $desc;
1337     if ($has_typedef) {
1338         # For structs with typedefs we just output the struct name.
1339         $synop = &MakeReturnField("") . "<link linkend=\"$id\">$symbol</link>;\n";
1340         $desc = "<refsect2 id=\"$id\" role=\"struct\"$condition>\n<title>$symbol</title>\n";
1341     } else {
1342         $synop = &MakeReturnField("struct") . "<link linkend=\"$id\">$symbol</link>;\n";
1343         $desc = "<refsect2 id=\"$id\" role=\"struct\"$condition>\n<title>struct $symbol</title>\n";
1344     }
1346     $desc .= MakeIndexterms($symbol, $id);
1347     $desc .= "\n";
1348     $desc .= OutputSymbolExtraLinks($symbol);
1350     # Form a pretty-printed, private-data-removed form of the declaration
1352     my $decl_out = "";
1353     if ($declaration =~ m/^\s*$/) {
1354         #print "Found opaque struct: $symbol\n";
1355         $decl_out = "typedef struct _$symbol $symbol;";
1356     } elsif ($declaration =~ m/^\s*struct\s+\w+\s*;\s*$/) {
1357         #print "Found opaque struct: $symbol\n";
1358         $decl_out = "struct $symbol;";
1359     } else {
1360         my $public = $default_to_public;
1361         my $new_declaration = "";
1362         my $decl_line;
1363         my $decl = $declaration;
1365         if ($decl =~ m/^\s*(typedef\s+)?struct\s*\w*\s*(?:\/\*.*\*\/)?\s*{(.*)}\s*\w*\s*;\s*$/s) {
1366             my $struct_contents = $2;
1368             foreach $decl_line (split (/\n/, $struct_contents)) {
1369                 #print "Struct line: $decl_line\n";
1370                 if ($decl_line =~ m%/\*\s*<\s*public\s*>\s*\*/%) {
1371                     $public = 1;
1372                 } elsif ($decl_line =~ m%/\*\s*<\s*(private|protected)\s*>\s*\*/%) {
1373                     $public = 0;
1374                 } elsif ($public) {
1375                     $new_declaration .= $decl_line . "\n";
1376                 }
1377             }
1379             if ($new_declaration) {
1380                 # Strip any blank lines off the ends.
1381                 $new_declaration =~ s/^\s*\n//;
1382                 $new_declaration =~ s/\n\s*$/\n/;
1384                 if ($has_typedef) {
1385                     $decl_out = "typedef struct {\n" . $new_declaration
1386                       . "} $symbol;\n";
1387                 } else {
1388                     $decl_out = "struct $symbol {\n" . $new_declaration
1389                       . "};\n";
1390                 }
1391             }
1392         } else {
1393             &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1394                 "Couldn't parse struct:\n$declaration");
1395         }
1397         # If we couldn't parse the struct or it was all private, output an
1398         # empty struct declaration.
1399         if ($decl_out eq "") {
1400             if ($has_typedef) {
1401                 $decl_out = "typedef struct _$symbol $symbol;";
1402             } else {
1403                 $decl_out = "struct $symbol;";
1404             }
1405         }
1406     }
1408     $decl_out = &CreateValidSGML ($decl_out);
1409     $desc .= "<programlisting>$decl_out</programlisting>\n";
1411     $desc .= &MakeDeprecationNote($symbol);
1413     if (defined ($SymbolDocs{$symbol})) {
1414         $desc .= &ExpandAbbreviations($symbol, $SymbolDocs{$symbol});
1415     }
1417     # Create a table of fields and descriptions
1419     # FIXME: Inserting &#160's into the produced type declarations here would
1420     #        improve the output in most situations ... except for function
1421     #        members of structs!
1422     my @fields = ParseStructDeclaration($declaration, !$default_to_public,
1423                                         0, \&MakeXRef,
1424                                         sub {
1425                                             "<structfield id=\"$id.$_[0]\">$_[0]</structfield>";
1426                                         });
1427     my $params = $SymbolParams{$symbol};
1429     # If no parameters are filled in, we don't generate the description
1430     # table, for backwards compatibility
1432     my $found = 0;
1433     if (defined $params) {
1434         for (my $i = 1; $i <= $#$params; $i += $PARAM_FIELD_COUNT) {
1435             if ($params->[$i] =~ /\S/) {
1436                 $found = 1;
1437                 last;
1438             }
1439         }
1440     }
1442     if ($found) {
1443         my %field_descrs = @$params;
1445             $desc .= <<EOF;
1446 <variablelist role="struct">
1448         while (@fields) {
1449             my $field_name = shift @fields;
1450             my $text = shift @fields;
1451             my $field_descr = $field_descrs{$field_name};
1453             $desc .= "<varlistentry>\n<term>$text</term>\n";
1454             if (defined $field_descr) {
1455                 $desc .= "<listitem><simpara>".&ExpandAbbreviations($symbol, $field_descr)."</simpara></listitem>\n";
1456             } else {
1457                 $desc .= "<listitem><simpara /></listitem>\n";
1458             }
1459             $desc .= "</varlistentry>\n";
1460         }
1462         $desc .= "</variablelist>";
1463     }
1464     $desc .= OutputSymbolTraits ($symbol);
1465     $desc .= "</refsect2>\n";
1466     return ($synop, $desc);
1470 #############################################################################
1471 # Function    : OutputEnum
1472 # Description : Returns the synopsis and detailed description of a enum.
1473 # Arguments   : $symbol - the enum.
1474 #               $declaration - the declaration of the enum.
1475 #############################################################################
1477 sub OutputEnum {
1478     my ($symbol, $declaration) = @_;
1479     my $id = &CreateValidSGMLID ($symbol);
1480     my $condition = &MakeConditionDescription ($symbol);
1481     my $synop = &MakeReturnField("enum") . "<link linkend=\"$id\">$symbol</link>;\n";
1482     my $desc = "<refsect2 id=\"$id\" role=\"enum\"$condition>\n<title>enum $symbol</title>\n";
1484     $desc .= MakeIndexterms($symbol, $id);
1485     $desc .= "\n";
1486     $desc .= OutputSymbolExtraLinks($symbol);
1488     my $decl_out = &CreateValidSGML ($declaration);
1489     $desc .= "<programlisting>$decl_out</programlisting>\n";
1491     $desc .= &MakeDeprecationNote($symbol);
1493     if (defined ($SymbolDocs{$symbol})) {
1494         $desc .= &ExpandAbbreviations($symbol, $SymbolDocs{$symbol});
1495     }
1497     # Create a table of fields and descriptions
1499     my @members = ParseEnumDeclaration($declaration);
1500     my $params = $SymbolParams{$symbol};
1502     # If no parameters are filled in, we don't generate the description
1503     # table, for backwards compatibility
1505     my $found = 0;
1506     if (defined $params) {
1507         for (my $i = 1; $i <= $#$params; $i += $PARAM_FIELD_COUNT) {
1508             if ($params->[$i] =~ /\S/) {
1509                 $found = 1;
1510                 last;
1511             }
1512         }
1513     }
1515     if ($found) {
1516         my %member_descrs = @$params;
1518             $desc .= <<EOF;
1519 <variablelist role="enum">
1521         for my $member_name (@members) {
1522             my $member_descr = $member_descrs{$member_name};
1524             $id = &CreateValidSGMLID ($member_name);
1525             $condition = &MakeConditionDescription ($member_name);
1526             $desc .= "<varlistentry id=\"$id\" role=\"constant\"$condition>\n<term><literal>$member_name</literal></term>\n";
1527             if (defined $member_descr) {
1528                 $desc .= "<listitem><simpara>".&ExpandAbbreviations($symbol, $member_descr)."</simpara></listitem>\n";
1529             } else {
1530                 $desc .= "<listitem></listitem>\n";
1531             }
1532             $desc .= "</varlistentry>\n";
1533         }
1535         $desc .= "</variablelist>";
1536     }
1538     $desc .= OutputSymbolTraits ($symbol);
1539     $desc .= "</refsect2>\n";
1540     return ($synop, $desc);
1544 #############################################################################
1545 # Function    : OutputUnion
1546 # Description : Returns the synopsis and detailed description of a union.
1547 # Arguments   : $symbol - the union.
1548 #               $declaration - the declaration of the union.
1549 #############################################################################
1551 sub OutputUnion {
1552     my ($symbol, $declaration) = @_;
1553     my $id = &CreateValidSGMLID ($symbol);
1554     my $condition = &MakeConditionDescription ($symbol);
1555     my $synop = &MakeReturnField("union") . "<link linkend=\"$id\">$symbol</link>;\n";
1556     my $desc = "<refsect2 id=\"$id\" role=\"union\"$condition>\n<title>union $symbol</title>\n";
1558     $desc .= MakeIndexterms($symbol, $id);
1559     $desc .= "\n";
1560     $desc .= OutputSymbolExtraLinks($symbol);
1562     my $decl_out = &CreateValidSGML ($declaration);
1563     $desc .= "<programlisting>$decl_out</programlisting>\n";
1565     $desc .= &MakeDeprecationNote($symbol);
1567     if (defined ($SymbolDocs{$symbol})) {
1568         $desc .= &ExpandAbbreviations($symbol, $SymbolDocs{$symbol});
1569     }
1570     $desc .= OutputSymbolTraits ($symbol);
1571     $desc .= "</refsect2>\n";
1572     return ($synop, $desc);
1576 #############################################################################
1577 # Function    : OutputVariable
1578 # Description : Returns the synopsis and detailed description of a variable.
1579 # Arguments   : $symbol - the extern'ed variable.
1580 #               $declaration - the declaration of the variable.
1581 #############################################################################
1583 sub OutputVariable {
1584     my ($symbol, $declaration) = @_;
1585     my $id = &CreateValidSGMLID ($symbol);
1586     my $condition = &MakeConditionDescription ($symbol);
1588     my $synop;
1589     if ($declaration =~ m/^\s*extern\s+((const\s+|unsigned\s+)*\w+)(\s+\*+|\*+|\s)(\s*)([A-Za-z]\w*)\s*;/) {
1590         my $mod = defined ($1) ? $1 : "";
1591         my $ptr = defined ($3) ? $3 : "";
1592         my $space = defined ($4) ? $4 : "";
1593         $synop = &MakeReturnField("extern") . "$mod$ptr$space<link linkend=\"$id\">$symbol</link>;\n";
1595     } else {
1596         $synop = &MakeReturnField("extern") . "<link linkend=\"$id\">$symbol</link>;\n";
1597     }
1599     my $desc = "<refsect2 id=\"$id\" role=\"variable\"$condition>\n<title>$symbol</title>\n";
1601     $desc .= MakeIndexterms($symbol, $id);
1602     $desc .= "\n";
1603     $desc .= OutputSymbolExtraLinks($symbol);
1605     my $decl_out = &CreateValidSGML ($declaration);
1606     $desc .= "<programlisting>$decl_out</programlisting>\n";
1608     $desc .= &MakeDeprecationNote($symbol);
1610     if (defined ($SymbolDocs{$symbol})) {
1611         $desc .= &ExpandAbbreviations($symbol, $SymbolDocs{$symbol});
1612     }
1613     $desc .= OutputSymbolTraits ($symbol);
1614     $desc .= "</refsect2>\n";
1615     return ($synop, $desc);
1619 #############################################################################
1620 # Function    : OutputFunction
1621 # Description : Returns the synopsis and detailed description of a function.
1622 # Arguments   : $symbol - the function.
1623 #               $declaration - the declaration of the function.
1624 #############################################################################
1626 sub OutputFunction {
1627     my ($symbol, $declaration, $symbol_type) = @_;
1628     my $id = &CreateValidSGMLID ($symbol);
1629     my $condition = &MakeConditionDescription ($symbol);
1631     # Take out the return type     $1                                                           $3   $4
1632     $declaration =~ s/<RETURNS>\s*((const\s+|G_CONST_RETURN\s+|unsigned\s+|struct\s+|enum\s+)*)(\w+)(\s*\**\s*(const|G_CONST_RETURN)?\s*\**\s*(restrict)?\s*)<\/RETURNS>\n//;
1633     my $type_modifier = defined($1) ? $1 : "";
1634     my $type = $3;
1635     my $pointer = $4;
1636     #print "$symbol pointer is $pointer\n";
1637     my $xref = &MakeXRef ($type);
1638     my $start = "";
1639     #if ($symbol_type eq 'USER_FUNCTION') {
1640     #    $start = "typedef ";
1641     #}
1643     # We output const rather than G_CONST_RETURN.
1644     $type_modifier =~ s/G_CONST_RETURN/const/g;
1645     $pointer =~ s/G_CONST_RETURN/const/g;
1646     $pointer =~ s/^\s+/ /g;
1648     my $ret_type_len = length ($start) + length ($type_modifier)
1649         + length ($pointer) + length ($type);
1650     my $ret_type_output;
1651     my $symbol_len;
1652     if ($ret_type_len < $RETURN_TYPE_FIELD_WIDTH) {
1653         $ret_type_output = "$start$type_modifier$xref$pointer"
1654             . (' ' x ($RETURN_TYPE_FIELD_WIDTH - $ret_type_len));
1655         $symbol_len = 0;
1656     } else {
1657 #       $ret_type_output = "$start$type_modifier$xref$pointer\n"
1658 #           . (' ' x $RETURN_TYPE_FIELD_WIDTH);
1660         $ret_type_output = "$start$type_modifier$xref$pointer ";
1661         $symbol_len = $ret_type_len + 1 - $RETURN_TYPE_FIELD_WIDTH;
1662     }
1664     $symbol_len += length ($symbol);
1665     my $char1 = my $char2 = my $char3 = "";
1666     if ($symbol_type eq 'USER_FUNCTION') {
1667         $symbol_len += 3;
1668         $char1 = "(";
1669         $char2 = "*";
1670         $char3 = ")";
1671     }
1673     my ($symbol_output, $symbol_desc_output);
1674     if ($symbol_len < $SYMBOL_FIELD_WIDTH) {
1675         $symbol_output = "$char1<link linkend=\"$id\">$char2$symbol</link>$char3"
1676             . (' ' x ($SYMBOL_FIELD_WIDTH - $symbol_len));
1677         $symbol_desc_output = "$char1$char2$symbol$char3"
1678             . (' ' x ($SYMBOL_FIELD_WIDTH - $symbol_len));
1679     } else {
1680         $symbol_output = "$char1<link linkend=\"$id\">$char2$symbol</link>$char3\n"
1681             . (' ' x ($SYMBOL_FIELD_WIDTH + $RETURN_TYPE_FIELD_WIDTH));
1682         $symbol_desc_output = "$char1$char2$symbol$char3\n"
1683             . (' ' x ($SYMBOL_FIELD_WIDTH + $RETURN_TYPE_FIELD_WIDTH));
1684     }
1686     my $synop = $ret_type_output . $symbol_output . '(';
1687     my $desc = "<refsect2 id=\"$id\" role=\"function\"$condition>\n<title>${symbol} ()</title>\n";
1689     $desc .= MakeIndexterms($symbol, $id);
1690     $desc .= "\n";
1691     $desc .= OutputSymbolExtraLinks($symbol);
1693     $desc  .= "<programlisting>${ret_type_output}$symbol_desc_output(";
1695     my $param_num = 0;
1696     while ($declaration ne "") {
1697         #print "$declaration";
1699         if ($declaration =~ s/^[\s,]+//) {
1700             # skip whitespace and commas
1701             next;
1703         } elsif ($declaration =~ s/^void\s*[,\n]//) {
1704             $synop .= "void";
1705             $desc  .= "void";
1707         } elsif ($declaration =~ s/^...\s*[,\n]//) {
1708             if ($param_num == 0) {
1709                 $synop .= "...";
1710                 $desc  .= "...";
1711             } else {
1712                 $synop .= ",\n"
1713                     . (' ' x ($SYMBOL_FIELD_WIDTH + $RETURN_TYPE_FIELD_WIDTH))
1714                     . " ...";
1715                 $desc  .= ",\n"
1716                     . (' ' x ($SYMBOL_FIELD_WIDTH + $RETURN_TYPE_FIELD_WIDTH))
1717                     . " ...";
1718             }
1720         # allow alphanumerics, '_', '[' & ']' in param names
1721         # Try to match a standard parameter (keep in sync with gtkdoc-mktmpl)
1722         #                                $1                                                                                                                                    $2                             $3                                                           $4       $5
1723         } elsif ($declaration =~ s/^\s*((?:G_CONST_RETURN|G_GNUC_UNUSED|unsigned long|unsigned short|signed long|signed short|unsigned|signed|long|short|volatile|const)\s+)*((?:struct\b|enum\b)?\s*\w+)\s*((?:(?:const\b|restrict\b)?\s*\*?\s*(?:const\b|restrict\b)?\s*)*)(\w+)?\s*((?:\[\S*\])*)\s*[,\n]//) {
1724             my $pre     = defined($1) ? $1 : "";
1725             my $type    = $2;
1726             my $ptr     = defined($3) ? $3 : "";
1727             my $name    = defined($4) ? $4 : "";
1728             my $array   = defined($5) ? $5 : "";
1730             $pre  =~ s/\s+/ /g;
1731             $type =~ s/\s+/ /g;
1732             $ptr  =~ s/\s+/ /g;
1733             $ptr  =~ s/\s+$//;
1734             if ($ptr && $ptr !~ m/\*$/) { $ptr .= " "; }
1736             #print "$symbol: '$pre' '$type' '$ptr' '$name' '$array'\n";
1738             if (($name eq "") && $pre =~ m/^((un)?signed .*)\s?/ ) {
1739                 $name = $type;
1740                 $type = "$1";
1741                 $pre = "";
1742             }
1744             my $xref = &MakeXRef ($type);
1745             my $label   = "$pre$xref $ptr$name$array";
1747             #print "$symbol: '$pre' '$type' '$ptr' '$name' '$array'\n";
1749             if ($param_num == 0) {
1750                 $synop .= "$label";
1751                 $desc  .= "$label";
1752             } else {
1753                 $synop .= ",\n"
1754                     . (' ' x ($SYMBOL_FIELD_WIDTH + $RETURN_TYPE_FIELD_WIDTH))
1755                     . " $label";
1756                 $desc  .= ",\n"
1757                     . (' ' x ($SYMBOL_FIELD_WIDTH + $RETURN_TYPE_FIELD_WIDTH))
1758                     . " $label";
1759             }
1761         # Try to match parameters which are functions (keep in sync with gtkdoc-mktmpl)
1762         #                              $1                                       $2          $3      $4                        $5                    $7             $8
1763         } elsif ($declaration =~ s/^(const\s+|G_CONST_RETURN\s+|unsigned\s+)*(struct\s+)?(\w+)\s*(\**)\s*(?:restrict\b)?\s*(const\s+)?\(\s*\*+\s*(\w+)\s*\)\s*\(([^)]*)\)\s*[,\n]//) {
1764             my $mod1 = defined($1) ? $1 : "";
1765             if (defined($2)) { $mod1 .= $2; }
1766             my $type = $3;
1767             my $ptr1 = $4;
1768             my $mod2 = defined($5) ? $5 : "";
1769             my $func_ptr = $6;
1770             my $name = $7;
1771             my $func_params = defined($8) ? $8 : "";
1772             
1773             #if (!defined($type)) { print "## no type\n"; };
1774             #if (!defined($ptr1)) { print "## no ptr1\n"; };
1775             #if (!defined($func_ptr)) { print "## no func_ptr\n"; };
1776             #if (!defined($name)) { print "## no name\n"; };
1778             if ($ptr1 && $ptr1 !~ m/\*$/) { $ptr1 .= " "; }
1779             $func_ptr  =~ s/\s+//g;
1780             my $xref = &MakeXRef ($type);
1781             my $label = "$mod1$xref$ptr1$mod2 ($func_ptr$name) ($func_params)";
1783             #print "Type: [$mod1][$xref][$ptr1][$mod2] ([$func_ptr][$name]) ($func_params)\n";
1784             if ($param_num == 0) {
1785                 $synop .= "$label";
1786                 $desc  .= "$label";
1787             } else {
1788                 $synop .= ",\n"
1789                     . (' ' x ($SYMBOL_FIELD_WIDTH + $RETURN_TYPE_FIELD_WIDTH))
1790                     . " $label";
1791                 $desc  .= ",\n"
1792                     . (' ' x ($SYMBOL_FIELD_WIDTH + $RETURN_TYPE_FIELD_WIDTH))
1793                     . " $label";
1794             }
1796         } else {
1797             &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1798                 "Can't parse args for function $symbol: $declaration");
1799             last;
1800         }
1801         $param_num++;
1802     }
1803     $synop .= ");\n";
1804     $desc  .= ");</programlisting>\n";
1806     $desc .= &MakeDeprecationNote($symbol);
1808     my $parameters = &OutputParamDescriptions ("FUNCTION", $symbol);
1809     my $parameters_output = 0;
1811     if (defined ($SymbolDocs{$symbol})) {
1812         my $symbol_docs = &ExpandAbbreviations($symbol, $SymbolDocs{$symbol});
1814         # Try to insert the parameter table at the author's desired position.
1815         # Otherwise we need to tag it onto the end.
1816         if ($symbol_docs =~ s/<!--PARAMETERS-->/$parameters/) {
1817           $parameters_output = 1;
1818         }
1819         $desc .= $symbol_docs;
1820     }
1822     if ($parameters_output == 0) {
1823         $desc .= $parameters;
1824     }
1826     $desc .= OutputSymbolTraits ($symbol);
1827     $desc .= "</refsect2>\n";
1828     return ($synop, $desc);
1832 #############################################################################
1833 # Function    : OutputParamDescriptions
1834 # Description : Returns the DocBook output describing the parameters of a
1835 #               function, macro or signal handler.
1836 # Arguments   : $symbol_type - 'FUNCTION', 'MACRO' or 'SIGNAL'. Signal
1837 #                 handlers have an implicit user_data parameter last.
1838 #               $symbol - the name of the function/macro being described.
1839 #############################################################################
1841 sub OutputParamDescriptions {
1842     my ($symbol_type, $symbol) = @_;
1843     my $output = "";
1844     if (defined ($SymbolParams{$symbol})) {
1845         my $returns = "";
1846         my $params = $SymbolParams{$symbol};
1847         my $params_desc = "";
1848         my $j;
1849         for ($j = 0; $j <= $#$params; $j += $PARAM_FIELD_COUNT) {
1850             my $param_name = $$params[$j];
1851             my $param_desc = $$params[$j + 1];
1852             my $param_annotations = "";
1854             if ($param_desc =~ m%\s*\((.*)\):%) {
1855                 my @annotations;
1856                 my $annotation;
1857                 my $annotation_extra = "";
1858                 $param_desc = $';
1859                 @annotations = split(/\)\s*\(/,$1);
1860                 foreach $annotation (@annotations) {
1861                     # need to search for the longest key-match in %AnnotationDefinition
1862                     my $match_length=0;
1863                     my $match_annotation="";
1864                     my $annotationdef;
1865                     foreach $annotationdef (keys %AnnotationDefinition) {
1866                         if ($annotation =~ m/^$annotationdef/) {
1867                             if (length($annotationdef)>$match_length) {
1868                                 $match_length=length($annotationdef);
1869                                 $match_annotation=$annotationdef;
1870                             }
1871                         }
1872                     }
1873                     if ($match_annotation ne "") {
1874                         if ($annotation =~ m%$match_annotation\s+(.*)%) {
1875                             $annotation_extra = " $1";
1876                         }
1877                         $AnnotationsUsed{$match_annotation} = 1;
1878                         $param_annotations .= "<acronym>$match_annotation</acronym>$annotation_extra. ";
1879                     }
1880                     else {
1881                         &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1882                             "unknown annotation \"$annotation\" in documentation for $symbol.");
1883                         $param_annotations=$annotation;
1884                     }
1885                 }
1886                 chomp($param_desc);
1887                 $param_desc =~ m/^(.*)\.*\s*$/;
1888                 $param_desc = "$1. ";
1889             }
1890             $param_desc = &ExpandAbbreviations($symbol, $param_desc);
1891             if ($param_name eq "Returns") {
1892                 $returns = "$param_desc$param_annotations";
1893             } else {
1894                 if ($param_name eq "Varargs") {
1895                     $param_name = "...";
1896                 }
1897                 $params_desc .= "<varlistentry><term><parameter>$param_name</parameter>&#160;:</term>\n<listitem><simpara>$param_desc$param_annotations</simpara></listitem></varlistentry>\n";
1898             }
1899         }
1901         # Signals have an implicit user_data parameter which we describe.
1902         if ($symbol_type eq "SIGNAL") {
1903             $params_desc .= "<varlistentry><term><parameter>user_data</parameter>&#160;:</term>\n<listitem><simpara>user data set when the signal handler was connected.</simpara></listitem></varlistentry>\n";
1904         }
1906         # Start a table if we need one.
1907         if ($params_desc || $returns) {
1908             $output .= <<EOF;
1909 <variablelist role="params">
1912             if ($params_desc ne "") {
1913 #               $output .= "<varlistentry><term>Parameters:</term><listitem></listitem></varlistentry>\n";
1914                 $output .= $params_desc;
1915             }
1917             # Output the returns info last.
1918             if ($returns) {
1919                 $output .= "<varlistentry><term><emphasis>Returns</emphasis>&#160;:</term><listitem><simpara>$returns</simpara></listitem></varlistentry>\n";
1920             }
1922             # Finish the table.
1923             $output .= "</variablelist>";
1924         }
1925     }
1926     return $output;
1930 #############################################################################
1931 # Function    : ParseStabilityLevel
1932 # Description : Parses a stability level and outputs a warning if it isn't
1933 #               valid.
1934 # Arguments   : $stability - the stability text.
1935 #               $file, $line - context for error message
1936 #               $message - description of where the level is from, to use in
1937 #               any error message.
1938 # Returns     : The parsed stability level string.
1939 #############################################################################
1941 sub ParseStabilityLevel {
1942     my ($stability, $file, $line, $message) = @_;
1944     $stability =~ s/^\s*//;
1945     $stability =~ s/\s*$//;
1946     if ($stability =~ m/^stable$/i) {
1947         $stability = "Stable";
1948     } elsif ($stability =~ m/^unstable$/i) {
1949         $stability = "Unstable";
1950     } elsif ($stability =~ m/^private$/i) {
1951         $stability = "Private";
1952     } else {
1953         &LogWarning ($file, $line, "$message is $stability.".
1954             "It should be one of these: Stable, Unstable, or Private.");
1955     }
1956     return $stability;
1960 #############################################################################
1961 # Function    : OutputSGMLFile
1962 # Description : Outputs the final DocBook file for one section.
1963 # Arguments   : $file - the name of the file.
1964 #               $title - the title from the $MODULE-sections.txt file, which
1965 #                 will be overridden by the title in the template file.
1966 #               $section_id - the SGML id to use for the toplevel tag.
1967 #               $includes - comma-separates list of include files added at top
1968 #                 of synopsis, with '<' '>' around them (if not already enclosed in "").
1969 #               $synopsis - reference to the DocBook for the Synopsis part.
1970 #               $details - reference to the DocBook for the Details part.
1971 #               $signal_synop - reference to the DocBook for the Signal Synopsis part
1972 #               $signal_desc - reference to the DocBook for the Signal Description part
1973 #               $args_synop - reference to the DocBook for the Arg Synopsis part
1974 #               $args_desc - reference to the DocBook for the Arg Description part
1975 #               $hierarchy - reference to the DocBook for the Object Hierarchy part
1976 #               $interfaces - reference to the DocBook for the Interfaces part
1977 #               $implementations - reference to the DocBook for the Known Implementations part
1978 #               $prerequisites - reference to the DocBook for the Prerequisites part
1979 #               $derived - reference to the DocBook for the Derived Interfaces part
1980 #               $file_objects - reference to an array of objects in this file
1981 #############################################################################
1983 sub OutputSGMLFile {
1984     my ($file, $title, $section_id, $includes, $synopsis, $details, $signals_synop, $signals_desc, $args_synop, $args_desc, $hierarchy, $interfaces, $implementations, $prerequisites, $derived, $file_objects) = @_;
1986     #print "Output sgml for file $file with title '$title'\n";
1987     
1988     # The edited title overrides the one from the sections file.
1989     my $new_title = $SymbolDocs{"$TMPL_DIR/$file:Title"};
1990     if (defined ($new_title) && $new_title !~ m/^\s*$/) {
1991         $title = $new_title;
1992         #print "Found title: $title\n";
1993     }
1994     my $short_desc = $SymbolDocs{"$TMPL_DIR/$file:Short_Description"};
1995     if (!defined ($short_desc) || $short_desc =~ m/^\s*$/) {
1996         $short_desc = "";
1997     } else {
1998         $short_desc = &ExpandAbbreviations("$title:Short_description",
1999                                            $short_desc);
2000         #print "Found short_desc: $short_desc";
2001     }
2002     my $long_desc = $SymbolDocs{"$TMPL_DIR/$file:Long_Description"};
2003     if (!defined ($long_desc) || $long_desc =~ m/^\s*$/) {
2004         $long_desc = "";
2005     } else {
2006         $long_desc = &ExpandAbbreviations("$title:Long_description",
2007                                           $long_desc);
2008         #print "Found long_desc: $long_desc";
2009     }
2010     my $see_also = $SymbolDocs{"$TMPL_DIR/$file:See_Also"};
2011     if (!defined ($see_also) || $see_also =~ m%^\s*(<para>)?\s*(</para>)?\s*$%) {
2012         $see_also = "";
2013     } else {
2014         $see_also = &ExpandAbbreviations("$title:See_Also", $see_also);
2015         #print "Found see_also: $see_also";
2016     }
2017     if ($see_also) {
2018         $see_also = "<refsect1 id=\"$section_id.see-also\">\n<title>See Also</title>\n$see_also\n</refsect1>\n";
2019     }
2020     my $stability = $SymbolDocs{"$TMPL_DIR/$file:Stability_Level"};
2021     if (!defined ($stability) || $stability =~ m/^\s*$/) {
2022         $stability = "";
2023     } else {
2024         $stability = &ParseStabilityLevel($stability, $file, $., "Section stability level");
2025         #print "Found stability: $stability";
2026     }
2027     if ($stability) {
2028         $stability = "<refsect1 id=\"$section_id.stability-level\">\n<title>Stability Level</title>\n$stability, unless otherwise indicated\n</refsect1>\n";
2029     } elsif ($DEFAULT_STABILITY) {
2030         $stability = "<refsect1 id=\"$section_id.stability-level\">\n<title>Stability Level</title>\n$DEFAULT_STABILITY, unless otherwise indicated\n</refsect1>\n";
2031     }
2033     my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) =
2034         gmtime (time);
2035     my $month = (qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec))[$mon];
2036     $year += 1900;
2038     my $include_output = "";
2039     my $include;
2040     foreach $include (split (/,/, $includes)) {
2041         if ($include =~ m/^\".+\"$/) {
2042             $include_output .= "#include ${include}\n";
2043         }
2044         else {
2045             $include =~ s/^\s+|\s+$//gs;
2046             $include_output .= "#include &lt;${include}&gt;\n";
2047         }
2048     }
2049     if ($include_output ne '') {
2050         $include_output = "\n$include_output\n";
2051     }
2052     
2053     my $extralinks = OutputSectionExtraLinks($title,"Section:$file");
2055     my $old_sgml_file = "$SGML_OUTPUT_DIR/$file.$OUTPUT_FORMAT";
2056     my $new_sgml_file = "$SGML_OUTPUT_DIR/$file.$OUTPUT_FORMAT.new";
2058     open (OUTPUT, ">$new_sgml_file")
2059         || die "Can't create $new_sgml_file: $!";
2061     my $object_anchors = "";
2062     foreach my $object (@$file_objects) {
2063         next if ($object eq $section_id);
2064         my $id = CreateValidSGMLID($object);
2065         #print "Debug: Adding anchor for $object\n";
2066         $object_anchors .= "<anchor id=\"$id\"$empty_element_end";
2067     }
2069     # We used to output this, but is messes up our UpdateFileIfChanged code
2070     # since it changes every day (and it is only used in the man pages):
2071     # "<refentry id="$section_id" revision="$mday $month $year">"
2073     if (lc($OUTPUT_FORMAT) eq "xml") {
2074         print OUTPUT $doctype_header;
2075     }
2077     print OUTPUT <<EOF;
2078 <refentry id="$section_id">
2079 <refmeta>
2080 <refentrytitle role="top_of_page" id="$section_id.top_of_page">$title</refentrytitle>
2081 <manvolnum>3</manvolnum>
2082 <refmiscinfo>\U$MODULE\E Library</refmiscinfo>
2083 </refmeta>
2085 <refnamediv>
2086 <refname>$title</refname>
2087 <refpurpose>$short_desc</refpurpose>
2088 </refnamediv>
2089 $stability
2090 <refsynopsisdiv id="$section_id.synopsis" role="synopsis">
2091 <title role="synopsis.title">Synopsis</title>
2092 $object_anchors
2093 <synopsis>
2094 $include_output$${synopsis}</synopsis>
2095 </refsynopsisdiv>
2097 $$hierarchy
2098 $$prerequisites
2099 $$derived
2100 $$interfaces
2101 $$implementations
2102 $$args_synop
2103 $$signals_synop
2105 <refsect1 id="$section_id.description" role="desc">
2106 <title role="desc.title">Description</title>
2107 $extralinks
2108 $long_desc
2109 </refsect1>
2111 <refsect1 id="$section_id.details" role="details">
2112 <title role="details.title">Details</title>
2113 $$details
2114 </refsect1>
2115 $$args_desc
2116 $$signals_desc
2118 $see_also
2119 </refentry>
2121     close (OUTPUT);
2123     return &UpdateFileIfChanged ($old_sgml_file, $new_sgml_file, 0);
2127 #############################################################################
2128 # Function    : OutputExtraFile
2129 # Description : Copies an "extra" DocBook file into the output directory,
2130 #               expanding abbreviations
2131 # Arguments   : $file - the source file.
2132 #############################################################################
2133 sub OutputExtraFile {
2134     my ($file) = @_;
2136     my $basename;
2138     ($basename = $file) =~ s!^.*/!!;
2140     my $old_sgml_file = "$SGML_OUTPUT_DIR/$basename";
2141     my $new_sgml_file = "$SGML_OUTPUT_DIR/$basename.new";
2143     my $contents;
2145     open(EXTRA_FILE, "<$file") || die "Can't open $file";
2147     {
2148         local $/;
2149         $contents = <EXTRA_FILE>;
2150     }
2152     open (OUTPUT, ">$new_sgml_file")
2153         || die "Can't create $new_sgml_file: $!";
2155     print OUTPUT &ExpandAbbreviations ("$basename file", $contents);
2156     close (OUTPUT);
2158     return &UpdateFileIfChanged ($old_sgml_file, $new_sgml_file, 0);
2160 #############################################################################
2161 # Function    : OutputBook
2162 # Description : Outputs the SGML entities that need to be included into the
2163 #               main SGML file for the module.
2164 # Arguments   : $book_top - the declarations of the entities, which are added
2165 #                 at the top of the main SGML file.
2166 #               $book_bottom - the references to the entities, which are
2167 #                 added in the main SGML file at the desired position.
2168 #############################################################################
2170 sub OutputBook {
2171     my ($book_top, $book_bottom) = @_;
2173     my $old_file = "$SGML_OUTPUT_DIR/$MODULE-doc.top";
2174     my $new_file = "$SGML_OUTPUT_DIR/$MODULE-doc.top.new";
2176     open (OUTPUT, ">$new_file")
2177         || die "Can't create $new_file: $!";
2178     print OUTPUT $book_top;
2179     close (OUTPUT);
2181     &UpdateFileIfChanged ($old_file, $new_file, 0);
2184     $old_file = "$SGML_OUTPUT_DIR/$MODULE-doc.bottom";
2185     $new_file = "$SGML_OUTPUT_DIR/$MODULE-doc.bottom.new";
2187     open (OUTPUT, ">$new_file")
2188         || die "Can't create $new_file: $!";
2189     print OUTPUT $book_bottom;
2190     close (OUTPUT);
2192     &UpdateFileIfChanged ($old_file, $new_file, 0);
2195     # If the main SGML file hasn't been created yet, we create it here.
2196     # The user can tweak it later.
2197     if ($MAIN_SGML_FILE && ! -e $MAIN_SGML_FILE) {
2198       open (OUTPUT, ">$MAIN_SGML_FILE")
2199         || die "Can't create $MAIN_SGML_FILE: $!";
2201       if (lc($OUTPUT_FORMAT) eq "xml") {
2202           print OUTPUT <<EOF;
2203 <?xml version="1.0"?>
2204 <!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
2205                "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"
2207   <!ENTITY % local.common.attrib "xmlns:xi  CDATA  #FIXED 'http://www.w3.org/2003/XInclude'">
2209 <book id="index">
2211       } else {
2212         print OUTPUT <<EOF;
2213 <!doctype book PUBLIC "-//Davenport//DTD DocBook V3.0//EN" [
2214 $book_top
2216 <book id="index">
2218       }
2220 print OUTPUT <<EOF;
2221   <bookinfo>
2222     <title>$MODULE Reference Manual</title>
2223     <releaseinfo>
2224       for $MODULE [VERSION].
2225       The latest version of this documentation can be found on-line at
2226       <ulink role="online-location" url="http://[SERVER]/$MODULE/index.html">http://[SERVER]/$MODULE/</ulink>.
2227     </releaseinfo>
2228   </bookinfo>
2230   <chapter>
2231     <title>[Insert title here]</title>
2232     $book_bottom
2233   </chapter>
2235   if (-e $OBJECT_TREE_FILE) {
2236     print OUTPUT <<EOF;
2237   <chapter id="object-tree">
2238     <title>Object Hierarchy</title>
2239      <xi:include href="xml/tree_index.sgml"/>
2240   </chapter>
2242   }
2244 print OUTPUT <<EOF;
2245   <index id="api-index-full">
2246     <title>API Index</title>
2247     <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
2248   </index>
2250   <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
2251 </book>
2254       close (OUTPUT);
2255     }
2259 #############################################################################
2260 # Function    : CreateValidSGMLID
2261 # Description : Creates a valid SGML 'id' from the given string.
2262 #               According to http://www.w3.org/TR/html4/types.html#type-id
2263 #                 "ID and NAME tokens must begin with a letter ([A-Za-z]) and
2264 #                  may be followed by any number of letters, digits ([0-9]),
2265 #                  hyphens ("-"), underscores ("_"), colons (":"), and
2266 #                  periods (".")."
2268 #               NOTE: SGML ids are case-insensitive, so we have a few special
2269 #                     cases to avoid clashes of ids.
2270 # Arguments   : $id - the string to be converted into a valid SGML id.
2271 #############################################################################
2273 sub CreateValidSGMLID {
2274     my ($id) = $_[0];
2276     # Special case, '_' would end up as '' so we use 'gettext-macro' instead.
2277     if ($id eq "_") { return "gettext-macro"; }
2279     $id =~ s/[_ ]/-/g;
2280     $id =~ s/[,;]//g;
2281     $id =~ s/^-*//;
2282     $id =~ s/::/-/g;
2283     $id =~ s/:/--/g;
2285     # Append ":CAPS" to all all-caps identifiers
2286     if ($id !~ /[a-z]/ && $id !~ /-CAPS$/) { $id .= ":CAPS" };
2288     return $id;
2292 #############################################################################
2293 # Function    : CreateValidSGML
2294 # Description : This turns any chars which are used in SGML into entities,
2295 #               e.g. '<' into '&lt;'
2296 # Arguments   : $text - the text to turn into proper SGML.
2297 #############################################################################
2299 sub CreateValidSGML {
2300     my ($text) = @_;
2301     $text =~ s/&/&amp;/g;       # Do this first, or the others get messed up.
2302     $text =~ s/</&lt;/g;
2303     $text =~ s/>/&gt;/g;
2304     # browers render single tabs inconsistently
2305     $text =~ s/([^\s])\t([^\s])/$1&#160;$2/g;
2306     return $text;
2309 #############################################################################
2310 # Function    : ConvertSGMLChars
2311 # Description : This is used for text in source code comment blocks, to turn
2312 #               chars which are used in SGML into entities, e.g. '<' into
2313 #               '&lt;'. Depending on $SGML_MODE, this is done
2314 #               unconditionally or only if the character doesn't seem to be
2315 #               part of an SGML construct (tag or entity reference).
2316 # Arguments   : $text - the text to turn into proper SGML.
2317 #############################################################################
2319 sub ConvertSGMLChars {
2320     my ($symbol, $text) = @_;
2322     if ($SGML_MODE) {
2323         # For the SGML mode only convert to entities outside CDATA sections.
2324         return &ModifyXMLElements ($text, $symbol,
2325                                    "<!\\[CDATA\\[|<programlisting[^>]*>",
2326                                    \&ConvertSGMLCharsEndTag,
2327                                    \&ConvertSGMLCharsCallback);
2328     } else {
2329         # For the simple non-sgml mode, convert to entities everywhere.
2330         $text =~ s/&/&amp;/g;   # Do this first, or the others get messed up.
2331         $text =~ s/</&lt;/g;
2332         $text =~ s/>/&gt;/g;
2333         return $text;
2334     }
2338 sub ConvertSGMLCharsEndTag {
2339   if ($_[0] eq "<!\[CDATA\[") {
2340     return "]]>";
2341   } else {
2342     return "</programlisting>";
2343   }
2346 sub ConvertSGMLCharsCallback {
2347   my ($text, $symbol, $tag) = @_;
2349   if ($tag =~ m/^<programlisting/) {
2350     # We can handle <programlisting> specially here.
2351     return &ModifyXMLElements ($text, $symbol,
2352                                "<!\\[CDATA\\[",
2353                                \&ConvertSGMLCharsEndTag,
2354                                \&ConvertSGMLCharsCallback2);
2355   } elsif ($tag eq "") {
2356     # If we're not in CDATA convert to entities.
2357     $text =~ s/&(?![a-zA-Z#]+;)/&amp;/g;        # Do this first, or the others get messed up.
2358     $text =~ s/<(?![a-zA-Z\/!])/&lt;/g;
2359     $text =~ s/(?<![a-zA-Z0-9"'\/-])>/&gt;/g;
2361     # Handle "#include <xxxxx>"
2362     $text =~ s/#include(\s+)<([^>]+)>/#include$1&lt;$2&gt;/g;
2363   }
2365   return $text;
2368 sub ConvertSGMLCharsCallback2 {
2369   my ($text, $symbol, $tag) = @_;
2371   # If we're not in CDATA convert to entities.
2372   # We could handle <programlisting> differently, though I'm not sure it helps.
2373   if ($tag eq "") {
2374     # replace only if its not a tag
2375     $text =~ s/&(?![a-zA-Z#]+;)/&amp;/g;        # Do this first, or the others get messed up.
2376     $text =~ s/<(?![a-zA-Z\/!])/&lt;/g;
2377     $text =~ s/(?<![a-zA-Z0-9"'\/-])>/&gt;/g;
2379     # Handle "#include <xxxxx>"
2380     $text =~ s/#include(\s+)<([^>]+)>/#include$1&lt;$2&gt;/g;
2381   }
2383   return $text;
2387 #############################################################################
2388 # Function    : ExpandAbbreviations
2389 # Description : This turns the abbreviations function(), macro(), @param,
2390 #               %constant, and #symbol into appropriate DocBook markup.
2391 #               CDATA sections and <programlisting> parts are skipped.
2392 # Arguments   : $symbol - the symbol being documented, for error messages.
2393 #               $text - the text to expand.
2394 #############################################################################
2396 sub ExpandAbbreviations {
2397   my ($symbol, $text) = @_;
2399   # Convert "|[" and "]|" into the start and end of program listing examples.
2400   $text =~ s%\|\[%<informalexample><programlisting>%g;
2401   $text =~ s%\]\|%</programlisting></informalexample>%g;
2402   # TODO: check for a xml comment after |[ and pick the language attribute from
2403   # that
2405   # keep CDATA unmodified, preserve ulink tags (ideally we preseve all tags
2406   # as such)
2407   return &ModifyXMLElements ($text, $symbol,
2408                              "<!\\[CDATA\\[|<ulink[^>]*>|<programlisting[^>]*>|<!DOCTYPE",
2409                              \&ExpandAbbreviationsEndTag,
2410                              \&ExpandAbbreviationsCallback);
2414 # Returns the end tag corresponding to the given start tag.
2415 sub ExpandAbbreviationsEndTag {
2416   my ($start_tag) = @_;
2418   if ($start_tag eq "<!\[CDATA\[") {
2419     return "]]>";
2420   } elsif ($start_tag eq "<!DOCTYPE") {
2421     return "]>";
2422   } elsif ($start_tag =~ m/<(\w+)/) {
2423     return "</$1>";
2424   }
2427 # Called inside or outside each CDATA or <programlisting> section.
2428 sub ExpandAbbreviationsCallback {
2429   my ($text, $symbol, $tag) = @_;
2431   if ($tag =~ m/^<programlisting/) {
2432     # Handle any embedded CDATA sections.
2433     return &ModifyXMLElements ($text, $symbol,
2434                                "<!\\[CDATA\\[",
2435                                \&ExpandAbbreviationsEndTag,
2436                                \&ExpandAbbreviationsCallback2);
2437   } elsif ($tag eq "") {
2438     # We are outside any CDATA or <programlisting> sections, so we expand
2439     # any gtk-doc abbreviations.
2441     # Convert 'function()' or 'macro()'.
2442     $text =~ s/(\w+)\s*\(\)/&MakeXRef($1, &tagify($1 . "()", "function"));/eg;
2444     # Convert '@param', but not '\@param'.
2445     $text =~ s/(\A|[^\\])\@(\w+((\.|->)\w+)*)/$1<parameter>$2<\/parameter>/g;
2446     $text =~ s/\\\@/\@/g;
2448     # Convert '%constant', but not '\%constant'.
2449     # Also allow negative numbers, e.g. %-1.
2450     $text =~ s/(\A|[^\\])\%(-?\w+)/$1.&MakeXRef($2, &tagify($2, "literal"));/eg;
2451     $text =~ s/\\\%/\%/g;
2453     # Convert '#symbol', but not '\#symbol'.
2454     $text =~ s/(\A|[^\\])#([\w\-:\.]+)/$1.&MakeHashXRef($2, "type");/eg;
2455     $text =~ s/\\#/#/g;
2456     
2457     # Expand urls
2458     # FIXME: should we skip urls that are already tagged? (e.g. <literal>http://...</literal>)
2459     # this is apparently also called for markup and not just for plain text
2460     # disable for now.
2461     #$text =~ s%(http|https|ftp)://(.*?)((?:\s|,|\)|\]|\<|\.\s))%<ulink url="$1://$2">$2</ulink>$3%g;
2462   }
2464   return $text;
2467 # This is called inside a <programlisting>
2468 sub ExpandAbbreviationsCallback2 {
2469   my ($text, $symbol, $tag) = @_;
2471   if ($tag eq "") {
2472     # We are inside a <programlisting> but outside any CDATA sections,
2473     # so we expand any gtk-doc abbreviations.
2474     # FIXME: why is this different from &ExpandAbbreviationsCallback(),
2475     #        why not just call it
2476     $text =~ s/#(\w+)/&MakeHashXRef($1, "");/eg;
2477   }
2479   return $text;
2482 sub MakeHashXRef {
2483     my ($symbol, $tag) = @_;;
2484     my $text = $symbol;
2486     # Check for things like '#include', '#define', and skip them.
2487     if ($PreProcessorDirectives{$symbol}) {
2488       return "#$symbol";
2489     }
2491     # Get rid of any special '-struct' suffix.
2492     $text =~ s/-struct$//;
2494     # If the symbol is in the form "Object::signal", then change the symbol to
2495     # "Object-signal" and use "signal" as the text.
2496     if ($symbol =~ s/::/-/) {
2497       $text = "\"$'\"";
2498     }
2500     # If the symbol is in the form "Object:property", then change the symbol to
2501     # "Object--property" and use "property" as the text.
2502     if ($symbol =~ s/:/--/) {
2503       $text = "\"$'\"";
2504     }
2506     if ($tag ne "") {
2507       $text = tagify ($text, $tag);
2508     }
2509     
2510     return &MakeXRef($symbol, $text);
2514 #############################################################################
2515 # Function    : ModifyXMLElements
2516 # Description : Looks for given XML element tags within the text, and calls
2517 #               the callback on pieces of text inside & outside those elements.
2518 #               Used for special handling of text inside things like CDATA
2519 #               and <programlisting>.
2520 # Arguments   : $text - the text.
2521 #               $symbol - the symbol currently being documented (only used for
2522 #                      error messages).
2523 #               $start_tag_regexp - the regular expression to match start tags.
2524 #                      e.g. "<!\\[CDATA\\[|<programlisting[^>]*>" to match
2525 #                      CDATA sections or programlisting elements.
2526 #               $end_tag_func - function which is passed the matched start tag
2527 #                      and should return the appropriate end tag string.
2528 #               $callback - callback called with each part of the text. It is
2529 #                      called with a piece of text, the symbol being
2530 #                      documented, and the matched start tag or "" if the text
2531 #                      is outside the XML elements being matched.
2532 #############################################################################
2533 sub ModifyXMLElements {
2534     my ($text, $symbol, $start_tag_regexp, $end_tag_func, $callback) = @_;
2535     my ($before_tag, $start_tag, $end_tag_regexp, $end_tag);
2536     my $result = "";
2538     while ($text =~ m/$start_tag_regexp/s) {
2539       $before_tag = $`; # Prematch for last successful match string
2540       $start_tag = $&;  # Last successful match
2541       $text = $';       # Postmatch for last successful match string
2543       $result .= &$callback ($before_tag, $symbol, "");
2544       $result .= $start_tag;
2546       # get the mathing end-tag for current tag
2547       $end_tag_regexp = &$end_tag_func ($start_tag);
2549       if ($text =~ m/$end_tag_regexp/s) {
2550         $before_tag = $`;
2551         $end_tag = $&;
2552         $text = $';
2554         $result .= &$callback ($before_tag, $symbol, $start_tag);
2555         $result .= $end_tag;
2556       } else {
2557         &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2558             "Can't find tag end: $end_tag_regexp in docs for: $symbol.");
2559         # Just assume it is all inside the tag.
2560         $result .= &$callback ($text, $symbol, $start_tag);
2561         $text = "";
2562       }
2563     }
2565     # Handle any remaining text outside the tags.
2566     $result .= &$callback ($text, $symbol, "");
2568     return $result;
2571 sub noop {
2572   return $_[0];
2575 # Adds a tag around some text.
2576 # e.g tagify("Text", "literal") => "<literal>Text</literal>".
2577 sub tagify {
2578    my ($text, $elem) = @_;
2579    return "<" . $elem . ">" . $text . "</" . $elem . ">";
2583 #############################################################################
2584 # Function    : MakeXRef
2585 # Description : This returns a cross-reference link to the given symbol.
2586 #               Though it doesn't try to do this for a few standard C types
2587 #               that it knows won't be in the documentation.
2588 # Arguments   : $symbol - the symbol to try to create a XRef to.
2589 #               $text - text text to put inside the XRef, defaults to $symbol
2590 #############################################################################
2592 sub MakeXRef {
2593     my ($symbol, $text) = ($_[0], $_[1]);
2594     if (!defined($text)) {
2595         $text = $symbol;
2597         # Get rid of special '-struct' suffix.
2598         $text =~ s/-struct$//;
2599     }
2601     #print "Getting type link for $symbol -> $text\n";
2603     my $symbol_id = &CreateValidSGMLID ($symbol);
2604     return "<link linkend=\"$symbol_id\">$text</link>";
2608 #############################################################################
2609 # Function    : MakeIndexterms
2610 # Description : This returns a indexterm elements for the given symbol
2611 # Arguments   : $symbol - the symbol to create indexterms for
2612 #############################################################################
2614 sub MakeIndexterms {
2615   my ($symbol, $id) = @_;
2616   my $terms =  "";
2617   my $sortas = "";
2618   
2619   # make the index useful, by ommiting the namespace when sorting
2620   if ($NAME_SPACE ne "") {
2621     if ($symbol =~ m/^$NAME_SPACE\_?(.*)/i) {
2622        $sortas=" sortas=\"$1\"";
2623     }
2624   }
2626   if (exists $Deprecated{$symbol}) {
2627       $terms .= "<indexterm zone=\"$id\" role=\"deprecated\"><primary$sortas>$symbol</primary></indexterm>";
2628       $IndexEntriesDeprecated{$symbol}=$id;
2629       $IndexEntriesFull{$symbol}=$id;
2630   }
2631   if (exists $Since{$symbol}) {
2632      my $since = $Since{$symbol};
2633      $since =~ s/^\s+//;
2634      $since =~ s/\s+$//;
2635      if ($since ne "") {
2636          $terms .= "<indexterm zone=\"$id\" role=\"$since\"><primary$sortas>$symbol</primary></indexterm>";
2637      }
2638      $IndexEntriesSince{$symbol}=$id;
2639      $IndexEntriesFull{$symbol}=$id;
2640   }
2641   if ($terms eq "") {
2642      $terms .= "<indexterm zone=\"$id\"><primary$sortas>$symbol</primary></indexterm>";
2643      $IndexEntriesFull{$symbol}=$id;
2644   }
2646   return $terms;
2649 #############################################################################
2650 # Function    : MakeDeprecationNote
2651 # Description : This returns a deprecation warning for the given symbol.
2652 # Arguments   : $symbol - the symbol to try to create a warning for.
2653 #############################################################################
2655 sub MakeDeprecationNote {
2656     my ($symbol) = $_[0];
2657     my $desc = "";
2658     my $note = "";
2659     if (exists $Deprecated{$symbol}) {
2660         $desc .= "<warning>";
2662         if ($Deprecated{$symbol} =~ /^\s*([0-9\.]+)\s*:/) {
2663                 $desc .= "<para><literal>$symbol</literal> has been deprecated since version $1 and should not be used in newly-written code.";
2664         } else {
2665                 $desc .= "<para><literal>$symbol</literal> is deprecated and should not be used in newly-written code.";
2666         }
2667         if ($Deprecated{$symbol} ne "") {
2668             $note = &ExpandAbbreviations($symbol, $Deprecated{$symbol});
2669             $note =~ s/^\s*([0-9\.]+)\s*:\s*//;
2670             $note =~ s/^\s+//;
2671             $note =~ s/\s+$//;
2672             $note =~ s%\n{2,}%\n</para>\n<para>\n%g;
2673             $desc .= " " . $note;
2674         }
2675         $desc .= "</para></warning>\n";
2676     }
2677     return $desc;
2680 #############################################################################
2681 # Function    : MakeConditionDescription
2682 # Description : This returns a sumary of conditions for the given symbol.
2683 # Arguments   : $symbol - the symbol to try to create the sumary.
2684 #############################################################################
2686 sub MakeConditionDescription {
2687     my ($symbol) = $_[0];
2688     my $desc = "";
2690     if (exists $Deprecated{$symbol}) {
2691         if ($desc ne "") {
2692             $desc .= "|";
2693         }
2695         if ($Deprecated{$symbol} =~ /^\s*(.*?)\s*$/) {
2696                 $desc .= "deprecated:$1";
2697         } else {
2698                 $desc .= "deprecated";
2699         }
2700     }
2702     if (exists $Since{$symbol}) {
2703         if ($desc ne "") {
2704             $desc .= "|";
2705         }
2707         if ($Since{$symbol} =~ /^\s*(.*?)\s*$/) {
2708                 $desc .= "since:$1";
2709         } else {
2710                 $desc .= "since";
2711         }
2712     }
2714     if (exists $StabilityLevel{$symbol}) {
2715         if ($desc ne "") {
2716             $desc .= "|";
2717         }
2718         $desc .= "stability:".$StabilityLevel{$symbol};
2719     }
2721     if ($desc ne "") {
2722         $desc=" condition=\"".$desc."\"";
2723         #print "condition for '$symbol' = '$desc'\n";
2724     }
2725     return $desc;
2728 #############################################################################
2729 # Function    : GetHierarchy
2730 # Description : Returns the DocBook output describing the ancestors and
2731 #               immediate children of a GObject subclass. It uses the
2732 #               global @Objects and @ObjectLevels arrays to walk the tree.
2733 # Arguments   : $object - the GtkObject subclass.
2734 #############################################################################
2736 sub GetHierarchy {
2737     my ($object) = @_;
2739     # Find object in the objects array.
2740     my $found = 0;
2741     my @children = ();
2742     my $i;
2743     my $level;
2744     my $j;
2745     for ($i = 0; $i < @Objects; $i++) {
2746         if ($found) {
2747             if ($ObjectLevels[$i] <= $level) {
2748             last;
2749         }
2750             elsif ($ObjectLevels[$i] == $level + 1) {
2751                 push (@children, $Objects[$i]);
2752             }
2753         }
2754         elsif ($Objects[$i] eq $object) {
2755             $found = 1;
2756             $j = $i;
2757             $level = $ObjectLevels[$i];
2758         }
2759     }
2760     if (!$found) {
2761         return "";
2762     }
2764     # Walk up the hierarchy, pushing ancestors onto the ancestors array.
2765     my @ancestors = ();
2766     push (@ancestors, $object);
2767     #print "Level: $level\n";
2768     while ($level > 1) {
2769         $j--;
2770         if ($ObjectLevels[$j] < $level) {
2771             push (@ancestors, $Objects[$j]);
2772             $level = $ObjectLevels[$j];
2773             #print "Level: $level\n";
2774         }
2775     }
2777     # Output the ancestors list, indented and with links.
2778     my $hierarchy = "<synopsis>\n";
2779     $level = 0;
2780     for ($i = $#ancestors; $i >= 0; $i--) {
2781         my $link_text;
2782         # Don't add a link to the current widget, i.e. when i == 0.
2783         if ($i > 0) {
2784             my $ancestor_id = &CreateValidSGMLID ($ancestors[$i]);
2785             $link_text = "<link linkend=\"$ancestor_id\">$ancestors[$i]</link>";
2786         } else {
2787             $link_text = "$ancestors[$i]";
2788         }
2789         if ($level == 0) {
2790             $hierarchy .= "  $link_text\n";
2791         } else {
2792 #           $hierarchy .= ' ' x ($level * 6 - 3) . "|\n";
2793             $hierarchy .= ' ' x ($level * 6 - 3) . "+----$link_text\n";
2794         }
2795         $level++;
2796     }
2797     for ($i = 0; $i <= $#children; $i++) {
2798       my $id = &CreateValidSGMLID ($children[$i]);
2799       my $link_text = "<link linkend=\"$id\">$children[$i]</link>";
2800       $hierarchy .= ' ' x ($level * 6 - 3) . "+----$link_text\n";
2801     }
2802     $hierarchy .= "</synopsis>\n";
2804     return $hierarchy;
2808 #############################################################################
2809 # Function    : GetInterfaces
2810 # Description : Returns the DocBook output describing the interfaces
2811 #               implemented by a class. It uses the global %Interfaces hash.
2812 # Arguments   : $object - the GtkObject subclass.
2813 #############################################################################
2815 sub GetInterfaces {
2816     my ($object) = @_;
2817     my $text = "";
2818     my $i;
2820     # Find object in the objects array.
2821     if (exists($Interfaces{$object})) {
2822         my @ifaces = split(' ', $Interfaces{$object});
2823         $text = <<EOF;
2824 <para>
2825 $object implements
2827         for ($i = 0; $i <= $#ifaces; $i++) {
2828             my $id = &CreateValidSGMLID ($ifaces[$i]);
2829             $text .= " <link linkend=\"$id\">$ifaces[$i]</link>";
2830             if ($i < $#ifaces - 1) {
2831                 $text .= ', ';
2832             }
2833             elsif ($i < $#ifaces) {
2834                 $text .= ' and ';
2835             }
2836             else {
2837                 $text .= '.';
2838             }
2839         }
2840         $text .= <<EOF;
2841 </para>
2843     }
2845     return $text;
2848 #############################################################################
2849 # Function    : GetImplementations
2850 # Description : Returns the DocBook output describing the implementations
2851 #               of an interface. It uses the global %Interfaces hash.
2852 # Arguments   : $object - the GtkObject subclass.
2853 #############################################################################
2855 sub GetImplementations {
2856     my ($object) = @_;
2857     my @impls = ();
2858     my $text = "";
2859     my $i;
2860     foreach my $key (keys %Interfaces) {
2861         if ($Interfaces{$key} =~ /\b$object\b/) {
2862             push (@impls, $key);
2863         }
2864     }
2865     if ($#impls >= 0) {
2866         $text = <<EOF;
2867 <para>
2868 $object is implemented by
2870         for ($i = 0; $i <= $#impls; $i++) {
2871             my $id = &CreateValidSGMLID ($impls[$i]);
2872             $text .= " <link linkend=\"$id\">$impls[$i]</link>";
2873             if ($i < $#impls - 1) {
2874                 $text .= ', ';
2875             }
2876             elsif ($i < $#impls) {
2877                 $text .= ' and ';
2878             }
2879             else {
2880                 $text .= '.';
2881             }
2882         }
2883         $text .= <<EOF;
2884 </para>
2886     }
2887     return $text;
2891 #############################################################################
2892 # Function    : GetPrerequisites
2893 # Description : Returns the DocBook output describing the prerequisites
2894 #               of an interface. It uses the global %Prerequisites hash.
2895 # Arguments   : $iface - the interface.
2896 #############################################################################
2898 sub GetPrerequisites {
2899     my ($iface) = @_;
2900     my $text = "";
2901     my $i;
2903     if (exists($Prerequisites{$iface})) {
2904         $text = <<EOF;
2905 <para>
2906 $iface requires
2908         my @prereqs = split(' ', $Prerequisites{$iface});
2909         for ($i = 0; $i <= $#prereqs; $i++) {
2910             my $id = &CreateValidSGMLID ($prereqs[$i]);
2911             $text .= " <link linkend=\"$id\">$prereqs[$i]</link>";
2912             if ($i < $#prereqs - 1) {
2913                 $text .= ', ';
2914             }
2915             elsif ($i < $#prereqs) {
2916                 $text .= ' and ';
2917             }
2918             else {
2919                 $text .= '.';
2920             }
2921         }
2922         $text .= <<EOF;
2923 </para>
2925     }
2926     return $text;
2929 #############################################################################
2930 # Function    : GetDerived
2931 # Description : Returns the DocBook output describing the derived interfaces
2932 #               of an interface. It uses the global %Prerequisites hash.
2933 # Arguments   : $iface - the interface.
2934 #############################################################################
2936 sub GetDerived {
2937     my ($iface) = @_;
2938     my $text = "";
2939     my $i;
2941     my @derived = ();
2942     foreach my $key (keys %Prerequisites) {
2943         if ($Prerequisites{$key} =~ /\b$iface\b/) {
2944             push (@derived, $key);
2945         }
2946     }
2947     if ($#derived >= 0) {
2948         $text = <<EOF;
2949 <para>
2950 $iface is required by
2952         for ($i = 0; $i <= $#derived; $i++) {
2953             my $id = &CreateValidSGMLID ($derived[$i]);
2954             $text .= " <link linkend=\"$id\">$derived[$i]</link>";
2955             if ($i < $#derived - 1) {
2956                 $text .= ', ';
2957             }
2958             elsif ($i < $#derived) {
2959                 $text .= ' and ';
2960             }
2961             else {
2962                 $text .= '.';
2963             }
2964         }
2965         $text .= <<EOF;
2966 </para>
2968     }
2969     return $text;
2973 #############################################################################
2974 # Function    : GetSignals
2975 # Description : Returns the synopsis and detailed description DocBook output
2976 #               for the signal handlers of a given GtkObject subclass.
2977 # Arguments   : $object - the GtkObject subclass, e.g. 'GtkButton'.
2978 #############################################################################
2980 sub GetSignals {
2981     my ($object) = @_;
2982     my $synop = "";
2983     my $desc = "";
2985     my $i;
2986     for ($i = 0; $i <= $#SignalObjects; $i++) {
2987         if ($SignalObjects[$i] eq $object) {
2988             #print "Found signal: $SignalNames[$i]\n";
2989             my $name = $SignalNames[$i];
2990             my $symbol = "${object}::${name}";
2991             my $id = &CreateValidSGMLID ("$object-$name");
2993             my $pad = ' ' x (46 - length($name));
2994             $synop .= "  &quot;<link linkend=\"$id\">$name</link>&quot;$pad ";
2996             $desc .= "<refsect2 id=\"$id\" role=\"signal\"><title>The <literal>&quot;$name&quot;</literal> signal</title>\n";
2997             $desc .= MakeIndexterms($symbol, $id);
2998             $desc .= "\n";
2999             $desc .= OutputSymbolExtraLinks($symbol);
3001             $desc .= "<programlisting>";
3003             $SignalReturns[$i] =~ m/\s*(const\s+)?(\w+)\s*(\**)/;
3004             my $type_modifier = defined($1) ? $1 : "";
3005             my $type = $2;
3006             my $pointer = $3;
3007             my $xref = &MakeXRef ($type);
3009             my $ret_type_len = length ($type_modifier) + length ($pointer)
3010                 + length ($type);
3011             my $ret_type_output = "$type_modifier$xref$pointer"
3012                 . (' ' x ($RETURN_TYPE_FIELD_WIDTH - $ret_type_len));
3014             $desc  .= "${ret_type_output}user_function " . &MakeReturnField("") . " (";
3016             my $sourceparams = $SourceSymbolParams{$symbol};
3017             my @params = split ("\n", $SignalPrototypes[$i]);
3018             my $j;
3019             my $l;
3020             my $type_len = length("gpointer");
3021             my $name_len = length("user_data");
3022             # do two passes, the first one is to calculate padding
3023             for ($l = 0; $l < 2; $l++) {
3024                 for ($j = 0; $j <= $#params; $j++) {
3025                     # allow alphanumerics, '_', '[' & ']' in param names
3026                     if ($params[$j] =~ m/^\s*(\w+)\s*(\**)\s*([\w\[\]]+)\s*$/) {
3027                         $type = $1;
3028                         $pointer = $2;
3029                         if (defined($sourceparams)) {
3030                             $name = $$sourceparams[$PARAM_FIELD_COUNT * $j];
3031                         }
3032                         else {
3033                             $name = $3;
3034                         }
3035                         if (!defined($name)) {
3036                             $name = "arg$j";
3037                         }
3038                         if ($l == 0) {
3039                             if (length($type) + length($pointer) > $type_len) {
3040                                 $type_len = length($type) + length($pointer);
3041                             }
3042                             if (length($name) > $name_len) {
3043                                 $name_len = length($name);
3044                             }
3045                         }
3046                         else {
3047                             $xref = &MakeXRef ($type);
3048                             $pad = ' ' x ($type_len - length($type) - length($pointer));
3049                             $desc .= "$xref$pad $pointer$name,\n";
3050                             $desc .= (' ' x ($SYMBOL_FIELD_WIDTH + $RETURN_TYPE_FIELD_WIDTH));
3051                         }
3052                     } else {
3053                         &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
3054                              "Can't parse arg: $params[$j]\nArgs:$SignalPrototypes[$i]");
3055                     }
3056                 }
3057             }
3058             $xref = &MakeXRef ("gpointer");
3059             $pad = ' ' x ($type_len - length("gpointer"));
3060             $desc  .= "$xref$pad user_data)";
3062             my $flags = $SignalFlags[$i];
3063             my $flags_string = "";
3065             if (defined ($flags)) {
3066               if ($flags =~ m/f/) {
3067                 $flags_string = "Run First";
3068               }
3069               elsif ($flags =~ m/l/) {
3070                 $flags_string = "Run Last";
3071               }
3072               elsif ($flags =~ m/c/) {
3073                 $flags_string = "Cleanup";
3074               }
3075               if ($flags =~ m/r/) {
3076                 if ($flags_string) { $flags_string .= " / "; }
3077                 $flags_string .= "No Recursion";
3078               }
3079               if ($flags =~ m/d/) {
3080                 if ($flags_string) { $flags_string .= " / "; }
3081                 $flags_string .= "Has Details";
3082               }
3083               if ($flags =~ m/a/) {
3084                 if ($flags_string) { $flags_string .= " / "; }
3085                 $flags_string .= "Action";
3086               }
3087               if ($flags =~ m/h/) {
3088                 if ($flags_string) { $flags_string .= " / "; }
3089                 $flags_string .= "No Hooks";
3090               }
3091             }
3093             if ($flags_string)
3094               {
3095                 $synop .= ": $flags_string\n";
3097                 $pad = ' ' x (5 + $name_len - length("user_data"));
3098                 $desc  .= "$pad : $flags_string</programlisting>\n";
3099               }
3100             else
3101               {
3102                 $synop .= "\n";
3103                 $desc  .= "</programlisting>\n";
3104               }
3106             $desc .= &MakeDeprecationNote($symbol);
3108             my $parameters = &OutputParamDescriptions ("SIGNAL", $symbol);
3109             my $parameters_output = 0;
3111             $AllSymbols{$symbol} = 1;
3112             if (defined ($SymbolDocs{$symbol})) {
3113                 my $symbol_docs = &ExpandAbbreviations($symbol, $SymbolDocs{$symbol});
3115                 # Try to insert the parameter table at the author's desired
3116                 # position. Otherwise we need to tag it onto the end.
3117                 if ($symbol_docs =~ s/<!--PARAMETERS-->/$parameters/) {
3118                   $parameters_output = 1;
3119                 }
3120                 $desc .= $symbol_docs;
3122                 if (!IsEmptyDoc($SymbolDocs{$symbol})) {
3123                     $AllDocumentedSymbols{$symbol} = 1;
3124                 }
3125             }
3127             if ($parameters_output == 0) {
3128                 $desc .= $parameters;
3129               }
3130             $desc .= OutputSymbolTraits ($symbol);
3131             $desc .= "</refsect2>";
3132         }
3133     }
3134     return ($synop, $desc);
3138 #############################################################################
3139 # Function    : GetArgs
3140 # Description : Returns the synopsis and detailed description DocBook output
3141 #               for the Args of a given GtkObject subclass.
3142 # Arguments   : $object - the GtkObject subclass, e.g. 'GtkButton'.
3143 #############################################################################
3145 sub GetArgs {
3146     my ($object) = @_;
3147     my $synop = "";
3148     my $desc = "";
3149     my $child_synop = "";
3150     my $child_desc = "";
3151     my $style_synop = "";
3152     my $style_desc = "";
3154     my $i;
3155     for ($i = 0; $i <= $#ArgObjects; $i++) {
3156         if ($ArgObjects[$i] eq $object) {
3157             #print "Found arg: $ArgNames[$i]\n";
3158             my $name = $ArgNames[$i];
3159             my $flags = $ArgFlags[$i];
3160             my $flags_string = "";
3161             my $kind = "";
3162             my $id_sep = "";
3164             if ($flags =~ m/c/) {
3165                 $kind = "child property";
3166                 $id_sep = "c-";
3167             }
3168             elsif ($flags =~ m/s/) {
3169                 $kind = "style property";
3170                 $id_sep = "s-";
3171             }
3172             else {
3173                 $kind = "property";
3174             }
3176             # Remember only one colon so we don't clash with signals.
3177             my $symbol = "${object}:${name}";
3178             # use two dashes and ev. an extra separator here for the same reason.
3179             my $id = &CreateValidSGMLID ("$object--$id_sep$name");
3181             my $type = $ArgTypes[$i];
3182             my $type_output;
3183             my $range = $ArgRanges[$i];
3184             my $range_output = CreateValidSGML ($range);
3185             my $default = $ArgDefaults[$i];
3186             my $default_output = CreateValidSGML ($default);
3188             if ($type eq "GtkString") {
3189                 $type = "char*";
3190             }
3191             if ($type eq "GtkSignal") {
3192                 $type = "GtkSignalFunc, gpointer";
3193                 $type_output = &MakeXRef ("GtkSignalFunc") . ", "
3194                     . &MakeXRef ("gpointer");
3195             } elsif ($type =~ m/^(\w+)\*$/) {
3196                 $type_output = &MakeXRef ($1) . "*";
3197             } else {
3198                 $type_output = &MakeXRef ($type);
3199             }
3201             if ($flags =~ m/r/) {
3202                 $flags_string = "Read";
3203             }
3204             if ($flags =~ m/w/) {
3205                 if ($flags_string) { $flags_string .= " / "; }
3206                 $flags_string .= "Write";
3207             }
3208             if ($flags =~ m/x/) {
3209                 if ($flags_string) { $flags_string .= " / "; }
3210                 $flags_string .= "Construct";
3211             }
3212             if ($flags =~ m/X/) {
3213                 if ($flags_string) { $flags_string .= " / "; }
3214                 $flags_string .= "Construct Only";
3215             }
3217             $AllSymbols{$symbol} = 1;
3218             my $blurb;
3219             if (defined($SymbolDocs{$symbol}) &&
3220                 !IsEmptyDoc($SymbolDocs{$symbol})) {
3221                 $blurb = &ExpandAbbreviations($symbol, $SymbolDocs{$symbol});
3222                 $AllDocumentedSymbols{$symbol} = 1;
3223             }
3224             else {
3225                 if (!($ArgBlurbs[$i] eq "")) {
3226                     $AllDocumentedSymbols{$symbol} = 1;
3227                 }
3228                 $blurb = "<para>" . &CreateValidSGML ($ArgBlurbs[$i]) . "</para>";
3229             }
3231             my $pad1 = " " x (24 - length ($name));
3232             my $pad2 = " " x (20 - length ($type));
3234             my $arg_synop = "  &quot;<link linkend=\"$id\">$name</link>&quot;$pad1 $type_output $pad2 : $flags_string\n";
3235             my $arg_desc = "<refsect2 id=\"$id\" role=\"property\"><title>The <literal>&quot;$name&quot;</literal> $kind</title>\n";
3236             $arg_desc .= MakeIndexterms($symbol, $id);
3237             $arg_desc .= "\n";
3238             $arg_desc .= OutputSymbolExtraLinks($symbol);
3240             $arg_desc .= "<programlisting>  &quot;$name&quot;$pad1 $type_output $pad2 : $flags_string</programlisting>\n";
3241             $arg_desc .= &MakeDeprecationNote($symbol);
3242             $arg_desc .= $blurb;
3243             if ($range ne "") {
3244                 $arg_desc .= "<para>Allowed values: $range_output</para>\n";
3245             }
3246             if ($default ne "") {
3247                 $arg_desc .= "<para>Default value: $default_output</para>\n";
3248             }
3249             $arg_desc .= OutputSymbolTraits ($symbol);
3250             $arg_desc .= "</refsect2>\n";
3252             if ($flags =~ m/c/) {
3253                 $child_synop .= $arg_synop;
3254                 $child_desc .= $arg_desc;
3255             }
3256             elsif ($flags =~ m/s/) {
3257                 $style_synop .= $arg_synop;
3258                 $style_desc .= $arg_desc;
3259             }
3260             else {
3261                 $synop .= $arg_synop;
3262                 $desc .= $arg_desc;
3263             }
3264         }
3265     }
3266     return ($synop, $child_synop, $style_synop, $desc, $child_desc, $style_desc);
3270 #############################################################################
3271 # Function    : ReadSourceDocumentation
3272 # Description : This reads in the documentation embedded in comment blocks
3273 #               in the source code (for Gnome).
3275 #               Parameter descriptions override any in the template files.
3276 #               Function descriptions are placed before any description from
3277 #               the template files.
3279 #               It recursively descends the source directory looking for .c
3280 #               files and scans them looking for specially-formatted comment
3281 #               blocks.
3283 # Arguments   : $source_dir - the directory to scan.
3284 #############m###############################################################
3286 sub ReadSourceDocumentation {
3287     my ($source_dir) = @_;
3288     my ($file, $dir, @suffix_list, $suffix);
3289     #print "Scanning source directory: $source_dir\n";
3291     # This array holds any subdirectories found.
3292     my (@subdirs) = ();
3294     @suffix_list = split (/,/, $SOURCE_SUFFIXES);
3296     opendir (SRCDIR, $source_dir)
3297         || die "Can't open source directory $source_dir: $!";
3299     foreach $file (readdir (SRCDIR)) {
3300       if ($file =~ /^\./) {
3301         next;
3302       } elsif (-d "$source_dir/$file") {
3303         push (@subdirs, $file);
3304       } elsif (@suffix_list) {
3305         foreach $suffix (@suffix_list) {
3306           if ($file =~ m/\.\Q${suffix}\E$/) {
3307             &ScanSourceFile ("$source_dir/$file");
3308           }
3309         }
3310       } elsif ($file =~ m/\.[ch]$/) {
3311         &ScanSourceFile ("$source_dir/$file");
3312       }
3313     }
3314     closedir (SRCDIR);
3316     # Now recursively scan the subdirectories.
3317     foreach $dir (@subdirs) {
3318         next if ($IGNORE_FILES =~ m/(\s|^)\Q${dir}\E(\s|$)/);
3319         &ReadSourceDocumentation ("$source_dir/$dir");
3320     }
3324 #############################################################################
3325 # Function    : ScanSourceFile
3326 # Description : Scans one source file looking for specially-formatted comment
3327 #               blocks. Later &MergeSourceDocumentation is used to merge any
3328 #               documentation found with the documentation already read in
3329 #               from the template files.
3331 # Arguments   : $file - the file to scan.
3332 #############################################################################
3334 sub ScanSourceFile {
3335     my ($file) = @_;
3336     my $basename;
3338     if ($file =~ m/^.*[\/\\]([^\/\\]*)$/) {
3339         $basename = $1;
3340     } else {
3341         &LogWarning ($file, 1, "Can't find basename for this filename.");
3342         $basename = $file;
3343     }
3345     # Check if the basename is in the list of files to ignore.
3346     if ($IGNORE_FILES =~ m/(\s|^)\Q${basename}\E(\s|$)/) {
3347         return;
3348     }
3350     #print "DEBUG: Scanning $file\n";
3352     open (SRCFILE, $file)
3353         || die "Can't open $file: $!";
3354     my $in_comment_block = 0;
3355     my $symbol;
3356     my ($in_description, $in_return, $in_since, $in_stability, $in_deprecated);
3357     my ($description, $return_desc, $return_start, $return_style);
3358     my ($since_desc, $stability_desc, $deprecated_desc);
3359     my $current_param;
3360     my $ignore_broken_returns;
3361     my @params;
3362     while (<SRCFILE>) {
3363         # Look for the start of a comment block.
3364         if (!$in_comment_block) {
3365             if (m%^\s*/\*.*\*/%) {
3366                 #one-line comment - not gtkdoc
3367             } elsif (m%^\s*/\*\*\s%) {
3368                 #print "Found comment block start\n";
3370                 $in_comment_block = 1;
3372                 # Reset all the symbol data.
3373                 $symbol = "";
3374                 $in_description = 0;
3375                 $in_return = 0;
3376                 $in_since = 0;
3377                 $in_deprecated = 0;
3378                 $in_stability = 0;
3379                 $description = "";
3380                 $return_desc = "";
3381                 $return_style = "";
3382                 $since_desc = "";
3383                 $deprecated_desc = "";
3384                 $stability_desc = "";
3385                 $current_param = -1;
3386                 $ignore_broken_returns = 0;
3387                 @params = ();
3388             }
3389             next;
3390         }
3392         # We're in a comment block. Check if we've found the end of it.
3393         if (m%^\s*\*+/%) {
3394             if (!$symbol) {
3395                 # maybe its not even meant to be a gtk-doc comment?
3396                 &LogWarning ($file, $., "Symbol name not found at the start of the comment block.");
3397             } else {
3398                 # Add the return value description onto the end of the params.
3399                 if ($return_desc) {
3400                     push (@params, "Returns");
3401                     push (@params, $return_desc);
3402                     if ($return_style eq 'broken') {
3403                         &LogWarning ($file, $., "Free-form return value description in $symbol. Use `Returns:' to avoid ambiguities.");
3404                     }
3405                 }
3406                 # Convert special SGML characters
3407                 $description = &ConvertSGMLChars ($symbol, $description);
3408                 my $k;
3409                 for ($k = 1; $k <= $#params; $k += $PARAM_FIELD_COUNT) {
3410                     $params[$k] = &ConvertSGMLChars ($symbol, $params[$k]);
3411                 }
3413                 # Handle Section docs
3414                 if ($symbol =~ m/SECTION:\s*(.*)/) {
3415                     my $real_symbol=$1;
3416                     my $key;
3418                     #print "SECTION DOCS found in source for : '$real_symbol'\n";
3419                     $ignore_broken_returns = 1;
3420                     for ($k = 0; $k <= $#params; $k += $PARAM_FIELD_COUNT) {
3421                         #print "   '".$params[$k]."'\n";
3422                         $params[$k] = "\L$params[$k]";
3423                         undef $key;
3424                         if ($params[$k] eq "short_description") {
3425                             $key = "$TMPL_DIR/$real_symbol:Short_Description";
3426                         } elsif ($params[$k] eq "see_also") {
3427                             $key = "$TMPL_DIR/$real_symbol:See_Also";
3428                         } elsif ($params[$k] eq "title") {
3429                             $key = "$TMPL_DIR/$real_symbol:Title";
3430                         } elsif ($params[$k] eq "stability") {
3431                             $key = "$TMPL_DIR/$real_symbol:Stability_Level";
3432                         } elsif ($params[$k] eq "section_id") {
3433                             $key = "$TMPL_DIR/$real_symbol:Section_Id";
3434                         } elsif ($params[$k] eq "include") {
3435                             $key = "$TMPL_DIR/$real_symbol:Include";
3436                         }
3437                         if (defined($key)) {
3438                             $SourceSymbolDocs{$key}=$params[$k+1];
3439                             $SourceSymbolSourceFile{$key} = $file;
3440                             $SourceSymbolSourceLine{$key} = $.;
3441                         }
3442                     }
3443                     $SourceSymbolDocs{"$TMPL_DIR/$real_symbol:Long_Description"}=$description;
3444                     $SourceSymbolSourceFile{"$TMPL_DIR/$real_symbol:Long_Description"} = $file;
3445                     $SourceSymbolSourceLine{"$TMPL_DIR/$real_symbol:Long_Description"} = $.;
3446                     #$SourceSymbolTypes{$symbol} = "SECTION";
3447                 } else {
3448                     #print "SYMBOL DOCS found in source for : '$symbol' ",length($description), "\n";
3449                     $SourceSymbolDocs{$symbol} = $description;
3450                     $SourceSymbolParams{$symbol} = [ @params ];
3451                     # FIXME $SourceSymbolTypes{$symbol} = "STRUCT,SIGNAL,ARG,FUNCTION,MACRO";
3452                     #if (defined $DeclarationTypes{$symbol}) {
3453                     #    $SourceSymbolTypes{$symbol} = $DeclarationTypes{$symbol}
3454                     #}
3455                     $SourceSymbolSourceFile{$symbol} = $file;
3456                     $SourceSymbolSourceLine{$symbol} = $.;
3457                 }
3459                 if ($since_desc) {
3460                      ($since_desc, my @extra_lines) = split ("\n", $since_desc);
3461                      $since_desc =~ s/^\s+//;
3462                      $since_desc =~ s/\s+$//;
3463                      #print "Since($symbol) : [$since_desc]\n";
3464                      $Since{$symbol} = &ConvertSGMLChars ($symbol, $since_desc);
3465                      if(scalar @extra_lines) {
3466                          &LogWarning ($file, $., "multi-line since docs found");
3467                      }
3468                 }
3470                 if ($stability_desc) {
3471                     $stability_desc = &ParseStabilityLevel($stability_desc, $file, $., "Stability level for $symbol");
3472                     $StabilityLevel{$symbol} = &ConvertSGMLChars ($symbol, $stability_desc);
3473                 }
3475                 if ($deprecated_desc) {
3476                     if (exists $Deprecated{$symbol}) {
3477                     }
3478                     else {
3479                          # don't warn for signals and properties
3480                          #if ($symbol !~ m/::?(.*)/) {
3481                          if (defined $DeclarationTypes{$symbol}) {
3482                              &LogWarning ($file, $., 
3483                                  "$symbol is deprecated in the inline comments, but no deprecation guards were found around the declaration.".
3484                                  " (See the --deprecated-guards option for gtkdoc-scan.)");
3485                          }
3486                     }
3487                     $Deprecated{$symbol} = &ConvertSGMLChars ($symbol, $deprecated_desc);
3488                 }
3489             }
3491             $in_comment_block = 0;
3492             next;
3493         }
3495         # Get rid of ' * ' at start of every line in the comment block.
3496         s%^\s*\*\s?%%;
3497         # But make sure we don't get rid of the newline at the end.
3498         if (!$_) {
3499             $_ = "\n";
3500         }
3501         #print "DEBUG: scanning :$_";
3503         # If we haven't found the symbol name yet, look for it.
3504         if (!$symbol) {
3505             if (m%^\s*(SECTION:\s*\S+)%) {
3506                 $symbol = $1;
3507                 #print "SECTION DOCS found in source for : '$symbol'\n";
3508                 $ignore_broken_returns = 1;
3509             } elsif (m%^\s*([\w:-]*\w)\s*:?\s*$%) {
3510                 $symbol = $1;
3511                 #print "SYMBOL DOCS found in source for : '$symbol'\n";
3512             }
3513             next;
3514         }
3516         # If we're in the return value description, add it to the end.
3517         if ($in_return) {
3518             # If we find another valid returns line, we assume that the first
3519             # one was really part of the description.
3520             if (m/^\s*(returns:|return\s+value:)/i) {
3521                 if ($return_style eq 'broken') {
3522                     $description .= $return_start . $return_desc;
3523                 }
3524                 $return_start = $1;
3525                 if ($return_style eq 'sane') {
3526                     &LogWarning ($file, $., "Multiple Returns for $symbol.");
3527                 }
3528                 $return_style = 'sane';
3529                 $ignore_broken_returns = 1;
3530                 $return_desc = $';
3531             } elsif (!$ignore_broken_returns && m/^\s*(returns\b\s*)/i) {
3532                 $description .= $return_start . $return_desc;
3533                 $return_start = $1;
3534                 $return_style = 'broken';
3535                 $return_desc = $';
3536             } elsif (m%^\s*since:%i) {
3537                 $since_desc = $';
3538                 $in_since = 1;
3539                 $in_return = 0;
3540             } elsif (m%^\s*stability:%i) {
3541                 $stability_desc = $';
3542                 $in_stability = 1;
3543                 $in_return = 0;
3544             } elsif (m%^\s*deprecated:%i) {
3545                 $deprecated_desc = $';
3546                 $in_deprecated = 1;
3547                 $in_return = 0;
3548             } else {
3549                 $return_desc .= $_;
3550             }
3551             next;
3552         }
3554         if ($in_since) {
3555             if (m/^\s*(returns:|return\s+value:)/i) {
3556                 if ($return_style eq 'broken') {
3557                     $description .= $return_start . $return_desc;
3558                 }
3559                 $return_start = $1;
3560                 if ($return_style eq 'sane') {
3561                     &LogWarning ($file, $., "Multiple Returns for $symbol.");
3562                 }
3563                 $return_style = 'sane';
3564                 $ignore_broken_returns = 1;
3565                 $return_desc = $';
3566                 $in_return = 1;
3567                 $in_since = 0;
3568             } elsif (!$ignore_broken_returns && m/^\s*(returns\b\s*)/i) {
3569                 $return_start = $1;
3570                 $return_style = 'broken';
3571                 $return_desc = $';
3572                 $in_return = 1;
3573                 $in_since = 0;
3574             } elsif (m%^\s*deprecated:%i) {
3575                 $deprecated_desc = $';
3576                 $in_deprecated = 1;
3577                 $in_since = 0;
3578             } elsif (m%^\s*stability:%i) {
3579                 $stability_desc = $';
3580                 $in_stability = 1;
3581                 $in_since = 0;
3582             } else {
3583                 $since_desc .= $_;
3584             }
3585             next;
3586         }
3588         if ($in_stability) {
3589             if (m/^\s*(returns:|return\s+value:)/i) {
3590                 if ($return_style eq 'broken') {
3591                     $description .= $return_start . $return_desc;
3592                 }
3593                 $return_start = $1;
3594                 if ($return_style eq 'sane') {
3595                     &LogWarning ($file, $., "Multiple Returns for $symbol.");
3596                 }
3597                 $return_style = 'sane';
3598                 $ignore_broken_returns = 1;
3599                 $return_desc = $';
3600                 $in_return = 1;
3601                 $in_stability = 0;
3602             } elsif (!$ignore_broken_returns && m/^\s*(returns\b\s*)/i) {
3603                 $return_start = $1;
3604                 $return_style = 'broken';
3605                 $return_desc = $';
3606                 $in_return = 1;
3607                 $in_stability = 0;
3608             } elsif (m%^\s*deprecated:%i) {
3609                 $deprecated_desc = $';
3610                 $in_deprecated = 1;
3611                 $in_stability = 0;
3612             } elsif (m%^\s*since:%i) {
3613                 $since_desc = $';
3614                 $in_since = 1;
3615                 $in_stability = 0;
3616             } else {
3617                 $stability_desc .= $_;
3618             }
3619             next;
3620         }
3622         if ($in_deprecated) {
3623             if (m/^\s*(returns:|return\s+value:)/i) {
3624                 if ($return_style eq 'broken') {
3625                     $description .= $return_start . $return_desc;
3626                 }
3627                 $return_start = $1;
3628                 if ($return_style eq 'sane') {
3629                     &LogWarning ($file, $., "Multiple Returns for $symbol.");
3630                 }
3631                 $return_style = 'sane';
3632                 $ignore_broken_returns = 1;
3633                 $return_desc = $';
3634                 $in_return = 1;
3635                 $in_deprecated = 0;
3636             } elsif (!$ignore_broken_returns && m/^\s*(returns\b\s*)/i) {
3637                 $return_start = $1;
3638                 $return_style = 'broken';
3639                 $return_desc = $';
3640                 $in_return = 1;
3641                 $in_deprecated = 0;
3642             } elsif (m%^\s*since:%i) {
3643                 $since_desc = $';
3644                 $in_since = 1;
3645                 $in_deprecated = 0;
3646             } elsif (m%^\s*stability:%i) {
3647                 $stability_desc = $';
3648                 $in_stability = 1;
3649                 $in_deprecated = 0;
3650             } else {
3651                 $deprecated_desc .= $_;
3652             }
3653             next;
3654         }
3656         # If we're in the description part, check for the 'Returns:' line.
3657         # If that isn't found, add the text to the end.
3658         if ($in_description) {
3659             # Get rid of 'Description:'
3660             s%^\s*Description:%%;
3662             if (m/^\s*(returns:|return\s+value:)/i) {
3663                 if ($return_style eq 'broken') {
3664                     $description .= $return_start . $return_desc;
3665                 }
3666                 $return_start = $1;
3667                 if ($return_style eq 'sane') {
3668                     &LogWarning ($file, $., "Multiple Returns for $symbol.");
3669                 }
3670                 $return_style = 'sane';
3671                 $ignore_broken_returns = 1;
3672                 $return_desc = $';
3673                 $in_return = 1;
3674                 next;
3675             } elsif (!$ignore_broken_returns && m/^\s*(returns\b\s*)/i) {
3676                 $return_start = $1;
3677                 $return_style = 'broken';
3678                 $return_desc = $';
3679                 $in_return = 1;
3680                 next;
3681             } elsif (m%^\s*since:%i) {
3682                 $since_desc = $';
3683                 $in_since = 1;
3684                 next;
3685             } elsif (m%^\s*deprecated:%i) {
3686                 $deprecated_desc = $';
3687                 $in_deprecated = 1;
3688                 next;
3689             } elsif (m%^\s*stability:%i) {
3690                 $stability_desc = $';
3691                 $in_stability = 1;
3692                 next;
3693             }
3695             $description .= $_;
3696             next;
3697         }
3699         # We must be in the parameters. Check for the empty line below them.
3700         if (m%^\s*$%) {
3701             $in_description = 1;
3702             next;
3703         }
3705         # Look for a parameter name.
3706         if (m%^\s*@(\S+)\s*:%) {
3707             my $param_name = $1;
3708             my $param_desc = $';
3709             #print "Found parameter: $param_name\n";
3710             # Allow '...' as the Varargs parameter.
3711             if ($param_name eq "...") {
3712                 $param_name = "Varargs";
3713             }
3714             if ("\L$param_name" eq "returns") {
3715                 $return_style = 'sane';
3716                 $ignore_broken_returns = 1;
3717             }
3718             push (@params, $param_name);
3719             push (@params, $param_desc);
3720             $current_param += $PARAM_FIELD_COUNT;
3721             next;
3722         }
3724         # We must be in the middle of a parameter description, so add it on
3725         # to the last element in @params.
3726         if ($current_param == -1) {
3727             &LogWarning ($file, $., "Parsing comment block file : parameter expected.");
3728         } else {
3729             $params[$#params] .= $_;
3730         }
3731     }
3732     close (SRCFILE);
3735 #############################################################################
3736 # Function    : OutputMissingDocumentation
3737 # Description : Outputs report of documentation coverage to a file
3739 # Arguments   : none
3740 #############################################################################
3742 sub OutputMissingDocumentation {
3743     my $n_documented = 0;
3744     my $n_incomplete = 0;
3745     my $total = 0;
3746     my $symbol;
3747     my $percent;
3748     my $msg;
3749     my $buffer = "";
3750     my $buffer_deprecated = "";
3751     my $buffer_descriptions = "";
3752     
3753     open (UNDOCUMENTED, ">$ROOT_DIR/$MODULE-undocumented.txt")
3754       || die "Can't create $ROOT_DIR/$MODULE-undocumented.txt: $!";
3755     
3756     foreach $symbol (sort (keys (%AllSymbols))) {
3757         # FIXME: should we print LogWarnings for undocumented stuff?
3758         # DEBUG
3759         #my $ssfile = &GetSymbolSourceFile($symbol);
3760         #my $ssline = &GetSymbolSourceLine($symbol);
3761         #my $location = "defined at " . (defined($ssfile)?$ssfile:"?") . ":" . (defined($ssline)?$ssline:"0") . "\n";
3762         # DEBUG
3763         if ($symbol !~ /:(Title|Long_Description|Short_Description|See_Also|Stability_Level|Include|Section_Id)/) {
3764             $total++;
3765             if (exists ($AllDocumentedSymbols{$symbol})) {
3766                 $n_documented++;
3767                 if (exists ($AllIncompleteSymbols{$symbol})) {
3768                     $n_incomplete++;
3769                     $buffer .= $symbol . " (" . $AllIncompleteSymbols{$symbol} . ")\n";
3770                     #$buffer .= "\t0: ".$location;
3771                 }
3772             } elsif (exists $Deprecated{$symbol}) {
3773                 if (exists ($AllIncompleteSymbols{$symbol})) {
3774                     $n_incomplete++;
3775                     $buffer_deprecated .= $symbol . " (" . $AllIncompleteSymbols{$symbol} . ")\n";
3776                     #$buffer .= "\t1a: ".$location;
3777                 } else {
3778                     $buffer_deprecated .= $symbol . "\n";
3779                     #$buffer .= "\t1b: ".$location;
3780                 }
3781             } else {
3782                 if (exists ($AllIncompleteSymbols{$symbol})) {
3783                     $n_incomplete++;
3784                     $buffer .= $symbol . " (" . $AllIncompleteSymbols{$symbol} . ")\n";
3785                     #$buffer .= "\t2a: ".$location;
3786                 } else {
3787                     $buffer .= $symbol . "\n";
3788                     #$buffer .= "\t2b: ".$location;
3789                 }
3790             }
3791         } elsif ($symbol =~ /:(Long_Description|Short_Description)/) {
3792             $total++;
3793             #my $len1=(exists($SymbolDocs{$symbol}))?length($SymbolDocs{$symbol}):-1;
3794             #my $len2=(exists($AllDocumentedSymbols{$symbol}))?length($AllDocumentedSymbols{$symbol}):-1;
3795             #print "%%%% $symbol : $len1,$len2\n";
3796             if (((exists ($SymbolDocs{$symbol})) && (length ($SymbolDocs{$symbol}) > 0))
3797             || ((exists ($AllDocumentedSymbols{$symbol})) && (length ($AllDocumentedSymbols{$symbol}) > 0))) {
3798               $n_documented++;
3799             } else {
3800               # cut off the leading namespace ($TMPL_DIR)
3801               $symbol =~ m/^.*\/(.*)$/;
3802               $buffer_descriptions .= $1 . "\n";
3803             }
3804         }
3805     }
3806     
3807     $buffer .= "\n" . $buffer_deprecated . "\n" . $buffer_descriptions;
3808     
3809     if ($total == 0) {
3810       $percent = 100;
3811     } else {
3812       $percent = ($n_documented / $total) * 100.0;
3813     }
3814     
3815     printf UNDOCUMENTED "%.0f%% symbol docs coverage.\n", $percent;
3816     print UNDOCUMENTED "$n_documented symbols documented.\n";
3817     print UNDOCUMENTED "$n_incomplete symbols incomplete.\n";
3818     print UNDOCUMENTED ($total - $n_documented) . " not documented.\n\n\n";
3819     
3820     print UNDOCUMENTED $buffer;
3821     
3822     close (UNDOCUMENTED);
3823     
3824     printf (("%.0f%% symbol docs coverage ($n_documented symbols documented, $n_incomplete symbols incomplete, " . ($total - $n_documented) . " not documented)\nSee $MODULE-undocumented.txt for a list of missing docs.\nThe doc coverage percentage doesn't include intro sections.\n"), $percent);
3828 #############################################################################
3829 # Function    : OutputUndeclaredSymbols
3830 # Description : Outputs symbols that are undeclared yet documented to a file
3832 # Arguments   : none
3833 #############################################################################
3835 sub OutputUndeclaredSymbols {
3836     open(UNDECLARED, ">$ROOT_DIR/$MODULE-undeclared.txt")
3837         || die "Can't create $ROOT_DIR/$MODULE-undeclared.txt";
3839     if (%UndeclaredSymbols) {
3840         print UNDECLARED (join("\n", sort keys %UndeclaredSymbols));
3841         print UNDECLARED "\n";
3842         print "See $MODULE-undeclared.txt for the list of undeclared symbols.\n"
3843     }
3844     close(UNDECLARED);
3848 #############################################################################
3849 # Function    : OutputAllSymbols
3850 # Description : Outputs list of all symbols to a file
3852 # Arguments   : none
3853 #############################################################################
3855 sub OutputAllSymbols {
3856      my $n_documented = 0;
3857      my $total = 0;
3858      my $symbol;
3859      my $percent;
3860      my $msg;
3862      open (SYMBOLS, ">$ROOT_DIR/$MODULE-symbols.txt")
3863           || die "Can't create $ROOT_DIR/$MODULE-symbols.txt: $!";
3865      foreach $symbol (sort (keys (%AllSymbols))) {
3866           print SYMBOLS $symbol . "\n"
3867      }
3869      close (SYMBOLS);
3873 #############################################################################
3874 # Function    : MergeSourceDocumentation
3875 # Description : This merges documentation read from a source file into the
3876 #               documentation read in from a template file.
3878 #               Parameter descriptions override any in the template files.
3879 #               Function descriptions are placed before any description from
3880 #               the template files.
3882 # Arguments   : none
3883 #############################################################################
3885 sub MergeSourceDocumentation {
3886     my $symbol;
3887     my @Symbols;
3889     if (scalar %SymbolDocs) {
3890         @Symbols=keys (%SymbolDocs);
3891         #print "num existing entries: ".(scalar @Symbols)."\n";
3892         #print "  ",$Symbols[0], " ",$Symbols[1],"\n";
3893     }
3894     else {
3895         # filter scanned declarations, with what we suppress from -sections.txt
3896         my %tmp = ();
3897         foreach $symbol (keys (%Declarations)) {
3898             if (defined($KnownSymbols{$symbol}) && $KnownSymbols{$symbol} == 1) {
3899                 $tmp{$symbol}=1;
3900             }
3901         }
3902         # , add the rest from -sections.txt
3903         foreach $symbol (keys (%KnownSymbols)) {
3904             if ($KnownSymbols{$symbol} == 1) {
3905                 $tmp{$symbol}=1;
3906             }
3907         }
3908         # and add whats found in the source
3909         foreach $symbol (keys (%SourceSymbolDocs)) {
3910             $tmp{$symbol}=1;
3911         }
3912         @Symbols = keys (%tmp);
3913         #print "num source entries: ".(scalar @Symbols)."\n";
3914     }
3915     foreach $symbol (@Symbols) {
3916         $AllSymbols{$symbol} = 1;
3918         my $have_tmpl_docs = 0;
3920         ## See if the symbol is documented in template
3921         my $tmpl_doc = defined ($SymbolDocs{$symbol}) ? $SymbolDocs{$symbol} : "";
3922         my $check_tmpl_doc =$tmpl_doc;
3923         # remove all xml-tags and whitespaces
3924         #$check_tmpl_doc =~ s/<\/?[a-z]+>//g;
3925         $check_tmpl_doc =~ s/<.*?>//g;
3926         $check_tmpl_doc =~ s/\s//g;
3927         # anything left ?
3928         if ($check_tmpl_doc ne "") {
3929             $have_tmpl_docs = 1;
3930             #print "## [$check_tmpl_doc]\n";
3931         }
3933         if (exists ($SourceSymbolDocs{$symbol})) {
3934             my $type = $DeclarationTypes {$symbol};
3936             #print "merging [$symbol] from source\n";
3938             my $item = "Parameter";
3939             if (defined ($type)) {
3940                 if ($type eq 'STRUCT') {
3941                     $item = "Field";
3942                 } elsif ($type eq 'ENUM') {
3943                     $item = "Value";
3944                 } elsif ($type eq 'UNION') {
3945                     $item = "Field";
3946                 }
3947             } else {
3948                 $type="SIGNAL";
3949             }
3951             my $src_doc = $SourceSymbolDocs{$symbol};
3952             # remove leading and training whitespaces
3953             $src_doc =~ s/^\s+//;
3954             $src_doc =~ s/\s+$//;
3956             # Don't output warnings for overridden titles as titles are
3957             # automatically generated in the -sections.txt file, and thus they
3958             # are often overridden.
3959             if ($have_tmpl_docs && $symbol !~ m/:Title$/) {
3960                 # check if content is different
3961                 if ($tmpl_doc ne $src_doc) {
3962                     #print "[$tmpl_doc] [$src_doc]\n";
3963                     &LogWarning ($SourceSymbolSourceFile{$symbol}, $SourceSymbolSourceLine{$symbol},
3964                         "Documentation in template ".$SymbolSourceFile{$symbol}.":".$SymbolSourceLine{$symbol}." for $symbol being overridden by inline comments.");
3965                 }
3966             }
3968             if ($src_doc ne "") {
3969                  $AllDocumentedSymbols{$symbol} = 1;
3970             }
3972             # Convert <!--PARAMETERS--> with any blank lines around it to
3973             # a </para> followed by <!--PARAMETERS--> followed by <para>.
3974             $src_doc =~ s%\n+\s*<!--PARAMETERS-->\s*\n+%\n</para>\n<!--PARAMETERS-->\n<para>\n%g;
3976             # If there is a blank line, finish the paragraph and start another.
3977             $src_doc = &ConvertBlankLines ($src_doc, $symbol);
3978             # Do not add <para> to nothing, it breaks missing docs checks.
3979             my $src_doc_para = $src_doc ? "<para>\n$src_doc</para>\n" : "";
3981             if ($symbol =~ m/$TMPL_DIR\/.+:Long_Description/) {
3982                 $SymbolDocs{$symbol} = "$src_doc_para$tmpl_doc";
3983             } elsif ($symbol =~ m/$TMPL_DIR\/.+:.+/) {
3984                 # For the title/summary/see also section docs we don't want to
3985                 # add any <para> tags.
3986                 $SymbolDocs{$symbol} = "$src_doc"
3987             } else {
3988                 $SymbolDocs{$symbol} = "$src_doc_para$tmpl_doc";
3989             }
3991             # merge parameters
3992             if ($symbol =~ m/.*::.*/) {
3993                 # For signals we prefer the param names from the source docs,
3994                 # since the ones from the templates are likely to contain the
3995                 # artificial argn names which are generated by gtkdoc-scangobj.
3996                 $SymbolParams{$symbol} = $SourceSymbolParams{$symbol};
3997                 # FIXME: we need to check for empty docs here as well!
3998             } else {
3999                 # The templates contain the definitive parameter names and order,
4000                 # so we will not change that. We only override the actual text.
4001                 my $tmpl_params = $SymbolParams{$symbol};
4002                 if (!defined ($tmpl_params)) {
4003                     #print "No merge needed for $symbol\n";
4004                     $SymbolParams{$symbol} = $SourceSymbolParams{$symbol};
4005                     #  FIXME: we still like to get the number of params and merge
4006                     #  1) we would noticed that params have been removed/renamed
4007                     #  2) we would catch undocumented params
4008                     #  params are not (yet) exported in -decl.txt so that we
4009                     #  could easily grab them :/
4010                 } else {
4011                     my $params = $SourceSymbolParams{$symbol};
4012                     my $j;
4013                     #print "Merge needed for $symbol, tmpl_params: ",$#$tmpl_params,", source_params: ",$#$params," \n";
4014                     for ($j = 0; $j <= $#$tmpl_params; $j += $PARAM_FIELD_COUNT) {
4015                         my $tmpl_param_name = $$tmpl_params[$j];
4017                         # Allow '...' as the Varargs parameter.
4018                         if ($tmpl_param_name eq "...") {
4019                             $tmpl_param_name = "Varargs";
4020                         }
4022                         # Try to find the param in the source comment documentation.
4023                         my $found = 0;
4024                         my $k;
4025                         for ($k = 0; $k <= $#$params; $k += $PARAM_FIELD_COUNT) {
4026                             my $param_name = $$params[$k];
4027                             my $param_desc = $$params[$k + 1];
4029                             # We accept changes in case, since the Gnome source docs
4030                             # contain a lot of these.
4031                             if ("\L$param_name" eq "\L$tmpl_param_name") {
4032                                 $found = 1;
4034                                 # Override the description.
4035                                 $$tmpl_params[$j + 1] = $param_desc;
4037                                 # Set the name to "" to mark it as used.
4038                                 $$params[$k] = "";
4039                                 last;
4040                             }
4041                         }
4043                         # If it looks like the parameters are there, but not
4044                         # in the right place, try to explain a bit better.
4045                         if ((!$found) && ($src_doc =~ m/\@$tmpl_param_name:/)) {
4046                             &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4047                                 "Parameters for $symbol must start on the line immediately after the function or macro name.");
4048                         }
4049                     }
4051                     # Now we output a warning if parameters have been described which
4052                     # do not exist.
4053                     for ($j = 0; $j <= $#$params; $j += $PARAM_FIELD_COUNT) {
4054                         my $param_name = $$params[$j];
4055                         if ($param_name) {
4056                             # the template builder cannot detect if a macro returns
4057                             # a result or not
4058                             if(($type eq "MACRO") && ($param_name eq "Returns")) {
4059                                 next;
4060                             }
4061                             &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4062                                 "$item described in source code comment block but does not exist. $type: $symbol $item: $param_name.");
4063                         }
4064                     }
4065                 }
4066             }
4067         } else {
4068             if ($have_tmpl_docs) {
4069                 $AllDocumentedSymbols{$symbol} = 1;
4070                 #print "merging [$symbol] from template\n";
4071             }
4072             else {
4073                 #print "[$symbol] undocumented\n";
4074             }
4075         }
4077         # if this symbol is documented, check if docs are complete
4078         $check_tmpl_doc = defined ($SymbolDocs{$symbol}) ? $SymbolDocs{$symbol} : "";
4079         # remove all xml-tags and whitespaces
4080         #$check_tmpl_doc =~ s/<\/?[a-z]+>//g;
4081         $check_tmpl_doc =~ s/<.*?>//g;
4082         $check_tmpl_doc =~ s/\s//g;
4083         if ($check_tmpl_doc ne "") {
4084             my $tmpl_params = $SymbolParams{$symbol};
4085             if (defined ($tmpl_params)) {
4086                 my $type = $DeclarationTypes {$symbol};
4088                 my $item = "Parameter";
4089                 if (defined ($type)) {
4090                     if ($type eq 'STRUCT') {
4091                         $item = "Field";
4092                     } elsif ($type eq 'ENUM') {
4093                         $item = "Value";
4094                     } elsif ($type eq 'UNION') {
4095                         $item = "Field";
4096                     }
4097                 } else {
4098                     $type="SIGNAL";
4099                 }
4101                 #print "Check param docs for $symbol, tmpl_params: ",$#$tmpl_params," entries, type=$type\n";
4103                 if ($#$tmpl_params > 0) {
4104                     my $j;
4105                     for ($j = 0; $j <= $#$tmpl_params; $j += $PARAM_FIELD_COUNT) {
4106                         # Output a warning if the parameter is empty and
4107                         # remember for stats.
4108                         my $tmpl_param_name = $$tmpl_params[$j];
4109                         my $tmpl_param_desc = $$tmpl_params[$j + 1];
4110                         if ($tmpl_param_desc !~ m/\S/) {
4111                             if (exists ($AllIncompleteSymbols{$symbol})) {
4112                                 $AllIncompleteSymbols{$symbol}.=", ".$tmpl_param_name;
4113                             } else {
4114                                 $AllIncompleteSymbols{$symbol}=$tmpl_param_name;
4115                             }
4116                             &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4117                                 "$item description for $symbol"."::"."$tmpl_param_name is missing in source code comment block.");
4118                         }
4119                     }
4120                 }
4121                 else {
4122                     if ($#$tmpl_params == 0) {
4123                         $AllIncompleteSymbols{$symbol}="<items>";
4124                         &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4125                             "$item descriptions for $symbol are missing in source code comment block.");
4126                     }
4127                     # $#$tmpl_params==-1 means we don't know about parameters
4128                     # this unfortunately does not tell if there should be some
4129                 }
4130             }
4131         }
4132    }
4133    #print "num doc entries: ".(scalar %SymbolDocs)."\n";
4136 sub IsEmptyDoc {
4137     my ($doc) = @_;
4139     if ($doc =~ /^\s*<para>\s*(FIXME)?\s*<\/para>\s*$/) {
4140         return 1;
4141     } else {
4142         return 0;
4143     }
4147 # This converts blank lines to "</para><para>", but only outside CDATA and
4148 # <programlisting> tags.
4149 sub ConvertBlankLines {
4150     return &ModifyXMLElements ($_[0], $_[1],
4151                                "<!\\[CDATA\\[|<programlisting[^>]*>|\\|\\[",
4152                                \&ConvertBlankLinesEndTag,
4153                                \&ConvertBlankLinesCallback);
4157 sub ConvertBlankLinesEndTag {
4158   if ($_[0] eq "<!\[CDATA\[") {
4159     return "]]>";
4160   } elsif ($_[0] eq "|[") {
4161     return "]\\|";
4162   } else {
4163     return "</programlisting>";
4164   }
4167 sub ConvertBlankLinesCallback {
4168   my ($text, $symbol, $tag) = @_;
4170   # If we're not in CDATA or a <programlisting> we convert blank lines so
4171   # they start a new <para>.
4172   if ($tag eq "") {
4173     $text =~ s%\n{2,}%\n</para>\n<para>\n%g;
4174   }
4176   return $text;
4180 #############################################################################
4181 # LIBRARY FUNCTIONS -   These functions are used in both gtkdoc-mkdb and
4182 #                       gtkdoc-mktmpl and should eventually be moved to a
4183 #                       separate library.
4184 #############################################################################
4186 #############################################################################
4187 # Function    : ReadDeclarationsFile
4188 # Description : This reads in a file containing the function/macro/enum etc.
4189 #               declarations.
4191 #               Note that in some cases there are several declarations with
4192 #               the same name, e.g. for conditional macros. In this case we
4193 #               set a flag in the %DeclarationConditional hash so the
4194 #               declaration is not shown in the docs.
4196 #               If a macro and a function have the same name, e.g. for
4197 #               gtk_object_ref, the function declaration takes precedence.
4199 #               Some opaque structs are just declared with 'typedef struct
4200 #               _name name;' in which case the declaration may be empty.
4201 #               The structure may have been found later in the header, so
4202 #               that overrides the empty declaration.
4204 # Arguments   : $file - the declarations file to read
4205 #               $override - if declarations in this file should override
4206 #                       any current declaration.
4207 #############################################################################
4209 sub ReadDeclarationsFile {
4210     my ($file, $override) = @_;
4212     if ($override == 0) {
4213         %Declarations = ();
4214         %DeclarationTypes = ();
4215         %DeclarationConditional = ();
4216         %DeclarationOutput = ();
4217     }
4219     open (INPUT, $file)
4220         || die "Can't open $file: $!";
4221     my $declaration_type = "";
4222     my $declaration_name;
4223     my $declaration;
4224     my $is_deprecated = 0;
4225     while (<INPUT>) {
4226         if (!$declaration_type) {
4227             if (m/^<([^>]+)>/) {
4228                 $declaration_type = $1;
4229                 $declaration_name = "";
4230                 #print "Found declaration: $declaration_type\n";
4231                 $declaration = "";
4232             }
4233         } else {
4234             if (m%^<NAME>(.*)</NAME>%) {
4235                 $declaration_name = $1;
4236             } elsif (m%^<DEPRECATED/>%) {
4237                 $is_deprecated = 1;
4238             } elsif (m%^</$declaration_type>%) {
4239                 #print "Found end of declaration: $declaration_name\n";
4240                 # Check that the declaration has a name
4241                 if ($declaration_name eq "") {
4242                     print "ERROR: $declaration_type has no name $file:$.\n";
4243                 }
4245                 # If the declaration is an empty typedef struct _XXX XXX
4246                 # set the flag to indicate the struct has a typedef.
4247                 if ($declaration_type eq 'STRUCT'
4248                     && $declaration =~ m/^\s*$/) {
4249                     #print "Struct has typedef: $declaration_name\n";
4250                     $StructHasTypedef{$declaration_name} = 1;
4251                 }
4253                 # Check if the symbol is already defined.
4254                 if (defined ($Declarations{$declaration_name})
4255                     && $override == 0) {
4256                     # Function declarations take precedence.
4257                     if ($DeclarationTypes{$declaration_name} eq 'FUNCTION') {
4258                         # Ignore it.
4259                     } elsif ($declaration_type eq 'FUNCTION') {
4260                         if ($is_deprecated) {
4261                             $Deprecated{$declaration_name} = "";
4262                         }
4263                         $Declarations{$declaration_name} = $declaration;
4264                         $DeclarationTypes{$declaration_name} = $declaration_type;
4265                     } elsif ($DeclarationTypes{$declaration_name}
4266                               eq $declaration_type) {
4267                         # If the existing declaration is empty, or is just a
4268                         # forward declaration of a struct, override it.
4269                         if ($declaration_type eq 'STRUCT') {
4270                             if ($Declarations{$declaration_name} =~ m/^\s*(struct\s+\w+\s*;)?\s*$/) {
4271                                 if ($is_deprecated) {
4272                                     $Deprecated{$declaration_name} = "";
4273                                 }
4274                                 $Declarations{$declaration_name} = $declaration;
4275                             } elsif ($declaration =~ m/^\s*(struct\s+\w+\s*;)?\s*$/) {
4276                                 # Ignore an empty or forward declaration.
4277                             } else {
4278                                 &LogWarning ($file, $., "Structure $declaration_name has multiple definitions.");
4279                             }
4280                         } else {
4281                             # set flag in %DeclarationConditional hash for
4282                             # multiply defined macros/typedefs.
4283                             $DeclarationConditional{$declaration_name} = 1;
4284                         }
4285                     } else {
4286                         &LogWarning ($file, $., "$declaration_name has multiple definitions.");
4287                     }
4288                 } else {
4289                     if ($is_deprecated) {
4290                         $Deprecated{$declaration_name} = "";
4291                     }
4292                     $Declarations{$declaration_name} = $declaration;
4293                     $DeclarationTypes{$declaration_name} = $declaration_type;
4294                 }
4296                 $declaration_type = "";
4297                 $is_deprecated = 0;
4298             } else {
4299                 $declaration .= $_;
4300             }
4301         }
4302     }
4303     close (INPUT);
4307 #############################################################################
4308 # Function    : ReadSignalsFile
4309 # Description : This reads in an existing file which contains information on
4310 #               all GTK signals. It creates the arrays @SignalNames and
4311 #               @SignalPrototypes containing info on the signals. The first
4312 #               line of the SignalPrototype is the return type of the signal
4313 #               handler. The remaining lines are the parameters passed to it.
4314 #               The last parameter, "gpointer user_data" is always the same
4315 #               so is not included.
4316 # Arguments   : $file - the file containing the signal handler prototype
4317 #                       information.
4318 #############################################################################
4320 sub ReadSignalsFile {
4321     my ($file) = @_;
4323     my $in_signal = 0;
4324     my $signal_object;
4325     my $signal_name;
4326     my $signal_returns;
4327     my $signal_flags;
4328     my $signal_prototype;
4330     # Reset the signal info.
4331     @SignalObjects = ();
4332     @SignalNames = ();
4333     @SignalReturns = ();
4334     @SignalFlags = ();
4335     @SignalPrototypes = ();
4337     if (! -f $file) {
4338         return;
4339     }
4340     if (!open (INPUT, $file)) {
4341         warn "Can't open $file - skipping signals\n";
4342         return;
4343     }
4344     while (<INPUT>) {
4345         if (!$in_signal) {
4346             if (m/^<SIGNAL>/) {
4347                 $in_signal = 1;
4348                 $signal_object = "";
4349                 $signal_name = "";
4350                 $signal_returns = "";
4351                 $signal_prototype = "";
4352             }
4353         } else {
4354             if (m/^<NAME>(.*)<\/NAME>/) {
4355                 $signal_name = $1;
4356                 if ($signal_name =~ m/^(.*)::(.*)$/) {
4357                     $signal_object = $1;
4358                     ($signal_name = $2) =~ s/_/-/g;
4359                     #print "Found signal: $signal_name\n";
4360                 } else {
4361                     &LogWarning ($file, $., "Invalid signal name: $signal_name.");
4362                 }
4363             } elsif (m/^<RETURNS>(.*)<\/RETURNS>/) {
4364                 $signal_returns = $1;
4365             } elsif (m/^<FLAGS>(.*)<\/FLAGS>/) {
4366                 $signal_flags = $1;
4367             } elsif (m%^</SIGNAL>%) {
4368                 #print "Found end of signal: ${signal_object}::${signal_name}\nReturns: ${signal_returns}\n${signal_prototype}";
4369                 push (@SignalObjects, $signal_object);
4370                 push (@SignalNames, $signal_name);
4371                 push (@SignalReturns, $signal_returns);
4372                 push (@SignalFlags, $signal_flags);
4373                 push (@SignalPrototypes, $signal_prototype);
4374                 $in_signal = 0;
4375             } else {
4376                 $signal_prototype .= $_;
4377             }
4378         }
4379     }
4380     close (INPUT);
4384 #############################################################################
4385 # Function    : ReadTemplateFile
4386 # Description : This reads in the manually-edited documentation file
4387 #               corresponding to the file currently being created, so we can
4388 #               insert the documentation at the appropriate places.
4389 #               It outputs %SymbolTypes, %SymbolDocs and %SymbolParams, which
4390 #               is a hash of arrays.
4391 #               NOTE: This function is duplicated in gtkdoc-mktmpl (but
4392 #               slightly different).
4393 # Arguments   : $docsfile - the template file to read in.
4394 #               $skip_unused_params - 1 if the unused parameters should be
4395 #                       skipped.
4396 #############################################################################
4398 sub ReadTemplateFile {
4399     my ($docsfile, $skip_unused_params) = @_;
4401     my $template = "$docsfile.sgml";
4402     if (! -f $template) {
4403         #print "File doesn't exist: $template\n";
4404         return 0;
4405     }
4406     #print "Reading $template\n";
4408     # start with empty hashes, we merge the source comment for each file
4409     # afterwards
4410     %SymbolDocs = ();
4411     %SymbolTypes = ();
4412     %SymbolParams = ();
4414     my $current_type = "";      # Type of symbol being read.
4415     my $current_symbol = "";    # Name of symbol being read.
4416     my $symbol_doc = "";                # Description of symbol being read.
4417     my @params;                 # Parameter names and descriptions of current
4418                                 #   function/macro/function typedef.
4419     my $current_param = -1;     # Index of parameter currently being read.
4420                                 #   Note that the param array contains pairs
4421                                 #   of param name & description.
4422     my $in_unused_params = 0;   # True if we are reading in the unused params.
4423     my $in_deprecated = 0;
4424     my $in_since = 0;
4425     my $in_stability = 0;
4427     open (DOCS, "$template")
4428         || die "Can't open $template: $!";
4429     while (<DOCS>) {
4430         if (m/^<!-- ##### ([A-Z_]+) (\S+) ##### -->/) {
4431             my $type = $1;
4432             my $symbol = $2;
4433             if ($symbol eq "Title"
4434                 || $symbol eq "Short_Description"
4435                 || $symbol eq "Long_Description"
4436                 || $symbol eq "See_Also"
4437                 || $symbol eq "Stability_Level"
4438                 || $symbol eq "Include") {
4440                 $symbol = $docsfile . ":" . $symbol;
4441             }
4443             #print "Found symbol: $symbol\n";
4444             # Remember file and line for the symbol
4445             $SymbolSourceFile{$symbol} = $template;
4446             $SymbolSourceLine{$symbol} = $.;
4448             # Store previous symbol, but remove any trailing blank lines.
4449             if ($current_symbol ne "") {
4450                 $symbol_doc =~ s/\s+$//;
4451                 $SymbolTypes{$current_symbol} = $current_type;
4452                 $SymbolDocs{$current_symbol} = $symbol_doc;
4454                 # Check that the stability level is valid.
4455                 if ($StabilityLevel{$current_symbol}) {
4456                     $StabilityLevel{$current_symbol} = &ParseStabilityLevel($StabilityLevel{$current_symbol}, $template, $., "Stability level for $current_symbol");
4457                 }
4459                 if ($current_param >= 0) {
4460                     $SymbolParams{$current_symbol} = [ @params ];
4461                 } else {
4462                     # Delete any existing params in case we are overriding a
4463                     # previously read template.
4464                     delete $SymbolParams{$current_symbol};
4465                 }
4466             }
4467             $current_type = $type;
4468             $current_symbol = $symbol;
4469             $current_param = -1;
4470             $in_unused_params = 0;
4471             $in_deprecated = 0;
4472             $in_since = 0;
4473             $in_stability = 0;
4474             $symbol_doc = "";
4475             @params = ();
4477         } elsif (m/^<!-- # Unused Parameters # -->/) {
4478             #print "DEBUG: Found unused parameters\n";
4479             $in_unused_params = 1;
4480             next;
4482         } elsif ($in_unused_params && $skip_unused_params) {
4483             # When outputting the DocBook we skip unused parameters.
4484             #print "DEBUG: Skipping unused param: $_";
4485             next;
4487         } else {
4488             # Check if param found. Need to handle "..." and "format...".
4489             if (s/^\@([\w\.]+):\040?//) {
4490                 my $param_name = $1;
4491                 my $param_desc = $_;
4492                 # Allow variations of 'Returns'
4493                 if ($param_name =~ m/^[Rr]eturns?$/) {
4494                     $param_name = "Returns";
4495                 }
4497                 # strip trailing whitespaces and blank lines
4498                 s/\s+\n$/\n/m;
4499                 s/\n+$/\n/sm;
4500                 #print "Found param for symbol $current_symbol : '$param_name'= '$_'";
4502                 if ($param_name eq "Deprecated") {
4503                     $in_deprecated = 1;
4504                     $Deprecated{$current_symbol} = $_;
4505                 } elsif ($param_name eq "Since") {
4506                     $in_since = 1;
4507                     chomp;
4508                     $Since{$current_symbol} = $_;
4509                 } elsif ($param_name eq "Stability") {
4510                     $in_stability = 1;
4511                     $StabilityLevel{$current_symbol} = $_;
4512                 } else {
4513                     push (@params, $param_name);
4514                     push (@params, $param_desc);
4515                     $current_param += $PARAM_FIELD_COUNT;
4516                 }
4517             } else {
4518                 # strip trailing whitespaces and blank lines
4519                 s/\s+\n$/\n/m;
4520                 s/\n+$/\n/sm;
4521                 
4522                 if (!m/^\s+$/) {
4523                     if ($in_deprecated) {
4524                         $Deprecated{$current_symbol} .= $_;
4525                     } elsif ($in_since) {
4526                         &LogWarning ($template, $., "multi-line since docs found");
4527                         #$Since{$current_symbol} .= $_;
4528                     } elsif ($in_stability) {
4529                         $StabilityLevel{$current_symbol} .= $_;
4530                     } elsif ($current_param >= 0) {
4531                         $params[$current_param] .= $_;
4532                     } else {
4533                         $symbol_doc .= $_;
4534                     }
4535                 }
4536             }
4537         }
4538     }
4540     # Remember to finish the current symbol doccs.
4541     if ($current_symbol ne "") {
4543         $symbol_doc =~ s/\s+$//;
4544         $SymbolTypes{$current_symbol} = $current_type;
4545         $SymbolDocs{$current_symbol} = $symbol_doc;
4547         # Check that the stability level is valid.
4548         if ($StabilityLevel{$current_symbol}) {
4549             $StabilityLevel{$current_symbol} = &ParseStabilityLevel($StabilityLevel{$current_symbol}, $template, $., "Stability level for $current_symbol");
4550         }
4552         if ($current_param >= 0) {
4553             $SymbolParams{$current_symbol} = [ @params ];
4554         } else {
4555             # Delete any existing params in case we are overriding a
4556             # previously read template.
4557             delete $SymbolParams{$current_symbol};
4558         }
4559     }
4561     close (DOCS);
4562     return 1;
4566 #############################################################################
4567 # Function    : ReadObjectHierarchy
4568 # Description : This reads in the $MODULE-hierarchy.txt file containing all
4569 #               the GtkObject subclasses described in this module (and their
4570 #               ancestors).
4571 #               It places them in the @Objects array, and places their level
4572 #               in the widget hierarchy in the @ObjectLevels array, at the
4573 #               same index. GtkObject, the root object, has a level of 1.
4575 #               FIXME: the version in gtkdoc-mkdb also generates tree_index.sgml
4576 #               as it goes along, this should be split out into a separate
4577 #               function.
4579 # Arguments   : none
4580 #############################################################################
4582 sub ReadObjectHierarchy {
4583     @Objects = ();
4584     @ObjectLevels = ();
4586     if (! -f $OBJECT_TREE_FILE) {
4587         return;
4588     }
4589     if (!open (INPUT, $OBJECT_TREE_FILE)) {
4590         warn "Can't open $OBJECT_TREE_FILE - skipping object tree\n";
4591         return;
4592     }
4594     # FIXME: use $OUTPUT_FORMAT
4595     # my $old_tree_index = "$SGML_OUTPUT_DIR/tree_index.$OUTPUT_FORMAT";
4596     my $old_tree_index = "$SGML_OUTPUT_DIR/tree_index.sgml";
4597     my $new_tree_index = "$SGML_OUTPUT_DIR/tree_index.new";
4599     open (OUTPUT, ">$new_tree_index")
4600         || die "Can't create $new_tree_index: $!";
4602     if (lc($OUTPUT_FORMAT) eq "xml") {
4603         my $tree_header = $doctype_header;
4605         $tree_header =~ s/<!DOCTYPE \w+/<!DOCTYPE screen/;
4606         print (OUTPUT "$tree_header");
4607     }
4608     print (OUTPUT "<screen>\n");
4610     # Only emit objects if they are supposed to be documented, or if
4611     # they have documented children. To implement this, we maintain a
4612     # stack of pending objects which will be emitted if a documented
4613     # child turns up.
4614     my @pending_objects = ();
4615     my @pending_levels = ();
4616     while (<INPUT>) {
4617         if (m/\S+/) {
4618             my $object = $&;
4619             my $level = (length($`)) / 2 + 1;
4620             my $xref = "";
4622             while (($#pending_levels >= 0) && ($pending_levels[$#pending_levels] >= $level)) {
4623                 my $pobject = pop(@pending_objects);
4624                 my $plevel = pop(@pending_levels);
4625             }
4627             push (@pending_objects, $object);
4628             push (@pending_levels, $level);
4630             if (exists($KnownSymbols{$object}) && $KnownSymbols{$object} == 1) {
4631                 while ($#pending_levels >= 0) {
4632                     $object = shift @pending_objects;
4633                     $level = shift @pending_levels;
4634                     $xref = &MakeXRef ($object);
4636                     print (OUTPUT ' ' x ($level * 4), "$xref\n");
4637                     push (@Objects, $object);
4638                     push (@ObjectLevels, $level);
4639                 }
4640             }
4641         }
4642     }
4643     print (OUTPUT "</screen>\n");
4645     close (INPUT);
4646     close (OUTPUT);
4648     &UpdateFileIfChanged ($old_tree_index, $new_tree_index, 0);
4650     &OutputObjectList;
4653 #############################################################################
4654 # Function    : ReadInterfaces
4655 # Description : This reads in the $MODULE.interfaces file.
4657 # Arguments   : none
4658 #############################################################################
4660 sub ReadInterfaces {
4661     %Interfaces = ();
4663     if (! -f $INTERFACES_FILE) {
4664         return;
4665     }
4666     if (!open (INPUT, $INTERFACES_FILE)) {
4667         warn "Can't open $INTERFACES_FILE - skipping interfaces\n";
4668         return;
4669     }
4671     while (<INPUT>) {
4672        chomp;
4673        my ($object, @ifaces) = split;
4674        if (exists($KnownSymbols{$object}) && $KnownSymbols{$object} == 1) {
4675            my @knownIfaces = ();
4677            # filter out private interfaces, but leave foreign interfaces
4678            foreach my $iface (@ifaces) {
4679                if (!exists($KnownSymbols{$iface}) || $KnownSymbols{$iface} == 1) {
4680                    push (@knownIfaces, $iface);
4681                }
4682              }
4684            $Interfaces{$object} = join(' ', @knownIfaces);
4685        }
4686     }
4687     close (INPUT);
4690 #############################################################################
4691 # Function    : ReadPrerequisites
4692 # Description : This reads in the $MODULE.prerequisites file.
4694 # Arguments   : none
4695 #############################################################################
4697 sub ReadPrerequisites {
4698     %Prerequisites = ();
4700     if (! -f $PREREQUISITES_FILE) {
4701         return;
4702     }
4703     if (!open (INPUT, $PREREQUISITES_FILE)) {
4704         warn "Can't open $PREREQUISITES_FILE - skipping prerequisites\n";
4705         return;
4706     }
4708     while (<INPUT>) {
4709        chomp;
4710        my ($iface, @prereqs) = split;
4711        if (exists($KnownSymbols{$iface}) && $KnownSymbols{$iface} == 1) {
4712            my @knownPrereqs = ();
4714            # filter out private prerequisites, but leave foreign prerequisites
4715            foreach my $prereq (@prereqs) {
4716                if (!exists($KnownSymbols{$prereq}) || $KnownSymbols{$prereq} == 1) {
4717                   push (@knownPrereqs, $prereq);
4718                }
4719            }
4721            $Prerequisites{$iface} = join(' ', @knownPrereqs);
4722        }
4723     }
4724     close (INPUT);
4727 #############################################################################
4728 # Function    : ReadArgsFile
4729 # Description : This reads in an existing file which contains information on
4730 #               all GTK args. It creates the arrays @ArgObjects, @ArgNames,
4731 #               @ArgTypes, @ArgFlags, @ArgNicks and @ArgBlurbs containing info
4732 #               on the args.
4733 # Arguments   : $file - the file containing the arg information.
4734 #############################################################################
4736 sub ReadArgsFile {
4737     my ($file) = @_;
4739     my $in_arg = 0;
4740     my $arg_object;
4741     my $arg_name;
4742     my $arg_type;
4743     my $arg_flags;
4744     my $arg_nick;
4745     my $arg_blurb;
4746     my $arg_default;
4747     my $arg_range;
4749     # Reset the args info.
4750     @ArgObjects = ();
4751     @ArgNames = ();
4752     @ArgTypes = ();
4753     @ArgFlags = ();
4754     @ArgNicks = ();
4755     @ArgBlurbs = ();
4756     @ArgDefaults = ();
4757     @ArgRanges = ();
4759     if (! -f $file) {
4760         return;
4761     }
4762     if (!open (INPUT, $file)) {
4763         warn "Can't open $file - skipping args\n";
4764         return;
4765     }
4766     while (<INPUT>) {
4767         if (!$in_arg) {
4768             if (m/^<ARG>/) {
4769                 $in_arg = 1;
4770                 $arg_object = "";
4771                 $arg_name = "";
4772                 $arg_type = "";
4773                 $arg_flags = "";
4774                 $arg_nick = "";
4775                 $arg_blurb = "";
4776                 $arg_default = "";
4777                 $arg_range = "";
4778             }
4779         } else {
4780             if (m/^<NAME>(.*)<\/NAME>/) {
4781                 $arg_name = $1;
4782                 if ($arg_name =~ m/^(.*)::(.*)$/) {
4783                     $arg_object = $1;
4784                     ($arg_name = $2) =~ s/_/-/g;
4785                     #print "Found arg: $arg_name\n";
4786                 } else {
4787                     &LogWarning ($file, $., "Invalid argument name: $arg_name");
4788                 }
4789             } elsif (m/^<TYPE>(.*)<\/TYPE>/) {
4790                 $arg_type = $1;
4791             } elsif (m/^<RANGE>(.*)<\/RANGE>/) {
4792                 $arg_range = $1;
4793             } elsif (m/^<FLAGS>(.*)<\/FLAGS>/) {
4794                 $arg_flags = $1;
4795             } elsif (m/^<NICK>(.*)<\/NICK>/) {
4796                 $arg_nick = $1;
4797             } elsif (m/^<BLURB>(.*)<\/BLURB>/) {
4798                 $arg_blurb = $1;
4799                 if ($arg_blurb eq "(null)") {
4800                   $arg_blurb = "";
4801                   &LogWarning ($file, $., "Property ${arg_object}:${arg_name} has no documentation.");
4802                 }
4803             } elsif (m/^<DEFAULT>(.*)<\/DEFAULT>/) {
4804                 $arg_default = $1;
4805             } elsif (m%^</ARG>%) {
4806                 #print "Found end of arg: ${arg_object}::${arg_name}\n${arg_type} : ${arg_flags}\n";
4807                 push (@ArgObjects, $arg_object);
4808                 push (@ArgNames, $arg_name);
4809                 push (@ArgTypes, $arg_type);
4810                 push (@ArgRanges, $arg_range);
4811                 push (@ArgFlags, $arg_flags);
4812                 push (@ArgNicks, $arg_nick);
4813                 push (@ArgBlurbs, $arg_blurb);
4814                 push (@ArgDefaults, $arg_default);
4815                 $in_arg = 0;
4816             }
4817         }
4818     }
4819     close (INPUT);
4823 #############################################################################
4824 # Function    : CheckIsObject
4825 # Description : Returns 1 if the given name is a GtkObject or a subclass.
4826 #               It uses the global @Objects array.
4827 #               Note that the @Objects array only contains classes in the
4828 #               current module and their ancestors - not all GTK classes.
4829 # Arguments   : $name - the name to check.
4830 #############################################################################
4832 sub CheckIsObject {
4833     my ($name) = @_;
4835     my $object;
4836     foreach $object (@Objects) {
4837         if ($object eq $name) {
4838             return 1;
4839         }
4840     }
4841     return 0;
4845 #############################################################################
4846 # Function    : MakeReturnField
4847 # Description : Pads a string to $RETURN_TYPE_FIELD_WIDTH.
4848 # Arguments   : $str - the string to pad.
4849 #############################################################################
4851 sub MakeReturnField {
4852     my ($str) = @_;
4854     return $str . (' ' x ($RETURN_TYPE_FIELD_WIDTH - length ($str)));
4857 #############################################################################
4858 # Function    : GetSymbolSourceFile
4859 # Description : Get the filename where the symbol docs where taken from.
4860 # Arguments   : $symbol - the symbol name
4861 #############################################################################
4863 sub GetSymbolSourceFile {
4864     my ($symbol) = @_;
4866     if (defined($SourceSymbolSourceFile{$symbol})) {
4867         return $SourceSymbolSourceFile{$symbol};
4868     } elsif (defined($SymbolSourceFile{$symbol})) {
4869         return $SymbolSourceFile{$symbol};
4870     } else {
4871         return "";
4872     }
4875 #############################################################################
4876 # Function    : GetSymbolSourceLine
4877 # Description : Get the file line where the symbol docs where taken from.
4878 # Arguments   : $symbol - the symbol name
4879 #############################################################################
4881 sub GetSymbolSourceLine {
4882     my ($symbol) = @_;
4884     if (defined($SourceSymbolSourceLine{$symbol})) {
4885         return $SourceSymbolSourceLine{$symbol};
4886     } elsif (defined($SymbolSourceLine{$symbol})) {
4887         return $SymbolSourceLine{$symbol};
4888     } else {
4889         return 0;
4890     }