index.sgml: stop generating index.sgml files
[gtk-doc.git] / gtkdoc-mkdb.in
blobcafc023a5e187d61dae8431dbe0b03e970917c81
1 #!@PERL@ -w
2 # -*- cperl -*-
4 # gtk-doc - GTK DocBook documentation generator.
5 # Copyright (C) 1998  Damon Chaplin
6 #               2007,2008,2009  Stefan Kost
8 # This program is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 2 of the License, or
11 # (at your option) any later version.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program; if not, write to the Free Software
20 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 #############################################################################
24 # Script      : gtkdoc-mkdb
25 # Description : This creates the DocBook files from the edited templates.
26 #############################################################################
28 use warnings;
29 use strict;
30 use Getopt::Long;
32 push @INC, '@PACKAGE_DATA_DIR@';
33 require "gtkdoc-common.pl";
35 # Options
37 # name of documentation module
38 my $MODULE;
39 my $TMPL_DIR;
40 my $DB_OUTPUT_DIR;
41 my @SOURCE_DIRS;
42 my $SOURCE_SUFFIXES = "";
43 my $IGNORE_FILES = "";
44 my $PRINT_VERSION;
45 my $PRINT_HELP;
46 my $MAIN_SGML_FILE;
47 my $EXPAND_CONTENT_FILES = "";
48 my $INLINE_MARKUP_MODE;
49 my $DEFAULT_STABILITY;
50 my $DEFAULT_INCLUDES;
51 my $OUTPUT_FORMAT;
52 my $NAME_SPACE = "";
53 my $OUTPUT_ALL_SYMBOLS;
54 my $OUTPUT_SYMBOLS_WITHOUT_SINCE;
55 my $ROOT_DIR = ".";
56 my $OBJECT_TREE_FILE;
57 my $INTERFACES_FILE;
58 my $PREREQUISITES_FILE;
59 my $SIGNALS_FILE;
60 my $ARGS_FILE;
62 # These global arrays store information on signals. Each signal has an entry
63 # in each of these arrays at the same index, like a multi-dimensional array.
64 my @SignalObjects;        # The GtkObject which emits the signal.
65 my @SignalNames;        # The signal name.
66 my @SignalReturns;        # The return type.
67 my @SignalFlags;        # Flags for the signal
68 my @SignalPrototypes;        # The rest of the prototype of the signal handler.
70 # These global arrays store information on Args. Each Arg has an entry
71 # in each of these arrays at the same index, like a multi-dimensional array.
72 my @ArgObjects;                # The GtkObject which has the Arg.
73 my @ArgNames;                # The Arg name.
74 my @ArgTypes;                # The Arg type - gint, GtkArrowType etc.
75 my @ArgFlags;                # How the Arg can be used - readable/writable etc.
76 my @ArgNicks;                # The nickname of the Arg.
77 my @ArgBlurbs;          # Docstring of the Arg.
78 my @ArgDefaults;        # Default value of the Arg.
79 my @ArgRanges;                # The range of the Arg type
80 # These global hashes store declaration info keyed on a symbol name.
81 my %Declarations;
82 my %DeclarationTypes;
83 my %DeclarationConditional;
84 my %DeclarationOutput;
85 my %Deprecated;
86 my %Since;
87 my %StabilityLevel;
88 my %StructHasTypedef;
90 # These global hashes store the existing documentation.
91 my %SymbolDocs;
92 my %SymbolTypes;
93 my %SymbolParams;
94 my %SymbolSourceFile;
95 my %SymbolSourceLine;
96 my %SymbolAnnotations;
98 # These global hashes store documentation scanned from the source files.
99 my %SourceSymbolDocs;
100 my %SourceSymbolParams;
101 my %SourceSymbolSourceFile;
102 my %SourceSymbolSourceLine;
104 # all documentation goes in here, so we can do coverage analysis
105 my %AllSymbols;
106 my %AllIncompleteSymbols;
107 my %AllUnusedSymbols;
108 my %AllDocumentedSymbols;
110 # Undeclared yet documented symbols
111 my %UndeclaredSymbols;
113 # These global arrays store GObject, subclasses and the hierarchy (also of
114 # non-object derived types).
115 my @Objects;
116 my @ObjectLevels;
117 my %ObjectRoots;
119 my %Interfaces;
120 my %Prerequisites;
122 # holds the symbols which are mentioned in $MODULE-sections.txt and in which
123 # section they are defined
124 my %KnownSymbols;
125 my %SymbolSection;
126 my %SymbolSectionId;
128 # collects index entries
129 my %IndexEntriesFull;
130 my %IndexEntriesSince;
131 my %IndexEntriesDeprecated;
133 # Standard C preprocessor directives, which we ignore for '#' abbreviations.
134 my %PreProcessorDirectives = (
135     'assert' => 1,
136     'define' => 1,
137     'elif' => 1,
138     'else' => 1,
139     'endif' => 1,
140     'error' => 1,
141     'if' => 1,
142     'ifdef' => 1,
143     'ifndef' => 1,
144     'include' => 1,
145     'line' => 1,
146     'pragma' => 1,
147     'unassert' => 1,
148     'undef' => 1,
149     'warning' => 1
152 # remember used annotation (to write minimal glossary)
153 my %AnnotationsUsed;
155 my %AnnotationDefinition = (
156     # the GObjectIntrospection annotations are defined at:
157     # https://live.gnome.org/GObjectIntrospection/Annotations
158     'allow-none' => "NULL is OK, both for passing and for returning.",
159     'nullable' => "NULL may be passed as the value in, out, in-out; or as a return value.",
160     'optional' => "NULL may be passed instead of a pointer to a location.",
161     'array' => "Parameter points to an array of items.",
162     'attribute' => "Deprecated free-form custom annotation, replaced by (attributes) annotation.",
163     'attributes' => "Free-form key-value pairs.",
164     'closure' => "This parameter is a 'user_data', for callbacks; many bindings can pass NULL here.",
165     'constructor' => "This symbol is a constructor, not a static method.",
166     'destroy' => "This parameter is a 'destroy_data', for callbacks.",
167     'default' => "Default parameter value (for in case the <acronym>shadows</acronym>-to function has less parameters).",
168     'element-type' => "Generics and defining elements of containers and arrays.",
169     'error-domains' => "Typed errors. Similar to throws in Java.",
170     'foreign' => "This is a foreign struct.",
171     'get-value-func' => "The specified function is used to convert a struct from a GValue, must be a GTypeInstance.",
172     'in' => "Parameter for input. Default is <acronym>transfer none</acronym>.",
173     'inout' => "Parameter for input and for returning results. Default is <acronym>transfer full</acronym>.",
174     'in-out' => "Parameter for input and for returning results. Default is <acronym>transfer full</acronym>.",
175     'method' => "This is a method",
176     'not-error' => "A GError parameter is not to be handled like a normal GError.",
177     'out' => "Parameter for returning results. Default is <acronym>transfer full</acronym>.",
178     'out caller-allocates' => "Out parameter, where caller must allocate storage.",
179     'out callee-allocates' => "Out parameter, where caller must allocate storage.",
180     'ref-func' => "The specified function is used to ref a struct, must be a GTypeInstance.",
181     'rename-to' => "Rename the original symbol's name to SYMBOL.",
182     'scope call' => "The callback is valid only during the call to the method.",
183     'scope async' => "The callback is valid until first called.",
184     'scope notified' => "The callback is valid until the GDestroyNotify argument is called.",
185     'set-value-func' => "The specified function is used to convert from a struct to a GValue, must be a GTypeInstance.",
186     'skip' => "Exposed in C code, not necessarily available in other languages.",
187     'transfer container' => "Free data container after the code is done.",
188     'transfer floating' => "Alias for <acronym>transfer none</acronym>, used for objects with floating refs.",
189     'transfer full' => "Free data after the code is done.",
190     'transfer none' => "Don't free data after the code is done.",
191     'type' => "Override the parsed C type with given type.",
192     'unref-func' => "The specified function is used to unref a struct, must be a GTypeInstance.",
193     'virtual' => "This is the invoker for a virtual method.",
194     'value' => "The specified value overrides the evaluated value of the constant.",
195     # Stability Level definition
196     # https://bugzilla.gnome.org/show_bug.cgi?id=170860
197     'Stable' => <<EOF,
198 The intention of a Stable interface is to enable arbitrary third parties to
199 develop applications to these interfaces, release them, and have confidence that
200 they will run on all minor releases of the product (after the one in which the
201 interface was introduced, and within the same major release). Even at a major
202 release, incompatible changes are expected to be rare, and to have strong
203 justifications.
205     'Unstable' => <<EOF,
206 Unstable interfaces are experimental or transitional. They are typically used to
207 give outside developers early access to new or rapidly changing technology, or
208 to provide an interim solution to a problem where a more general solution is
209 anticipated. No claims are made about either source or binary compatibility from
210 one minor release to the next.
212 The Unstable interface level is a warning that these interfaces are  subject to
213 change without warning and should not be used in unbundled products.
215 Given such caveats, customer impact need not be a factor when considering
216 incompatible changes to an Unstable interface in a major or minor release.
217 Nonetheless, when such changes are introduced, the changes should still be
218 mentioned in the release notes for the affected release.
220     'Private' => <<EOF
221 An interface that can be used within the GNOME stack itself, but that is not
222 documented for end-users.  Such functions should only be used in specified and
223 documented ways.
227 # Elements to consider non-block items in MarkDown parsing
228 my %MD_TEXT_LEVEL_ELEMENTS = ( "literal" => 1,
229                                "emphasis" => 1,
230                                "envar" => 1,
231                                "filename" => 1,
232                                "firstterm" => 1,
233                                "footnote" => 1,
234                                "function" => 1,
235                                "manvolnum" => 1,
236                                "option" => 1,
237                                "replaceable" => 1,
238                                "structfield" => 1,
239                                "structname" => 1,
240                                "title" => 1,
241                                "varname" => 1 );
242 my %MD_ESCAPABLE_CHARS = ( "\\" => 1,
243                            "`" => 1,
244                            "*" => 1,
245                            "_" => 1,
246                            "{" => 1,
247                            "}" => 1,
248                            "[" => 1,
249                            "]" => 1,
250                            "(" => 1,
251                            ")" => 1,
252                            ">" => 1,
253                            "#" => 1,
254                            "+" => 1,
255                            "-" => 1,
256                            "." => 1,
257                            "!" => 1 );
258 my %MD_GTK_ESCAPABLE_CHARS = ( "@" => 1,
259                                "%" => 1 );
261 # Function and other declaration output settings.
262 my $RETURN_TYPE_FIELD_WIDTH = 20;
263 my $SYMBOL_FIELD_WIDTH = 36;
264 my $MAX_SYMBOL_FIELD_WIDTH = 40;
265 my $SIGNAL_FIELD_WIDTH = 16;
266 my $PARAM_FIELD_COUNT = 2;
268 # XML, SGML formatting helper
269 my $doctype_header;
272 run() unless caller; # Run program unless loaded as a module
275 sub run {
276     my %optctl = ('module' => \$MODULE,
277                   'source-dir' => \@SOURCE_DIRS,
278                   'source-suffixes' => \$SOURCE_SUFFIXES,
279                   'ignore-files' => \$IGNORE_FILES,
280                   'output-dir' => \$DB_OUTPUT_DIR,
281                   'tmpl-dir' => \$TMPL_DIR,
282                   'version' => \$PRINT_VERSION,
283                   'help' => \$PRINT_HELP,
284                   'main-sgml-file' => \$MAIN_SGML_FILE,
285                   'expand-content-files' => \$EXPAND_CONTENT_FILES,
286                   'sgml-mode' => \$INLINE_MARKUP_MODE,
287                   'xml-mode' => \$INLINE_MARKUP_MODE,
288                   'default-stability' => \$DEFAULT_STABILITY,
289                   'default-includes' => \$DEFAULT_INCLUDES,
290                   'output-format' => \$OUTPUT_FORMAT,
291                   'name-space' => \$NAME_SPACE,
292                   'outputallsymbols' => \$OUTPUT_ALL_SYMBOLS,
293                   'outputsymbolswithoutsince' => \$OUTPUT_SYMBOLS_WITHOUT_SINCE
294                   );
295     GetOptions(\%optctl, "module=s", "source-dir:s", "source-suffixes:s",
296         "ignore-files:s", "output-dir:s", "tmpl-dir:s", "version", 
297         "outputallsymbols", "outputsymbolswithoutsince",
298         "expand-content-files:s", "main-sgml-file:s", "extra-db-files:s", "help",
299         "sgml-mode", "xml-mode", "default-stability:s", "default-includes:s",
300         "output-format:s", "name-space:s");
301     
302     if ($PRINT_VERSION) {
303         print "@VERSION@\n";
304         exit 0;
305     }
306     
307     if (!$MODULE) {
308         $PRINT_HELP = 1;
309     }
310     
311     if ($DEFAULT_STABILITY && $DEFAULT_STABILITY ne "Stable"
312         && $DEFAULT_STABILITY ne "Private" && $DEFAULT_STABILITY ne "Unstable") {
313         $PRINT_HELP = 1;
314     }
315     
316     if ($PRINT_HELP) {
317         print <<EOF;
318 gtkdoc-mkdb version @VERSION@ - generate docbook files
320 --module=MODULE_NAME       Name of the doc module being parsed
321 --source-dir=DIRNAME       Directories which contain inline reference material
322 --source-suffixes=SUFFIXES Suffixes of source files to scan, comma-separated
323 --ignore-files=FILES       A space-separated list of header files/dirs not to
324                            scan
325 --output-dir=DIRNAME       Directory to put the generated DocBook files in
326 --tmpl-dir=DIRNAME         Directory in which template files may be found
327 --main-sgml-file=FILE      File containing the toplevel DocBook file.
328 --expand-content-files=FILES Extra DocBook files to expand abbreviations in.
329 --output-format=FORMAT     Format to use for the generated docbook, XML or SGML.
330 --{xml,sgml}-mode          Allow DocBook markup in inline documentation.
331 --default-stability=LEVEL  Specify default stability Level. Valid values are
332                            Stable, Unstable, or Private.
333 --default-includes=FILENAMES Specify default includes for section Synopsis
334 --name-space=NS            Omit namespace in index.
335 --version                  Print the version of this program
336 --help                     Print this help
338         exit 0;
339     }
340   
341     @TRACE@(" ignore files: [$IGNORE_FILES]\n");
342         
343     # check output format
344     if (! defined($OUTPUT_FORMAT) || ($OUTPUT_FORMAT eq "")) {
345         $OUTPUT_FORMAT = "xml";    
346     } else {
347         $OUTPUT_FORMAT = lc($OUTPUT_FORMAT);
348     }
349     if ($OUTPUT_FORMAT ne "xml") {
350         die "Invalid format '$OUTPUT_FORMAT' passed to --output.format"
351     }
352     
353     if (!$MAIN_SGML_FILE) {
354         # backwards compatibility
355         if (-e "${MODULE}-docs.sgml") {
356             $MAIN_SGML_FILE = "${MODULE}-docs.sgml";
357         } else {
358             $MAIN_SGML_FILE = "${MODULE}-docs.xml";
359         }
360     }
362     # extract docbook header or define default
363     if (-e $MAIN_SGML_FILE) {
364         open(INPUT, "<$MAIN_SGML_FILE") || die "Can't open $MAIN_SGML_FILE";
365         $doctype_header = "";
366         while (<INPUT>) {
367             if (/^\s*<(book|chapter|article)/) {
368                 # check that the top-level tag or the doctype decl contain the xinclude namespace decl
369                 if (($_ !~ m/http:\/\/www.w3.org\/200[13]\/XInclude/) && ($doctype_header !~ m/http:\/\/www.w3.org\/200[13]\/XInclude/m)) {
370                     $doctype_header = "";
371                 }
372                 last;
373             }
374             $doctype_header .= $_;
375         }
376         close(INPUT);
377         # if there are SYSTEM ENTITIES here, we should prepend "../" to the path
378         # FIXME: not sure if we can do this now, as people already work-around the problem
379         # $doctype_header =~ s#<!ENTITY % ([a-zA-Z-]+) SYSTEM \"([^/][a-zA-Z./]+)\">#<!ENTITY % $1 SYSTEM \"../$2\">#g;
380     } else {
381         $doctype_header = <<EOF;
382 <?xml version="1.0"?>
383 <!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
384                "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"
386   <!ENTITY % local.common.attrib "xmlns:xi  CDATA  #FIXED 'http://www.w3.org/2003/XInclude'">
389     }
390     chomp($doctype_header);
391     
392     # All the files are written in subdirectories beneath here.
393     $TMPL_DIR = $TMPL_DIR ? $TMPL_DIR : "$ROOT_DIR/tmpl";
394     
395     # This is where we put all the DocBook output.
396     $DB_OUTPUT_DIR = $DB_OUTPUT_DIR ? $DB_OUTPUT_DIR : "$ROOT_DIR/xml";
397   
398     # This file contains the object hierarchy.
399     $OBJECT_TREE_FILE = "$ROOT_DIR/$MODULE.hierarchy";
400   
401     # This file contains the interfaces.
402     $INTERFACES_FILE = "$ROOT_DIR/$MODULE.interfaces";
403     
404     # This file contains the prerequisites.
405     $PREREQUISITES_FILE = "$ROOT_DIR/$MODULE.prerequisites";
406     
407     # This file contains signal arguments and names.
408     $SIGNALS_FILE = "$ROOT_DIR/$MODULE.signals";
409     
410     # The file containing Arg information.
411     $ARGS_FILE = "$ROOT_DIR/$MODULE.args";
412   
413     # Create the root DocBook output directory if it doens't exist.
414     if (! -e $DB_OUTPUT_DIR) {
415         mkdir ("$DB_OUTPUT_DIR", 0777)
416             || die "Can't create directory: $DB_OUTPUT_DIR";
417     }
418     
419     &ReadKnownSymbols ("$ROOT_DIR/$MODULE-sections.txt");
420     &ReadSignalsFile ($SIGNALS_FILE);
421     &ReadArgsFile ($ARGS_FILE);
422     &ReadObjectHierarchy;
423     &ReadInterfaces;
424     &ReadPrerequisites;
425     
426     &ReadDeclarationsFile ("$ROOT_DIR/$MODULE-decl.txt", 0);
427     if (-f "$ROOT_DIR/$MODULE-overrides.txt") {
428         &ReadDeclarationsFile ("$ROOT_DIR/$MODULE-overrides.txt", 1);
429     }
430     
431     for my $dir (@SOURCE_DIRS) {
432         &ReadSourceDocumentation ($dir);
433     }
434     
435     my $changed = &OutputDB ("$ROOT_DIR/$MODULE-sections.txt");
436     
437     # If any of the DocBook files have changed, update the timestamp file (so
438     # it can be used for Makefile dependencies).
439     if ($changed || ! -e "$ROOT_DIR/sgml.stamp") {
440     
441         # try to detect the common prefix
442         # GtkWidget, GTK_WIDGET, gtk_widget -> gtk
443         if ($NAME_SPACE eq "") {
444             $NAME_SPACE="";
445             my $pos=0;
446             my $ratio=0.0;
447             do {
448                 my %prefix;
449                 my $letter="";
450                 foreach my $symbol (keys(%IndexEntriesFull)) {
451                     if(($NAME_SPACE eq "") || $symbol =~ /^$NAME_SPACE/i) {
452                         if (length($symbol)>$pos) {
453                             $letter=substr($symbol,$pos,1);
454                             # stop prefix scanning
455                             if ($letter eq "_") {
456                                 # stop on "_"
457                                 last;
458                             }
459                             # Should we also stop on a uppercase char, if last was lowercase
460                             #   GtkWidget, if we have the 'W' and had the 't' before
461                             # or should we count upper and lowercase, and stop one 2nd uppercase, if we already had a lowercase
462                             #   GtkWidget, the 'W' would be the 2nd uppercase and with 't','k' we had lowercase chars before
463                             # need to recound each time as this is per symbol
464                             $prefix{uc($letter)}++;
465                         }
466                     }
467                 }
468                 if ($letter ne "" && $letter ne "_") {
469                     my $maxletter="";
470                     my $maxsymbols=0;
471                     foreach $letter (keys(%prefix)) {
472                         #print "$letter: $prefix{$letter}.\n";
473                         if ($prefix{$letter}>$maxsymbols) {
474                             $maxletter=$letter;
475                             $maxsymbols=$prefix{$letter};
476                         }
477                     }
478                     $ratio = scalar(keys(%IndexEntriesFull)) / $prefix{$maxletter};
479                     #print "most symbols start with $maxletter, that is ". (100 * $ratio) ." %\n";
480                     if ($ratio > 0.9) {
481                         # do another round
482                         $NAME_SPACE .= $maxletter;
483                     }
484                     $pos++;
485                 }
486                 else {
487                     $ratio=0.0;
488                 }
489             } while ($ratio > 0.9);
490             #print "most symbols start with $NAME_SPACE\n";
491         }
492     
493         &OutputIndexFull;
494         &OutputDeprecatedIndex;
495         &OutputSinceIndexes;
496         &OutputAnnotationGlossary;
497     
498         open (TIMESTAMP, ">$ROOT_DIR/sgml.stamp")
499             || die "Can't create $ROOT_DIR/sgml.stamp: $!";
500         print (TIMESTAMP "timestamp");
501         close (TIMESTAMP);
502     }
505 #############################################################################
506 # Function    : OutputObjectList
507 # Description : This outputs the alphabetical list of objects, in a columned
508 #                table.
509 #               FIXME: Currently this also outputs ancestor objects
510 #                which may not actually be in this module.
511 # Arguments   : none
512 #############################################################################
514 sub OutputObjectList {
515     my $cols = 3;
517     # FIXME: use .xml
518     # my $old_object_index = "$DB_OUTPUT_DIR/object_index.xml";
519     my $old_object_index = "$DB_OUTPUT_DIR/object_index.sgml";
520     my $new_object_index = "$DB_OUTPUT_DIR/object_index.new";
522     open (OUTPUT, ">$new_object_index")
523         || die "Can't create $new_object_index: $!";
525     print (OUTPUT <<EOF);
526 ${\( MakeDocHeader ("informaltable") )}
527 <informaltable pgwide="1" frame="none">
528 <tgroup cols="$cols">
529 <colspec colwidth="1*"/>
530 <colspec colwidth="1*"/>
531 <colspec colwidth="1*"/>
532 <tbody>
535     my $count = 0;
536     my $object;
537     foreach $object (sort (@Objects)) {
538         my $xref = &MakeXRef ($object);
539         if ($count % $cols == 0) { print (OUTPUT "<row>\n"); }
540         print (OUTPUT "<entry>$xref</entry>\n");
541         if ($count % $cols == ($cols - 1)) { print (OUTPUT "</row>\n"); }
542         $count++;
543     }
544     if ($count == 0) {
545         # emit an empty row, since empty tables are invalid
546         print (OUTPUT "<row><entry> </entry></row>\n");
547     }
548     else {
549         if ($count % $cols > 0) {
550             print (OUTPUT "</row>\n");
551         }
552     }
554     print (OUTPUT <<EOF);
555 </tbody></tgroup></informaltable>
557     close (OUTPUT);
559     &UpdateFileIfChanged ($old_object_index, $new_object_index, 0);
562 #############################################################################
563 # Function    : TrimTextBlock
564 # Description : Trims extra whitespace. Empty lines inside a block are
565 #                preserved.
566 # Arguments   : $desc - the text block to trim. May contain newlines.
567 #############################################################################
569 sub TrimTextBlock {
570   my ($desc) = @_;
571   
572   # strip leading spaces on the block
573   $desc =~ s/^\s+//s;
574   # strip trailing spaces on every line
575   $desc =~ s/\s+$/\n/mg;
576   
577   return $desc;
581 #############################################################################
582 # Function    : OutputDB
583 # Description : This collects the output for each section of the docs, and
584 #                outputs each file when the end of the section is found.
585 # Arguments   : $file - the $MODULE-sections.txt file which contains all of
586 #                the functions/macros/structs etc. being documented, organised
587 #                into sections and subsections.
588 #############################################################################
590 sub OutputDB {
591     my ($file) = @_;
593     @TRACE@("Reading: $file\n");
594     open (INPUT, $file)
595         || die "Can't open $file: $!";
596     my $filename = "";
597     my $book_top = "";
598     my $book_bottom = "";
599     my $includes = (defined $DEFAULT_INCLUDES) ? $DEFAULT_INCLUDES : "";
600     my $section_includes = "";
601     my $in_section = 0;
602     my $title = "";
603     my $section_id = "";
604     my $subsection = "";
605     my $num_symbols;
606     my $changed = 0;
607     my $functions_synop = "";
608     my $other_synop = "";
609     my $functions_details = "";
610     my $other_details = "";
611     my $signals_synop = "";
612     my $signals_desc = "";
613     my $args_synop = "";
614     my $child_args_synop = "";
615     my $style_args_synop = "";
616     my $args_desc = "";
617     my $child_args_desc = "";
618     my $style_args_desc = "";
619     my $hierarchy_str = "";
620     my @hierarchy = ();
621     my $interfaces = "";
622     my $implementations = "";
623     my $prerequisites = "";
624     my $derived = "";
625     my @file_objects = ();
626     my %templates = ();
627     my %symbol_def_line = ();
629     # merge the source docs, in case there are no templates
630     &MergeSourceDocumentation;
632     while (<INPUT>) {
633         if (m/^#/) {
634             next;
636         } elsif (m/^<SECTION>/) {
637             $num_symbols = 0;
638             $in_section = 1;
639             @file_objects = ();
640             %symbol_def_line = ();
642         } elsif (m/^<SUBSECTION\s*(.*)>/i) {
643             $other_synop .= "\n";
644             $functions_synop .= "\n";
645             $subsection = $1;
647         } elsif (m/^<SUBSECTION>/) {
649         } elsif (m/^<TITLE>(.*)<\/TITLE>/) {
650             $title = $1;
651             @TRACE@("Section: $title\n");
653             # We don't want warnings if object & class structs aren't used.
654             $DeclarationOutput{$title} = 1;
655             $DeclarationOutput{"${title}Class"} = 1;
656             $DeclarationOutput{"${title}Iface"} = 1;
657             $DeclarationOutput{"${title}Interface"} = 1;
659         } elsif (m/^<FILE>(.*)<\/FILE>/) {
660             $filename = $1;
661             if (! defined $templates{$filename}) {
662                if (&ReadTemplateFile ("$TMPL_DIR/$filename", 1)) {
663                    &MergeSourceDocumentation;
664                    $templates{$filename}=$.;
665                }
666             } else {
667                 &LogWarning ($file, $., "Double <FILE>$filename</FILE> entry. ".
668                     "Previous occurrence on line ".$templates{$filename}.".");
669             }
670             if (($title eq "") and (defined $SourceSymbolDocs{"$TMPL_DIR/$filename:Title"})) {
671                 $title = $SourceSymbolDocs{"$TMPL_DIR/$filename:Title"};
672                  # Remove trailing blanks
673                 $title =~ s/\s+$//;
674            }
676         } elsif (m/^<INCLUDE>(.*)<\/INCLUDE>/) {
677             if ($in_section) {
678                 $section_includes = $1;
679             } else {
680                 if (defined $DEFAULT_INCLUDES) {
681                     &LogWarning ($file, $., "Default <INCLUDE> being overridden by command line option.");
682                 }
683                 else {
684                     $includes = $1;
685                 }
686             }
688         } elsif (m/^<\/SECTION>/) {
689             @TRACE@("End of section: $title\n");
690             if ($num_symbols > 0) {
691                 # collect documents
692                 $book_bottom .= "    <xi:include href=\"xml/$filename.xml\"/>\n";
694                 if (defined ($SourceSymbolDocs{"$TMPL_DIR/$filename:Include"})) {
695                     if ($section_includes) {
696                         &LogWarning ($file, $., "Section <INCLUDE> being overridden by inline comments.");
697                     }
698                     $section_includes = $SourceSymbolDocs{"$TMPL_DIR/$filename:Include"};
699                 }
700                 if ($section_includes eq "") {
701                     $section_includes = $includes;
702                 }
704                  $signals_synop =~ s/^\n*//g;
705                  $signals_synop =~ s/\n+$/\n/g;
706                 if ($signals_synop ne '') {
707                     $signals_synop = <<EOF;
708 <refsect1 id="$section_id.signals" role="signal_proto">
709 <title role="signal_proto.title">Signals</title>
710 <informaltable frame="none">
711 <tgroup cols="3">
712 <colspec colname="signals_return" colwidth="150px"/>
713 <colspec colname="signals_name" colwidth="300px"/>
714 <colspec colname="signals_flags" colwidth="200px"/>
715 <tbody>
716 ${signals_synop}
717 </tbody>
718 </tgroup>
719 </informaltable>
720 </refsect1>
722                      $signals_desc = TrimTextBlock($signals_desc);
723                     $signals_desc  = <<EOF;
724 <refsect1 id="$section_id.signal-details" role="signals">
725 <title role="signals.title">Signal Details</title>
726 $signals_desc
727 </refsect1>
729                 }
731                 $args_synop =~ s/^\n*//g;
732                 $args_synop =~ s/\n+$/\n/g;
733                 if ($args_synop ne '') {
734                     $args_synop = <<EOF;
735 <refsect1 id="$section_id.properties" role="properties">
736 <title role="properties.title">Properties</title>
737 <informaltable frame="none">
738 <tgroup cols="3">
739 <colspec colname="properties_type" colwidth="150px"/>
740 <colspec colname="properties_name" colwidth="300px"/>
741 <colspec colname="properties_flags" colwidth="200px"/>
742 <tbody>
743 ${args_synop}
744 </tbody>
745 </tgroup>
746 </informaltable>
747 </refsect1>
749                      $args_desc = TrimTextBlock($args_desc);
750                     $args_desc  = <<EOF;
751 <refsect1 id="$section_id.property-details" role="property_details">
752 <title role="property_details.title">Property Details</title>
753 $args_desc
754 </refsect1>
756                 }
758                 $child_args_synop =~ s/^\n*//g;
759                 $child_args_synop =~ s/\n+$/\n/g;
760                 if ($child_args_synop ne '') {
761                     $args_synop .= <<EOF;
762 <refsect1 id="$section_id.child-properties" role="child_properties">
763 <title role="child_properties.title">Child Properties</title>
764 <informaltable frame="none">
765 <tgroup cols="3">
766 <colspec colname="child_properties_type" colwidth="150px"/>
767 <colspec colname="child_properties_name" colwidth="300px"/>
768 <colspec colname="child_properties_flags" colwidth="200px"/>
769 <tbody>
770 ${child_args_synop}
771 </tbody>
772 </tgroup>
773 </informaltable>
774 </refsect1>
776                      $child_args_desc = TrimTextBlock($child_args_desc);
777                      $args_desc .= <<EOF;
778 <refsect1 id="$section_id.child-property-details" role="child_property_details">
779 <title role="child_property_details.title">Child Property Details</title>
780 $child_args_desc
781 </refsect1>
783                 }
785                 $style_args_synop =~ s/^\n*//g;
786                 $style_args_synop =~ s/\n+$/\n/g;
787                 if ($style_args_synop ne '') {
788                     $args_synop .= <<EOF;
789 <refsect1 id="$section_id.style-properties" role="style_properties">
790 <title role="style_properties.title">Style Properties</title>
791 <informaltable frame="none">
792 <tgroup cols="3">
793 <colspec colname="style_properties_type" colwidth="150px"/>
794 <colspec colname="style_properties_name" colwidth="300px"/>
795 <colspec colname="style_properties_flags" colwidth="200px"/>
796 <tbody>
797 ${style_args_synop}
798 </tbody>
799 </tgroup>
800 </informaltable>
801 </refsect1>
803                      $style_args_desc = TrimTextBlock($style_args_desc);
804                     $args_desc .= <<EOF;
805 <refsect1 id="$section_id.style-property-details" role="style_properties_details">
806 <title role="style_properties_details.title">Style Property Details</title>
807 $style_args_desc
808 </refsect1>
810                 }
812                 $hierarchy_str = &AddTreeLineArt(\@hierarchy);
813                 if ($hierarchy_str ne "") {
814                     $hierarchy_str = <<EOF;
815 <refsect1 id="$section_id.object-hierarchy" role="object_hierarchy">
816 <title role="object_hierarchy.title">Object Hierarchy</title>
817 <screen>$hierarchy_str
818 </screen>
819 </refsect1>
821                 }
823                  $interfaces =~ TrimTextBlock($interfaces);
824                 if ($interfaces ne "") {
825                     $interfaces = <<EOF;
826 <refsect1 id="$section_id.implemented-interfaces" role="impl_interfaces">
827 <title role="impl_interfaces.title">Implemented Interfaces</title>
828 $interfaces
829 </refsect1>
831                 }
833                  $implementations = TrimTextBlock($implementations);
834                 if ($implementations ne "") {
835                     $implementations = <<EOF;
836 <refsect1 id="$section_id.implementations" role="implementations">
837 <title role="implementations.title">Known Implementations</title>
838 $implementations
839 </refsect1>
841                 }
843                  $prerequisites = TrimTextBlock($prerequisites);
844                 if ($prerequisites ne "") {
845                     $prerequisites = <<EOF;
846 <refsect1 id="$section_id.prerequisites" role="prerequisites">
847 <title role="prerequisites.title">Prerequisites</title>
848 $prerequisites
849 </refsect1>
851                 }
853                  $derived = TrimTextBlock($derived);
854                 if ($derived ne "") {
855                     $derived = <<EOF;
856 <refsect1 id="$section_id.derived-interfaces" role="derived_interfaces">
857 <title role="derived_interfaces.title">Known Derived Interfaces</title>
858 $derived
859 </refsect1>
861                 }
863                 $functions_synop =~ s/^\n*//g;
864                 $functions_synop =~ s/\n+$/\n/g;
865                 if ($functions_synop ne '') {
866                   $functions_synop = <<EOF;
867 <refsect1 id="$section_id.functions" role="functions_proto">
868 <title role="functions_proto.title">Functions</title>
869 <informaltable pgwide="1" frame="none">
870 <tgroup cols="2">
871 <colspec colname="functions_return" colwidth="150px"/>
872 <colspec colname="functions_name"/>
873 <tbody>
874 ${functions_synop}
875 </tbody>
876 </tgroup>
877 </informaltable>
878 </refsect1>
880                 }
882                 $other_synop =~ s/^\n*//g;
883                 $other_synop =~ s/\n+$/\n/g;
884                 if ($other_synop ne '') {
885                   $other_synop = <<EOF;
886 <refsect1 id="$section_id.other" role="other_proto">
887 <title role="other_proto.title">Types and Values</title>
888 <informaltable role="enum_members_table" pgwide="1" frame="none">
889 <tgroup cols="2">
890 <colspec colname="name" colwidth="150px"/>
891 <colspec colname="description"/>
892 <tbody>
893 ${other_synop}
894 </tbody>
895 </tgroup>
896 </informaltable>
897 </refsect1>
899                 }
901                 my $file_changed = &OutputDBFile ($filename, $title, $section_id,
902                                                     $section_includes,
903                                                     \$functions_synop, \$other_synop,
904                                                     \$functions_details, \$other_details,
905                                                     \$signals_synop, \$signals_desc,
906                                                     \$args_synop, \$args_desc,
907                                                     \$hierarchy_str, \$interfaces,
908                                                     \$implementations,
909                                                     \$prerequisites, \$derived,
910                                                     \@file_objects);
911                 if ($file_changed) {
912                     $changed = 1;
913                 }
914             }
915             $title = "";
916             $section_id = "";
917             $subsection = "";
918             $in_section = 0;
919             $section_includes = "";
920             $functions_synop = "";
921             $other_synop = "";
922             $functions_details = "";
923             $other_details = "";
924             $signals_synop = "";
925             $signals_desc = "";
926             $args_synop = "";
927             $child_args_synop = "";
928             $style_args_synop = "";
929             $args_desc = "";
930             $child_args_desc = "";
931             $style_args_desc = "";
932             $hierarchy_str = "";
933             @hierarchy = ();
934             $interfaces = "";
935             $implementations = "";
936             $prerequisites = "";
937             $derived = "";
939         } elsif (m/^(\S+)/) {
940             my $symbol = $1;
941             @TRACE@("  Symbol: $symbol in subsection: $subsection\n");
943             # check for duplicate entries
944             if (! defined $symbol_def_line{$symbol}) {
945                 my $declaration = $Declarations{$symbol};
946                 if (defined ($declaration)) {
947                     if (&CheckIsObject ($symbol)) {
948                         push @file_objects, $symbol;
949                     }
950                     # We don't want standard macros/functions of GObjects,
951                     # or private declarations.
952                     if ($subsection ne "Standard" && $subsection ne "Private") {
953                         my ($synop, $desc) = &OutputDeclaration ($symbol,
954                                                                  $declaration);
955                         my $type = $DeclarationTypes {$symbol};
956         
957                         if ($type eq 'FUNCTION' || $type eq 'USER_FUNCTION') {
958                           $functions_synop .= $synop;
959                           $functions_details .= $desc;
960                         } elsif ($type eq 'MACRO' && $declaration =~ /$symbol[ ]*\(/) {
961                           $functions_synop .= $synop;
962                           $functions_details .= $desc;
963                         } else {
964                           $other_synop .= $synop;
965                           $other_details .= $desc;
966                         }
967                     }
968                     my ($sig_synop, $sig_desc) = &GetSignals ($symbol);
969                     my ($arg_synop, $child_arg_synop, $style_arg_synop,
970                         $arg_desc, $child_arg_desc, $style_arg_desc) = &GetArgs ($symbol);
971                     my $ifaces = &GetInterfaces ($symbol);
972                     my $impls = &GetImplementations ($symbol);
973                     my $prereqs = &GetPrerequisites ($symbol);
974                     my $der = &GetDerived ($symbol);
975                     @hierarchy = &GetHierarchy ($symbol, \@hierarchy);
977                     $signals_synop .= $sig_synop;
978                     $signals_desc .= $sig_desc;
979                     $args_synop .= $arg_synop;
980                     $child_args_synop .= $child_arg_synop;
981                     $style_args_synop .= $style_arg_synop;
982                     $args_desc .= $arg_desc;
983                     $child_args_desc .= $child_arg_desc;
984                     $style_args_desc .= $style_arg_desc;
985                     $interfaces .= $ifaces;
986                     $implementations .= $impls;
987                     $prerequisites .= $prereqs;
988                     $derived .= $der;
990                     # Note that the declaration has been output.
991                     $DeclarationOutput{$symbol} = 1;
992                 } elsif ($subsection ne "Standard" && $subsection ne "Private") {
993                     $UndeclaredSymbols{$symbol} = 1;
994                     &LogWarning ($file, $., "No declaration found for $symbol.");
995                 }
996                 $num_symbols++;
997                 $symbol_def_line{$symbol}=$.;
999                 if ($section_id eq "") {
1000                     if($title eq "" && $filename eq "") {
1001                         &LogWarning ($file, $., "Section has no title and no file.");
1002                     }
1003                     # FIXME: one of those would be enough
1004                     # filename should be an internal detail for gtk-doc
1005                     if ($title eq "") {
1006                         $title = $filename;
1007                     } elsif ($filename eq "") {
1008                         $filename = $title;
1009                     }
1010                     $filename =~ s/\s/_/g;
1012                     $section_id = $SourceSymbolDocs{"$TMPL_DIR/$filename:Section_Id"};
1013                     if (defined ($section_id) && $section_id !~ m/^\s*$/) {
1014                         # Remove trailing blanks and use as is
1015                         $section_id =~ s/\s+$//;
1016                     } elsif (&CheckIsObject ($title)) {
1017                         # GObjects use their class name as the ID.
1018                         $section_id = &CreateValidSGMLID ($title);
1019                     } else {
1020                         $section_id = &CreateValidSGMLID ("$MODULE-$title");
1021                     }
1022                 }
1023                 $SymbolSection{$symbol}=$title;
1024                 $SymbolSectionId{$symbol}=$section_id;
1025             }
1026             else {
1027                 &LogWarning ($file, $., "Double symbol entry for $symbol. ".
1028                     "Previous occurrence on line ".$symbol_def_line{$symbol}.".");
1029             }
1030         }
1031     }
1032     close (INPUT);
1034     &OutputMissingDocumentation;
1035     &OutputUndeclaredSymbols;
1036     &OutputUnusedSymbols;
1038     if ($OUTPUT_ALL_SYMBOLS) {
1039         &OutputAllSymbols;
1040     }
1041     if ($OUTPUT_SYMBOLS_WITHOUT_SINCE) {
1042         &OutputSymbolsWithoutSince;
1043     }
1045     for $filename (split (' ', $EXPAND_CONTENT_FILES)) {
1046         my $file_changed = &OutputExtraFile ($filename);
1047         if ($file_changed) {
1048             $changed = 1;
1049         }
1050     }
1052     &OutputBook ($book_top, $book_bottom);
1054     return $changed;
1057 #############################################################################
1058 # Function    : OutputIndex
1059 # Description : This writes an indexlist that can be included into the main-
1060 #               document into an <index> tag.
1061 #############################################################################
1063 sub OutputIndex {
1064     my ($basename, $apiindexref ) = @_;
1065     my %apiindex = %{$apiindexref};
1066     my $old_index = "$DB_OUTPUT_DIR/$basename.xml";
1067     my $new_index = "$DB_OUTPUT_DIR/$basename.new";
1068     my $lastletter = " ";
1069     my $divopen = 0;
1070     my $symbol;
1071     my $short_symbol;
1073     open (OUTPUT, ">$new_index")
1074         || die "Can't create $new_index";
1076     print (OUTPUT &MakeDocHeader ("indexdiv")."\n<indexdiv id=\"$basename\">\n");
1078     @TRACE@("generate $basename index (".%apiindex." entries)\n");
1080     # do a case insensitive sort while chopping off the prefix
1081     foreach my $hash (
1082         sort { $$a{criteria} cmp $$b{criteria} or $$a{original} cmp $$b{original} }
1083         map { my $x = uc($_); $x =~ s/^$NAME_SPACE\_?(.*)/$1/i; { criteria => $x, original => $_, short => $1 } }
1084         keys %apiindex) {
1086         $symbol = $$hash{original};
1087         if (defined($$hash{short})) {
1088             $short_symbol = $$hash{short};
1089         } else {
1090             $short_symbol = $symbol;
1091         }
1093         # generate a short symbol description
1094         my $symbol_desc = "";
1095         my $symbol_section = "";
1096         my $symbol_section_id = "";
1097         my $symbol_type = "";
1098         if (defined($DeclarationTypes{$symbol})) {
1099           $symbol_type = lc($DeclarationTypes{$symbol});
1100         }
1101         if ($symbol_type eq "") {
1102             @TRACE@("trying symbol $symbol\n");
1103             if ($symbol =~ m/(.*)::(.*)/) {
1104                 my $oname = $1;
1105                 my $osym = $2;
1106                 my $i;
1107                 @TRACE@("  trying object signal ${oname}:$osym in ".$#SignalNames." signals\n");
1108                 for ($i = 0; $i <= $#SignalNames; $i++) {
1109                     if ($SignalNames[$i] eq $osym) {
1110                         $symbol_type = "object signal";
1111                         if (defined($SymbolSection{$oname})) {
1112                            $symbol_section = $SymbolSection{$oname};
1113                            $symbol_section_id = $SymbolSectionId{$oname};
1114                         }
1115                         last;
1116                     }
1117                 }
1118             } elsif ($symbol =~ m/(.*):(.*)/) {
1119                 my $oname = $1;
1120                 my $osym = $2;
1121                 my $i;
1122                 @TRACE@("  trying object property ${oname}::$osym in ".$#ArgNames." properties\n");
1123                 for ($i = 0; $i <= $#ArgNames; $i++) {
1124                     @TRACE@("    ".$ArgNames[$i]."\n");
1125                     if ($ArgNames[$i] eq $osym) {
1126                         $symbol_type = "object property";
1127                         if (defined($SymbolSection{$oname})) {
1128                            $symbol_section = $SymbolSection{$oname};
1129                            $symbol_section_id = $SymbolSectionId{$oname};
1130                         }
1131                         last;
1132                     }
1133                 }
1134             }
1135         } else {
1136            if (defined($SymbolSection{$symbol})) {
1137                $symbol_section = $SymbolSection{$symbol};
1138                $symbol_section_id = $SymbolSectionId{$symbol};
1139            }
1140         }
1141         if ($symbol_type ne "") {
1142            $symbol_desc=", $symbol_type";
1143            if ($symbol_section ne "") {
1144                $symbol_desc.=" in <link linkend=\"$symbol_section_id\">$symbol_section</link>";
1145                #$symbol_desc.=" in ". &ExpandAbbreviations($symbol, "#$symbol_section");
1146            }
1147         }
1149         my $curletter = uc(substr($short_symbol,0,1));
1150         my $id = $apiindex{$symbol};
1152         @TRACE@("  add symbol $symbol with $id to index in section $curletter\n");
1154         if ($curletter ne $lastletter) {
1155             $lastletter = $curletter;
1157             if ($divopen == 1) {
1158                 print (OUTPUT "</indexdiv>\n");
1159             }
1160             print (OUTPUT "<indexdiv><title>$curletter</title>\n");
1161             $divopen = 1;
1162         }
1164         print (OUTPUT <<EOF);
1165 <indexentry><primaryie linkends="$id"><link linkend="$id">$symbol</link>$symbol_desc</primaryie></indexentry>
1167     }
1169     if ($divopen == 1) {
1170         print (OUTPUT "</indexdiv>\n");
1171     }
1172     print (OUTPUT "</indexdiv>\n");
1173     close (OUTPUT);
1175     &UpdateFileIfChanged ($old_index, $new_index, 0);
1179 #############################################################################
1180 # Function    : OutputIndexFull
1181 # Description : This writes the full api indexlist that can be included into the
1182 #               main document into an <index> tag.
1183 #############################################################################
1185 sub OutputIndexFull {
1186     &OutputIndex ("api-index-full", \%IndexEntriesFull);
1190 #############################################################################
1191 # Function    : OutputDeprecatedIndex
1192 # Description : This writes the deprecated api indexlist that can be included
1193 #               into the main document into an <index> tag.
1194 #############################################################################
1196 sub OutputDeprecatedIndex {
1197     &OutputIndex ("api-index-deprecated", \%IndexEntriesDeprecated);
1201 #############################################################################
1202 # Function    : OutputSinceIndexes
1203 # Description : This writes the 'since' api indexlists that can be included into
1204 #               the main document into an <index> tag.
1205 #############################################################################
1207 sub OutputSinceIndexes {
1208     my @sinces = keys %{{ map { $_ => 1 } values %Since }};
1210     foreach my $version (@sinces) {
1211         @TRACE@("Since : [$version]\n");
1212         # TODO make filtered hash
1213         #my %index = grep { $Since{$_} eq $version } %IndexEntriesSince;
1214         my %index = map { $_ => $IndexEntriesSince{$_} } grep { $Since{$_} eq $version } keys %IndexEntriesSince;
1216         &OutputIndex ("api-index-$version", \%index);
1217     }
1220 #############################################################################
1221 # Function    : OutputAnnotationGlossary
1222 # Description : This writes a glossary of the used annotation terms into a
1223 #               separate glossary file that can be included into the main
1224 #               document.
1225 #############################################################################
1227 sub OutputAnnotationGlossary {
1228     my $old_glossary = "$DB_OUTPUT_DIR/annotation-glossary.xml";
1229     my $new_glossary = "$DB_OUTPUT_DIR/annotation-glossary.new";
1230     my $lastletter = " ";
1231     my $divopen = 0;
1233     # if there are no annotations used return
1234     return if (! keys(%AnnotationsUsed));
1236     # add acronyms that are referenced from acronym text
1237 rerun:
1238     foreach my $annotation (keys(%AnnotationsUsed)) {
1239         if(defined($AnnotationDefinition{$annotation})) {
1240             if($AnnotationDefinition{$annotation} =~ m/<acronym>([\w ]+)<\/acronym>/) {
1241                 if (!exists($AnnotationsUsed{$1})) {
1242                     $AnnotationsUsed{$1} = 1;
1243                     goto rerun;
1244                 }
1245             }
1246         }
1247     }
1249     open (OUTPUT, ">$new_glossary")
1250         || die "Can't create $new_glossary";
1252     print (OUTPUT  <<EOF);
1253 ${\( MakeDocHeader ("glossary") )}
1254 <glossary id="annotation-glossary">
1255   <title>Annotation Glossary</title>
1258     foreach my $annotation (sort({lc $a cmp lc $b} keys(%AnnotationsUsed))) {
1259         if(defined($AnnotationDefinition{$annotation})) {
1260             my $def = $AnnotationDefinition{$annotation};
1261             my $curletter = uc(substr($annotation,0,1));
1263             if ($curletter ne $lastletter) {
1264                 $lastletter = $curletter;
1266                 if ($divopen == 1) {
1267                     print (OUTPUT "</glossdiv>\n");
1268                 }
1269                 print (OUTPUT "<glossdiv><title>$curletter</title>\n");
1270                 $divopen = 1;
1271             }
1272             print (OUTPUT <<EOF);
1273     <glossentry>
1274       <glossterm><anchor id="annotation-glossterm-$annotation"/>$annotation</glossterm>
1275       <glossdef>
1276         <para>$def</para>
1277       </glossdef>
1278     </glossentry>
1280         }
1281     }
1283     if ($divopen == 1) {
1284         print (OUTPUT "</glossdiv>\n");
1285     }
1286     print (OUTPUT "</glossary>\n");
1287     close (OUTPUT);
1289     &UpdateFileIfChanged ($old_glossary, $new_glossary, 0);
1292 #############################################################################
1293 # Function    : ReadKnownSymbols
1294 # Description : This collects the names of non-private symbols from the
1295 #               $MODULE-sections.txt file.
1296 # Arguments   : $file - the $MODULE-sections.txt file which contains all of
1297 #                the functions/macros/structs etc. being documented, organised
1298 #                into sections and subsections.
1299 #############################################################################
1301 sub ReadKnownSymbols {
1302     my ($file) = @_;
1304     my $subsection = "";
1306     @TRACE@("Reading: $file\n");
1307     open (INPUT, $file)
1308         || die "Can't open $file: $!";
1310     while (<INPUT>) {
1311         if (m/^#/) {
1312             next;
1314         } elsif (m/^<SECTION>/) {
1315             $subsection = "";
1317         } elsif (m/^<SUBSECTION\s*(.*)>/i) {
1318             $subsection = $1;
1320         } elsif (m/^<SUBSECTION>/) {
1321             next;
1323         } elsif (m/^<TITLE>(.*)<\/TITLE>/) {
1324             next;
1326         } elsif (m/^<FILE>(.*)<\/FILE>/) {
1327             $KnownSymbols{"$TMPL_DIR/$1:Long_Description"} = 1;
1328             $KnownSymbols{"$TMPL_DIR/$1:Short_Description"} = 1;
1329             next;
1331         } elsif (m/^<INCLUDE>(.*)<\/INCLUDE>/) {
1332             next;
1334         } elsif (m/^<\/SECTION>/) {
1335             next;
1337         } elsif (m/^(\S+)/) {
1338             my $symbol = $1;
1340             if ($subsection ne "Standard" && $subsection ne "Private") {
1341                 $KnownSymbols{$symbol} = 1;
1342             }
1343             else {
1344                 $KnownSymbols{$symbol} = 0;
1345             }
1346         }
1347     }
1348     close (INPUT);
1352 #############################################################################
1353 # Function    : OutputDeclaration
1354 # Description : Returns the synopsis and detailed description DocBook
1355 #                describing one function/macro etc.
1356 # Arguments   : $symbol - the name of the function/macro begin described.
1357 #                $declaration - the declaration of the function/macro.
1358 #############################################################################
1360 sub OutputDeclaration {
1361     my ($symbol, $declaration) = @_;
1363     my $type = $DeclarationTypes {$symbol};
1364     if ($type eq 'MACRO') {
1365         return &OutputMacro ($symbol, $declaration);
1366     } elsif ($type eq 'TYPEDEF') {
1367         return &OutputTypedef ($symbol, $declaration);
1368     } elsif ($type eq 'STRUCT') {
1369         return &OutputStruct ($symbol, $declaration);
1370     } elsif ($type eq 'ENUM') {
1371         return &OutputEnum ($symbol, $declaration);
1372     } elsif ($type eq 'UNION') {
1373         return &OutputUnion ($symbol, $declaration);
1374     } elsif ($type eq 'VARIABLE') {
1375         return &OutputVariable ($symbol, $declaration);
1376     } elsif ($type eq 'FUNCTION') {
1377         return &OutputFunction ($symbol, $declaration, $type);
1378     } elsif ($type eq 'USER_FUNCTION') {
1379         return &OutputFunction ($symbol, $declaration, $type);
1380     } else {
1381         die "Unknown symbol type";
1382     }
1386 #############################################################################
1387 # Function    : OutputSymbolTraits
1388 # Description : Returns the Since and StabilityLevel paragraphs for a symbol.
1389 # Arguments   : $symbol - the name of the function/macro begin described.
1390 #############################################################################
1392 sub OutputSymbolTraits {
1393     my ($symbol) = @_;
1394     my $desc = "";
1396     if (exists $Since{$symbol}) {
1397         my $link_id = "api-index-".$Since{$symbol};
1398         $desc .= "<para role=\"since\">Since: <link linkend=\"$link_id\">$Since{$symbol}</link></para>";
1399     }
1400     if (exists $StabilityLevel{$symbol}) {
1401         my $stability = $StabilityLevel{$symbol};
1402         $AnnotationsUsed{$stability} = 1;
1403         $desc .= "<para role=\"stability\">Stability Level: <acronym>$stability</acronym></para>";
1404     }
1405     return $desc;
1408 #############################################################################
1409 # Function    : Output{Symbol,Section}ExtraLinks
1410 # Description : Returns extralinks for the symbol (if enabled).
1411 # Arguments   : $symbol - the name of the function/macro begin described.
1412 #############################################################################
1414 sub uri_escape {
1415     my $text = $_[0];
1416     return undef unless defined $text;
1418     # Build a char to hex map
1419     my %escapes = ();
1420     for (0..255) {
1421             $escapes{chr($_)} = sprintf("%%%02X", $_);
1422     }
1424     # Default unsafe characters.  RFC 2732 ^(uric - reserved)
1425     $text =~ s/([^A-Za-z0-9\-_.!~*'()])/$escapes{$1}/g;
1427     return $text;
1430 sub OutputSymbolExtraLinks {
1431     my ($symbol) = @_;
1432     my $desc = "";
1434     if (0) { # NEW FEATURE: needs configurability
1435     my $sstr = &uri_escape($symbol);
1436     my $mstr = &uri_escape($MODULE);
1437     $desc .= <<EOF;
1438 <ulink role="extralinks" url="http://www.google.com/codesearch?q=$sstr">code search</ulink>
1439 <ulink role="extralinks" url="http://library.gnome.org/edit?module=$mstr&amp;symbol=$sstr">edit documentation</ulink>
1441     }
1442     return $desc;
1445 sub OutputSectionExtraLinks {
1446     my ($symbol,$docsymbol) = @_;
1447     my $desc = "";
1449     if (0) { # NEW FEATURE: needs configurability
1450     my $sstr = &uri_escape($symbol);
1451     my $mstr = &uri_escape($MODULE);
1452     my $dsstr = &uri_escape($docsymbol);
1453     $desc .= <<EOF;
1454 <ulink role="extralinks" url="http://www.google.com/codesearch?q=$sstr">code search</ulink>
1455 <ulink role="extralinks" url="http://library.gnome.org/edit?module=$mstr&amp;symbol=$dsstr">edit documentation</ulink>
1457     }
1458     return $desc;
1462 #############################################################################
1463 # Function    : OutputMacro
1464 # Description : Returns the synopsis and detailed description of a macro.
1465 # Arguments   : $symbol - the macro.
1466 #                $declaration - the declaration of the macro.
1467 #############################################################################
1469 sub OutputMacro {
1470     my ($symbol, $declaration) = @_;
1471     my $id = &CreateValidSGMLID ($symbol);
1472     my $condition = &MakeConditionDescription ($symbol);
1473     my $synop = "<row><entry role=\"define_keyword\">#define</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link>";
1474     my $desc;
1476     my @fields = ParseMacroDeclaration($declaration, \&CreateValidSGML);
1477     my $title = $symbol . (@fields ? "()" : "");
1479     $desc = "<refsect2 id=\"$id\" role=\"macro\"$condition>\n<title>$title</title>\n";
1480     $desc .= MakeIndexterms($symbol, $id);
1481     $desc .= "\n";
1482     $desc .= OutputSymbolExtraLinks($symbol);
1484     if (@fields) {
1485         $synop .= "<phrase role=\"c_punctuation\">()</phrase>";
1486     }
1487     $synop .= "</entry></row>\n";
1489     # Don't output the macro definition if is is a conditional macro or it
1490     # looks like a function, i.e. starts with "g_" or "_?gnome_", or it is
1491     # longer than 2 lines, otherwise we get lots of complicated macros like
1492     # g_assert.
1493     if (!defined ($DeclarationConditional{$symbol}) && ($symbol !~ m/^g_/)
1494         && ($symbol !~ m/^_?gnome_/) && (($declaration =~ tr/\n//) < 2)) {
1495         my $decl_out = &CreateValidSGML ($declaration);
1496         $desc .= "<programlisting language=\"C\">$decl_out</programlisting>\n";
1497     } else {
1498         $desc .= "<programlisting language=\"C\">" . &MakeReturnField("#define") . "$symbol";
1499         if ($declaration =~ m/^\s*#\s*define\s+\w+(\([^\)]*\))/) {
1500             my $args = $1;
1501             my $pad = ' ' x ($RETURN_TYPE_FIELD_WIDTH - length ("#define "));
1502             # Align each line so that if should all line up OK.
1503             $args =~ s/\n/\n$pad/gm;
1504             $desc .= &CreateValidSGML ($args);
1505         }
1506         $desc .= "</programlisting>\n";
1507     }
1509     $desc .= &MakeDeprecationNote($symbol);
1511     my $parameters = &OutputParamDescriptions ("MACRO", $symbol, @fields);
1513     if (defined ($SymbolDocs{$symbol})) {
1514         my $symbol_docs = &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1515         $desc .= $symbol_docs;
1516     }
1518     $desc .= $parameters;
1519     $desc .= OutputSymbolTraits ($symbol);
1520     $desc .= "</refsect2>\n";
1521     return ($synop, $desc);
1525 #############################################################################
1526 # Function    : OutputTypedef
1527 # Description : Returns the synopsis and detailed description of a typedef.
1528 # Arguments   : $symbol - the typedef.
1529 #                $declaration - the declaration of the typedef,
1530 #                  e.g. 'typedef unsigned int guint;'
1531 #############################################################################
1533 sub OutputTypedef {
1534     my ($symbol, $declaration) = @_;
1535     my $id = &CreateValidSGMLID ($symbol);
1536     my $condition = &MakeConditionDescription ($symbol);
1537     my $desc = "<refsect2 id=\"$id\" role=\"typedef\"$condition>\n<title>$symbol</title>\n";
1538     my $synop = "<row><entry role=\"typedef_keyword\">typedef</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
1540     $desc .= MakeIndexterms($symbol, $id);
1541     $desc .= "\n";
1542     $desc .= OutputSymbolExtraLinks($symbol);
1544     if (!defined ($DeclarationConditional{$symbol})) {
1545         my $decl_out = &CreateValidSGML ($declaration);
1546         $desc .= "<programlisting language=\"C\">$decl_out</programlisting>\n";
1547     }
1549     $desc .= &MakeDeprecationNote($symbol);
1551     if (defined ($SymbolDocs{$symbol})) {
1552         $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1553     }
1554     $desc .= OutputSymbolTraits ($symbol);
1555     $desc .= "</refsect2>\n";
1556     return ($synop, $desc);
1560 #############################################################################
1561 # Function    : OutputStruct
1562 # Description : Returns the synopsis and detailed description of a struct.
1563 #                We check if it is a object struct, and if so we only output
1564 #                parts of it that are noted as public fields.
1565 #                We also use a different IDs for object structs, since the
1566 #                original ID is used for the entire RefEntry.
1567 # Arguments   : $symbol - the struct.
1568 #                $declaration - the declaration of the struct.
1569 #############################################################################
1571 sub OutputStruct {
1572     my ($symbol, $declaration) = @_;
1574     my $is_gtype = 0;
1575     my $default_to_public = 1;
1576     if (&CheckIsObject ($symbol)) {
1577         @TRACE@("Found struct gtype: $symbol\n");
1578         $is_gtype = 1;
1579         $default_to_public = $ObjectRoots{$symbol} eq 'GBoxed';
1580     }
1582     my $id;
1583     my $condition;
1584     if ($is_gtype) {
1585         $id = &CreateValidSGMLID ($symbol . "_struct");
1586         $condition = &MakeConditionDescription ($symbol . "_struct");
1587     } else {
1588         $id = &CreateValidSGMLID ($symbol);
1589         $condition = &MakeConditionDescription ($symbol);
1590     }
1592     # Determine if it is a simple struct or it also has a typedef.
1593     my $has_typedef = 0;
1594     if ($StructHasTypedef{$symbol} || $declaration =~ m/^\s*typedef\s+/) {
1595       $has_typedef = 1;
1596     }
1598     my $type_output;
1599     my $desc;
1600     if ($has_typedef) {
1601         # For structs with typedefs we just output the struct name.
1602         $type_output = "";
1603         $desc = "<refsect2 id=\"$id\" role=\"struct\"$condition>\n<title>$symbol</title>\n";
1604     } else {
1605         $type_output = "struct";
1606         $desc = "<refsect2 id=\"$id\" role=\"struct\"$condition>\n<title>struct $symbol</title>\n";
1607     }
1608     my $synop = "<row><entry role=\"datatype_keyword\">${type_output}</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
1610     $desc .= MakeIndexterms($symbol, $id);
1611     $desc .= "\n";
1612     $desc .= OutputSymbolExtraLinks($symbol);
1614     # Form a pretty-printed, private-data-removed form of the declaration
1616     my $decl_out = "";
1617     if ($declaration =~ m/^\s*$/) {
1618         @TRACE@("Found opaque struct: $symbol\n");
1619         $decl_out = "typedef struct _$symbol $symbol;";
1620     } elsif ($declaration =~ m/^\s*struct\s+\w+\s*;\s*$/) {
1621         @TRACE@("Found opaque struct: $symbol\n");
1622         $decl_out = "struct $symbol;";
1623     } else {
1624         my $public = $default_to_public;
1625         my $new_declaration = "";
1626         my $decl_line;
1627         my $decl = $declaration;
1629         if ($decl =~ m/^\s*(typedef\s+)?struct\s*\w*\s*(?:\/\*.*\*\/)?\s*{(.*)}\s*\w*\s*;\s*$/s) {
1630             my $struct_contents = $2;
1632             foreach $decl_line (split (/\n/, $struct_contents)) {
1633                 @TRACE@("Struct line: $decl_line\n");
1634                 if ($decl_line =~ m%/\*\s*<\s*public\s*>\s*\*/%) {
1635                     $public = 1;
1636                 } elsif ($decl_line =~ m%/\*\s*<\s*(private|protected)\s*>\s*\*/%) {
1637                     $public = 0;
1638                 } elsif ($public) {
1639                     $new_declaration .= $decl_line . "\n";
1640                 }
1641             }
1643             if ($new_declaration) {
1644                 # Strip any blank lines off the ends.
1645                 $new_declaration =~ s/^\s*\n//;
1646                 $new_declaration =~ s/\n\s*$/\n/;
1648                 if ($has_typedef) {
1649                     $decl_out = "typedef struct {\n" . $new_declaration
1650                       . "} $symbol;\n";
1651                 } else {
1652                     $decl_out = "struct $symbol {\n" . $new_declaration
1653                       . "};\n";
1654                 }
1655             }
1656         } else {
1657             &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1658                 "Couldn't parse struct:\n$declaration");
1659         }
1661         # If we couldn't parse the struct or it was all private, output an
1662         # empty struct declaration.
1663         if ($decl_out eq "") {
1664             if ($has_typedef) {
1665                 $decl_out = "typedef struct _$symbol $symbol;";
1666             } else {
1667                 $decl_out = "struct $symbol;";
1668             }
1669         }
1670     }
1672     $decl_out = &CreateValidSGML ($decl_out);
1673     $desc .= "<programlisting language=\"C\">$decl_out</programlisting>\n";
1675     $desc .= &MakeDeprecationNote($symbol);
1677     if (defined ($SymbolDocs{$symbol})) {
1678         $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1679     }
1681     # Create a table of fields and descriptions
1683     # FIXME: Inserting &#160's into the produced type declarations here would
1684     #        improve the output in most situations ... except for function
1685     #        members of structs!
1686     my @fields = ParseStructDeclaration($declaration, !$default_to_public,
1687                                         0, \&MakeXRef,
1688                                         sub {
1689                                             "<structfield id=\"".&CreateValidSGMLID("$id.$_[0]")."\">$_[0]</structfield>";
1690                                         });
1691     my $params = $SymbolParams{$symbol};
1693     # If no parameters are filled in, we don't generate the description
1694     # table, for backwards compatibility.
1696     my $found = 0;
1697     if (defined $params) {
1698         for (my $i = 1; $i <= $#$params; $i += $PARAM_FIELD_COUNT) {
1699             if ($params->[$i] =~ /\S/) {
1700                 $found = 1;
1701                 last;
1702             }
1703         }
1704     }
1706     if ($found) {
1707         my %field_descrs = @$params;
1708         my $missing_parameters = "";
1709         my $unused_parameters = "";
1711         $desc .= <<EOF;
1712 <refsect3 role="struct_members">\n<title>Members</title>
1713 <informaltable role="struct_members_table" pgwide="1" frame="none">
1714 <tgroup cols="3">
1715 <colspec colname="struct_members_name" colwidth="300px"/>
1716 <colspec colname="struct_members_description"/>
1717 <colspec colname="struct_members_annotations" colwidth="200px"/>
1718 <tbody>
1721         while (@fields) {
1722             my $field_name = shift @fields;
1723             my $text = shift @fields;
1724             my $field_descr = $field_descrs{$field_name};
1725             my $param_annotations = "";
1727             $desc .= "<row><entry role=\"struct_member_name\"><para>$text</para></entry>\n";
1728             if (defined $field_descr) {
1729                 ($field_descr,$param_annotations) = &ExpandAnnotation($symbol, $field_descr);
1730                 $field_descr = &ConvertMarkDown($symbol, $field_descr);
1731                 # trim
1732                 $field_descr =~ s/^(\s|\n)+//msg;
1733                 $field_descr =~ s/(\s|\n)+$//msg;
1734                 $desc .= "<entry role=\"struct_member_description\">$field_descr</entry>\n<entry role=\"struct_member_annotations\">$param_annotations</entry>\n";
1735                 delete $field_descrs{$field_name};
1736             } else {
1737                 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1738                     "Field description for $symbol"."::"."$field_name is missing in source code comment block.");
1739                 if ($missing_parameters ne "") {
1740                   $missing_parameters .= ", ".$field_name;
1741                 } else {
1742                     $missing_parameters = $field_name;
1743                 }
1744                 $desc .= "<entry /><entry />\n";
1745             }
1746             $desc .= "</row>\n";
1747         }
1748         $desc .= "</tbody></tgroup></informaltable>\n</refsect3>\n";
1749         foreach my $field_name (keys %field_descrs) {
1750             &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1751                 "Field description for $symbol"."::"."$field_name is not used from source code comment block.");
1752             if ($unused_parameters ne "") {
1753               $unused_parameters .= ", ".$field_name;
1754             } else {
1755                $unused_parameters = $field_name;
1756             }
1757         }
1759         # remember missing/unused parameters (needed in tmpl-free build)
1760         if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) {
1761             $AllIncompleteSymbols{$symbol}=$missing_parameters;
1762         }
1763         if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) {
1764             $AllUnusedSymbols{$symbol}=$unused_parameters;
1765         }
1766     }
1767     else {
1768         if (scalar(@fields) > 0) {
1769             if (! exists ($AllIncompleteSymbols{$symbol})) {
1770                 $AllIncompleteSymbols{$symbol}="<items>";
1771                 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1772                     "Field descriptions for struct $symbol are missing in source code comment block.");
1773                 @TRACE@("Remaining structs fields: ".@fields.":".join(',',@fields)."\n");
1774             }
1775         }
1776     }
1778     $desc .= OutputSymbolTraits ($symbol);
1779     $desc .= "</refsect2>\n";
1780     return ($synop, $desc);
1784 #############################################################################
1785 # Function    : OutputUnion
1786 # Description : Returns the synopsis and detailed description of a union.
1787 # Arguments   : $symbol - the union.
1788 #                $declaration - the declaration of the union.
1789 #############################################################################
1791 sub OutputUnion {
1792     my ($symbol, $declaration) = @_;
1794     my $is_gtype = 0;
1795     if (&CheckIsObject ($symbol)) {
1796         @TRACE@("Found union gtype: $symbol\n");
1797         $is_gtype = 1;
1798     }
1800     my $id;
1801     my $condition;
1802     if ($is_gtype) {
1803         $id = &CreateValidSGMLID ($symbol . "_union");
1804         $condition = &MakeConditionDescription ($symbol . "_union");
1805     } else {
1806         $id = &CreateValidSGMLID ($symbol);
1807         $condition = &MakeConditionDescription ($symbol);
1808     }
1810     # Determine if it is a simple struct or it also has a typedef.
1811     my $has_typedef = 0;
1812     if ($StructHasTypedef{$symbol} || $declaration =~ m/^\s*typedef\s+/) {
1813       $has_typedef = 1;
1814     }
1816     my $type_output;
1817     my $desc;
1818     if ($has_typedef) {
1819         # For unions with typedefs we just output the union name.
1820         $type_output = "";
1821         $desc = "<refsect2 id=\"$id\" role=\"union\"$condition>\n<title>$symbol</title>\n";
1822     } else {
1823         $type_output = "union";
1824         $desc = "<refsect2 id=\"$id\" role=\"union\"$condition>\n<title>union $symbol</title>\n";
1825     }
1826     my $synop = "<row><entry role=\"datatype_keyword\">${type_output}</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
1828     $desc .= MakeIndexterms($symbol, $id);
1829     $desc .= "\n";
1830     $desc .= OutputSymbolExtraLinks($symbol);
1831     $desc .= &MakeDeprecationNote($symbol);
1833     if (defined ($SymbolDocs{$symbol})) {
1834         $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1835     }
1837     # Create a table of fields and descriptions
1839     # FIXME: Inserting &#160's into the produced type declarations here would
1840     #        improve the output in most situations ... except for function
1841     #        members of structs!
1842     my @fields = ParseStructDeclaration($declaration, 0,
1843                                         0, \&MakeXRef,
1844                                         sub {
1845                                             "<structfield id=\"".&CreateValidSGMLID("$id.$_[0]")."\">$_[0]</structfield>";
1846                                         });
1847     my $params = $SymbolParams{$symbol};
1849     # If no parameters are filled in, we don't generate the description
1850     # table, for backwards compatibility
1852     my $found = 0;
1853     if (defined $params) {
1854         for (my $i = 1; $i <= $#$params; $i += $PARAM_FIELD_COUNT) {
1855             if ($params->[$i] =~ /\S/) {
1856                 $found = 1;
1857                 last;
1858             }
1859         }
1860     }
1862     if ($found) {
1863         my %field_descrs = @$params;
1864         my $missing_parameters = "";
1865         my $unused_parameters = "";
1867         $desc .= <<EOF;
1868 <refsect3 role="union_members">\n<title>Members</title>
1869 <informaltable role="union_members_table" pgwide="1" frame="none">
1870 <tgroup cols="3">
1871 <colspec colname="union_members_name" colwidth="300px"/>
1872 <colspec colname="union_members_description"/>
1873 <colspec colname="union_members_annotations" colwidth="200px"/>
1874 <tbody>
1877         while (@fields) {
1878             my $field_name = shift @fields;
1879             my $text = shift @fields;
1880             my $field_descr = $field_descrs{$field_name};
1881             my $param_annotations = "";
1883             $desc .= "<row><entry role=\"union_member_name\"><para>$text</para></entry>\n";
1884             if (defined $field_descr) {
1885                 ($field_descr,$param_annotations) = &ExpandAnnotation($symbol, $field_descr);
1886                 $field_descr = &ConvertMarkDown($symbol, $field_descr);
1888                 # trim
1889                 $field_descr =~ s/^(\s|\n)+//msg;
1890                 $field_descr =~ s/(\s|\n)+$//msg;
1891                 $desc .= "<entry role=\"union_member_description\">$field_descr</entry>\n<entry role=\"union_member_annotations\">$param_annotations</entry>\n";
1892                 delete $field_descrs{$field_name};
1893             } else {
1894                 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1895                     "Field description for $symbol"."::"."$field_name is missing in source code comment block.");
1896                 if ($missing_parameters ne "") {
1897                     $missing_parameters .= ", ".$field_name;
1898                 } else {
1899                     $missing_parameters = $field_name;
1900                 }
1901                 $desc .= "<entry /><entry />\n";
1902             }
1903             $desc .= "</row>\n";
1904         }
1905         $desc .= "</tbody></tgroup></informaltable>\n</refsect3>";
1906         foreach my $field_name (keys %field_descrs) {
1907             &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1908                 "Field description for $symbol"."::"."$field_name is not used from source code comment block.");
1909             if ($unused_parameters ne "") {
1910               $unused_parameters .= ", ".$field_name;
1911             } else {
1912                $unused_parameters = $field_name;
1913             }
1914         }
1916         # remember missing/unused parameters (needed in tmpl-free build)
1917         if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) {
1918             $AllIncompleteSymbols{$symbol}=$missing_parameters;
1919         }
1920         if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) {
1921             $AllUnusedSymbols{$symbol}=$unused_parameters;
1922         }
1923     }
1924     else {
1925         if (scalar(@fields) > 0) {
1926             if (! exists ($AllIncompleteSymbols{$symbol})) {
1927                 $AllIncompleteSymbols{$symbol}="<items>";
1928                 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1929                     "Field descriptions for union $symbol are missing in source code comment block.");
1930                 @TRACE@("Remaining union fields: ".@fields.":".join(',',@fields)."\n");
1931             }
1932         }
1933     }
1935     $desc .= OutputSymbolTraits ($symbol);
1936     $desc .= "</refsect2>\n";
1937     return ($synop, $desc);
1941 #############################################################################
1942 # Function    : OutputEnum
1943 # Description : Returns the synopsis and detailed description of a enum.
1944 # Arguments   : $symbol - the enum.
1945 #                $declaration - the declaration of the enum.
1946 #############################################################################
1948 sub OutputEnum {
1949     my ($symbol, $declaration) = @_;
1951     my $is_gtype = 0;
1952     if (&CheckIsObject ($symbol)) {
1953         @TRACE@("Found enum gtype: $symbol\n");
1954         $is_gtype = 1;
1955     }
1957     my $id;
1958     my $condition;
1959     if ($is_gtype) {
1960         $id = &CreateValidSGMLID ($symbol . "_enum");
1961         $condition = &MakeConditionDescription ($symbol . "_enum");
1962     } else {
1963         $id = &CreateValidSGMLID ($symbol);
1964         $condition = &MakeConditionDescription ($symbol);
1965     }
1967     my $synop = "<row><entry role=\"datatype_keyword\">enum</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
1968     my $desc = "<refsect2 id=\"$id\" role=\"enum\"$condition>\n<title>enum $symbol</title>\n";
1970     $desc .= MakeIndexterms($symbol, $id);
1971     $desc .= "\n";
1972     $desc .= OutputSymbolExtraLinks($symbol);
1973     $desc .= &MakeDeprecationNote($symbol);
1975     if (defined ($SymbolDocs{$symbol})) {
1976         $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1977     }
1979     # Create a table of fields and descriptions
1981     my @fields = ParseEnumDeclaration($declaration);
1982     my $params = $SymbolParams{$symbol};
1984     # If nothing at all is documented log a single summary warning at the end.
1985     # Otherwise, warn about each undocumented item.
1987     my $found = 0;
1988     if (defined $params) {
1989         for (my $i = 1; $i <= $#$params; $i += $PARAM_FIELD_COUNT) {
1990             if ($params->[$i] =~ /\S/) {
1991                 $found = 1;
1992                 last;
1993             }
1994         }
1995     }
1997     my %field_descrs = (defined $params ? @$params : ());
1998     my $missing_parameters = "";
1999     my $unused_parameters = "";
2001     $desc .= <<EOF;
2002 <refsect3 role="enum_members">\n<title>Members</title>
2003 <informaltable role="enum_members_table" pgwide="1" frame="none">
2004 <tgroup cols="3">
2005 <colspec colname="enum_members_name" colwidth="300px"/>
2006 <colspec colname="enum_members_description"/>
2007 <colspec colname="enum_members_annotations" colwidth="200px"/>
2008 <tbody>
2011     for my $field_name (@fields) {
2012         my $field_descr = $field_descrs{$field_name};
2013         my $param_annotations = "";
2015         $id = &CreateValidSGMLID ($field_name);
2016         $condition = &MakeConditionDescription ($field_name);
2017         $desc .= "<row role=\"constant\"><entry role=\"enum_member_name\"><para id=\"$id\">$field_name</para></entry>\n";
2018         if (defined $field_descr) {
2019             ($field_descr,$param_annotations) = &ExpandAnnotation($symbol, $field_descr);
2020             $field_descr = &ConvertMarkDown($symbol, $field_descr);
2021             $desc .= "<entry role=\"enum_member_description\">$field_descr</entry>\n<entry role=\"enum_member_annotations\">$param_annotations</entry>\n";
2022             delete $field_descrs{$field_name};
2023         } else {
2024             if ($found) {
2025                 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2026                     "Value description for $symbol"."::"."$field_name is missing in source code comment block.");
2027                 if ($missing_parameters ne "") {
2028                     $missing_parameters .= ", ".$field_name;
2029                 } else {
2030                     $missing_parameters = $field_name;
2031                 }
2032             }
2033             $desc .= "<entry /><entry />\n";
2034         }
2035         $desc .= "</row>\n";
2036     }
2037     $desc .= "</tbody></tgroup></informaltable>\n</refsect3>";
2038     foreach my $field_name (keys %field_descrs) {
2039         &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2040             "Value description for $symbol"."::"."$field_name is not used from source code comment block.");
2041         if ($unused_parameters ne "") {
2042             $unused_parameters .= ", ".$field_name;
2043         } else {
2044             $unused_parameters = $field_name;
2045         }
2046     }
2048     # remember missing/unused parameters (needed in tmpl-free build)
2049     if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) {
2050         $AllIncompleteSymbols{$symbol}=$missing_parameters;
2051     }
2052     if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) {
2053         $AllUnusedSymbols{$symbol}=$unused_parameters;
2054     }
2056     if (!$found) {
2057         if (scalar(@fields) > 0) {
2058             if (! exists ($AllIncompleteSymbols{$symbol})) {
2059                 $AllIncompleteSymbols{$symbol}="<items>";
2060                 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2061                     "Value descriptions for $symbol are missing in source code comment block.");
2062             }
2063         }
2064     }
2066     $desc .= OutputSymbolTraits ($symbol);
2067     $desc .= "</refsect2>\n";
2068     return ($synop, $desc);
2072 #############################################################################
2073 # Function    : OutputVariable
2074 # Description : Returns the synopsis and detailed description of a variable.
2075 # Arguments   : $symbol - the extern'ed variable.
2076 #                $declaration - the declaration of the variable.
2077 #############################################################################
2079 sub OutputVariable {
2080     my ($symbol, $declaration) = @_;
2081     my $id = &CreateValidSGMLID ($symbol);
2082     my $condition = &MakeConditionDescription ($symbol);
2083     
2084     @TRACE@("ouputing variable: '$symbol' '$declaration'");
2086     my $type_output;
2087     if ($declaration =~ m/^\s*extern\s+((const\s+|signed\s+|unsigned\s+|long\s+|short\s+)*\w+)(\s+\*+|\*+|\s)(\s*)(const\s+)*([A-Za-z]\w*)\s*;/) {
2088         my $mod1 = defined ($1) ? $1 : "";
2089         my $ptr = defined ($3) ? $3 : "";
2090         my $space = defined ($4) ? $4 : "";
2091         my $mod2 = defined ($5) ? $5 : "";
2092         $type_output = "extern $mod1$ptr$space$mod2";
2093     } elsif ($declaration =~ m/^\s*((const\s+|signed\s+|unsigned\s+|long\s+|short\s+)*\w+)(\s+\*+|\*+|\s)(\s*)(const\s+)*([A-Za-z]\w*)\s*=/) {
2094         my $mod1 = defined ($1) ? $1 : "";
2095         my $ptr = defined ($3) ? $3 : "";
2096         my $space = defined ($4) ? $4 : "";
2097         my $mod2 = defined ($5) ? $5 : "";
2098         $type_output = "$mod1$ptr$space$mod2";
2099     } else {
2100         $type_output = "extern";
2101     }
2102     my $synop = "<row><entry role=\"variable_type\">${type_output}</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
2104     my $desc = "<refsect2 id=\"$id\" role=\"variable\"$condition>\n<title>$symbol</title>\n";
2106     $desc .= MakeIndexterms($symbol, $id);
2107     $desc .= "\n";
2108     $desc .= OutputSymbolExtraLinks($symbol);
2110     my $decl_out = &CreateValidSGML ($declaration);
2111     $desc .= "<programlisting language=\"C\">$decl_out</programlisting>\n";
2113     $desc .= &MakeDeprecationNote($symbol);
2115     if (defined ($SymbolDocs{$symbol})) {
2116         $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
2117     }
2118     if (defined ($SymbolAnnotations{$symbol})) {
2119         my $param_desc = $SymbolAnnotations{$symbol};
2120         my $param_annotations = "";
2121         ($param_desc,$param_annotations) = &ExpandAnnotation($symbol, $param_desc);
2122         if ($param_annotations ne "") {
2123             $desc .= "\n<para>$param_annotations</para>";
2124         }
2125     }
2127     $desc .= OutputSymbolTraits ($symbol);
2128     $desc .= "</refsect2>\n";
2129     return ($synop, $desc);
2133 #############################################################################
2134 # Function    : OutputFunction
2135 # Description : Returns the synopsis and detailed description of a function.
2136 # Arguments   : $symbol - the function.
2137 #                $declaration - the declaration of the function.
2138 #############################################################################
2140 sub OutputFunction {
2141     my ($symbol, $declaration, $symbol_type) = @_;
2142     my $id = &CreateValidSGMLID ($symbol);
2143     my $condition = &MakeConditionDescription ($symbol);
2145     # Take out the return type     $1                                                                                       $2   $3
2146     $declaration =~ s/<RETURNS>\s*((?:const\s+|G_CONST_RETURN\s+|signed\s+|unsigned\s+|long\s+|short\s+|struct\s+|enum\s+)*)(\w+)(\s*\**\s*(?:const|G_CONST_RETURN)?\s*\**\s*(?:restrict)?\s*)<\/RETURNS>\n//;
2147     my $type_modifier = defined($1) ? $1 : "";
2148     my $type = $2;
2149     my $pointer = $3;
2150     # Trim trailing spaces as we are going to pad to $RETURN_TYPE_FIELD_WIDTH below anyway
2151     $pointer =~ s/\s+$//;
2152     my $xref = &MakeXRef ($type, &tagify($type, "returnvalue"));
2153     my $start = "";
2154     #if ($symbol_type eq 'USER_FUNCTION') {
2155     #    $start = "typedef ";
2156     #}
2158     # We output const rather than G_CONST_RETURN.
2159     $type_modifier =~ s/G_CONST_RETURN/const/g;
2160     $pointer =~ s/G_CONST_RETURN/const/g;
2161     $pointer =~ s/^\s+/&#160;/g;
2163     my $ret_type_output;
2164     $ret_type_output = "$start$type_modifier$xref$pointer\n";
2166     my $indent_len;
2167     $indent_len = length ($symbol) + 2;
2168     my $char1 = my $char2 = my $char3 = "";
2169     if ($symbol_type eq 'USER_FUNCTION') {
2170         $indent_len += 3;
2171         $char1 = "<phrase role=\"c_punctuation\">(</phrase>";
2172         $char2 = "*";
2173         $char3 = "<phrase role=\"c_punctuation\">)</phrase>";
2174     }
2176     my ($symbol_output, $symbol_desc_output);
2177     $symbol_output = "$char1<link linkend=\"$id\">$char2$symbol</link>$char3";
2178     if ($indent_len < $MAX_SYMBOL_FIELD_WIDTH) {
2179         $symbol_desc_output = "$char1$char2$symbol$char3 ";
2180     } else {
2181         $indent_len = $MAX_SYMBOL_FIELD_WIDTH - 8;
2182         $symbol_desc_output = "$char1$char2$symbol$char3\n"
2183           . (' ' x ($indent_len - 1));
2184     }
2186     my $synop = "<row><entry role=\"function_type\">${ret_type_output}</entry><entry role=\"function_name\">${symbol_output}&#160;<phrase role=\"c_punctuation\">()</phrase></entry></row>\n";
2188     my $desc = "<refsect2 id=\"$id\" role=\"function\"$condition>\n<title>${symbol}&#160;()</title>\n";
2190     $desc .= MakeIndexterms($symbol, $id);
2191     $desc .= "\n";
2192     $desc .= OutputSymbolExtraLinks($symbol);
2194     $desc  .= "<programlisting language=\"C\">${ret_type_output}$symbol_desc_output(";
2196     my @fields = ParseFunctionDeclaration($declaration, \&MakeXRef,
2197                                         sub {
2198                                             &tagify($_[0],"parameter");
2199                                         });
2201     for (my $i = 1; $i <= $#fields; $i += 2) {
2202         my $field_name = $fields[$i];
2204         if ($i == 1) {
2205             $desc  .= "$field_name";
2206         } else {
2207             $desc  .= ",\n"
2208                 . (' ' x $indent_len)
2209                 . "$field_name";
2210         }
2212     }
2214     $desc  .= ");</programlisting>\n";
2216     $desc .= &MakeDeprecationNote($symbol);
2218     if (defined ($SymbolDocs{$symbol})) {
2219         $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
2220     }
2221     if (defined ($SymbolAnnotations{$symbol})) {
2222         my $param_desc = $SymbolAnnotations{$symbol};
2223         my $param_annotations = "";
2224         ($param_desc,$param_annotations) = &ExpandAnnotation($symbol, $param_desc);
2225         if ($param_annotations ne "") {
2226             $desc .= "\n<para>$param_annotations</para>";
2227         }
2228     }
2230     $desc .= &OutputParamDescriptions ("FUNCTION", $symbol, @fields);
2231     $desc .= OutputSymbolTraits ($symbol);
2232     $desc .= "</refsect2>\n";
2233     return ($synop, $desc);
2237 #############################################################################
2238 # Function    : OutputParamDescriptions
2239 # Description : Returns the DocBook output describing the parameters of a
2240 #                function, macro or signal handler.
2241 # Arguments   : $symbol_type - 'FUNCTION', 'MACRO' or 'SIGNAL'. Signal
2242 #                  handlers have an implicit user_data parameter last.
2243 #                $symbol - the name of the function/macro being described.
2244 #               @fields - parsed fields from the declaration, used to determine
2245 #                  undocumented/unused entries
2246 #############################################################################
2248 sub OutputParamDescriptions {
2249     my ($symbol_type, $symbol, @fields) = @_;
2250     my $output = "";
2251     my $params = $SymbolParams{$symbol};
2252     my $num_params = 0;
2253     my %field_descrs = ();
2255     if (@fields) {
2256         %field_descrs = @fields;
2257         delete $field_descrs{"void"};
2258         delete $field_descrs{"Returns"};
2259     }
2261     if (defined $params) {
2262         my $returns = "";
2263         my $params_desc = "";
2264         my $missing_parameters = "";
2265         my $unused_parameters = "";
2266         my $j;
2268         for ($j = 0; $j <= $#$params; $j += $PARAM_FIELD_COUNT) {
2269             my $param_name = $$params[$j];
2270             my $param_desc = $$params[$j + 1];
2271             my $param_annotations = "";
2273             ($param_desc,$param_annotations) = &ExpandAnnotation($symbol, $param_desc);
2274             $param_desc = &ConvertMarkDown($symbol, $param_desc);
2275             # trim
2276             $param_desc =~ s/^(\s|\n)+//msg;
2277             $param_desc =~ s/(\s|\n)+$//msg;
2278             if ($param_name eq "Returns") {
2279                 $returns = $param_desc;
2280                 if ($param_annotations ne "") {
2281                     $returns .= "\n<para>$param_annotations</para>";
2282                 }
2283             } elsif ($param_name eq "void") {
2284                 # FIXME: &LogWarning()?
2285                 @TRACE@("!!!! void in params for $symbol?\n");
2286             } else {
2287                 if (@fields) {
2288                     if (!defined $field_descrs{$param_name}) {
2289                         &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2290                             "Parameter description for $symbol"."::"."$param_name is not used from source code comment block.");
2291                         if ($unused_parameters ne "") {
2292                           $unused_parameters .= ", ".$param_name;
2293                         } else {
2294                            $unused_parameters = $param_name;
2295                         }
2296                     } else {
2297                         delete $field_descrs{$param_name};
2298                     }
2299                 }
2300                 if($param_desc ne "") {
2301                     $params_desc .= "<row><entry role=\"parameter_name\"><para>$param_name</para></entry>\n<entry role=\"parameter_description\">$param_desc</entry>\n<entry role=\"parameter_annotations\">$param_annotations</entry></row>\n";
2302                     $num_params++;
2303                 }
2304             }
2305         }
2306         foreach my $param_name (keys %field_descrs) {
2307             &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2308                 "Parameter description for $symbol"."::"."$param_name is missing in source code comment block.");
2309             if ($missing_parameters ne "") {
2310               $missing_parameters .= ", ".$param_name;
2311             } else {
2312                $missing_parameters = $param_name;
2313             }
2314         }
2316         # Signals have an implicit user_data parameter which we describe.
2317         if ($symbol_type eq "SIGNAL") {
2318             $params_desc .= "<row><entry role=\"parameter_name\"><simpara>user_data</simpara></entry>\n<entry role=\"parameter_description\"><simpara>user data set when the signal handler was connected.</simpara></entry>\n<entry role=\"parameter_annotations\"></entry></row>\n";
2319         }
2321         # Start a table if we need one.
2322         if ($params_desc ne "") {
2323           $output .= <<EOF;
2324 <refsect3 role="parameters">\n<title>Parameters</title>
2325 <informaltable role="parameters_table" pgwide="1" frame="none">
2326 <tgroup cols="3">
2327 <colspec colname="parameters_name" colwidth="150px"/>
2328 <colspec colname="parameters_description"/>
2329 <colspec colname="parameters_annotations" colwidth="200px"/>
2330 <tbody>
2332           $output .= $params_desc;
2333           $output .= "</tbody></tgroup></informaltable>\n</refsect3>";
2334         }
2336         # Output the returns info last
2337         if ($returns ne "") {
2338           $output .= <<EOF;
2339 <refsect3 role=\"returns\">\n<title>Returns</title>
2341           $output .= $returns;
2342           $output .= "\n</refsect3>";
2343         }
2345         # remember missing/unused parameters (needed in tmpl-free build)
2346         if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) {
2347             $AllIncompleteSymbols{$symbol}=$missing_parameters;
2348         }
2349         if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) {
2350             $AllUnusedSymbols{$symbol}=$unused_parameters;
2351         }
2352     }
2353     if (($num_params == 0) && @fields && (scalar(keys(%field_descrs)) > 0)) {
2354         if (! exists ($AllIncompleteSymbols{$symbol})) {
2355             $AllIncompleteSymbols{$symbol}="<parameters>";
2356         }
2357     }
2359     return $output;
2363 #############################################################################
2364 # Function    : ParseStabilityLevel
2365 # Description : Parses a stability level and outputs a warning if it isn't
2366 #               valid.
2367 # Arguments   : $stability - the stability text.
2368 #                $file, $line - context for error message
2369 #                $message - description of where the level is from, to use in
2370 #               any error message.
2371 # Returns     : The parsed stability level string.
2372 #############################################################################
2374 sub ParseStabilityLevel {
2375     my ($stability, $file, $line, $message) = @_;
2377     $stability =~ s/^\s*//;
2378     $stability =~ s/\s*$//;
2379     if ($stability =~ m/^stable$/i) {
2380         $stability = "Stable";
2381     } elsif ($stability =~ m/^unstable$/i) {
2382         $stability = "Unstable";
2383     } elsif ($stability =~ m/^private$/i) {
2384         $stability = "Private";
2385     } else {
2386         &LogWarning ($file, $line, "$message is $stability.".
2387             "It should be one of these: Stable, Unstable, or Private.");
2388     }
2389     return $stability;
2393 #############################################################################
2394 # Function    : OutputDBFile
2395 # Description : Outputs the final DocBook file for one section.
2396 # Arguments   : $file - the name of the file.
2397 #               $title - the title from the $MODULE-sections.txt file, which
2398 #                 will be overridden by the title in the template file.
2399 #               $section_id - the id to use for the toplevel tag.
2400 #               $includes - comma-separates list of include files added at top of
2401 #                 synopsis, with '<' '>' around them (if not already enclosed in "").
2402 #               $functions_synop - reference to the DocBook for the Functions Synopsis part.
2403 #               $other_synop - reference to the DocBook for the Types and Values Synopsis part.
2404 #               $functions_details - reference to the DocBook for the Functions Details part.
2405 #               $other_details - reference to the DocBook for the Types and Values Details part.
2406 #               $signal_synop - reference to the DocBook for the Signal Synopsis part
2407 #               $signal_desc - reference to the DocBook for the Signal Description part
2408 #               $args_synop - reference to the DocBook for the Arg Synopsis part
2409 #               $args_desc - reference to the DocBook for the Arg Description part
2410 #               $hierarchy - reference to the DocBook for the Object Hierarchy part
2411 #               $interfaces - reference to the DocBook for the Interfaces part
2412 #               $implementations - reference to the DocBook for the Known Implementations part
2413 #               $prerequisites - reference to the DocBook for the Prerequisites part
2414 #               $derived - reference to the DocBook for the Derived Interfaces part
2415 #               $file_objects - reference to an array of objects in this file
2416 #############################################################################
2418 sub OutputDBFile {
2419     my ($file, $title, $section_id, $includes, $functions_synop, $other_synop, $functions_details, $other_details, $signals_synop, $signals_desc, $args_synop, $args_desc, $hierarchy, $interfaces, $implementations, $prerequisites, $derived, $file_objects) = @_;
2421     @TRACE@("Output docbook for file $file with title '$title'\n");
2423     # The edited title overrides the one from the sections file.
2424     my $new_title = $SymbolDocs{"$TMPL_DIR/$file:Title"};
2425     if (defined ($new_title) && $new_title !~ m/^\s*$/) {
2426         $title = $new_title;
2427         @TRACE@("Found title: $title\n");
2428     }
2429     my $short_desc = $SymbolDocs{"$TMPL_DIR/$file:Short_Description"};
2430     if (!defined ($short_desc) || $short_desc =~ m/^\s*$/) {
2431         $short_desc = "";
2432     } else {
2433         # Don't use ConvertMarkDown here for now since we don't want blocks
2434         $short_desc = &ExpandAbbreviations("$title:Short_description",
2435                                            $short_desc);
2436         @TRACE@("Found short_desc: $short_desc");
2437     }
2438     my $long_desc = $SymbolDocs{"$TMPL_DIR/$file:Long_Description"};
2439     if (!defined ($long_desc) || $long_desc =~ m/^\s*$/) {
2440         $long_desc = "";
2441     } else {
2442         $long_desc = &ConvertMarkDown("$title:Long_description",
2443                                           $long_desc);
2444         @TRACE@("Found long_desc: $long_desc");
2445     }
2446     my $see_also = $SymbolDocs{"$TMPL_DIR/$file:See_Also"};
2447     if (!defined ($see_also) || $see_also =~ m%^\s*(<para>)?\s*(</para>)?\s*$%) {
2448         $see_also = "";
2449     } else {
2450         $see_also = &ConvertMarkDown("$title:See_Also", $see_also);
2451         @TRACE@("Found see_also: $see_also");
2452     }
2453     if ($see_also) {
2454         $see_also = "<refsect1 id=\"$section_id.see-also\">\n<title>See Also</title>\n$see_also\n</refsect1>\n";
2455     }
2456     my $stability = $SymbolDocs{"$TMPL_DIR/$file:Stability_Level"};
2457     if (!defined ($stability) || $stability =~ m/^\s*$/) {
2458         $stability = "";
2459     } else {
2460         $stability = &ParseStabilityLevel($stability, $file, $., "Section stability level");
2461         @TRACE@("Found stability: $stability");
2462     }
2463     if ($stability) {
2464         $AnnotationsUsed{$stability} = 1;
2465         $stability = "<refsect1 id=\"$section_id.stability-level\">\n<title>Stability Level</title>\n<acronym>$stability</acronym>, unless otherwise indicated\n</refsect1>\n";
2466     } elsif ($DEFAULT_STABILITY) {
2467         $AnnotationsUsed{$DEFAULT_STABILITY} = 1;
2468         $stability = "<refsect1 id=\"$section_id.stability-level\">\n<title>Stability Level</title>\n<acronym>$DEFAULT_STABILITY</acronym>, unless otherwise indicated\n</refsect1>\n";
2469     }
2471     my $image = $SymbolDocs{"$TMPL_DIR/$file:Image"};
2472     if (!defined ($image) || $image =~ m/^\s*$/) {
2473       $image = "";
2474     } else {
2475       $image =~ s/^\s*//;
2476       $image =~ s/\s*$//;
2478       my $format;
2480       if ($image =~ /jpe?g$/i) {
2481         $format = "format='JPEG'";
2482       } elsif ($image =~ /png$/i) {
2483         $format = "format='PNG'";
2484       } elsif ($image =~ /svg$/i) {
2485         $format = "format='SVG'";
2486       } else {
2487         $format = "";
2488       }
2490       $image = "  <inlinegraphic fileref='$image' $format/>\n"
2491     }
2493     my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) =
2494         gmtime (time);
2495     my $month = (qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec))[$mon];
2496     $year += 1900;
2498     my $include_output = "";
2499     if ($includes) {
2500       $include_output .= "<refsect1 id=\"$section_id.includes\"><title>Includes</title><synopsis>";
2501       my $include;
2502       foreach $include (split (/,/, $includes)) {
2503         if ($include =~ m/^\".+\"$/) {
2504           $include_output .= "#include ${include}\n";
2505         }
2506         else {
2507           $include =~ s/^\s+|\s+$//gs;
2508           $include_output .= "#include &lt;${include}&gt;\n";
2509         }
2510       }
2511       $include_output .= "</synopsis></refsect1>\n";
2512     }
2514     my $extralinks = OutputSectionExtraLinks($title,"Section:$file");
2516     my $old_db_file = "$DB_OUTPUT_DIR/$file.xml";
2517     my $new_db_file = "$DB_OUTPUT_DIR/$file.xml.new";
2519     open (OUTPUT, ">$new_db_file")
2520         || die "Can't create $new_db_file: $!";
2522     my $object_anchors = "";
2523     foreach my $object (@$file_objects) {
2524         next if ($object eq $section_id);
2525         my $id = CreateValidSGMLID($object);
2526         @TRACE@("Adding anchor for $object\n");
2527         $object_anchors .= "<anchor id=\"$id\"/>";
2528     }
2530     # Make sure we produce valid docbook
2531     $$functions_details ||= "<para />";
2533     # We used to output this, but is messes up our UpdateFileIfChanged code
2534     # since it changes every day (and it is only used in the man pages):
2535     # "<refentry id="$section_id" revision="$mday $month $year">"
2537     print OUTPUT <<EOF;
2538 ${\( MakeDocHeader ("refentry") )}
2539 <refentry id="$section_id">
2540 <refmeta>
2541 <refentrytitle role="top_of_page" id="$section_id.top_of_page">$title</refentrytitle>
2542 <manvolnum>3</manvolnum>
2543 <refmiscinfo>\U$MODULE\E Library$image</refmiscinfo>
2544 </refmeta>
2545 <refnamediv>
2546 <refname>$title</refname>
2547 <refpurpose>$short_desc</refpurpose>
2548 </refnamediv>
2549 $stability
2550 $$functions_synop$$args_synop$$signals_synop$object_anchors$$other_synop$$hierarchy$$prerequisites$$derived$$interfaces$$implementations
2551 $include_output
2552 <refsect1 id="$section_id.description" role="desc">
2553 <title role="desc.title">Description</title>
2554 $extralinks$long_desc
2555 </refsect1>
2556 <refsect1 id="$section_id.functions_details" role="details">
2557 <title role="details.title">Functions</title>
2558 $$functions_details
2559 </refsect1>
2560 <refsect1 id="$section_id.other_details" role="details">
2561 <title role="details.title">Types and Values</title>
2562 $$other_details
2563 </refsect1>
2564 $$args_desc$$signals_desc$see_also
2565 </refentry>
2567     close (OUTPUT);
2569     return &UpdateFileIfChanged ($old_db_file, $new_db_file, 0);
2573 #############################################################################
2574 # Function    : OutputExtraFile
2575 # Description : Copies an "extra" DocBook file into the output directory,
2576 #               expanding abbreviations
2577 # Arguments   : $file - the source file.
2578 #############################################################################
2579 sub OutputExtraFile {
2580     my ($file) = @_;
2582     my $basename;
2584     ($basename = $file) =~ s!^.*/!!;
2586     my $old_db_file = "$DB_OUTPUT_DIR/$basename";
2587     my $new_db_file = "$DB_OUTPUT_DIR/$basename.new";
2589     my $contents;
2591     open(EXTRA_FILE, "<$file") || die "Can't open $file";
2593     {
2594         local $/;
2595         $contents = <EXTRA_FILE>;
2596     }
2598     open (OUTPUT, ">$new_db_file")
2599         || die "Can't create $new_db_file: $!";
2601     print OUTPUT &ExpandAbbreviations ("$basename file", $contents);
2602     close (OUTPUT);
2604     return &UpdateFileIfChanged ($old_db_file, $new_db_file, 0);
2606 #############################################################################
2607 # Function    : OutputBook
2608 # Description : Outputs the entities that need to be included into the
2609 #                main docbook file for the module.
2610 # Arguments   : $book_top - the declarations of the entities, which are added
2611 #                  at the top of the main docbook file.
2612 #                $book_bottom - the references to the entities, which are
2613 #                  added in the main docbook file at the desired position.
2614 #############################################################################
2616 sub OutputBook {
2617     my ($book_top, $book_bottom) = @_;
2619     my $old_file = "$DB_OUTPUT_DIR/$MODULE-doc.top";
2620     my $new_file = "$DB_OUTPUT_DIR/$MODULE-doc.top.new";
2622     open (OUTPUT, ">$new_file")
2623         || die "Can't create $new_file: $!";
2624     print OUTPUT $book_top;
2625     close (OUTPUT);
2627     &UpdateFileIfChanged ($old_file, $new_file, 0);
2630     $old_file = "$DB_OUTPUT_DIR/$MODULE-doc.bottom";
2631     $new_file = "$DB_OUTPUT_DIR/$MODULE-doc.bottom.new";
2633     open (OUTPUT, ">$new_file")
2634         || die "Can't create $new_file: $!";
2635     print OUTPUT $book_bottom;
2636     close (OUTPUT);
2638     &UpdateFileIfChanged ($old_file, $new_file, 0);
2641     # If the main docbook file hasn't been created yet, we create it here.
2642     # The user can tweak it later.
2643     if ($MAIN_SGML_FILE && ! -e $MAIN_SGML_FILE) {
2644         open (OUTPUT, ">$MAIN_SGML_FILE")
2645           || die "Can't create $MAIN_SGML_FILE: $!";
2646           
2647         print OUTPUT <<EOF;
2648 ${\( MakeDocHeader ("book") )}
2649 <book id="index">
2650   <bookinfo>
2651     <title>$MODULE Reference Manual</title>
2652     <releaseinfo>
2653       for $MODULE [VERSION].
2654       The latest version of this documentation can be found on-line at
2655       <ulink role="online-location" url="http://[SERVER]/$MODULE/index.html">http://[SERVER]/$MODULE/</ulink>.
2656     </releaseinfo>
2657   </bookinfo>
2659   <chapter>
2660     <title>[Insert title here]</title>
2661     $book_bottom
2662   </chapter>
2664         if (-e $OBJECT_TREE_FILE) {
2665             print OUTPUT <<EOF;
2666   <chapter id="object-tree">
2667     <title>Object Hierarchy</title>
2668     <xi:include href="xml/tree_index.sgml"/>
2669   </chapter>
2671         } else {
2672             print OUTPUT <<EOF;
2673   <!-- enable this when you use gobject types
2674   <chapter id="object-tree">
2675     <title>Object Hierarchy</title>
2676     <xi:include href="xml/tree_index.sgml"/>
2677   </chapter>
2678   -->
2680         }
2681         print OUTPUT <<EOF;
2682   <index id="api-index-full">
2683     <title>API Index</title>
2684     <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
2685   </index>
2686   <index id="deprecated-api-index" role="deprecated">
2687     <title>Index of deprecated API</title>
2688     <xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include>
2689   </index>
2691         if (keys(%AnnotationsUsed)) {
2692             print OUTPUT <<EOF;
2693   <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
2695         } else {
2696             print OUTPUT <<EOF;
2697   <!-- enable this when you use gobject introspection annotations
2698   <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
2699   -->
2701         }
2702         print OUTPUT <<EOF;
2703 </book>
2706         close (OUTPUT);
2707     }
2711 #############################################################################
2712 # Function    : CreateValidSGML
2713 # Description : This turns any chars which are used in SGML into entities,
2714 #                e.g. '<' into '&lt;'
2715 # Arguments   : $text - the text to turn into proper SGML.
2716 #############################################################################
2718 sub CreateValidSGML {
2719     my ($text) = @_;
2720     $text =~ s/&/&amp;/g;        # Do this first, or the others get messed up.
2721     $text =~ s/</&lt;/g;
2722     $text =~ s/>/&gt;/g;
2723     # browers render single tabs inconsistently
2724     $text =~ s/([^\s])\t([^\s])/$1&#160;$2/g;
2725     return $text;
2728 #############################################################################
2729 # Function    : ConvertSGMLChars
2730 # Description : This is used for text in source code comment blocks, to turn
2731 #               chars which are used in SGML into entities, e.g. '<' into
2732 #               '&lt;'. Depending on $INLINE_MARKUP_MODE, this is done
2733 #               unconditionally or only if the character doesn't seem to be
2734 #               part of an SGML construct (tag or entity reference).
2735 # Arguments   : $text - the text to turn into proper SGML.
2736 #############################################################################
2738 sub ConvertSGMLChars {
2739     my ($symbol, $text) = @_;
2741     if ($INLINE_MARKUP_MODE) {
2742         # For the XML/SGML mode only convert to entities outside CDATA sections.
2743         return &ModifyXMLElements ($text, $symbol,
2744                                    "<!\\[CDATA\\[|<programlisting[^>]*>",
2745                                    \&ConvertSGMLCharsEndTag,
2746                                    \&ConvertSGMLCharsCallback);
2747     } else {
2748         # For the simple non-sgml mode, convert to entities everywhere.
2750         # First, convert freestanding & to &amp;
2751         $text =~ s/&(?![a-zA-Z#]+;)/&amp;/g;
2752         $text =~ s/</&lt;/g;
2753         # Allow ">" at beginning of string for blockquote markdown
2754         $text =~ s/(?<=[^\w\n"'\/-])>/&gt;/g;
2756         return $text;
2757     }
2761 sub ConvertSGMLCharsEndTag {
2762   if ($_[0] eq "<!\[CDATA\[") {
2763     return "]]>";
2764   } else {
2765     return "</programlisting>";
2766   }
2769 sub ConvertSGMLCharsCallback {
2770   my ($text, $symbol, $tag) = @_;
2772   if ($tag =~ m/^<programlisting/) {
2773     # We can handle <programlisting> specially here.
2774     return &ModifyXMLElements ($text, $symbol,
2775                                "<!\\[CDATA\\[",
2776                                \&ConvertSGMLCharsEndTag,
2777                                \&ConvertSGMLCharsCallback2);
2778   } elsif ($tag eq "") {
2779     # If we're not in CDATA convert to entities.
2780     $text =~ s/&(?![a-zA-Z#]+;)/&amp;/g;        # Do this first, or the others get messed up.
2781     $text =~ s/<(?![a-zA-Z\/!])/&lt;/g;
2782     # Allow ">" at beginning of string for blockquote markdown
2783     $text =~ s/(?<=[^\w\n"'\/-])>/&gt;/g;
2785     # Handle "#include <xxxxx>"
2786     $text =~ s/#include(\s+)<([^>]+)>/#include$1&lt;$2&gt;/g;
2787   }
2789   return $text;
2792 sub ConvertSGMLCharsCallback2 {
2793   my ($text, $symbol, $tag) = @_;
2795   # If we're not in CDATA convert to entities.
2796   # We could handle <programlisting> differently, though I'm not sure it helps.
2797   if ($tag eq "") {
2798     # replace only if its not a tag
2799     $text =~ s/&(?![a-zA-Z#]+;)/&amp;/g;        # Do this first, or the others get messed up.
2800     $text =~ s/<(?![a-zA-Z\/!])/&lt;/g;
2801     $text =~ s/(?<![a-zA-Z0-9"'\/-])>/&gt;/g;
2803     # Handle "#include <xxxxx>"
2804     $text =~ s/#include(\s+)<([^>]+)>/#include$1&lt;$2&gt;/g;
2805   }
2807   return $text;
2810 #############################################################################
2811 # Function    : ExpandAnnotation
2812 # Description : This turns annotations into acronym tags.
2813 # Arguments   : $symbol - the symbol being documented, for error messages.
2814 #                $text - the text to expand.
2815 #############################################################################
2816 sub ExpandAnnotation {
2817     my ($symbol, $param_desc) = @_;
2818     my $param_annotations = "";
2820     # look for annotations at the start of the comment part
2821     # function level annotations don't end with a colon ':'
2822     if ($param_desc =~ m%^\s*\((.*?)\)(:|$)%) {
2823         my @annotations;
2824         my $annotation;
2825         $param_desc = $';
2827         @annotations = split(/\)\s*\(/,$1);
2828         @TRACE@("annotations for $symbol: '$1'\n");
2829         foreach $annotation (@annotations) {
2830             # need to search for the longest key-match in %AnnotationDefinition
2831             my $match_length=0;
2832             my $match_annotation="";
2833             my $annotationdef;
2834             foreach $annotationdef (keys %AnnotationDefinition) {
2835                 if ($annotation =~ m/^$annotationdef/) {
2836                     if (length($annotationdef)>$match_length) {
2837                         $match_length=length($annotationdef);
2838                         $match_annotation=$annotationdef;
2839                     }
2840                 }
2841             }
2842             my $annotation_extra = "";
2843             if ($match_annotation ne "") {
2844                 if ($annotation =~ m%$match_annotation\s+(.*)%) {
2845                     $annotation_extra = " $1";
2846                 }
2847                 $AnnotationsUsed{$match_annotation} = 1;
2848                 $param_annotations .= "[<acronym>$match_annotation</acronym>$annotation_extra]";
2849             }
2850             else {
2851                 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2852                     "unknown annotation \"$annotation\" in documentation for $symbol.");
2853                 $param_annotations .= "[$annotation]";
2854             }
2855         }
2856         chomp($param_desc);
2857         $param_desc =~ m/^(.*?)\.*\s*$/s;
2858         $param_desc = "$1. ";
2859     }
2860     if ($param_annotations ne "") {
2861         $param_annotations = "<emphasis role=\"annotation\">$param_annotations</emphasis>";
2862     }
2863     return ($param_desc, $param_annotations);
2866 #############################################################################
2867 # Function    : ExpandAbbreviations
2868 # Description : This turns the abbreviations function(), macro(), @param,
2869 #                %constant, and #symbol into appropriate DocBook markup.
2870 #               CDATA sections and <programlisting> parts are skipped.
2871 # Arguments   : $symbol - the symbol being documented, for error messages.
2872 #                $text - the text to expand.
2873 #############################################################################
2875 sub ExpandAbbreviations {
2876   my ($symbol, $text) = @_;
2878   # Note: This is a fallback and normally done in the markdown parser
2880   # Convert "|[" and "]|" into the start and end of program listing examples.
2881   # Support \[<!-- language="C" --> modifiers
2882   $text =~ s%\|\[<!-- language="([^"]+)" -->%<informalexample><programlisting language="$1"><![CDATA[%g;
2883   $text =~ s%\|\[%<informalexample><programlisting><![CDATA[%g;
2884   $text =~ s%\]\|%]]></programlisting></informalexample>%g;
2886   # keep CDATA unmodified, preserve ulink tags (ideally we preseve all tags
2887   # as such)
2888   return &ModifyXMLElements ($text, $symbol,
2889                              "<!\\[CDATA\\[|<ulink[^>]*>|<programlisting[^>]*>|<!DOCTYPE",
2890                              \&ExpandAbbreviationsEndTag,
2891                              \&ExpandAbbreviationsCallback);
2895 # Returns the end tag (as a regexp) corresponding to the given start tag.
2896 sub ExpandAbbreviationsEndTag {
2897   my ($start_tag) = @_;
2899   if ($start_tag eq "<!\[CDATA\[") {
2900     return "]]>";
2901   } elsif ($start_tag eq "<!DOCTYPE") {
2902     return ">";
2903   } elsif ($start_tag =~ m/<(\w+)/) {
2904     return "</$1>";
2905   }
2908 # Called inside or outside each CDATA or <programlisting> section.
2909 sub ExpandAbbreviationsCallback {
2910   my ($text, $symbol, $tag) = @_;
2912   if ($tag =~ m/^<programlisting/) {
2913     # Handle any embedded CDATA sections.
2914     return &ModifyXMLElements ($text, $symbol,
2915                                "<!\\[CDATA\\[",
2916                                \&ExpandAbbreviationsEndTag,
2917                                \&ExpandAbbreviationsCallback2);
2918   } elsif ($tag eq "") {
2919     # NOTE: this is a fallback. It is normally done by the Markdown parser.
2921     # We are outside any CDATA or <programlisting> sections, so we expand
2922     # any gtk-doc abbreviations.
2924     # Convert '@param()'
2925     # FIXME: we could make those also links ($symbol.$2), but that would be less
2926     # useful as the link target is a few lines up or down
2927     $text =~ s/(\A|[^\\])\@(\w+((\.|->)\w+)*)\s*\(\)/$1<parameter>$2()<\/parameter>/g;
2929     # Convert 'function()' or 'macro()'.
2930     # if there is abc_*_def() we don't want to make a link to _def()
2931     # FIXME: also handle abc(def(....)) : but that would need to be done recursively :/
2932     $text =~ s/([^\*.\w])(\w+)\s*\(\)/$1.&MakeXRef($2, &tagify($2 . "()", "function"));/eg;
2933     # handle #Object.func()
2934     $text =~ s/(\A|[^\\])#([\w\-:\.]+[\w]+)\s*\(\)/$1.&MakeXRef($2, &tagify($2 . "()", "function"));/eg;
2936     # Convert '@param', but not '\@param'.
2937     $text =~ s/(\A|[^\\])\@(\w+((\.|->)\w+)*)/$1<parameter>$2<\/parameter>/g;
2938     $text =~ s/\\\@/\@/g;
2940     # Convert '%constant', but not '\%constant'.
2941     # Also allow negative numbers, e.g. %-1.
2942     $text =~ s/(\A|[^\\])\%(-?\w+)/$1.&MakeXRef($2, &tagify($2, "literal"));/eg;
2943     $text =~ s/\\\%/\%/g;
2945     # Convert '#symbol', but not '\#symbol'.
2946     $text =~ s/(\A|[^\\])#([\w\-:\.]+[\w]+)/$1.&MakeHashXRef($2, "type");/eg;
2947     $text =~ s/\\#/#/g;
2948   }
2950   return $text;
2953 # This is called inside a <programlisting>
2954 sub ExpandAbbreviationsCallback2 {
2955   my ($text, $symbol, $tag) = @_;
2957   if ($tag eq "") {
2958     # We are inside a <programlisting> but outside any CDATA sections,
2959     # so we expand any gtk-doc abbreviations.
2960     # FIXME: why is this different from &ExpandAbbreviationsCallback(),
2961     #        why not just call it
2962     $text =~ s/#(\w+)/&MakeHashXRef($1, "");/eg;
2963   } elsif ($tag eq "<![CDATA[") {
2964     # NOTE: this is a fallback. It is normally done by the Markdown parser.
2965     $text = &ReplaceEntities ($text, $symbol);
2966   }
2968   return $text;
2971 sub MakeHashXRef {
2972     my ($symbol, $tag) = @_;;
2973     my $text = $symbol;
2975     # Check for things like '#include', '#define', and skip them.
2976     if ($PreProcessorDirectives{$symbol}) {
2977       return "#$symbol";
2978     }
2980     # Get rid of special suffixes ('-struct','-enum').
2981     $text =~ s/-struct$//;
2982     $text =~ s/-enum$//;
2984     # If the symbol is in the form "Object::signal", then change the symbol to
2985     # "Object-signal" and use "signal" as the text.
2986     if ($symbol =~ s/::/-/) {
2987       $text = "“$'”";
2988     }
2990     # If the symbol is in the form "Object:property", then change the symbol to
2991     # "Object--property" and use "property" as the text.
2992     if ($symbol =~ s/:/--/) {
2993       $text = "“$'”";
2994     }
2996     if ($tag ne "") {
2997       $text = tagify ($text, $tag);
2998     }
3000     return &MakeXRef($symbol, $text);
3004 #############################################################################
3005 # Function    : ModifyXMLElements
3006 # Description : Looks for given XML element tags within the text, and calls
3007 #               the callback on pieces of text inside & outside those elements.
3008 #               Used for special handling of text inside things like CDATA
3009 #               and <programlisting>.
3010 # Arguments   : $text - the text.
3011 #               $symbol - the symbol currently being documented (only used for
3012 #                      error messages).
3013 #               $start_tag_regexp - the regular expression to match start tags.
3014 #                      e.g. "<!\\[CDATA\\[|<programlisting[^>]*>" to match
3015 #                      CDATA sections or programlisting elements.
3016 #               $end_tag_func - function which is passed the matched start tag
3017 #                      and should return the appropriate end tag string regexp.
3018 #               $callback - callback called with each part of the text. It is
3019 #                      called with a piece of text, the symbol being
3020 #                      documented, and the matched start tag or "" if the text
3021 #                      is outside the XML elements being matched.
3022 #############################################################################
3023 sub ModifyXMLElements {
3024     my ($text, $symbol, $start_tag_regexp, $end_tag_func, $callback) = @_;
3025     my ($before_tag, $start_tag, $end_tag_regexp, $end_tag);
3026     my $result = "";
3028     while ($text =~ m/$start_tag_regexp/s) {
3029       $before_tag = $`; # Prematch for last successful match string
3030       $start_tag = $&;  # Last successful match
3031       $text = $';       # Postmatch for last successful match string
3033       $result .= &$callback ($before_tag, $symbol, "");
3034       $result .= $start_tag;
3036       # get the matching end-tag for current tag
3037       $end_tag_regexp = &$end_tag_func ($start_tag);
3039       if ($text =~ m/$end_tag_regexp/s) {
3040         $before_tag = $`;
3041         $end_tag = $&;
3042         $text = $';
3044         $result .= &$callback ($before_tag, $symbol, $start_tag);
3045         $result .= $end_tag;
3046       } else {
3047         &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
3048             "Can't find tag end: $end_tag_regexp in docs for: $symbol.");
3049         # Just assume it is all inside the tag.
3050         $result .= &$callback ($text, $symbol, $start_tag);
3051         $text = "";
3052       }
3053     }
3055     # Handle any remaining text outside the tags.
3056     $result .= &$callback ($text, $symbol, "");
3058     return $result;
3061 sub noop {
3062   return $_[0];
3065 # Adds a tag around some text.
3066 # e.g tagify("Text", "literal") => "<literal>Text</literal>".
3067 sub tagify {
3068    my ($text, $elem) = @_;
3069    return "<" . $elem . ">" . $text . "</" . $elem . ">";
3072 #############################################################################
3073 # Function    : MakeDocHeader
3074 # Description : Builds a docbook header for the given tag
3075 # Arguments   : $tag - doctype tag
3076 #############################################################################
3078 sub MakeDocHeader {
3079     my ($tag) = @_;
3080     my $header = $doctype_header;
3081     $header =~ s/<!DOCTYPE \w+/<!DOCTYPE $tag/;
3082     return $header;
3086 #############################################################################
3087 # Function    : MakeXRef
3088 # Description : This returns a cross-reference link to the given symbol.
3089 #                Though it doesn't try to do this for a few standard C types
3090 #                that it        knows won't be in the documentation.
3091 # Arguments   : $symbol - the symbol to try to create a XRef to.
3092 #               $text - text text to put inside the XRef, defaults to $symbol
3093 #############################################################################
3095 sub MakeXRef {
3096     my ($symbol, $text) = ($_[0], $_[1]);
3098     $symbol =~ s/^\s+//;
3099     $symbol =~ s/\s+$//;
3101     if (!defined($text)) {
3102         $text = $symbol;
3104         # Get rid of special suffixes ('-struct','-enum').
3105         $text =~ s/-struct$//;
3106         $text =~ s/-enum$//;
3107     }
3109     if ($symbol =~ m/ /) {
3110         return "$text";
3111     }
3113     @TRACE@("Getting type link for $symbol -> $text\n");
3115     my $symbol_id = &CreateValidSGMLID ($symbol);
3116     return "<link linkend=\"$symbol_id\">$text</link>";
3120 #############################################################################
3121 # Function    : MakeIndexterms
3122 # Description : This returns a indexterm elements for the given symbol
3123 # Arguments   : $symbol - the symbol to create indexterms for
3124 #############################################################################
3126 sub MakeIndexterms {
3127   my ($symbol, $id) = @_;
3128   my $terms =  "";
3129   my $sortas = "";
3131   # make the index useful, by ommiting the namespace when sorting
3132   if ($NAME_SPACE ne "") {
3133     if ($symbol =~ m/^$NAME_SPACE\_?(.*)/i) {
3134        $sortas=" sortas=\"$1\"";
3135     }
3136   }
3138   if (exists $Deprecated{$symbol}) {
3139       $terms .= "<indexterm zone=\"$id\" role=\"deprecated\"><primary$sortas>$symbol</primary></indexterm>";
3140       $IndexEntriesDeprecated{$symbol}=$id;
3141       $IndexEntriesFull{$symbol}=$id;
3142   }
3143   if (exists $Since{$symbol}) {
3144      my $since = $Since{$symbol};
3145      $since =~ s/^\s+//;
3146      $since =~ s/\s+$//;
3147      if ($since ne "") {
3148          $terms .= "<indexterm zone=\"$id\" role=\"$since\"><primary$sortas>$symbol</primary></indexterm>";
3149      }
3150      $IndexEntriesSince{$symbol}=$id;
3151      $IndexEntriesFull{$symbol}=$id;
3152   }
3153   if ($terms eq "") {
3154      $terms .= "<indexterm zone=\"$id\"><primary$sortas>$symbol</primary></indexterm>";
3155      $IndexEntriesFull{$symbol}=$id;
3156   }
3158   return $terms;
3161 #############################################################################
3162 # Function    : MakeDeprecationNote
3163 # Description : This returns a deprecation warning for the given symbol.
3164 # Arguments   : $symbol - the symbol to try to create a warning for.
3165 #############################################################################
3167 sub MakeDeprecationNote {
3168     my ($symbol) = $_[0];
3169     my $desc = "";
3170     if (exists $Deprecated{$symbol}) {
3171         my $note;
3173         $desc .= "<warning><para><literal>$symbol</literal> ";
3175         $note = $Deprecated{$symbol};
3177         if ($note =~ /^\s*([0-9\.]+)\s*:?/) {
3178                 $desc .= "has been deprecated since version $1 and should not be used in newly-written code.</para>";
3179         } else {
3180                 $desc .= "is deprecated and should not be used in newly-written code.</para>";
3181         }
3182         $note =~ s/^\s*([0-9\.]+)\s*:?\s*//;
3183         $note =~ s/^\s+//;
3184         $note =~ s/\s+$//;
3185         if ($note ne "") {
3186             $note = &ConvertMarkDown($symbol, $note);
3187             $desc .= " " . $note;
3188         }
3189         $desc .= "</warning>\n";
3190     }
3191     return $desc;
3194 #############################################################################
3195 # Function    : MakeConditionDescription
3196 # Description : This returns a sumary of conditions for the given symbol.
3197 # Arguments   : $symbol - the symbol to try to create the sumary.
3198 #############################################################################
3200 sub MakeConditionDescription {
3201     my ($symbol) = $_[0];
3202     my $desc = "";
3204     if (exists $Deprecated{$symbol}) {
3205         if ($desc ne "") {
3206             $desc .= "|";
3207         }
3209         if ($Deprecated{$symbol} =~ /^\s*(.*?)\s*$/) {
3210                 $desc .= "deprecated:$1";
3211         } else {
3212                 $desc .= "deprecated";
3213         }
3214     }
3216     if (exists $Since{$symbol}) {
3217         if ($desc ne "") {
3218             $desc .= "|";
3219         }
3221         if ($Since{$symbol} =~ /^\s*(.*?)\s*$/) {
3222                 $desc .= "since:$1";
3223         } else {
3224                 $desc .= "since";
3225         }
3226     }
3228     if (exists $StabilityLevel{$symbol}) {
3229         if ($desc ne "") {
3230             $desc .= "|";
3231         }
3232         $desc .= "stability:".$StabilityLevel{$symbol};
3233     }
3235     if ($desc ne "") {
3236         my $cond = $desc;
3237         $cond =~ s/\"/&quot;/g;
3238         $desc=" condition=\"".$cond."\"";
3239         @TRACE@("condition for '$symbol' = '$desc'\n");
3240     }
3241     return $desc;
3244 #############################################################################
3245 # Function    : GetHierarchy
3246 # Description : Returns the DocBook output describing the ancestors and
3247 #               immediate children of a GObject subclass. It uses the
3248 #               global @Objects and @ObjectLevels arrays to walk the tree.
3250 # Arguments   : $object - the GtkObject subclass.
3251 #               @hierarchy - previous hierarchy
3252 #############################################################################
3254 sub GetHierarchy {
3255     my ($object,$hierarchy_ref) = @_;
3256     my @hierarchy = @{$hierarchy_ref};
3257     
3258     # Find object in the objects array.
3259     my $found = 0;
3260     my @children = ();
3261     my $i;
3262     my $level;
3263     my $j;
3264     for ($i = 0; $i < @Objects; $i++) {
3265         if ($found) {
3266             if ($ObjectLevels[$i] <= $level) {
3267             last;
3268         }
3269             elsif ($ObjectLevels[$i] == $level + 1) {
3270                 push (@children, $Objects[$i]);
3271             }
3272         }
3273         elsif ($Objects[$i] eq $object) {
3274             $found = 1;
3275             $j = $i;
3276             $level = $ObjectLevels[$i];
3277         }
3278     }
3279     if (!$found) {
3280         return @hierarchy;
3281     }
3283     # Walk up the hierarchy, pushing ancestors onto the ancestors array.
3284     my @ancestors = ();
3285     push (@ancestors, $object);
3286     @TRACE@("Level: $level\n");
3287     while ($level > 1) {
3288         $j--;
3289         if ($ObjectLevels[$j] < $level) {
3290             push (@ancestors, $Objects[$j]);
3291             $level = $ObjectLevels[$j];
3292             @TRACE@("Level: $level\n");
3293         }
3294     }
3296     # Output the ancestors, indented and with links.
3297     my $last_index = 0;
3298     $level = 1;
3299     for ($i = $#ancestors; $i >= 0; $i--) {
3300         my $entry_text;
3301         my $alt_text;
3302         my $ancestor = $ancestors[$i];
3303         my $ancestor_id = &CreateValidSGMLID ($ancestor);
3304         my $indent = ' ' x ($level * 4);
3305         # Don't add a link to the current object, i.e. when i == 0.
3306         if ($i > 0) {
3307             $entry_text = $indent . "<link linkend=\"$ancestor_id\">$ancestor</link>";
3308             $alt_text = $indent . $ancestor;
3309         } else {
3310             $entry_text = $indent . $ancestor;
3311             $alt_text = $indent . "<link linkend=\"$ancestor_id\">$ancestor</link>";
3312         }
3313         @TRACE@("Checking for '$entry_text' or '$alt_text'"); 
3314         # Check if we already have this object
3315         my $index = -1;
3316         for ($j = 0; $j <= $#hierarchy; $j++) {
3317             if (($hierarchy[$j] eq $entry_text) or ($hierarchy[$j] eq $alt_text)) {
3318                 $index = $j;
3319                 last;
3320             }
3321         }
3322         if ($index == -1) {
3323             # We have a new entry, find insert position in alphabetical order
3324             my $found = 0;
3325             for ($j = $last_index; $j <= $#hierarchy; $j++) {
3326                 if ($hierarchy[$j] !~ m/^${indent}/) {
3327                     $last_index = $j;
3328                     $found = 1;
3329                     last;
3330                 } elsif ($hierarchy[$j] =~ m/^${indent}[^ ]/) {
3331                     my $stripped_text = $hierarchy[$j];
3332                     if ($entry_text !~ m/<link linkend/) {
3333                         $stripped_text =~ s%<link linkend="[A-Za-z]*">%%;
3334                         $stripped_text =~ s%</link>%%;
3335                     }
3336                     if ($entry_text lt $stripped_text) {
3337                         $last_index = $j;
3338                         $found = 1;
3339                         last;
3340                     } 
3341                 }
3342             }
3343             # Append to bottom
3344             if (!$found) {
3345               $last_index = 1 + $#hierarchy;
3346             }
3347             splice @hierarchy, $last_index, 0, ($entry_text);
3348             $last_index++;
3349         } else {
3350             # Already have this one, make sure we use the not linked version
3351             if ($entry_text !~ m/<link linkend=/) {
3352               $hierarchy[$j] = $entry_text;
3353             }
3354             # Remember index as base insert point
3355             $last_index = $index + 1;
3356         }
3357         $level++;
3358     }
3359     # Output the children, indented and with links.
3360     for ($i = 0; $i <= $#children; $i++) {
3361         my $id = &CreateValidSGMLID ($children[$i]);
3362         my $indented_text = ' ' x ($level * 4) . "<link linkend=\"$id\">$children[$i]</link>";
3363         splice @hierarchy, $last_index, 0, ($indented_text);
3364         $last_index++;
3365     }    
3367     return @hierarchy; 
3370 #############################################################################
3371 # Function    : GetInterfaces
3372 # Description : Returns the DocBook output describing the interfaces
3373 #               implemented by a class. It uses the global %Interfaces hash.
3374 # Arguments   : $object - the GtkObject subclass.
3375 #############################################################################
3377 sub GetInterfaces {
3378     my ($object) = @_;
3379     my $text = "";
3380     my $i;
3382     # Find object in the objects array.
3383     if (exists($Interfaces{$object})) {
3384         my @ifaces = split(' ', $Interfaces{$object});
3385         $text = <<EOF;
3386 <para>
3387 $object implements
3389         for ($i = 0; $i <= $#ifaces; $i++) {
3390             my $id = &CreateValidSGMLID ($ifaces[$i]);
3391             $text .= " <link linkend=\"$id\">$ifaces[$i]</link>";
3392             if ($i < $#ifaces - 1) {
3393                 $text .= ', ';
3394             }
3395             elsif ($i < $#ifaces) {
3396                 $text .= ' and ';
3397             }
3398             else {
3399                 $text .= '.';
3400             }
3401         }
3402         $text .= <<EOF;
3403 </para>
3405     }
3407     return $text;
3410 #############################################################################
3411 # Function    : GetImplementations
3412 # Description : Returns the DocBook output describing the implementations
3413 #               of an interface. It uses the global %Interfaces hash.
3414 # Arguments   : $object - the GtkObject subclass.
3415 #############################################################################
3417 sub GetImplementations {
3418     my ($object) = @_;
3419     my @impls = ();
3420     my $text = "";
3421     my $i;
3422     foreach my $key (keys %Interfaces) {
3423         if ($Interfaces{$key} =~ /\b$object\b/) {
3424             push (@impls, $key);
3425         }
3426     }
3427     if ($#impls >= 0) {
3428         @impls = sort @impls;
3429         $text = <<EOF;
3430 <para>
3431 $object is implemented by
3433         for ($i = 0; $i <= $#impls; $i++) {
3434             my $id = &CreateValidSGMLID ($impls[$i]);
3435             $text .= " <link linkend=\"$id\">$impls[$i]</link>";
3436             if ($i < $#impls - 1) {
3437                 $text .= ', ';
3438             }
3439             elsif ($i < $#impls) {
3440                 $text .= ' and ';
3441             }
3442             else {
3443                 $text .= '.';
3444             }
3445         }
3446         $text .= <<EOF;
3447 </para>
3449     }
3450     return $text;
3454 #############################################################################
3455 # Function    : GetPrerequisites
3456 # Description : Returns the DocBook output describing the prerequisites
3457 #               of an interface. It uses the global %Prerequisites hash.
3458 # Arguments   : $iface - the interface.
3459 #############################################################################
3461 sub GetPrerequisites {
3462     my ($iface) = @_;
3463     my $text = "";
3464     my $i;
3466     if (exists($Prerequisites{$iface})) {
3467         $text = <<EOF;
3468 <para>
3469 $iface requires
3471         my @prereqs = split(' ', $Prerequisites{$iface});
3472         for ($i = 0; $i <= $#prereqs; $i++) {
3473             my $id = &CreateValidSGMLID ($prereqs[$i]);
3474             $text .= " <link linkend=\"$id\">$prereqs[$i]</link>";
3475             if ($i < $#prereqs - 1) {
3476                 $text .= ', ';
3477             }
3478             elsif ($i < $#prereqs) {
3479                 $text .= ' and ';
3480             }
3481             else {
3482                 $text .= '.';
3483             }
3484         }
3485         $text .= <<EOF;
3486 </para>
3488     }
3489     return $text;
3492 #############################################################################
3493 # Function    : GetDerived
3494 # Description : Returns the DocBook output describing the derived interfaces
3495 #               of an interface. It uses the global %Prerequisites hash.
3496 # Arguments   : $iface - the interface.
3497 #############################################################################
3499 sub GetDerived {
3500     my ($iface) = @_;
3501     my $text = "";
3502     my $i;
3504     my @derived = ();
3505     foreach my $key (keys %Prerequisites) {
3506         if ($Prerequisites{$key} =~ /\b$iface\b/) {
3507             push (@derived, $key);
3508         }
3509     }
3510     if ($#derived >= 0) {
3511         @derived = sort @derived;
3512         $text = <<EOF;
3513 <para>
3514 $iface is required by
3516         for ($i = 0; $i <= $#derived; $i++) {
3517             my $id = &CreateValidSGMLID ($derived[$i]);
3518             $text .= " <link linkend=\"$id\">$derived[$i]</link>";
3519             if ($i < $#derived - 1) {
3520                 $text .= ', ';
3521             }
3522             elsif ($i < $#derived) {
3523                 $text .= ' and ';
3524             }
3525             else {
3526                 $text .= '.';
3527             }
3528         }
3529         $text .= <<EOF;
3530 </para>
3532     }
3533     return $text;
3537 #############################################################################
3538 # Function    : GetSignals
3539 # Description : Returns the synopsis and detailed description DocBook output
3540 #                for the signal handlers of a given GtkObject subclass.
3541 # Arguments   : $object - the GtkObject subclass, e.g. 'GtkButton'.
3542 #############################################################################
3544 sub GetSignals {
3545     my ($object) = @_;
3546     my $synop = "";
3547     my $desc = "";
3549     my $i;
3550     for ($i = 0; $i <= $#SignalObjects; $i++) {
3551         if ($SignalObjects[$i] eq $object) {
3552             @TRACE@("Found signal: $SignalNames[$i]\n");
3553             my $name = $SignalNames[$i];
3554             my $symbol = "${object}::${name}";
3555             my $id = &CreateValidSGMLID ("$object-$name");
3557             $desc .= "<refsect2 id=\"$id\" role=\"signal\"><title>The <literal>“$name”</literal> signal</title>\n";
3558             $desc .= MakeIndexterms($symbol, $id);
3559             $desc .= "\n";
3560             $desc .= OutputSymbolExtraLinks($symbol);
3562             $desc .= "<programlisting language=\"C\">";
3564             $SignalReturns[$i] =~ m/\s*(const\s+)?(\w+)\s*(\**)/;
3565             my $type_modifier = defined($1) ? $1 : "";
3566             my $type = $2;
3567             my $pointer = $3;
3568             my $xref = &MakeXRef ($type, &tagify($type, "returnvalue"));
3570             my $ret_type_output = "$type_modifier$xref$pointer";
3571             my $callback_name = "user_function";
3572             $desc  .= "${ret_type_output}\n${callback_name} (";
3574             my $indentation = ' ' x (length($callback_name) + 2);
3575             my $pad = $indentation;
3577             my $sourceparams = $SourceSymbolParams{$symbol};
3578             my @params = split ("\n", $SignalPrototypes[$i]);
3579             my $j;
3580             my $l;
3581             my $type_len = length("gpointer");
3582             my $name_len = length("user_data");
3583             # do two passes, the first one is to calculate padding
3584             for ($l = 0; $l < 2; $l++) {
3585                 for ($j = 0; $j <= $#params; $j++) {
3586                     my $param_name;
3587                     # allow alphanumerics, '_', '[' & ']' in param names
3588                     if ($params[$j] =~ m/^\s*(\w+)\s*(\**)\s*([\w\[\]]+)\s*$/) {
3589                         $type = $1;
3590                         $pointer = $2;
3591                         if (defined($sourceparams)) {
3592                             $param_name = $$sourceparams[$PARAM_FIELD_COUNT * $j];
3593                         }
3594                         else {
3595                             $param_name = $3;
3596                         }
3597                         if (!defined($param_name)) {
3598                             $param_name = "arg$j";
3599                         }
3600                         if ($l == 0) {
3601                             if (length($type) + length($pointer) > $type_len) {
3602                                 $type_len = length($type) + length($pointer);
3603                             }
3604                             if (length($param_name) > $name_len) {
3605                                 $name_len = length($param_name);
3606                             }
3607                         }
3608                         else {
3609                             $xref = &MakeXRef ($type, &tagify($type, "type"));
3610                             $pad = ' ' x ($type_len - length($type) - length($pointer));
3611                             $desc .= "$xref$pad $pointer${param_name},\n";
3612                             $desc .= $indentation;
3613                         }
3614                     } else {
3615                         &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
3616                              "Can't parse arg: $params[$j]\nArgs:$SignalPrototypes[$i]");
3617                     }
3618                 }
3619             }
3620             $xref = &MakeXRef ("gpointer", &tagify("gpointer", "type"));
3621             $pad = ' ' x ($type_len - length("gpointer"));
3622             $desc  .= "$xref$pad user_data)";
3623             $desc  .= "</programlisting>\n";
3625             my $flags = $SignalFlags[$i];
3626             my $flags_string = "";
3628             if (defined ($flags)) {
3629               if ($flags =~ m/f/) {
3630                 $flags_string = "<link linkend=\"G-SIGNAL-RUN-FIRST:CAPS\">Run First</link>";
3631               }
3632               elsif ($flags =~ m/l/) {
3633                 $flags_string = "<link linkend=\"G-SIGNAL-RUN-LAST:CAPS\">Run Last</link>";
3634               }
3635               elsif ($flags =~ m/c/) {
3636                 $flags_string = "<link linkend=\"G-SIGNAL-RUN-CLEANUP:CAPS\">Cleanup</link>";
3637                 $flags_string = "Cleanup";
3638               }
3639               if ($flags =~ m/r/) {
3640                 if ($flags_string) { $flags_string .= " / "; }
3641                 $flags_string = "<link linkend=\"G-SIGNAL-NO-RECURSE:CAPS\">No Recursion</link>";
3642               }
3643               if ($flags =~ m/d/) {
3644                 if ($flags_string) { $flags_string .= " / "; }
3645                 $flags_string = "<link linkend=\"G-SIGNAL-DETAILED:CAPS\">Has Details</link>";
3646               }
3647               if ($flags =~ m/a/) {
3648                 if ($flags_string) { $flags_string .= " / "; }
3649                 $flags_string = "<link linkend=\"G-SIGNAL-ACTION:CAPS\">Action</link>";
3650               }
3651               if ($flags =~ m/h/) {
3652                 if ($flags_string) { $flags_string .= " / "; }
3653                 $flags_string = "<link linkend=\"G-SIGNAL-NO-HOOKS:CAPS\">No Hooks</link>";
3654               }
3655             }
3657             $synop .= "<row><entry role=\"signal_type\">${ret_type_output}</entry><entry role=\"signal_name\"><link linkend=\"$id\">${name}</link></entry><entry role=\"signal_flags\">${flags_string}</entry></row>\n";
3659             my $parameters = &OutputParamDescriptions ("SIGNAL", $symbol);
3661             $AllSymbols{$symbol} = 1;
3662             if (defined ($SymbolDocs{$symbol})) {
3663                 my $symbol_docs = &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
3665                 $desc .= $symbol_docs;
3667                 if (!IsEmptyDoc($SymbolDocs{$symbol})) {
3668                     $AllDocumentedSymbols{$symbol} = 1;
3669                 }
3670             }
3671             if (defined ($SymbolAnnotations{$symbol})) {
3672                 my $param_desc = $SymbolAnnotations{$symbol};
3673                 my $param_annotations = "";
3674                 ($param_desc,$param_annotations) = &ExpandAnnotation($symbol, $param_desc);
3675                 if ($param_annotations ne "") {
3676                     $desc .= "\n<para>$param_annotations</para>";
3677                 }
3678             }
3679             $desc .= &MakeDeprecationNote($symbol);
3681             $desc .= $parameters;
3682             if ($flags_string) {
3683                 $desc  .= "<para>Flags: $flags_string</para>\n";
3684             }
3685             $desc .= OutputSymbolTraits ($symbol);
3686             $desc .= "</refsect2>";
3687         }
3688     }
3689     return ($synop, $desc);
3693 #############################################################################
3694 # Function    : GetArgs
3695 # Description : Returns the synopsis and detailed description DocBook output
3696 #                for the Args of a given GtkObject subclass.
3697 # Arguments   : $object - the GtkObject subclass, e.g. 'GtkButton'.
3698 #############################################################################
3700 sub GetArgs {
3701     my ($object) = @_;
3702     my $synop = "";
3703     my $desc = "";
3704     my $child_synop = "";
3705     my $child_desc = "";
3706     my $style_synop = "";
3707     my $style_desc = "";
3709     my $i;
3710     for ($i = 0; $i <= $#ArgObjects; $i++) {
3711         if ($ArgObjects[$i] eq $object) {
3712             @TRACE@("Found arg: $ArgNames[$i]\n");
3713             my $name = $ArgNames[$i];
3714             my $flags = $ArgFlags[$i];
3715             my $flags_string = "";
3716             my $kind = "";
3717             my $id_sep = "";
3719             if ($flags =~ m/c/) {
3720                 $kind = "child property";
3721                 $id_sep = "c-";
3722             }
3723             elsif ($flags =~ m/s/) {
3724                 $kind = "style property";
3725                 $id_sep = "s-";
3726             }
3727             else {
3728                 $kind = "property";
3729             }
3731             # Remember only one colon so we don't clash with signals.
3732             my $symbol = "${object}:${name}";
3733             # use two dashes and ev. an extra separator here for the same reason.
3734             my $id = &CreateValidSGMLID ("$object--$id_sep$name");
3736             my $type = $ArgTypes[$i];
3737             my $type_output;
3738             my $range = $ArgRanges[$i];
3739             my $range_output = CreateValidSGML ($range);
3740             my $default = $ArgDefaults[$i];
3741             my $default_output = CreateValidSGML ($default);
3743             if ($type eq "GtkString") {
3744                 $type = "char&#160;*";
3745             }
3746             if ($type eq "GtkSignal") {
3747                 $type = "GtkSignalFunc, gpointer";
3748                 $type_output = &MakeXRef ("GtkSignalFunc") . ", "
3749                     . &MakeXRef ("gpointer");
3750             } elsif ($type =~ m/^(\w+)\*$/) {
3751                 $type_output = &MakeXRef ($1, &tagify($1, "type")) . "&#160;*";
3752             } else {
3753                 $type_output = &MakeXRef ($type, &tagify($type, "type"));
3754             }
3756             if ($flags =~ m/r/) {
3757                 $flags_string = "Read";
3758             }
3759             if ($flags =~ m/w/) {
3760                 if ($flags_string) { $flags_string .= " / "; }
3761                 $flags_string .= "Write";
3762             }
3763             if ($flags =~ m/x/) {
3764                 if ($flags_string) { $flags_string .= " / "; }
3765                 $flags_string .= "Construct";
3766             }
3767             if ($flags =~ m/X/) {
3768                 if ($flags_string) { $flags_string .= " / "; }
3769                 $flags_string .= "Construct Only";
3770             }
3772             $AllSymbols{$symbol} = 1;
3773             my $blurb = "";
3774             if (defined($SymbolDocs{$symbol}) &&
3775                 !IsEmptyDoc($SymbolDocs{$symbol})) {
3776                 $blurb = &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
3777                 @TRACE@(".. [$SymbolDocs{$symbol}][$blurb]\n");
3778                 $AllDocumentedSymbols{$symbol} = 1;
3779             }
3780             else {
3781                 if ($ArgBlurbs[$i] ne "") {
3782                     $blurb = "<para>" . &CreateValidSGML ($ArgBlurbs[$i]) . "</para>";
3783                     $AllDocumentedSymbols{$symbol} = 1;
3784                 } else {
3785                     # FIXME: print a warning?
3786                     @TRACE@(".. no description\n");
3787                 }
3788             }
3790             my $pad1 = " " x (24 - length ($name));
3792             my $arg_synop = "<row><entry role=\"property_type\">$type_output</entry><entry role=\"property_name\"><link linkend=\"$id\">$name</link></entry><entry role=\"property_flags\">$flags_string</entry></row>\n";
3793             my $arg_desc = "<refsect2 id=\"$id\" role=\"property\"><title>The <literal>“$name”</literal> $kind</title>\n";
3794             $arg_desc .= MakeIndexterms($symbol, $id);
3795             $arg_desc .= "\n";
3796             $arg_desc .= OutputSymbolExtraLinks($symbol);
3798             $arg_desc .= "<programlisting>  “$name”$pad1 $type_output</programlisting>\n";
3799             $arg_desc .= $blurb;
3800             if (defined ($SymbolAnnotations{$symbol})) {
3801                 my $param_desc = $SymbolAnnotations{$symbol};
3802                 my $param_annotations = "";
3803                 ($param_desc,$param_annotations) = &ExpandAnnotation($symbol, $param_desc);
3804                 if ($param_annotations ne "") {
3805                     $arg_desc .= "\n<para>$param_annotations</para>";
3806                 }
3807             }
3808             $arg_desc .= &MakeDeprecationNote($symbol);
3810             if ($flags_string) {
3811               $arg_desc  .= "<para>Flags: $flags_string</para>\n";
3812             }
3813             if ($range ne "") {
3814                 $arg_desc .= "<para>Allowed values: $range_output</para>\n";
3815             }
3816             if ($default ne "") {
3817                 $arg_desc .= "<para>Default value: $default_output</para>\n";
3818             }
3819             $arg_desc .= OutputSymbolTraits ($symbol);
3820             $arg_desc .= "</refsect2>\n";
3822             if ($flags =~ m/c/) {
3823                 $child_synop .= $arg_synop;
3824                 $child_desc .= $arg_desc;
3825             }
3826             elsif ($flags =~ m/s/) {
3827                 $style_synop .= $arg_synop;
3828                 $style_desc .= $arg_desc;
3829             }
3830             else {
3831                 $synop .= $arg_synop;
3832                 $desc .= $arg_desc;
3833             }
3834         }
3835     }
3836     return ($synop, $child_synop, $style_synop, $desc, $child_desc, $style_desc);
3840 #############################################################################
3841 # Function    : ReadSourceDocumentation
3842 # Description : This reads in the documentation embedded in comment blocks
3843 #                in the source code (for Gnome).
3845 #                Parameter descriptions override any in the template files.
3846 #                Function descriptions are placed before any description from
3847 #                the template files.
3849 #                It recursively descends the source directory looking for .c
3850 #                files and scans them looking for specially-formatted comment
3851 #                blocks.
3853 # Arguments   : $source_dir - the directory to scan.
3854 #############m###############################################################
3856 sub ReadSourceDocumentation {
3857     my ($source_dir) = @_;
3858     my ($file, $dir, @suffix_list, $suffix);
3860     # prepend entries from @SOURCE_DIR
3861     for my $dir (@SOURCE_DIRS) {
3862         # Check if the filename is in the ignore list.
3863         if ($source_dir =~ m%^\Q$dir\E/(.*)$% and $IGNORE_FILES =~ m/(\s|^)\Q$1\E(\s|$)/) {
3864             @TRACE@("Skipping source directory: $source_dir");
3865             return;
3866         } else {
3867             @TRACE@("No match for: ".($1 || $source_dir));
3868         }
3869     }
3871     @TRACE@("Scanning source directory: $source_dir");
3873     # This array holds any subdirectories found.
3874     my (@subdirs) = ();
3876     @suffix_list = split (/,/, $SOURCE_SUFFIXES);
3878     opendir (SRCDIR, $source_dir)
3879         || die "Can't open source directory $source_dir: $!";
3881     foreach $file (readdir (SRCDIR)) {
3882       if ($file =~ /^\./) {
3883         next;
3884       } elsif (-d "$source_dir/$file") {
3885         push (@subdirs, $file);
3886       } elsif (@suffix_list) {
3887         foreach $suffix (@suffix_list) {
3888           if ($file =~ m/\.\Q${suffix}\E$/) {
3889             &ScanSourceFile ("$source_dir/$file");
3890           }
3891         }
3892       } elsif ($file =~ m/\.[ch]$/) {
3893         &ScanSourceFile ("$source_dir/$file");
3894       }
3895     }
3896     closedir (SRCDIR);
3898     # Now recursively scan the subdirectories.
3899     foreach $dir (@subdirs) {
3900         &ReadSourceDocumentation ("$source_dir/$dir");
3901     }
3905 #############################################################################
3906 # Function    : ScanSourceFile
3907 # Description : Scans one source file looking for specially-formatted comment
3908 #                blocks. Later &MergeSourceDocumentation is used to merge any
3909 #                documentation found with the documentation already read in
3910 #                from the template files.
3912 # Arguments   : $file - the file to scan.
3913 #############################################################################
3915 sub ScanSourceFile {
3916     my ($file) = @_;
3917     my $basename;
3919     # prepend entries from @SOURCE_DIR
3920     for my $dir (@SOURCE_DIRS) {
3921         # Check if the filename is in the ignore list.
3922         if ($file =~ m%^\Q$dir\E/(.*)$% and $IGNORE_FILES =~ m/(\s|^)\Q$1\E(\s|$)/) {
3923             @TRACE@("Skipping source file: $file");
3924             return;
3925         }
3926     }
3928     if ($file =~ m/^.*[\/\\]([^\/\\]*)$/) {
3929         $basename = $1;
3930     } else {
3931         &LogWarning ($file, 1, "Can't find basename for this filename.");
3932         $basename = $file;
3933     }
3935     # Check if the basename is in the list of files to ignore.
3936     if ($IGNORE_FILES =~ m/(\s|^)\Q${basename}\E(\s|$)/) {
3937         @TRACE@("Skipping source file: $file");
3938         return;
3939     }
3941     @TRACE@("Scanning source file: $file");
3943     open (SRCFILE, $file)
3944         || die "Can't open $file: $!";
3945     my $in_comment_block = 0;
3946     my $symbol;
3947     my $in_part = "";
3948     my ($description, $return_desc);
3949     my ($since_desc, $stability_desc, $deprecated_desc);
3950     my $current_param;
3951     my @params;
3952     while (<SRCFILE>) {
3953         # Look for the start of a comment block.
3954         if (!$in_comment_block) {
3955             if (m%^\s*/\*.*\*/%) {
3956                 #one-line comment - not gtkdoc
3957             } elsif (m%^\s*/\*\*\s%) {
3958                 @TRACE@("Found comment block start\n");
3960                 $in_comment_block = 1;
3962                 # Reset all the symbol data.
3963                 $symbol = "";
3964                 $in_part = "";
3965                 $description = "";
3966                 $return_desc = "";
3967                 $since_desc = "";
3968                 $deprecated_desc = "";
3969                 $stability_desc = "";
3970                 $current_param = -1;
3971                 @params = ();
3972             }
3973             next;
3974         }
3976         # We're in a comment block. Check if we've found the end of it.
3977         if (m%^\s*\*+/%) {
3978             if (!$symbol) {
3979                 # maybe its not even meant to be a gtk-doc comment?
3980                 &LogWarning ($file, $., "Symbol name not found at the start of the comment block.");
3981             } else {
3982                 # Add the return value description onto the end of the params.
3983                 if ($return_desc) {
3984                     # TODO(ensonic): check for duplicated Return docs
3985                     # &LogWarning ($file, $., "Multiple Returns for $symbol.");
3986                     push (@params, "Returns");
3987                     push (@params, $return_desc);
3988                 }
3989                 # Convert special characters
3990                 $description = &ConvertSGMLChars ($symbol, $description);
3991                 my $k;
3992                 for ($k = 1; $k <= $#params; $k += $PARAM_FIELD_COUNT) {
3993                     $params[$k] = &ConvertSGMLChars ($symbol, $params[$k]);
3994                 }
3996                 # Handle Section docs
3997                 if ($symbol =~ m/SECTION:\s*(.*)/) {
3998                     my $real_symbol=$1;
3999                     my $key;
4001                     if (scalar %KnownSymbols) {
4002                         if ((! defined($KnownSymbols{"$TMPL_DIR/$real_symbol:Long_Description"})) || $KnownSymbols{"$TMPL_DIR/$real_symbol:Long_Description"} != 1) {
4003                             &LogWarning ($file, $., "Section $real_symbol is not defined in the $MODULE-sections.txt file.");
4004                         }
4005                     }
4007                     @TRACE@("SECTION DOCS found in source for : '$real_symbol'\n");
4008                     for ($k = 0; $k <= $#params; $k += $PARAM_FIELD_COUNT) {
4009                         @TRACE@("   '".$params[$k]."'\n");
4010                         $params[$k] = "\L$params[$k]";
4011                         undef $key;
4012                         if ($params[$k] eq "short_description") {
4013                             $key = "$TMPL_DIR/$real_symbol:Short_Description";
4014                         } elsif ($params[$k] eq "see_also") {
4015                             $key = "$TMPL_DIR/$real_symbol:See_Also";
4016                         } elsif ($params[$k] eq "title") {
4017                             $key = "$TMPL_DIR/$real_symbol:Title";
4018                         } elsif ($params[$k] eq "stability") {
4019                             $key = "$TMPL_DIR/$real_symbol:Stability_Level";
4020                         } elsif ($params[$k] eq "section_id") {
4021                             $key = "$TMPL_DIR/$real_symbol:Section_Id";
4022                         } elsif ($params[$k] eq "include") {
4023                             $key = "$TMPL_DIR/$real_symbol:Include";
4024                         } elsif ($params[$k] eq "image") {
4025                             $key = "$TMPL_DIR/$real_symbol:Image";
4026                         }
4027                         if (defined($key)) {
4028                             $SourceSymbolDocs{$key}=$params[$k+1];
4029                             $SourceSymbolSourceFile{$key} = $file;
4030                             $SourceSymbolSourceLine{$key} = $.;
4031                         }
4032                     }
4033                     $SourceSymbolDocs{"$TMPL_DIR/$real_symbol:Long_Description"}=$description;
4034                     $SourceSymbolSourceFile{"$TMPL_DIR/$real_symbol:Long_Description"} = $file;
4035                     $SourceSymbolSourceLine{"$TMPL_DIR/$real_symbol:Long_Description"} = $.;
4036                     #$SourceSymbolTypes{$symbol} = "SECTION";
4037                 } else {
4038                     @TRACE@("SYMBOL DOCS found in source for : '$symbol' ",length($description), "\n");
4039                     $SourceSymbolDocs{$symbol} = $description;
4040                     $SourceSymbolParams{$symbol} = [ @params ];
4041                     # FIXME $SourceSymbolTypes{$symbol} = "STRUCT,SIGNAL,ARG,FUNCTION,MACRO";
4042                     #if (defined $DeclarationTypes{$symbol}) {
4043                     #    $SourceSymbolTypes{$symbol} = $DeclarationTypes{$symbol}
4044                     #}
4045                     $SourceSymbolSourceFile{$symbol} = $file;
4046                     $SourceSymbolSourceLine{$symbol} = $.;
4047                 }                
4049                 if ($since_desc) {
4050                      ($since_desc, my @extra_lines) = split ("\n", $since_desc);
4051                      $since_desc =~ s/^\s+//;
4052                      $since_desc =~ s/\s+$//;
4053                      @TRACE@("Since($symbol) : [$since_desc]\n");
4054                      $Since{$symbol} = &ConvertSGMLChars ($symbol, $since_desc);
4055                      if(scalar @extra_lines) {
4056                          &LogWarning ($file, $., "multi-line since docs found");
4057                      }
4058                 }
4060                 if ($stability_desc) {
4061                     $stability_desc = &ParseStabilityLevel($stability_desc, $file, $., "Stability level for $symbol");
4062                     $StabilityLevel{$symbol} = &ConvertSGMLChars ($symbol, $stability_desc);
4063                 }
4065                 if ($deprecated_desc) {
4066                     if (!exists $Deprecated{$symbol}) {
4067                          # don't warn for signals and properties
4068                          #if ($symbol !~ m/::?(.*)/) {
4069                          if (defined $DeclarationTypes{$symbol}) {
4070                              &LogWarning ($file, $.,
4071                                  "$symbol is deprecated in the inline comments, but no deprecation guards were found around the declaration.".
4072                                  " (See the --deprecated-guards option for gtkdoc-scan.)");
4073                          }
4074                     }
4075                     $Deprecated{$symbol} = &ConvertSGMLChars ($symbol, $deprecated_desc);
4076                 }
4077             }
4079             $in_comment_block = 0;
4080             next;
4081         }
4083         # Get rid of ' * ' at start of every line in the comment block.
4084         s%^\s*\*\s?%%;
4085         # But make sure we don't get rid of the newline at the end.
4086         if (!$_) {
4087             $_ = "\n";
4088         }
4089         @TRACE@("scanning :$_");
4091         # If we haven't found the symbol name yet, look for it.
4092         if (!$symbol) {
4093             if (m%^\s*(SECTION:\s*\S+)%) {
4094                 $symbol = $1;
4095                 @TRACE@("SECTION DOCS found in source for : '$symbol'\n");
4096             } elsif (m%^\s*([\w:-]*\w)\s*:?\s*(\([-A-Za-z0-9._() ]+?\)\s*)*$%) {
4097                 $symbol = $1;
4098                 my $annotation = $2;
4099                 @TRACE@("SYMBOL DOCS found in source for : '$symbol'\n");
4100                 if (defined($annotation)) {
4101                     chomp($annotation);
4102                     if ($annotation ne "") {
4103                         $SymbolAnnotations{$symbol} = $annotation;
4104                         @TRACE@("remaining text for $symbol: '$annotation'\n");
4105                     }
4106                 }
4107             }
4108             next;
4109         }
4111         if ($in_part eq "description") {
4112             # Get rid of 'Description:'
4113             s%^\s*Description:%%;
4114         }
4116         if (m%^\s*(returns|return\s+value):%i) {
4117             # we're in param section and have not seen the blank line
4118             if($in_part ne "") {
4119               $return_desc = $';
4120               $in_part = "return";
4121               next;
4122             }
4123         } elsif (m%^\s*since:%i) {
4124             # we're in param section and have not seen the blank line
4125             if($in_part ne "param") {
4126               $since_desc = $';
4127               $in_part = "since";
4128               next;
4129             }
4130         } elsif (m%^\s*deprecated:%i) {
4131             # we're in param section and have not seen the blank line
4132             if($in_part ne "param") {
4133               $deprecated_desc = $';
4134               $in_part = "deprecated";
4135               next;
4136             }
4137         } elsif (m%^\s*stability:%i) {
4138             $stability_desc = $';
4139             $in_part = "stability";
4140             next;
4141         }
4143         if ($in_part eq "description") {
4144             $description .= $_;
4145             next;
4146         } elsif ($in_part eq "return") {
4147             $return_desc .= $_;
4148             next;
4149         } elsif ($in_part eq "since") {
4150             $since_desc .= $_;
4151             next;
4152         } elsif ($in_part eq "stability") {
4153             $stability_desc .= $_;
4154             next;
4155         } elsif ($in_part eq "deprecated") {
4156             $deprecated_desc .= $_;
4157             next;
4158         }
4160         # We must be in the parameters. Check for the empty line below them.
4161         if (m%^\s*$%) {
4162             $in_part = "description";
4163             next;
4164         }
4166         # Look for a parameter name.
4167         if (m%^\s*@(\S+)\s*:\s*%) {
4168             my $param_name = $1;
4169             my $param_desc = $';
4171             @TRACE@("Found parameter: $param_name\n");
4172             # Allow varargs variations
4173             if ($param_name =~ m/^\.\.\.$/) {
4174                 $param_name = "...";
4175             }
4176             @TRACE@("Found param for symbol $symbol : '$param_name'= '$_'");
4178             push (@params, $param_name);
4179             push (@params, $param_desc);
4180             $current_param += $PARAM_FIELD_COUNT;
4181             $in_part = "param";
4182             next;
4183         } elsif ($in_part eq "") {
4184             @TRACE@("continuation for $symbol annotation '$_'");
4185             my $annotation = $_;
4186             $annotation =~ s/^\s+|\s+$//g ; 
4187             $SymbolAnnotations{$symbol} .= $annotation;
4188             next;
4189         }
4191         # We must be in the middle of a parameter description, so add it on
4192         # to the last element in @params.
4193         if ($current_param == -1) {
4194             &LogWarning ($file, $., "Parsing comment block file : parameter expected, but got '$_'");
4195         } else {
4196             $params[$#params] .= $_;
4197         }
4198     }
4199     close (SRCFILE);
4202 #############################################################################
4203 # Function    : OutputMissingDocumentation
4204 # Description : Outputs report of documentation coverage to a file
4206 # Arguments   : none
4207 #############################################################################
4209 sub OutputMissingDocumentation {
4210     my $old_undocumented_file = "$ROOT_DIR/$MODULE-undocumented.txt";
4211     my $new_undocumented_file = "$ROOT_DIR/$MODULE-undocumented.new";
4213     my $n_documented = 0;
4214     my $n_incomplete = 0;
4215     my $total = 0;
4216     my $symbol;
4217     my $percent;
4218     my $msg;
4219     my $buffer = "";
4220     my $buffer_deprecated = "";
4221     my $buffer_descriptions = "";
4223     open(UNDOCUMENTED, ">$new_undocumented_file")
4224       || die "Can't create $new_undocumented_file";
4226     foreach $symbol (sort (keys (%AllSymbols))) {
4227         # FIXME: should we print LogWarnings for undocumented stuff?
4228         # DEBUG
4229         #my $ssfile = &GetSymbolSourceFile($symbol);
4230         #my $ssline = &GetSymbolSourceLine($symbol);
4231         #my $location = "defined at " . (defined($ssfile)?$ssfile:"?") . ":" . (defined($ssline)?$ssline:"0") . "\n";
4232         # DEBUG
4233         if ($symbol !~ /:(Title|Long_Description|Short_Description|See_Also|Stability_Level|Include|Section_Id|Image)/) {
4234             $total++;
4235             if (exists ($AllDocumentedSymbols{$symbol})) {
4236                 $n_documented++;
4237                 if (exists ($AllIncompleteSymbols{$symbol})) {
4238                     $n_incomplete++;
4239                     $buffer .= $symbol . " (" . $AllIncompleteSymbols{$symbol} . ")\n";
4240                     #$buffer .= "\t0: ".$location;
4241                 }
4242             } elsif (exists $Deprecated{$symbol}) {
4243                 if (exists ($AllIncompleteSymbols{$symbol})) {
4244                     $n_incomplete++;
4245                     $buffer_deprecated .= $symbol . " (" . $AllIncompleteSymbols{$symbol} . ")\n";
4246                     #$buffer .= "\t1a: ".$location;
4247                 } else {
4248                     $buffer_deprecated .= $symbol . "\n";
4249                     #$buffer .= "\t1b: ".$location;
4250                 }
4251             } else {
4252                 if (exists ($AllIncompleteSymbols{$symbol})) {
4253                     $n_incomplete++;
4254                     $buffer .= $symbol . " (" . $AllIncompleteSymbols{$symbol} . ")\n";
4255                     #$buffer .= "\t2a: ".$location;
4256                 } else {
4257                     $buffer .= $symbol . "\n";
4258                     #$buffer .= "\t2b: ".$location;
4259                 }
4260             }
4261         } elsif ($symbol =~ /:(Long_Description|Short_Description)/) {
4262             $total++;
4263             if (((exists ($SymbolDocs{$symbol})) && (length ($SymbolDocs{$symbol}) > 0))
4264             || ((exists ($AllDocumentedSymbols{$symbol})) && (length ($AllDocumentedSymbols{$symbol}) > 0))) {
4265               $n_documented++;
4266             } else {
4267               # cut off the leading namespace ($TMPL_DIR)
4268               $symbol =~ m/^.*\/(.*)$/;
4269               $buffer_descriptions .= $1 . "\n";
4270             }
4271         }
4272     }
4274     $buffer .= "\n" . $buffer_deprecated . "\n" . $buffer_descriptions;
4276     if ($total == 0) {
4277       $percent = 100;
4278     } else {
4279       $percent = ($n_documented / $total) * 100.0;
4280     }
4282     printf UNDOCUMENTED "%.0f%% symbol docs coverage.\n", $percent;
4283     print UNDOCUMENTED "$n_documented symbols documented.\n";
4284     print UNDOCUMENTED "$n_incomplete symbols incomplete.\n";
4285     print UNDOCUMENTED ($total - $n_documented) . " not documented.\n\n\n";
4287     print UNDOCUMENTED $buffer;
4288     close (UNDOCUMENTED);
4290     return &UpdateFileIfChanged ($old_undocumented_file, $new_undocumented_file, 0);
4292     printf "%.0f%% symbol docs coverage", $percent;
4293     print "($n_documented symbols documented, $n_incomplete symbols incomplete, " . ($total - $n_documented) . " not documented)\n";
4294     print "See $MODULE-undocumented.txt for a list of missing docs.\nThe doc coverage percentage doesn't include intro sections.\n";
4298 #############################################################################
4299 # Function    : OutputUndeclaredSymbols
4300 # Description : Outputs symbols that are listed in the section file, but not
4301 #               declaration is found in the sources
4303 # Arguments   : none
4304 #############################################################################
4306 sub OutputUndeclaredSymbols {
4307     my $old_undeclared_file = "$ROOT_DIR/$MODULE-undeclared.txt";
4308     my $new_undeclared_file = "$ROOT_DIR/$MODULE-undeclared.new";
4310     open(UNDECLARED, ">$new_undeclared_file")
4311         || die "Can't create $new_undeclared_file";
4313     if (%UndeclaredSymbols) {
4314         print UNDECLARED (join("\n", sort keys %UndeclaredSymbols));
4315         print UNDECLARED "\n";
4316         print "See $MODULE-undeclared.txt for the list of undeclared symbols.\n"
4317     }
4318     close(UNDECLARED);
4320     return &UpdateFileIfChanged ($old_undeclared_file, $new_undeclared_file, 0);
4323 #############################################################################
4324 # Function    : OutputUnusedSymbols
4325 # Description : Outputs symbols that are documented in comments, but not
4326 #               declared in the sources
4328 # Arguments   : none
4329 #############################################################################
4331 sub OutputUnusedSymbols {
4332     my $num_unused = 0;
4333     my $old_unused_file = "$ROOT_DIR/$MODULE-unused.txt";
4334     my $new_unused_file = "$ROOT_DIR/$MODULE-unused.new";
4336     open (UNUSED, ">$new_unused_file")
4337         || die "Can't open $new_unused_file";
4338     my ($symbol);
4339     foreach $symbol (sort keys (%Declarations)) {
4340         if (!defined ($DeclarationOutput{$symbol})) {
4341             print (UNUSED "$symbol\n");
4342             $num_unused++;
4343         }
4344     }
4345     foreach $symbol (sort (keys (%AllUnusedSymbols))) {
4346         print (UNUSED "$symbol(" . $AllUnusedSymbols{$symbol} . ")\n");
4347         $num_unused++;
4348     }
4349     close (UNUSED);
4350     if ($num_unused != 0) {
4351         &LogWarning ($old_unused_file, 1, "$num_unused unused declarations.".
4352             "They should be added to $MODULE-sections.txt in the appropriate place.");
4353     }
4355     return &UpdateFileIfChanged ($old_unused_file, $new_unused_file, 0);
4359 #############################################################################
4360 # Function    : OutputAllSymbols
4361 # Description : Outputs list of all symbols to a file
4363 # Arguments   : none
4364 #############################################################################
4366 sub OutputAllSymbols {
4367      my $n_documented = 0;
4368      my $total = 0;
4369      my $symbol;
4370      my $percent;
4371      my $msg;
4373      open (SYMBOLS, ">$ROOT_DIR/$MODULE-symbols.txt")
4374           || die "Can't create $ROOT_DIR/$MODULE-symbols.txt: $!";
4376      foreach $symbol (sort (keys (%AllSymbols))) {
4377           print SYMBOLS $symbol . "\n";
4378      }
4380      close (SYMBOLS);
4383 #############################################################################
4384 # Function    : OutputSymbolsWithoutSince
4385 # Description : Outputs list of all symbols without a since tag to a file
4387 # Arguments   : none
4388 #############################################################################
4390 sub OutputSymbolsWithoutSince {
4391      my $n_documented = 0;
4392      my $total = 0;
4393      my $symbol;
4394      my $percent;
4395      my $msg;
4397      open (SYMBOLS, ">$ROOT_DIR/$MODULE-nosince.txt")
4398           || die "Can't create $ROOT_DIR/$MODULE-nosince.txt: $!";
4400      foreach $symbol (sort (keys (%SourceSymbolDocs))) {
4401          if (!defined $Since{$symbol}) {
4402              print SYMBOLS $symbol . "\n";
4403          }
4404      }
4406      close (SYMBOLS);
4410 #############################################################################
4411 # Function    : MergeSourceDocumentation
4412 # Description : This merges documentation read from a source file into the
4413 #                documentation read in from a template file.
4415 #                Parameter descriptions override any in the template files.
4416 #                Function descriptions are placed before any description from
4417 #                the template files.
4419 # Arguments   : none
4420 #############################################################################
4422 sub MergeSourceDocumentation {
4423     my $symbol;
4424     my @Symbols;
4426     if (scalar %SymbolDocs) {
4427         @Symbols=keys (%SymbolDocs);
4428         @TRACE@("num existing entries: ".(scalar @Symbols)."\n");
4429     }
4430     else {
4431         # filter scanned declarations, with what we suppress from -sections.txt
4432         my %tmp = ();
4433         foreach $symbol (keys (%Declarations)) {
4434             if (defined($KnownSymbols{$symbol}) && $KnownSymbols{$symbol} == 1) {
4435                 $tmp{$symbol}=1;
4436             }
4437         }
4438         # , add the rest from -sections.txt
4439         foreach $symbol (keys (%KnownSymbols)) {
4440             if ($KnownSymbols{$symbol} == 1) {
4441                 $tmp{$symbol}=1;
4442             }
4443         }
4444         # and add whats found in the source
4445         foreach $symbol (keys (%SourceSymbolDocs)) {
4446             $tmp{$symbol}=1;
4447         }
4448         @Symbols = keys (%tmp);
4449         @TRACE@("num source entries: ".(scalar @Symbols)."\n");
4450     }
4451     foreach $symbol (@Symbols) {
4452         $AllSymbols{$symbol} = 1;
4454         my $have_tmpl_docs = 0;
4456         ## see if the symbol is documented in template
4457         my $tmpl_doc = defined ($SymbolDocs{$symbol}) ? $SymbolDocs{$symbol} : "";
4458         my $check_tmpl_doc =$tmpl_doc;
4459         # remove all xml-tags and whitespaces
4460         $check_tmpl_doc =~ s/<.*?>//g;
4461         $check_tmpl_doc =~ s/\s//g;
4462         # anything left ?
4463         if ($check_tmpl_doc ne "") {
4464             $have_tmpl_docs = 1;
4465         } else {
4466             # if the docs have just an empty para, don't merge that.
4467             $check_tmpl_doc = $tmpl_doc;
4468             $check_tmpl_doc =~ s/(\s|\n)//msg;
4469             if ($check_tmpl_doc eq "<para></para>") {
4470                $tmpl_doc = "";
4471             }
4472         }
4474         if (exists ($SourceSymbolDocs{$symbol})) {
4475             my $type = $DeclarationTypes {$symbol};
4477             @TRACE@("merging [$symbol] from source\n");
4479             my $item = "Parameter";
4480             if (defined ($type)) {
4481                 if ($type eq 'STRUCT') {
4482                     $item = "Field";
4483                 } elsif ($type eq 'ENUM') {
4484                     $item = "Value";
4485                 } elsif ($type eq 'UNION') {
4486                     $item = "Field";
4487                 }
4488             } else {
4489                 $type="SIGNAL";
4490             }
4492             my $src_doc = $SourceSymbolDocs{$symbol};
4493             # remove leading and training whitespaces
4494             $src_doc =~ s/^\s+//;
4495             $src_doc =~ s/\s+$//;
4497             # Don't output warnings for overridden titles as titles are
4498             # automatically generated in the -sections.txt file, and thus they
4499             # are often overridden.
4500             if ($have_tmpl_docs && $symbol !~ m/:Title$/) {
4501                 # check if content is different
4502                 if ($tmpl_doc ne $src_doc) {
4503                     #print "[$tmpl_doc] [$src_doc]\n";
4504                     &LogWarning ($SourceSymbolSourceFile{$symbol}, $SourceSymbolSourceLine{$symbol},
4505                         "Documentation in template ".$SymbolSourceFile{$symbol}.":".$SymbolSourceLine{$symbol}." for $symbol being overridden by inline comments.");
4506                 }
4507             }
4509             if ($src_doc ne "") {
4510                  $AllDocumentedSymbols{$symbol} = 1;
4511             }
4513             # Do not add <para> to nothing, it breaks missing docs checks.
4514             my $src_doc_para = "";
4515             if ($src_doc ne "") {
4516                 $src_doc_para = $src_doc;
4517             }
4519             if ($symbol =~ m/$TMPL_DIR\/.+:Long_Description/) {
4520                 $SymbolDocs{$symbol} = "$src_doc_para$tmpl_doc";
4521             } elsif ($symbol =~ m/$TMPL_DIR\/.+:.+/) {
4522                 # For the title/summary/see also section docs we don't want to
4523                 # add any <para> tags.
4524                 $SymbolDocs{$symbol} = "$src_doc"
4525             } else {
4526                 $SymbolDocs{$symbol} = "$src_doc_para$tmpl_doc";
4527             }
4529             # merge parameters
4530             if ($symbol =~ m/.*::.*/) {
4531                 # For signals we prefer the param names from the source docs,
4532                 # since the ones from the templates are likely to contain the
4533                 # artificial argn names which are generated by gtkdoc-scangobj.
4534                 $SymbolParams{$symbol} = $SourceSymbolParams{$symbol};
4535                 # FIXME: we need to check for empty docs here as well!
4536             } else {
4537                 # The templates contain the definitive parameter names and order,
4538                 # so we will not change that. We only override the actual text.
4539                 my $tmpl_params = $SymbolParams{$symbol};
4540                 if (!defined ($tmpl_params)) {
4541                     @TRACE@("No merge needed for $symbol\n");
4542                     $SymbolParams{$symbol} = $SourceSymbolParams{$symbol};
4543                     #  FIXME: we still like to get the number of params and merge
4544                     #  1) we would noticed that params have been removed/renamed
4545                     #  2) we would catch undocumented params
4546                     #  params are not (yet) exported in -decl.txt so that we
4547                     #  could easily grab them :/
4548                 } else {
4549                     my $params = $SourceSymbolParams{$symbol};
4550                     my $j;
4551                     @TRACE@("Merge needed for $symbol, tmpl_params: ",$#$tmpl_params,", source_params: ",$#$params," \n");
4552                     for ($j = 0; $j <= $#$tmpl_params; $j += $PARAM_FIELD_COUNT) {
4553                         my $tmpl_param_name = $$tmpl_params[$j];
4555                         # Try to find the param in the source comment documentation.
4556                         my $found = 0;
4557                         my $k;
4558                         @TRACE@("  try merge param $tmpl_param_name\n");
4559                         for ($k = 0; $k <= $#$params; $k += $PARAM_FIELD_COUNT) {
4560                             my $param_name = $$params[$k];
4561                             my $param_desc = $$params[$k + 1];
4563                             @TRACE@("    test param  $param_name\n");
4564                             # We accept changes in case, since the Gnome source
4565                             # docs contain a lot of these.
4566                             if ("\L$param_name" eq "\L$tmpl_param_name") {
4567                                 $found = 1;
4569                                 # Override the description.
4570                                 $$tmpl_params[$j + 1] = $param_desc;
4572                                 # Set the name to "" to mark it as used.
4573                                 $$params[$k] = "";
4574                                 last;
4575                             }
4576                         }
4578                         # If it looks like the parameters are there, but not
4579                         # in the right place, try to explain a bit better.
4580                         if ((!$found) && ($src_doc =~ m/\@$tmpl_param_name:/)) {
4581                             &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4582                                 "Parameters for $symbol must start on the line immediately after the function or macro name.");
4583                         }
4584                     }
4586                     # Now we output a warning if parameters have been described which
4587                     # do not exist.
4588                     for ($j = 0; $j <= $#$params; $j += $PARAM_FIELD_COUNT) {
4589                         my $param_name = $$params[$j];
4590                         if ($param_name) {
4591                             # the template builder cannot detect if a macro returns
4592                             # a result or not
4593                             if(($type eq "MACRO") && ($param_name eq "Returns")) {
4594                                 # FIXME: do we need to add it then to tmpl_params[] ?
4595                                 my $num=$#$tmpl_params;
4596                                 @TRACE@("  adding Returns: to macro docs for $symbol.\n");
4597                                 $$tmpl_params[$num+1]="Returns";
4598                                 $$tmpl_params[$num+2]=$$params[$j+1];
4599                                 next;
4600                             }
4601                             &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4602                                 "$item described in source code comment block but does not exist. $type: $symbol $item: $param_name.");
4603                         }
4604                     }
4605                 }
4606             }
4607         } else {
4608             if ($have_tmpl_docs) {
4609                 $AllDocumentedSymbols{$symbol} = 1;
4610                 @TRACE@("merging [$symbol] from template\n");
4611             }
4612             else {
4613                 @TRACE@("[$symbol] undocumented\n");
4614             }
4615         }
4617         # if this symbol is documented, check if docs are complete
4618         $check_tmpl_doc = defined ($SymbolDocs{$symbol}) ? $SymbolDocs{$symbol} : "";
4619         # remove all xml-tags and whitespaces
4620         $check_tmpl_doc =~ s/<.*?>//g;
4621         $check_tmpl_doc =~ s/\s//g;
4622         if ($check_tmpl_doc ne "") {
4623             my $tmpl_params = $SymbolParams{$symbol};
4624             if (defined ($tmpl_params)) {
4625                 my $type = $DeclarationTypes {$symbol};
4627                 my $item = "Parameter";
4628                 if (defined ($type)) {
4629                     if ($type eq 'STRUCT') {
4630                         $item = "Field";
4631                     } elsif ($type eq 'ENUM') {
4632                         $item = "Value";
4633                     } elsif ($type eq 'UNION') {
4634                         $item = "Field";
4635                     }
4636                 } else {
4637                     $type="SIGNAL";
4638                 }
4640                 @TRACE@("Check param docs for $symbol, tmpl_params: ",$#$tmpl_params," entries, type=$type\n");
4642                 if ($#$tmpl_params > 0) {
4643                     my $j;
4644                     for ($j = 0; $j <= $#$tmpl_params; $j += $PARAM_FIELD_COUNT) {
4645                         # Output a warning if the parameter is empty and
4646                         # remember for stats.
4647                         my $tmpl_param_name = $$tmpl_params[$j];
4648                         my $tmpl_param_desc = $$tmpl_params[$j + 1];
4649                         if ($tmpl_param_name ne "void" && $tmpl_param_desc !~ m/\S/) {
4650                             if (exists ($AllIncompleteSymbols{$symbol})) {
4651                                 $AllIncompleteSymbols{$symbol}.=", ".$tmpl_param_name;
4652                             } else {
4653                                 $AllIncompleteSymbols{$symbol}=$tmpl_param_name;
4654                             }
4655                             &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4656                                 "$item description for $symbol"."::"."$tmpl_param_name is missing in source code comment block.");
4657                         }
4658                     }
4659                 }
4660                 else {
4661                     if ($#$tmpl_params == 0) {
4662                         $AllIncompleteSymbols{$symbol}="<items>";
4663                         &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4664                             "$item descriptions for $symbol are missing in source code comment block.");
4665                     }
4666                     # $#$tmpl_params==-1 means we don't know about parameters
4667                     # this unfortunately does not tell if there should be some
4668                 }
4669             }
4670         }
4671    }
4672    @TRACE@("num doc entries: ".(scalar %SymbolDocs)."\n");
4675 #############################################################################
4676 # Function    : IsEmptyDoc
4677 # Description : Check if a doc-string is empty. Its also regarded as empty if
4678 #               it only consist of whitespace or e.g. FIXME.
4679 # Arguments   : the doc-string
4680 #############################################################################
4681 sub IsEmptyDoc {
4682     my ($doc) = @_;
4684     if ($doc =~ /^\s*$/) {
4685         return 1;
4686     }
4688     if ($doc =~ /^\s*<para>\s*(FIXME)?\s*<\/para>\s*$/) {
4689         return 1;
4690     }
4692     return 0;
4695 #############################################################################
4696 # Function    : ConvertMarkDown
4697 # Description : Converts mark down syntax to the respective docbook.
4698 #               http://de.wikipedia.org/wiki/Markdown
4699 #               Inspired by the design of ParseDown
4700 #               http://parsedown.org/
4701 #               Copyright (c) 2013 Emanuil Rusev, erusev.com
4702 # Arguments   : the symbol name, the doc-string
4703 #############################################################################
4705 sub ConvertMarkDown {
4706     my ($symbol, $text) = @_;
4708     $text = &MarkDownParse ($text, $symbol);
4710     return $text
4713 # SUPPORTED MARKDOWN
4714 # ==================
4716 # Atx-style Headers
4717 # -----------------
4719 # # Header 1
4721 # ## Header 2 ##
4723 # Setext-style Headers
4724 # --------------------
4726 # Header 1
4727 # ========
4729 # Header 2
4730 # --------
4732 # Ordered (unnested) Lists
4733 # ------------------------
4735 # 1. item 1
4737 # 1. item 2 with loooong
4738 #    description
4740 # 3. item 3
4742 # Note: we require a blank line above the list items
4745 # TODO(ensonic): it would be nice to add id parameters to the refsect2 elements
4747 sub MarkDownParseBlocks {
4748   my ($linesref, $symbol, $context) = @_;
4749   my $line;
4750   my @md_blocks = ();
4751   my $md_block = { type => "" };
4753  OUTER: foreach $line (@$linesref) {
4754     my $first_char = substr ($line, 0, 1);
4755     my $deindented_line;
4757     @TRACE@("in '".$md_block->{"type"}."' state, parsing '$line'");
4759     if ($md_block->{"type"} eq "markup") {
4760       if (!$md_block->{"closed"}) {
4761         if (index ($line, $md_block->{"start"}) != -1) {
4762           $md_block->{"depth"}++;
4763         }
4764         if (index ($line, $md_block->{"end"}) != -1) {
4765           if ($md_block->{"depth"} > 0) {
4766             $md_block->{"depth"}--;
4767           } else {
4768             @TRACE@("closing tag '$line'");
4769             $md_block->{"closed"} = 1;
4770             # TODO(ensonic): reparse inner text with MarkDownParseLines?
4771           }
4772         }
4773         $md_block->{"text"} .= "\n" . $line;
4774         @TRACE@("add to markup");
4775         next OUTER;
4776       }
4777     }
4779     $deindented_line = $line;
4780     $deindented_line =~ s/^\s+//;
4782     if ($md_block->{"type"} eq "heading") {
4783       # a heading is ended by any level less than or equal
4784       if ($md_block->{"level"} == 1) {
4785         if ($line =~ /^={4,}[ \t]*$/) {
4786           my $text = pop @{$md_block->{"lines"}};
4787           $md_block->{"interrupted"} = 0;
4788           push @md_blocks, $md_block;
4790           $md_block = { type => "heading",
4791                         text => $text,
4792                         lines => [],
4793                         level => 1 };
4794           next OUTER;
4795         } elsif ($line =~ /^[#][ \t]+(.+?)[ \t]*[#]*[ \t]*(?:{#([^}]+)})?[ \t]*$/) {
4796           $md_block->{"interrupted"} = 0;
4797           push @md_blocks, $md_block;
4799           $md_block = { type => "heading",
4800                         text => $1,
4801                         id => $2,
4802                         lines => [],
4803                         level => 1 };
4804           next OUTER;
4805         } else {
4806           # push lines into the block until the end is reached
4807           push @{$md_block->{"lines"}}, $line;
4808           next OUTER;
4809         }
4810       } else {
4811         if ($line =~ /^[=]{4,}[ \t]*$/) {
4812           my $text = pop @{$md_block->{"lines"}};
4813           $md_block->{"interrupted"} = 0;
4814           push @md_blocks, $md_block;
4816           $md_block = { type => "heading",
4817                         text => $text,
4818                         lines => [],
4819                         level => 1 };
4820           next OUTER;
4821         } elsif ($line =~ /^[-]{4,}[ \t]*$/) {
4822           my $text = pop @{$md_block->{"lines"}};
4823           $md_block->{"interrupted"} = 0;
4824           push @md_blocks, $md_block;
4826           $md_block = { type => "heading",
4827                         text => $text,
4828                         lines => [],
4829                         level => 2 };
4830           next OUTER;
4831         } elsif ($line =~ /^([#]{1,2})[ \t]+(.+?)[ \t]*[#]*[ \t]*(?:{#([^}]+)})?[ \t]*$/) {
4832           $md_block->{"interrupted"} = 0;
4833           push @md_blocks, $md_block;
4835           $md_block = { type => "heading",
4836                         text => $2,
4837                         id => $3,
4838                         lines => [],
4839                         level => length($1) };
4840           next OUTER;
4841         } else {
4842           # push lines into the block until the end is reached
4843           push @{$md_block->{"lines"}}, $line;
4844           next OUTER;
4845         }
4846       }
4847     } elsif ($md_block->{"type"} eq "code") {
4848       if ($line =~ /^[ \t]*\]\|(.*)/) {
4849         push @md_blocks, $md_block;
4850         $md_block = { type => "paragraph",
4851                       text => "$1",
4852                       lines => [] };
4853       } else {
4854         push @{$md_block->{"lines"}}, $line;
4855       }
4856       next OUTER;
4857     }
4859     if ($deindented_line eq "") {
4860       $md_block->{"interrupted"} = 1;
4861       next;
4862     }
4864     if ($md_block->{"type"} eq "quote") {
4865       if (!$md_block->{"interrupted"}) {
4866         $line =~ s/^[ ]*>[ ]?//;
4867         push @{$md_block->{"lines"}}, $line;
4868         next OUTER;
4869       }
4870     } elsif ($md_block->{"type"} eq "li") {
4871       my $marker = $md_block->{"marker"};
4872       if ($line =~ /^([ ]{0,3})($marker)[ ](.*)/) {
4873         my $indentation = $1;
4874         if ($md_block->{"indentation"} ne $indentation) {
4875           push @{$md_block->{"lines"}}, $line;
4876         } else {
4877           my $lines = $3;
4878           my $ordered = $md_block->{"ordered"};
4879           $lines =~ s/^[ ]{0,4}//;
4880           $md_block->{"last"} = 0;
4881           push @md_blocks, $md_block;
4882           $md_block = { type => "li",
4883                         ordered => $ordered,
4884                         indentation => $indentation,
4885                         marker => $marker,
4886                         first => 0,
4887                         last => 1,
4888                         lines => [ $lines ] };
4889         }
4890         next OUTER;
4891       }
4893       if ($md_block->{"interrupted"}) {
4894         if ($first_char eq " ") {
4895           push @{$md_block->{"lines"}}, "";
4896           $line =~ s/^[ ]{0,4}//;
4897           push @{$md_block->{"lines"}}, $line;
4898           $md_block->{"interrupted"} = 0;
4899           next OUTER;
4900         }
4901       } else {
4902         $line =~ s/^[ ]{0,4}//;
4903         push @{$md_block->{"lines"}}, $line;
4904         next OUTER;
4905       }
4906     }
4908     # indentation sensitive types
4909     @TRACE@("parsing '$line'");
4911     if ($line =~ /^([#]{1,2})[ \t]+(.+?)[ \t]*[#]*[ \t]*(?:{#([^}]+)})?[ \t]*$/) {
4912       # atx heading (#)
4913       push @md_blocks, $md_block;
4915       $md_block = { type => "heading",
4916                     text => $2,
4917                     id => $3,
4918                     lines => [],
4919                     level => length($1) };
4921       next OUTER;
4922     } elsif ($line =~ /^={4,}[ \t]*$/) {
4923       # setext heading (====)
4925       if ($md_block->{"type"} eq "paragraph" && $md_block->{"interrupted"}) {
4926         push @md_blocks, $md_block;
4927         $md_block->{"type"} = "heading";
4928         $md_block->{"lines"} = [];
4929         $md_block->{"level"} = 1;
4930       }
4932       next OUTER;
4933     } elsif ($line =~ /^-{4,}[ \t]*$/) {
4934       # setext heading (-----)
4936       if ($md_block->{"type"} eq "paragraph" && $md_block->{"interrupted"}) {
4937         push @md_blocks, $md_block;
4938         $md_block->{"type"} = "heading";
4939         $md_block->{"lines"} = [];
4940         $md_block->{"level"} = 2;
4941       }
4943       next OUTER;
4944     } elsif ($line =~ /^[ \t]*\|\[[ ]*(?:<!-- language="([^"]+?)" -->)?/) {
4945       # code
4946       $md_block->{"interrupted"} = 1;
4947       push @md_blocks, $md_block;
4948       $md_block = { type => "code",
4949                     language => $1,
4950                     lines => [] };
4951       next OUTER;
4952     }
4954     # indentation insensitive types
4955     if ($line =~ /^[ ]*<!DOCTYPE/) {
4956       push @md_blocks, $md_block;
4958       $md_block = { type   => "markup",
4959                     text   => $deindented_line,
4960                     start  => "<",
4961                     end    => ">",
4962                     closed => 0,
4963                     depth  => 0 };
4965     } elsif ($line =~ /^[ ]*<\??(\w+)[^>]*([\/\?])?[ \t]*>/) {
4966       # markup, including <?xml version="1.0"?>
4967       my $tag = $1;
4968       my $is_self_closing = defined($2);
4969       
4970       # skip link markdown
4971       # TODO(ensonic): consider adding more uri schemes (ftp, ...)
4972       if ($tag =~ /^https?/) {
4973         @TRACE@("skipping link '$tag'");
4974       } else {
4975         # for TEXT_LEVEL_ELEMENTS, we want to keep them as-is in the paragraph
4976         # instead of creation a markdown block.
4977         my $scanning_for_end_of_text_level_tag = (
4978             $md_block->{"type"} eq "paragraph" && 
4979             defined($md_block->{"start"}) &&
4980             !$md_block->{"closed"}); 
4981         @TRACE@("markup found '$tag', scanning $scanning_for_end_of_text_level_tag ?");
4982         if (!$MD_TEXT_LEVEL_ELEMENTS{$tag} && !$scanning_for_end_of_text_level_tag) {
4983           push @md_blocks, $md_block;
4984   
4985           if ($is_self_closing) {
4986             @TRACE@("self-closing docbook '$tag'");
4987             $md_block = { type => "self-closing tag",
4988                           text => $deindented_line };
4989             $is_self_closing = 0;
4990             next OUTER;
4991           }
4992   
4993           @TRACE@("new markup '$tag'");
4994           $md_block = { type   => "markup",
4995                         text   => $deindented_line,
4996                         start  => "<" . $tag . ">",
4997                         end    => "</" . $tag . ">",
4998                         closed => 0,
4999                         depth  => 0 };
5000           if ($deindented_line =~ /<\/$tag>/) {
5001             $md_block->{"closed"} = 1;
5002           }
5003           next OUTER;
5004         } else {
5005           if ($MD_TEXT_LEVEL_ELEMENTS{$tag}) {
5006             @TRACE@("text level docbook '$tag' in '".$md_block->{"type"}."' state");
5007             # TODO(ensonic): handle nesting
5008             if (!$scanning_for_end_of_text_level_tag) {
5009               if ($deindented_line !~ /<\/$tag>/) {
5010                 @TRACE@("new text level markup '$tag'");
5011                 $md_block->{"start"} = "<" . $tag . ">";
5012                 $md_block->{"end"} = "</" . $tag . ">";
5013                 $md_block->{"closed"} = 0;
5014                 @TRACE@("scanning for end of '$tag'");
5015               }
5016             } else {
5017               if ($deindented_line =~ /$md_block->{"end"}/) {
5018                 $md_block->{"closed"} = 1;
5019                 @TRACE@("found end of '$tag'");
5020               }
5021             }
5022           }
5023         }
5024       }
5025     } elsif ($line =~ /^([ ]*)[*+-][ ](.*)/) {
5026       # li
5027       push @md_blocks, $md_block;
5028       my $lines = $2;
5029       my $indentation = $1;
5030       $lines =~ s/^[ ]{0,4}//;
5031       $md_block = { type => "li",
5032                     ordered => 0,
5033                     indentation => $indentation,
5034                     marker => "[*+-]",
5035                     first => 1,
5036                     last => 1,
5037                     lines => [ $lines ] };
5038       next OUTER;
5039     } elsif ($line =~ /^[ ]*>[ ]?(.*)/) {
5040       push @md_blocks, $md_block;
5041       $md_block = { type => "quote",
5042                     lines => [ $1 ] };
5043       next OUTER;
5044     }
5046     # list item
5047     if ($line =~ /^([ ]{0,4})\d+[.][ ]+(.*)/) {
5048       push @md_blocks, $md_block;
5049       my $lines = $2;
5050       my $indentation = $1;
5051       $lines =~ s/^[ ]{0,4}//;
5053       $md_block = { type => "li",
5054                     ordered => 1,
5055                     indentation => $indentation,
5056                     marker => "\\d+[.]",
5057                     first => 1,
5058                     last => 1,
5059                     lines => [ $lines ] };
5061       next;
5062     }
5064     # paragraph
5065     if ($md_block->{"type"} eq "paragraph") {
5066       if ($md_block->{"interrupted"}) {
5067         push @md_blocks, $md_block;
5068         $md_block = { type => "paragraph",
5069                       interrupted => 0,
5070                       text => $line };
5071         @TRACE@("new paragraph due to interrupted");
5072       } else {
5073         $md_block->{"text"} .= "\n" . $line;
5074         @TRACE@("add to paragraph");
5075       }
5076     } else {
5077       push @md_blocks, $md_block;
5078       $md_block = { type => "paragraph",
5079                     text => $line };
5080       @TRACE@("new paragraph due to different block type");
5081     }
5082   }
5084   push @md_blocks, $md_block;
5086   shift @md_blocks;
5088   return @md_blocks;
5091 sub MarkDownParseSpanElementsInner {
5092   my ($text, $markersref) = @_;
5093   my $markup = "";
5094   my %markers = map { $_ => 1 } @$markersref;
5096   while ($text ne "") {
5097     my $closest_marker = "";
5098     my $closest_marker_index = 0;
5099     my $closest_marker_position = -1;
5100     my $text_marker = "";
5101     my $i = 0;
5102     my $offset = 0;
5103     my @markers_rest;
5104     my $marker;
5105     my $use;
5107     while ( ($marker, $use) = each %markers ) {
5108       my $marker_position;
5110       if (!$use) {
5111         next;
5112       }
5114       $marker_position = index ($text, $marker);
5116       if ($marker_position < 0) {
5117         $markers{$marker} = 0;
5118         next;
5119       }
5121       if ($closest_marker eq "" || $marker_position < $closest_marker_position) {
5122         $closest_marker = $marker;
5123         $closest_marker_index = $i;
5124         $closest_marker_position = $marker_position;
5125       }
5126     }
5128     if ($closest_marker_position >= 0) {
5129       $text_marker = substr ($text, $closest_marker_position);
5130     }
5132     if ($text_marker eq "") {
5133       $markup .= $text;
5134       $text = "";
5135       next; # last
5136     }
5138     $markup .= substr ($text, 0, $closest_marker_position);
5139     $text = substr ($text, $closest_marker_position);
5140     @markers_rest = map { $markers{$_} ? ($_ eq $closest_marker ? () : $_) : () } keys %markers;
5142     if ($closest_marker eq "![" || $closest_marker eq "[") {
5143       my %element;
5145       if (index ($text, "]") && $text =~ /\[((?:[^][]|(?R))*)\]/) {
5146         my $remaining_text;
5148         %element = ( "!" => (substr ($text, 0, 1) eq "!"),
5149                      "a" => $1 );
5151         $offset = length ($&);
5152         if ($element{"!"}) {
5153           $offset++;
5154         }
5156         $remaining_text = substr ($text, $offset);
5157         if ($remaining_text =~ /^\([ ]*([^)'"]*?)(?:[ ]+['"](.+?)['"])?[ ]*\)/) {
5158           $element{"»"} = $1;
5159           if (defined ($2)) {
5160             $element{"#"} = $2;
5161           }
5162           $offset += length ($&);
5163         } elsif ($remaining_text =~ /^\s*\[([^\]<]*?)\]/) {
5164           $element{"ref"} = $1;
5165           $offset += length ($&);
5166         } else {
5167           undef %element;
5168         }
5169       }
5171       if (%element) {
5172         if ($element{"»"}) {
5173           $element{"»"} =~ s/&/&amp;/g;
5174           $element{"»"} =~ s/</&lt;/g;
5175         }
5176         if ($element{"!"}) {
5177           $markup .= "<inlinemediaobject><imageobject><imagedata fileref=\"" . $element{"»"} . "\"></imagedata></imageobject>";
5179           if (defined ($element{"a"})) {
5180             $markup .= "<textobject><phrase>" . $element{"a"} . "</phrase></textobject>";
5181           }
5183           $markup .= "</inlinemediaobject>";
5184         } elsif ($element{"ref"}) {
5185           $element{"a"} = &MarkDownParseSpanElementsInner ($element{"a"}, \@markers_rest);
5186           $markup .= "<link linkend=\"" . $element{"ref"} . "\"";
5188           if (defined ($element{"#"})) {
5189             # title attribute not supported
5190           }
5192           $markup .= ">" . $element{"a"} . "</link>";
5193         } else {
5194           $element{"a"} = &MarkDownParseSpanElementsInner ($element{"a"}, \@markers_rest);
5195           $markup .= "<ulink url=\"" . $element{"»"} . "\"";
5197           if (defined ($element{"#"})) {
5198             # title attribute not supported
5199           }
5201           $markup .= ">" . $element{"a"} . "</ulink>";
5202         }
5203       } else {
5204         $markup .= $closest_marker;
5205         if ($closest_marker eq "![") {
5206           $offset = 2;
5207         } else {
5208           $offset = 1;
5209         }
5210       }
5211     } elsif ($closest_marker eq "<") {
5212       if ($text =~ /^<(https?:[\/]{2}[^\s]+?)>/i) {
5213         my $element_url = $1;
5214         $element_url =~ s/&/&amp;/g;
5215         $element_url =~ s/</&lt;/g;
5217         $markup .= "<ulink url=\"" . $element_url . "\">" . $element_url . "</ulink>";
5218         $offset = length ($&);
5219       } elsif ($text =~ /^<([A-Za-z0-9._-]+?@[A-Za-z0-9._-]+?)>/) {
5220         $markup .= "<ulink url=\"mailto:" . $1 . "\">" . $1 . "</ulink>";
5221         $offset = length ($&);
5222       } elsif ($text =~ /^<[^>]+?>/) {
5223         $markup .= $&;
5224         $offset = length ($&);
5225       } else {
5226         $markup .= "&lt;";
5227         $offset = 1;
5228       }
5229     } elsif ($closest_marker eq "\\") {
5230       my $special_char = substr ($text, 1, 1);
5231       if ($MD_ESCAPABLE_CHARS{$special_char} ||
5232           $MD_GTK_ESCAPABLE_CHARS{$special_char}) {
5233         $markup .= $special_char;
5234         $offset = 2;
5235       } else {
5236         $markup .= "\\";
5237         $offset = 1;
5238       }
5239     } elsif ($closest_marker eq "`") {
5240       if ($text =~ /^(`+)([^`]+?)\1(?!`)/) {
5241         my $element_text = $2;
5242         $markup .= "<literal>" . $element_text . "</literal>";
5243         $offset = length ($&);
5244       } else {
5245         $markup .= "`";
5246         $offset = 1;
5247       }
5248     } elsif ($closest_marker eq "@") {
5249       # Convert '@param()'
5250       # FIXME: we could make those also links ($symbol.$2), but that would be less
5251       # useful as the link target is a few lines up or down
5252       if ($text =~ /^(\A|[^\\])\@(\w+((\.|->)\w+)*)\s*\(\)/) {
5253         $markup .= $1 . "<parameter>" . $2 . "()</parameter>\n";
5254         $offset = length ($&);
5255       } elsif ($text =~ /^(\A|[^\\])\@(\w+((\.|->)\w+)*)/) {
5256         # Convert '@param', but not '\@param'.
5257         $markup .= $1 . "<parameter>" . $2 . "</parameter>\n";
5258         $offset = length ($&);
5259       } elsif ($text =~ /^\\\@/) {
5260         $markup .= "\@";
5261         $offset = length ($&);
5262       } else {
5263         $markup .= "@";
5264         $offset = 1;
5265       }
5266     } elsif ($closest_marker eq "#") {
5267       if ($text =~ /^(\A|[^\\])#([\w\-:\.]+[\w]+)\s*\(\)/) {
5268         # handle #Object.func()
5269         $markup .= $1 . &MakeXRef ($2, &tagify ($2 . "()", "function"));
5270         $offset = length ($&);
5271       } elsif ($text =~ /^(\A|[^\\])#([\w\-:\.]+[\w]+)/) {
5272         # Convert '#symbol', but not '\#symbol'.
5273         $markup .= $1 . &MakeHashXRef ($2, "type");
5274         $offset = length ($&);
5275       } elsif ($text =~ /^\\#/) {
5276         $markup .= "#";
5277         $offset = length ($&);
5278       } else {
5279         $markup .= "#";
5280         $offset = 1;
5281       }
5282     } elsif ($closest_marker eq "%") {
5283       if ($text =~ /^(\A|[^\\])\%(-?\w+)/) {
5284         # Convert '%constant', but not '\%constant'.
5285         # Also allow negative numbers, e.g. %-1.
5286         $markup .= $1 . &MakeXRef ($2, &tagify ($2, "literal"));
5287         $offset = length ($&);
5288       } elsif ($text =~ /^\\%/) {
5289         $markup .= "\%";
5290         $offset = length ($&);
5291       } else {
5292         $markup .= "%";
5293         $offset = 1;
5294       }
5295     }
5297     if ($offset > 0) {
5298       $text = substr ($text, $offset);
5299     }
5300   }
5302   return $markup;
5305 sub MarkDownParseSpanElements {
5306   my ($text) = @_;
5307   my @markers = ( "\\", "<", "![", "[", "`", "%", "#", "@" );
5309   $text = &MarkDownParseSpanElementsInner ($text, \@markers);
5311   # Convert 'function()' or 'macro()'.
5312   # if there is abc_*_def() we don't want to make a link to _def()
5313   # FIXME: also handle abc(def(....)) : but that would need to be done recursively :/
5314   $text =~ s/([^\*.\w])(\w+)\s*\(\)/$1.&MakeXRef($2, &tagify($2 . "()", "function"));/eg;
5316   return $text;
5319 sub ReplaceEntities {
5320   my ($text, $symbol) = @_;
5321   my $warn = "";
5322   my @entities = ( [ "&lt;", "<" ],
5323                    [ "&gt;", ">" ],
5324                    [ "&ast;", "*" ],
5325                    [ "&num;", "#" ],
5326                    [ "&percnt;", "%"],
5327                    [ "&colon;", ":" ],
5328                    [ "&quot;", "\"" ],
5329                    [ "&apos;", "'" ],
5330                    [ "&nbsp;", " " ],
5331                    [ "&amp;", "&" ] ); # Do this last, or the others get messed up.
5332   my $i;
5334   # Expand entities in <programlisting> even inside CDATA since
5335   # we changed the definition of |[ to add CDATA
5336   for ($i = 0; $i <= $#entities; $i++) {
5337     $text =~ s/$entities[$i][0]/$entities[$i][1]/g;
5338   }
5340   return $text;
5343 sub MarkDownOutputDocBook {
5344   my ($blocksref, $symbol, $context) = @_;
5345   my $output = "";
5346   my $block;
5347   my @blocks = @$blocksref;
5349   foreach $block (@blocks) {
5350     my $text;
5351     my $title;
5353     #$output .= "\n<!-- beg type='" . $block->{"type"} . "'-->\n";
5355     if ($block->{"type"} eq "paragraph") {
5356       $text = &MarkDownParseSpanElements ($block->{"text"});
5357       if ($context eq "li" && $output eq "") {
5358         if ($block->{"interrupted"}) {
5359           $output .= "\n<para>$text</para>\n";
5360         } else {
5361           $output .= "<para>$text</para>";
5362           if ($#blocks > 0) {
5363             $output .= "\n";
5364           }
5365         }
5366       } else {
5367         $output .= "<para>$text</para>\n";
5368       }
5370     } elsif ($block->{"type"} eq "heading") {
5371       my $tag;
5373       $title = &MarkDownParseSpanElements ($block->{"text"});
5375       if ($block->{"level"} == 1) {
5376         $tag = "refsect2";
5377       } else {
5378         $tag = "refsect3";
5379       }
5381       $text = &MarkDownParseLines ($block->{"lines"}, $symbol, "heading");
5382       if (defined ($block->{"id"})) {
5383         $output .= "<$tag id=\"" . $block->{"id"} . "\">";
5384       } else {
5385         $output .= "<$tag>";
5386       }
5388       $output .= "<title>$title</title>$text</$tag>\n";
5389     } elsif ($block->{"type"} eq "li") {
5390       my $tag = "itemizedlist";
5392       if ($block->{"first"}) {
5393         if ($block->{"ordered"}) {
5394           $tag = "orderedlist";
5395         }
5396         $output .= "<$tag>\n";
5397       }
5399       if ($block->{"interrupted"}) {
5400         push @{$block->{"lines"}}, "";
5401       }
5403       $text = &MarkDownParseLines ($block->{"lines"}, $symbol, "li");
5404       $output .= "<listitem>".$text."</listitem>\n";
5405       if ($block->{"last"}) {
5406         if ($block->{"ordered"}) {
5407           $tag = "orderedlist";
5408         }
5409         $output .= "</$tag>\n";
5410       }
5411     } elsif ($block->{"type"} eq "quote") {
5412       $text = &MarkDownParseLines ($block->{"lines"}, $symbol, "quote");
5413       $output .= "<blockquote>\n$text</blockquote>\n";
5414     } elsif ($block->{"type"} eq "code") {
5415       if ($block->{"language"}) {
5416         $output .= "<informalexample><programlisting language=\"" . $block->{"language"} . "\"><![CDATA[\n";
5417       } else {
5418         $output .= "<informalexample><programlisting><![CDATA[\n";
5419       }
5420       foreach (@{$block->{"lines"}}) {
5421         $output .= &ReplaceEntities ($_, $symbol) . "\n";
5422       }
5423       $output .= "]]></programlisting></informalexample>\n";
5424     } elsif ($block->{"type"} eq "markup") {
5425       $text = &ExpandAbbreviations($symbol, $block->{"text"});
5426       $output .= $text."\n";
5427     } else {
5428       $output .= $block->{"text"}."\n";
5429     }
5430     #$output .= "\n<!-- end type='" . $block->{"type"} . "'-->\n";
5431   }
5433   return $output;
5436 sub MarkDownParseLines {
5437   my ($linesref, $symbol, $context) = @_;
5438   my $output;
5439   my @lines = @$linesref;
5440   my @blocks;
5442   @blocks = &MarkDownParseBlocks (\@lines, $symbol, $context);
5443   $output = &MarkDownOutputDocBook (\@blocks, $symbol, $context);
5445   return $output;
5448 sub MarkDownParse {
5449   my ($text, $symbol) = @_;
5450   my @lines;
5452   # take out some variability in line endings
5453   $text =~ s%\r\n%\n%g;
5454   $text =~ s%\r%\n%g;
5456   # split lines
5457   @lines = split("\n", $text);
5458   $text = MarkDownParseLines(\@lines, $symbol, "");
5460   return $text;
5463 #############################################################################
5464 # LIBRARY FUNCTIONS -        These functions are used in both gtkdoc-mkdb and
5465 #                        gtkdoc-mktmpl and should eventually be moved to a
5466 #                        separate library.
5467 #############################################################################
5469 #############################################################################
5470 # Function    : ReadDeclarationsFile
5471 # Description : This reads in a file containing the function/macro/enum etc.
5472 #                declarations.
5474 #                Note that in some cases there are several declarations with
5475 #                the same name, e.g. for conditional macros. In this case we
5476 #                set a flag in the %DeclarationConditional hash so the
5477 #                declaration is not shown in the docs.
5479 #                If a macro and a function have the same name, e.g. for
5480 #                gtk_object_ref, the function declaration takes precedence.
5482 #                Some opaque structs are just declared with 'typedef struct
5483 #                _name name;' in which case the declaration may be empty.
5484 #                The structure may have been found later in the header, so
5485 #                that overrides the empty declaration.
5487 # Arguments   : $file - the declarations file to read
5488 #                $override - if declarations in this file should override
5489 #                        any current declaration.
5490 #############################################################################
5492 sub ReadDeclarationsFile {
5493     my ($file, $override) = @_;
5495     if ($override == 0) {
5496         %Declarations = ();
5497         %DeclarationTypes = ();
5498         %DeclarationConditional = ();
5499         %DeclarationOutput = ();
5500     }
5502     open (INPUT, $file)
5503         || die "Can't open $file: $!";
5504     my $declaration_type = "";
5505     my $declaration_name;
5506     my $declaration;
5507     my $is_deprecated = 0;
5508     while (<INPUT>) {
5509         if (!$declaration_type) {
5510             if (m/^<([^>]+)>/) {
5511                 $declaration_type = $1;
5512                 $declaration_name = "";
5513                 @TRACE@("Found declaration: $declaration_type\n");
5514                 $declaration = "";
5515             }
5516         } else {
5517             if (m%^<NAME>(.*)</NAME>%) {
5518                 $declaration_name = $1;
5519             } elsif (m%^<DEPRECATED/>%) {
5520                 $is_deprecated = 1;
5521             } elsif (m%^</$declaration_type>%) {
5522                 @TRACE@("Found end of declaration: $declaration_name\n");
5523                 # Check that the declaration has a name
5524                 if ($declaration_name eq "") {
5525                     &LogWarning ($file, $., "$declaration_type has no name.\n");
5526                 }
5528                 # If the declaration is an empty typedef struct _XXX XXX
5529                 # set the flag to indicate the struct has a typedef.
5530                 if (($declaration_type eq 'STRUCT' || $declaration_type eq 'UNION')
5531                     && $declaration =~ m/^\s*$/) {
5532                     @TRACE@("Struct has typedef: $declaration_name\n");
5533                     $StructHasTypedef{$declaration_name} = 1;
5534                 }
5536                 # Check if the symbol is already defined.
5537                 if (defined ($Declarations{$declaration_name})
5538                     && $override == 0) {
5539                     # Function declarations take precedence.
5540                     if ($DeclarationTypes{$declaration_name} eq 'FUNCTION') {
5541                         # Ignore it.
5542                     } elsif ($declaration_type eq 'FUNCTION') {
5543                         if ($is_deprecated) {
5544                             $Deprecated{$declaration_name} = "";
5545                         }
5546                         $Declarations{$declaration_name} = $declaration;
5547                         $DeclarationTypes{$declaration_name} = $declaration_type;
5548                     } elsif ($DeclarationTypes{$declaration_name}
5549                               eq $declaration_type) {
5550                         # If the existing declaration is empty, or is just a
5551                         # forward declaration of a struct, override it.
5552                         if ($declaration_type eq 'STRUCT' || $declaration_type eq 'UNION') {
5553                             if ($Declarations{$declaration_name} =~ m/^\s*((struct|union)\s+\w+\s*;)?\s*$/) {
5554                                 if ($is_deprecated) {
5555                                     $Deprecated{$declaration_name} = "";
5556                                 }
5557                                 $Declarations{$declaration_name} = $declaration;
5558                             } elsif ($declaration =~ m/^\s*((struct|union)\s+\w+\s*;)?\s*$/) {
5559                                 # Ignore an empty or forward declaration.
5560                             } else {
5561                                 &LogWarning ($file, $., "Structure $declaration_name has multiple definitions.");
5562                             }
5563                         } else {
5564                             # set flag in %DeclarationConditional hash for
5565                             # multiply defined macros/typedefs.
5566                             $DeclarationConditional{$declaration_name} = 1;
5567                         }
5568                     } else {
5569                         &LogWarning ($file, $., "$declaration_name has multiple definitions.");
5570                     }
5571                 } else {
5572                     if ($is_deprecated) {
5573                         $Deprecated{$declaration_name} = "";
5574                     }
5575                     $Declarations{$declaration_name} = $declaration;
5576                     $DeclarationTypes{$declaration_name} = $declaration_type;
5577                 }
5579                 $declaration_type = "";
5580                 $is_deprecated = 0;
5581             } else {
5582                 $declaration .= $_;
5583             }
5584         }
5585     }
5586     close (INPUT);
5590 #############################################################################
5591 # Function    : ReadSignalsFile
5592 # Description : This reads in an existing file which contains information on
5593 #                all GTK signals. It creates the arrays @SignalNames and
5594 #                @SignalPrototypes containing info on the signals. The first
5595 #                line of the SignalPrototype is the return type of the signal
5596 #                handler. The remaining lines are the parameters passed to it.
5597 #                The last parameter, "gpointer user_data" is always the same
5598 #                so is not included.
5599 # Arguments   : $file - the file containing the signal handler prototype
5600 #                        information.
5601 #############################################################################
5603 sub ReadSignalsFile {
5604     my ($file) = @_;
5606     my $in_signal = 0;
5607     my $signal_object;
5608     my $signal_name;
5609     my $signal_returns;
5610     my $signal_flags;
5611     my $signal_prototype;
5613     # Reset the signal info.
5614     @SignalObjects = ();
5615     @SignalNames = ();
5616     @SignalReturns = ();
5617     @SignalFlags = ();
5618     @SignalPrototypes = ();
5620     if (! -f $file) {
5621         return;
5622     }
5623     if (!open (INPUT, $file)) {
5624         warn "Can't open $file - skipping signals\n";
5625         return;
5626     }
5627     while (<INPUT>) {
5628         if (!$in_signal) {
5629             if (m/^<SIGNAL>/) {
5630                 $in_signal = 1;
5631                 $signal_object = "";
5632                 $signal_name = "";
5633                 $signal_returns = "";
5634                 $signal_prototype = "";
5635             }
5636         } else {
5637             if (m/^<NAME>(.*)<\/NAME>/) {
5638                 $signal_name = $1;
5639                 if ($signal_name =~ m/^(.*)::(.*)$/) {
5640                     $signal_object = $1;
5641                     ($signal_name = $2) =~ s/_/-/g;
5642                     @TRACE@("Found signal: $signal_name\n");
5643                 } else {
5644                     &LogWarning ($file, $., "Invalid signal name: $signal_name.");
5645                 }
5646             } elsif (m/^<RETURNS>(.*)<\/RETURNS>/) {
5647                 $signal_returns = $1;
5648             } elsif (m/^<FLAGS>(.*)<\/FLAGS>/) {
5649                 $signal_flags = $1;
5650             } elsif (m%^</SIGNAL>%) {
5651                 @TRACE@("Found end of signal: ${signal_object}::${signal_name}\nReturns: ${signal_returns}\n${signal_prototype}");
5652                 push (@SignalObjects, $signal_object);
5653                 push (@SignalNames, $signal_name);
5654                 push (@SignalReturns, $signal_returns);
5655                 push (@SignalFlags, $signal_flags);
5656                 push (@SignalPrototypes, $signal_prototype);
5657                 $in_signal = 0;
5658             } else {
5659                 $signal_prototype .= $_;
5660             }
5661         }
5662     }
5663     close (INPUT);
5667 #############################################################################
5668 # Function    : ReadTemplateFile
5669 # Description : This reads in the manually-edited documentation file
5670 #               corresponding to the file currently being created, so we can
5671 #               insert the documentation at the appropriate places.
5672 #               It outputs %SymbolTypes, %SymbolDocs and %SymbolParams, which
5673 #               is a hash of arrays.
5674 #               NOTE: This function is duplicated in gtkdoc-mktmpl (but
5675 #               slightly different).
5676 # Arguments   : $docsfile - the template file to read in.
5677 #               $skip_unused_params - 1 if the unused parameters should be
5678 #                 skipped.
5679 #############################################################################
5681 sub ReadTemplateFile {
5682     my ($docsfile, $skip_unused_params) = @_;
5684     my $template = "$docsfile.sgml";
5685     if (! -f $template) {
5686         @TRACE@("File doesn't exist: $template\n");
5687         return 0;
5688     }
5690     # start with empty hashes, we merge the source comment for each file
5691     # afterwards
5692     %SymbolDocs = ();
5693     %SymbolTypes = ();
5694     %SymbolParams = ();
5696     my $current_type = "";        # Type of symbol being read.
5697     my $current_symbol = "";        # Name of symbol being read.
5698     my $symbol_doc = "";                # Description of symbol being read.
5699     my @params;                        # Parameter names and descriptions of current
5700                                 #   function/macro/function typedef.
5701     my $current_param = -1;        # Index of parameter currently being read.
5702                                 #   Note that the param array contains pairs
5703                                 #   of param name & description.
5704     my $in_unused_params = 0;        # True if we are reading in the unused params.
5705     my $in_deprecated = 0;
5706     my $in_since = 0;
5707     my $in_stability = 0;
5709     open (DOCS, "$template")
5710         || die "Can't open $template: $!";
5712     @TRACE@("reading template $template");
5714     while (<DOCS>) {
5715         if (m/^<!-- ##### ([A-Z_]+) (\S+) ##### -->/) {
5716             my $type = $1;
5717             my $symbol = $2;
5718             if ($symbol eq "Title"
5719                 || $symbol eq "Short_Description"
5720                 || $symbol eq "Long_Description"
5721                 || $symbol eq "See_Also"
5722                 || $symbol eq "Stability_Level"
5723                 || $symbol eq "Include"
5724                 || $symbol eq "Image") {
5726                 $symbol = $docsfile . ":" . $symbol;
5727             }
5729             @TRACE@("Found symbol: $symbol\n");
5730             # Remember file and line for the symbol
5731             $SymbolSourceFile{$symbol} = $template;
5732             $SymbolSourceLine{$symbol} = $.;
5734             # Store previous symbol, but remove any trailing blank lines.
5735             if ($current_symbol ne "") {
5736                 $symbol_doc =~ s/\s+$//;
5737                 $SymbolTypes{$current_symbol} = $current_type;
5738                 $SymbolDocs{$current_symbol} = $symbol_doc;
5740                 # Check that the stability level is valid.
5741                 if ($StabilityLevel{$current_symbol}) {
5742                     $StabilityLevel{$current_symbol} = &ParseStabilityLevel($StabilityLevel{$current_symbol}, $template, $., "Stability level for $current_symbol");
5743                 }
5745                 if ($current_param >= 0) {
5746                     $SymbolParams{$current_symbol} = [ @params ];
5747                 } else {
5748                     # Delete any existing params in case we are overriding a
5749                     # previously read template.
5750                     delete $SymbolParams{$current_symbol};
5751                 }
5752             }
5753             $current_type = $type;
5754             $current_symbol = $symbol;
5755             $current_param = -1;
5756             $in_unused_params = 0;
5757             $in_deprecated = 0;
5758             $in_since = 0;
5759             $in_stability = 0;
5760             $symbol_doc = "";
5761             @params = ();
5763         } elsif (m/^<!-- # Unused Parameters # -->/) {
5764             @TRACE@("Found unused parameters\n");
5765             $in_unused_params = 1;
5766             next;
5768         } elsif ($in_unused_params && $skip_unused_params) {
5769             # When outputting the DocBook we skip unused parameters.
5770             @TRACE@("Skipping unused param: $_");
5771             next;
5773         } else {
5774             # Check if param found. Need to handle "..." and "format...".
5775             if (s/^\@([\w\.]+):\040?//) {
5776                 my $param_name = $1;
5777                 my $param_desc = $_;
5778                 # Allow variations of 'Returns'
5779                 if ($param_name =~ m/^[Rr]eturns?$/) {
5780                     $param_name = "Returns";
5781                 }
5782                 # Allow varargs variations
5783                 if ($param_name =~ m/^.*\.\.\.$/) {
5784                     $param_name = "...";
5785                 }
5787                 # strip trailing whitespaces and blank lines
5788                 s/\s+\n$/\n/m;
5789                 s/\n+$/\n/sm;
5790                 @TRACE@("Found param for symbol $current_symbol : '$param_name'= '$_'");
5792                 if ($param_name eq "Deprecated") {
5793                     $in_deprecated = 1;
5794                     $Deprecated{$current_symbol} = $_;
5795                 } elsif ($param_name eq "Since") {
5796                     $in_since = 1;
5797                     chomp;
5798                     $Since{$current_symbol} = $_;
5799                 } elsif ($param_name eq "Stability") {
5800                     $in_stability = 1;
5801                     $StabilityLevel{$current_symbol} = $_;
5802                 } else {
5803                     push (@params, $param_name);
5804                     push (@params, $param_desc);
5805                     $current_param += $PARAM_FIELD_COUNT;
5806                 }
5807             } else {
5808                 # strip trailing whitespaces and blank lines
5809                 s/\s+\n$/\n/m;
5810                 s/\n+$/\n/sm;
5812                 if (!m/^\s+$/) {
5813                     if ($in_deprecated) {
5814                         $Deprecated{$current_symbol} .= $_;
5815                     } elsif ($in_since) {
5816                         &LogWarning ($template, $., "multi-line since docs found");
5817                         #$Since{$current_symbol} .= $_;
5818                     } elsif ($in_stability) {
5819                         $StabilityLevel{$current_symbol} .= $_;
5820                     } elsif ($current_param >= 0) {
5821                         $params[$current_param] .= $_;
5822                     } else {
5823                         $symbol_doc .= $_;
5824                     }
5825                 }
5826             }
5827         }
5828     }
5830     # Remember to finish the current symbol doccs.
5831     if ($current_symbol ne "") {
5833         $symbol_doc =~ s/\s+$//;
5834         $SymbolTypes{$current_symbol} = $current_type;
5835         $SymbolDocs{$current_symbol} = $symbol_doc;
5837         # Check that the stability level is valid.
5838         if ($StabilityLevel{$current_symbol}) {
5839             $StabilityLevel{$current_symbol} = &ParseStabilityLevel($StabilityLevel{$current_symbol}, $template, $., "Stability level for $current_symbol");
5840         }
5842         if ($current_param >= 0) {
5843             $SymbolParams{$current_symbol} = [ @params ];
5844         } else {
5845             # Delete any existing params in case we are overriding a
5846             # previously read template.
5847             delete $SymbolParams{$current_symbol};
5848         }
5849     }
5851     close (DOCS);
5852     return 1;
5856 #############################################################################
5857 # Function    : ReadObjectHierarchy
5858 # Description : This reads in the $MODULE-hierarchy.txt file containing all
5859 #               the GtkObject subclasses described in this module (and their
5860 #               ancestors).
5861 #               It places them in the @Objects array, and places their level
5862 #               in the object hierarchy in the @ObjectLevels array, at the
5863 #               same index. GtkObject, the root object, has a level of 1.
5865 #               This also generates tree_index.sgml as it goes along.
5867 # Arguments   : none
5868 #############################################################################
5870 sub ReadObjectHierarchy {
5871     @Objects = ();
5872     @ObjectLevels = ();
5874     if (! -f $OBJECT_TREE_FILE) {
5875         return;
5876     }
5877     if (!open (INPUT, $OBJECT_TREE_FILE)) {
5878         warn "Can't open $OBJECT_TREE_FILE - skipping object tree\n";
5879         return;
5880     }
5882     # Only emit objects if they are supposed to be documented, or if
5883     # they have documented children. To implement this, we maintain a
5884     # stack of pending objects which will be emitted if a documented
5885     # child turns up.
5886     my @pending_objects = ();
5887     my @pending_levels = ();
5888     my $root;
5889     my @tree = ();
5890     while (<INPUT>) {
5891         if (m/\S+/) {
5892             my $object = $&;
5893             my $level = (length($`)) / 2 + 1;
5894             my $xref = "";
5896             if ($level == 1) {
5897                 $root = $object;
5898             }
5900             while (($#pending_levels >= 0) && ($pending_levels[$#pending_levels] >= $level)) {
5901                 my $pobject = pop(@pending_objects);
5902                 my $plevel = pop(@pending_levels);
5903             }
5905             push (@pending_objects, $object);
5906             push (@pending_levels, $level);
5908             if (exists($KnownSymbols{$object})) {
5909                 while ($#pending_levels >= 0) {
5910                     $object = shift @pending_objects;
5911                     $level = shift @pending_levels;
5912                     $xref = &MakeXRef ($object);
5914                     push (@tree, ' ' x ($level * 4) . "$xref");
5915                     push (@Objects, $object);
5916                     push (@ObjectLevels, $level);
5917                     $ObjectRoots{$object} = $root;
5918                 }
5919             }
5920             #else {
5921             #    LogWarning ($OBJECT_TREE_FILE, $., "unknown type $object");
5922             #}
5923         }
5924     }
5925     close (INPUT);
5927     # FIXME: use xml
5928     # my $old_tree_index = "$DB_OUTPUT_DIR/tree_index.$xml";
5929     my $old_tree_index = "$DB_OUTPUT_DIR/tree_index.sgml";
5930     my $new_tree_index = "$DB_OUTPUT_DIR/tree_index.new";
5932     open (OUTPUT, ">$new_tree_index")
5933         || die "Can't create $new_tree_index: $!";
5935     print (OUTPUT &MakeDocHeader ("screen")."\n<screen>\n".&AddTreeLineArt(\@tree)."\n</screen>\n");
5936     close (OUTPUT);
5938     &UpdateFileIfChanged ($old_tree_index, $new_tree_index, 0);
5940     &OutputObjectList;
5943 #############################################################################
5944 # Function    : ReadInterfaces
5945 # Description : This reads in the $MODULE.interfaces file.
5947 # Arguments   : none
5948 #############################################################################
5950 sub ReadInterfaces {
5951     %Interfaces = ();
5953     if (! -f $INTERFACES_FILE) {
5954         return;
5955     }
5956     if (!open (INPUT, $INTERFACES_FILE)) {
5957         warn "Can't open $INTERFACES_FILE - skipping interfaces\n";
5958         return;
5959     }
5961     while (<INPUT>) {
5962        chomp;
5963        my ($object, @ifaces) = split;
5964        if (exists($KnownSymbols{$object}) && $KnownSymbols{$object} == 1) {
5965            my @knownIfaces = ();
5967            # filter out private interfaces, but leave foreign interfaces
5968            foreach my $iface (@ifaces) {
5969                if (!exists($KnownSymbols{$iface}) || $KnownSymbols{$iface} == 1) {
5970                    push (@knownIfaces, $iface);
5971                }
5972              }
5974            $Interfaces{$object} = join(' ', @knownIfaces);
5975            @TRACE@("Interfaces for $object: $Interfaces{$object}\n");
5976        } else {
5977          @TRACE@("skipping interfaces for unknown symbol: $object\n");
5978        }
5979     }
5980     close (INPUT);
5983 #############################################################################
5984 # Function    : ReadPrerequisites
5985 # Description : This reads in the $MODULE.prerequisites file.
5987 # Arguments   : none
5988 #############################################################################
5990 sub ReadPrerequisites {
5991     %Prerequisites = ();
5993     if (! -f $PREREQUISITES_FILE) {
5994         return;
5995     }
5996     if (!open (INPUT, $PREREQUISITES_FILE)) {
5997         warn "Can't open $PREREQUISITES_FILE - skipping prerequisites\n";
5998         return;
5999     }
6001     while (<INPUT>) {
6002        chomp;
6003        my ($iface, @prereqs) = split;
6004        if (exists($KnownSymbols{$iface}) && $KnownSymbols{$iface} == 1) {
6005            my @knownPrereqs = ();
6007            # filter out private prerequisites, but leave foreign prerequisites
6008            foreach my $prereq (@prereqs) {
6009                if (!exists($KnownSymbols{$prereq}) || $KnownSymbols{$prereq} == 1) {
6010                   push (@knownPrereqs, $prereq);
6011                }
6012            }
6014            $Prerequisites{$iface} = join(' ', @knownPrereqs);
6015        }
6016     }
6017     close (INPUT);
6020 #############################################################################
6021 # Function    : ReadArgsFile
6022 # Description : This reads in an existing file which contains information on
6023 #                all GTK args. It creates the arrays @ArgObjects, @ArgNames,
6024 #                @ArgTypes, @ArgFlags, @ArgNicks and @ArgBlurbs containing info
6025 #               on the args.
6026 # Arguments   : $file - the file containing the arg information.
6027 #############################################################################
6029 sub ReadArgsFile {
6030     my ($file) = @_;
6032     my $in_arg = 0;
6033     my $arg_object;
6034     my $arg_name;
6035     my $arg_type;
6036     my $arg_flags;
6037     my $arg_nick;
6038     my $arg_blurb;
6039     my $arg_default;
6040     my $arg_range;
6042     # Reset the args info.
6043     @ArgObjects = ();
6044     @ArgNames = ();
6045     @ArgTypes = ();
6046     @ArgFlags = ();
6047     @ArgNicks = ();
6048     @ArgBlurbs = ();
6049     @ArgDefaults = ();
6050     @ArgRanges = ();
6052     if (! -f $file) {
6053         return;
6054     }
6055     if (!open (INPUT, $file)) {
6056         warn "Can't open $file - skipping args\n";
6057         return;
6058     }
6059     while (<INPUT>) {
6060         if (!$in_arg) {
6061             if (m/^<ARG>/) {
6062                 $in_arg = 1;
6063                 $arg_object = "";
6064                 $arg_name = "";
6065                 $arg_type = "";
6066                 $arg_flags = "";
6067                 $arg_nick = "";
6068                 $arg_blurb = "";
6069                 $arg_default = "";
6070                 $arg_range = "";
6071             }
6072         } else {
6073             if (m/^<NAME>(.*)<\/NAME>/) {
6074                 $arg_name = $1;
6075                 if ($arg_name =~ m/^(.*)::(.*)$/) {
6076                     $arg_object = $1;
6077                     ($arg_name = $2) =~ s/_/-/g;
6078                     @TRACE@("Found arg: $arg_name\n");
6079                 } else {
6080                     &LogWarning ($file, $., "Invalid argument name: $arg_name");
6081                 }
6082             } elsif (m/^<TYPE>(.*)<\/TYPE>/) {
6083                 $arg_type = $1;
6084             } elsif (m/^<RANGE>(.*)<\/RANGE>/) {
6085                 $arg_range = $1;
6086             } elsif (m/^<FLAGS>(.*)<\/FLAGS>/) {
6087                 $arg_flags = $1;
6088             } elsif (m/^<NICK>(.*)<\/NICK>/) {
6089                 $arg_nick = $1;
6090             } elsif (m/^<BLURB>(.*)<\/BLURB>/) {
6091                 $arg_blurb = $1;
6092                 if ($arg_blurb eq "(null)") {
6093                   $arg_blurb = "";
6094                   &LogWarning ($file, $., "Property ${arg_object}:${arg_name} has no documentation.");
6095                 }
6096             } elsif (m/^<DEFAULT>(.*)<\/DEFAULT>/) {
6097                 $arg_default = $1;
6098             } elsif (m%^</ARG>%) {
6099                 @TRACE@("Found end of arg: ${arg_object}::${arg_name}\n${arg_type} : ${arg_flags}\n");
6100                 push (@ArgObjects, $arg_object);
6101                 push (@ArgNames, $arg_name);
6102                 push (@ArgTypes, $arg_type);
6103                 push (@ArgRanges, $arg_range);
6104                 push (@ArgFlags, $arg_flags);
6105                 push (@ArgNicks, $arg_nick);
6106                 push (@ArgBlurbs, $arg_blurb);
6107                 push (@ArgDefaults, $arg_default);
6108                 $in_arg = 0;
6109             }
6110         }
6111     }
6112     close (INPUT);
6115 #############################################################################
6116 # Function    : AddTreeLineArt
6117 # Description : Add unicode lineart to a pre-indented string array and returns
6118 #               it as as multiline string.
6119 # Arguments   : @tree - array of indented strings.
6120 #############################################################################
6122 sub AddTreeLineArt {
6123   my @tree = @{$_[0]};
6124   my $i;
6125   my $j;
6126   my $indent;
6127   
6128   # iterate bottom up over the tree 
6129   for ($i = $#tree; $i >= 0; $i--) {
6130     # count leading spaces
6131     $tree[$i] =~ /^([^<A-Za-z]*)/;
6132     $indent = length( $1 );
6133     # replace with ╰───, if place of ╰ is not space insert ├
6134     if ($indent > 4) {
6135       if (substr($tree[$i],$indent-4,1) eq " ") {
6136         substr($tree[$i],$indent-4,4) = "--- ";
6137       } else {
6138         substr($tree[$i],$indent-4,4) = "+-- ";
6139       }
6140       # go lines up while space and insert |
6141       for ($j = $i - 1; ($j >= 0 && substr($tree[$j],$indent-4,1) eq ' '); $j--) {
6142         substr($tree[$j],$indent-4,1) = '|';
6143       }
6144     }
6145   }
6146   
6147   my $res = join("\n", @tree);
6148   # unicode chars for: ╰──
6149   $res =~ s%---%<phrase role=\"lineart\">&#9584;&#9472;&#9472;</phrase>%g;
6150   # unicde chars for: ├──
6151   $res =~ s%\+--%<phrase role=\"lineart\">&#9500;&#9472;&#9472;</phrase>%g;
6152   # unicode char for: │
6153   $res =~ s%\|%<phrase role=\"lineart\">&#9474;</phrase>%g;
6154   
6155   return $res;
6159 #############################################################################
6160 # Function    : CheckIsObject
6161 # Description : Returns 1 if the given name is a GObject or a subclass.
6162 #                It uses the global @Objects array.
6163 #                Note that the @Objects array only contains classes in the
6164 #                current module and their ancestors - not all GObject classes.
6165 # Arguments   : $name - the name to check.
6166 #############################################################################
6168 sub CheckIsObject {
6169     my ($name) = @_;
6170     my $root = $ObjectRoots{$name};
6171     # Let GBoxed pass as an object here to get -struct appended to the id
6172     # and prevent conflicts with sections.
6173     return (defined($root) and $root ne 'GEnum' and $root ne 'GFlags');
6177 #############################################################################
6178 # Function    : MakeReturnField
6179 # Description : Pads a string to $RETURN_TYPE_FIELD_WIDTH.
6180 # Arguments   : $str - the string to pad.
6181 #############################################################################
6183 sub MakeReturnField {
6184     my ($str) = @_;
6186     return $str . (' ' x ($RETURN_TYPE_FIELD_WIDTH - length ($str)));
6189 #############################################################################
6190 # Function    : GetSymbolSourceFile
6191 # Description : Get the filename where the symbol docs where taken from.
6192 # Arguments   : $symbol - the symbol name
6193 #############################################################################
6195 sub GetSymbolSourceFile {
6196     my ($symbol) = @_;
6198     if (defined($SourceSymbolSourceFile{$symbol})) {
6199         return $SourceSymbolSourceFile{$symbol};
6200     } elsif (defined($SymbolSourceFile{$symbol})) {
6201         return $SymbolSourceFile{$symbol};
6202     } else {
6203         return "";
6204     }
6207 #############################################################################
6208 # Function    : GetSymbolSourceLine
6209 # Description : Get the file line where the symbol docs where taken from.
6210 # Arguments   : $symbol - the symbol name
6211 #############################################################################
6213 sub GetSymbolSourceLine {
6214     my ($symbol) = @_;
6216     if (defined($SourceSymbolSourceLine{$symbol})) {
6217         return $SourceSymbolSourceLine{$symbol};
6218     } elsif (defined($SymbolSourceLine{$symbol})) {
6219         return $SymbolSourceLine{$symbol};
6220     } else {
6221         return 0;
6222     }