Handle file or line being undefined. Warn about repeated symbols in
[gtk-doc.git] / gtkdoc-mkdb.in
blob1f10d31dacb008e333bff9eec5a520bfce25c058
1 #!@PERL@ -w
2 # -*- cperl -*-
4 # gtk-doc - GTK DocBook documentation generator.
5 # Copyright (C) 1998  Damon Chaplin
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 2 of the License, or
10 # (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 #############################################################################
23 # Script      : gtkdoc-mkdb
24 # Description : This creates the DocBook files from the edited templates.
26 #               NOTE: When creating SGML IDS, we append ":CAPS" to all
27 #               all-caps identifiers to prevent name clashes. (It basically
28 #               never is the case that mixed-case identifiers would collide.)
29 #               See the CreateValidSGMLID function.
30 #############################################################################
32 use strict;
33 use Getopt::Long;
35 unshift @INC, '@PACKAGE_DATA_DIR@';
36 require "gtkdoc-common.pl";
38 # Options
40 # name of documentation module
41 my $MODULE;
42 my $TMPL_DIR;
43 my $SGML_OUTPUT_DIR;
44 my @SOURCE_DIRS;
45 my $SOURCE_SUFFIXES = "";
46 my $IGNORE_FILES = "";
47 my $PRINT_VERSION;
48 my $PRINT_HELP;
49 my $OUTPUT_ALL_SYMBOLS;
50 my $MAIN_SGML_FILE;
51 my $EXPAND_CONTENT_FILES = "";
52 my $SGML_MODE;
53 my $DEFAULT_STABILITY;
54 my $DEFAULT_INCLUDES;
55 my $OUTPUT_FORMAT;
57 my %optctl = (module => \$MODULE,
58               'source-dir' => \@SOURCE_DIRS,
59               'source-suffixes' => \$SOURCE_SUFFIXES,
60               'ignore-files' => \$IGNORE_FILES,
61               'output-dir' => \$SGML_OUTPUT_DIR,
62               'tmpl-dir' => \$TMPL_DIR,
63               'version' => \$PRINT_VERSION,
64               'help' => \$PRINT_HELP,
65               'main-sgml-file' => \$MAIN_SGML_FILE,
66               'expand-content-files' => \$EXPAND_CONTENT_FILES,
67               'outputallsymbols' => \$OUTPUT_ALL_SYMBOLS,
68               'sgml-mode' => \$SGML_MODE,
69               'default-stability' => \$DEFAULT_STABILITY,
70               'default-includes' => \$DEFAULT_INCLUDES,
71               'output-format' => \$OUTPUT_FORMAT);
72 GetOptions(\%optctl, "module=s", "source-dir:s", "source-suffixes:s", "ignore-files:s", "output-dir:s", "tmpl-dir:s", "version", "outputallsymbols", "expand-content-files:s", "main-sgml-file:s", "extra-db-files:s", "help", "sgml-mode", "default-stability:s", "default-includes:s", "output-format:s");
74 if ($PRINT_VERSION) {
75     print "@VERSION@\n";
76     exit 0;
79 if (!$MODULE) {
80     $PRINT_HELP = 1;
83 if ($DEFAULT_STABILITY && $DEFAULT_STABILITY ne "Stable"
84     && $DEFAULT_STABILITY ne "Private" && $DEFAULT_STABILITY ne "Unstable") {
85     $PRINT_HELP = 1;
88 if ($PRINT_HELP) {
89     print <<EOF;
90 gtkdoc-mkdb version @VERSION@
92 --module=MODULE_NAME       Name of the doc module being parsed
93 --source-dir=DIRNAME       Directories which contain inline reference material
94 --source-suffixes=SUFFIXES Suffixes of source files to scan, comma-separated
95 --ignore-files=FILES       Files or directories which should not be scanned
96                            May be used more than once for multiple directories
97 --output-dir=DIRNAME       Directory to put the generated DocBook files in
98 --tmpl-dir=DIRNAME         Directory in which template files may be found
99 --main-sgml-file=FILE      File containing the toplevel DocBook file.
100 --expand-content-files=FILES Extra DocBook files to expand abbreviations in.
101 --output-format=FORMAT     Format to use for the generated docbook, XML or SGML.
102 --sgml-mode                Allow DocBook markup in inline documentation.
103 --default-stability=LEVEL  Specify default stability Level. Valid values are
104                            Stable, Unstable, or Private.
105 --default-includes=FILENAMES Specify default includes for section Synopsis
106 --version                  Print the version of this program
107 --help                     Print this help
109     exit 0;
112 my ($empty_element_end, $doctype_header);
114 if (lc($OUTPUT_FORMAT) eq "xml") {
115     if (!$MAIN_SGML_FILE) {
116         # backwards compatibility
117         if (-e "${MODULE}-docs.sgml") {
118             $MAIN_SGML_FILE="${MODULE}-docs.sgml";
119         } else {
120             $MAIN_SGML_FILE="${MODULE}-docs.xml";
121         }
122     }
123     $OUTPUT_FORMAT = "xml";
124     $empty_element_end = "/>";
126     if (-e $MAIN_SGML_FILE) {
127         open(INPUT, "<$MAIN_SGML_FILE") || die "Can't open $MAIN_SGML_FILE";
128         $doctype_header = "";
129         while (<INPUT>) {
130             if (/^\s*<(book|chapter|article)/) {
131                 if ($_ !~ m/http:\/\/www.w3.org\/200[13]\/XInclude/) {
132                     $doctype_header = "";
133                 }
134                 last;
135             }
136             $doctype_header .= $_;
137         }
138         close(INPUT);
139     } else {
140         $doctype_header =
141 "<?xml version=\"1.0\"?>\n" .
142 "<!DOCTYPE book PUBLIC \"-//OASIS//DTD DocBook XML V4.1.2//EN\"\n" .
143 "               \"http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd\">\n";
144     }
145     $doctype_header =~ s/<!DOCTYPE \w+/<!DOCTYPE refentry/;
146 } else {
147     if (!$MAIN_SGML_FILE) {
148         $MAIN_SGML_FILE="${MODULE}-docs.sgml";
149     }
150     $OUTPUT_FORMAT = "sgml";
151     $doctype_header = "";
152     $empty_element_end = ">";
155 my $ROOT_DIR = ".";
157 # All the files are written in subdirectories beneath here.
158 $TMPL_DIR = $TMPL_DIR ? $TMPL_DIR : "$ROOT_DIR/tmpl";
160 # This is where we put all the DocBook output.
161 $SGML_OUTPUT_DIR = $SGML_OUTPUT_DIR ? $SGML_OUTPUT_DIR : "$ROOT_DIR/$OUTPUT_FORMAT";
163 # This file contains the object hierarchy.
164 my $OBJECT_TREE_FILE = "$ROOT_DIR/$MODULE.hierarchy";
166 # This file contains the interfaces.
167 my $INTERFACES_FILE = "$ROOT_DIR/$MODULE.interfaces";
169 # This file contains the prerequisites.
170 my $PREREQUISITES_FILE = "$ROOT_DIR/$MODULE.prerequisites";
172 # This file contains signal arguments and names.
173 my $SIGNALS_FILE = "$ROOT_DIR/$MODULE.signals";
175 # The file containing Arg information.
176 my $ARGS_FILE = "$ROOT_DIR/$MODULE.args";
178 # These global arrays store information on signals. Each signal has an entry
179 # in each of these arrays at the same index, like a multi-dimensional array.
180 my @SignalObjects;      # The GtkObject which emits the signal.
181 my @SignalNames;        # The signal name.
182 my @SignalReturns;      # The return type.
183 my @SignalFlags;        # Flags for the signal
184 my @SignalPrototypes;   # The rest of the prototype of the signal handler.
186 # These global arrays store information on Args. Each Arg has an entry
187 # in each of these arrays at the same index, like a multi-dimensional array.
188 my @ArgObjects;         # The GtkObject which has the Arg.
189 my @ArgNames;           # The Arg name.
190 my @ArgTypes;           # The Arg type - gint, GtkArrowType etc.
191 my @ArgFlags;           # How the Arg can be used - readable/writable etc.
192 my @ArgNicks;           # The nickname of the Arg.
193 my @ArgBlurbs;          # Docstring of the Arg.
194 my @ArgDefaults;        # Default value of the Arg.
195 my @ArgRanges;          # The range of the Arg type
196 # These global hashes store declaration info keyed on a symbol name.
197 my %Declarations;
198 my %DeclarationTypes;
199 my %DeclarationConditional;
200 my %DeclarationOutput;
201 my %Deprecated;
202 my %Since;
203 my %StabilityLevel;
204 my %StructHasTypedef;
206 # These global hashes store the existing documentation.
207 my %SymbolDocs;
208 my %SymbolTypes;
209 my %SymbolParams;
210 my %SymbolSourceFile;
211 my %SymbolSourceLine;
213 # These global hashes store documentation scanned from the source files.
214 my %SourceSymbolDocs;
215 my %SourceSymbolParams;
216 my %SourceSymbolSourceFile;
217 my %SourceSymbolSourceLine;
219 # all documentation goes in here, so we can do coverage analysis
220 my %AllSymbols;
221 my %AllIncompleteSymbols;
222 my %AllDocumentedSymbols;
224 # Undeclared yet documented symbols
225 my %UndeclaredSymbols;
227 # These global arrays store GtkObject and subclasses and the hierarchy.
228 my @Objects;
229 my @ObjectLevels;
231 my %Interfaces;
232 my %Prerequisites;
234 # holds the symbols which are mentioned in $MODULE-sections.txt
235 my %KnownSymbols;
237 # Standard C preprocessor directives, which we ignore for '#' abbreviations.
238 my %PreProcessorDirectives;
239 $PreProcessorDirectives{"assert"} = 1;
240 $PreProcessorDirectives{"define"} = 1;
241 $PreProcessorDirectives{"elif"} = 1;
242 $PreProcessorDirectives{"else"} = 1;
243 $PreProcessorDirectives{"endif"} = 1;
244 $PreProcessorDirectives{"error"} = 1;
245 $PreProcessorDirectives{"if"} = 1;
246 $PreProcessorDirectives{"ifdef"} = 1;
247 $PreProcessorDirectives{"ifndef"} = 1;
248 $PreProcessorDirectives{"include"} = 1;
249 $PreProcessorDirectives{"line"} = 1;
250 $PreProcessorDirectives{"pragma"} = 1;
251 $PreProcessorDirectives{"unassert"} = 1;
252 $PreProcessorDirectives{"undef"} = 1;
253 $PreProcessorDirectives{"warning"} = 1;
255 # Create the root DocBook output directory if it doens't exist.
256 if (! -e $SGML_OUTPUT_DIR) {
257     mkdir ("$SGML_OUTPUT_DIR", 0777)
258         || die "Can't create directory: $SGML_OUTPUT_DIR";
261 # Function and other declaration output settings.
262 my $RETURN_TYPE_FIELD_WIDTH = 20;
263 my $SYMBOL_FIELD_WIDTH = 36;
264 my $SIGNAL_FIELD_WIDTH = 16;
266 &ReadKnownSymbols ("$ROOT_DIR/$MODULE-sections.txt");
267 &ReadSignalsFile ($SIGNALS_FILE);
268 &ReadArgsFile ($ARGS_FILE);
269 &ReadObjectHierarchy;
270 &ReadInterfaces;
271 &ReadPrerequisites;
273 &ReadDeclarationsFile ("$ROOT_DIR/$MODULE-decl.txt", 0);
274 if (-f "$ROOT_DIR/$MODULE-overrides.txt") {
275     &ReadDeclarationsFile ("$ROOT_DIR/$MODULE-overrides.txt", 1);
278 for my $dir (@SOURCE_DIRS) {
279     &ReadSourceDocumentation ($dir);
282 my $changed = &OutputSGML ("$ROOT_DIR/$MODULE-sections.txt");
284 # If any of the DocBook SGML files have changed, update the timestamp file (so
285 # it can be used for Makefile dependencies).
286 if ($changed || ! -e "$ROOT_DIR/sgml.stamp") {
287     open (TIMESTAMP, ">$ROOT_DIR/sgml.stamp")
288         || die "Can't create $ROOT_DIR/sgml.stamp: $!";
289     print (TIMESTAMP "timestamp");
290     close (TIMESTAMP);
293 #############################################################################
294 # Function    : OutputObjectList
295 # Description : This outputs the alphabetical list of objects, in a columned
296 #               table. FIXME: Currently this also outputs ancestor objects
297 #               which may not actually be in this module.
298 # Arguments   : none
299 #############################################################################
301 sub OutputObjectList {
302     my $cols = 3;
303     my $old_object_index = "$SGML_OUTPUT_DIR/object_index.sgml";
304     my $new_object_index = "$SGML_OUTPUT_DIR/object_index.new";
306     open (OUTPUT, ">$new_object_index")
307         || die "Can't create $new_object_index: $!";
308     print (OUTPUT <<EOF);
309 <informaltable pgwide="1" frame="none">
310 <tgroup cols="$cols">
311 <colspec colwidth="1*"${empty_element_end}
312 <colspec colwidth="1*"${empty_element_end}
313 <colspec colwidth="1*"${empty_element_end}
314 <tbody>
317     my $count = 0;
318     my $object;
319     foreach $object (sort(@Objects)) {
320         my $xref = &MakeXRef ($object);
321         if ($count % $cols == 0) { print (OUTPUT "<row>\n"); }
322         print (OUTPUT "<entry>$xref</entry>\n");
323         if ($count % $cols == ($cols - 1)) { print (OUTPUT "</row>\n"); }
324         $count++;
325     }
326     if ($count == 0) {
327         # emit an empty row, since empty tables are invalid
328         print (OUTPUT "<row><entry> </entry></row>\n");
329     }
331     print (OUTPUT <<EOF);
332 </tbody></tgroup></informaltable>
334     close (OUTPUT);
336     &UpdateFileIfChanged ($old_object_index, $new_object_index, 0);
340 #############################################################################
341 # Function    : OutputSGML
342 # Description : This collects the output for each section of the docs, and
343 #               outputs each file when the end of the section is found.
344 # Arguments   : $file - the $MODULE-sections.txt file which contains all of
345 #               the functions/macros/structs etc. being documented, organised
346 #               into sections and subsections.
347 #############################################################################
349 sub OutputSGML {
350     my ($file) = @_;
352     #print "Reading: $file\n";
353     open (INPUT, $file)
354         || die "Can't open $file: $!";
355     my $filename = "";
356     my $book_top = "";
357     my $book_bottom = "";
358     my $includes = (defined $DEFAULT_INCLUDES) ? $DEFAULT_INCLUDES : "";
359     my $section_includes = "";
360     my $in_section = 0;
361     my $title = "";
362     my $subsection = "";
363     my $synopsis;
364     my $details;
365     my $num_symbols;
366     my $changed = 0;
367     my $signals_synop = "";
368     my $signals_desc = "";
369     my $args_synop = "";
370     my $child_args_synop = "";
371     my $style_args_synop = "";
372     my $args_desc = "";
373     my $child_args_desc = "";
374     my $style_args_desc = "";
375     my $hierarchy = "";
376     my $interfaces = "";
377     my $implementations = "";
378     my $prerequisites = "";
379     my $derived = "";
380     my @file_objects = ();
381     my %templates = ();
382     my %symbols = ();
384     # merge the source docs, in case there are no templates
385     &MergeSourceDocumentation;
387     while (<INPUT>) {
388         if (m/^#/) {
389             next;
391         } elsif (m/^<SECTION>/) {
392             $synopsis = "";
393             $details = "";
394             $num_symbols = 0;
395             $in_section = 1;
396             @file_objects = ();
397             %symbols = ();
399         } elsif (m/^<SUBSECTION\s*(.*)>/i) {
400             $synopsis .= "\n";
401             $subsection = $1;
403         } elsif (m/^<SUBSECTION>/) {
405         } elsif (m/^<TITLE>(.*)<\/TITLE>/) {
406             $title = $1;
407             #print "Section: $title\n";
409             # We don't want warnings if object & class structs aren't used.
410             $DeclarationOutput{$title} = 1;
411             $DeclarationOutput{"${title}Class"} = 1;
413         } elsif (m/^<FILE>(.*)<\/FILE>/) {
414             my $sym;
416             $filename = $1;
417             if (! defined $templates{$filename}) {
418                if (&ReadTemplateFile ("$TMPL_DIR/$filename", 1)) {
419                    &MergeSourceDocumentation;
420                    $templates{$filename}=$.;
421                }
422             } else {
423                 &LogWarning ($file, $., "Double <FILE>$filename</FILE> entry. ".
424                     "Previous occurrence on line ".$templates{$filename}.".");
425             }
427         } elsif (m/^<INCLUDE>(.*)<\/INCLUDE>/) {
428             if ($in_section) {
429                 $section_includes = $1;
430             } else {
431                 if (defined $DEFAULT_INCLUDES) {
432                     &LogWarning ($file, $., "Default <INCLUDE> being overridden by command line option.");
433                 }
434                 else {
435                     $includes = $1;
436                 }
437             }
439         } elsif (m/^<\/SECTION>/) {
440             if($title eq "" && $filename eq "") {
441                 &LogWarning ($file, $., "Section has no title and no file.");
442             }
443             # FIXME: one of those would be enough
444             if ($title eq "") {
445                 $title = $filename;
446             }
447             if ($filename eq "") {
448                 $filename = $title;
449             }
450             #print "End of section: $title\n";
452             $filename =~ s/\s/_/g;
454             my $section_id = $SourceSymbolDocs{"$TMPL_DIR/$filename:Section_Id"};
455             if (defined ($section_id) && $section_id !~ m/^\s*$/) {
456                 # Do nothing. Use section_id as it is.
457             } elsif (&CheckIsObject ($title)) {
458                 # GtkObjects use their class name as the ID.
459                 $section_id = &CreateValidSGMLID ($title);
460             } else {
461                 $section_id = &CreateValidSGMLID ("$MODULE-$title");
462             }
464             if ($num_symbols > 0) {
465                 if (lc($OUTPUT_FORMAT) eq "xml") {
466                     $book_bottom .= "    <xi:include href=\"xml/$filename.xml\"/>\n";
467                 } else {
468                     $book_top.="<!ENTITY $section_id SYSTEM \"sgml/$filename.sgml\">\n";
469                     $book_bottom .= "    &$section_id;\n";
470                 }
472                 if (defined ($SourceSymbolDocs{"$TMPL_DIR/$filename:Include"})) {
473                     if ($section_includes) {
474                         &LogWarning ($file, $., "Section <INCLUDE> being overridden by inline comments.");
475                     }
476                     $section_includes = $SourceSymbolDocs{"$TMPL_DIR/$filename:Include"};
477                 }
478                 if ($section_includes eq "") {
479                     $section_includes = $includes;
480                 }
482                  $signals_synop =~ s/^\n*//g;
483                  $signals_synop =~ s/\n+$/\n/g;
484                 if ($signals_synop ne '') {
485                     $signals_synop = <<EOF;
486 <refsect1 id="$section_id.signals" role="signal_proto">
487 <title role="signal_proto.title">Signals</title>
488 <synopsis>
489 ${signals_synop}</synopsis>
490 </refsect1>
492                      $signals_desc =~ s/^\n*//g;
493                      $signals_desc =~ s/\n+$/\n/g;
494                     $signals_desc  = <<EOF;
495 <refsect1 id="$section_id.signal-details" role="signals">
496 <title role="signals.title">Signal Details</title>
497 $signals_desc
498 </refsect1>
500                 }
502                  $args_synop =~ s/^\n*//g;
503                  $args_synop =~ s/\n+$/\n/g;
504                 if ($args_synop ne '') {
505                     $args_synop = <<EOF;
506 <refsect1 id="$section_id.properties" role="properties">
507 <title role="properties.title">Properties</title>
508 <synopsis>
509 ${args_synop}</synopsis>
510 </refsect1>
512                      $args_desc =~ s/^\n*//g;
513                      $args_desc =~ s/\n+$/\n/g;
514                     $args_desc  = <<EOF;
515 <refsect1 id="$section_id.property-details" role="property_details">
516 <title role="property_details.title">Property Details</title>
517 $args_desc
518 </refsect1>
520                 }
522                  $child_args_synop =~ s/^\n*//g;
523                  $child_args_synop =~ s/\n+$/\n/g;
524                 if ($child_args_synop ne '') {
525                     $args_synop .= <<EOF;
526 <refsect1 id="$section_id.child-properties" role="child_properties">
527 <title role="child_properties.title">Child Properties</title>
528 <synopsis>
529 ${child_args_synop}</synopsis>
530 </refsect1>
532                      $args_desc =~ s/^\n*//g;
533                      $args_desc =~ s/\n+$/\n/g;
534                     $args_desc .= <<EOF;
535 <refsect1 id="$section_id.child-property-details" role="child_property_details">
536 <title role="child_property_details.title">Child Property Details</title>
537 $child_args_desc
538 </refsect1>
540                 }
542                  $style_args_synop =~ s/^\n*//g;
543                  $style_args_synop =~ s/\n+$/\n/g;
544                 if ($style_args_synop ne '') {
545                     $args_synop .= <<EOF;
546 <refsect1 id="$section_id.style-properties" role="style_properties">
547 <title role="style_properties.title">Style Properties</title>
548 <synopsis>
549 ${style_args_synop}</synopsis>
550 </refsect1>
552                      $args_desc =~ s/^\n*//g;
553                      $args_desc =~ s/\n+$/\n/g;
554                     $args_desc .= <<EOF;
555 <refsect1 id="$section_id.style-property-details" role="style_properties_details">
556 <title role="style_properties_details.title">Style Property Details</title>
557 $style_args_desc
558 </refsect1>
560                 }
562                  $hierarchy =~ s/^\n*//g;
563                  $hierarchy =~ s/\n+$/\n/g;
564                 if ($hierarchy ne "") {
565                     $hierarchy = <<EOF;
566 <refsect1 id="$section_id.object-hierarchy" role="object_hierarchy">
567 <title role="object_hierarchy.title">Object Hierarchy</title>
568 $hierarchy
569 </refsect1>
571                 }
573                  $interfaces =~ s/^\n*//g;
574                  $interfaces =~ s/\n+$/\n/g;
575                 if ($interfaces ne "") {
576                     $interfaces = <<EOF;
577 <refsect1 id="$section_id.implemented-interfaces" role="impl_interfaces">
578 <title role="impl_interfaces.title">Implemented Interfaces</title>
579 $interfaces
580 </refsect1>
582                 }
584                  $implementations =~ s/^\n*//g;
585                  $implementations =~ s/\n+$/\n/g;
586                 if ($implementations ne "") {
587                     $implementations = <<EOF;
588 <refsect1 id="$section_id.implementations" role="implementations">
589 <title role="implementations.title">Known Implementations</title>
590 $implementations
591 </refsect1>
593                 }
595                  $prerequisites =~ s/^\n*//g;
596                  $prerequisites =~ s/\n+$/\n/g;
597                 if ($prerequisites ne "") {
598                     $prerequisites = <<EOF;
599 <refsect1 id="$section_id.prerequisites" role="prerequisites">
600 <title role="prerequisites.title">Prerequisites</title>
601 $prerequisites
602 </refsect1>
604                 }
606                  $derived =~ s/^\n*//g;
607                  $derived =~ s/\n+$/\n/g;
608                 if ($derived ne "") {
609                     $derived = <<EOF;
610 <refsect1 id="$section_id.derived-interfaces" role="derived_interfaces">
611 <title role="derived_interfaces.title">Known Derived Interfaces</title>
612 $derived
613 </refsect1>
615                 }
617                 $synopsis =~ s/^\n*//g;
618                 $synopsis =~ s/\n+$/\n/g;
619                 my $file_changed = &OutputSGMLFile ($filename, $title, $section_id,
620                                                     $section_includes,
621                                                     \$synopsis, \$details,
622                                                     \$signals_synop, \$signals_desc,
623                                                     \$args_synop, \$args_desc,
624                                                     \$hierarchy, \$interfaces,
625                                                     \$implementations,
626                                                     \$prerequisites, \$derived,
627                                                     \@file_objects);
628                 if ($file_changed) {
629                     $changed = 1;
630                 }
631             }
632             $title = "";
633             $subsection = "";
634             $in_section = 0;
635             $section_includes = "";
636             $signals_synop = "";
637             $signals_desc = "";
638             $args_synop = "";
639             $child_args_synop = "";
640             $style_args_synop = "";
641             $args_desc = "";
642             $child_args_desc = "";
643             $style_args_desc = "";
644             $hierarchy = "";
645             $interfaces = "";
646             $implementations = "";
647             $prerequisites = "";
648             $derived = "";
650         } elsif (m/^(\S+)/) {
651             my $symbol = $1;
652             #print "  Symbol: $symbol\n";
654             # FIXME: check for duplicate entries
655             if (! defined $symbols{$symbol}) {
656                 my $declaration = $Declarations{$symbol};
657                 if (defined ($declaration)) {
658                     # We don't want standard macros/functions of GtkObjects,
659                     # or private declarations.
660                     if ($subsection ne "Standard" && $subsection ne "Private") {
661                         if (&CheckIsObject ($symbol)) {
662                             push @file_objects, $symbol;
663                         }
664                         my ($synop, $desc) = &OutputDeclaration ($symbol,
665                                                                  $declaration);
666                         my ($sig_synop, $sig_desc) = &GetSignals ($symbol);
667                         my ($arg_synop, $child_arg_synop, $style_arg_synop,
668                             $arg_desc, $child_arg_desc, $style_arg_desc) = &GetArgs ($symbol);
669                         my $hier = &GetHierarchy ($symbol);
670                         my $ifaces = &GetInterfaces ($symbol);
671                         my $impls = &GetImplementations ($symbol);
672                         my $prereqs = &GetPrerequisites ($symbol);
673                         my $der = &GetDerived ($symbol);
674                         $synopsis .= $synop;
675                         $details .= $desc;
676                         $signals_synop .= $sig_synop;
677                         $signals_desc .= $sig_desc;
678                         $args_synop .= $arg_synop;
679                         $child_args_synop .= $child_arg_synop;
680                         $style_args_synop .= $style_arg_synop;
681                         $args_desc .= $arg_desc;
682                         $child_args_desc .= $child_arg_desc;
683                         $style_args_desc .= $style_arg_desc;
684                         $hierarchy .= $hier;
685                         $interfaces .= $ifaces;
686                         $implementations .= $impls;
687                         $prerequisites .= $prereqs;
688                         $derived .= $der;
689                     }
690     
691                     # Note that the declaration has been output.
692                     $DeclarationOutput{$symbol} = 1;
693                 } elsif ($subsection ne "Standard" && $subsection ne "Private") {
694                     $UndeclaredSymbols{$symbol} = 1;
695                     &LogWarning ($file, $., "No declaration found for $symbol.");
696                 }
697                 $num_symbols++;
698                 $symbols{$symbol}=$.;
699             }
700             else {
701                 &LogWarning ($file, $., "Double symbol entry for $symbol. ".
702                     "Previous occurrence on line ".$symbols{$symbol}.".");
703             }
704         }
705     }
706     close (INPUT);
708     &OutputMissingDocumentation;
709     &OutputUndeclaredSymbols;
711     if ($OUTPUT_ALL_SYMBOLS) {
712         &OutputAllSymbols;
713     }
715     for $filename (split (' ', $EXPAND_CONTENT_FILES)) {
716         my $file_changed = &OutputExtraFile ($filename);
717         if ($file_changed) {
718             $changed = 1;
719         }
720     }
722     &OutputBook ($book_top, $book_bottom);
724     return $changed;
728 #############################################################################
729 # Function    : ReadKnownSymbols
730 # Description : This collects the names of non-private symbols from the
731 #               $MODULE-sections.txt file.
732 # Arguments   : $file - the $MODULE-sections.txt file which contains all of
733 #               the functions/macros/structs etc. being documented, organised
734 #               into sections and subsections.
735 #############################################################################
737 sub ReadKnownSymbols {
738     my ($file) = @_;
740     my $subsection = "";
742     #print "Reading: $file\n";
743     open (INPUT, $file)
744         || die "Can't open $file: $!";
746     while (<INPUT>) {
747         if (m/^#/) {
748             next;
750         } elsif (m/^<SECTION>/) {
751             $subsection = "";
753         } elsif (m/^<SUBSECTION\s*(.*)>/i) {
754             $subsection = $1;
756         } elsif (m/^<SUBSECTION>/) {
757             next;
759         } elsif (m/^<TITLE>(.*)<\/TITLE>/) {
760             next;
762         } elsif (m/^<FILE>(.*)<\/FILE>/) {
763             next;
765         } elsif (m/^<INCLUDE>(.*)<\/INCLUDE>/) {
766             next;
768         } elsif (m/^<\/SECTION>/) {
769             next;
771         } elsif (m/^(\S+)/) {
772             my $symbol = $1;
774             if ($subsection ne "Standard" && $subsection ne "Private") {
775                 $KnownSymbols{$symbol} = 1;
776             }
777             else {
778                 $KnownSymbols{$symbol} = 0;
779             }
780         }
781     }
782     close (INPUT);
786 #############################################################################
787 # Function    : OutputDeclaration
788 # Description : Returns the synopsis and detailed description DocBook
789 #               describing one function/macro etc.
790 # Arguments   : $symbol - the name of the function/macro begin described.
791 #               $declaration - the declaration of the function/macro.
792 #############################################################################
794 sub OutputDeclaration {
795     my ($symbol, $declaration) = @_;
797     my $type = $DeclarationTypes {$symbol};
798     if ($type eq 'MACRO') {
799         return &OutputMacro ($symbol, $declaration);
800     } elsif ($type eq 'TYPEDEF') {
801         return &OutputTypedef ($symbol, $declaration);
802     } elsif ($type eq 'STRUCT') {
803         return &OutputStruct ($symbol, $declaration);
804     } elsif ($type eq 'ENUM') {
805         return &OutputEnum ($symbol, $declaration);
806     } elsif ($type eq 'UNION') {
807         return &OutputUnion ($symbol, $declaration);
808     } elsif ($type eq 'VARIABLE') {
809         return &OutputVariable ($symbol, $declaration);
811     } elsif ($type eq 'FUNCTION') {
812         return &OutputFunction ($symbol, $declaration, $type);
813     } elsif ($type eq 'USER_FUNCTION') {
814         return &OutputFunction ($symbol, $declaration, $type);
815     } else {
816         die "Unknown symbol type";
817     }
821 #############################################################################
822 # Function    : OutputSymbolTraits
823 # Description : Returns the Since and StabilityLevel paragraphs for a symbol.
824 # Arguments   : $symbol - the name of the function/macro begin described.
825 #############################################################################
827 sub OutputSymbolTraits {
828     my ($symbol) = @_;
829     my $desc = "";
831     if (exists $Since{$symbol}) {
832         $desc .= "<para role=\"since\">Since $Since{$symbol}</para>";
833     }
834     if (exists $StabilityLevel{$symbol}) {
835         $desc .= "<para role=\"stability\">Stability Level: $StabilityLevel{$symbol}</para>";
836     }
837     return $desc;
841 #############################################################################
842 # Function    : OutputMacro
843 # Description : Returns the synopsis and detailed description of a macro.
844 # Arguments   : $symbol - the macro.
845 #               $declaration - the declaration of the macro.
846 #############################################################################
848 sub OutputMacro {
849     my ($symbol, $declaration) = @_;
850     my $id = &CreateValidSGMLID ($symbol);
851     my $condition = &MakeConditionDescription ($symbol);
852     my $synop = &MakeReturnField("#define") . "<link linkend=\"$id\">$symbol</link>";
853     my $desc;
854     my $padding = "";
855     my $args = "";
856     my $pad;
858     if ($declaration =~ m/^\s*#\s*define(\s+)\w+(\([^\)]*\))/) {
859         $padding = $1;
860         $args = $2;
862         if (length ($symbol) < $SYMBOL_FIELD_WIDTH) {
863             $synop .= (' ' x ($SYMBOL_FIELD_WIDTH - length ($symbol)));
864         }
866         # Try to align all the lines of args correctly.
867         $pad = ' ' x ($RETURN_TYPE_FIELD_WIDTH + $SYMBOL_FIELD_WIDTH + 1);
868         my $args_padded = $args;
869         $args_padded =~ s/ *\\\n */\n$pad/gm;
870         $synop .= &CreateValidSGML ($args_padded);
871     }
872     $synop .= "\n";
874     my $title = $symbol . ($args ? "()" : "");
875     $desc = "<refsect2 id=\"$id\" role=\"macro\"$condition>\n<title>$title</title>\n";
876     $desc .= MakeIndexterms($symbol, $id);
878     # Don't output the macro definition if is is a conditional macro or it
879     # looks like a function, i.e. starts with "g_" or "_?gnome_", or it is
880     # longer than 2 lines, otherwise we get lots of complicated macros like
881     # g_assert.
882     if (!defined ($DeclarationConditional{$symbol}) && ($symbol !~ m/^g_/)
883         && ($symbol !~ m/^_?gnome_/) && (($declaration =~ tr/\n//) < 2)) {
884         $declaration = &CreateValidSGML ($declaration);
885         $desc .= "<programlisting>$declaration</programlisting>\n";
886     } else {
887         $desc .= "<programlisting>" . &MakeReturnField("#define") . "$symbol";
888         # Align each line so that if should all line up OK.
889         $pad = ' ' x ($RETURN_TYPE_FIELD_WIDTH - length ("#define "));
890         $args =~ s/\n/\n$pad/gm;
891         $desc .= &CreateValidSGML ($args);
892         $desc .= "</programlisting>\n";
893     }
895     $desc .= &MakeDeprecationNote($symbol);
897     my $parameters = &OutputParamDescriptions ("MACRO", $symbol);
898     my $parameters_output = 0;
900     if (defined ($SymbolDocs{$symbol})) {
901         my $symbol_docs = &ExpandAbbreviations($symbol, $SymbolDocs{$symbol});
903         # Try to insert the parameter table at the author's desired position.
904         # Otherwise we need to tag it onto the end.
905         if ($symbol_docs =~ s/<!--PARAMETERS-->/$parameters/) {
906           $parameters_output = 1;
907         }
908         $desc .= $symbol_docs;
909     }
911     if ($parameters_output == 0) {
912         $desc .= $parameters;
913     }
915     $desc .= OutputSymbolTraits ($symbol);
916     $desc .= "</refsect2>\n";
917     return ($synop, $desc);
921 #############################################################################
922 # Function    : OutputTypedef
923 # Description : Returns the synopsis and detailed description of a typedef.
924 # Arguments   : $symbol - the typedef.
925 #               $declaration - the declaration of the typedef,
926 #                 e.g. 'typedef unsigned int guint;'
927 #############################################################################
929 sub OutputTypedef {
930     my ($symbol, $declaration) = @_;
931     my $id = &CreateValidSGMLID ($symbol);
932     my $condition = &MakeConditionDescription ($symbol);
933     my $synop = &MakeReturnField("typedef") . "<link linkend=\"$id\">$symbol</link>;\n";
934     my $desc = "<refsect2 id=\"$id\" role=\"typedef\"$condition>\n<title>$symbol</title>\n";
936     $desc .= MakeIndexterms($symbol, $id);
938     if (!defined ($DeclarationConditional{$symbol})) {
939         $declaration = &CreateValidSGML ($declaration);
940         $desc .= "<programlisting>$declaration</programlisting>\n";
941     }
943     $desc .= &MakeDeprecationNote($symbol);
945     if (defined ($SymbolDocs{$symbol})) {
946         $desc .= &ExpandAbbreviations($symbol, $SymbolDocs{$symbol});
947     }
948     $desc .= OutputSymbolTraits ($symbol);
949     $desc .= "</refsect2>\n";
950     return ($synop, $desc);
954 #############################################################################
955 # Function    : OutputStruct
956 # Description : Returns the synopsis and detailed description of a struct.
957 #               We check if it is a widget struct, and if so we only output
958 #               parts of it that are noted as public fields.
959 #               We also use a different SGML ID for widget structs, since the
960 #               original ID is used for the entire RefEntry.
961 # Arguments   : $symbol - the struct.
962 #               $declaration - the declaration of the struct.
963 #############################################################################
965 sub OutputStruct {
966     my ($symbol, $declaration) = @_;
968     my $is_widget_struct = 0;
969     my $default_to_public = 1;
970     if (&CheckIsObject ($symbol)) {
971     #print "Found widget struct: $symbol\n";
972     $is_widget_struct = 1;
973         $default_to_public = 0;
974     }
976     my $id;
977     my $condition;
978     if ($is_widget_struct) {
979         $id = &CreateValidSGMLID ($symbol . "_struct");
980         $condition = &MakeConditionDescription ($symbol . "_struct");
981     } else {
982         $id = &CreateValidSGMLID ($symbol);
983         $condition = &MakeConditionDescription ($symbol);
984     }
986     # Determine if it is a simple struct or it also has a typedef.
987     my $has_typedef = 0;
988     if ($StructHasTypedef{$symbol} || $declaration =~ m/^\s*typedef\s+/) {
989       $has_typedef = 1;
990     }
992     my $synop;
993     my $desc;
994     if ($has_typedef) {
995         # For structs with typedefs we just output the struct name.
996         $synop = &MakeReturnField("") . "<link linkend=\"$id\">$symbol</link>;\n";
997         $desc = "<refsect2 id=\"$id\" role=\"struct\"$condition>\n<title>$symbol</title>\n";
998     } else {
999         $synop = &MakeReturnField("struct") . "<link linkend=\"$id\">$symbol</link>;\n";
1000         $desc = "<refsect2 id=\"$id\" role=\"struct\"$condition>\n<title>struct $symbol</title>\n";
1001     }
1003     $desc .= MakeIndexterms($symbol, $id);
1005     # Form a pretty-printed, private-data-removed form of the declaration
1007     my $decl_out = "";
1008     if ($declaration =~ m/^\s*$/) {
1009         #print "Found opaque struct: $symbol\n";
1010         $decl_out = "typedef struct _$symbol $symbol;";
1011     } elsif ($declaration =~ m/^\s*struct\s+\w+\s*;\s*$/) {
1012         #print "Found opaque struct: $symbol\n";
1013         $decl_out = "struct $symbol;";
1014     } else {
1015         my $public = $default_to_public;
1016         my $new_declaration = "";
1017         my $decl_line;
1018         my $decl = $declaration;
1020         if ($decl =~ m/^\s*(typedef\s+)?struct\s*\w*\s*(?:\/\*.*\*\/)?\s*{(.*)}\s*\w*\s*;\s*$/s) {
1021             my $struct_contents = $2;
1023             foreach $decl_line (split (/\n/, $struct_contents)) {
1024                 #print "Struct line: $decl_line\n";
1025                 if ($decl_line =~ m%/\*\s*<\s*public\s*>\s*\*/%) {
1026                     $public = 1;
1027                 } elsif ($decl_line =~ m%/\*\s*<\s*(private|protected)\s*>\s*\*/%) {
1028                     $public = 0;
1029                 } elsif ($public) {
1030                     $new_declaration .= $decl_line . "\n";
1031                 }
1032             }
1034             if ($new_declaration) {
1035                 # Strip any blank lines off the ends.
1036                 $new_declaration =~ s/^\s*\n//;
1037                 $new_declaration =~ s/\n\s*$/\n/;
1039                 if ($has_typedef) {
1040                     $decl_out = "typedef struct {\n" . $new_declaration
1041                       . "} $symbol;\n";
1042                 } else {
1043                     $decl_out = "struct $symbol {\n" . $new_declaration
1044                       . "};\n";
1045                 }
1046             }
1047         } else {
1048             &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1049                 "Couldn't parse struct:\n$declaration");
1050         }
1052         # If we couldn't parse the struct or it was all private, output an
1053         # empty struct declaration.
1054         if ($decl_out eq "") {
1055             if ($has_typedef) {
1056                 $decl_out = "typedef struct _$symbol $symbol;";
1057             } else {
1058                 $decl_out = "struct $symbol;";
1059             }
1060         }
1061     }
1063     $decl_out = &CreateValidSGML ($decl_out);
1064     $desc .= "<programlisting>$decl_out</programlisting>\n";
1066     $desc .= &MakeDeprecationNote($symbol);
1068     if (defined ($SymbolDocs{$symbol})) {
1069         $desc .= &ExpandAbbreviations($symbol, $SymbolDocs{$symbol});
1070     }
1072     # Create a table of fields and descriptions
1074     # FIXME: Inserting &nbsp's into the produced type declarations here would
1075     #        improve the output in most situations ... except for function
1076     #        members of structs!
1077     my @fields = ParseStructDeclaration($declaration, !$default_to_public,
1078                                         0, \&MakeXRef,
1079                                         sub {
1080                                             "<structfield>$_[0]</structfield>";
1081                                         });
1082     my $params = $SymbolParams{$symbol};
1084     # If no parameters are filled in, we don't generate the description
1085     # table, for backwards compatibility
1087     my $found = 0;
1088     if (defined $params) {
1089         for (my $i = 1; $i <= $#$params; $i += 2) {
1090             if ($params->[$i] =~ /\S/) {
1091                 $found = 1;
1092                 last;
1093             }
1094         }
1095     }
1097     if ($found) {
1098         my %field_descrs = @$params;
1100             $desc .= <<EOF;
1101 <variablelist role="struct">
1103         while (@fields) {
1104             my $field_name = shift @fields;
1105             my $text = shift @fields;
1106             my $field_descr = $field_descrs{$field_name};
1108             $desc .= "<varlistentry>\n<term>$text</term>\n";
1109             if (defined $field_descr) {
1110                 $desc .= "<listitem><simpara>".&ExpandAbbreviations($symbol, $field_descr)."</simpara></listitem>\n";
1111             } else {
1112                 $desc .= "<listitem></listitem>\n";
1113             }
1114             $desc .= "</varlistentry>\n";
1115         }
1117         $desc .= "</variablelist>";
1118     }
1119     $desc .= OutputSymbolTraits ($symbol);
1120     $desc .= "</refsect2>\n";
1121     return ($synop, $desc);
1125 #############################################################################
1126 # Function    : OutputEnum
1127 # Description : Returns the synopsis and detailed description of a enum.
1128 # Arguments   : $symbol - the enum.
1129 #               $declaration - the declaration of the enum.
1130 #############################################################################
1132 sub OutputEnum {
1133     my ($symbol, $declaration) = @_;
1134     my $id = &CreateValidSGMLID ($symbol);
1135     my $condition = &MakeConditionDescription ($symbol);
1136     my $synop = &MakeReturnField("enum") . "<link linkend=\"$id\">$symbol</link>;\n";
1137     my $desc = "<refsect2 id=\"$id\" role=\"enum\"$condition>\n<title>enum $symbol</title>\n";
1139     $desc .= MakeIndexterms($symbol, $id);
1141     $declaration = &CreateValidSGML ($declaration);
1142     $desc .= "<programlisting>$declaration</programlisting>\n";
1144     $desc .= &MakeDeprecationNote($symbol);
1146     if (defined ($SymbolDocs{$symbol})) {
1147         $desc .= &ExpandAbbreviations($symbol, $SymbolDocs{$symbol});
1148     }
1150     # Create a table of fields and descriptions
1152     my @members = ParseEnumDeclaration($declaration);
1153     my $params = $SymbolParams{$symbol};
1155     # If no parameters are filled in, we don't generate the description
1156     # table, for backwards compatibility
1158     my $found = 0;
1159     if (defined $params) {
1160         for (my $i = 1; $i <= $#$params; $i += 2) {
1161             if ($params->[$i] =~ /\S/) {
1162                 $found = 1;
1163                 last;
1164             }
1165         }
1166     }
1168     if ($found) {
1169         my %member_descrs = @$params;
1171             $desc .= <<EOF;
1172 <variablelist role="enum">
1174         for my $member_name (@members) {
1175             my $member_descr = $member_descrs{$member_name};
1177             $id = &CreateValidSGMLID ($member_name);
1178             $condition = &MakeConditionDescription ($member_name);
1179             $desc .= "<varlistentry id=\"$id\" role=\"constant\"$condition>\n<term><literal>$member_name</literal></term>\n";
1180             if (defined $member_descr) {
1181                 $desc .= "<listitem><simpara>".&ExpandAbbreviations($symbol, $member_descr)."</simpara></listitem>\n";
1182             } else {
1183                 $desc .= "<listitem></listitem>\n";
1184             }
1185             $desc .= "</varlistentry>\n";
1186         }
1188         $desc .= "</variablelist>";
1189     }
1191     $desc .= OutputSymbolTraits ($symbol);
1192     $desc .= "</refsect2>\n";
1193     return ($synop, $desc);
1197 #############################################################################
1198 # Function    : OutputUnion
1199 # Description : Returns the synopsis and detailed description of a union.
1200 # Arguments   : $symbol - the union.
1201 #               $declaration - the declaration of the union.
1202 #############################################################################
1204 sub OutputUnion {
1205     my ($symbol, $declaration) = @_;
1206     my $id = &CreateValidSGMLID ($symbol);
1207     my $condition = &MakeConditionDescription ($symbol);
1208     my $synop = &MakeReturnField("union") . "<link linkend=\"$id\">$symbol</link>;\n";
1209     my $desc = "<refsect2 id=\"$id\" role=\"union\"$condition>\n<title>union $symbol</title>\n";
1211     $desc .= MakeIndexterms($symbol, $id);
1213     $declaration = &CreateValidSGML ($declaration);
1214     $desc .= "<programlisting>$declaration</programlisting>\n";
1216     $desc .= &MakeDeprecationNote($symbol);
1218     if (defined ($SymbolDocs{$symbol})) {
1219         $desc .= &ExpandAbbreviations($symbol, $SymbolDocs{$symbol});
1220     }
1221     $desc .= OutputSymbolTraits ($symbol);
1222     $desc .= "</refsect2>\n";
1223     return ($synop, $desc);
1227 #############################################################################
1228 # Function    : OutputVariable
1229 # Description : Returns the synopsis and detailed description of a variable.
1230 # Arguments   : $symbol - the extern'ed variable.
1231 #               $declaration - the declaration of the variable.
1232 #############################################################################
1234 sub OutputVariable {
1235     my ($symbol, $declaration) = @_;
1236     my $id = &CreateValidSGMLID ($symbol);
1237     my $condition = &MakeConditionDescription ($symbol);
1239     my $synop;
1240     if ($declaration =~ m/^\s*extern\s+((const\s+|unsigned\s+)*\w+)(\s+\*+|\*+|\s)(\s*)([A-Za-z]\w*)\s*;/) {
1241         my $mod = defined ($1) ? $1 : "";
1242         my $ptr = defined ($3) ? $3 : "";
1243         my $space = defined ($4) ? $4 : "";
1244         $synop = &MakeReturnField("extern") . "$mod$ptr$space<link linkend=\"$id\">$symbol</link>;\n";
1246     } else {
1247         $synop = &MakeReturnField("extern") . "<link linkend=\"$id\">$symbol</link>;\n";
1248     }
1250     my $desc = "<refsect2 id=\"$id\" role=\"variable\"$condition>\n<title>$symbol</title>\n";
1252     $desc .= MakeIndexterms($symbol, $id);
1254     $declaration = &CreateValidSGML ($declaration);
1255     $desc .= "<programlisting>$declaration</programlisting>\n";
1257     $desc .= &MakeDeprecationNote($symbol);
1259     if (defined ($SymbolDocs{$symbol})) {
1260         $desc .= &ExpandAbbreviations($symbol, $SymbolDocs{$symbol});
1261     }
1262     $desc .= OutputSymbolTraits ($symbol);
1263     $desc .= "</refsect2>\n";
1264     return ($synop, $desc);
1268 #############################################################################
1269 # Function    : OutputFunction
1270 # Description : Returns the synopsis and detailed description of a function.
1271 # Arguments   : $symbol - the function.
1272 #               $declaration - the declaration of the function.
1273 #############################################################################
1275 sub OutputFunction {
1276     my ($symbol, $declaration, $symbol_type) = @_;
1277     my $id = &CreateValidSGMLID ($symbol);
1278     my $condition = &MakeConditionDescription ($symbol);
1280     # Take out the return type
1281     $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//;
1282     my $type_modifier = defined($1) ? $1 : "";
1283     my $type = $3;
1284     my $pointer = $4;
1285     #print "$symbol pointer is $pointer\n";
1286     my $xref = &MakeXRef ($type);
1287     my $start = "";
1288     #if ($symbol_type eq 'USER_FUNCTION') {
1289     #    $start = "typedef ";
1290     #}
1292     # We output const rather than G_CONST_RETURN.
1293     $type_modifier =~ s/G_CONST_RETURN/const/g;
1294     $pointer =~ s/G_CONST_RETURN/const/g;
1296     my $ret_type_len = length ($start) + length ($type_modifier)
1297         + length ($pointer) + length ($type);
1298     my $ret_type_output;
1299     my $symbol_len;
1300     if ($ret_type_len < $RETURN_TYPE_FIELD_WIDTH) {
1301         $ret_type_output = "$start$type_modifier$xref$pointer"
1302             . (' ' x ($RETURN_TYPE_FIELD_WIDTH - $ret_type_len));
1303         $symbol_len = 0;
1304     } else {
1305 #       $ret_type_output = "$start$type_modifier$xref$pointer\n"
1306 #           . (' ' x $RETURN_TYPE_FIELD_WIDTH);
1308         $ret_type_output = "$start$type_modifier$xref$pointer ";
1309         $symbol_len = $ret_type_len + 1 - $RETURN_TYPE_FIELD_WIDTH;
1310     }
1312     $symbol_len += length ($symbol);
1313     my $char1 = my $char2 = my $char3 = "";
1314     if ($symbol_type eq 'USER_FUNCTION') {
1315         $symbol_len += 3;
1316         $char1 = "(";
1317         $char2 = "*";
1318         $char3 = ")";
1319     }
1321     my ($symbol_output, $symbol_desc_output);
1322     if ($symbol_len < $SYMBOL_FIELD_WIDTH) {
1323         $symbol_output = "$char1<link linkend=\"$id\">$char2$symbol</link>$char3"
1324             . (' ' x ($SYMBOL_FIELD_WIDTH - $symbol_len));
1325         $symbol_desc_output = "$char1$char2$symbol$char3"
1326             . (' ' x ($SYMBOL_FIELD_WIDTH - $symbol_len));
1327     } else {
1328         $symbol_output = "$char1<link linkend=\"$id\">$char2$symbol</link>$char3\n"
1329             . (' ' x ($SYMBOL_FIELD_WIDTH + $RETURN_TYPE_FIELD_WIDTH));
1330         $symbol_desc_output = "$char1$char2$symbol$char3\n"
1331             . (' ' x ($SYMBOL_FIELD_WIDTH + $RETURN_TYPE_FIELD_WIDTH));
1332     }
1334     my $synop = $ret_type_output . $symbol_output . '(';
1335     my $desc = "<refsect2 id=\"$id\" role=\"function\"$condition>\n<title>${symbol} ()</title>\n";
1337     $desc .= MakeIndexterms($symbol, $id);
1339     $desc  .= "<programlisting>${ret_type_output}$symbol_desc_output(";
1341     my $param_num = 0;
1342     while ($declaration ne "") {
1343         #print "$declaration";
1345         if ($declaration =~ s/^[\s,]+//) {
1346             # skip whitespace and commas
1347             next;
1349         } elsif ($declaration =~ s/^void\s*[,\n]//) {
1350             $synop .= "void";
1351             $desc  .= "void";
1353         } elsif ($declaration =~ s/^...\s*[,\n]//) {
1354             if ($param_num == 0) {
1355                 $synop .= "...";
1356                 $desc  .= "...";
1357             } else {
1358                 $synop .= ",\n"
1359                     . (' ' x ($SYMBOL_FIELD_WIDTH + $RETURN_TYPE_FIELD_WIDTH))
1360                     . " ...";
1361                 $desc  .= ",\n"
1362                     . (' ' x ($SYMBOL_FIELD_WIDTH + $RETURN_TYPE_FIELD_WIDTH))
1363                     . " ...";
1364             }
1366         # allow alphanumerics, '_', '[' & ']' in param names
1367         # Try to match a standard parameter (keep in sync with gtkdoc-mktmpl)
1368         #                                $1                                                                                                                                    $2                             $3                                                           $4       $5
1369         } 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]//) {
1370             my $pre     = defined($1) ? $1 : "";
1371             my $type    = $2;
1372             my $ptr     = defined($3) ? $3 : "";
1373             my $name    = defined($4) ? $4 : "";
1374             my $array   = defined($5) ? $5 : "";
1376             $pre  =~ s/\s+/ /g;
1377             $type =~ s/\s+/ /g;
1378             $ptr  =~ s/\s+/ /g;
1379             $ptr  =~ s/\s+$//;
1380             if ($ptr && $ptr !~ m/\*$/) { $ptr .= " "; }
1382             #print "$symbol: '$pre' '$type' '$ptr' '$name' '$array'\n";
1384             if (($name eq "") && $pre =~ m/^((un)?signed .*)\s?/ ) {
1385                 $name = $type;
1386                 $type = "$1";
1387                 $pre = "";
1388             }
1390             my $xref = &MakeXRef ($type);
1391             my $label   = "$pre$xref $ptr$name$array";
1393             #print "$symbol: '$pre' '$type' '$ptr' '$name' '$array'\n";
1395             if ($param_num == 0) {
1396                 $synop .= "$label";
1397                 $desc  .= "$label";
1398             } else {
1399                 $synop .= ",\n"
1400                     . (' ' x ($SYMBOL_FIELD_WIDTH + $RETURN_TYPE_FIELD_WIDTH))
1401                     . " $label";
1402                 $desc  .= ",\n"
1403                     . (' ' x ($SYMBOL_FIELD_WIDTH + $RETURN_TYPE_FIELD_WIDTH))
1404                     . " $label";
1405             }
1407         # Try to match parameters which are functions (keep in sync with gtkdoc-mktmpl)
1408         #                              $1                                       $2          $3      $4                        $5                    $7             $8
1409         } 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]//) {
1410             my $mod1 = defined($1) ? $1 : "";
1411             if (defined($2)) { $mod1 .= $2; }
1412             my $type = $3;
1413             my $ptr1 = $4;
1414             my $mod2 = defined($5) ? $5 : "";
1415             my $func_ptr = $6;
1416             my $name = $7;
1417             my $func_params = defined($8) ? $8 : "";
1418             
1419             #if (!defined($type)) { print "## no type\n"; };
1420             #if (!defined($ptr1)) { print "## no ptr1\n"; };
1421             #if (!defined($func_ptr)) { print "## no func_ptr\n"; };
1422             #if (!defined($name)) { print "## no name\n"; };
1424             if ($ptr1 && $ptr1 !~ m/\*$/) { $ptr1 .= " "; }
1425             $func_ptr  =~ s/\s+//g;
1426             my $xref = &MakeXRef ($type);
1427             my $label = "$mod1$xref$ptr1$mod2 ($func_ptr$name) ($func_params)";
1429             #print "Type: [$mod1][$xref][$ptr1][$mod2] ([$func_ptr][$name]) ($func_params)\n";
1430             if ($param_num == 0) {
1431                 $synop .= "$label";
1432                 $desc  .= "$label";
1433             } else {
1434                 $synop .= ",\n"
1435                     . (' ' x ($SYMBOL_FIELD_WIDTH + $RETURN_TYPE_FIELD_WIDTH))
1436                     . " $label";
1437                 $desc  .= ",\n"
1438                     . (' ' x ($SYMBOL_FIELD_WIDTH + $RETURN_TYPE_FIELD_WIDTH))
1439                     . " $label";
1440             }
1442         } else {
1443             &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1444                 "Can't parse args for function $symbol: $declaration");
1445             last;
1446         }
1447         $param_num++;
1448     }
1449     $synop .= ");\n";
1450     $desc  .= ");</programlisting>\n";
1452     $desc .= &MakeDeprecationNote($symbol);
1454     my $parameters = &OutputParamDescriptions ("FUNCTION", $symbol);
1455     my $parameters_output = 0;
1457     if (defined ($SymbolDocs{$symbol})) {
1458         my $symbol_docs = &ExpandAbbreviations($symbol, $SymbolDocs{$symbol});
1460         # Try to insert the parameter table at the author's desired position.
1461         # Otherwise we need to tag it onto the end.
1462         if ($symbol_docs =~ s/<!--PARAMETERS-->/$parameters/) {
1463           $parameters_output = 1;
1464         }
1465         $desc .= $symbol_docs;
1466     }
1468     if ($parameters_output == 0) {
1469         $desc .= $parameters;
1470     }
1472     $desc .= OutputSymbolTraits ($symbol);
1473     $desc .= "</refsect2>\n";
1474     return ($synop, $desc);
1478 #############################################################################
1479 # Function    : OutputParamDescriptions
1480 # Description : Returns the DocBook output describing the parameters of a
1481 #               function, macro or signal handler.
1482 # Arguments   : $symbol_type - 'FUNCTION', 'MACRO' or 'SIGNAL'. Signal
1483 #                 handlers have an implicit user_data parameter last.
1484 #               $symbol - the name of the function/macro being described.
1485 #############################################################################
1487 sub OutputParamDescriptions {
1488     my ($symbol_type, $symbol) = @_;
1489     my $output = "";
1490     if (defined ($SymbolParams{$symbol})) {
1491         my $returns = "";
1492         my $params = $SymbolParams{$symbol};
1493         my $params_desc = "";
1494         my $j;
1495         for ($j = 0; $j <= $#$params; $j += 2) {
1496             my $param_name = $$params[$j];
1497             my $param = $$params[$j + 1];
1498             if ($param_name eq "Returns") {
1499                 $returns = &ExpandAbbreviations($symbol, $param);
1500             } else {
1501                 if ($param_name eq "Varargs") {
1502                     $param_name = "...";
1503                 }
1504                 $param = &ExpandAbbreviations($symbol, $param);
1505                 $params_desc .= "<varlistentry><term><parameter>$param_name</parameter>&nbsp;:</term>\n<listitem><simpara>$param</simpara></listitem></varlistentry>\n";
1506             }
1507         }
1509         # Signals have an implicit user_data parameter which we describe.
1510         if ($symbol_type eq "SIGNAL") {
1511             $params_desc .= "<varlistentry><term><parameter>user_data</parameter>&nbsp;:</term>\n<listitem><simpara>user data set when the signal handler was connected.</simpara></listitem></varlistentry>\n";
1512         }
1514         # Start a table if we need one.
1515         if ($params_desc || $returns) {
1516             $output .= <<EOF;
1517 <variablelist role="params">
1520             if ($params_desc ne "") {
1521 #               $output .= "<varlistentry><term>Parameters:</term><listitem></listitem></varlistentry>\n";
1522                 $output .= $params_desc;
1523             }
1525             # Output the returns info last.
1526             if ($returns) {
1527                 $output .= "<varlistentry><term><emphasis>Returns</emphasis>&nbsp;:</term><listitem><simpara>$returns</simpara></listitem></varlistentry>\n";
1528             }
1530             # Finish the table.
1531             $output .= "</variablelist>";
1532         }
1533     }
1534     return $output;
1538 #############################################################################
1539 # Function    : ParseStabilityLevel
1540 # Description : Parses a stability level and outputs a warning if it isn't
1541 #               valid.
1542 # Arguments   : $stability - the stability text.
1543 #               $file, $line - context for error message
1544 #               $message - description of where the level is from, to use in
1545 #               any error message.
1546 # Returns     : The parsed stability level string.
1547 #############################################################################
1549 sub ParseStabilityLevel {
1550     my ($stability, $file, $line, $message) = @_;
1552     $stability =~ s/^\s*//;
1553     $stability =~ s/\s*$//;
1554     if ($stability =~ m/^stable$/i) {
1555         $stability = "Stable";
1556     } elsif ($stability =~ m/^unstable$/i) {
1557         $stability = "Unstable";
1558     } elsif ($stability =~ m/^private$/i) {
1559         $stability = "Private";
1560     } else {
1561         &LogWarning ($file, $line, "$message is $stability.".
1562             "It should be one of these: Stable, Unstable, or Private.");
1563     }
1564     return $stability;
1568 #############################################################################
1569 # Function    : OutputSGMLFile
1570 # Description : Outputs the final DocBook file for one section.
1571 # Arguments   : $file - the name of the file.
1572 #               $title - the title from the $MODULE-sections.txt file, which
1573 #                 will be overridden by the title in the template file.
1574 #               $section_id - the SGML id to use for the toplevel tag.
1575 #               $includes - comma-separates list of include files added at top
1576 #                 of synopsis, with '<' '>' around them (if not already enclosed in "").
1577 #               $synopsis - reference to the DocBook for the Synopsis part.
1578 #               $details - reference to the DocBook for the Details part.
1579 #               $signal_synop - reference to the DocBook for the Signal Synopsis part
1580 #               $signal_desc - reference to the DocBook for the Signal Description part
1581 #               $args_synop - reference to the DocBook for the Arg Synopsis part
1582 #               $args_desc - reference to the DocBook for the Arg Description part
1583 #               $hierarchy - reference to the DocBook for the Object Hierarchy part
1584 #               $interfaces - reference to the DocBook for the Interfaces part
1585 #               $implementations - reference to the DocBook for the Known Implementations part
1586 #               $prerequisites - reference to the DocBook for the Prerequisites part
1587 #               $derived - reference to the DocBook for the Derived Interfaces part
1588 #               $file_objects - reference to an array of objects in this file
1589 #############################################################################
1591 sub OutputSGMLFile {
1592     my ($file, $title, $section_id, $includes, $synopsis, $details, $signals_synop, $signals_desc, $args_synop, $args_desc, $hierarchy, $interfaces, $implementations, $prerequisites, $derived, $file_objects) = @_;
1594     #print "Output sgml for file $file with title '$title'\n";
1595     
1596     # The edited title overrides the one from the sections file.
1597     my $new_title = $SymbolDocs{"$TMPL_DIR/$file:Title"};
1598     if (defined ($new_title) && $new_title !~ m/^\s*$/) {
1599         $title = $new_title;
1600         #print "Found title: $title\n";
1601     }
1602     my $short_desc = $SymbolDocs{"$TMPL_DIR/$file:Short_Description"};
1603     if (!defined ($short_desc) || $short_desc =~ m/^\s*$/) {
1604         $short_desc = "";
1605     } else {
1606         $short_desc = &ExpandAbbreviations("$title:Short_description",
1607                                            $short_desc);
1608         #print "Found short_desc: $short_desc";
1609     }
1610     my $long_desc = $SymbolDocs{"$TMPL_DIR/$file:Long_Description"};
1611     if (!defined ($long_desc) || $long_desc =~ m/^\s*$/) {
1612         $long_desc = "";
1613     } else {
1614         $long_desc = &ExpandAbbreviations("$title:Long_description",
1615                                           $long_desc);
1616         #print "Found long_desc: $long_desc";
1617     }
1618     my $see_also = $SymbolDocs{"$TMPL_DIR/$file:See_Also"};
1619     if (!defined ($see_also) || $see_also =~ m%^\s*(<para>)?\s*(</para>)?\s*$%) {
1620         $see_also = "";
1621     } else {
1622         $see_also = &ExpandAbbreviations("$title:See_Also", $see_also);
1623         #print "Found see_also: $see_also";
1624     }
1625     if ($see_also) {
1626         $see_also = "<refsect1 id=\"$section_id.see-also\">\n<title>See Also</title>\n$see_also\n</refsect1>\n";
1627     }
1628     my $stability = $SymbolDocs{"$TMPL_DIR/$file:Stability_Level"};
1629     if (!defined ($stability) || $stability =~ m/^\s*$/) {
1630         $stability = "";
1631     } else {
1632         $stability = &ParseStabilityLevel($stability, $file, $., "Section stability level");
1633         #print "Found stability: $stability";
1634     }
1635     if ($stability) {
1636         $stability = "<refsect1 id=\"$section_id.stability-level\">\n<title>Stability Level</title>\n$stability, unless otherwise indicated\n</refsect1>\n";
1637     } elsif ($DEFAULT_STABILITY) {
1638         $stability = "<refsect1 id=\"$section_id.stability-level\">\n<title>Stability Level</title>\n$DEFAULT_STABILITY, unless otherwise indicated\n</refsect1>\n";
1639     }
1641     my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) =
1642         gmtime (time);
1643     my $month = (qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec))[$mon];
1644     $year += 1900;
1646     my $include_output = "";
1647     my $include;
1648     foreach $include (split (/,/, $includes)) {
1649         if ($include =~ m/^\".+\"$/) {
1650             $include_output .= "#include ${include}\n";
1651         }
1652         else {
1653             $include =~ s/^\s+|\s+$//gs;
1654             $include_output .= "#include &lt;${include}&gt;\n";
1655         }
1656     }
1657     if ($include_output ne '') {
1658         $include_output = "\n$include_output\n";
1659     }
1661     my $old_sgml_file = "$SGML_OUTPUT_DIR/$file.$OUTPUT_FORMAT";
1662     my $new_sgml_file = "$SGML_OUTPUT_DIR/$file.$OUTPUT_FORMAT.new";
1664     open (OUTPUT, ">$new_sgml_file")
1665         || die "Can't create $new_sgml_file: $!";
1667     my $object_anchors = "";
1668     foreach my $object (@$file_objects) {
1669         next if ($object eq $section_id);
1670         my $id = CreateValidSGMLID($object);
1671         #print "Debug: Adding anchor for $object\n";
1672         $object_anchors .= "<anchor id=\"$id\"$empty_element_end";
1673     }
1675     # We used to output this, but is messes up our UpdateFileIfChanged code
1676     # since it changes every day (and it is only used in the man pages):
1677     # "<refentry id="$section_id" revision="$mday $month $year">"
1679     if (lc($OUTPUT_FORMAT) eq "xml") {
1680         print OUTPUT $doctype_header;
1681     }
1683     print OUTPUT <<EOF;
1684 <refentry id="$section_id">
1685 <refmeta>
1686 <refentrytitle role="top_of_page" id="$section_id.top_of_page">$title</refentrytitle>
1687 <manvolnum>3</manvolnum>
1688 <refmiscinfo>\U$MODULE\E Library</refmiscinfo>
1689 </refmeta>
1691 <refnamediv>
1692 <refname>$title</refname>
1693 <refpurpose>$short_desc</refpurpose>
1694 <!--[<xref linkend="desc" endterm="desc.title"/>]-->
1695 </refnamediv>
1696 $stability
1697 <refsynopsisdiv id="$section_id.synopsis" role="synopsis">
1698 <title role="synopsis.title">Synopsis</title>
1699 $object_anchors
1700 <synopsis>
1701 $include_output$${synopsis}</synopsis>
1702 </refsynopsisdiv>
1704 $$hierarchy
1705 $$prerequisites
1706 $$derived
1707 $$interfaces
1708 $$implementations
1709 $$args_synop
1710 $$signals_synop
1712 <refsect1 id="$section_id.description" role="desc">
1713 <title role="desc.title">Description</title>
1714 $long_desc
1715 </refsect1>
1717 <refsect1 id="$section_id.details" role="details">
1718 <title role="details.title">Details</title>
1719 $$details
1720 </refsect1>
1721 $$args_desc
1722 $$signals_desc
1724 $see_also
1725 </refentry>
1727     close (OUTPUT);
1729     return &UpdateFileIfChanged ($old_sgml_file, $new_sgml_file, 0);
1733 #############################################################################
1734 # Function    : OutputExtraFile
1735 # Description : Copies an "extra" DocBook file into the output directory,
1736 #               expanding abbreviations
1737 # Arguments   : $file - the source file.
1738 #############################################################################
1739 sub OutputExtraFile {
1740     my ($file) = @_;
1742     my $basename;
1744     ($basename = $file) =~ s!^.*/!!;
1746     my $old_sgml_file = "$SGML_OUTPUT_DIR/$basename";
1747     my $new_sgml_file = "$SGML_OUTPUT_DIR/$basename.new";
1749     my $contents;
1751     open(EXTRA_FILE, "<$file") || die "Can't open $file";
1753     {
1754         local $/;
1755         $contents = <EXTRA_FILE>;
1756     }
1758     open (OUTPUT, ">$new_sgml_file")
1759         || die "Can't create $new_sgml_file: $!";
1761     print OUTPUT &ExpandAbbreviations ("$basename file", $contents);
1762     close (OUTPUT);
1764     return &UpdateFileIfChanged ($old_sgml_file, $new_sgml_file, 0);
1766 #############################################################################
1767 # Function    : OutputBook
1768 # Description : Outputs the SGML entities that need to be included into the
1769 #               main SGML file for the module.
1770 # Arguments   : $book_top - the declarations of the entities, which are added
1771 #                 at the top of the main SGML file.
1772 #               $book_bottom - the references to the entities, which are
1773 #                 added in the main SGML file at the desired position.
1774 #############################################################################
1776 sub OutputBook {
1777     my ($book_top, $book_bottom) = @_;
1779     my $old_file = "$SGML_OUTPUT_DIR/$MODULE-doc.top";
1780     my $new_file = "$SGML_OUTPUT_DIR/$MODULE-doc.top.new";
1782     open (OUTPUT, ">$new_file")
1783         || die "Can't create $new_file: $!";
1784     print OUTPUT $book_top;
1785     close (OUTPUT);
1787     &UpdateFileIfChanged ($old_file, $new_file, 0);
1790     $old_file = "$SGML_OUTPUT_DIR/$MODULE-doc.bottom";
1791     $new_file = "$SGML_OUTPUT_DIR/$MODULE-doc.bottom.new";
1793     open (OUTPUT, ">$new_file")
1794         || die "Can't create $new_file: $!";
1795     print OUTPUT $book_bottom;
1796     close (OUTPUT);
1798     &UpdateFileIfChanged ($old_file, $new_file, 0);
1801     # If the main SGML file hasn't been created yet, we create it here.
1802     # The user can tweak it later.
1803     if ($MAIN_SGML_FILE && ! -e $MAIN_SGML_FILE) {
1804       open (OUTPUT, ">$MAIN_SGML_FILE")
1805         || die "Can't create $MAIN_SGML_FILE: $!";
1807       if (lc($OUTPUT_FORMAT) eq "xml") {
1808           print OUTPUT <<EOF;
1809 <?xml version="1.0"?>
1810 <!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
1811                "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd">
1812 <book id="index" xmlns:xi="http://www.w3.org/2003/XInclude">
1814       } else {
1815         print OUTPUT <<EOF;
1816 <!doctype book PUBLIC "-//Davenport//DTD DocBook V3.0//EN" [
1818         print OUTPUT $book_top;
1819         print OUTPUT <<EOF;
1821 <book id="index">
1823       }
1825 print OUTPUT <<EOF;
1826   <bookinfo>
1827     <title>$MODULE Reference Manual</title>
1828     <releaseinfo>
1829       for $MODULE [VERSION]
1830       The latest version of this documentation can be found on-line at
1831       <ulink role="online-location" url="http://[SERVER]/$MODULE/index.html">http://[SERVER]/$MODULE/</ulink>.
1832     </releaseinfo>
1833   </bookinfo>
1835   <chapter>
1836     <title>[Insert title here]</title>
1838       print OUTPUT $book_bottom;
1840       print OUTPUT <<EOF;
1841   </chapter>
1842 </book>
1845       close (OUTPUT);
1846     }
1850 #############################################################################
1851 # Function    : CreateValidSGMLID
1852 # Description : Creates a valid SGML 'id' from the given string.
1853 #               NOTE: SGML ids are case-insensitive, so we have a few special
1854 #                     cases to avoid clashes of ids.
1855 # Arguments   : $id - the string to be converted into a valid SGML id.
1856 #############################################################################
1858 sub CreateValidSGMLID {
1859     my ($id) = $_[0];
1861     # Append ":CAPS" to all all-caps identifiers
1863     # Special case, '_' would end up as '' so we use 'gettext-macro' instead.
1864     if ($id eq "_") { return "gettext-macro"; }
1866     if ($id !~ /[a-z]/) { $id .= ":CAPS" };
1868     $id =~ s/[_ ]/-/g;
1869     $id =~ s/[,\.]//g;
1870     $id =~ s/^-*//;
1871     $id =~ s/::/-/g;
1873     return $id;
1877 #############################################################################
1878 # Function    : CreateValidSGML
1879 # Description : This turns any chars which are used in SGML into entities,
1880 #               e.g. '<' into '&lt;'
1881 # Arguments   : $text - the text to turn into proper SGML.
1882 #############################################################################
1884 sub CreateValidSGML {
1885     my ($text) = @_;
1886     $text =~ s/&/&amp;/g;       # Do this first, or the others get messed up.
1887     $text =~ s/</&lt;/g;
1888     $text =~ s/>/&gt;/g;
1889     return $text;
1892 #############################################################################
1893 # Function    : ConvertSGMLChars
1894 # Description : This is used for text in source code comment blocks, to turn
1895 #               chars which are used in SGML into entities, e.g. '<' into
1896 #               '&lt;'. Depending on $SGML_MODE, this is done
1897 #               unconditionally or only if the character doesn't seem to be
1898 #               part of an SGML construct (tag or entity reference).
1899 # Arguments   : $text - the text to turn into proper SGML.
1900 #############################################################################
1902 sub ConvertSGMLChars {
1903     my ($symbol, $text) = @_;
1905     if ($SGML_MODE) {
1906         # For the SGML mode only convert to entities outside CDATA sections.
1907         return &ModifyXMLElements ($text, $symbol,
1908                                    "<!\\[CDATA\\[|<programlisting[^>]*>",
1909                                    \&ConvertSGMLCharsEndTag,
1910                                    \&ConvertSGMLCharsCallback);
1911     } else {
1912         # For the simple non-sgml mode, convert to entities everywhere.
1913         $text =~ s/&/&amp;/g;   # Do this first, or the others get messed up.
1914         $text =~ s/</&lt;/g;
1915         $text =~ s/>/&gt;/g;
1916         return $text;
1917     }
1921 sub ConvertSGMLCharsEndTag {
1922   if ($_[0] eq "<!\[CDATA\[") {
1923     return "]]>";
1924   } else {
1925     return "</programlisting>";
1926   }
1929 sub ConvertSGMLCharsCallback {
1930   my ($text, $symbol, $tag) = @_;
1932   if ($tag =~ m/^<programlisting/) {
1933     # We can handle <programlisting> specially here.
1934     return &ModifyXMLElements ($text, $symbol,
1935                                "<!\\[CDATA\\[",
1936                                \&ConvertSGMLCharsEndTag,
1937                                \&ConvertSGMLCharsCallback2);
1938   } elsif ($tag eq "") {
1939     # If we're not in CDATA convert to entities.
1940     $text =~ s/&(?![a-zA-Z#]+;)/&amp;/g;        # Do this first, or the others get messed up.
1941     $text =~ s/<(?![a-zA-Z\/!])/&lt;/g;
1942     $text =~ s/(?<![a-zA-Z0-9"'\/-])>/&gt;/g;
1944     # Handle "#include <xxxxx>"
1945     $text =~ s/#include(\s+)<([^>]+)>/#include$1&lt;$2&gt;/g;
1946   }
1948   return $text;
1951 sub ConvertSGMLCharsCallback2 {
1952   my ($text, $symbol, $tag) = @_;
1954   # If we're not in CDATA convert to entities.
1955   # We could handle <programlisting> differently, though I'm not sure it helps.
1956   if ($tag eq "") {
1957     $text =~ s/&(?![a-zA-Z#]+;)/&amp;/g;        # Do this first, or the others get messed up.
1958 #    $text =~ s/&/&amp;/g;      # Do this first, or the others get messed up.
1959     $text =~ s/<(?![a-zA-Z\/!])/&lt;/g;
1960     $text =~ s/(?<![a-zA-Z0-9"'\/-])>/&gt;/g;
1962     # Handle "#include <xxxxx>"
1963     $text =~ s/#include(\s+)<([^>]+)>/#include$1&lt;$2&gt;/g;
1964   }
1966   return $text;
1970 #############################################################################
1971 # Function    : ExpandAbbreviations
1972 # Description : This turns the abbreviations function(), macro(), @param,
1973 #               %constant, and #symbol into appropriate DocBook markup.
1974 #               CDATA sections and <programlisting> parts are skipped.
1975 # Arguments   : $symbol - the symbol being documented, for error messages.
1976 #               $text - the text to expand.
1977 #############################################################################
1979 sub ExpandAbbreviations {
1980   my ($symbol, $text) = @_;
1982   # Convert "|[" and "]|" into the start and end of program listing examples.
1983   $text =~ s%\|\[%<informalexample><programlisting>%g;
1984   $text =~ s%\]\|%</programlisting></informalexample>%g;
1986   # keep CDATA unmodified, preserve ulink tags (ideally we preseve all tags
1987   # as such)
1988   return &ModifyXMLElements ($text, $symbol,
1989                              "<!\\[CDATA\\[|<ulink[^>]*>|<programlisting[^>]*>",
1990                              \&ExpandAbbreviationsEndTag,
1991                              \&ExpandAbbreviationsCallback);
1995 # Returns the end tag corresponding to the given start tag.
1996 sub ExpandAbbreviationsEndTag {
1997   my ($start_tag) = @_;
1999   if ($start_tag eq "<!\[CDATA\[") {
2000     return "]]>";
2001   } elsif ($start_tag =~ m/<(\w+)/) {
2002     return "</$1>";
2003   }
2006 # Called inside or outside each CDATA or <programlisting> section.
2007 sub ExpandAbbreviationsCallback {
2008   my ($text, $symbol, $tag) = @_;
2010   if ($tag =~ m/^<programlisting/) {
2011     # Handle any embedded CDATA sections.
2012     return &ModifyXMLElements ($text, $symbol,
2013                                "<!\\[CDATA\\[",
2014                                \&ExpandAbbreviationsEndTag,
2015                                \&ExpandAbbreviationsCallback2);
2016   } elsif ($tag eq "") {
2017     # We are outside any CDATA or <programlisting> sections, so we expand
2018     # any gtk-doc abbreviations.
2020     # Convert 'function()' or 'macro()'.
2021     $text =~ s/(\w+)\s*\(\)/&MakeXRef($1, &tagify($1 . "()", "function"));/eg;
2023     # Convert '@param', but not '\@param'.
2024     #$text =~ s/\@(\w+((\.|->)\w+)*)/<parameter>$1<\/parameter>/g;
2025     $text =~ s/([^\\])\@(\w+((\.|->)\w+)*)/$1<parameter>$2<\/parameter>/g;
2026     $text =~ s/\\\@/\@/g;
2028     # Convert '%constant', but not '\%constant'.
2029     # Also allow negative numbers, e.g. %-1.
2030     #$text =~ s/\%(-?\w+)/&MakeXRef($1, &tagify($1, "literal"));/eg;
2031     $text =~ s/([^\\])\%(-?\w+)/$1.&MakeXRef($2, &tagify($2, "literal"));/eg;
2032     $text =~ s/\\\%/\%/g;
2034     # Convert '#symbol', but not '\#symbol'.
2035     #$text =~ s/#([\w\-:]+)/&MakeHashXRef($1, "type");/eg;
2036     $text =~ s/([^\\])#([\w\-:]+)/$1.&MakeHashXRef($2, "type");/eg;
2037     $text =~ s/\\#/#/g;
2038   }
2040   return $text;
2043 # This is called inside a <programlisting>
2044 sub ExpandAbbreviationsCallback2 {
2045   my ($text, $symbol, $tag) = @_;
2047   if ($tag eq "") {
2048     # We are inside a <programlisting> but outside any CDATA sections,
2049     # so we expand any gtk-doc abbreviations.
2050     # FIXME: why is this different from &ExpandAbbreviationsCallback(),
2051     #        why not just call it
2052     $text =~ s/#(\w+)/&MakeHashXRef($1, "");/eg;
2053   }
2055   return $text;
2058 sub MakeHashXRef {
2059     my ($symbol, $tag) = @_;;
2060     my $text = $symbol;
2062     # Check for things like '#include', '#define', and skip them.
2063     if ($PreProcessorDirectives{$symbol}) {
2064       return "#$symbol";
2065     }
2067     # Get rid of any special '-struct' suffix.
2068     $text =~ s/-struct$//;
2070     # If the symbol is in the form "Object::signal", then change the symbol to
2071     # "Object-signal" and use "signal" as the text.
2072     if ($symbol =~ s/::/-/) {
2073       $text = "\"$'\"";
2074     }
2076     # If the symbol is in the form "Object:property", then change the symbol to
2077     # "Object--property" and use "property" as the text.
2078     if ($symbol =~ s/:/--/) {
2079       $text = "\"$'\"";
2080     }
2082     if ($tag ne "") {
2083       $text = tagify ($text, $tag);
2084     }
2086     return &MakeXRef($symbol, $text);
2090 #############################################################################
2091 # Function    : ModifyXMLElements
2092 # Description : Looks for given XML element tags within the text, and calls
2093 #               the callback on pieces of text inside & outside those elements.
2094 #               Used for special handling of text inside things like CDATA
2095 #               and <programlisting>.
2096 # Arguments   : $text - the text.
2097 #               $symbol - the symbol currently being documented (only used for
2098 #                      error messages).
2099 #               $start_tag_regexp - the regular expression to match start tags.
2100 #                      e.g. "<!\\[CDATA\\[|<programlisting[^>]*>" to match
2101 #                      CDATA sections or programlisting elements.
2102 #               $end_tag_func - function which is passed the matched start tag
2103 #                      and should return the appropriate end tag string.
2104 #               $callback - callback called with each part of the text. It is
2105 #                      called with a piece of text, the symbol being
2106 #                      documented, and the matched start tag or "" if the text
2107 #                      is outside the XML elements being matched.
2108 #############################################################################
2109 sub ModifyXMLElements {
2110     my ($text, $symbol, $start_tag_regexp, $end_tag_func, $callback) = @_;
2111     my ($before_tag, $start_tag, $end_tag_regexp, $end_tag);
2112     my $result = "";
2114     while ($text =~ m/$start_tag_regexp/s) {
2115       $before_tag = $`; # Prematch for last successful match string
2116       $start_tag = $&;  # Last successful match
2117       $text = $';       # Postmatch for last successful match string
2119       $result .= &$callback ($before_tag, $symbol, "");
2120       $result .= $start_tag;
2122       # get the mathing end-tag for current tag
2123       $end_tag_regexp = &$end_tag_func ($start_tag);
2125       if ($text =~ m/$end_tag_regexp/s) {
2126         $before_tag = $`;
2127         $end_tag = $&;
2128         $text = $';
2130         $result .= &$callback ($before_tag, $symbol, $start_tag);
2131         $result .= $end_tag;
2132       } else {
2133         &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2134             "Can't find tag end: $end_tag_regexp in docs for: $symbol.");
2135         # Just assume it is all inside the tag.
2136         $result .= &$callback ($text, $symbol, $start_tag);
2137         $text = "";
2138       }
2139     }
2141     # Handle any remaining text outside the tags.
2142     $result .= &$callback ($text, $symbol, "");
2144     return $result;
2147 sub noop {
2148   return $_[0];
2151 # Adds a tag around some text.
2152 # e.g tagify("Text", "literal") => "<literal>Text</literal>".
2153 sub tagify {
2154    my ($text, $elem) = @_;
2155    return "<" . $elem . ">" . $text . "</" . $elem . ">";
2159 #############################################################################
2160 # Function    : MakeXRef
2161 # Description : This returns a cross-reference link to the given symbol.
2162 #               Though it doesn't try to do this for a few standard C types
2163 #               that it knows won't be in the documentation.
2164 # Arguments   : $symbol - the symbol to try to create a XRef to.
2165 #               $text - text text to put inside the XRef, defaults to $symbol
2166 #############################################################################
2168 sub MakeXRef {
2169     my ($symbol, $text) = ($_[0], $_[1]);
2170     if (!defined($text)) {
2171         $text = $symbol;
2173         # Get rid of special '-struct' suffix.
2174         $text =~ s/-struct$//;
2175     }
2177     #print "Getting type link for $symbol -> $text\n";
2179     my $symbol_id = &CreateValidSGMLID ($symbol);
2180     return "<link linkend=\"$symbol_id\">$text</link>";
2184 #############################################################################
2185 # Function    : MakeIndexterms
2186 # Description : This returns a indexterm elements for the given symbol
2187 # Arguments   : $symbol - the symbol to create indexterms for
2188 #############################################################################
2190 sub MakeIndexterms {
2191   my ($symbol, $id) = @_;
2192   my $terms =  "";
2194   if (exists $Deprecated{$symbol}) {
2195       $terms .= "<indexterm zone=\"$id\" role=\"deprecated\"><primary>$symbol</primary></indexterm>"
2196   }
2197   if (exists $Since{$symbol}) {
2198      my $since = $Since{$symbol};
2199      $since =~ s/^\s+//;
2200      $since =~ s/\s+$//;
2201      if ($since ne "") {
2202          $terms .= "<indexterm zone=\"$id\" role=\"$since\"><primary>$symbol</primary></indexterm>";
2203      }
2204   }
2205   if ($terms eq "") {
2206      $terms .= "<indexterm zone=\"$id\"><primary>$symbol</primary></indexterm>";
2207   }
2209   return $terms;
2212 #############################################################################
2213 # Function    : MakeDeprecationNote
2214 # Description : This returns a deprecation warning for the given symbol.
2215 # Arguments   : $symbol - the symbol to try to create a warning for.
2216 #############################################################################
2218 sub MakeDeprecationNote {
2219     my ($symbol) = $_[0];
2220     my $desc = "";
2221     my $note = "";
2222     if (exists $Deprecated{$symbol}) {
2223         $desc .= "<warning>";
2225         if ($Deprecated{$symbol} =~ /^\s*([0-9\.]+)\s*:/) {
2226                 $desc .= "<para><literal>$symbol</literal> has been deprecated since version $1 and should not be used in newly-written code.";
2227         } else {
2228                 $desc .= "<para><literal>$symbol</literal> is deprecated and should not be used in newly-written code.";
2229         }
2230         if ($Deprecated{$symbol} ne "") {
2231             $note = &ExpandAbbreviations($symbol, $Deprecated{$symbol});
2232             $note =~ s/^\s*([0-9\.]+)\s*:\s*//;
2233             $note =~ s/^\s+//;
2234             $note =~ s/\s+$//;
2235             $note =~ s%\n{2,}%\n</para>\n<para>\n%g;
2236             $desc .= " " . $note;
2237         }
2238         $desc .= "</para></warning>\n";
2239     }
2240     return $desc;
2243 #############################################################################
2244 # Function    : MakeConditionDescription
2245 # Description : This returns a sumary of conditions for the given symbol.
2246 # Arguments   : $symbol - the symbol to try to create the sumary.
2247 #############################################################################
2249 sub MakeConditionDescription {
2250     my ($symbol) = $_[0];
2251     my $desc = "";
2253     if (exists $Deprecated{$symbol}) {
2254         if ($desc ne "") {
2255             $desc .= "|";
2256         }
2258         if ($Deprecated{$symbol} =~ /^\s*(.*?)\s*$/) {
2259                 $desc .= "deprecated:$1";
2260         } else {
2261                 $desc .= "deprecated";
2262         }
2263     }
2265     if (exists $Since{$symbol}) {
2266         if ($desc ne "") {
2267             $desc .= "|";
2268         }
2270         if ($Since{$symbol} =~ /^\s*(.*?)\s*$/) {
2271                 $desc .= "since:$1";
2272         } else {
2273                 $desc .= "since";
2274         }
2275     }
2277     if (exists $StabilityLevel{$symbol}) {
2278         if ($desc ne "") {
2279             $desc .= "|";
2280         }
2281         $desc .= "stability:".$StabilityLevel{$symbol};
2282     }
2284     if ($desc ne "") {
2285         $desc=" condition=\"".$desc."\"";
2286         #print "condition for '$symbol' = '$desc'\n";
2287     }
2288     return $desc;
2291 #############################################################################
2292 # Function    : GetHierarchy
2293 # Description : Returns the DocBook output describing the ancestors and
2294 #               immediate children of a GObject subclass. It uses the
2295 #               global @Objects and @ObjectLevels arrays to walk the tree.
2296 # Arguments   : $object - the GtkObject subclass.
2297 #############################################################################
2299 sub GetHierarchy {
2300     my ($object) = @_;
2302     # Find object in the objects array.
2303     my $found = 0;
2304     my @children = ();
2305     my $i;
2306     my $level;
2307     my $j;
2308     for ($i = 0; $i < @Objects; $i++) {
2309         if ($found) {
2310             if ($ObjectLevels[$i] <= $level) {
2311             last;
2312         }
2313             elsif ($ObjectLevels[$i] == $level + 1) {
2314                 push (@children, $Objects[$i]);
2315             }
2316         }
2317         elsif ($Objects[$i] eq $object) {
2318             $found = 1;
2319             $j = $i;
2320             $level = $ObjectLevels[$i];
2321         }
2322     }
2323     if (!$found) {
2324         return "";
2325     }
2327     # Walk up the hierarchy, pushing ancestors onto the ancestors array.
2328     my @ancestors = ();
2329     push (@ancestors, $object);
2330     #print "Level: $level\n";
2331     while ($level > 1) {
2332         $j--;
2333         if ($ObjectLevels[$j] < $level) {
2334             push (@ancestors, $Objects[$j]);
2335             $level = $ObjectLevels[$j];
2336             #print "Level: $level\n";
2337         }
2338     }
2340     # Output the ancestors list, indented and with links.
2341     my $hierarchy = "<synopsis>\n";
2342     $level = 0;
2343     for ($i = $#ancestors; $i >= 0; $i--) {
2344         my $link_text;
2345         # Don't add a link to the current widget, i.e. when i == 0.
2346         if ($i > 0) {
2347             my $ancestor_id = &CreateValidSGMLID ($ancestors[$i]);
2348             $link_text = "<link linkend=\"$ancestor_id\">$ancestors[$i]</link>";
2349         } else {
2350             $link_text = "$ancestors[$i]";
2351         }
2352         if ($level == 0) {
2353             $hierarchy .= "  $link_text\n";
2354         } else {
2355 #           $hierarchy .= ' ' x ($level * 6 - 3) . "|\n";
2356             $hierarchy .= ' ' x ($level * 6 - 3) . "+----$link_text\n";
2357         }
2358         $level++;
2359     }
2360     for ($i = 0; $i <= $#children; $i++) {
2361       my $id = &CreateValidSGMLID ($children[$i]);
2362       my $link_text = "<link linkend=\"$id\">$children[$i]</link>";
2363       $hierarchy .= ' ' x ($level * 6 - 3) . "+----$link_text\n";
2364     }
2365     $hierarchy .= "</synopsis>\n";
2367     return $hierarchy;
2371 #############################################################################
2372 # Function    : GetInterfaces
2373 # Description : Returns the DocBook output describing the interfaces
2374 #               implemented by a class. It uses the global %Interfaces hash.
2375 # Arguments   : $object - the GtkObject subclass.
2376 #############################################################################
2378 sub GetInterfaces {
2379     my ($object) = @_;
2380     my $text = "";
2381     my $i;
2383     # Find object in the objects array.
2384     if (exists($Interfaces{$object})) {
2385         my @ifaces = split(' ', $Interfaces{$object});
2386         $text = <<EOF;
2387 <para>
2388 $object implements
2390         for ($i = 0; $i <= $#ifaces; $i++) {
2391             my $id = &CreateValidSGMLID ($ifaces[$i]);
2392             $text .= " <link linkend=\"$id\">$ifaces[$i]</link>";
2393             if ($i < $#ifaces - 1) {
2394                 $text .= ', ';
2395             }
2396             elsif ($i < $#ifaces) {
2397                 $text .= ' and ';
2398             }
2399             else {
2400                 $text .= '.';
2401             }
2402         }
2403         $text .= <<EOF;
2404 </para>
2406     }
2408     return $text;
2411 #############################################################################
2412 # Function    : GetImplementations
2413 # Description : Returns the DocBook output describing the implementations
2414 #               of an interface. It uses the global %Interfaces hash.
2415 # Arguments   : $object - the GtkObject subclass.
2416 #############################################################################
2418 sub GetImplementations {
2419     my ($object) = @_;
2420     my @impls = ();
2421     my $text = "";
2422     my $i;
2423     foreach my $key (keys %Interfaces) {
2424         if ($Interfaces{$key} =~ /\b$object\b/) {
2425             push (@impls, $key);
2426         }
2427     }
2428     if ($#impls >= 0) {
2429         $text = <<EOF;
2430 <para>
2431 $object is implemented by
2433         for ($i = 0; $i <= $#impls; $i++) {
2434             my $id = &CreateValidSGMLID ($impls[$i]);
2435             $text .= " <link linkend=\"$id\">$impls[$i]</link>";
2436             if ($i < $#impls - 1) {
2437                 $text .= ', ';
2438             }
2439             elsif ($i < $#impls) {
2440                 $text .= ' and ';
2441             }
2442             else {
2443                 $text .= '.';
2444             }
2445         }
2446         $text .= <<EOF;
2447 </para>
2449     }
2450     return $text;
2454 #############################################################################
2455 # Function    : GetPrerequisites
2456 # Description : Returns the DocBook output describing the prerequisites
2457 #               of an interface. It uses the global %Prerequisites hash.
2458 # Arguments   : $iface - the interface.
2459 #############################################################################
2461 sub GetPrerequisites {
2462     my ($iface) = @_;
2463     my $text = "";
2464     my $i;
2466     if (exists($Prerequisites{$iface})) {
2467         $text = <<EOF;
2468 <para>
2469 $iface requires
2471         my @prereqs = split(' ', $Prerequisites{$iface});
2472         for ($i = 0; $i <= $#prereqs; $i++) {
2473             my $id = &CreateValidSGMLID ($prereqs[$i]);
2474             $text .= " <link linkend=\"$id\">$prereqs[$i]</link>";
2475             if ($i < $#prereqs - 1) {
2476                 $text .= ', ';
2477             }
2478             elsif ($i < $#prereqs) {
2479                 $text .= ' and ';
2480             }
2481             else {
2482                 $text .= '.';
2483             }
2484         }
2485         $text .= <<EOF;
2486 </para>
2488     }
2489     return $text;
2492 #############################################################################
2493 # Function    : GetDerived
2494 # Description : Returns the DocBook output describing the derived interfaces
2495 #               of an interface. It uses the global %Prerequisites hash.
2496 # Arguments   : $iface - the interface.
2497 #############################################################################
2499 sub GetDerived {
2500     my ($iface) = @_;
2501     my $text = "";
2502     my $i;
2504     my @derived = ();
2505     foreach my $key (keys %Prerequisites) {
2506         if ($Prerequisites{$key} =~ /\b$iface\b/) {
2507             push (@derived, $key);
2508         }
2509     }
2510     if ($#derived >= 0) {
2511         $text = <<EOF;
2512 <para>
2513 $iface is required by
2515         for ($i = 0; $i <= $#derived; $i++) {
2516             my $id = &CreateValidSGMLID ($derived[$i]);
2517             $text .= " <link linkend=\"$id\">$derived[$i]</link>";
2518             if ($i < $#derived - 1) {
2519                 $text .= ', ';
2520             }
2521             elsif ($i < $#derived) {
2522                 $text .= ' and ';
2523             }
2524             else {
2525                 $text .= '.';
2526             }
2527         }
2528         $text .= <<EOF;
2529 </para>
2531     }
2532     return $text;
2536 #############################################################################
2537 # Function    : GetSignals
2538 # Description : Returns the synopsis and detailed description DocBook output
2539 #               for the signal handlers of a given GtkObject subclass.
2540 # Arguments   : $object - the GtkObject subclass, e.g. 'GtkButton'.
2541 #############################################################################
2543 sub GetSignals {
2544     my ($object) = @_;
2545     my $synop = "";
2546     my $desc = "";
2548     my $i;
2549     for ($i = 0; $i <= $#SignalObjects; $i++) {
2550         if ($SignalObjects[$i] eq $object) {
2551             #print "Found signal: $SignalNames[$i]\n";
2552             my $name = $SignalNames[$i];
2553             my $symbol = "${object}::${name}";
2554             my $id = &CreateValidSGMLID ("$object-$name");
2556             my $pad = ' ' x (46 - length($name));
2557             $synop .= "  &quot;<link linkend=\"$id\">$name</link>&quot;$pad ";
2559             $desc .= "<refsect2 id=\"$id\"><title>The <literal>&quot;$name&quot;</literal> signal</title>\n";
2560             $desc .= MakeIndexterms($symbol, $id);
2561             $desc .= "<programlisting>";
2563             $SignalReturns[$i] =~ m/\s*(const\s+)?(\w+)\s*(\**)/;
2564             my $type_modifier = defined($1) ? $1 : "";
2565             my $type = $2;
2566             my $pointer = $3;
2567             my $xref = &MakeXRef ($type);
2569             my $ret_type_len = length ($type_modifier) + length ($pointer)
2570                 + length ($type);
2571             my $ret_type_output = "$type_modifier$xref$pointer"
2572                 . (' ' x ($RETURN_TYPE_FIELD_WIDTH - $ret_type_len));
2574             $desc  .= "${ret_type_output}user_function " . &MakeReturnField("") . " (";
2576             my $sourceparams = $SourceSymbolParams{$symbol};
2577             my @params = split ("\n", $SignalPrototypes[$i]);
2578             my $j;
2579             my $l;
2580             my $type_len = length("gpointer");
2581             my $name_len = length("user_data");
2582             # do two passes, the first one is to calculate padding
2583             for ($l = 0; $l < 2; $l++) {
2584                 for ($j = 0; $j <= $#params; $j++) {
2585                     # allow alphanumerics, '_', '[' & ']' in param names
2586                     if ($params[$j] =~ m/^\s*(\w+)\s*(\**)\s*([\w\[\]]+)\s*$/) {
2587                         $type = $1;
2588                         $pointer = $2;
2589                         if (defined($sourceparams)) {
2590                             $name = $$sourceparams[2 * $j];
2591                         }
2592                         else {
2593                             $name = $3;
2594                         }
2595                         if (!defined($name)) {
2596                             $name = "arg$j";
2597                         }
2598                         if ($l == 0) {
2599                             if (length($type) + length($pointer) > $type_len) {
2600                                 $type_len = length($type) + length($pointer);
2601                             }
2602                             if (length($name) > $name_len) {
2603                                 $name_len = length($name);
2604                             }
2605                         }
2606                         else {
2607                             $xref = &MakeXRef ($type);
2608                             $pad = ' ' x ($type_len - length($type) - length($pointer));
2609                             $desc .= "$xref$pad $pointer$name,\n";
2610                             $desc .= (' ' x ($SYMBOL_FIELD_WIDTH + $RETURN_TYPE_FIELD_WIDTH));
2611                         }
2612                     } else {
2613                         &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2614                              "Can't parse arg: $params[$j]\nArgs:$SignalPrototypes[$i]");
2615                     }
2616                 }
2617             }
2618             $xref = &MakeXRef ("gpointer");
2619             $pad = ' ' x ($type_len - length("gpointer"));
2620             $desc  .= "$xref$pad user_data)";
2622             my $flags = $SignalFlags[$i];
2623             my $flags_string = "";
2625             if (defined ($flags)) {
2626               if ($flags =~ m/f/) {
2627                 $flags_string = "Run First";
2628               }
2629               elsif ($flags =~ m/l/) {
2630                 $flags_string = "Run Last";
2631               }
2632               elsif ($flags =~ m/c/) {
2633                 $flags_string = "Cleanup";
2634               }
2635               if ($flags =~ m/r/) {
2636                 if ($flags_string) { $flags_string .= " / "; }
2637                 $flags_string .= "No Recursion";
2638               }
2639               if ($flags =~ m/d/) {
2640                 if ($flags_string) { $flags_string .= " / "; }
2641                 $flags_string .= "Has Details";
2642               }
2643               if ($flags =~ m/a/) {
2644                 if ($flags_string) { $flags_string .= " / "; }
2645                 $flags_string .= "Action";
2646               }
2647               if ($flags =~ m/h/) {
2648                 if ($flags_string) { $flags_string .= " / "; }
2649                 $flags_string .= "No Hooks";
2650               }
2651             }
2653             if ($flags_string)
2654               {
2655                 $synop .= ": $flags_string\n";
2657                 $pad = ' ' x (5 + $name_len - length("user_data"));
2658                 $desc  .= "$pad : $flags_string</programlisting>\n";
2659               }
2660             else
2661               {
2662                 $synop .= "\n";
2663                 $desc  .= "</programlisting>\n";
2664               }
2666             my $parameters = &OutputParamDescriptions ("SIGNAL", $symbol);
2667             my $parameters_output = 0;
2669             $AllSymbols{$symbol} = 1;
2670             if (defined ($SymbolDocs{$symbol})) {
2671                 my $symbol_docs = &ExpandAbbreviations($symbol, $SymbolDocs{$symbol});
2673                 # Try to insert the parameter table at the author's desired
2674                 # position. Otherwise we need to tag it onto the end.
2675                 if ($symbol_docs =~ s/<!--PARAMETERS-->/$parameters/) {
2676                   $parameters_output = 1;
2677                 }
2678                 $desc .= $symbol_docs;
2680                 if (!IsEmptyDoc($SymbolDocs{$symbol})) {
2681                     $AllDocumentedSymbols{$symbol} = 1;
2682                 }
2683             }
2685             if ($parameters_output == 0) {
2686                 $desc .= $parameters;
2687               }
2689             if (exists $Since{$symbol}) {
2690               $desc .= "<para>Since $Since{$symbol}</para>";
2691             }
2692             if (exists $StabilityLevel{$symbol}) {
2693               $desc .= "<para>Stability Level: $StabilityLevel{$symbol}</para>";
2694             }
2695             $desc .= "</refsect2>";
2696         }
2697     }
2698     return ($synop, $desc);
2702 #############################################################################
2703 # Function    : GetArgs
2704 # Description : Returns the synopsis and detailed description DocBook output
2705 #               for the Args of a given GtkObject subclass.
2706 # Arguments   : $object - the GtkObject subclass, e.g. 'GtkButton'.
2707 #############################################################################
2709 sub GetArgs {
2710     my ($object) = @_;
2711     my $synop = "";
2712     my $desc = "";
2713     my $child_synop = "";
2714     my $child_desc = "";
2715     my $style_synop = "";
2716     my $style_desc = "";
2718     my $i;
2719     for ($i = 0; $i <= $#ArgObjects; $i++) {
2720         if ($ArgObjects[$i] eq $object) {
2721             #print "Found arg: $ArgNames[$i]\n";
2722             my $name = $ArgNames[$i];
2723             # Remember only one colon so we don't clash with signals.
2724             my $symbol = "${object}:${name}";
2725             # I've used two dashes here for the same reason.
2726             my $id = &CreateValidSGMLID ("$object--$name");
2728             my $type = $ArgTypes[$i];
2729             my $type_output;
2730             my $range = $ArgRanges[$i];
2731             my $range_output = CreateValidSGML($range);
2732             my $default = $ArgDefaults[$i];
2733             my $default_output = CreateValidSGML($default);
2735             if ($type eq "GtkString") {
2736                 $type = "char*";
2737             }
2738             if ($type eq "GtkSignal") {
2739                 $type = "GtkSignalFunc, gpointer";
2740                 $type_output = &MakeXRef ("GtkSignalFunc") . ", "
2741                     . &MakeXRef ("gpointer");
2742             } elsif ($type =~ m/^(\w+)\*$/) {
2743                 $type_output = &MakeXRef ($1) . "*";
2744             } else {
2745                 $type_output = &MakeXRef ($type);
2746             }
2748             my $flags = $ArgFlags[$i];
2749             my $flags_string = "";
2751             if ($flags =~ m/r/) {
2752                 $flags_string = "Read";
2753             }
2754             if ($flags =~ m/w/) {
2755                 if ($flags_string) { $flags_string .= " / "; }
2756                 $flags_string .= "Write";
2757             }
2758             if ($flags =~ m/x/) {
2759                 if ($flags_string) { $flags_string .= " / "; }
2760                 $flags_string .= "Construct";
2761             }
2762             if ($flags =~ m/X/) {
2763                 if ($flags_string) { $flags_string .= " / "; }
2764                 $flags_string .= "Construct Only";
2765             }
2767             $AllSymbols{$symbol} = 1;
2768             my $blurb;
2769             if (defined($SymbolDocs{$symbol}) &&
2770                 !IsEmptyDoc($SymbolDocs{$symbol})) {
2771                 $blurb = &ExpandAbbreviations($symbol, $SymbolDocs{$symbol});
2772                 $AllDocumentedSymbols{$symbol} = 1;
2773             }
2774             else {
2775                 if (!($ArgBlurbs[$i] eq "")) {
2776                     $AllDocumentedSymbols{$symbol} = 1;
2777                 }
2778                 $blurb = "<para>" . &CreateValidSGML ($ArgBlurbs[$i]) . "</para>";
2779             }
2781             my $pad1 = " " x (24 - length ($name));
2782             my $pad2 = " " x (20 - length ($type));
2783             my $kind = "";
2785             if ($flags =~ m/c/) {
2786                 $kind = "child property";
2787             }
2788             elsif ($flags =~ m/s/) {
2789                 $kind = "style property";
2790             }
2791             else {
2792                 $kind = "property";
2793             }
2795             my $arg_synop = "  &quot;<link linkend=\"$id\">$name</link>&quot;$pad1 $type_output $pad2 : $flags_string\n";
2796             my $arg_desc = "<refsect2 id=\"$id\"><title>The <literal>&quot;$name&quot;</literal> $kind</title>\n";
2797             $arg_desc .= MakeIndexterms($symbol, $id);
2798             $arg_desc .= "<programlisting>  &quot;$name&quot;$pad1 $type_output $pad2 : $flags_string</programlisting>\n";
2799             $arg_desc .= $blurb;
2800             if ($range ne "") {
2801                 $arg_desc .= "<para>Allowed values: $range_output</para>\n";
2802             }
2803             if ($default ne "") {
2804                 $arg_desc .= "<para>Default value: $default_output</para>\n";
2805             }
2806             if (exists $Since{$symbol}) {
2807               $arg_desc .= "<para>Since $Since{$symbol}</para>\n";
2808             }
2809             if (exists $StabilityLevel{$symbol}) {
2810               $arg_desc .= "<para>Stability Level $StabilityLevel{$symbol}</para>\n";
2811             }
2812             $arg_desc .= "</refsect2>\n";
2814             if ($flags =~ m/c/) {
2815                 $child_synop .= $arg_synop;
2816                 $child_desc .= $arg_desc;
2817             }
2818             elsif ($flags =~ m/s/) {
2819                 $style_synop .= $arg_synop;
2820                 $style_desc .= $arg_desc;
2821             }
2822             else {
2823                 $synop .= $arg_synop;
2824                 $desc .= $arg_desc;
2825             }
2826         }
2827     }
2828     return ($synop, $child_synop, $style_synop, $desc, $child_desc, $style_desc);
2832 #############################################################################
2833 # Function    : ReadSourceDocumentation
2834 # Description : This reads in the documentation embedded in comment blocks
2835 #               in the source code (for Gnome).
2837 #               Parameter descriptions override any in the template files.
2838 #               Function descriptions are placed before any description from
2839 #               the template files.
2841 #               It recursively descends the source directory looking for .c
2842 #               files and scans them looking for specially-formatted comment
2843 #               blocks.
2845 # Arguments   : $source_dir - the directory to scan.
2846 #############m###############################################################
2848 sub ReadSourceDocumentation {
2849     my ($source_dir) = @_;
2850     my ($file, $dir, @suffix_list, $suffix);
2851     #print "Scanning source directory: $source_dir\n";
2853     # This array holds any subdirectories found.
2854     my (@subdirs) = ();
2856     @suffix_list = split (/,/, $SOURCE_SUFFIXES);
2858     opendir (SRCDIR, $source_dir)
2859         || die "Can't open source directory $source_dir: $!";
2861     foreach $file (readdir (SRCDIR)) {
2862       if ($file =~ /^\./) {
2863         next;
2864       } elsif (-d "$source_dir/$file") {
2865         push (@subdirs, $file);
2866       } elsif (@suffix_list) {
2867         foreach $suffix (@suffix_list) {
2868           if ($file =~ m/\.\Q${suffix}\E$/) {
2869             &ScanSourceFile ("$source_dir/$file");
2870           }
2871         }
2872       } elsif ($file =~ m/\.[ch]$/) {
2873         &ScanSourceFile ("$source_dir/$file");
2874       }
2875     }
2876     closedir (SRCDIR);
2878     # Now recursively scan the subdirectories.
2879     foreach $dir (@subdirs) {
2880         next if ($IGNORE_FILES =~ m/(\s|^)\Q${dir}\E(\s|$)/);
2881         &ReadSourceDocumentation ("$source_dir/$dir");
2882     }
2886 #############################################################################
2887 # Function    : ScanSourceFile
2888 # Description : Scans one source file looking for specially-formatted comment
2889 #               blocks. Later &MergeSourceDocumentation is used to merge any
2890 #               documentation found with the documentation already read in
2891 #               from the template files.
2893 # Arguments   : $file - the file to scan.
2894 #############################################################################
2896 sub ScanSourceFile {
2897     my ($file) = @_;
2898     my $basename;
2900     if ($file =~ m/^.*[\/\\]([^\/\\]*)$/) {
2901         $basename = $1;
2902     } else {
2903         &LogWarning ($file, 1, "Can't find basename for this filename.");
2904         $basename = $file;
2905     }
2907     # Check if the basename is in the list of files to ignore.
2908     if ($IGNORE_FILES =~ m/(\s|^)\Q${basename}\E(\s|$)/) {
2909         return;
2910     }
2912     #print "DEBUG: Scanning $file\n";
2914     open (SRCFILE, $file)
2915         || die "Can't open $file: $!";
2916     my $in_comment_block = 0;
2917     my $symbol;
2918     my ($in_description, $in_return, $in_since, $in_stability, $in_deprecated);
2919     my ($description, $return_desc, $return_start, $return_style);
2920     my ($since_desc, $stability_desc, $deprecated_desc);
2921     my $current_param;
2922     my $ignore_broken_returns;
2923     my @params;
2924     while (<SRCFILE>) {
2925         # Look for the start of a comment block.
2926         if (!$in_comment_block) {
2927             if (m%^\s*/\*.*\*/%) {
2928                 #one-line comment - not gtkdoc
2929             } elsif (m%^\s*/\*\*\s%) {
2930                 #print "Found comment block start\n";
2932                 $in_comment_block = 1;
2934                 # Reset all the symbol data.
2935                 $symbol = "";
2936                 $in_description = 0;
2937                 $in_return = 0;
2938                 $in_since = 0;
2939                 $in_deprecated = 0;
2940                 $in_stability = 0;
2941                 $description = "";
2942                 $return_desc = "";
2943                 $return_style = "";
2944                 $since_desc = "";
2945                 $deprecated_desc = "";
2946                 $stability_desc = "";
2947                 $current_param = -1;
2948                 $ignore_broken_returns = 0;
2949                 @params = ();
2950             }
2951             next;
2952         }
2954         # We're in a comment block. Check if we've found the end of it.
2955         if (m%^\s*\*+/%) {
2956             if (!$symbol) {
2957                 &LogWarning ($file, $., "Symbol name not found at the start of the comment block.");
2958             } else {
2959                 # Add the return value description onto the end of the params.
2960                 if ($return_desc) {
2961                     push (@params, "Returns");
2962                     push (@params, $return_desc);
2963                     if ($return_style eq 'broken') {
2964                         &LogWarning ($file, $., "Free-form return value description in $symbol. Use `Returns:' to avoid ambiguities.");
2965                     }
2966                 }
2967                 # Convert special SGML characters
2968                 $description = &ConvertSGMLChars ($symbol, $description);
2969                 my $k;
2970                 for ($k = 1; $k <= $#params; $k += 2) {
2971                     $params[$k] = &ConvertSGMLChars ($symbol, $params[$k]);
2972                 }
2974                 # Handle Section docs
2975                 if ($symbol =~ m/SECTION:\s*(.*)/) {
2976                     my $real_symbol=$1;
2977                     my $key;
2979                     #print "SECTION DOCS found in source for : '$real_symbol'\n";
2980                     $ignore_broken_returns = 1;
2981                     for ($k = 0; $k <= $#params; $k += 2) {
2982                         #print "   '".$params[$k]."'\n";
2983                         $params[$k] = "\L$params[$k]";
2984                         undef $key;
2985                         if ($params[$k] eq "short_description") {
2986                             $key = "$TMPL_DIR/$real_symbol:Short_Description";
2987                         } elsif ($params[$k] eq "see_also") {
2988                             $key = "$TMPL_DIR/$real_symbol:See_Also";
2989                         } elsif ($params[$k] eq "title") {
2990                             $key = "$TMPL_DIR/$real_symbol:Title";
2991                         } elsif ($params[$k] eq "stability") {
2992                             $key = "$TMPL_DIR/$real_symbol:Stability_Level";
2993                         } elsif ($params[$k] eq "section_id") {
2994                             $key = "$TMPL_DIR/$real_symbol:Section_Id";
2995                         } elsif ($params[$k] eq "include") {
2996                             $key = "$TMPL_DIR/$real_symbol:Include";
2997                         }
2998                         if (defined($key)) {
2999                             $SourceSymbolDocs{$key}=$params[$k+1];
3000                             $SourceSymbolSourceFile{$key} = $file;
3001                             $SourceSymbolSourceLine{$key} = $.;
3002                         }
3003                     }
3004                     $SourceSymbolDocs{"$TMPL_DIR/$real_symbol:Long_Description"}=$description;
3005                     $SourceSymbolSourceFile{"$TMPL_DIR/$real_symbol:Long_Description"} = $file;
3006                     $SourceSymbolSourceLine{"$TMPL_DIR/$real_symbol:Long_Description"} = $.;
3007                     #$SourceSymbolTypes{$symbol} = "SECTION";
3008                 } else {
3009                     #print "SYMBOL DOCS found in source for : '$symbol' ",length($description), "\n";
3010                     $SourceSymbolDocs{$symbol} = $description;
3011                     $SourceSymbolParams{$symbol} = [ @params ];
3012                     # FIXME $SourceSymbolTypes{$symbol} = "STRUCT,SIGNAL,ARG,FUNCTION,MACRO";
3013                     #if (defined $DeclarationTypes{$symbol}) {
3014                     #    $SourceSymbolTypes{$symbol} = $DeclarationTypes{$symbol}
3015                     #}
3016                     $SourceSymbolSourceFile{$symbol} = $file;
3017                     $SourceSymbolSourceLine{$symbol} = $.;
3018                 }
3020                 if ($since_desc) {
3021                     $Since{$symbol} = &ConvertSGMLChars ($symbol, $since_desc);
3022                 }
3024                 if ($stability_desc) {
3025                     $stability_desc = &ParseStabilityLevel($stability_desc, $file, $., "Stability level for $symbol");
3026                     $StabilityLevel{$symbol} = &ConvertSGMLChars ($symbol, $stability_desc);
3027                 }
3029                 if ($deprecated_desc) {
3030                     if (exists $Deprecated{$symbol}) {
3031                     }
3032                     else {
3033                          # don't warn for signals and properties
3034                          #if ($symbol !~ m/::?(.*)/) {
3035                          if (defined $DeclarationTypes{$symbol}) {
3036                              &LogWarning ($file, $., 
3037                                  "$symbol is deprecated in the inline comments, but no deprecation guards were found around the declaration.".
3038                                  " (See the --deprecated-guards option for gtkdoc-scan.)");
3039                          }
3040                     }
3041                     $Deprecated{$symbol} = &ConvertSGMLChars ($symbol, $deprecated_desc);
3042                 }
3043             }
3045             $in_comment_block = 0;
3046             next;
3047         }
3049         # Get rid of ' * ' at start of every line in the comment block.
3050         s%^\s*\*\s?%%;
3051         # But make sure we don't get rid of the newline at the end.
3052         if (!$_) {
3053             $_ = "\n";
3054         }
3055         #print "DEBUG: scanning :$_";
3057         # If we haven't found the symbol name yet, look for it.
3058         if (!$symbol) {
3059             if (m%^\s*(SECTION:\s*\S+)%) {
3060                 $symbol = $1;
3061                 #print "SECTION DOCS found in source for : '$symbol'\n";
3062                 $ignore_broken_returns = 1;
3063             } elsif (m%^\s*([\w:-]*\w)\s*:?%) {
3064                 $symbol = $1;
3065                 #print "SYMBOL DOCS found in source for : '$symbol'\n";
3066             }
3067             next;
3068         }
3070         # If we're in the return value description, add it to the end.
3071         if ($in_return) {
3072             # If we find another valid returns line, we assume that the first
3073             # one was really part of the description.
3074             if (m/^\s*(returns:|return\s+value:)/i) {
3075                 if ($return_style eq 'broken') {
3076                     $description .= $return_start . $return_desc;
3077                 }
3078                 $return_start = $1;
3079                 if ($return_style eq 'sane') {
3080                     &LogWarning ($file, $., "Multiple Returns for $symbol.");
3081                 }
3082                 $return_style = 'sane';
3083                 $ignore_broken_returns = 1;
3084                 $return_desc = $';
3085             } elsif (!$ignore_broken_returns && m/^\s*(returns\b\s*)/i) {
3086                 $description .= $return_start . $return_desc;
3087                 $return_start = $1;
3088                 $return_style = 'broken';
3089                 $return_desc = $';
3090             } elsif (m%^\s*since:%i) {
3091                 $since_desc = $';
3092                 $in_since = 1;
3093                 $in_return = 0;
3094             } elsif (m%^\s*stability:%i) {
3095                 $stability_desc = $';
3096                 $in_stability = 1;
3097                 $in_return = 0;
3098             } elsif (m%^\s*deprecated:%i) {
3099                 $deprecated_desc = $';
3100                 $in_deprecated = 1;
3101                 $in_return = 0;
3102             } else {
3103                 $return_desc .= $_;
3104             }
3105             next;
3106         }
3108         if ($in_since) {
3109             if (m/^\s*(returns:|return\s+value:)/i) {
3110                 if ($return_style eq 'broken') {
3111                     $description .= $return_start . $return_desc;
3112                 }
3113                 $return_start = $1;
3114                 if ($return_style eq 'sane') {
3115                     &LogWarning ($file, $., "Multiple Returns for $symbol.");
3116                 }
3117                 $return_style = 'sane';
3118                 $ignore_broken_returns = 1;
3119                 $return_desc = $';
3120                 $in_return = 1;
3121                 $in_since = 0;
3122             } elsif (!$ignore_broken_returns && m/^\s*(returns\b\s*)/i) {
3123                 $return_start = $1;
3124                 $return_style = 'broken';
3125                 $return_desc = $';
3126                 $in_return = 1;
3127                 $in_since = 0;
3128             } elsif (m%^\s*deprecated:%i) {
3129                 $deprecated_desc = $';
3130                 $in_deprecated = 1;
3131                 $in_since = 0;
3132             } elsif (m%^\s*stability:%i) {
3133                 $stability_desc = $';
3134                 $in_stability = 1;
3135                 $in_since = 0;
3136             } else {
3137                 $since_desc .= $_;
3138             }
3139             next;
3140         }
3142         if ($in_stability) {
3143             if (m/^\s*(returns:|return\s+value:)/i) {
3144                 if ($return_style eq 'broken') {
3145                     $description .= $return_start . $return_desc;
3146                 }
3147                 $return_start = $1;
3148                 if ($return_style eq 'sane') {
3149                     &LogWarning ($file, $., "Multiple Returns for $symbol.");
3150                 }
3151                 $return_style = 'sane';
3152                 $ignore_broken_returns = 1;
3153                 $return_desc = $';
3154                 $in_return = 1;
3155                 $in_stability = 0;
3156             } elsif (!$ignore_broken_returns && m/^\s*(returns\b\s*)/i) {
3157                 $return_start = $1;
3158                 $return_style = 'broken';
3159                 $return_desc = $';
3160                 $in_return = 1;
3161                 $in_stability = 0;
3162             } elsif (m%^\s*deprecated:%i) {
3163                 $deprecated_desc = $';
3164                 $in_deprecated = 1;
3165                 $in_stability = 0;
3166             } elsif (m%^\s*since:%i) {
3167                 $since_desc = $';
3168                 $in_since = 1;
3169                 $in_stability = 0;
3170             } else {
3171                 $stability_desc .= $_;
3172             }
3173             next;
3174         }
3176         if ($in_deprecated) {
3177             if (m/^\s*(returns:|return\s+value:)/i) {
3178                 if ($return_style eq 'broken') {
3179                     $description .= $return_start . $return_desc;
3180                 }
3181                 $return_start = $1;
3182                 if ($return_style eq 'sane') {
3183                     &LogWarning ($file, $., "Multiple Returns for $symbol.");
3184                 }
3185                 $return_style = 'sane';
3186                 $ignore_broken_returns = 1;
3187                 $return_desc = $';
3188                 $in_return = 1;
3189                 $in_deprecated = 0;
3190             } elsif (!$ignore_broken_returns && m/^\s*(returns\b\s*)/i) {
3191                 $return_start = $1;
3192                 $return_style = 'broken';
3193                 $return_desc = $';
3194                 $in_return = 1;
3195                 $in_deprecated = 0;
3196             } elsif (m%^\s*since:%i) {
3197                 $since_desc = $';
3198                 $in_since = 1;
3199                 $in_deprecated = 0;
3200             } elsif (m%^\s*stability:%i) {
3201                 $stability_desc = $';
3202                 $in_stability = 1;
3203                 $in_deprecated = 0;
3204             } else {
3205                 $deprecated_desc .= $_;
3206             }
3207             next;
3208         }
3210         # If we're in the description part, check for the 'Returns:' line.
3211         # If that isn't found, add the text to the end.
3212         if ($in_description) {
3213             # Get rid of 'Description:'
3214             s%^\s*Description:%%;
3216             if (m/^\s*(returns:|return\s+value:)/i) {
3217                 if ($return_style eq 'broken') {
3218                     $description .= $return_start . $return_desc;
3219                 }
3220                 $return_start = $1;
3221                 if ($return_style eq 'sane') {
3222                     &LogWarning ($file, $., "Multiple Returns for $symbol.");
3223                 }
3224                 $return_style = 'sane';
3225                 $ignore_broken_returns = 1;
3226                 $return_desc = $';
3227                 $in_return = 1;
3228                 next;
3229             } elsif (!$ignore_broken_returns && m/^\s*(returns\b\s*)/i) {
3230                 $return_start = $1;
3231                 $return_style = 'broken';
3232                 $return_desc = $';
3233                 $in_return = 1;
3234                 next;
3235             } elsif (m%^\s*since:%i) {
3236                 $since_desc = $';
3237                 $in_since = 1;
3238                 next;
3239             } elsif (m%^\s*deprecated:%i) {
3240                 $deprecated_desc = $';
3241                 $in_deprecated = 1;
3242                 next;
3243             } elsif (m%^\s*stability:%i) {
3244                 $stability_desc = $';
3245                 $in_stability = 1;
3246                 next;
3247             }
3249             $description .= $_;
3250             next;
3251         }
3253         # We must be in the parameters. Check for the empty line below them.
3254         if (m%^\s*$%) {
3255             $in_description = 1;
3256             next;
3257         }
3259         # Look for a parameter name.
3260         if (m%^\s*@(\S+)\s*:%) {
3261             my $param_name = $1;
3262             #print "Found parameter: $param_name\n";
3263             # Allow '...' as the Varargs parameter.
3264             if ($param_name eq "...") {
3265                 $param_name = "Varargs";
3266             }
3267             if ("\L$param_name" eq "returns") {
3268                 $return_style = 'sane';
3269                 $ignore_broken_returns = 1;
3270             }
3271             push (@params, $param_name);
3272             push (@params, $');
3273             $current_param += 2;
3274             next;
3275         }
3277         # We must be in the middle of a parameter description, so add it on
3278         # to the last element in @params.
3279         if ($current_param == -1) {
3280             &LogWarning ($file, $., "Parsing comment block file : parameter expected.");
3281         } else {
3282             $params[$#params] .= $_;
3283         }
3284     }
3285     close (SRCFILE);
3288 #############################################################################
3289 # Function    : OutputMissingDocumentation
3290 # Description : Outputs report of documentation coverage to a file
3292 # Arguments   : none
3293 #############################################################################
3295 sub OutputMissingDocumentation {
3296      my $n_documented = 0;
3297      my $n_incomplete = 0;
3298      my $total = 0;
3299      my $symbol;
3300      my $percent;
3301      my $msg;
3302      my $buffer = "";
3303      my $buffer_deprecated = "";
3304      my $buffer_descriptions = "";
3306      open (UNDOCUMENTED, ">$ROOT_DIR/$MODULE-undocumented.txt")
3307           || die "Can't create $ROOT_DIR/$MODULE-undocumented.txt: $!";
3309      foreach $symbol (sort (keys (%AllSymbols))) {
3310           if ($symbol !~ /:(Title|Long_Description|Short_Description|See_Also|Stability_Level|Include)/) {
3311               $total++;
3312               if (exists ($AllDocumentedSymbols{$symbol})) {
3313                   $n_documented++;
3314                   if (exists ($AllIncompleteSymbols{$symbol})) {
3315                       $n_incomplete++;
3316                       $buffer .= $symbol . " (" . $AllIncompleteSymbols{$symbol} . ")\n";
3317                   }
3318               } elsif (exists $Deprecated{$symbol}) {
3319                   if (exists ($AllIncompleteSymbols{$symbol})) {
3320                       $n_incomplete++;
3321                       $buffer_deprecated .= $symbol . " (" . $AllIncompleteSymbols{$symbol} . ")\n";
3322                   } else {
3323                       $buffer_deprecated .= $symbol . "\n";
3324                   }
3325               } else {
3326                   if (exists ($AllIncompleteSymbols{$symbol})) {
3327                       $n_incomplete++;
3328                       $buffer .= $symbol . " (" . $AllIncompleteSymbols{$symbol} . ")\n";
3329                   } else {
3330                       $buffer .= $symbol . "\n";
3331                   }
3332               }
3333           } elsif ($symbol =~ /:(Long_Description|Short_Description)/) {
3334               $total++;
3335               #my $len1=(exists($SymbolDocs{$symbol}))?length($SymbolDocs{$symbol}):-1;
3336               #my $len2=(exists($AllDocumentedSymbols{$symbol}))?length($AllDocumentedSymbols{$symbol}):-1;
3337               #print "%%%% $symbol : $len1,$len2\n";
3338               if (((exists ($SymbolDocs{$symbol})) && (length ($SymbolDocs{$symbol}) > 0))
3339               || ((exists ($AllDocumentedSymbols{$symbol})) && (length ($AllDocumentedSymbols{$symbol}) > 0))) {
3340                   $n_documented++;
3341               } else {
3342                   $symbol =~ m/^.*\/(.*)$/;
3343                   $buffer_descriptions .= $1 . "\n";
3344               }
3345           }
3346      }
3348      $buffer .= "\n" . $buffer_deprecated . "\n" . $buffer_descriptions;
3350      if ($total == 0) {
3351           $percent = 100;
3352      } else {
3353           $percent = ($n_documented / $total) * 100.0;
3354      }
3356      printf UNDOCUMENTED "%.0f%% symbol docs coverage.\n", $percent;
3357      print UNDOCUMENTED "$n_documented symbols documented.\n";
3358      print UNDOCUMENTED "$n_incomplete symbols incomplete.\n";
3359      print UNDOCUMENTED ($total - $n_documented) . " not documented.\n\n\n";
3361      print UNDOCUMENTED $buffer;
3363      close (UNDOCUMENTED);
3365      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);
3369 #############################################################################
3370 # Function    : OutputUndeclaredSymbols
3371 # Description : Outputs symbols that are undeclared yet documented to a file
3373 # Arguments   : none
3374 #############################################################################
3376 sub OutputUndeclaredSymbols {
3377     open(UNDECLARED, ">$ROOT_DIR/$MODULE-undeclared.txt")
3378         || die "Can't create $ROOT_DIR/$MODULE-undeclared.txt";
3380     if (%UndeclaredSymbols) {
3381         print UNDECLARED (join("\n", sort keys %UndeclaredSymbols));
3382         print UNDECLARED "\n";
3383         print "See $MODULE-undeclared.txt for the list of undeclared symbols.\n"
3384     }
3385     close(UNDECLARED);
3389 #############################################################################
3390 # Function    : OutputAllSymbols
3391 # Description : Outputs list of all symbols to a file
3393 # Arguments   : none
3394 #############################################################################
3396 sub OutputAllSymbols {
3397      my $n_documented = 0;
3398      my $total = 0;
3399      my $symbol;
3400      my $percent;
3401      my $msg;
3403      open (SYMBOLS, ">$ROOT_DIR/$MODULE-symbols.txt")
3404           || die "Can't create $ROOT_DIR/$MODULE-symbols.txt: $!";
3406      foreach $symbol (sort (keys (%AllSymbols))) {
3407           print SYMBOLS $symbol . "\n"
3408      }
3410      close (SYMBOLS);
3414 #############################################################################
3415 # Function    : MergeSourceDocumentation
3416 # Description : This merges documentation read from a source file into the
3417 #               documentation read in from a template file.
3419 #               Parameter descriptions override any in the template files.
3420 #               Function descriptions are placed before any description from
3421 #               the template files.
3423 # Arguments   : none
3424 #############################################################################
3426 sub MergeSourceDocumentation {
3427     my $symbol;
3428     my @Symbols;
3430     if (scalar %SymbolDocs) {
3431         @Symbols=keys (%SymbolDocs);
3432         #print "num existing entries: ".(scalar @Symbols)."\n";
3433         #print "  ",$Symbols[0], " ",$Symbols[1],"\n";
3434     }
3435     else {
3436         # filter scanned declarations, with what we suppress from -sections.txt
3437         my %tmp = ();
3438         foreach $symbol (keys (%Declarations)) {
3439             if ($KnownSymbols{$symbol}) {
3440                 $tmp{$symbol}=1;
3441             }
3442         }
3443         # and add whats found in the source
3444         foreach $symbol (keys (%SourceSymbolDocs)) {
3445             $tmp{$symbol}=1;
3446         }
3447         @Symbols = keys (%tmp);
3448         #print "num source entries: ".(scalar @Symbols)."\n";
3449     }
3450     foreach $symbol (@Symbols) {
3451         $AllSymbols{$symbol} = 1;
3453         my $have_tmpl_docs = 0;
3455         ## See if the symbol is documented in template
3456         my $tmpl_doc = defined ($SymbolDocs{$symbol}) ? $SymbolDocs{$symbol} : "";
3457         my $check_tmpl_doc =$tmpl_doc;
3458         # remove all xml-tags and whitespaces
3459         #$check_tmpl_doc =~ s/<\/?[a-z]+>//g;
3460         $check_tmpl_doc =~ s/<.*?>//g;
3461         $check_tmpl_doc =~ s/\s//g;
3462         # anything left ?
3463         if ($check_tmpl_doc ne "") {
3464             $have_tmpl_docs = 1;
3465             #print "## [$check_tmpl_doc]\n";
3466         }
3468         if (exists ($SourceSymbolDocs{$symbol})) {
3469             my $type = $DeclarationTypes {$symbol};
3471             #print "merging [$symbol] from source\n";
3473             my $item = "Parameter";
3474             if (defined ($type)) {
3475                 if ($type eq 'STRUCT') {
3476                     $item = "Field";
3477                 } elsif ($type eq 'ENUM') {
3478                     $item = "Value";
3479                 } elsif ($type eq 'UNION') {
3480                     $item = "Field";
3481                 }
3482             } else {
3483                 $type="SIGNAL";
3484             }
3486             my $src_doc = $SourceSymbolDocs{$symbol};
3487             # remove leading and training whitespaces
3488             $src_doc =~ s/^\s+//;
3489             $src_doc =~ s/\s+$//;
3491             # Don't output warnings for overridden titles as titles are
3492             # automatically generated in the -sections.txt file, and thus they
3493             # are often overridden.
3494             if ($have_tmpl_docs && $symbol !~ m/:Title$/) {
3495                 # check if content is different
3496                 if ($tmpl_doc ne $src_doc) {
3497                     #print "[$tmpl_doc] [$src_doc]\n";
3498                     &LogWarning ($SourceSymbolSourceFile{$symbol}, $SourceSymbolSourceLine{$symbol},
3499                         "Documentation in template ".$SymbolSourceFile{$symbol}.":".$SymbolSourceLine{$symbol}." for $symbol being overridden by inline comments.");
3500                 }
3501             }
3503             if ($src_doc ne "") {
3504                  $AllDocumentedSymbols{$symbol} = 1;
3505             }
3507             # Convert <!--PARAMETERS--> with any blank lines around it to
3508             # a </para> followed by <!--PARAMETERS--> followed by <para>.
3509             $src_doc =~ s%\n+\s*<!--PARAMETERS-->\s*\n+%\n</para>\n<!--PARAMETERS-->\n<para>\n%g;
3511             # If there is a blank line, finish the paragraph and start another.
3512             $src_doc = &ConvertBlankLines ($src_doc, $symbol);
3514             if ($symbol =~ m/$TMPL_DIR\/.+:Long_Description/) {
3515                 $SymbolDocs{$symbol} = "<para>\n$src_doc</para>\n$tmpl_doc";
3516             } elsif ($symbol =~ m/$TMPL_DIR\/.+:.+/) {
3517                 # For the title/summary/see also section docs we don't want to
3518                 # add any <para> tags.
3519                 $SymbolDocs{$symbol} = "$src_doc"
3520             } else {
3521                 $SymbolDocs{$symbol} = "<para>\n$src_doc</para>\n$tmpl_doc";
3522             }
3524             # merge parameters
3525             if ($symbol =~ m/.*::.*/) {
3526                 # For signals we prefer the param names from the source docs,
3527                 # since the ones from the templates are likely to contain the
3528                 # artificial argn names which are generated by gtkdoc-scangobj.
3529                 $SymbolParams{$symbol} = $SourceSymbolParams{$symbol};
3530                 # FIXME: we need to check for empty docs here as well!
3531             } else {
3532                 # The templates contain the definitive parameter names and order,
3533                 # so we will not change that. We only override the actual text.
3534                 my $tmpl_params = $SymbolParams{$symbol};
3535                 if (!defined ($tmpl_params)) {
3536                     #print "No merge needed for $symbol\n";
3537                     $SymbolParams{$symbol} = $SourceSymbolParams{$symbol};
3538                 } else {
3539                     my $params = $SourceSymbolParams{$symbol};
3540                     my $j;
3541                     #print "Merge needed for $symbol, tmpl_params: ",$#$tmpl_params,", source_params: ",$#$params," \n";
3542                     for ($j = 0; $j <= $#$tmpl_params; $j += 2) {
3543                         my $tmpl_param_name = $$tmpl_params[$j];
3545                         # Allow '...' as the Varargs parameter.
3546                         if ($tmpl_param_name eq "...") {
3547                             $tmpl_param_name = "Varargs";
3548                         }
3550                         # Try to find the param in the source comment documentation.
3551                         my $found = 0;
3552                         my $k;
3553                         for ($k = 0; $k <= $#$params; $k += 2) {
3554                             my $param_name = $$params[$k];
3555                             my $param_desc = $$params[$k + 1];
3557                             # We accept changes in case, since the Gnome source docs
3558                             # contain a lot of these.
3559                             if ("\L$param_name" eq "\L$tmpl_param_name") {
3560                                 $found = 1;
3562                                 # Override the description.
3563                                 $$tmpl_params[$j + 1] = $param_desc;
3565                                 # Set the name to "" to mark it as used.
3566                                 $$params[$k] = "";
3567                                 last;
3568                             }
3569                         }
3571                         # If it looks like the parameters are there, but not
3572                         # in the right place, try to explain a bit better.
3573                         if ((!$found) && ($src_doc =~ m/\@$tmpl_param_name:/)) {
3574                             &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
3575                                 "Parameters for $symbol must start on the line immediately after the function or macro name.");
3576                         }
3577                     }
3579                     # Now we output a warning if parameters have been described which
3580                     # do not exist.
3581                     for ($j = 0; $j <= $#$params; $j += 2) {
3582                         my $param_name = $$params[$j];
3583                         if ($param_name) {
3584                             # the template builder cannot detect if a macro returns
3585                             # a result or not
3586                             if(($type eq "MACRO") && ($param_name eq "Returns")) {
3587                                 next;
3588                             }
3589                             &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
3590                                 "$item described in source code comment block but does not exist. $type: $symbol $item: $param_name.");
3591                         }
3592                     }
3593                 }
3594             }
3595         } else {
3596             if ($have_tmpl_docs) {
3597                 $AllDocumentedSymbols{$symbol} = 1;
3598                 #print "merging [$symbol] from template\n";
3599             }
3600             else {
3601                 #print "[$symbol] undocumented\n";
3602             }
3603         }
3605         # if this symbol is documented, check if docs are complete
3606         $check_tmpl_doc = defined ($SymbolDocs{$symbol}) ? $SymbolDocs{$symbol} : "";
3607         # remove all xml-tags and whitespaces
3608         #$check_tmpl_doc =~ s/<\/?[a-z]+>//g;
3609         $check_tmpl_doc =~ s/<.*?>//g;
3610         $check_tmpl_doc =~ s/\s//g;
3611         if ($check_tmpl_doc ne "") {
3612             my $tmpl_params = $SymbolParams{$symbol};
3613             if (defined ($tmpl_params)) {
3614                 my $type = $DeclarationTypes {$symbol};
3616                 my $item = "Parameter";
3617                 if (defined ($type)) {
3618                     if ($type eq 'STRUCT') {
3619                         $item = "Field";
3620                     } elsif ($type eq 'ENUM') {
3621                         $item = "Value";
3622                     } elsif ($type eq 'UNION') {
3623                         $item = "Field";
3624                     }
3625                 } else {
3626                     $type="SIGNAL";
3627                 }
3629                 #print "Check param docs for $symbol, tmpl_params: ",$#$tmpl_params,"\n";
3631                 my $j;
3632                 for ($j = 0; $j <= $#$tmpl_params; $j += 2) {
3633                     # Output a warning if the parameter is empty and
3634                     # remember for stats.
3635                     my $tmpl_param_name = $$tmpl_params[$j];
3636                     my $tmpl_param_desc = $$tmpl_params[$j + 1];
3637                     if ($tmpl_param_desc !~ m/\S/) {
3638                         if (exists ($AllIncompleteSymbols{$symbol})) {
3639                             $AllIncompleteSymbols{$symbol}.=", ".$tmpl_param_name;
3640                         } else {
3641                             $AllIncompleteSymbols{$symbol}=$tmpl_param_name;
3642                         }
3643                         &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
3644                             "$item description for $tmpl_param_name is missing in source code comment block.");
3645                     }
3646                 }
3647             }
3648         }
3649    }
3650    #print "num doc entries: ".(scalar %SymbolDocs)."\n";
3653 sub IsEmptyDoc {
3654     my ($doc) = @_;
3656     if ($doc =~ /^\s*<para>\s*(FIXME)?\s*<\/para>\s*$/) {
3657         return 1;
3658     } else {
3659         return 0;
3660     }
3664 # This converts blank lines to "</para><para>", but only outside CDATA and
3665 # <programlisting> tags.
3666 sub ConvertBlankLines {
3667     return &ModifyXMLElements ($_[0], $_[1],
3668                                "<!\\[CDATA\\[|<programlisting[^>]*>|\\|\\[",
3669                                \&ConvertBlankLinesEndTag,
3670                                \&ConvertBlankLinesCallback);
3674 sub ConvertBlankLinesEndTag {
3675   if ($_[0] eq "<!\[CDATA\[") {
3676     return "]]>";
3677   } elsif ($_[0] eq "|[") {
3678     return "]\\|";
3679   } else {
3680     return "</programlisting>";
3681   }
3684 sub ConvertBlankLinesCallback {
3685   my ($text, $symbol, $tag) = @_;
3687   # If we're not in CDATA or a <programlisting> we convert blank lines so
3688   # they start a new <para>.
3689   if ($tag eq "") {
3690     $text =~ s%\n{2,}%\n</para>\n<para>\n%g;
3691   }
3693   return $text;
3697 #############################################################################
3698 # LIBRARY FUNCTIONS -   These functions are used in both gtkdoc-mkdb and
3699 #                       gtkdoc-mktmpl and should eventually be moved to a
3700 #                       separate library.
3701 #############################################################################
3703 #############################################################################
3704 # Function    : ReadDeclarationsFile
3705 # Description : This reads in a file containing the function/macro/enum etc.
3706 #               declarations.
3708 #               Note that in some cases there are several declarations with
3709 #               the same name, e.g. for conditional macros. In this case we
3710 #               set a flag in the %DeclarationConditional hash so the
3711 #               declaration is not shown in the docs.
3713 #               If a macro and a function have the same name, e.g. for
3714 #               gtk_object_ref, the function declaration takes precedence.
3716 #               Some opaque structs are just declared with 'typedef struct
3717 #               _name name;' in which case the declaration may be empty.
3718 #               The structure may have been found later in the header, so
3719 #               that overrides the empty declaration.
3721 # Arguments   : $file - the declarations file to read
3722 #               $override - if declarations in this file should override
3723 #                       any current declaration.
3724 #############################################################################
3726 sub ReadDeclarationsFile {
3727     my ($file, $override) = @_;
3729     if ($override == 0) {
3730         %Declarations = ();
3731         %DeclarationTypes = ();
3732         %DeclarationConditional = ();
3733         %DeclarationOutput = ();
3734     }
3736     open (INPUT, $file)
3737         || die "Can't open $file: $!";
3738     my $declaration_type = "";
3739     my $declaration_name;
3740     my $declaration;
3741     my $is_deprecated = 0;
3742     while (<INPUT>) {
3743         if (!$declaration_type) {
3744             if (m/^<([^>]+)>/) {
3745                 $declaration_type = $1;
3746                 $declaration_name = "";
3747                 #print "Found declaration: $declaration_type\n";
3748                 $declaration = "";
3749             }
3750         } else {
3751             if (m%^<NAME>(.*)</NAME>%) {
3752                 $declaration_name = $1;
3753             } elsif (m%^<DEPRECATED/>%) {
3754                 $is_deprecated = 1;
3755             } elsif (m%^</$declaration_type>%) {
3756                 #print "Found end of declaration: $declaration_name\n";
3757                 # Check that the declaration has a name
3758                 if ($declaration_name eq "") {
3759                     print "ERROR: $declaration_type has no name $file:$.\n";
3760                 }
3762                 # If the declaration is an empty typedef struct _XXX XXX
3763                 # set the flag to indicate the struct has a typedef.
3764                 if ($declaration_type eq 'STRUCT'
3765                     && $declaration =~ m/^\s*$/) {
3766                     #print "Struct has typedef: $declaration_name\n";
3767                     $StructHasTypedef{$declaration_name} = 1;
3768                 }
3770                 # Check if the symbol is already defined.
3771                 if (defined ($Declarations{$declaration_name})
3772                     && $override == 0) {
3773                     # Function declarations take precedence.
3774                     if ($DeclarationTypes{$declaration_name} eq 'FUNCTION') {
3775                         # Ignore it.
3776                     } elsif ($declaration_type eq 'FUNCTION') {
3777                         if ($is_deprecated) {
3778                             $Deprecated{$declaration_name} = "";
3779                         }
3780                         $Declarations{$declaration_name} = $declaration;
3781                         $DeclarationTypes{$declaration_name} = $declaration_type;
3782                     } elsif ($DeclarationTypes{$declaration_name}
3783                               eq $declaration_type) {
3784                         # If the existing declaration is empty, or is just a
3785                         # forward declaration of a struct, override it.
3786                         if ($declaration_type eq 'STRUCT') {
3787                             if ($Declarations{$declaration_name} =~ m/^\s*(struct\s+\w+\s*;)?\s*$/) {
3788                                 if ($is_deprecated) {
3789                                     $Deprecated{$declaration_name} = "";
3790                                 }
3791                                 $Declarations{$declaration_name} = $declaration;
3792                             } elsif ($declaration =~ m/^\s*(struct\s+\w+\s*;)?\s*$/) {
3793                                 # Ignore an empty or forward declaration.
3794                             } else {
3795                                 &LogWarning ($file, $., "Structure $declaration_name has multiple definitions.");
3796                             }
3797                         } else {
3798                             # set flag in %DeclarationConditional hash for
3799                             # multiply defined macros/typedefs.
3800                             $DeclarationConditional{$declaration_name} = 1;
3801                         }
3802                     } else {
3803                         &LogWarning ($file, $., "$declaration_name has multiple definitions.");
3804                     }
3805                 } else {
3806                     if ($is_deprecated) {
3807                         $Deprecated{$declaration_name} = "";
3808                     }
3809                     $Declarations{$declaration_name} = $declaration;
3810                     $DeclarationTypes{$declaration_name} = $declaration_type;
3811                 }
3813                 $declaration_type = "";
3814                 $is_deprecated = 0;
3815             } else {
3816                 $declaration .= $_;
3817             }
3818         }
3819     }
3820     close (INPUT);
3824 #############################################################################
3825 # Function    : ReadSignalsFile
3826 # Description : This reads in an existing file which contains information on
3827 #               all GTK signals. It creates the arrays @SignalNames and
3828 #               @SignalPrototypes containing info on the signals. The first
3829 #               line of the SignalPrototype is the return type of the signal
3830 #               handler. The remaining lines are the parameters passed to it.
3831 #               The last parameter, "gpointer user_data" is always the same
3832 #               so is not included.
3833 # Arguments   : $file - the file containing the signal handler prototype
3834 #                       information.
3835 #############################################################################
3837 sub ReadSignalsFile {
3838     my ($file) = @_;
3840     my $in_signal = 0;
3841     my $signal_object;
3842     my $signal_name;
3843     my $signal_returns;
3844     my $signal_flags;
3845     my $signal_prototype;
3847     # Reset the signal info.
3848     @SignalObjects = ();
3849     @SignalNames = ();
3850     @SignalReturns = ();
3851     @SignalFlags = ();
3852     @SignalPrototypes = ();
3854     if (! -f $file) {
3855         return;
3856     }
3857     if (!open (INPUT, $file)) {
3858         warn "Can't open $file - skipping signals\n";
3859         return;
3860     }
3861     while (<INPUT>) {
3862         if (!$in_signal) {
3863             if (m/^<SIGNAL>/) {
3864                 $in_signal = 1;
3865                 $signal_object = "";
3866                 $signal_name = "";
3867                 $signal_returns = "";
3868                 $signal_prototype = "";
3869             }
3870         } else {
3871             if (m/^<NAME>(.*)<\/NAME>/) {
3872                 $signal_name = $1;
3873                 if ($signal_name =~ m/^(.*)::(.*)$/) {
3874                     $signal_object = $1;
3875                     ($signal_name = $2) =~ s/_/-/g;
3876                     #print "Found signal: $signal_name\n";
3877                 } else {
3878                     &LogWarning ($file, $., "Invalid signal name: $signal_name.");
3879                 }
3880             } elsif (m/^<RETURNS>(.*)<\/RETURNS>/) {
3881                 $signal_returns = $1;
3882             } elsif (m/^<FLAGS>(.*)<\/FLAGS>/) {
3883                 $signal_flags = $1;
3884             } elsif (m%^</SIGNAL>%) {
3885                 #print "Found end of signal: ${signal_object}::${signal_name}\nReturns: ${signal_returns}\n${signal_prototype}";
3886                 push (@SignalObjects, $signal_object);
3887                 push (@SignalNames, $signal_name);
3888                 push (@SignalReturns, $signal_returns);
3889                 push (@SignalFlags, $signal_flags);
3890                 push (@SignalPrototypes, $signal_prototype);
3891                 $in_signal = 0;
3892             } else {
3893                 $signal_prototype .= $_;
3894             }
3895         }
3896     }
3897     close (INPUT);
3901 #############################################################################
3902 # Function    : ReadTemplateFile
3903 # Description : This reads in the manually-edited documentation file
3904 #               corresponding to the file currently being created, so we can
3905 #               insert the documentation at the appropriate places.
3906 #               It outputs %SymbolTypes, %SymbolDocs and %SymbolParams, which
3907 #               is a hash of arrays.
3908 #               NOTE: This function is duplicated in gtkdoc-mktmpl (but
3909 #               slightly different).
3910 # Arguments   : $docsfile - the template file to read in.
3911 #               $skip_unused_params - 1 if the unused parameters should be
3912 #                       skipped.
3913 #############################################################################
3915 sub ReadTemplateFile {
3916     my ($docsfile, $skip_unused_params) = @_;
3918     my $template = "$docsfile.sgml";
3919     if (! -f $template) {
3920         #print "File doesn't exist: $template\n";
3921         return 0;
3922     }
3923     #print "Reading $template\n";
3925     # start with empty hashes, we merge the source comment for each file
3926     # afterwards
3927     %SymbolDocs = ();
3928     %SymbolTypes = ();
3929     %SymbolParams = ();
3931     my $current_type = "";      # Type of symbol being read.
3932     my $current_symbol = "";    # Name of symbol being read.
3933     my $symbol_doc = "";                # Description of symbol being read.
3934     my @params;                 # Parameter names and descriptions of current
3935                                 #   function/macro/function typedef.
3936     my $current_param = -1;     # Index of parameter currently being read.
3937                                 #   Note that the param array contains pairs
3938                                 #   of param name & description.
3939     my $in_unused_params = 0;   # True if we are reading in the unused params.
3940     my $in_deprecated = 0;
3941     my $in_since = 0;
3942     my $in_stability = 0;
3944     open (DOCS, "$template")
3945         || die "Can't open $template: $!";
3946     while (<DOCS>) {
3947         if (m/^<!-- ##### ([A-Z_]+) (\S+) ##### -->/) {
3948             my $type = $1;
3949             my $symbol = $2;
3950             if ($symbol eq "Title"
3951                 || $symbol eq "Short_Description"
3952                 || $symbol eq "Long_Description"
3953                 || $symbol eq "See_Also"
3954                 || $symbol eq "Stability_Level"
3955                 || $symbol eq "Include") {
3957                 $symbol = $docsfile . ":" . $symbol;
3958             }
3960             #print "Found symbol: $symbol\n";
3961             # Remember file and line for the symbol
3962             $SymbolSourceFile{$symbol} = $template;
3963             $SymbolSourceLine{$symbol} = $.;
3965             # Store previous symbol, but remove any trailing blank lines.
3966             if ($current_symbol ne "") {
3967                 $symbol_doc =~ s/\s+$//;
3968                 $SymbolTypes{$current_symbol} = $current_type;
3969                 $SymbolDocs{$current_symbol} = $symbol_doc;
3971                 # Check that the stability level is valid.
3972                 if ($StabilityLevel{$current_symbol}) {
3973                     $StabilityLevel{$current_symbol} = &ParseStabilityLevel($StabilityLevel{$current_symbol}, $template, $., "Stability level for $current_symbol");
3974                 }
3976                 if ($current_param >= 0) {
3977                     $SymbolParams{$current_symbol} = [ @params ];
3978                 } else {
3979                     # Delete any existing params in case we are overriding a
3980                     # previously read template.
3981                     delete $SymbolParams{$current_symbol};
3982                 }
3983             }
3984             $current_type = $type;
3985             $current_symbol = $symbol;
3986             $current_param = -1;
3987             $in_unused_params = 0;
3988             $in_deprecated = 0;
3989             $in_since = 0;
3990             $in_stability = 0;
3991             $symbol_doc = "";
3992             @params = ();
3994         } elsif (m/^<!-- # Unused Parameters # -->/) {
3995             #print "DEBUG: Found unused parameters\n";
3996             $in_unused_params = 1;
3997             next;
3999         } elsif ($in_unused_params && $skip_unused_params) {
4000             # When outputting the DocBook we skip unused parameters.
4001             #print "DEBUG: Skipping unused param: $_";
4002             next;
4004         } else {
4005             # Check if param found. Need to handle "..." and "format...".
4006             if (s/^\@([\w\.]+):\040?//) {
4007                 my $param_name = $1;
4008                 # Allow variations of 'Returns'
4009                 if ($param_name =~ m/^[Rr]eturns?$/) {
4010                     $param_name = "Returns";
4011                 }
4012                 #print "Found param for symbol $current_symbol : '$param_name'= '$_'\n";
4014                 if ($param_name eq "Deprecated") {
4015                     $in_deprecated = 1;
4016                     $Deprecated{$current_symbol} = $_;
4017                 } elsif ($param_name eq "Since") {
4018                     $in_since = 1;
4019                     $Since{$current_symbol} = $_;
4020                 } elsif ($param_name eq "Stability") {
4021                     $in_stability = 1;
4022                     $StabilityLevel{$current_symbol} = $_;
4023                 } else {
4024                     push (@params, $param_name);
4025                     push (@params, $_);
4026                     $current_param += 2;
4027                 }
4028             } else {
4029                 if ($in_deprecated) {
4030                     $Deprecated{$current_symbol} .= $_;
4031                 } elsif ($in_since) {
4032                     $Since{$current_symbol} .= $_;
4033                 } elsif ($in_stability) {
4034                     $StabilityLevel{$current_symbol} .= $_;
4035                 } elsif ($current_param >= 0) {
4036                     $params[$current_param] .= $_;
4037                 } else {
4038                     $symbol_doc .= $_;
4039                 }
4040             }
4041         }
4042     }
4044     # Remember to finish the current symbol doccs.
4045     if ($current_symbol ne "") {
4047         $symbol_doc =~ s/\s+$//;
4048         $SymbolTypes{$current_symbol} = $current_type;
4049         $SymbolDocs{$current_symbol} = $symbol_doc;
4051         # Check that the stability level is valid.
4052         if ($StabilityLevel{$current_symbol}) {
4053             $StabilityLevel{$current_symbol} = &ParseStabilityLevel($StabilityLevel{$current_symbol}, $template, $., "Stability level for $current_symbol");
4054         }
4056         if ($current_param >= 0) {
4057             $SymbolParams{$current_symbol} = [ @params ];
4058         } else {
4059             # Delete any existing params in case we are overriding a
4060             # previously read template.
4061             delete $SymbolParams{$current_symbol};
4062         }
4063     }
4065     close (DOCS);
4066     return 1;
4070 #############################################################################
4071 # Function    : ReadObjectHierarchy
4072 # Description : This reads in the $MODULE-hierarchy.txt file containing all
4073 #               the GtkObject subclasses described in this module (and their
4074 #               ancestors).
4075 #               It places them in the @Objects array, and places their level
4076 #               in the widget hierarchy in the @ObjectLevels array, at the
4077 #               same index. GtkObject, the root object, has a level of 1.
4079 #               FIXME: the version in gtkdoc-mkdb also generates tree_index.sgml
4080 #               as it goes along, this should be split out into a separate
4081 #               function.
4083 # Arguments   : none
4084 #############################################################################
4086 sub ReadObjectHierarchy {
4087     @Objects = ();
4088     @ObjectLevels = ();
4090     if (! -f $OBJECT_TREE_FILE) {
4091         return;
4092     }
4093     if (!open (INPUT, $OBJECT_TREE_FILE)) {
4094         warn "Can't open $OBJECT_TREE_FILE - skipping object tree\n";
4095         return;
4096     }
4098     my $old_tree_index = "$SGML_OUTPUT_DIR/tree_index.sgml";
4099     my $new_tree_index = "$SGML_OUTPUT_DIR/tree_index.new";
4100     my $tree_header = $doctype_header;
4101     $tree_header =~ s/<!DOCTYPE \w+/<!DOCTYPE screen/;
4103     open (OUTPUT, ">$new_tree_index")
4104         || die "Can't create $new_tree_index: $!";
4105     print (OUTPUT "$tree_header<screen>\n");
4107     # Only emit objects if they are supposed to be documented, or if
4108     # they have documented children. To implement this, we maintain a
4109     # stack of pending objects which will be emitted if a documented
4110     # child turns up.
4111     my @pending_objects = ();
4112     my @pending_levels = ();
4113     while (<INPUT>) {
4114         if (m/\S+/) {
4115             my $object = $&;
4116             my $level = (length($`)) / 2 + 1;
4117             my $xref = "";
4119             while (($#pending_levels >= 0) && ($pending_levels[$#pending_levels] >= $level)) {
4120                 my $pobject = pop(@pending_objects);
4121                 my $plevel = pop(@pending_levels);
4122             }
4124             push (@pending_objects, $object);
4125             push (@pending_levels, $level);
4127             if (exists($KnownSymbols{$object}) && $KnownSymbols{$object} == 1) {
4128                 while ($#pending_levels >= 0) {
4129                     $object = shift @pending_objects;
4130                     $level = shift @pending_levels;
4131                     $xref = &MakeXRef ($object);
4133                     print (OUTPUT ' ' x ($level * 4), "$xref\n");
4134                     push (@Objects, $object);
4135                     push (@ObjectLevels, $level);
4136                 }
4137             }
4138         }
4139     }
4140     print (OUTPUT "</screen>\n");
4142     close (INPUT);
4143     close (OUTPUT);
4145     &UpdateFileIfChanged ($old_tree_index, $new_tree_index, 0);
4147     &OutputObjectList;
4150 #############################################################################
4151 # Function    : ReadInterfaces
4152 # Description : This reads in the $MODULE.interfaces file.
4154 # Arguments   : none
4155 #############################################################################
4157 sub ReadInterfaces {
4158     %Interfaces = ();
4160     if (! -f $INTERFACES_FILE) {
4161         return;
4162     }
4163     if (!open (INPUT, $INTERFACES_FILE)) {
4164         warn "Can't open $INTERFACES_FILE - skipping interfaces\n";
4165         return;
4166     }
4168     while (<INPUT>) {
4169        chomp;
4170        my ($object, @ifaces) = split;
4171        if (exists($KnownSymbols{$object}) && $KnownSymbols{$object} == 1) {
4172            my @knownIfaces = ();
4174            # filter out private interfaces, but leave foreign interfaces
4175            foreach my $iface (@ifaces) {
4176                if (!exists($KnownSymbols{$iface}) || $KnownSymbols{$iface} == 1) {
4177                    push (@knownIfaces, $iface);
4178                }
4179              }
4181            $Interfaces{$object} = join(' ', @knownIfaces);
4182        }
4183     }
4184     close (INPUT);
4187 #############################################################################
4188 # Function    : ReadPrerequisites
4189 # Description : This reads in the $MODULE.prerequisites file.
4191 # Arguments   : none
4192 #############################################################################
4194 sub ReadPrerequisites {
4195     %Prerequisites = ();
4197     if (! -f $PREREQUISITES_FILE) {
4198         return;
4199     }
4200     if (!open (INPUT, $PREREQUISITES_FILE)) {
4201         warn "Can't open $PREREQUISITES_FILE - skipping prerequisites\n";
4202         return;
4203     }
4205     while (<INPUT>) {
4206        chomp;
4207        my ($iface, @prereqs) = split;
4208        if (exists($KnownSymbols{$iface}) && $KnownSymbols{$iface} == 1) {
4209            my @knownPrereqs = ();
4211            # filter out private prerequisites, but leave foreign prerequisites
4212            foreach my $prereq (@prereqs) {
4213                if (!exists($KnownSymbols{$prereq}) || $KnownSymbols{$prereq} == 1) {
4214                   push (@knownPrereqs, $prereq);
4215                }
4216            }
4218            $Prerequisites{$iface} = join(' ', @knownPrereqs);
4219        }
4220     }
4221     close (INPUT);
4224 #############################################################################
4225 # Function    : ReadArgsFile
4226 # Description : This reads in an existing file which contains information on
4227 #               all GTK args. It creates the arrays @ArgObjects, @ArgNames,
4228 #               @ArgTypes, @ArgFlags, @ArgNicks and @ArgBlurbs containing info
4229 #               on the args.
4230 # Arguments   : $file - the file containing the arg information.
4231 #############################################################################
4233 sub ReadArgsFile {
4234     my ($file) = @_;
4236     my $in_arg = 0;
4237     my $arg_object;
4238     my $arg_name;
4239     my $arg_type;
4240     my $arg_flags;
4241     my $arg_nick;
4242     my $arg_blurb;
4243     my $arg_default;
4244     my $arg_range;
4246     # Reset the args info.
4247     @ArgObjects = ();
4248     @ArgNames = ();
4249     @ArgTypes = ();
4250     @ArgFlags = ();
4251     @ArgNicks = ();
4252     @ArgBlurbs = ();
4253     @ArgDefaults = ();
4254     @ArgRanges = ();
4256     if (! -f $file) {
4257         return;
4258     }
4259     if (!open (INPUT, $file)) {
4260         warn "Can't open $file - skipping args\n";
4261         return;
4262     }
4263     while (<INPUT>) {
4264         if (!$in_arg) {
4265             if (m/^<ARG>/) {
4266                 $in_arg = 1;
4267                 $arg_object = "";
4268                 $arg_name = "";
4269                 $arg_type = "";
4270                 $arg_flags = "";
4271                 $arg_nick = "";
4272                 $arg_blurb = "";
4273                 $arg_default = "";
4274                 $arg_range = "";
4275             }
4276         } else {
4277             if (m/^<NAME>(.*)<\/NAME>/) {
4278                 $arg_name = $1;
4279                 if ($arg_name =~ m/^(.*)::(.*)$/) {
4280                     $arg_object = $1;
4281                     ($arg_name = $2) =~ s/_/-/g;
4282                     #print "Found arg: $arg_name\n";
4283                 } else {
4284                     &LogWarning ($file, $., "Invalid argument name: $arg_name");
4285                 }
4286             } elsif (m/^<TYPE>(.*)<\/TYPE>/) {
4287                 $arg_type = $1;
4288             } elsif (m/^<RANGE>(.*)<\/RANGE>/) {
4289                 $arg_range = $1;
4290             } elsif (m/^<FLAGS>(.*)<\/FLAGS>/) {
4291                 $arg_flags = $1;
4292             } elsif (m/^<NICK>(.*)<\/NICK>/) {
4293                 $arg_nick = $1;
4294             } elsif (m/^<BLURB>(.*)<\/BLURB>/) {
4295                 $arg_blurb = $1;
4296                 if ($arg_blurb eq "(null)") {
4297                   $arg_blurb = "";
4298                   &LogWarning ($file, $., "Property ${arg_object}:${arg_name} has no documentation.");
4299                 }
4300             } elsif (m/^<DEFAULT>(.*)<\/DEFAULT>/) {
4301                 $arg_default = $1;
4302             } elsif (m%^</ARG>%) {
4303                 #print "Found end of arg: ${arg_object}::${arg_name}\n${arg_type} : ${arg_flags}\n";
4304                 push (@ArgObjects, $arg_object);
4305                 push (@ArgNames, $arg_name);
4306                 push (@ArgTypes, $arg_type);
4307                 push (@ArgRanges, $arg_range);
4308                 push (@ArgFlags, $arg_flags);
4309                 push (@ArgNicks, $arg_nick);
4310                 push (@ArgBlurbs, $arg_blurb);
4311                 push (@ArgDefaults, $arg_default);
4312                 $in_arg = 0;
4313             }
4314         }
4315     }
4316     close (INPUT);
4320 #############################################################################
4321 # Function    : CheckIsObject
4322 # Description : Returns 1 if the given name is a GtkObject or a subclass.
4323 #               It uses the global @Objects array.
4324 #               Note that the @Objects array only contains classes in the
4325 #               current module and their ancestors - not all GTK classes.
4326 # Arguments   : $name - the name to check.
4327 #############################################################################
4329 sub CheckIsObject {
4330     my ($name) = @_;
4332     my $object;
4333     foreach $object (@Objects) {
4334         if ($object eq $name) {
4335             return 1;
4336         }
4337     }
4338     return 0;
4342 #############################################################################
4343 # Function    : MakeReturnField
4344 # Description : Pads a string to $RETURN_TYPE_FIELD_WIDTH.
4345 # Arguments   : $str - the string to pad.
4346 #############################################################################
4348 sub MakeReturnField {
4349     my ($str) = @_;
4351     return $str . (' ' x ($RETURN_TYPE_FIELD_WIDTH - length ($str)));
4354 #############################################################################
4355 # Function    : GetSymbolSourceFile
4356 # Description : Get the filename where the symbol docs where taken from.
4357 # Arguments   : $symbol - the symbol name
4358 #############################################################################
4360 sub GetSymbolSourceFile {
4361     my ($symbol) = @_;
4363     if (defined($SourceSymbolSourceFile{$symbol})) {
4364         return $SourceSymbolSourceFile{$symbol};
4365     } elsif (defined($SymbolSourceFile{$symbol})) {
4366         return $SymbolSourceFile{$symbol};
4367     } else {
4368         return "";
4369     }
4372 #############################################################################
4373 # Function    : GetSymbolSourceLine
4374 # Description : Get the file line where the symbol docs where taken from.
4375 # Arguments   : $symbol - the symbol name
4376 #############################################################################
4378 sub GetSymbolSourceLine {
4379     my ($symbol) = @_;
4381     if (defined($SourceSymbolSourceLine{$symbol})) {
4382         return $SourceSymbolSourceLine{$symbol};
4383     } elsif (defined($SymbolSourceLine{$symbol})) {
4384         return $SymbolSourceLine{$symbol};
4385     } else {
4386         return 0;
4387     }