Updated Spanish translation
[gtk-doc.git] / gtkdoc-mkdb.in
blob4c38aaa939f947da20b2cd64f5a4c9e94fbc7786
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 tagSYSTEM 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             # if there are SYSTEM ENTITIES here, we should prepend "../" to the path
375             # FIXME: not sure if we can do this now, as people already work-around the problem
376             # s#<!ENTITY % ([a-zA-Z-]+) SYSTEM \"([^/][a-zA-Z./]+)\">#<!ENTITY % $1 SYSTEM \"../$2\">#;
377             s#<!ENTITY % gtkdocentities SYSTEM \"([^"]*)\">#<!ENTITY % gtkdocentities SYSTEM \"../$1\">#;
378             $doctype_header .= $_;
379         }
380         close(INPUT);
381     } else {
382         $doctype_header = <<EOF;
383 <?xml version="1.0"?>
384 <!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
385                "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"
387   <!ENTITY % local.common.attrib "xmlns:xi  CDATA  #FIXED 'http://www.w3.org/2003/XInclude'">
388   <!ENTITY % gtkdocentities SYSTEM "../xml/gtkdocentities.ent">
389   %gtkdocentities;
392     }
393     chomp($doctype_header);
394     
395     # All the files are written in subdirectories beneath here.
396     $TMPL_DIR = $TMPL_DIR ? $TMPL_DIR : "$ROOT_DIR/tmpl";
397     
398     # This is where we put all the DocBook output.
399     $DB_OUTPUT_DIR = $DB_OUTPUT_DIR ? $DB_OUTPUT_DIR : "$ROOT_DIR/xml";
400   
401     # This file contains the object hierarchy.
402     $OBJECT_TREE_FILE = "$ROOT_DIR/$MODULE.hierarchy";
403   
404     # This file contains the interfaces.
405     $INTERFACES_FILE = "$ROOT_DIR/$MODULE.interfaces";
406     
407     # This file contains the prerequisites.
408     $PREREQUISITES_FILE = "$ROOT_DIR/$MODULE.prerequisites";
409     
410     # This file contains signal arguments and names.
411     $SIGNALS_FILE = "$ROOT_DIR/$MODULE.signals";
412     
413     # The file containing Arg information.
414     $ARGS_FILE = "$ROOT_DIR/$MODULE.args";
415   
416     # Create the root DocBook output directory if it doens't exist.
417     if (! -e $DB_OUTPUT_DIR) {
418         mkdir ("$DB_OUTPUT_DIR", 0777)
419             || die "Can't create directory: $DB_OUTPUT_DIR";
420     }
421     
422     &ReadKnownSymbols ("$ROOT_DIR/$MODULE-sections.txt");
423     &ReadSignalsFile ($SIGNALS_FILE);
424     &ReadArgsFile ($ARGS_FILE);
425     &ReadObjectHierarchy;
426     &ReadInterfaces;
427     &ReadPrerequisites;
428     
429     &ReadDeclarationsFile ("$ROOT_DIR/$MODULE-decl.txt", 0);
430     if (-f "$ROOT_DIR/$MODULE-overrides.txt") {
431         &ReadDeclarationsFile ("$ROOT_DIR/$MODULE-overrides.txt", 1);
432     }
433     
434     for my $dir (@SOURCE_DIRS) {
435         &ReadSourceDocumentation ($dir);
436     }
437     
438     my $changed = &OutputDB ("$ROOT_DIR/$MODULE-sections.txt");
439     
440     # If any of the DocBook files have changed, update the timestamp file (so
441     # it can be used for Makefile dependencies).
442     if ($changed || ! -e "$ROOT_DIR/sgml.stamp") {
443     
444         # try to detect the common prefix
445         # GtkWidget, GTK_WIDGET, gtk_widget -> gtk
446         if ($NAME_SPACE eq "") {
447             $NAME_SPACE="";
448             my $pos=0;
449             my $ratio=0.0;
450             do {
451                 my %prefix;
452                 my $letter="";
453                 foreach my $symbol (keys(%IndexEntriesFull)) {
454                     if(($NAME_SPACE eq "") || $symbol =~ /^$NAME_SPACE/i) {
455                         if (length($symbol)>$pos) {
456                             $letter=substr($symbol,$pos,1);
457                             # stop prefix scanning
458                             if ($letter eq "_") {
459                                 # stop on "_"
460                                 last;
461                             }
462                             # Should we also stop on a uppercase char, if last was lowercase
463                             #   GtkWidget, if we have the 'W' and had the 't' before
464                             # or should we count upper and lowercase, and stop one 2nd uppercase, if we already had a lowercase
465                             #   GtkWidget, the 'W' would be the 2nd uppercase and with 't','k' we had lowercase chars before
466                             # need to recound each time as this is per symbol
467                             $prefix{uc($letter)}++;
468                         }
469                     }
470                 }
471                 if ($letter ne "" && $letter ne "_") {
472                     my $maxletter="";
473                     my $maxsymbols=0;
474                     foreach $letter (keys(%prefix)) {
475                         #print "$letter: $prefix{$letter}.\n";
476                         if ($prefix{$letter}>$maxsymbols) {
477                             $maxletter=$letter;
478                             $maxsymbols=$prefix{$letter};
479                         }
480                     }
481                     $ratio = scalar(keys(%IndexEntriesFull)) / $prefix{$maxletter};
482                     #print "most symbols start with $maxletter, that is ". (100 * $ratio) ." %\n";
483                     if ($ratio > 0.9) {
484                         # do another round
485                         $NAME_SPACE .= $maxletter;
486                     }
487                     $pos++;
488                 }
489                 else {
490                     $ratio=0.0;
491                 }
492             } while ($ratio > 0.9);
493             #print "most symbols start with $NAME_SPACE\n";
494         }
495     
496         &OutputIndexFull;
497         &OutputDeprecatedIndex;
498         &OutputSinceIndexes;
499         &OutputAnnotationGlossary;
500     
501         open (TIMESTAMP, ">$ROOT_DIR/sgml.stamp")
502             || die "Can't create $ROOT_DIR/sgml.stamp: $!";
503         print (TIMESTAMP "timestamp");
504         close (TIMESTAMP);
505     }
508 #############################################################################
509 # Function    : OutputObjectList
510 # Description : This outputs the alphabetical list of objects, in a columned
511 #                table.
512 #               FIXME: Currently this also outputs ancestor objects
513 #                which may not actually be in this module.
514 # Arguments   : none
515 #############################################################################
517 sub OutputObjectList {
518     my $cols = 3;
520     # FIXME: use .xml
521     # my $old_object_index = "$DB_OUTPUT_DIR/object_index.xml";
522     my $old_object_index = "$DB_OUTPUT_DIR/object_index.sgml";
523     my $new_object_index = "$DB_OUTPUT_DIR/object_index.new";
525     open (OUTPUT, ">$new_object_index")
526         || die "Can't create $new_object_index: $!";
528     print (OUTPUT <<EOF);
529 ${\( MakeDocHeader ("informaltable") )}
530 <informaltable pgwide="1" frame="none">
531 <tgroup cols="$cols">
532 <colspec colwidth="1*"/>
533 <colspec colwidth="1*"/>
534 <colspec colwidth="1*"/>
535 <tbody>
538     my $count = 0;
539     my $object;
540     foreach $object (sort (@Objects)) {
541         my $xref = &MakeXRef ($object);
542         if ($count % $cols == 0) { print (OUTPUT "<row>\n"); }
543         print (OUTPUT "<entry>$xref</entry>\n");
544         if ($count % $cols == ($cols - 1)) { print (OUTPUT "</row>\n"); }
545         $count++;
546     }
547     if ($count == 0) {
548         # emit an empty row, since empty tables are invalid
549         print (OUTPUT "<row><entry> </entry></row>\n");
550     }
551     else {
552         if ($count % $cols > 0) {
553             print (OUTPUT "</row>\n");
554         }
555     }
557     print (OUTPUT <<EOF);
558 </tbody></tgroup></informaltable>
560     close (OUTPUT);
562     &UpdateFileIfChanged ($old_object_index, $new_object_index, 0);
565 #############################################################################
566 # Function    : TrimTextBlock
567 # Description : Trims extra whitespace. Empty lines inside a block are
568 #                preserved.
569 # Arguments   : $desc - the text block to trim. May contain newlines.
570 #############################################################################
572 sub TrimTextBlock {
573   my ($desc) = @_;
574   
575   # strip leading spaces on the block
576   $desc =~ s/^\s+//s;
577   # strip trailing spaces on every line
578   $desc =~ s/\s+$/\n/mg;
579   
580   return $desc;
584 #############################################################################
585 # Function    : OutputDB
586 # Description : This collects the output for each section of the docs, and
587 #                outputs each file when the end of the section is found.
588 # Arguments   : $file - the $MODULE-sections.txt file which contains all of
589 #                the functions/macros/structs etc. being documented, organised
590 #                into sections and subsections.
591 #############################################################################
593 sub OutputDB {
594     my ($file) = @_;
596     @TRACE@("Reading: $file\n");
597     open (INPUT, $file)
598         || die "Can't open $file: $!";
599     my $filename = "";
600     my $book_top = "";
601     my $book_bottom = "";
602     my $includes = (defined $DEFAULT_INCLUDES) ? $DEFAULT_INCLUDES : "";
603     my $section_includes = "";
604     my $in_section = 0;
605     my $title = "";
606     my $section_id = "";
607     my $subsection = "";
608     my $num_symbols;
609     my $changed = 0;
610     my $functions_synop = "";
611     my $other_synop = "";
612     my $functions_details = "";
613     my $other_details = "";
614     my $signals_synop = "";
615     my $signals_desc = "";
616     my $args_synop = "";
617     my $child_args_synop = "";
618     my $style_args_synop = "";
619     my $args_desc = "";
620     my $child_args_desc = "";
621     my $style_args_desc = "";
622     my $hierarchy_str = "";
623     my @hierarchy = ();
624     my $interfaces = "";
625     my $implementations = "";
626     my $prerequisites = "";
627     my $derived = "";
628     my @file_objects = ();
629     my %templates = ();
630     my %symbol_def_line = ();
632     # merge the source docs, in case there are no templates
633     &MergeSourceDocumentation;
635     while (<INPUT>) {
636         if (m/^#/) {
637             next;
639         } elsif (m/^<SECTION>/) {
640             $num_symbols = 0;
641             $in_section = 1;
642             @file_objects = ();
643             %symbol_def_line = ();
645         } elsif (m/^<SUBSECTION\s*(.*)>/i) {
646             $other_synop .= "\n";
647             $functions_synop .= "\n";
648             $subsection = $1;
650         } elsif (m/^<SUBSECTION>/) {
652         } elsif (m/^<TITLE>(.*)<\/TITLE>/) {
653             $title = $1;
654             @TRACE@("Section: $title\n");
656             # We don't want warnings if object & class structs aren't used.
657             $DeclarationOutput{$title} = 1;
658             $DeclarationOutput{"${title}Class"} = 1;
659             $DeclarationOutput{"${title}Iface"} = 1;
660             $DeclarationOutput{"${title}Interface"} = 1;
662         } elsif (m/^<FILE>(.*)<\/FILE>/) {
663             $filename = $1;
664             if (! defined $templates{$filename}) {
665                if (&ReadTemplateFile ("$TMPL_DIR/$filename", 1)) {
666                    &MergeSourceDocumentation;
667                    $templates{$filename}=$.;
668                }
669             } else {
670                 &LogWarning ($file, $., "Double <FILE>$filename</FILE> entry. ".
671                     "Previous occurrence on line ".$templates{$filename}.".");
672             }
673             if (($title eq "") and (defined $SourceSymbolDocs{"$TMPL_DIR/$filename:Title"})) {
674                 $title = $SourceSymbolDocs{"$TMPL_DIR/$filename:Title"};
675                  # Remove trailing blanks
676                 $title =~ s/\s+$//;
677            }
679         } elsif (m/^<INCLUDE>(.*)<\/INCLUDE>/) {
680             if ($in_section) {
681                 $section_includes = $1;
682             } else {
683                 if (defined $DEFAULT_INCLUDES) {
684                     &LogWarning ($file, $., "Default <INCLUDE> being overridden by command line option.");
685                 }
686                 else {
687                     $includes = $1;
688                 }
689             }
691         } elsif (m/^<\/SECTION>/) {
692             @TRACE@("End of section: $title\n");
693             if ($num_symbols > 0) {
694                 # collect documents
695                 $book_bottom .= "    <xi:include href=\"xml/$filename.xml\"/>\n";
697                 if (defined ($SourceSymbolDocs{"$TMPL_DIR/$filename:Include"})) {
698                     if ($section_includes) {
699                         &LogWarning ($file, $., "Section <INCLUDE> being overridden by inline comments.");
700                     }
701                     $section_includes = $SourceSymbolDocs{"$TMPL_DIR/$filename:Include"};
702                 }
703                 if ($section_includes eq "") {
704                     $section_includes = $includes;
705                 }
707                  $signals_synop =~ s/^\n*//g;
708                  $signals_synop =~ s/\n+$/\n/g;
709                 if ($signals_synop ne '') {
710                     $signals_synop = <<EOF;
711 <refsect1 id="$section_id.signals" role="signal_proto">
712 <title role="signal_proto.title">Signals</title>
713 <informaltable frame="none">
714 <tgroup cols="3">
715 <colspec colname="signals_return" colwidth="150px"/>
716 <colspec colname="signals_name" colwidth="300px"/>
717 <colspec colname="signals_flags" colwidth="200px"/>
718 <tbody>
719 ${signals_synop}
720 </tbody>
721 </tgroup>
722 </informaltable>
723 </refsect1>
725                      $signals_desc = TrimTextBlock($signals_desc);
726                     $signals_desc  = <<EOF;
727 <refsect1 id="$section_id.signal-details" role="signals">
728 <title role="signals.title">Signal Details</title>
729 $signals_desc
730 </refsect1>
732                 }
734                 $args_synop =~ s/^\n*//g;
735                 $args_synop =~ s/\n+$/\n/g;
736                 if ($args_synop ne '') {
737                     $args_synop = <<EOF;
738 <refsect1 id="$section_id.properties" role="properties">
739 <title role="properties.title">Properties</title>
740 <informaltable frame="none">
741 <tgroup cols="3">
742 <colspec colname="properties_type" colwidth="150px"/>
743 <colspec colname="properties_name" colwidth="300px"/>
744 <colspec colname="properties_flags" colwidth="200px"/>
745 <tbody>
746 ${args_synop}
747 </tbody>
748 </tgroup>
749 </informaltable>
750 </refsect1>
752                      $args_desc = TrimTextBlock($args_desc);
753                     $args_desc  = <<EOF;
754 <refsect1 id="$section_id.property-details" role="property_details">
755 <title role="property_details.title">Property Details</title>
756 $args_desc
757 </refsect1>
759                 }
761                 $child_args_synop =~ s/^\n*//g;
762                 $child_args_synop =~ s/\n+$/\n/g;
763                 if ($child_args_synop ne '') {
764                     $args_synop .= <<EOF;
765 <refsect1 id="$section_id.child-properties" role="child_properties">
766 <title role="child_properties.title">Child Properties</title>
767 <informaltable frame="none">
768 <tgroup cols="3">
769 <colspec colname="child_properties_type" colwidth="150px"/>
770 <colspec colname="child_properties_name" colwidth="300px"/>
771 <colspec colname="child_properties_flags" colwidth="200px"/>
772 <tbody>
773 ${child_args_synop}
774 </tbody>
775 </tgroup>
776 </informaltable>
777 </refsect1>
779                      $child_args_desc = TrimTextBlock($child_args_desc);
780                      $args_desc .= <<EOF;
781 <refsect1 id="$section_id.child-property-details" role="child_property_details">
782 <title role="child_property_details.title">Child Property Details</title>
783 $child_args_desc
784 </refsect1>
786                 }
788                 $style_args_synop =~ s/^\n*//g;
789                 $style_args_synop =~ s/\n+$/\n/g;
790                 if ($style_args_synop ne '') {
791                     $args_synop .= <<EOF;
792 <refsect1 id="$section_id.style-properties" role="style_properties">
793 <title role="style_properties.title">Style Properties</title>
794 <informaltable frame="none">
795 <tgroup cols="3">
796 <colspec colname="style_properties_type" colwidth="150px"/>
797 <colspec colname="style_properties_name" colwidth="300px"/>
798 <colspec colname="style_properties_flags" colwidth="200px"/>
799 <tbody>
800 ${style_args_synop}
801 </tbody>
802 </tgroup>
803 </informaltable>
804 </refsect1>
806                      $style_args_desc = TrimTextBlock($style_args_desc);
807                     $args_desc .= <<EOF;
808 <refsect1 id="$section_id.style-property-details" role="style_properties_details">
809 <title role="style_properties_details.title">Style Property Details</title>
810 $style_args_desc
811 </refsect1>
813                 }
815                 $hierarchy_str = &AddTreeLineArt(\@hierarchy);
816                 if ($hierarchy_str ne "") {
817                     $hierarchy_str = <<EOF;
818 <refsect1 id="$section_id.object-hierarchy" role="object_hierarchy">
819 <title role="object_hierarchy.title">Object Hierarchy</title>
820 <screen>$hierarchy_str
821 </screen>
822 </refsect1>
824                 }
826                  $interfaces =~ TrimTextBlock($interfaces);
827                 if ($interfaces ne "") {
828                     $interfaces = <<EOF;
829 <refsect1 id="$section_id.implemented-interfaces" role="impl_interfaces">
830 <title role="impl_interfaces.title">Implemented Interfaces</title>
831 $interfaces
832 </refsect1>
834                 }
836                  $implementations = TrimTextBlock($implementations);
837                 if ($implementations ne "") {
838                     $implementations = <<EOF;
839 <refsect1 id="$section_id.implementations" role="implementations">
840 <title role="implementations.title">Known Implementations</title>
841 $implementations
842 </refsect1>
844                 }
846                  $prerequisites = TrimTextBlock($prerequisites);
847                 if ($prerequisites ne "") {
848                     $prerequisites = <<EOF;
849 <refsect1 id="$section_id.prerequisites" role="prerequisites">
850 <title role="prerequisites.title">Prerequisites</title>
851 $prerequisites
852 </refsect1>
854                 }
856                  $derived = TrimTextBlock($derived);
857                 if ($derived ne "") {
858                     $derived = <<EOF;
859 <refsect1 id="$section_id.derived-interfaces" role="derived_interfaces">
860 <title role="derived_interfaces.title">Known Derived Interfaces</title>
861 $derived
862 </refsect1>
864                 }
866                 $functions_synop =~ s/^\n*//g;
867                 $functions_synop =~ s/\n+$/\n/g;
868                 if ($functions_synop ne '') {
869                   $functions_synop = <<EOF;
870 <refsect1 id="$section_id.functions" role="functions_proto">
871 <title role="functions_proto.title">Functions</title>
872 <informaltable pgwide="1" frame="none">
873 <tgroup cols="2">
874 <colspec colname="functions_return" colwidth="150px"/>
875 <colspec colname="functions_name"/>
876 <tbody>
877 ${functions_synop}
878 </tbody>
879 </tgroup>
880 </informaltable>
881 </refsect1>
883                 }
885                 $other_synop =~ s/^\n*//g;
886                 $other_synop =~ s/\n+$/\n/g;
887                 if ($other_synop ne '') {
888                   $other_synop = <<EOF;
889 <refsect1 id="$section_id.other" role="other_proto">
890 <title role="other_proto.title">Types and Values</title>
891 <informaltable role="enum_members_table" pgwide="1" frame="none">
892 <tgroup cols="2">
893 <colspec colname="name" colwidth="150px"/>
894 <colspec colname="description"/>
895 <tbody>
896 ${other_synop}
897 </tbody>
898 </tgroup>
899 </informaltable>
900 </refsect1>
902                 }
904                 my $file_changed = &OutputDBFile ($filename, $title, $section_id,
905                                                     $section_includes,
906                                                     \$functions_synop, \$other_synop,
907                                                     \$functions_details, \$other_details,
908                                                     \$signals_synop, \$signals_desc,
909                                                     \$args_synop, \$args_desc,
910                                                     \$hierarchy_str, \$interfaces,
911                                                     \$implementations,
912                                                     \$prerequisites, \$derived,
913                                                     \@file_objects);
914                 if ($file_changed) {
915                     $changed = 1;
916                 }
917             }
918             $title = "";
919             $section_id = "";
920             $subsection = "";
921             $in_section = 0;
922             $section_includes = "";
923             $functions_synop = "";
924             $other_synop = "";
925             $functions_details = "";
926             $other_details = "";
927             $signals_synop = "";
928             $signals_desc = "";
929             $args_synop = "";
930             $child_args_synop = "";
931             $style_args_synop = "";
932             $args_desc = "";
933             $child_args_desc = "";
934             $style_args_desc = "";
935             $hierarchy_str = "";
936             @hierarchy = ();
937             $interfaces = "";
938             $implementations = "";
939             $prerequisites = "";
940             $derived = "";
942         } elsif (m/^(\S+)/) {
943             my $symbol = $1;
944             @TRACE@("  Symbol: $symbol in subsection: $subsection\n");
946             # check for duplicate entries
947             if (! defined $symbol_def_line{$symbol}) {
948                 my $declaration = $Declarations{$symbol};
949                 if (defined ($declaration)) {
950                     if (&CheckIsObject ($symbol)) {
951                         push @file_objects, $symbol;
952                     }
953                     # We don't want standard macros/functions of GObjects,
954                     # or private declarations.
955                     if ($subsection ne "Standard" && $subsection ne "Private") {
956                         my ($synop, $desc) = &OutputDeclaration ($symbol,
957                                                                  $declaration);
958                         my $type = $DeclarationTypes {$symbol};
959         
960                         if ($type eq 'FUNCTION' || $type eq 'USER_FUNCTION') {
961                           $functions_synop .= $synop;
962                           $functions_details .= $desc;
963                         } elsif ($type eq 'MACRO' && $declaration =~ /$symbol[ ]*\(/) {
964                           $functions_synop .= $synop;
965                           $functions_details .= $desc;
966                         } else {
967                           $other_synop .= $synop;
968                           $other_details .= $desc;
969                         }
970                     }
971                     my ($sig_synop, $sig_desc) = &GetSignals ($symbol);
972                     my ($arg_synop, $child_arg_synop, $style_arg_synop,
973                         $arg_desc, $child_arg_desc, $style_arg_desc) = &GetArgs ($symbol);
974                     my $ifaces = &GetInterfaces ($symbol);
975                     my $impls = &GetImplementations ($symbol);
976                     my $prereqs = &GetPrerequisites ($symbol);
977                     my $der = &GetDerived ($symbol);
978                     @hierarchy = &GetHierarchy ($symbol, \@hierarchy);
980                     $signals_synop .= $sig_synop;
981                     $signals_desc .= $sig_desc;
982                     $args_synop .= $arg_synop;
983                     $child_args_synop .= $child_arg_synop;
984                     $style_args_synop .= $style_arg_synop;
985                     $args_desc .= $arg_desc;
986                     $child_args_desc .= $child_arg_desc;
987                     $style_args_desc .= $style_arg_desc;
988                     $interfaces .= $ifaces;
989                     $implementations .= $impls;
990                     $prerequisites .= $prereqs;
991                     $derived .= $der;
993                     # Note that the declaration has been output.
994                     $DeclarationOutput{$symbol} = 1;
995                 } elsif ($subsection ne "Standard" && $subsection ne "Private") {
996                     $UndeclaredSymbols{$symbol} = 1;
997                     &LogWarning ($file, $., "No declaration found for $symbol.");
998                 }
999                 $num_symbols++;
1000                 $symbol_def_line{$symbol}=$.;
1002                 if ($section_id eq "") {
1003                     if($title eq "" && $filename eq "") {
1004                         &LogWarning ($file, $., "Section has no title and no file.");
1005                     }
1006                     # FIXME: one of those would be enough
1007                     # filename should be an internal detail for gtk-doc
1008                     if ($title eq "") {
1009                         $title = $filename;
1010                     } elsif ($filename eq "") {
1011                         $filename = $title;
1012                     }
1013                     $filename =~ s/\s/_/g;
1015                     $section_id = $SourceSymbolDocs{"$TMPL_DIR/$filename:Section_Id"};
1016                     if (defined ($section_id) && $section_id !~ m/^\s*$/) {
1017                         # Remove trailing blanks and use as is
1018                         $section_id =~ s/\s+$//;
1019                     } elsif (&CheckIsObject ($title)) {
1020                         # GObjects use their class name as the ID.
1021                         $section_id = &CreateValidSGMLID ($title);
1022                     } else {
1023                         $section_id = &CreateValidSGMLID ("$MODULE-$title");
1024                     }
1025                 }
1026                 $SymbolSection{$symbol}=$title;
1027                 $SymbolSectionId{$symbol}=$section_id;
1028             }
1029             else {
1030                 &LogWarning ($file, $., "Double symbol entry for $symbol. ".
1031                     "Previous occurrence on line ".$symbol_def_line{$symbol}.".");
1032             }
1033         }
1034     }
1035     close (INPUT);
1037     &OutputMissingDocumentation;
1038     &OutputUndeclaredSymbols;
1039     &OutputUnusedSymbols;
1041     if ($OUTPUT_ALL_SYMBOLS) {
1042         &OutputAllSymbols;
1043     }
1044     if ($OUTPUT_SYMBOLS_WITHOUT_SINCE) {
1045         &OutputSymbolsWithoutSince;
1046     }
1048     for $filename (split (' ', $EXPAND_CONTENT_FILES)) {
1049         my $file_changed = &OutputExtraFile ($filename);
1050         if ($file_changed) {
1051             $changed = 1;
1052         }
1053     }
1055     &OutputBook ($book_top, $book_bottom);
1057     return $changed;
1060 #############################################################################
1061 # Function    : OutputIndex
1062 # Description : This writes an indexlist that can be included into the main-
1063 #               document into an <index> tag.
1064 #############################################################################
1066 sub OutputIndex {
1067     my ($basename, $apiindexref ) = @_;
1068     my %apiindex = %{$apiindexref};
1069     my $old_index = "$DB_OUTPUT_DIR/$basename.xml";
1070     my $new_index = "$DB_OUTPUT_DIR/$basename.new";
1071     my $lastletter = " ";
1072     my $divopen = 0;
1073     my $symbol;
1074     my $short_symbol;
1076     open (OUTPUT, ">$new_index")
1077         || die "Can't create $new_index";
1079     print (OUTPUT &MakeDocHeader ("indexdiv")."\n<indexdiv id=\"$basename\">\n");
1081     @TRACE@("generate $basename index (".%apiindex." entries)\n");
1083     # do a case insensitive sort while chopping off the prefix
1084     foreach my $hash (
1085         sort { $$a{criteria} cmp $$b{criteria} or $$a{original} cmp $$b{original} }
1086         map { my $x = uc($_); $x =~ s/^$NAME_SPACE\_?(.*)/$1/i; { criteria => $x, original => $_, short => $1 } }
1087         keys %apiindex) {
1089         $symbol = $$hash{original};
1090         if (defined($$hash{short})) {
1091             $short_symbol = $$hash{short};
1092         } else {
1093             $short_symbol = $symbol;
1094         }
1096         # generate a short symbol description
1097         my $symbol_desc = "";
1098         my $symbol_section = "";
1099         my $symbol_section_id = "";
1100         my $symbol_type = "";
1101         if (defined($DeclarationTypes{$symbol})) {
1102           $symbol_type = lc($DeclarationTypes{$symbol});
1103         }
1104         if ($symbol_type eq "") {
1105             @TRACE@("trying symbol $symbol\n");
1106             if ($symbol =~ m/(.*)::(.*)/) {
1107                 my $oname = $1;
1108                 my $osym = $2;
1109                 my $i;
1110                 @TRACE@("  trying object signal ${oname}:$osym in ".$#SignalNames." signals\n");
1111                 for ($i = 0; $i <= $#SignalNames; $i++) {
1112                     if ($SignalNames[$i] eq $osym) {
1113                         $symbol_type = "object signal";
1114                         if (defined($SymbolSection{$oname})) {
1115                            $symbol_section = $SymbolSection{$oname};
1116                            $symbol_section_id = $SymbolSectionId{$oname};
1117                         }
1118                         last;
1119                     }
1120                 }
1121             } elsif ($symbol =~ m/(.*):(.*)/) {
1122                 my $oname = $1;
1123                 my $osym = $2;
1124                 my $i;
1125                 @TRACE@("  trying object property ${oname}::$osym in ".$#ArgNames." properties\n");
1126                 for ($i = 0; $i <= $#ArgNames; $i++) {
1127                     @TRACE@("    ".$ArgNames[$i]."\n");
1128                     if ($ArgNames[$i] eq $osym) {
1129                         $symbol_type = "object property";
1130                         if (defined($SymbolSection{$oname})) {
1131                            $symbol_section = $SymbolSection{$oname};
1132                            $symbol_section_id = $SymbolSectionId{$oname};
1133                         }
1134                         last;
1135                     }
1136                 }
1137             }
1138         } else {
1139            if (defined($SymbolSection{$symbol})) {
1140                $symbol_section = $SymbolSection{$symbol};
1141                $symbol_section_id = $SymbolSectionId{$symbol};
1142            }
1143         }
1144         if ($symbol_type ne "") {
1145            $symbol_desc=", $symbol_type";
1146            if ($symbol_section ne "") {
1147                $symbol_desc.=" in <link linkend=\"$symbol_section_id\">$symbol_section</link>";
1148                #$symbol_desc.=" in ". &ExpandAbbreviations($symbol, "#$symbol_section");
1149            }
1150         }
1152         my $curletter = uc(substr($short_symbol,0,1));
1153         my $id = $apiindex{$symbol};
1155         @TRACE@("  add symbol $symbol with $id to index in section $curletter\n");
1157         if ($curletter ne $lastletter) {
1158             $lastletter = $curletter;
1160             if ($divopen == 1) {
1161                 print (OUTPUT "</indexdiv>\n");
1162             }
1163             print (OUTPUT "<indexdiv><title>$curletter</title>\n");
1164             $divopen = 1;
1165         }
1167         print (OUTPUT <<EOF);
1168 <indexentry><primaryie linkends="$id"><link linkend="$id">$symbol</link>$symbol_desc</primaryie></indexentry>
1170     }
1172     if ($divopen == 1) {
1173         print (OUTPUT "</indexdiv>\n");
1174     }
1175     print (OUTPUT "</indexdiv>\n");
1176     close (OUTPUT);
1178     &UpdateFileIfChanged ($old_index, $new_index, 0);
1182 #############################################################################
1183 # Function    : OutputIndexFull
1184 # Description : This writes the full api indexlist that can be included into the
1185 #               main document into an <index> tag.
1186 #############################################################################
1188 sub OutputIndexFull {
1189     &OutputIndex ("api-index-full", \%IndexEntriesFull);
1193 #############################################################################
1194 # Function    : OutputDeprecatedIndex
1195 # Description : This writes the deprecated api indexlist that can be included
1196 #               into the main document into an <index> tag.
1197 #############################################################################
1199 sub OutputDeprecatedIndex {
1200     &OutputIndex ("api-index-deprecated", \%IndexEntriesDeprecated);
1204 #############################################################################
1205 # Function    : OutputSinceIndexes
1206 # Description : This writes the 'since' api indexlists that can be included into
1207 #               the main document into an <index> tag.
1208 #############################################################################
1210 sub OutputSinceIndexes {
1211     my @sinces = keys %{{ map { $_ => 1 } values %Since }};
1213     foreach my $version (@sinces) {
1214         @TRACE@("Since : [$version]\n");
1215         # TODO make filtered hash
1216         #my %index = grep { $Since{$_} eq $version } %IndexEntriesSince;
1217         my %index = map { $_ => $IndexEntriesSince{$_} } grep { $Since{$_} eq $version } keys %IndexEntriesSince;
1219         &OutputIndex ("api-index-$version", \%index);
1220     }
1223 #############################################################################
1224 # Function    : OutputAnnotationGlossary
1225 # Description : This writes a glossary of the used annotation terms into a
1226 #               separate glossary file that can be included into the main
1227 #               document.
1228 #############################################################################
1230 sub OutputAnnotationGlossary {
1231     my $old_glossary = "$DB_OUTPUT_DIR/annotation-glossary.xml";
1232     my $new_glossary = "$DB_OUTPUT_DIR/annotation-glossary.new";
1233     my $lastletter = " ";
1234     my $divopen = 0;
1236     # if there are no annotations used return
1237     return if (! keys(%AnnotationsUsed));
1239     # add acronyms that are referenced from acronym text
1240 rerun:
1241     foreach my $annotation (keys(%AnnotationsUsed)) {
1242         if(defined($AnnotationDefinition{$annotation})) {
1243             if($AnnotationDefinition{$annotation} =~ m/<acronym>([\w ]+)<\/acronym>/) {
1244                 if (!exists($AnnotationsUsed{$1})) {
1245                     $AnnotationsUsed{$1} = 1;
1246                     goto rerun;
1247                 }
1248             }
1249         }
1250     }
1252     open (OUTPUT, ">$new_glossary")
1253         || die "Can't create $new_glossary";
1255     print (OUTPUT  <<EOF);
1256 ${\( MakeDocHeader ("glossary") )}
1257 <glossary id="annotation-glossary">
1258   <title>Annotation Glossary</title>
1261     foreach my $annotation (sort({lc $a cmp lc $b} keys(%AnnotationsUsed))) {
1262         if(defined($AnnotationDefinition{$annotation})) {
1263             my $def = $AnnotationDefinition{$annotation};
1264             my $curletter = uc(substr($annotation,0,1));
1266             if ($curletter ne $lastletter) {
1267                 $lastletter = $curletter;
1269                 if ($divopen == 1) {
1270                     print (OUTPUT "</glossdiv>\n");
1271                 }
1272                 print (OUTPUT "<glossdiv><title>$curletter</title>\n");
1273                 $divopen = 1;
1274             }
1275             print (OUTPUT <<EOF);
1276     <glossentry>
1277       <glossterm><anchor id="annotation-glossterm-$annotation"/>$annotation</glossterm>
1278       <glossdef>
1279         <para>$def</para>
1280       </glossdef>
1281     </glossentry>
1283         }
1284     }
1286     if ($divopen == 1) {
1287         print (OUTPUT "</glossdiv>\n");
1288     }
1289     print (OUTPUT "</glossary>\n");
1290     close (OUTPUT);
1292     &UpdateFileIfChanged ($old_glossary, $new_glossary, 0);
1295 #############################################################################
1296 # Function    : ReadKnownSymbols
1297 # Description : This collects the names of non-private symbols from the
1298 #               $MODULE-sections.txt file.
1299 # Arguments   : $file - the $MODULE-sections.txt file which contains all of
1300 #                the functions/macros/structs etc. being documented, organised
1301 #                into sections and subsections.
1302 #############################################################################
1304 sub ReadKnownSymbols {
1305     my ($file) = @_;
1307     my $subsection = "";
1309     @TRACE@("Reading: $file\n");
1310     open (INPUT, $file)
1311         || die "Can't open $file: $!";
1313     while (<INPUT>) {
1314         if (m/^#/) {
1315             next;
1317         } elsif (m/^<SECTION>/) {
1318             $subsection = "";
1320         } elsif (m/^<SUBSECTION\s*(.*)>/i) {
1321             $subsection = $1;
1323         } elsif (m/^<SUBSECTION>/) {
1324             next;
1326         } elsif (m/^<TITLE>(.*)<\/TITLE>/) {
1327             next;
1329         } elsif (m/^<FILE>(.*)<\/FILE>/) {
1330             $KnownSymbols{"$TMPL_DIR/$1:Long_Description"} = 1;
1331             $KnownSymbols{"$TMPL_DIR/$1:Short_Description"} = 1;
1332             next;
1334         } elsif (m/^<INCLUDE>(.*)<\/INCLUDE>/) {
1335             next;
1337         } elsif (m/^<\/SECTION>/) {
1338             next;
1340         } elsif (m/^(\S+)/) {
1341             my $symbol = $1;
1343             if ($subsection ne "Standard" && $subsection ne "Private") {
1344                 $KnownSymbols{$symbol} = 1;
1345             }
1346             else {
1347                 $KnownSymbols{$symbol} = 0;
1348             }
1349         }
1350     }
1351     close (INPUT);
1355 #############################################################################
1356 # Function    : OutputDeclaration
1357 # Description : Returns the synopsis and detailed description DocBook
1358 #                describing one function/macro etc.
1359 # Arguments   : $symbol - the name of the function/macro begin described.
1360 #                $declaration - the declaration of the function/macro.
1361 #############################################################################
1363 sub OutputDeclaration {
1364     my ($symbol, $declaration) = @_;
1366     my $type = $DeclarationTypes {$symbol};
1367     if ($type eq 'MACRO') {
1368         return &OutputMacro ($symbol, $declaration);
1369     } elsif ($type eq 'TYPEDEF') {
1370         return &OutputTypedef ($symbol, $declaration);
1371     } elsif ($type eq 'STRUCT') {
1372         return &OutputStruct ($symbol, $declaration);
1373     } elsif ($type eq 'ENUM') {
1374         return &OutputEnum ($symbol, $declaration);
1375     } elsif ($type eq 'UNION') {
1376         return &OutputUnion ($symbol, $declaration);
1377     } elsif ($type eq 'VARIABLE') {
1378         return &OutputVariable ($symbol, $declaration);
1379     } elsif ($type eq 'FUNCTION') {
1380         return &OutputFunction ($symbol, $declaration, $type);
1381     } elsif ($type eq 'USER_FUNCTION') {
1382         return &OutputFunction ($symbol, $declaration, $type);
1383     } else {
1384         die "Unknown symbol type";
1385     }
1389 #############################################################################
1390 # Function    : OutputSymbolTraits
1391 # Description : Returns the Since and StabilityLevel paragraphs for a symbol.
1392 # Arguments   : $symbol - the name of the function/macro begin described.
1393 #############################################################################
1395 sub OutputSymbolTraits {
1396     my ($symbol) = @_;
1397     my $desc = "";
1399     if (exists $Since{$symbol}) {
1400         my $link_id = "api-index-".$Since{$symbol};
1401         $desc .= "<para role=\"since\">Since: <link linkend=\"$link_id\">$Since{$symbol}</link></para>";
1402     }
1403     if (exists $StabilityLevel{$symbol}) {
1404         my $stability = $StabilityLevel{$symbol};
1405         $AnnotationsUsed{$stability} = 1;
1406         $desc .= "<para role=\"stability\">Stability Level: <acronym>$stability</acronym></para>";
1407     }
1408     return $desc;
1411 #############################################################################
1412 # Function    : Output{Symbol,Section}ExtraLinks
1413 # Description : Returns extralinks for the symbol (if enabled).
1414 # Arguments   : $symbol - the name of the function/macro begin described.
1415 #############################################################################
1417 sub uri_escape {
1418     my $text = $_[0];
1419     return undef unless defined $text;
1421     # Build a char to hex map
1422     my %escapes = ();
1423     for (0..255) {
1424             $escapes{chr($_)} = sprintf("%%%02X", $_);
1425     }
1427     # Default unsafe characters.  RFC 2732 ^(uric - reserved)
1428     $text =~ s/([^A-Za-z0-9\-_.!~*'()])/$escapes{$1}/g;
1430     return $text;
1433 sub OutputSymbolExtraLinks {
1434     my ($symbol) = @_;
1435     my $desc = "";
1437     if (0) { # NEW FEATURE: needs configurability
1438     my $sstr = &uri_escape($symbol);
1439     my $mstr = &uri_escape($MODULE);
1440     $desc .= <<EOF;
1441 <ulink role="extralinks" url="http://www.google.com/codesearch?q=$sstr">code search</ulink>
1442 <ulink role="extralinks" url="http://library.gnome.org/edit?module=$mstr&amp;symbol=$sstr">edit documentation</ulink>
1444     }
1445     return $desc;
1448 sub OutputSectionExtraLinks {
1449     my ($symbol,$docsymbol) = @_;
1450     my $desc = "";
1452     if (0) { # NEW FEATURE: needs configurability
1453     my $sstr = &uri_escape($symbol);
1454     my $mstr = &uri_escape($MODULE);
1455     my $dsstr = &uri_escape($docsymbol);
1456     $desc .= <<EOF;
1457 <ulink role="extralinks" url="http://www.google.com/codesearch?q=$sstr">code search</ulink>
1458 <ulink role="extralinks" url="http://library.gnome.org/edit?module=$mstr&amp;symbol=$dsstr">edit documentation</ulink>
1460     }
1461     return $desc;
1465 #############################################################################
1466 # Function    : OutputMacro
1467 # Description : Returns the synopsis and detailed description of a macro.
1468 # Arguments   : $symbol - the macro.
1469 #                $declaration - the declaration of the macro.
1470 #############################################################################
1472 sub OutputMacro {
1473     my ($symbol, $declaration) = @_;
1474     my $id = &CreateValidSGMLID ($symbol);
1475     my $condition = &MakeConditionDescription ($symbol);
1476     my $synop = "<row><entry role=\"define_keyword\">#define</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link>";
1477     my $desc;
1479     my @fields = ParseMacroDeclaration($declaration, \&CreateValidSGML);
1480     my $title = $symbol . (@fields ? "()" : "");
1482     $desc = "<refsect2 id=\"$id\" role=\"macro\"$condition>\n<title>$title</title>\n";
1483     $desc .= MakeIndexterms($symbol, $id);
1484     $desc .= "\n";
1485     $desc .= OutputSymbolExtraLinks($symbol);
1487     if (@fields) {
1488         $synop .= "<phrase role=\"c_punctuation\">()</phrase>";
1489     }
1490     $synop .= "</entry></row>\n";
1492     # Don't output the macro definition if is is a conditional macro or it
1493     # looks like a function, i.e. starts with "g_" or "_?gnome_", or it is
1494     # longer than 2 lines, otherwise we get lots of complicated macros like
1495     # g_assert.
1496     if (!defined ($DeclarationConditional{$symbol}) && ($symbol !~ m/^g_/)
1497         && ($symbol !~ m/^_?gnome_/) && (($declaration =~ tr/\n//) < 2)) {
1498         my $decl_out = &CreateValidSGML ($declaration);
1499         $desc .= "<programlisting language=\"C\">$decl_out</programlisting>\n";
1500     } else {
1501         $desc .= "<programlisting language=\"C\">" . &MakeReturnField("#define") . "$symbol";
1502         if ($declaration =~ m/^\s*#\s*define\s+\w+(\([^\)]*\))/) {
1503             my $args = $1;
1504             my $pad = ' ' x ($RETURN_TYPE_FIELD_WIDTH - length ("#define "));
1505             # Align each line so that if should all line up OK.
1506             $args =~ s/\n/\n$pad/gm;
1507             $desc .= &CreateValidSGML ($args);
1508         }
1509         $desc .= "</programlisting>\n";
1510     }
1512     $desc .= &MakeDeprecationNote($symbol);
1514     my $parameters = &OutputParamDescriptions ("MACRO", $symbol, @fields);
1516     if (defined ($SymbolDocs{$symbol})) {
1517         my $symbol_docs = &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1518         $desc .= $symbol_docs;
1519     }
1521     $desc .= $parameters;
1522     $desc .= OutputSymbolTraits ($symbol);
1523     $desc .= "</refsect2>\n";
1524     return ($synop, $desc);
1528 #############################################################################
1529 # Function    : OutputTypedef
1530 # Description : Returns the synopsis and detailed description of a typedef.
1531 # Arguments   : $symbol - the typedef.
1532 #                $declaration - the declaration of the typedef,
1533 #                  e.g. 'typedef unsigned int guint;'
1534 #############################################################################
1536 sub OutputTypedef {
1537     my ($symbol, $declaration) = @_;
1538     my $id = &CreateValidSGMLID ($symbol);
1539     my $condition = &MakeConditionDescription ($symbol);
1540     my $desc = "<refsect2 id=\"$id\" role=\"typedef\"$condition>\n<title>$symbol</title>\n";
1541     my $synop = "<row><entry role=\"typedef_keyword\">typedef</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
1543     $desc .= MakeIndexterms($symbol, $id);
1544     $desc .= "\n";
1545     $desc .= OutputSymbolExtraLinks($symbol);
1547     if (!defined ($DeclarationConditional{$symbol})) {
1548         my $decl_out = &CreateValidSGML ($declaration);
1549         $desc .= "<programlisting language=\"C\">$decl_out</programlisting>\n";
1550     }
1552     $desc .= &MakeDeprecationNote($symbol);
1554     if (defined ($SymbolDocs{$symbol})) {
1555         $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1556     }
1557     $desc .= OutputSymbolTraits ($symbol);
1558     $desc .= "</refsect2>\n";
1559     return ($synop, $desc);
1563 #############################################################################
1564 # Function    : OutputStruct
1565 # Description : Returns the synopsis and detailed description of a struct.
1566 #                We check if it is a object struct, and if so we only output
1567 #                parts of it that are noted as public fields.
1568 #                We also use a different IDs for object structs, since the
1569 #                original ID is used for the entire RefEntry.
1570 # Arguments   : $symbol - the struct.
1571 #                $declaration - the declaration of the struct.
1572 #############################################################################
1574 sub OutputStruct {
1575     my ($symbol, $declaration) = @_;
1577     my $is_gtype = 0;
1578     my $default_to_public = 1;
1579     if (&CheckIsObject ($symbol)) {
1580         @TRACE@("Found struct gtype: $symbol\n");
1581         $is_gtype = 1;
1582         $default_to_public = $ObjectRoots{$symbol} eq 'GBoxed';
1583     }
1585     my $id;
1586     my $condition;
1587     if ($is_gtype) {
1588         $id = &CreateValidSGMLID ($symbol . "_struct");
1589         $condition = &MakeConditionDescription ($symbol . "_struct");
1590     } else {
1591         $id = &CreateValidSGMLID ($symbol);
1592         $condition = &MakeConditionDescription ($symbol);
1593     }
1595     # Determine if it is a simple struct or it also has a typedef.
1596     my $has_typedef = 0;
1597     if ($StructHasTypedef{$symbol} || $declaration =~ m/^\s*typedef\s+/) {
1598       $has_typedef = 1;
1599     }
1601     my $type_output;
1602     my $desc;
1603     if ($has_typedef) {
1604         # For structs with typedefs we just output the struct name.
1605         $type_output = "";
1606         $desc = "<refsect2 id=\"$id\" role=\"struct\"$condition>\n<title>$symbol</title>\n";
1607     } else {
1608         $type_output = "struct";
1609         $desc = "<refsect2 id=\"$id\" role=\"struct\"$condition>\n<title>struct $symbol</title>\n";
1610     }
1611     my $synop = "<row><entry role=\"datatype_keyword\">${type_output}</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
1613     $desc .= MakeIndexterms($symbol, $id);
1614     $desc .= "\n";
1615     $desc .= OutputSymbolExtraLinks($symbol);
1617     # Form a pretty-printed, private-data-removed form of the declaration
1619     my $decl_out = "";
1620     if ($declaration =~ m/^\s*$/) {
1621         @TRACE@("Found opaque struct: $symbol\n");
1622         $decl_out = "typedef struct _$symbol $symbol;";
1623     } elsif ($declaration =~ m/^\s*struct\s+\w+\s*;\s*$/) {
1624         @TRACE@("Found opaque struct: $symbol\n");
1625         $decl_out = "struct $symbol;";
1626     } else {
1627         my $public = $default_to_public;
1628         my $new_declaration = "";
1629         my $decl_line;
1630         my $decl = $declaration;
1632         if ($decl =~ m/^\s*(typedef\s+)?struct\s*\w*\s*(?:\/\*.*\*\/)?\s*{(.*)}\s*\w*\s*;\s*$/s) {
1633             my $struct_contents = $2;
1635             foreach $decl_line (split (/\n/, $struct_contents)) {
1636                 @TRACE@("Struct line: $decl_line\n");
1637                 if ($decl_line =~ m%/\*\s*<\s*public\s*>\s*\*/%) {
1638                     $public = 1;
1639                 } elsif ($decl_line =~ m%/\*\s*<\s*(private|protected)\s*>\s*\*/%) {
1640                     $public = 0;
1641                 } elsif ($public) {
1642                     $new_declaration .= $decl_line . "\n";
1643                 }
1644             }
1646             if ($new_declaration) {
1647                 # Strip any blank lines off the ends.
1648                 $new_declaration =~ s/^\s*\n//;
1649                 $new_declaration =~ s/\n\s*$/\n/;
1651                 if ($has_typedef) {
1652                     $decl_out = "typedef struct {\n" . $new_declaration
1653                       . "} $symbol;\n";
1654                 } else {
1655                     $decl_out = "struct $symbol {\n" . $new_declaration
1656                       . "};\n";
1657                 }
1658             }
1659         } else {
1660             &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1661                 "Couldn't parse struct:\n$declaration");
1662         }
1664         # If we couldn't parse the struct or it was all private, output an
1665         # empty struct declaration.
1666         if ($decl_out eq "") {
1667             if ($has_typedef) {
1668                 $decl_out = "typedef struct _$symbol $symbol;";
1669             } else {
1670                 $decl_out = "struct $symbol;";
1671             }
1672         }
1673     }
1675     $decl_out = &CreateValidSGML ($decl_out);
1676     $desc .= "<programlisting language=\"C\">$decl_out</programlisting>\n";
1678     $desc .= &MakeDeprecationNote($symbol);
1680     if (defined ($SymbolDocs{$symbol})) {
1681         $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1682     }
1684     # Create a table of fields and descriptions
1686     # FIXME: Inserting &#160's into the produced type declarations here would
1687     #        improve the output in most situations ... except for function
1688     #        members of structs!
1689     my @fields = ParseStructDeclaration($declaration, !$default_to_public,
1690                                         0, \&MakeXRef,
1691                                         sub {
1692                                             "<structfield id=\"".&CreateValidSGMLID("$id.$_[0]")."\">$_[0]</structfield>";
1693                                         });
1694     my $params = $SymbolParams{$symbol};
1696     # If no parameters are filled in, we don't generate the description
1697     # table, for backwards compatibility.
1699     my $found = 0;
1700     if (defined $params) {
1701         for (my $i = 1; $i <= $#$params; $i += $PARAM_FIELD_COUNT) {
1702             if ($params->[$i] =~ /\S/) {
1703                 $found = 1;
1704                 last;
1705             }
1706         }
1707     }
1709     if ($found) {
1710         my %field_descrs = @$params;
1711         my $missing_parameters = "";
1712         my $unused_parameters = "";
1714         $desc .= <<EOF;
1715 <refsect3 role="struct_members">\n<title>Members</title>
1716 <informaltable role="struct_members_table" pgwide="1" frame="none">
1717 <tgroup cols="3">
1718 <colspec colname="struct_members_name" colwidth="300px"/>
1719 <colspec colname="struct_members_description"/>
1720 <colspec colname="struct_members_annotations" colwidth="200px"/>
1721 <tbody>
1724         while (@fields) {
1725             my $field_name = shift @fields;
1726             my $text = shift @fields;
1727             my $field_descr = $field_descrs{$field_name};
1728             my $param_annotations = "";
1730             $desc .= "<row role=\"member\"><entry role=\"struct_member_name\"><para>$text</para></entry>\n";
1731             if (defined $field_descr) {
1732                 ($field_descr,$param_annotations) = &ExpandAnnotation($symbol, $field_descr);
1733                 $field_descr = &ConvertMarkDown($symbol, $field_descr);
1734                 # trim
1735                 $field_descr =~ s/^(\s|\n)+//msg;
1736                 $field_descr =~ s/(\s|\n)+$//msg;
1737                 $desc .= "<entry role=\"struct_member_description\">$field_descr</entry>\n<entry role=\"struct_member_annotations\">$param_annotations</entry>\n";
1738                 delete $field_descrs{$field_name};
1739             } else {
1740                 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1741                     "Field description for $symbol"."::"."$field_name is missing in source code comment block.");
1742                 if ($missing_parameters ne "") {
1743                   $missing_parameters .= ", ".$field_name;
1744                 } else {
1745                     $missing_parameters = $field_name;
1746                 }
1747                 $desc .= "<entry /><entry />\n";
1748             }
1749             $desc .= "</row>\n";
1750         }
1751         $desc .= "</tbody></tgroup></informaltable>\n</refsect3>\n";
1752         foreach my $field_name (keys %field_descrs) {
1753             # Documenting those standard fields is not required anymore, but
1754             # we don't want to warn if they are documented anyway.
1755             if ($field_name =~ /(g_iface|parent_instance|parent_class)/) {
1756                 next;
1757             }
1758             &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1759                 "Field description for $symbol"."::"."$field_name is not used from source code comment block.");
1760             if ($unused_parameters ne "") {
1761               $unused_parameters .= ", ".$field_name;
1762             } else {
1763                $unused_parameters = $field_name;
1764             }
1765         }
1767         # remember missing/unused parameters (needed in tmpl-free build)
1768         if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) {
1769             $AllIncompleteSymbols{$symbol}=$missing_parameters;
1770         }
1771         if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) {
1772             $AllUnusedSymbols{$symbol}=$unused_parameters;
1773         }
1774     }
1775     else {
1776         if (scalar(@fields) > 0) {
1777             if (! exists ($AllIncompleteSymbols{$symbol})) {
1778                 $AllIncompleteSymbols{$symbol}="<items>";
1779                 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1780                     "Field descriptions for struct $symbol are missing in source code comment block.");
1781                 @TRACE@("Remaining structs fields: ".@fields.":".join(',',@fields)."\n");
1782             }
1783         }
1784     }
1786     $desc .= OutputSymbolTraits ($symbol);
1787     $desc .= "</refsect2>\n";
1788     return ($synop, $desc);
1792 #############################################################################
1793 # Function    : OutputUnion
1794 # Description : Returns the synopsis and detailed description of a union.
1795 # Arguments   : $symbol - the union.
1796 #                $declaration - the declaration of the union.
1797 #############################################################################
1799 sub OutputUnion {
1800     my ($symbol, $declaration) = @_;
1802     my $is_gtype = 0;
1803     if (&CheckIsObject ($symbol)) {
1804         @TRACE@("Found union gtype: $symbol\n");
1805         $is_gtype = 1;
1806     }
1808     my $id;
1809     my $condition;
1810     if ($is_gtype) {
1811         $id = &CreateValidSGMLID ($symbol . "_union");
1812         $condition = &MakeConditionDescription ($symbol . "_union");
1813     } else {
1814         $id = &CreateValidSGMLID ($symbol);
1815         $condition = &MakeConditionDescription ($symbol);
1816     }
1818     # Determine if it is a simple struct or it also has a typedef.
1819     my $has_typedef = 0;
1820     if ($StructHasTypedef{$symbol} || $declaration =~ m/^\s*typedef\s+/) {
1821       $has_typedef = 1;
1822     }
1824     my $type_output;
1825     my $desc;
1826     if ($has_typedef) {
1827         # For unions with typedefs we just output the union name.
1828         $type_output = "";
1829         $desc = "<refsect2 id=\"$id\" role=\"union\"$condition>\n<title>$symbol</title>\n";
1830     } else {
1831         $type_output = "union";
1832         $desc = "<refsect2 id=\"$id\" role=\"union\"$condition>\n<title>union $symbol</title>\n";
1833     }
1834     my $synop = "<row><entry role=\"datatype_keyword\">${type_output}</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
1836     $desc .= MakeIndexterms($symbol, $id);
1837     $desc .= "\n";
1838     $desc .= OutputSymbolExtraLinks($symbol);
1839     $desc .= &MakeDeprecationNote($symbol);
1841     if (defined ($SymbolDocs{$symbol})) {
1842         $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1843     }
1845     # Create a table of fields and descriptions
1847     # FIXME: Inserting &#160's into the produced type declarations here would
1848     #        improve the output in most situations ... except for function
1849     #        members of structs!
1850     my @fields = ParseStructDeclaration($declaration, 0,
1851                                         0, \&MakeXRef,
1852                                         sub {
1853                                             "<structfield id=\"".&CreateValidSGMLID("$id.$_[0]")."\">$_[0]</structfield>";
1854                                         });
1855     my $params = $SymbolParams{$symbol};
1857     # If no parameters are filled in, we don't generate the description
1858     # table, for backwards compatibility
1860     my $found = 0;
1861     if (defined $params) {
1862         for (my $i = 1; $i <= $#$params; $i += $PARAM_FIELD_COUNT) {
1863             if ($params->[$i] =~ /\S/) {
1864                 $found = 1;
1865                 last;
1866             }
1867         }
1868     }
1870     if ($found) {
1871         my %field_descrs = @$params;
1872         my $missing_parameters = "";
1873         my $unused_parameters = "";
1875         $desc .= <<EOF;
1876 <refsect3 role="union_members">\n<title>Members</title>
1877 <informaltable role="union_members_table" pgwide="1" frame="none">
1878 <tgroup cols="3">
1879 <colspec colname="union_members_name" colwidth="300px"/>
1880 <colspec colname="union_members_description"/>
1881 <colspec colname="union_members_annotations" colwidth="200px"/>
1882 <tbody>
1885         while (@fields) {
1886             my $field_name = shift @fields;
1887             my $text = shift @fields;
1888             my $field_descr = $field_descrs{$field_name};
1889             my $param_annotations = "";
1891             $desc .= "<row><entry role=\"union_member_name\"><para>$text</para></entry>\n";
1892             if (defined $field_descr) {
1893                 ($field_descr,$param_annotations) = &ExpandAnnotation($symbol, $field_descr);
1894                 $field_descr = &ConvertMarkDown($symbol, $field_descr);
1896                 # trim
1897                 $field_descr =~ s/^(\s|\n)+//msg;
1898                 $field_descr =~ s/(\s|\n)+$//msg;
1899                 $desc .= "<entry role=\"union_member_description\">$field_descr</entry>\n<entry role=\"union_member_annotations\">$param_annotations</entry>\n";
1900                 delete $field_descrs{$field_name};
1901             } else {
1902                 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1903                     "Field description for $symbol"."::"."$field_name is missing in source code comment block.");
1904                 if ($missing_parameters ne "") {
1905                     $missing_parameters .= ", ".$field_name;
1906                 } else {
1907                     $missing_parameters = $field_name;
1908                 }
1909                 $desc .= "<entry /><entry />\n";
1910             }
1911             $desc .= "</row>\n";
1912         }
1913         $desc .= "</tbody></tgroup></informaltable>\n</refsect3>";
1914         foreach my $field_name (keys %field_descrs) {
1915             &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1916                 "Field description for $symbol"."::"."$field_name is not used from source code comment block.");
1917             if ($unused_parameters ne "") {
1918               $unused_parameters .= ", ".$field_name;
1919             } else {
1920                $unused_parameters = $field_name;
1921             }
1922         }
1924         # remember missing/unused parameters (needed in tmpl-free build)
1925         if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) {
1926             $AllIncompleteSymbols{$symbol}=$missing_parameters;
1927         }
1928         if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) {
1929             $AllUnusedSymbols{$symbol}=$unused_parameters;
1930         }
1931     }
1932     else {
1933         if (scalar(@fields) > 0) {
1934             if (! exists ($AllIncompleteSymbols{$symbol})) {
1935                 $AllIncompleteSymbols{$symbol}="<items>";
1936                 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1937                     "Field descriptions for union $symbol are missing in source code comment block.");
1938                 @TRACE@("Remaining union fields: ".@fields.":".join(',',@fields)."\n");
1939             }
1940         }
1941     }
1943     $desc .= OutputSymbolTraits ($symbol);
1944     $desc .= "</refsect2>\n";
1945     return ($synop, $desc);
1949 #############################################################################
1950 # Function    : OutputEnum
1951 # Description : Returns the synopsis and detailed description of a enum.
1952 # Arguments   : $symbol - the enum.
1953 #                $declaration - the declaration of the enum.
1954 #############################################################################
1956 sub OutputEnum {
1957     my ($symbol, $declaration) = @_;
1959     my $is_gtype = 0;
1960     if (&CheckIsObject ($symbol)) {
1961         @TRACE@("Found enum gtype: $symbol\n");
1962         $is_gtype = 1;
1963     }
1965     my $id;
1966     my $condition;
1967     if ($is_gtype) {
1968         $id = &CreateValidSGMLID ($symbol . "_enum");
1969         $condition = &MakeConditionDescription ($symbol . "_enum");
1970     } else {
1971         $id = &CreateValidSGMLID ($symbol);
1972         $condition = &MakeConditionDescription ($symbol);
1973     }
1975     my $synop = "<row><entry role=\"datatype_keyword\">enum</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
1976     my $desc = "<refsect2 id=\"$id\" role=\"enum\"$condition>\n<title>enum $symbol</title>\n";
1978     $desc .= MakeIndexterms($symbol, $id);
1979     $desc .= "\n";
1980     $desc .= OutputSymbolExtraLinks($symbol);
1981     $desc .= &MakeDeprecationNote($symbol);
1983     if (defined ($SymbolDocs{$symbol})) {
1984         $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1985     }
1987     # Create a table of fields and descriptions
1989     my @fields = ParseEnumDeclaration($declaration);
1990     my $params = $SymbolParams{$symbol};
1992     # If nothing at all is documented log a single summary warning at the end.
1993     # Otherwise, warn about each undocumented item.
1995     my $found = 0;
1996     if (defined $params) {
1997         for (my $i = 1; $i <= $#$params; $i += $PARAM_FIELD_COUNT) {
1998             if ($params->[$i] =~ /\S/) {
1999                 $found = 1;
2000                 last;
2001             }
2002         }
2003     }
2005     my %field_descrs = (defined $params ? @$params : ());
2006     my $missing_parameters = "";
2007     my $unused_parameters = "";
2009     $desc .= <<EOF;
2010 <refsect3 role="enum_members">\n<title>Members</title>
2011 <informaltable role="enum_members_table" pgwide="1" frame="none">
2012 <tgroup cols="3">
2013 <colspec colname="enum_members_name" colwidth="300px"/>
2014 <colspec colname="enum_members_description"/>
2015 <colspec colname="enum_members_annotations" colwidth="200px"/>
2016 <tbody>
2019     for my $field_name (@fields) {
2020         my $field_descr = $field_descrs{$field_name};
2021         my $param_annotations = "";
2023         $id = &CreateValidSGMLID ($field_name);
2024         $condition = &MakeConditionDescription ($field_name);
2025         $desc .= "<row role=\"constant\"><entry role=\"enum_member_name\"><para id=\"$id\">$field_name</para></entry>\n";
2026         if (defined $field_descr) {
2027             ($field_descr,$param_annotations) = &ExpandAnnotation($symbol, $field_descr);
2028             $field_descr = &ConvertMarkDown($symbol, $field_descr);
2029             $desc .= "<entry role=\"enum_member_description\">$field_descr</entry>\n<entry role=\"enum_member_annotations\">$param_annotations</entry>\n";
2030             delete $field_descrs{$field_name};
2031         } else {
2032             if ($found) {
2033                 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2034                     "Value description for $symbol"."::"."$field_name is missing in source code comment block.");
2035                 if ($missing_parameters ne "") {
2036                     $missing_parameters .= ", ".$field_name;
2037                 } else {
2038                     $missing_parameters = $field_name;
2039                 }
2040             }
2041             $desc .= "<entry /><entry />\n";
2042         }
2043         $desc .= "</row>\n";
2044     }
2045     $desc .= "</tbody></tgroup></informaltable>\n</refsect3>";
2046     foreach my $field_name (keys %field_descrs) {
2047         &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2048             "Value description for $symbol"."::"."$field_name is not used from source code comment block.");
2049         if ($unused_parameters ne "") {
2050             $unused_parameters .= ", ".$field_name;
2051         } else {
2052             $unused_parameters = $field_name;
2053         }
2054     }
2056     # remember missing/unused parameters (needed in tmpl-free build)
2057     if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) {
2058         $AllIncompleteSymbols{$symbol}=$missing_parameters;
2059     }
2060     if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) {
2061         $AllUnusedSymbols{$symbol}=$unused_parameters;
2062     }
2064     if (!$found) {
2065         if (scalar(@fields) > 0) {
2066             if (! exists ($AllIncompleteSymbols{$symbol})) {
2067                 $AllIncompleteSymbols{$symbol}="<items>";
2068                 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2069                     "Value descriptions for $symbol are missing in source code comment block.");
2070             }
2071         }
2072     }
2074     $desc .= OutputSymbolTraits ($symbol);
2075     $desc .= "</refsect2>\n";
2076     return ($synop, $desc);
2080 #############################################################################
2081 # Function    : OutputVariable
2082 # Description : Returns the synopsis and detailed description of a variable.
2083 # Arguments   : $symbol - the extern'ed variable.
2084 #                $declaration - the declaration of the variable.
2085 #############################################################################
2087 sub OutputVariable {
2088     my ($symbol, $declaration) = @_;
2089     my $id = &CreateValidSGMLID ($symbol);
2090     my $condition = &MakeConditionDescription ($symbol);
2091     
2092     @TRACE@("ouputing variable: '$symbol' '$declaration'");
2094     my $type_output;
2095     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*;/) {
2096         my $mod1 = defined ($1) ? $1 : "";
2097         my $ptr = defined ($3) ? $3 : "";
2098         my $space = defined ($4) ? $4 : "";
2099         my $mod2 = defined ($5) ? $5 : "";
2100         $type_output = "extern $mod1$ptr$space$mod2";
2101     } 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*=/) {
2102         my $mod1 = defined ($1) ? $1 : "";
2103         my $ptr = defined ($3) ? $3 : "";
2104         my $space = defined ($4) ? $4 : "";
2105         my $mod2 = defined ($5) ? $5 : "";
2106         $type_output = "$mod1$ptr$space$mod2";
2107     } else {
2108         $type_output = "extern";
2109     }
2110     my $synop = "<row><entry role=\"variable_type\">${type_output}</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
2112     my $desc = "<refsect2 id=\"$id\" role=\"variable\"$condition>\n<title>$symbol</title>\n";
2114     $desc .= MakeIndexterms($symbol, $id);
2115     $desc .= "\n";
2116     $desc .= OutputSymbolExtraLinks($symbol);
2118     my $decl_out = &CreateValidSGML ($declaration);
2119     $desc .= "<programlisting language=\"C\">$decl_out</programlisting>\n";
2121     $desc .= &MakeDeprecationNote($symbol);
2123     if (defined ($SymbolDocs{$symbol})) {
2124         $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
2125     }
2126     if (defined ($SymbolAnnotations{$symbol})) {
2127         my $param_desc = $SymbolAnnotations{$symbol};
2128         my $param_annotations = "";
2129         ($param_desc,$param_annotations) = &ExpandAnnotation($symbol, $param_desc);
2130         if ($param_annotations ne "") {
2131             $desc .= "\n<para>$param_annotations</para>";
2132         }
2133     }
2135     $desc .= OutputSymbolTraits ($symbol);
2136     $desc .= "</refsect2>\n";
2137     return ($synop, $desc);
2141 #############################################################################
2142 # Function    : OutputFunction
2143 # Description : Returns the synopsis and detailed description of a function.
2144 # Arguments   : $symbol - the function.
2145 #                $declaration - the declaration of the function.
2146 #############################################################################
2148 sub OutputFunction {
2149     my ($symbol, $declaration, $symbol_type) = @_;
2150     my $id = &CreateValidSGMLID ($symbol);
2151     my $condition = &MakeConditionDescription ($symbol);
2153     # Take out the return type     $1                                                                                       $2   $3
2154     $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//;
2155     my $type_modifier = defined($1) ? $1 : "";
2156     my $type = $2;
2157     my $pointer = $3;
2158     # Trim trailing spaces as we are going to pad to $RETURN_TYPE_FIELD_WIDTH below anyway
2159     $pointer =~ s/\s+$//;
2160     my $xref = &MakeXRef ($type, &tagify($type, "returnvalue"));
2161     my $start = "";
2162     #if ($symbol_type eq 'USER_FUNCTION') {
2163     #    $start = "typedef ";
2164     #}
2166     # We output const rather than G_CONST_RETURN.
2167     $type_modifier =~ s/G_CONST_RETURN/const/g;
2168     $pointer =~ s/G_CONST_RETURN/const/g;
2169     $pointer =~ s/^\s+/&#160;/g;
2171     my $ret_type_output;
2172     $ret_type_output = "$start$type_modifier$xref$pointer\n";
2174     my $indent_len;
2175     $indent_len = length ($symbol) + 2;
2176     my $char1 = my $char2 = my $char3 = "";
2177     if ($symbol_type eq 'USER_FUNCTION') {
2178         $indent_len += 3;
2179         $char1 = "<phrase role=\"c_punctuation\">(</phrase>";
2180         $char2 = "*";
2181         $char3 = "<phrase role=\"c_punctuation\">)</phrase>";
2182     }
2184     my ($symbol_output, $symbol_desc_output);
2185     $symbol_output = "$char1<link linkend=\"$id\">$char2$symbol</link>$char3";
2186     if ($indent_len < $MAX_SYMBOL_FIELD_WIDTH) {
2187         $symbol_desc_output = "$char1$char2$symbol$char3 ";
2188     } else {
2189         $indent_len = $MAX_SYMBOL_FIELD_WIDTH - 8;
2190         $symbol_desc_output = "$char1$char2$symbol$char3\n"
2191           . (' ' x ($indent_len - 1));
2192     }
2194     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";
2196     my $desc = "<refsect2 id=\"$id\" role=\"function\"$condition>\n<title>${symbol}&#160;()</title>\n";
2198     $desc .= MakeIndexterms($symbol, $id);
2199     $desc .= "\n";
2200     $desc .= OutputSymbolExtraLinks($symbol);
2202     $desc  .= "<programlisting language=\"C\">${ret_type_output}$symbol_desc_output(";
2204     my @fields = ParseFunctionDeclaration($declaration, \&MakeXRef,
2205                                         sub {
2206                                             &tagify($_[0],"parameter");
2207                                         });
2209     for (my $i = 1; $i <= $#fields; $i += 2) {
2210         my $field_name = $fields[$i];
2212         if ($i == 1) {
2213             $desc  .= "$field_name";
2214         } else {
2215             $desc  .= ",\n"
2216                 . (' ' x $indent_len)
2217                 . "$field_name";
2218         }
2220     }
2222     $desc  .= ");</programlisting>\n";
2224     $desc .= &MakeDeprecationNote($symbol);
2226     if (defined ($SymbolDocs{$symbol})) {
2227         $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
2228     }
2229     if (defined ($SymbolAnnotations{$symbol})) {
2230         my $param_desc = $SymbolAnnotations{$symbol};
2231         my $param_annotations = "";
2232         ($param_desc,$param_annotations) = &ExpandAnnotation($symbol, $param_desc);
2233         if ($param_annotations ne "") {
2234             $desc .= "\n<para>$param_annotations</para>";
2235         }
2236     }
2238     $desc .= &OutputParamDescriptions ("FUNCTION", $symbol, @fields);
2239     $desc .= OutputSymbolTraits ($symbol);
2240     $desc .= "</refsect2>\n";
2241     return ($synop, $desc);
2245 #############################################################################
2246 # Function    : OutputParamDescriptions
2247 # Description : Returns the DocBook output describing the parameters of a
2248 #                function, macro or signal handler.
2249 # Arguments   : $symbol_type - 'FUNCTION', 'MACRO' or 'SIGNAL'. Signal
2250 #                  handlers have an implicit user_data parameter last.
2251 #                $symbol - the name of the function/macro being described.
2252 #               @fields - parsed fields from the declaration, used to determine
2253 #                  undocumented/unused entries
2254 #############################################################################
2256 sub OutputParamDescriptions {
2257     my ($symbol_type, $symbol, @fields) = @_;
2258     my $output = "";
2259     my $params = $SymbolParams{$symbol};
2260     my $num_params = 0;
2261     my %field_descrs = ();
2263     if (@fields) {
2264         %field_descrs = @fields;
2265         delete $field_descrs{"void"};
2266         delete $field_descrs{"Returns"};
2267     }
2269     if (defined $params) {
2270         my $returns = "";
2271         my $params_desc = "";
2272         my $missing_parameters = "";
2273         my $unused_parameters = "";
2274         my $j;
2276         for ($j = 0; $j <= $#$params; $j += $PARAM_FIELD_COUNT) {
2277             my $param_name = $$params[$j];
2278             my $param_desc = $$params[$j + 1];
2279             my $param_annotations = "";
2281             ($param_desc,$param_annotations) = &ExpandAnnotation($symbol, $param_desc);
2282             $param_desc = &ConvertMarkDown($symbol, $param_desc);
2283             # trim
2284             $param_desc =~ s/^(\s|\n)+//msg;
2285             $param_desc =~ s/(\s|\n)+$//msg;
2286             if ($param_name eq "Returns") {
2287                 $returns = $param_desc;
2288                 if ($param_annotations ne "") {
2289                     $returns .= "\n<para>$param_annotations</para>";
2290                 }
2291             } elsif ($param_name eq "void") {
2292                 # FIXME: &LogWarning()?
2293                 @TRACE@("!!!! void in params for $symbol?\n");
2294             } else {
2295                 if (@fields) {
2296                     if (!defined $field_descrs{$param_name}) {
2297                         &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2298                             "Parameter description for $symbol"."::"."$param_name is not used from source code comment block.");
2299                         if ($unused_parameters ne "") {
2300                           $unused_parameters .= ", ".$param_name;
2301                         } else {
2302                            $unused_parameters = $param_name;
2303                         }
2304                     } else {
2305                         delete $field_descrs{$param_name};
2306                     }
2307                 }
2308                 if($param_desc ne "") {
2309                     $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";
2310                     $num_params++;
2311                 }
2312             }
2313         }
2314         foreach my $param_name (keys %field_descrs) {
2315             &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2316                 "Parameter description for $symbol"."::"."$param_name is missing in source code comment block.");
2317             if ($missing_parameters ne "") {
2318               $missing_parameters .= ", ".$param_name;
2319             } else {
2320                $missing_parameters = $param_name;
2321             }
2322         }
2324         # Signals have an implicit user_data parameter which we describe.
2325         if ($symbol_type eq "SIGNAL") {
2326             $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";
2327         }
2329         # Start a table if we need one.
2330         if ($params_desc ne "") {
2331           $output .= <<EOF;
2332 <refsect3 role="parameters">\n<title>Parameters</title>
2333 <informaltable role="parameters_table" pgwide="1" frame="none">
2334 <tgroup cols="3">
2335 <colspec colname="parameters_name" colwidth="150px"/>
2336 <colspec colname="parameters_description"/>
2337 <colspec colname="parameters_annotations" colwidth="200px"/>
2338 <tbody>
2340           $output .= $params_desc;
2341           $output .= "</tbody></tgroup></informaltable>\n</refsect3>";
2342         }
2344         # Output the returns info last
2345         if ($returns ne "") {
2346           $output .= <<EOF;
2347 <refsect3 role=\"returns\">\n<title>Returns</title>
2349           $output .= $returns;
2350           $output .= "\n</refsect3>";
2351         }
2353         # remember missing/unused parameters (needed in tmpl-free build)
2354         if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) {
2355             $AllIncompleteSymbols{$symbol}=$missing_parameters;
2356         }
2357         if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) {
2358             $AllUnusedSymbols{$symbol}=$unused_parameters;
2359         }
2360     }
2361     if (($num_params == 0) && @fields && (scalar(keys(%field_descrs)) > 0)) {
2362         if (! exists ($AllIncompleteSymbols{$symbol})) {
2363             $AllIncompleteSymbols{$symbol}="<parameters>";
2364         }
2365     }
2367     return $output;
2371 #############################################################################
2372 # Function    : ParseStabilityLevel
2373 # Description : Parses a stability level and outputs a warning if it isn't
2374 #               valid.
2375 # Arguments   : $stability - the stability text.
2376 #                $file, $line - context for error message
2377 #                $message - description of where the level is from, to use in
2378 #               any error message.
2379 # Returns     : The parsed stability level string.
2380 #############################################################################
2382 sub ParseStabilityLevel {
2383     my ($stability, $file, $line, $message) = @_;
2385     $stability =~ s/^\s*//;
2386     $stability =~ s/\s*$//;
2387     if ($stability =~ m/^stable$/i) {
2388         $stability = "Stable";
2389     } elsif ($stability =~ m/^unstable$/i) {
2390         $stability = "Unstable";
2391     } elsif ($stability =~ m/^private$/i) {
2392         $stability = "Private";
2393     } else {
2394         &LogWarning ($file, $line, "$message is $stability.".
2395             "It should be one of these: Stable, Unstable, or Private.");
2396     }
2397     return $stability;
2401 #############################################################################
2402 # Function    : OutputDBFile
2403 # Description : Outputs the final DocBook file for one section.
2404 # Arguments   : $file - the name of the file.
2405 #               $title - the title from the $MODULE-sections.txt file, which
2406 #                 will be overridden by the title in the template file.
2407 #               $section_id - the id to use for the toplevel tag.
2408 #               $includes - comma-separates list of include files added at top of
2409 #                 synopsis, with '<' '>' around them (if not already enclosed in "").
2410 #               $functions_synop - reference to the DocBook for the Functions Synopsis part.
2411 #               $other_synop - reference to the DocBook for the Types and Values Synopsis part.
2412 #               $functions_details - reference to the DocBook for the Functions Details part.
2413 #               $other_details - reference to the DocBook for the Types and Values Details part.
2414 #               $signal_synop - reference to the DocBook for the Signal Synopsis part
2415 #               $signal_desc - reference to the DocBook for the Signal Description part
2416 #               $args_synop - reference to the DocBook for the Arg Synopsis part
2417 #               $args_desc - reference to the DocBook for the Arg Description part
2418 #               $hierarchy - reference to the DocBook for the Object Hierarchy part
2419 #               $interfaces - reference to the DocBook for the Interfaces part
2420 #               $implementations - reference to the DocBook for the Known Implementations part
2421 #               $prerequisites - reference to the DocBook for the Prerequisites part
2422 #               $derived - reference to the DocBook for the Derived Interfaces part
2423 #               $file_objects - reference to an array of objects in this file
2424 #############################################################################
2426 sub OutputDBFile {
2427     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) = @_;
2429     @TRACE@("Output docbook for file $file with title '$title'\n");
2431     # The edited title overrides the one from the sections file.
2432     my $new_title = $SymbolDocs{"$TMPL_DIR/$file:Title"};
2433     if (defined ($new_title) && $new_title !~ m/^\s*$/) {
2434         $title = $new_title;
2435         @TRACE@("Found title: $title\n");
2436     }
2437     my $short_desc = $SymbolDocs{"$TMPL_DIR/$file:Short_Description"};
2438     if (!defined ($short_desc) || $short_desc =~ m/^\s*$/) {
2439         $short_desc = "";
2440     } else {
2441         # Don't use ConvertMarkDown here for now since we don't want blocks
2442         $short_desc = &ExpandAbbreviations("$title:Short_description",
2443                                            $short_desc);
2444         @TRACE@("Found short_desc: $short_desc");
2445     }
2446     my $long_desc = $SymbolDocs{"$TMPL_DIR/$file:Long_Description"};
2447     if (!defined ($long_desc) || $long_desc =~ m/^\s*$/) {
2448         $long_desc = "";
2449     } else {
2450         $long_desc = &ConvertMarkDown("$title:Long_description",
2451                                           $long_desc);
2452         @TRACE@("Found long_desc: $long_desc");
2453     }
2454     my $see_also = $SymbolDocs{"$TMPL_DIR/$file:See_Also"};
2455     if (!defined ($see_also) || $see_also =~ m%^\s*(<para>)?\s*(</para>)?\s*$%) {
2456         $see_also = "";
2457     } else {
2458         $see_also = &ConvertMarkDown("$title:See_Also", $see_also);
2459         @TRACE@("Found see_also: $see_also");
2460     }
2461     if ($see_also) {
2462         $see_also = "<refsect1 id=\"$section_id.see-also\">\n<title>See Also</title>\n$see_also\n</refsect1>\n";
2463     }
2464     my $stability = $SymbolDocs{"$TMPL_DIR/$file:Stability_Level"};
2465     if (!defined ($stability) || $stability =~ m/^\s*$/) {
2466         $stability = "";
2467     } else {
2468         $stability = &ParseStabilityLevel($stability, $file, $., "Section stability level");
2469         @TRACE@("Found stability: $stability");
2470     }
2471     if ($stability) {
2472         $AnnotationsUsed{$stability} = 1;
2473         $stability = "<refsect1 id=\"$section_id.stability-level\">\n<title>Stability Level</title>\n<acronym>$stability</acronym>, unless otherwise indicated\n</refsect1>\n";
2474     } elsif ($DEFAULT_STABILITY) {
2475         $AnnotationsUsed{$DEFAULT_STABILITY} = 1;
2476         $stability = "<refsect1 id=\"$section_id.stability-level\">\n<title>Stability Level</title>\n<acronym>$DEFAULT_STABILITY</acronym>, unless otherwise indicated\n</refsect1>\n";
2477     }
2479     my $image = $SymbolDocs{"$TMPL_DIR/$file:Image"};
2480     if (!defined ($image) || $image =~ m/^\s*$/) {
2481       $image = "";
2482     } else {
2483       $image =~ s/^\s*//;
2484       $image =~ s/\s*$//;
2486       my $format;
2488       if ($image =~ /jpe?g$/i) {
2489         $format = "format='JPEG'";
2490       } elsif ($image =~ /png$/i) {
2491         $format = "format='PNG'";
2492       } elsif ($image =~ /svg$/i) {
2493         $format = "format='SVG'";
2494       } else {
2495         $format = "";
2496       }
2498       $image = "  <inlinegraphic fileref='$image' $format/>\n"
2499     }
2501     my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) =
2502         gmtime (time);
2503     my $month = (qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec))[$mon];
2504     $year += 1900;
2506     my $include_output = "";
2507     if ($includes) {
2508       $include_output .= "<refsect1 id=\"$section_id.includes\"><title>Includes</title><synopsis>";
2509       my $include;
2510       foreach $include (split (/,/, $includes)) {
2511         if ($include =~ m/^\".+\"$/) {
2512           $include_output .= "#include ${include}\n";
2513         }
2514         else {
2515           $include =~ s/^\s+|\s+$//gs;
2516           $include_output .= "#include &lt;${include}&gt;\n";
2517         }
2518       }
2519       $include_output .= "</synopsis></refsect1>\n";
2520     }
2522     my $extralinks = OutputSectionExtraLinks($title,"Section:$file");
2524     my $old_db_file = "$DB_OUTPUT_DIR/$file.xml";
2525     my $new_db_file = "$DB_OUTPUT_DIR/$file.xml.new";
2527     open (OUTPUT, ">$new_db_file")
2528         || die "Can't create $new_db_file: $!";
2530     my $object_anchors = "";
2531     foreach my $object (@$file_objects) {
2532         next if ($object eq $section_id);
2533         my $id = CreateValidSGMLID($object);
2534         @TRACE@("Adding anchor for $object\n");
2535         $object_anchors .= "<anchor id=\"$id\"/>";
2536     }
2538     # Make sure we produce valid docbook
2539     $$functions_details ||= "<para />";
2541     # We used to output this, but is messes up our UpdateFileIfChanged code
2542     # since it changes every day (and it is only used in the man pages):
2543     # "<refentry id="$section_id" revision="$mday $month $year">"
2545     print OUTPUT <<EOF;
2546 ${\( MakeDocHeader ("refentry") )}
2547 <refentry id="$section_id">
2548 <refmeta>
2549 <refentrytitle role="top_of_page" id="$section_id.top_of_page">$title</refentrytitle>
2550 <manvolnum>3</manvolnum>
2551 <refmiscinfo>\U$MODULE\E Library$image</refmiscinfo>
2552 </refmeta>
2553 <refnamediv>
2554 <refname>$title</refname>
2555 <refpurpose>$short_desc</refpurpose>
2556 </refnamediv>
2557 $stability
2558 $$functions_synop$$args_synop$$signals_synop$object_anchors$$other_synop$$hierarchy$$prerequisites$$derived$$interfaces$$implementations
2559 $include_output
2560 <refsect1 id="$section_id.description" role="desc">
2561 <title role="desc.title">Description</title>
2562 $extralinks$long_desc
2563 </refsect1>
2564 <refsect1 id="$section_id.functions_details" role="details">
2565 <title role="details.title">Functions</title>
2566 $$functions_details
2567 </refsect1>
2568 <refsect1 id="$section_id.other_details" role="details">
2569 <title role="details.title">Types and Values</title>
2570 $$other_details
2571 </refsect1>
2572 $$args_desc$$signals_desc$see_also
2573 </refentry>
2575     close (OUTPUT);
2577     return &UpdateFileIfChanged ($old_db_file, $new_db_file, 0);
2581 #############################################################################
2582 # Function    : OutputExtraFile
2583 # Description : Copies an "extra" DocBook file into the output directory,
2584 #               expanding abbreviations
2585 # Arguments   : $file - the source file.
2586 #############################################################################
2587 sub OutputExtraFile {
2588     my ($file) = @_;
2590     my $basename;
2592     ($basename = $file) =~ s!^.*/!!;
2594     my $old_db_file = "$DB_OUTPUT_DIR/$basename";
2595     my $new_db_file = "$DB_OUTPUT_DIR/$basename.new";
2597     my $contents;
2599     open(EXTRA_FILE, "<$file") || die "Can't open $file";
2601     {
2602         local $/;
2603         $contents = <EXTRA_FILE>;
2604     }
2606     open (OUTPUT, ">$new_db_file")
2607         || die "Can't create $new_db_file: $!";
2609     print OUTPUT &ExpandAbbreviations ("$basename file", $contents);
2610     close (OUTPUT);
2612     return &UpdateFileIfChanged ($old_db_file, $new_db_file, 0);
2614 #############################################################################
2615 # Function    : OutputBook
2616 # Description : Outputs the entities that need to be included into the
2617 #                main docbook file for the module.
2618 # Arguments   : $book_top - the declarations of the entities, which are added
2619 #                  at the top of the main docbook file.
2620 #                $book_bottom - the references to the entities, which are
2621 #                  added in the main docbook file at the desired position.
2622 #############################################################################
2624 sub OutputBook {
2625     my ($book_top, $book_bottom) = @_;
2627     my $old_file = "$DB_OUTPUT_DIR/$MODULE-doc.top";
2628     my $new_file = "$DB_OUTPUT_DIR/$MODULE-doc.top.new";
2630     open (OUTPUT, ">$new_file")
2631         || die "Can't create $new_file: $!";
2632     print OUTPUT $book_top;
2633     close (OUTPUT);
2635     &UpdateFileIfChanged ($old_file, $new_file, 0);
2638     $old_file = "$DB_OUTPUT_DIR/$MODULE-doc.bottom";
2639     $new_file = "$DB_OUTPUT_DIR/$MODULE-doc.bottom.new";
2641     open (OUTPUT, ">$new_file")
2642         || die "Can't create $new_file: $!";
2643     print OUTPUT $book_bottom;
2644     close (OUTPUT);
2646     &UpdateFileIfChanged ($old_file, $new_file, 0);
2649     # If the main docbook file hasn't been created yet, we create it here.
2650     # The user can tweak it later.
2651     if ($MAIN_SGML_FILE && ! -e $MAIN_SGML_FILE) {
2652         open (OUTPUT, ">$MAIN_SGML_FILE")
2653           || die "Can't create $MAIN_SGML_FILE: $!";
2654           
2655         print OUTPUT <<EOF;
2656 ${\( MakeDocHeader ("book") )}
2657 <book id="index">
2658   <bookinfo>
2659     <title>&package_name; Reference Manual</title>
2660     <releaseinfo>
2661       for &package_string;.
2662       The latest version of this documentation can be found on-line at
2663       <ulink role="online-location" url="http://[SERVER]/&package_name;/index.html">http://[SERVER]/&package_name;/</ulink>.
2664     </releaseinfo>
2665   </bookinfo>
2667   <chapter>
2668     <title>[Insert title here]</title>
2669     $book_bottom
2670   </chapter>
2672         if (-e $OBJECT_TREE_FILE) {
2673             print OUTPUT <<EOF;
2674   <chapter id="object-tree">
2675     <title>Object Hierarchy</title>
2676     <xi:include href="xml/tree_index.sgml"/>
2677   </chapter>
2679         } else {
2680             print OUTPUT <<EOF;
2681   <!-- enable this when you use gobject types
2682   <chapter id="object-tree">
2683     <title>Object Hierarchy</title>
2684     <xi:include href="xml/tree_index.sgml"/>
2685   </chapter>
2686   -->
2688         }
2689         print OUTPUT <<EOF;
2690   <index id="api-index-full">
2691     <title>API Index</title>
2692     <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
2693   </index>
2694   <index id="deprecated-api-index" role="deprecated">
2695     <title>Index of deprecated API</title>
2696     <xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include>
2697   </index>
2699         if (keys(%AnnotationsUsed)) {
2700             print OUTPUT <<EOF;
2701   <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
2703         } else {
2704             print OUTPUT <<EOF;
2705   <!-- enable this when you use gobject introspection annotations
2706   <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
2707   -->
2709         }
2710         print OUTPUT <<EOF;
2711 </book>
2714         close (OUTPUT);
2715     }
2719 #############################################################################
2720 # Function    : CreateValidSGML
2721 # Description : This turns any chars which are used in SGML into entities,
2722 #                e.g. '<' into '&lt;'
2723 # Arguments   : $text - the text to turn into proper SGML.
2724 #############################################################################
2726 sub CreateValidSGML {
2727     my ($text) = @_;
2728     $text =~ s/&/&amp;/g;        # Do this first, or the others get messed up.
2729     $text =~ s/</&lt;/g;
2730     $text =~ s/>/&gt;/g;
2731     # browers render single tabs inconsistently
2732     $text =~ s/([^\s])\t([^\s])/$1&#160;$2/g;
2733     return $text;
2736 #############################################################################
2737 # Function    : ConvertSGMLChars
2738 # Description : This is used for text in source code comment blocks, to turn
2739 #               chars which are used in SGML into entities, e.g. '<' into
2740 #               '&lt;'. Depending on $INLINE_MARKUP_MODE, this is done
2741 #               unconditionally or only if the character doesn't seem to be
2742 #               part of an SGML construct (tag or entity reference).
2743 # Arguments   : $text - the text to turn into proper SGML.
2744 #############################################################################
2746 sub ConvertSGMLChars {
2747     my ($symbol, $text) = @_;
2749     if ($INLINE_MARKUP_MODE) {
2750         # For the XML/SGML mode only convert to entities outside CDATA sections.
2751         return &ModifyXMLElements ($text, $symbol,
2752                                    "<!\\[CDATA\\[|<programlisting[^>]*>",
2753                                    \&ConvertSGMLCharsEndTag,
2754                                    \&ConvertSGMLCharsCallback);
2755     } else {
2756         # For the simple non-sgml mode, convert to entities everywhere.
2758         # First, convert freestanding & to &amp;
2759         $text =~ s/&(?![a-zA-Z#]+;)/&amp;/g;
2760         $text =~ s/</&lt;/g;
2761         # Allow ">" at beginning of string for blockquote markdown
2762         $text =~ s/(?<=[^\w\n"'\/-])>/&gt;/g;
2764         return $text;
2765     }
2769 sub ConvertSGMLCharsEndTag {
2770   if ($_[0] eq "<!\[CDATA\[") {
2771     return "]]>";
2772   } else {
2773     return "</programlisting>";
2774   }
2777 sub ConvertSGMLCharsCallback {
2778   my ($text, $symbol, $tag) = @_;
2780   if ($tag =~ m/^<programlisting/) {
2781     # We can handle <programlisting> specially here.
2782     return &ModifyXMLElements ($text, $symbol,
2783                                "<!\\[CDATA\\[",
2784                                \&ConvertSGMLCharsEndTag,
2785                                \&ConvertSGMLCharsCallback2);
2786   } elsif ($tag eq "") {
2787     # If we're not in CDATA convert to entities.
2788     $text =~ s/&(?![a-zA-Z#]+;)/&amp;/g;        # Do this first, or the others get messed up.
2789     $text =~ s/<(?![a-zA-Z\/!])/&lt;/g;
2790     # Allow ">" at beginning of string for blockquote markdown
2791     $text =~ s/(?<=[^\w\n"'\/-])>/&gt;/g;
2793     # Handle "#include <xxxxx>"
2794     $text =~ s/#include(\s+)<([^>]+)>/#include$1&lt;$2&gt;/g;
2795   }
2797   return $text;
2800 sub ConvertSGMLCharsCallback2 {
2801   my ($text, $symbol, $tag) = @_;
2803   # If we're not in CDATA convert to entities.
2804   # We could handle <programlisting> differently, though I'm not sure it helps.
2805   if ($tag eq "") {
2806     # replace only if its not a tag
2807     $text =~ s/&(?![a-zA-Z#]+;)/&amp;/g;        # Do this first, or the others get messed up.
2808     $text =~ s/<(?![a-zA-Z\/!])/&lt;/g;
2809     $text =~ s/(?<![a-zA-Z0-9"'\/-])>/&gt;/g;
2811     # Handle "#include <xxxxx>"
2812     $text =~ s/#include(\s+)<([^>]+)>/#include$1&lt;$2&gt;/g;
2813   }
2815   return $text;
2818 #############################################################################
2819 # Function    : ExpandAnnotation
2820 # Description : This turns annotations into acronym tags.
2821 # Arguments   : $symbol - the symbol being documented, for error messages.
2822 #                $text - the text to expand.
2823 #############################################################################
2824 sub ExpandAnnotation {
2825     my ($symbol, $param_desc) = @_;
2826     my $param_annotations = "";
2828     # look for annotations at the start of the comment part
2829     # function level annotations don't end with a colon ':'
2830     if ($param_desc =~ m%^\s*\((.*?)\)(:|$)%) {
2831         my @annotations;
2832         my $annotation;
2833         $param_desc = $';
2835         @annotations = split(/\)\s*\(/,$1);
2836         @TRACE@("annotations for $symbol: '$1'\n");
2837         foreach $annotation (@annotations) {
2838             # need to search for the longest key-match in %AnnotationDefinition
2839             my $match_length=0;
2840             my $match_annotation="";
2841             my $annotationdef;
2842             foreach $annotationdef (keys %AnnotationDefinition) {
2843                 if ($annotation =~ m/^$annotationdef/) {
2844                     if (length($annotationdef)>$match_length) {
2845                         $match_length=length($annotationdef);
2846                         $match_annotation=$annotationdef;
2847                     }
2848                 }
2849             }
2850             my $annotation_extra = "";
2851             if ($match_annotation ne "") {
2852                 if ($annotation =~ m%$match_annotation\s+(.*)%) {
2853                     $annotation_extra = " $1";
2854                 }
2855                 $AnnotationsUsed{$match_annotation} = 1;
2856                 $param_annotations .= "[<acronym>$match_annotation</acronym>$annotation_extra]";
2857             }
2858             else {
2859                 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2860                     "unknown annotation \"$annotation\" in documentation for $symbol.");
2861                 $param_annotations .= "[$annotation]";
2862             }
2863         }
2864         chomp($param_desc);
2865         $param_desc =~ m/^(.*?)\.*\s*$/s;
2866         $param_desc = "$1. ";
2867     }
2868     if ($param_annotations ne "") {
2869         $param_annotations = "<emphasis role=\"annotation\">$param_annotations</emphasis>";
2870     }
2871     return ($param_desc, $param_annotations);
2874 #############################################################################
2875 # Function    : ExpandAbbreviations
2876 # Description : This turns the abbreviations function(), macro(), @param,
2877 #                %constant, and #symbol into appropriate DocBook markup.
2878 #               CDATA sections and <programlisting> parts are skipped.
2879 # Arguments   : $symbol - the symbol being documented, for error messages.
2880 #                $text - the text to expand.
2881 #############################################################################
2883 sub ExpandAbbreviations {
2884   my ($symbol, $text) = @_;
2886   # Note: This is a fallback and normally done in the markdown parser
2888   # Convert "|[" and "]|" into the start and end of program listing examples.
2889   # Support \[<!-- language="C" --> modifiers
2890   $text =~ s%\|\[<!-- language="([^"]+)" -->%<informalexample><programlisting language="$1"><![CDATA[%g;
2891   $text =~ s%\|\[%<informalexample><programlisting><![CDATA[%g;
2892   $text =~ s%\]\|%]]></programlisting></informalexample>%g;
2894   # keep CDATA unmodified, preserve ulink tags (ideally we preseve all tags
2895   # as such)
2896   return &ModifyXMLElements ($text, $symbol,
2897                              "<!\\[CDATA\\[|<ulink[^>]*>|<programlisting[^>]*>|<!DOCTYPE",
2898                              \&ExpandAbbreviationsEndTag,
2899                              \&ExpandAbbreviationsCallback);
2903 # Returns the end tag (as a regexp) corresponding to the given start tag.
2904 sub ExpandAbbreviationsEndTag {
2905   my ($start_tag) = @_;
2907   if ($start_tag eq "<!\[CDATA\[") {
2908     return "]]>";
2909   } elsif ($start_tag eq "<!DOCTYPE") {
2910     return ">";
2911   } elsif ($start_tag =~ m/<(\w+)/) {
2912     return "</$1>";
2913   }
2916 # Called inside or outside each CDATA or <programlisting> section.
2917 sub ExpandAbbreviationsCallback {
2918   my ($text, $symbol, $tag) = @_;
2920   if ($tag =~ m/^<programlisting/) {
2921     # Handle any embedded CDATA sections.
2922     return &ModifyXMLElements ($text, $symbol,
2923                                "<!\\[CDATA\\[",
2924                                \&ExpandAbbreviationsEndTag,
2925                                \&ExpandAbbreviationsCallback2);
2926   } elsif ($tag eq "") {
2927     # NOTE: this is a fallback. It is normally done by the Markdown parser.
2929     # We are outside any CDATA or <programlisting> sections, so we expand
2930     # any gtk-doc abbreviations.
2932     # Convert '@param()'
2933     # FIXME: we could make those also links ($symbol.$2), but that would be less
2934     # useful as the link target is a few lines up or down
2935     $text =~ s/(\A|[^\\])\@(\w+((\.|->)\w+)*)\s*\(\)/$1<parameter>$2()<\/parameter>/g;
2937     # Convert 'function()' or 'macro()'.
2938     # if there is abc_*_def() we don't want to make a link to _def()
2939     # FIXME: also handle abc(def(....)) : but that would need to be done recursively :/
2940     $text =~ s/([^\*.\w])(\w+)\s*\(\)/$1.&MakeXRef($2, &tagify($2 . "()", "function"));/eg;
2941     # handle #Object.func()
2942     $text =~ s/(\A|[^\\])#([\w\-:\.]+[\w]+)\s*\(\)/$1.&MakeXRef($2, &tagify($2 . "()", "function"));/eg;
2944     # Convert '@param', but not '\@param'.
2945     $text =~ s/(\A|[^\\])\@(\w+((\.|->)\w+)*)/$1<parameter>$2<\/parameter>/g;
2946     $text =~ s/\\\@/\@/g;
2948     # Convert '%constant', but not '\%constant'.
2949     # Also allow negative numbers, e.g. %-1.
2950     $text =~ s/(\A|[^\\])\%(-?\w+)/$1.&MakeXRef($2, &tagify($2, "literal"));/eg;
2951     $text =~ s/\\\%/\%/g;
2953     # Convert '#symbol', but not '\#symbol'.
2954     $text =~ s/(\A|[^\\])#([\w\-:\.]+[\w]+)/$1.&MakeHashXRef($2, "type");/eg;
2955     $text =~ s/\\#/#/g;
2956   }
2958   return $text;
2961 # This is called inside a <programlisting>
2962 sub ExpandAbbreviationsCallback2 {
2963   my ($text, $symbol, $tag) = @_;
2965   if ($tag eq "") {
2966     # We are inside a <programlisting> but outside any CDATA sections,
2967     # so we expand any gtk-doc abbreviations.
2968     # FIXME: why is this different from &ExpandAbbreviationsCallback(),
2969     #        why not just call it
2970     $text =~ s/#(\w+)/&MakeHashXRef($1, "");/eg;
2971   } elsif ($tag eq "<![CDATA[") {
2972     # NOTE: this is a fallback. It is normally done by the Markdown parser.
2973     $text = &ReplaceEntities ($text, $symbol);
2974   }
2976   return $text;
2979 sub MakeHashXRef {
2980     my ($symbol, $tag) = @_;;
2981     my $text = $symbol;
2983     # Check for things like '#include', '#define', and skip them.
2984     if ($PreProcessorDirectives{$symbol}) {
2985       return "#$symbol";
2986     }
2988     # Get rid of special suffixes ('-struct','-enum').
2989     $text =~ s/-struct$//;
2990     $text =~ s/-enum$//;
2992     # If the symbol is in the form "Object::signal", then change the symbol to
2993     # "Object-signal" and use "signal" as the text.
2994     if ($symbol =~ s/::/-/) {
2995       $text = "“$'”";
2996     }
2998     # If the symbol is in the form "Object:property", then change the symbol to
2999     # "Object--property" and use "property" as the text.
3000     if ($symbol =~ s/:/--/) {
3001       $text = "“$'”";
3002     }
3004     if ($tag ne "") {
3005       $text = tagify ($text, $tag);
3006     }
3008     return &MakeXRef($symbol, $text);
3012 #############################################################################
3013 # Function    : ModifyXMLElements
3014 # Description : Looks for given XML element tags within the text, and calls
3015 #               the callback on pieces of text inside & outside those elements.
3016 #               Used for special handling of text inside things like CDATA
3017 #               and <programlisting>.
3018 # Arguments   : $text - the text.
3019 #               $symbol - the symbol currently being documented (only used for
3020 #                      error messages).
3021 #               $start_tag_regexp - the regular expression to match start tags.
3022 #                      e.g. "<!\\[CDATA\\[|<programlisting[^>]*>" to match
3023 #                      CDATA sections or programlisting elements.
3024 #               $end_tag_func - function which is passed the matched start tag
3025 #                      and should return the appropriate end tag string regexp.
3026 #               $callback - callback called with each part of the text. It is
3027 #                      called with a piece of text, the symbol being
3028 #                      documented, and the matched start tag or "" if the text
3029 #                      is outside the XML elements being matched.
3030 #############################################################################
3031 sub ModifyXMLElements {
3032     my ($text, $symbol, $start_tag_regexp, $end_tag_func, $callback) = @_;
3033     my ($before_tag, $start_tag, $end_tag_regexp, $end_tag);
3034     my $result = "";
3036     while ($text =~ m/$start_tag_regexp/s) {
3037       $before_tag = $`; # Prematch for last successful match string
3038       $start_tag = $&;  # Last successful match
3039       $text = $';       # Postmatch for last successful match string
3041       $result .= &$callback ($before_tag, $symbol, "");
3042       $result .= $start_tag;
3044       # get the matching end-tag for current tag
3045       $end_tag_regexp = &$end_tag_func ($start_tag);
3047       if ($text =~ m/$end_tag_regexp/s) {
3048         $before_tag = $`;
3049         $end_tag = $&;
3050         $text = $';
3052         $result .= &$callback ($before_tag, $symbol, $start_tag);
3053         $result .= $end_tag;
3054       } else {
3055         &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
3056             "Can't find tag end: $end_tag_regexp in docs for: $symbol.");
3057         # Just assume it is all inside the tag.
3058         $result .= &$callback ($text, $symbol, $start_tag);
3059         $text = "";
3060       }
3061     }
3063     # Handle any remaining text outside the tags.
3064     $result .= &$callback ($text, $symbol, "");
3066     return $result;
3069 sub noop {
3070   return $_[0];
3073 # Adds a tag around some text.
3074 # e.g tagify("Text", "literal") => "<literal>Text</literal>".
3075 sub tagify {
3076    my ($text, $elem) = @_;
3077    return "<" . $elem . ">" . $text . "</" . $elem . ">";
3080 #############################################################################
3081 # Function    : MakeDocHeader
3082 # Description : Builds a docbook header for the given tag
3083 # Arguments   : $tag - doctype tag
3084 #############################################################################
3086 sub MakeDocHeader {
3087     my ($tag) = @_;
3088     my $header = $doctype_header;
3089     $header =~ s/<!DOCTYPE \w+/<!DOCTYPE $tag/;
3091     # fix the path for book since this is one level up
3092     if ($tag eq "book") {
3093         $header =~ s#<!ENTITY % gtkdocentities SYSTEM \"../([a-zA-Z./]+)\">#<!ENTITY % gtkdocentities SYSTEM \"$1\">#;
3094     }
3096     return $header;
3100 #############################################################################
3101 # Function    : MakeXRef
3102 # Description : This returns a cross-reference link to the given symbol.
3103 #                Though it doesn't try to do this for a few standard C types
3104 #                that it        knows won't be in the documentation.
3105 # Arguments   : $symbol - the symbol to try to create a XRef to.
3106 #               $text - text text to put inside the XRef, defaults to $symbol
3107 #############################################################################
3109 sub MakeXRef {
3110     my ($symbol, $text) = ($_[0], $_[1]);
3112     $symbol =~ s/^\s+//;
3113     $symbol =~ s/\s+$//;
3115     if (!defined($text)) {
3116         $text = $symbol;
3118         # Get rid of special suffixes ('-struct','-enum').
3119         $text =~ s/-struct$//;
3120         $text =~ s/-enum$//;
3121     }
3123     if ($symbol =~ m/ /) {
3124         return "$text";
3125     }
3127     @TRACE@("Getting type link for $symbol -> $text\n");
3129     my $symbol_id = &CreateValidSGMLID ($symbol);
3130     return "<link linkend=\"$symbol_id\">$text</link>";
3134 #############################################################################
3135 # Function    : MakeIndexterms
3136 # Description : This returns a indexterm elements for the given symbol
3137 # Arguments   : $symbol - the symbol to create indexterms for
3138 #############################################################################
3140 sub MakeIndexterms {
3141   my ($symbol, $id) = @_;
3142   my $terms =  "";
3143   my $sortas = "";
3145   # make the index useful, by ommiting the namespace when sorting
3146   if ($NAME_SPACE ne "") {
3147     if ($symbol =~ m/^$NAME_SPACE\_?(.*)/i) {
3148        $sortas=" sortas=\"$1\"";
3149     }
3150   }
3152   if (exists $Deprecated{$symbol}) {
3153       $terms .= "<indexterm zone=\"$id\" role=\"deprecated\"><primary$sortas>$symbol</primary></indexterm>";
3154       $IndexEntriesDeprecated{$symbol}=$id;
3155       $IndexEntriesFull{$symbol}=$id;
3156   }
3157   if (exists $Since{$symbol}) {
3158      my $since = $Since{$symbol};
3159      $since =~ s/^\s+//;
3160      $since =~ s/\s+$//;
3161      if ($since ne "") {
3162          $terms .= "<indexterm zone=\"$id\" role=\"$since\"><primary$sortas>$symbol</primary></indexterm>";
3163      }
3164      $IndexEntriesSince{$symbol}=$id;
3165      $IndexEntriesFull{$symbol}=$id;
3166   }
3167   if ($terms eq "") {
3168      $terms .= "<indexterm zone=\"$id\"><primary$sortas>$symbol</primary></indexterm>";
3169      $IndexEntriesFull{$symbol}=$id;
3170   }
3172   return $terms;
3175 #############################################################################
3176 # Function    : MakeDeprecationNote
3177 # Description : This returns a deprecation warning for the given symbol.
3178 # Arguments   : $symbol - the symbol to try to create a warning for.
3179 #############################################################################
3181 sub MakeDeprecationNote {
3182     my ($symbol) = $_[0];
3183     my $desc = "";
3184     if (exists $Deprecated{$symbol}) {
3185         my $note;
3187         $desc .= "<warning><para><literal>$symbol</literal> ";
3189         $note = $Deprecated{$symbol};
3191         if ($note =~ /^\s*([0-9\.]+)\s*:?/) {
3192                 $desc .= "has been deprecated since version $1 and should not be used in newly-written code.</para>";
3193         } else {
3194                 $desc .= "is deprecated and should not be used in newly-written code.</para>";
3195         }
3196         $note =~ s/^\s*([0-9\.]+)\s*:?\s*//;
3197         $note =~ s/^\s+//;
3198         $note =~ s/\s+$//;
3199         if ($note ne "") {
3200             $note = &ConvertMarkDown($symbol, $note);
3201             $desc .= " " . $note;
3202         }
3203         $desc .= "</warning>\n";
3204     }
3205     return $desc;
3208 #############################################################################
3209 # Function    : MakeConditionDescription
3210 # Description : This returns a sumary of conditions for the given symbol.
3211 # Arguments   : $symbol - the symbol to try to create the sumary.
3212 #############################################################################
3214 sub MakeConditionDescription {
3215     my ($symbol) = $_[0];
3216     my $desc = "";
3218     if (exists $Deprecated{$symbol}) {
3219         if ($desc ne "") {
3220             $desc .= "|";
3221         }
3223         if ($Deprecated{$symbol} =~ /^\s*(.*?)\s*$/) {
3224                 $desc .= "deprecated:$1";
3225         } else {
3226                 $desc .= "deprecated";
3227         }
3228     }
3230     if (exists $Since{$symbol}) {
3231         if ($desc ne "") {
3232             $desc .= "|";
3233         }
3235         if ($Since{$symbol} =~ /^\s*(.*?)\s*$/) {
3236                 $desc .= "since:$1";
3237         } else {
3238                 $desc .= "since";
3239         }
3240     }
3242     if (exists $StabilityLevel{$symbol}) {
3243         if ($desc ne "") {
3244             $desc .= "|";
3245         }
3246         $desc .= "stability:".$StabilityLevel{$symbol};
3247     }
3249     if ($desc ne "") {
3250         my $cond = $desc;
3251         $cond =~ s/\"/&quot;/g;
3252         $desc=" condition=\"".$cond."\"";
3253         @TRACE@("condition for '$symbol' = '$desc'\n");
3254     }
3255     return $desc;
3258 #############################################################################
3259 # Function    : GetHierarchy
3260 # Description : Returns the DocBook output describing the ancestors and
3261 #               immediate children of a GObject subclass. It uses the
3262 #               global @Objects and @ObjectLevels arrays to walk the tree.
3264 # Arguments   : $object - the GtkObject subclass.
3265 #               @hierarchy - previous hierarchy
3266 #############################################################################
3268 sub GetHierarchy {
3269     my ($object,$hierarchy_ref) = @_;
3270     my @hierarchy = @{$hierarchy_ref};
3271     
3272     # Find object in the objects array.
3273     my $found = 0;
3274     my @children = ();
3275     my $i;
3276     my $level;
3277     my $j;
3278     for ($i = 0; $i < @Objects; $i++) {
3279         if ($found) {
3280             if ($ObjectLevels[$i] <= $level) {
3281             last;
3282         }
3283             elsif ($ObjectLevels[$i] == $level + 1) {
3284                 push (@children, $Objects[$i]);
3285             }
3286         }
3287         elsif ($Objects[$i] eq $object) {
3288             $found = 1;
3289             $j = $i;
3290             $level = $ObjectLevels[$i];
3291         }
3292     }
3293     if (!$found) {
3294         return @hierarchy;
3295     }
3297     # Walk up the hierarchy, pushing ancestors onto the ancestors array.
3298     my @ancestors = ();
3299     push (@ancestors, $object);
3300     @TRACE@("Level: $level\n");
3301     while ($level > 1) {
3302         $j--;
3303         if ($ObjectLevels[$j] < $level) {
3304             push (@ancestors, $Objects[$j]);
3305             $level = $ObjectLevels[$j];
3306             @TRACE@("Level: $level\n");
3307         }
3308     }
3310     # Output the ancestors, indented and with links.
3311     my $last_index = 0;
3312     $level = 1;
3313     for ($i = $#ancestors; $i >= 0; $i--) {
3314         my $entry_text;
3315         my $alt_text;
3316         my $ancestor = $ancestors[$i];
3317         my $ancestor_id = &CreateValidSGMLID ($ancestor);
3318         my $indent = ' ' x ($level * 4);
3319         # Don't add a link to the current object, i.e. when i == 0.
3320         if ($i > 0) {
3321             $entry_text = $indent . "<link linkend=\"$ancestor_id\">$ancestor</link>";
3322             $alt_text = $indent . $ancestor;
3323         } else {
3324             $entry_text = $indent . $ancestor;
3325             $alt_text = $indent . "<link linkend=\"$ancestor_id\">$ancestor</link>";
3326         }
3327         @TRACE@("Checking for '$entry_text' or '$alt_text'"); 
3328         # Check if we already have this object
3329         my $index = -1;
3330         for ($j = 0; $j <= $#hierarchy; $j++) {
3331             if (($hierarchy[$j] eq $entry_text) or ($hierarchy[$j] eq $alt_text)) {
3332                 $index = $j;
3333                 last;
3334             }
3335         }
3336         if ($index == -1) {
3337             # We have a new entry, find insert position in alphabetical order
3338             my $found = 0;
3339             for ($j = $last_index; $j <= $#hierarchy; $j++) {
3340                 if ($hierarchy[$j] !~ m/^${indent}/) {
3341                     $last_index = $j;
3342                     $found = 1;
3343                     last;
3344                 } elsif ($hierarchy[$j] =~ m/^${indent}[^ ]/) {
3345                     my $stripped_text = $hierarchy[$j];
3346                     if ($entry_text !~ m/<link linkend/) {
3347                         $stripped_text =~ s%<link linkend="[A-Za-z]*">%%;
3348                         $stripped_text =~ s%</link>%%;
3349                     }
3350                     if ($entry_text lt $stripped_text) {
3351                         $last_index = $j;
3352                         $found = 1;
3353                         last;
3354                     } 
3355                 }
3356             }
3357             # Append to bottom
3358             if (!$found) {
3359               $last_index = 1 + $#hierarchy;
3360             }
3361             splice @hierarchy, $last_index, 0, ($entry_text);
3362             $last_index++;
3363         } else {
3364             # Already have this one, make sure we use the not linked version
3365             if ($entry_text !~ m/<link linkend=/) {
3366               $hierarchy[$j] = $entry_text;
3367             }
3368             # Remember index as base insert point
3369             $last_index = $index + 1;
3370         }
3371         $level++;
3372     }
3373     # Output the children, indented and with links.
3374     for ($i = 0; $i <= $#children; $i++) {
3375         my $id = &CreateValidSGMLID ($children[$i]);
3376         my $indented_text = ' ' x ($level * 4) . "<link linkend=\"$id\">$children[$i]</link>";
3377         splice @hierarchy, $last_index, 0, ($indented_text);
3378         $last_index++;
3379     }    
3381     return @hierarchy; 
3384 #############################################################################
3385 # Function    : GetInterfaces
3386 # Description : Returns the DocBook output describing the interfaces
3387 #               implemented by a class. It uses the global %Interfaces hash.
3388 # Arguments   : $object - the GtkObject subclass.
3389 #############################################################################
3391 sub GetInterfaces {
3392     my ($object) = @_;
3393     my $text = "";
3394     my $i;
3396     # Find object in the objects array.
3397     if (exists($Interfaces{$object})) {
3398         my @ifaces = split(' ', $Interfaces{$object});
3399         $text = <<EOF;
3400 <para>
3401 $object implements
3403         for ($i = 0; $i <= $#ifaces; $i++) {
3404             my $id = &CreateValidSGMLID ($ifaces[$i]);
3405             $text .= " <link linkend=\"$id\">$ifaces[$i]</link>";
3406             if ($i < $#ifaces - 1) {
3407                 $text .= ', ';
3408             }
3409             elsif ($i < $#ifaces) {
3410                 $text .= ' and ';
3411             }
3412             else {
3413                 $text .= '.';
3414             }
3415         }
3416         $text .= <<EOF;
3417 </para>
3419     }
3421     return $text;
3424 #############################################################################
3425 # Function    : GetImplementations
3426 # Description : Returns the DocBook output describing the implementations
3427 #               of an interface. It uses the global %Interfaces hash.
3428 # Arguments   : $object - the GtkObject subclass.
3429 #############################################################################
3431 sub GetImplementations {
3432     my ($object) = @_;
3433     my @impls = ();
3434     my $text = "";
3435     my $i;
3436     foreach my $key (keys %Interfaces) {
3437         if ($Interfaces{$key} =~ /\b$object\b/) {
3438             push (@impls, $key);
3439         }
3440     }
3441     if ($#impls >= 0) {
3442         @impls = sort @impls;
3443         $text = <<EOF;
3444 <para>
3445 $object is implemented by
3447         for ($i = 0; $i <= $#impls; $i++) {
3448             my $id = &CreateValidSGMLID ($impls[$i]);
3449             $text .= " <link linkend=\"$id\">$impls[$i]</link>";
3450             if ($i < $#impls - 1) {
3451                 $text .= ', ';
3452             }
3453             elsif ($i < $#impls) {
3454                 $text .= ' and ';
3455             }
3456             else {
3457                 $text .= '.';
3458             }
3459         }
3460         $text .= <<EOF;
3461 </para>
3463     }
3464     return $text;
3468 #############################################################################
3469 # Function    : GetPrerequisites
3470 # Description : Returns the DocBook output describing the prerequisites
3471 #               of an interface. It uses the global %Prerequisites hash.
3472 # Arguments   : $iface - the interface.
3473 #############################################################################
3475 sub GetPrerequisites {
3476     my ($iface) = @_;
3477     my $text = "";
3478     my $i;
3480     if (exists($Prerequisites{$iface})) {
3481         $text = <<EOF;
3482 <para>
3483 $iface requires
3485         my @prereqs = split(' ', $Prerequisites{$iface});
3486         for ($i = 0; $i <= $#prereqs; $i++) {
3487             my $id = &CreateValidSGMLID ($prereqs[$i]);
3488             $text .= " <link linkend=\"$id\">$prereqs[$i]</link>";
3489             if ($i < $#prereqs - 1) {
3490                 $text .= ', ';
3491             }
3492             elsif ($i < $#prereqs) {
3493                 $text .= ' and ';
3494             }
3495             else {
3496                 $text .= '.';
3497             }
3498         }
3499         $text .= <<EOF;
3500 </para>
3502     }
3503     return $text;
3506 #############################################################################
3507 # Function    : GetDerived
3508 # Description : Returns the DocBook output describing the derived interfaces
3509 #               of an interface. It uses the global %Prerequisites hash.
3510 # Arguments   : $iface - the interface.
3511 #############################################################################
3513 sub GetDerived {
3514     my ($iface) = @_;
3515     my $text = "";
3516     my $i;
3518     my @derived = ();
3519     foreach my $key (keys %Prerequisites) {
3520         if ($Prerequisites{$key} =~ /\b$iface\b/) {
3521             push (@derived, $key);
3522         }
3523     }
3524     if ($#derived >= 0) {
3525         @derived = sort @derived;
3526         $text = <<EOF;
3527 <para>
3528 $iface is required by
3530         for ($i = 0; $i <= $#derived; $i++) {
3531             my $id = &CreateValidSGMLID ($derived[$i]);
3532             $text .= " <link linkend=\"$id\">$derived[$i]</link>";
3533             if ($i < $#derived - 1) {
3534                 $text .= ', ';
3535             }
3536             elsif ($i < $#derived) {
3537                 $text .= ' and ';
3538             }
3539             else {
3540                 $text .= '.';
3541             }
3542         }
3543         $text .= <<EOF;
3544 </para>
3546     }
3547     return $text;
3551 #############################################################################
3552 # Function    : GetSignals
3553 # Description : Returns the synopsis and detailed description DocBook output
3554 #                for the signal handlers of a given GtkObject subclass.
3555 # Arguments   : $object - the GtkObject subclass, e.g. 'GtkButton'.
3556 #############################################################################
3558 sub GetSignals {
3559     my ($object) = @_;
3560     my $synop = "";
3561     my $desc = "";
3563     my $i;
3564     for ($i = 0; $i <= $#SignalObjects; $i++) {
3565         if ($SignalObjects[$i] eq $object) {
3566             @TRACE@("Found signal: $SignalNames[$i]\n");
3567             my $name = $SignalNames[$i];
3568             my $symbol = "${object}::${name}";
3569             my $id = &CreateValidSGMLID ("$object-$name");
3571             $desc .= "<refsect2 id=\"$id\" role=\"signal\"><title>The <literal>“$name”</literal> signal</title>\n";
3572             $desc .= MakeIndexterms($symbol, $id);
3573             $desc .= "\n";
3574             $desc .= OutputSymbolExtraLinks($symbol);
3576             $desc .= "<programlisting language=\"C\">";
3578             $SignalReturns[$i] =~ m/\s*(const\s+)?(\w+)\s*(\**)/;
3579             my $type_modifier = defined($1) ? $1 : "";
3580             my $type = $2;
3581             my $pointer = $3;
3582             my $xref = &MakeXRef ($type, &tagify($type, "returnvalue"));
3584             my $ret_type_output = "$type_modifier$xref$pointer";
3585             my $callback_name = "user_function";
3586             $desc  .= "${ret_type_output}\n${callback_name} (";
3588             my $indentation = ' ' x (length($callback_name) + 2);
3589             my $pad = $indentation;
3591             my $sourceparams = $SourceSymbolParams{$symbol};
3592             my @params = split ("\n", $SignalPrototypes[$i]);
3593             my $j;
3594             my $l;
3595             my $type_len = length("gpointer");
3596             my $name_len = length("user_data");
3597             # do two passes, the first one is to calculate padding
3598             for ($l = 0; $l < 2; $l++) {
3599                 for ($j = 0; $j <= $#params; $j++) {
3600                     my $param_name;
3601                     # allow alphanumerics, '_', '[' & ']' in param names
3602                     if ($params[$j] =~ m/^\s*(\w+)\s*(\**)\s*([\w\[\]]+)\s*$/) {
3603                         $type = $1;
3604                         $pointer = $2;
3605                         if (defined($sourceparams)) {
3606                             $param_name = $$sourceparams[$PARAM_FIELD_COUNT * $j];
3607                         }
3608                         else {
3609                             $param_name = $3;
3610                         }
3611                         if (!defined($param_name)) {
3612                             $param_name = "arg$j";
3613                         }
3614                         if ($l == 0) {
3615                             if (length($type) + length($pointer) > $type_len) {
3616                                 $type_len = length($type) + length($pointer);
3617                             }
3618                             if (length($param_name) > $name_len) {
3619                                 $name_len = length($param_name);
3620                             }
3621                         }
3622                         else {
3623                             $xref = &MakeXRef ($type, &tagify($type, "type"));
3624                             $pad = ' ' x ($type_len - length($type) - length($pointer));
3625                             $desc .= "$xref$pad $pointer${param_name},\n";
3626                             $desc .= $indentation;
3627                         }
3628                     } else {
3629                         &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
3630                              "Can't parse arg: $params[$j]\nArgs:$SignalPrototypes[$i]");
3631                     }
3632                 }
3633             }
3634             $xref = &MakeXRef ("gpointer", &tagify("gpointer", "type"));
3635             $pad = ' ' x ($type_len - length("gpointer"));
3636             $desc  .= "$xref$pad user_data)";
3637             $desc  .= "</programlisting>\n";
3639             my $flags = $SignalFlags[$i];
3640             my $flags_string = "";
3642             if (defined ($flags)) {
3643               if ($flags =~ m/f/) {
3644                 $flags_string = "<link linkend=\"G-SIGNAL-RUN-FIRST:CAPS\">Run First</link>";
3645               }
3646               elsif ($flags =~ m/l/) {
3647                 $flags_string = "<link linkend=\"G-SIGNAL-RUN-LAST:CAPS\">Run Last</link>";
3648               }
3649               elsif ($flags =~ m/c/) {
3650                 $flags_string = "<link linkend=\"G-SIGNAL-RUN-CLEANUP:CAPS\">Cleanup</link>";
3651                 $flags_string = "Cleanup";
3652               }
3653               if ($flags =~ m/r/) {
3654                 if ($flags_string) { $flags_string .= " / "; }
3655                 $flags_string = "<link linkend=\"G-SIGNAL-NO-RECURSE:CAPS\">No Recursion</link>";
3656               }
3657               if ($flags =~ m/d/) {
3658                 if ($flags_string) { $flags_string .= " / "; }
3659                 $flags_string = "<link linkend=\"G-SIGNAL-DETAILED:CAPS\">Has Details</link>";
3660               }
3661               if ($flags =~ m/a/) {
3662                 if ($flags_string) { $flags_string .= " / "; }
3663                 $flags_string = "<link linkend=\"G-SIGNAL-ACTION:CAPS\">Action</link>";
3664               }
3665               if ($flags =~ m/h/) {
3666                 if ($flags_string) { $flags_string .= " / "; }
3667                 $flags_string = "<link linkend=\"G-SIGNAL-NO-HOOKS:CAPS\">No Hooks</link>";
3668               }
3669             }
3671             $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";
3673             my $parameters = &OutputParamDescriptions ("SIGNAL", $symbol);
3675             $AllSymbols{$symbol} = 1;
3676             if (defined ($SymbolDocs{$symbol})) {
3677                 my $symbol_docs = &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
3679                 $desc .= $symbol_docs;
3681                 if (!IsEmptyDoc($SymbolDocs{$symbol})) {
3682                     $AllDocumentedSymbols{$symbol} = 1;
3683                 }
3684             }
3685             if (defined ($SymbolAnnotations{$symbol})) {
3686                 my $param_desc = $SymbolAnnotations{$symbol};
3687                 my $param_annotations = "";
3688                 ($param_desc,$param_annotations) = &ExpandAnnotation($symbol, $param_desc);
3689                 if ($param_annotations ne "") {
3690                     $desc .= "\n<para>$param_annotations</para>";
3691                 }
3692             }
3693             $desc .= &MakeDeprecationNote($symbol);
3695             $desc .= $parameters;
3696             if ($flags_string) {
3697                 $desc  .= "<para>Flags: $flags_string</para>\n";
3698             }
3699             $desc .= OutputSymbolTraits ($symbol);
3700             $desc .= "</refsect2>";
3701         }
3702     }
3703     return ($synop, $desc);
3707 #############################################################################
3708 # Function    : GetArgs
3709 # Description : Returns the synopsis and detailed description DocBook output
3710 #                for the Args of a given GtkObject subclass.
3711 # Arguments   : $object - the GtkObject subclass, e.g. 'GtkButton'.
3712 #############################################################################
3714 sub GetArgs {
3715     my ($object) = @_;
3716     my $synop = "";
3717     my $desc = "";
3718     my $child_synop = "";
3719     my $child_desc = "";
3720     my $style_synop = "";
3721     my $style_desc = "";
3723     my $i;
3724     for ($i = 0; $i <= $#ArgObjects; $i++) {
3725         if ($ArgObjects[$i] eq $object) {
3726             @TRACE@("Found arg: $ArgNames[$i]\n");
3727             my $name = $ArgNames[$i];
3728             my $flags = $ArgFlags[$i];
3729             my $flags_string = "";
3730             my $kind = "";
3731             my $id_sep = "";
3733             if ($flags =~ m/c/) {
3734                 $kind = "child property";
3735                 $id_sep = "c-";
3736             }
3737             elsif ($flags =~ m/s/) {
3738                 $kind = "style property";
3739                 $id_sep = "s-";
3740             }
3741             else {
3742                 $kind = "property";
3743             }
3745             # Remember only one colon so we don't clash with signals.
3746             my $symbol = "${object}:${name}";
3747             # use two dashes and ev. an extra separator here for the same reason.
3748             my $id = &CreateValidSGMLID ("$object--$id_sep$name");
3750             my $type = $ArgTypes[$i];
3751             my $type_output;
3752             my $range = $ArgRanges[$i];
3753             my $range_output = CreateValidSGML ($range);
3754             my $default = $ArgDefaults[$i];
3755             my $default_output = CreateValidSGML ($default);
3757             if ($type eq "GtkString") {
3758                 $type = "char&#160;*";
3759             }
3760             if ($type eq "GtkSignal") {
3761                 $type = "GtkSignalFunc, gpointer";
3762                 $type_output = &MakeXRef ("GtkSignalFunc") . ", "
3763                     . &MakeXRef ("gpointer");
3764             } elsif ($type =~ m/^(\w+)\*$/) {
3765                 $type_output = &MakeXRef ($1, &tagify($1, "type")) . "&#160;*";
3766             } else {
3767                 $type_output = &MakeXRef ($type, &tagify($type, "type"));
3768             }
3770             if ($flags =~ m/r/) {
3771                 $flags_string = "Read";
3772             }
3773             if ($flags =~ m/w/) {
3774                 if ($flags_string) { $flags_string .= " / "; }
3775                 $flags_string .= "Write";
3776             }
3777             if ($flags =~ m/x/) {
3778                 if ($flags_string) { $flags_string .= " / "; }
3779                 $flags_string .= "Construct";
3780             }
3781             if ($flags =~ m/X/) {
3782                 if ($flags_string) { $flags_string .= " / "; }
3783                 $flags_string .= "Construct Only";
3784             }
3786             $AllSymbols{$symbol} = 1;
3787             my $blurb = "";
3788             if (defined($SymbolDocs{$symbol}) &&
3789                 !IsEmptyDoc($SymbolDocs{$symbol})) {
3790                 $blurb = &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
3791                 @TRACE@(".. [$SymbolDocs{$symbol}][$blurb]\n");
3792                 $AllDocumentedSymbols{$symbol} = 1;
3793             }
3794             else {
3795                 if ($ArgBlurbs[$i] ne "") {
3796                     $blurb = "<para>" . &CreateValidSGML ($ArgBlurbs[$i]) . "</para>";
3797                     $AllDocumentedSymbols{$symbol} = 1;
3798                 } else {
3799                     # FIXME: print a warning?
3800                     @TRACE@(".. no description\n");
3801                 }
3802             }
3804             my $pad1 = " " x (24 - length ($name));
3806             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";
3807             my $arg_desc = "<refsect2 id=\"$id\" role=\"property\"><title>The <literal>“$name”</literal> $kind</title>\n";
3808             $arg_desc .= MakeIndexterms($symbol, $id);
3809             $arg_desc .= "\n";
3810             $arg_desc .= OutputSymbolExtraLinks($symbol);
3812             $arg_desc .= "<programlisting>  “$name”$pad1 $type_output</programlisting>\n";
3813             $arg_desc .= $blurb;
3814             if (defined ($SymbolAnnotations{$symbol})) {
3815                 my $param_desc = $SymbolAnnotations{$symbol};
3816                 my $param_annotations = "";
3817                 ($param_desc,$param_annotations) = &ExpandAnnotation($symbol, $param_desc);
3818                 if ($param_annotations ne "") {
3819                     $arg_desc .= "\n<para>$param_annotations</para>";
3820                 }
3821             }
3822             $arg_desc .= &MakeDeprecationNote($symbol);
3824             if ($flags_string) {
3825               $arg_desc  .= "<para>Flags: $flags_string</para>\n";
3826             }
3827             if ($range ne "") {
3828                 $arg_desc .= "<para>Allowed values: $range_output</para>\n";
3829             }
3830             if ($default ne "") {
3831                 $arg_desc .= "<para>Default value: $default_output</para>\n";
3832             }
3833             $arg_desc .= OutputSymbolTraits ($symbol);
3834             $arg_desc .= "</refsect2>\n";
3836             if ($flags =~ m/c/) {
3837                 $child_synop .= $arg_synop;
3838                 $child_desc .= $arg_desc;
3839             }
3840             elsif ($flags =~ m/s/) {
3841                 $style_synop .= $arg_synop;
3842                 $style_desc .= $arg_desc;
3843             }
3844             else {
3845                 $synop .= $arg_synop;
3846                 $desc .= $arg_desc;
3847             }
3848         }
3849     }
3850     return ($synop, $child_synop, $style_synop, $desc, $child_desc, $style_desc);
3854 #############################################################################
3855 # Function    : ReadSourceDocumentation
3856 # Description : This reads in the documentation embedded in comment blocks
3857 #                in the source code (for Gnome).
3859 #                Parameter descriptions override any in the template files.
3860 #                Function descriptions are placed before any description from
3861 #                the template files.
3863 #                It recursively descends the source directory looking for .c
3864 #                files and scans them looking for specially-formatted comment
3865 #                blocks.
3867 # Arguments   : $source_dir - the directory to scan.
3868 #############m###############################################################
3870 sub ReadSourceDocumentation {
3871     my ($source_dir) = @_;
3872     my ($file, $dir, @suffix_list, $suffix);
3874     # prepend entries from @SOURCE_DIR
3875     for my $dir (@SOURCE_DIRS) {
3876         # Check if the filename is in the ignore list.
3877         if ($source_dir =~ m%^\Q$dir\E/(.*)$% and $IGNORE_FILES =~ m/(\s|^)\Q$1\E(\s|$)/) {
3878             @TRACE@("Skipping source directory: $source_dir");
3879             return;
3880         } else {
3881             @TRACE@("No match for: ".($1 || $source_dir));
3882         }
3883     }
3885     @TRACE@("Scanning source directory: $source_dir");
3887     # This array holds any subdirectories found.
3888     my (@subdirs) = ();
3890     @suffix_list = split (/,/, $SOURCE_SUFFIXES);
3892     opendir (SRCDIR, $source_dir)
3893         || die "Can't open source directory $source_dir: $!";
3895     foreach $file (readdir (SRCDIR)) {
3896       if ($file =~ /^\./) {
3897         next;
3898       } elsif (-d "$source_dir/$file") {
3899         push (@subdirs, $file);
3900       } elsif (@suffix_list) {
3901         foreach $suffix (@suffix_list) {
3902           if ($file =~ m/\.\Q${suffix}\E$/) {
3903             &ScanSourceFile ("$source_dir/$file");
3904           }
3905         }
3906       } elsif ($file =~ m/\.[ch]$/) {
3907         &ScanSourceFile ("$source_dir/$file");
3908       }
3909     }
3910     closedir (SRCDIR);
3912     # Now recursively scan the subdirectories.
3913     foreach $dir (@subdirs) {
3914         &ReadSourceDocumentation ("$source_dir/$dir");
3915     }
3919 #############################################################################
3920 # Function    : ScanSourceFile
3921 # Description : Scans one source file looking for specially-formatted comment
3922 #                blocks. Later &MergeSourceDocumentation is used to merge any
3923 #                documentation found with the documentation already read in
3924 #                from the template files.
3926 # Arguments   : $file - the file to scan.
3927 #############################################################################
3929 sub ScanSourceFile {
3930     my ($file) = @_;
3931     my $basename;
3933     # prepend entries from @SOURCE_DIR
3934     for my $dir (@SOURCE_DIRS) {
3935         # Check if the filename is in the ignore list.
3936         if ($file =~ m%^\Q$dir\E/(.*)$% and $IGNORE_FILES =~ m/(\s|^)\Q$1\E(\s|$)/) {
3937             @TRACE@("Skipping source file: $file");
3938             return;
3939         }
3940     }
3942     if ($file =~ m/^.*[\/\\]([^\/\\]*)$/) {
3943         $basename = $1;
3944     } else {
3945         &LogWarning ($file, 1, "Can't find basename for this filename.");
3946         $basename = $file;
3947     }
3949     # Check if the basename is in the list of files to ignore.
3950     if ($IGNORE_FILES =~ m/(\s|^)\Q${basename}\E(\s|$)/) {
3951         @TRACE@("Skipping source file: $file");
3952         return;
3953     }
3955     @TRACE@("Scanning source file: $file");
3957     open (SRCFILE, $file)
3958         || die "Can't open $file: $!";
3959     my $in_comment_block = 0;
3960     my $symbol;
3961     my $in_part = "";
3962     my ($description, $return_desc);
3963     my ($since_desc, $stability_desc, $deprecated_desc);
3964     my $current_param;
3965     my @params;
3966     while (<SRCFILE>) {
3967         # Look for the start of a comment block.
3968         if (!$in_comment_block) {
3969             if (m%^\s*/\*.*\*/%) {
3970                 #one-line comment - not gtkdoc
3971             } elsif (m%^\s*/\*\*\s%) {
3972                 @TRACE@("Found comment block start\n");
3974                 $in_comment_block = 1;
3976                 # Reset all the symbol data.
3977                 $symbol = "";
3978                 $in_part = "";
3979                 $description = "";
3980                 $return_desc = "";
3981                 $since_desc = "";
3982                 $deprecated_desc = "";
3983                 $stability_desc = "";
3984                 $current_param = -1;
3985                 @params = ();
3986             }
3987             next;
3988         }
3990         # We're in a comment block. Check if we've found the end of it.
3991         if (m%^\s*\*+/%) {
3992             if (!$symbol) {
3993                 # maybe its not even meant to be a gtk-doc comment?
3994                 &LogWarning ($file, $., "Symbol name not found at the start of the comment block.");
3995             } else {
3996                 # Add the return value description onto the end of the params.
3997                 if ($return_desc) {
3998                     # TODO(ensonic): check for duplicated Return docs
3999                     # &LogWarning ($file, $., "Multiple Returns for $symbol.");
4000                     push (@params, "Returns");
4001                     push (@params, $return_desc);
4002                 }
4003                 # Convert special characters
4004                 $description = &ConvertSGMLChars ($symbol, $description);
4005                 my $k;
4006                 for ($k = 1; $k <= $#params; $k += $PARAM_FIELD_COUNT) {
4007                     $params[$k] = &ConvertSGMLChars ($symbol, $params[$k]);
4008                 }
4010                 # Handle Section docs
4011                 if ($symbol =~ m/SECTION:\s*(.*)/) {
4012                     my $real_symbol=$1;
4013                     my $key;
4015                     if (scalar %KnownSymbols) {
4016                         if ((! defined($KnownSymbols{"$TMPL_DIR/$real_symbol:Long_Description"})) || $KnownSymbols{"$TMPL_DIR/$real_symbol:Long_Description"} != 1) {
4017                             &LogWarning ($file, $., "Section $real_symbol is not defined in the $MODULE-sections.txt file.");
4018                         }
4019                     }
4021                     @TRACE@("SECTION DOCS found in source for : '$real_symbol'\n");
4022                     for ($k = 0; $k <= $#params; $k += $PARAM_FIELD_COUNT) {
4023                         @TRACE@("   '".$params[$k]."'\n");
4024                         $params[$k] = "\L$params[$k]";
4025                         undef $key;
4026                         if ($params[$k] eq "short_description") {
4027                             $key = "$TMPL_DIR/$real_symbol:Short_Description";
4028                         } elsif ($params[$k] eq "see_also") {
4029                             $key = "$TMPL_DIR/$real_symbol:See_Also";
4030                         } elsif ($params[$k] eq "title") {
4031                             $key = "$TMPL_DIR/$real_symbol:Title";
4032                         } elsif ($params[$k] eq "stability") {
4033                             $key = "$TMPL_DIR/$real_symbol:Stability_Level";
4034                         } elsif ($params[$k] eq "section_id") {
4035                             $key = "$TMPL_DIR/$real_symbol:Section_Id";
4036                         } elsif ($params[$k] eq "include") {
4037                             $key = "$TMPL_DIR/$real_symbol:Include";
4038                         } elsif ($params[$k] eq "image") {
4039                             $key = "$TMPL_DIR/$real_symbol:Image";
4040                         }
4041                         if (defined($key)) {
4042                             $SourceSymbolDocs{$key}=$params[$k+1];
4043                             $SourceSymbolSourceFile{$key} = $file;
4044                             $SourceSymbolSourceLine{$key} = $.;
4045                         }
4046                     }
4047                     $SourceSymbolDocs{"$TMPL_DIR/$real_symbol:Long_Description"}=$description;
4048                     $SourceSymbolSourceFile{"$TMPL_DIR/$real_symbol:Long_Description"} = $file;
4049                     $SourceSymbolSourceLine{"$TMPL_DIR/$real_symbol:Long_Description"} = $.;
4050                     #$SourceSymbolTypes{$symbol} = "SECTION";
4051                 } else {
4052                     @TRACE@("SYMBOL DOCS found in source for : '$symbol' ",length($description), "\n");
4053                     $SourceSymbolDocs{$symbol} = $description;
4054                     $SourceSymbolParams{$symbol} = [ @params ];
4055                     # FIXME $SourceSymbolTypes{$symbol} = "STRUCT,SIGNAL,ARG,FUNCTION,MACRO";
4056                     #if (defined $DeclarationTypes{$symbol}) {
4057                     #    $SourceSymbolTypes{$symbol} = $DeclarationTypes{$symbol}
4058                     #}
4059                     $SourceSymbolSourceFile{$symbol} = $file;
4060                     $SourceSymbolSourceLine{$symbol} = $.;
4061                 }                
4063                 if ($since_desc) {
4064                      ($since_desc, my @extra_lines) = split ("\n", $since_desc);
4065                      $since_desc =~ s/^\s+//;
4066                      $since_desc =~ s/\s+$//;
4067                      @TRACE@("Since($symbol) : [$since_desc]\n");
4068                      $Since{$symbol} = &ConvertSGMLChars ($symbol, $since_desc);
4069                      if(scalar @extra_lines) {
4070                          &LogWarning ($file, $., "multi-line since docs found");
4071                      }
4072                 }
4074                 if ($stability_desc) {
4075                     $stability_desc = &ParseStabilityLevel($stability_desc, $file, $., "Stability level for $symbol");
4076                     $StabilityLevel{$symbol} = &ConvertSGMLChars ($symbol, $stability_desc);
4077                 }
4079                 if ($deprecated_desc) {
4080                     if (!exists $Deprecated{$symbol}) {
4081                          # don't warn for signals and properties
4082                          #if ($symbol !~ m/::?(.*)/) {
4083                          if (defined $DeclarationTypes{$symbol}) {
4084                              &LogWarning ($file, $.,
4085                                  "$symbol is deprecated in the inline comments, but no deprecation guards were found around the declaration.".
4086                                  " (See the --deprecated-guards option for gtkdoc-scan.)");
4087                          }
4088                     }
4089                     $Deprecated{$symbol} = &ConvertSGMLChars ($symbol, $deprecated_desc);
4090                 }
4091             }
4093             $in_comment_block = 0;
4094             next;
4095         }
4097         # Get rid of ' * ' at start of every line in the comment block.
4098         s%^\s*\*\s?%%;
4099         # But make sure we don't get rid of the newline at the end.
4100         if (!$_) {
4101             $_ = "\n";
4102         }
4103         @TRACE@("scanning :$_");
4105         # If we haven't found the symbol name yet, look for it.
4106         if (!$symbol) {
4107             if (m%^\s*(SECTION:\s*\S+)%) {
4108                 $symbol = $1;
4109                 @TRACE@("SECTION DOCS found in source for : '$symbol'\n");
4110             } elsif (m%^\s*([\w:-]*\w)\s*:?\s*(\([-A-Za-z0-9._() ]+?\)\s*)*$%) {
4111                 $symbol = $1;
4112                 my $annotation = $2;
4113                 @TRACE@("SYMBOL DOCS found in source for : '$symbol'\n");
4114                 if (defined($annotation)) {
4115                     chomp($annotation);
4116                     if ($annotation ne "") {
4117                         $SymbolAnnotations{$symbol} = $annotation;
4118                         @TRACE@("remaining text for $symbol: '$annotation'\n");
4119                     }
4120                 }
4121             }
4122             next;
4123         }
4125         if ($in_part eq "description") {
4126             # Get rid of 'Description:'
4127             s%^\s*Description:%%;
4128         }
4130         if (m%^\s*(returns|return\s+value):%i) {
4131             # we're in param section and have not seen the blank line
4132             if($in_part ne "") {
4133               $return_desc = $';
4134               $in_part = "return";
4135               next;
4136             }
4137         } elsif (m%^\s*since:%i) {
4138             # we're in param section and have not seen the blank line
4139             if($in_part ne "param") {
4140               $since_desc = $';
4141               $in_part = "since";
4142               next;
4143             }
4144         } elsif (m%^\s*deprecated:%i) {
4145             # we're in param section and have not seen the blank line
4146             if($in_part ne "param") {
4147               $deprecated_desc = $';
4148               $in_part = "deprecated";
4149               next;
4150             }
4151         } elsif (m%^\s*stability:%i) {
4152             $stability_desc = $';
4153             $in_part = "stability";
4154             next;
4155         }
4157         if ($in_part eq "description") {
4158             $description .= $_;
4159             next;
4160         } elsif ($in_part eq "return") {
4161             $return_desc .= $_;
4162             next;
4163         } elsif ($in_part eq "since") {
4164             $since_desc .= $_;
4165             next;
4166         } elsif ($in_part eq "stability") {
4167             $stability_desc .= $_;
4168             next;
4169         } elsif ($in_part eq "deprecated") {
4170             $deprecated_desc .= $_;
4171             next;
4172         }
4174         # We must be in the parameters. Check for the empty line below them.
4175         if (m%^\s*$%) {
4176             $in_part = "description";
4177             next;
4178         }
4180         # Look for a parameter name.
4181         if (m%^\s*@(\S+)\s*:\s*%) {
4182             my $param_name = $1;
4183             my $param_desc = $';
4185             @TRACE@("Found parameter: $param_name\n");
4186             # Allow varargs variations
4187             if ($param_name =~ m/^\.\.\.$/) {
4188                 $param_name = "...";
4189             }
4190             @TRACE@("Found param for symbol $symbol : '$param_name'= '$_'");
4192             push (@params, $param_name);
4193             push (@params, $param_desc);
4194             $current_param += $PARAM_FIELD_COUNT;
4195             $in_part = "param";
4196             next;
4197         } elsif ($in_part eq "") {
4198             @TRACE@("continuation for $symbol annotation '$_'");
4199             my $annotation = $_;
4200             $annotation =~ s/^\s+|\s+$//g ; 
4201             $SymbolAnnotations{$symbol} .= $annotation;
4202             next;
4203         }
4205         # We must be in the middle of a parameter description, so add it on
4206         # to the last element in @params.
4207         if ($current_param == -1) {
4208             &LogWarning ($file, $., "Parsing comment block file : parameter expected, but got '$_'");
4209         } else {
4210             $params[$#params] .= $_;
4211         }
4212     }
4213     close (SRCFILE);
4216 #############################################################################
4217 # Function    : OutputMissingDocumentation
4218 # Description : Outputs report of documentation coverage to a file
4220 # Arguments   : none
4221 #############################################################################
4223 sub OutputMissingDocumentation {
4224     my $old_undocumented_file = "$ROOT_DIR/$MODULE-undocumented.txt";
4225     my $new_undocumented_file = "$ROOT_DIR/$MODULE-undocumented.new";
4227     my $n_documented = 0;
4228     my $n_incomplete = 0;
4229     my $total = 0;
4230     my $symbol;
4231     my $percent;
4232     my $msg;
4233     my $buffer = "";
4234     my $buffer_deprecated = "";
4235     my $buffer_descriptions = "";
4237     open(UNDOCUMENTED, ">$new_undocumented_file")
4238       || die "Can't create $new_undocumented_file";
4240     foreach $symbol (sort (keys (%AllSymbols))) {
4241         # FIXME: should we print LogWarnings for undocumented stuff?
4242         # DEBUG
4243         #my $ssfile = &GetSymbolSourceFile($symbol);
4244         #my $ssline = &GetSymbolSourceLine($symbol);
4245         #my $location = "defined at " . (defined($ssfile)?$ssfile:"?") . ":" . (defined($ssline)?$ssline:"0") . "\n";
4246         # DEBUG
4247         if ($symbol !~ /:(Title|Long_Description|Short_Description|See_Also|Stability_Level|Include|Section_Id|Image)/) {
4248             $total++;
4249             if (exists ($AllDocumentedSymbols{$symbol})) {
4250                 $n_documented++;
4251                 if (exists ($AllIncompleteSymbols{$symbol})) {
4252                     $n_incomplete++;
4253                     $buffer .= $symbol . " (" . $AllIncompleteSymbols{$symbol} . ")\n";
4254                     #$buffer .= "\t0: ".$location;
4255                 }
4256             } elsif (exists $Deprecated{$symbol}) {
4257                 if (exists ($AllIncompleteSymbols{$symbol})) {
4258                     $n_incomplete++;
4259                     $buffer_deprecated .= $symbol . " (" . $AllIncompleteSymbols{$symbol} . ")\n";
4260                     #$buffer .= "\t1a: ".$location;
4261                 } else {
4262                     $buffer_deprecated .= $symbol . "\n";
4263                     #$buffer .= "\t1b: ".$location;
4264                 }
4265             } else {
4266                 if (exists ($AllIncompleteSymbols{$symbol})) {
4267                     $n_incomplete++;
4268                     $buffer .= $symbol . " (" . $AllIncompleteSymbols{$symbol} . ")\n";
4269                     #$buffer .= "\t2a: ".$location;
4270                 } else {
4271                     $buffer .= $symbol . "\n";
4272                     #$buffer .= "\t2b: ".$location;
4273                 }
4274             }
4275         } elsif ($symbol =~ /:(Long_Description|Short_Description)/) {
4276             $total++;
4277             if (((exists ($SymbolDocs{$symbol})) && (length ($SymbolDocs{$symbol}) > 0))
4278             || ((exists ($AllDocumentedSymbols{$symbol})) && (length ($AllDocumentedSymbols{$symbol}) > 0))) {
4279               $n_documented++;
4280             } else {
4281               # cut off the leading namespace ($TMPL_DIR)
4282               $symbol =~ m/^.*\/(.*)$/;
4283               $buffer_descriptions .= $1 . "\n";
4284             }
4285         }
4286     }
4288     if ($total == 0) {
4289       $percent = 100;
4290     } else {
4291       $percent = ($n_documented / $total) * 100.0;
4292     }
4294     printf UNDOCUMENTED "%.0f%% symbol docs coverage.\n", $percent;
4295     print UNDOCUMENTED "$n_documented symbols documented.\n";
4296     print UNDOCUMENTED "$n_incomplete symbols incomplete.\n";
4297     print UNDOCUMENTED ($total - $n_documented) . " not documented.\n";
4299     if ($buffer_deprecated ne "") {
4300       $buffer .= "\n" . $buffer_deprecated;
4301     }
4302     if ($buffer_descriptions ne "") {
4303       $buffer .= "\n" . $buffer_descriptions;
4304     }
4305     if ($buffer ne "") {
4306       print UNDOCUMENTED "\n\n$buffer";
4307     }
4308     close (UNDOCUMENTED);
4310     return &UpdateFileIfChanged ($old_undocumented_file, $new_undocumented_file, 0);
4312     printf "%.0f%% symbol docs coverage", $percent;
4313     print "($n_documented symbols documented, $n_incomplete symbols incomplete, " . ($total - $n_documented) . " not documented)\n";
4314     print "See $MODULE-undocumented.txt for a list of missing docs.\nThe doc coverage percentage doesn't include intro sections.\n";
4318 #############################################################################
4319 # Function    : OutputUndeclaredSymbols
4320 # Description : Outputs symbols that are listed in the section file, but not
4321 #               declaration is found in the sources
4323 # Arguments   : none
4324 #############################################################################
4326 sub OutputUndeclaredSymbols {
4327     my $old_undeclared_file = "$ROOT_DIR/$MODULE-undeclared.txt";
4328     my $new_undeclared_file = "$ROOT_DIR/$MODULE-undeclared.new";
4330     open(UNDECLARED, ">$new_undeclared_file")
4331         || die "Can't create $new_undeclared_file";
4333     if (%UndeclaredSymbols) {
4334         print UNDECLARED (join("\n", sort keys %UndeclaredSymbols));
4335         print UNDECLARED "\n";
4336         print "See $MODULE-undeclared.txt for the list of undeclared symbols.\n"
4337     }
4338     close(UNDECLARED);
4340     return &UpdateFileIfChanged ($old_undeclared_file, $new_undeclared_file, 0);
4343 #############################################################################
4344 # Function    : OutputUnusedSymbols
4345 # Description : Outputs symbols that are documented in comments, but not
4346 #               declared in the sources
4348 # Arguments   : none
4349 #############################################################################
4351 sub OutputUnusedSymbols {
4352     my $num_unused = 0;
4353     my $old_unused_file = "$ROOT_DIR/$MODULE-unused.txt";
4354     my $new_unused_file = "$ROOT_DIR/$MODULE-unused.new";
4356     open (UNUSED, ">$new_unused_file")
4357         || die "Can't open $new_unused_file";
4358     my ($symbol);
4359     foreach $symbol (sort keys (%Declarations)) {
4360         if (!defined ($DeclarationOutput{$symbol})) {
4361             print (UNUSED "$symbol\n");
4362             $num_unused++;
4363         }
4364     }
4365     foreach $symbol (sort (keys (%AllUnusedSymbols))) {
4366         print (UNUSED "$symbol(" . $AllUnusedSymbols{$symbol} . ")\n");
4367         $num_unused++;
4368     }
4369     close (UNUSED);
4370     if ($num_unused != 0) {
4371         &LogWarning ($old_unused_file, 1, "$num_unused unused declarations.".
4372             "They should be added to $MODULE-sections.txt in the appropriate place.");
4373     }
4375     return &UpdateFileIfChanged ($old_unused_file, $new_unused_file, 0);
4379 #############################################################################
4380 # Function    : OutputAllSymbols
4381 # Description : Outputs list of all symbols to a file
4383 # Arguments   : none
4384 #############################################################################
4386 sub OutputAllSymbols {
4387      my $n_documented = 0;
4388      my $total = 0;
4389      my $symbol;
4390      my $percent;
4391      my $msg;
4393      open (SYMBOLS, ">$ROOT_DIR/$MODULE-symbols.txt")
4394           || die "Can't create $ROOT_DIR/$MODULE-symbols.txt: $!";
4396      foreach $symbol (sort (keys (%AllSymbols))) {
4397           print SYMBOLS $symbol . "\n";
4398      }
4400      close (SYMBOLS);
4403 #############################################################################
4404 # Function    : OutputSymbolsWithoutSince
4405 # Description : Outputs list of all symbols without a since tag to a file
4407 # Arguments   : none
4408 #############################################################################
4410 sub OutputSymbolsWithoutSince {
4411      my $n_documented = 0;
4412      my $total = 0;
4413      my $symbol;
4414      my $percent;
4415      my $msg;
4417      open (SYMBOLS, ">$ROOT_DIR/$MODULE-nosince.txt")
4418           || die "Can't create $ROOT_DIR/$MODULE-nosince.txt: $!";
4420      foreach $symbol (sort (keys (%SourceSymbolDocs))) {
4421          if (!defined $Since{$symbol}) {
4422              print SYMBOLS $symbol . "\n";
4423          }
4424      }
4426      close (SYMBOLS);
4430 #############################################################################
4431 # Function    : MergeSourceDocumentation
4432 # Description : This merges documentation read from a source file into the
4433 #                documentation read in from a template file.
4435 #                Parameter descriptions override any in the template files.
4436 #                Function descriptions are placed before any description from
4437 #                the template files.
4439 # Arguments   : none
4440 #############################################################################
4442 sub MergeSourceDocumentation {
4443     my $symbol;
4444     my @Symbols;
4446     if (scalar %SymbolDocs) {
4447         @Symbols=keys (%SymbolDocs);
4448         @TRACE@("num existing entries: ".(scalar @Symbols)."\n");
4449     }
4450     else {
4451         # filter scanned declarations, with what we suppress from -sections.txt
4452         my %tmp = ();
4453         foreach $symbol (keys (%Declarations)) {
4454             if (defined($KnownSymbols{$symbol}) && $KnownSymbols{$symbol} == 1) {
4455                 $tmp{$symbol}=1;
4456             }
4457         }
4458         # , add the rest from -sections.txt
4459         foreach $symbol (keys (%KnownSymbols)) {
4460             if ($KnownSymbols{$symbol} == 1) {
4461                 $tmp{$symbol}=1;
4462             }
4463         }
4464         # and add whats found in the source
4465         foreach $symbol (keys (%SourceSymbolDocs)) {
4466             $tmp{$symbol}=1;
4467         }
4468         @Symbols = keys (%tmp);
4469         @TRACE@("num source entries: ".(scalar @Symbols)."\n");
4470     }
4471     foreach $symbol (@Symbols) {
4472         $AllSymbols{$symbol} = 1;
4474         my $have_tmpl_docs = 0;
4476         ## see if the symbol is documented in template
4477         my $tmpl_doc = defined ($SymbolDocs{$symbol}) ? $SymbolDocs{$symbol} : "";
4478         my $check_tmpl_doc =$tmpl_doc;
4479         # remove all xml-tags and whitespaces
4480         $check_tmpl_doc =~ s/<.*?>//g;
4481         $check_tmpl_doc =~ s/\s//g;
4482         # anything left ?
4483         if ($check_tmpl_doc ne "") {
4484             $have_tmpl_docs = 1;
4485         } else {
4486             # if the docs have just an empty para, don't merge that.
4487             $check_tmpl_doc = $tmpl_doc;
4488             $check_tmpl_doc =~ s/(\s|\n)//msg;
4489             if ($check_tmpl_doc eq "<para></para>") {
4490                $tmpl_doc = "";
4491             }
4492         }
4494         if (exists ($SourceSymbolDocs{$symbol})) {
4495             my $type = $DeclarationTypes {$symbol};
4497             @TRACE@("merging [$symbol] from source\n");
4499             my $item = "Parameter";
4500             if (defined ($type)) {
4501                 if ($type eq 'STRUCT') {
4502                     $item = "Field";
4503                 } elsif ($type eq 'ENUM') {
4504                     $item = "Value";
4505                 } elsif ($type eq 'UNION') {
4506                     $item = "Field";
4507                 }
4508             } else {
4509                 $type="SIGNAL";
4510             }
4512             my $src_doc = $SourceSymbolDocs{$symbol};
4513             # remove leading and training whitespaces
4514             $src_doc =~ s/^\s+//;
4515             $src_doc =~ s/\s+$//;
4517             # Don't output warnings for overridden titles as titles are
4518             # automatically generated in the -sections.txt file, and thus they
4519             # are often overridden.
4520             if ($have_tmpl_docs && $symbol !~ m/:Title$/) {
4521                 # check if content is different
4522                 if ($tmpl_doc ne $src_doc) {
4523                     #print "[$tmpl_doc] [$src_doc]\n";
4524                     &LogWarning ($SourceSymbolSourceFile{$symbol}, $SourceSymbolSourceLine{$symbol},
4525                         "Documentation in template ".$SymbolSourceFile{$symbol}.":".$SymbolSourceLine{$symbol}." for $symbol being overridden by inline comments.");
4526                 }
4527             }
4529             if ($src_doc ne "") {
4530                  $AllDocumentedSymbols{$symbol} = 1;
4531             }
4533             # Do not add <para> to nothing, it breaks missing docs checks.
4534             my $src_doc_para = "";
4535             if ($src_doc ne "") {
4536                 $src_doc_para = $src_doc;
4537             }
4539             if ($symbol =~ m/$TMPL_DIR\/.+:Long_Description/) {
4540                 $SymbolDocs{$symbol} = "$src_doc_para$tmpl_doc";
4541             } elsif ($symbol =~ m/$TMPL_DIR\/.+:.+/) {
4542                 # For the title/summary/see also section docs we don't want to
4543                 # add any <para> tags.
4544                 $SymbolDocs{$symbol} = "$src_doc"
4545             } else {
4546                 $SymbolDocs{$symbol} = "$src_doc_para$tmpl_doc";
4547             }
4549             # merge parameters
4550             if ($symbol =~ m/.*::.*/) {
4551                 # For signals we prefer the param names from the source docs,
4552                 # since the ones from the templates are likely to contain the
4553                 # artificial argn names which are generated by gtkdoc-scangobj.
4554                 $SymbolParams{$symbol} = $SourceSymbolParams{$symbol};
4555                 # FIXME: we need to check for empty docs here as well!
4556             } else {
4557                 # The templates contain the definitive parameter names and order,
4558                 # so we will not change that. We only override the actual text.
4559                 my $tmpl_params = $SymbolParams{$symbol};
4560                 if (!defined ($tmpl_params)) {
4561                     @TRACE@("No merge needed for $symbol\n");
4562                     $SymbolParams{$symbol} = $SourceSymbolParams{$symbol};
4563                     #  FIXME: we still like to get the number of params and merge
4564                     #  1) we would noticed that params have been removed/renamed
4565                     #  2) we would catch undocumented params
4566                     #  params are not (yet) exported in -decl.txt so that we
4567                     #  could easily grab them :/
4568                 } else {
4569                     my $params = $SourceSymbolParams{$symbol};
4570                     my $j;
4571                     @TRACE@("Merge needed for $symbol, tmpl_params: ",$#$tmpl_params,", source_params: ",$#$params," \n");
4572                     for ($j = 0; $j <= $#$tmpl_params; $j += $PARAM_FIELD_COUNT) {
4573                         my $tmpl_param_name = $$tmpl_params[$j];
4575                         # Try to find the param in the source comment documentation.
4576                         my $found = 0;
4577                         my $k;
4578                         @TRACE@("  try merge param $tmpl_param_name\n");
4579                         for ($k = 0; $k <= $#$params; $k += $PARAM_FIELD_COUNT) {
4580                             my $param_name = $$params[$k];
4581                             my $param_desc = $$params[$k + 1];
4583                             @TRACE@("    test param  $param_name\n");
4584                             # We accept changes in case, since the Gnome source
4585                             # docs contain a lot of these.
4586                             if ("\L$param_name" eq "\L$tmpl_param_name") {
4587                                 $found = 1;
4589                                 # Override the description.
4590                                 $$tmpl_params[$j + 1] = $param_desc;
4592                                 # Set the name to "" to mark it as used.
4593                                 $$params[$k] = "";
4594                                 last;
4595                             }
4596                         }
4598                         # If it looks like the parameters are there, but not
4599                         # in the right place, try to explain a bit better.
4600                         if ((!$found) && ($src_doc =~ m/\@$tmpl_param_name:/)) {
4601                             &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4602                                 "Parameters for $symbol must start on the line immediately after the function or macro name.");
4603                         }
4604                     }
4606                     # Now we output a warning if parameters have been described which
4607                     # do not exist.
4608                     for ($j = 0; $j <= $#$params; $j += $PARAM_FIELD_COUNT) {
4609                         my $param_name = $$params[$j];
4610                         if ($param_name) {
4611                             # the template builder cannot detect if a macro returns
4612                             # a result or not
4613                             if(($type eq "MACRO") && ($param_name eq "Returns")) {
4614                                 # FIXME: do we need to add it then to tmpl_params[] ?
4615                                 my $num=$#$tmpl_params;
4616                                 @TRACE@("  adding Returns: to macro docs for $symbol.\n");
4617                                 $$tmpl_params[$num+1]="Returns";
4618                                 $$tmpl_params[$num+2]=$$params[$j+1];
4619                                 next;
4620                             }
4621                             &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4622                                 "$item described in source code comment block but does not exist. $type: $symbol $item: $param_name.");
4623                         }
4624                     }
4625                 }
4626             }
4627         } else {
4628             if ($have_tmpl_docs) {
4629                 $AllDocumentedSymbols{$symbol} = 1;
4630                 @TRACE@("merging [$symbol] from template\n");
4631             }
4632             else {
4633                 @TRACE@("[$symbol] undocumented\n");
4634             }
4635         }
4637         # if this symbol is documented, check if docs are complete
4638         $check_tmpl_doc = defined ($SymbolDocs{$symbol}) ? $SymbolDocs{$symbol} : "";
4639         # remove all xml-tags and whitespaces
4640         $check_tmpl_doc =~ s/<.*?>//g;
4641         $check_tmpl_doc =~ s/\s//g;
4642         if ($check_tmpl_doc ne "") {
4643             my $tmpl_params = $SymbolParams{$symbol};
4644             if (defined ($tmpl_params)) {
4645                 my $type = $DeclarationTypes {$symbol};
4647                 my $item = "Parameter";
4648                 if (defined ($type)) {
4649                     if ($type eq 'STRUCT') {
4650                         $item = "Field";
4651                     } elsif ($type eq 'ENUM') {
4652                         $item = "Value";
4653                     } elsif ($type eq 'UNION') {
4654                         $item = "Field";
4655                     }
4656                 } else {
4657                     $type="SIGNAL";
4658                 }
4660                 @TRACE@("Check param docs for $symbol, tmpl_params: ",$#$tmpl_params," entries, type=$type\n");
4662                 if ($#$tmpl_params > 0) {
4663                     my $j;
4664                     for ($j = 0; $j <= $#$tmpl_params; $j += $PARAM_FIELD_COUNT) {
4665                         # Output a warning if the parameter is empty and
4666                         # remember for stats.
4667                         my $tmpl_param_name = $$tmpl_params[$j];
4668                         my $tmpl_param_desc = $$tmpl_params[$j + 1];
4669                         if ($tmpl_param_name ne "void" && $tmpl_param_desc !~ m/\S/) {
4670                             if (exists ($AllIncompleteSymbols{$symbol})) {
4671                                 $AllIncompleteSymbols{$symbol}.=", ".$tmpl_param_name;
4672                             } else {
4673                                 $AllIncompleteSymbols{$symbol}=$tmpl_param_name;
4674                             }
4675                             &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4676                                 "$item description for $symbol"."::"."$tmpl_param_name is missing in source code comment block.");
4677                         }
4678                     }
4679                 }
4680                 else {
4681                     if ($#$tmpl_params == 0) {
4682                         $AllIncompleteSymbols{$symbol}="<items>";
4683                         &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4684                             "$item descriptions for $symbol are missing in source code comment block.");
4685                     }
4686                     # $#$tmpl_params==-1 means we don't know about parameters
4687                     # this unfortunately does not tell if there should be some
4688                 }
4689             }
4690         }
4691    }
4692    @TRACE@("num doc entries: ".(scalar %SymbolDocs)."\n");
4695 #############################################################################
4696 # Function    : IsEmptyDoc
4697 # Description : Check if a doc-string is empty. Its also regarded as empty if
4698 #               it only consist of whitespace or e.g. FIXME.
4699 # Arguments   : the doc-string
4700 #############################################################################
4701 sub IsEmptyDoc {
4702     my ($doc) = @_;
4704     if ($doc =~ /^\s*$/) {
4705         return 1;
4706     }
4708     if ($doc =~ /^\s*<para>\s*(FIXME)?\s*<\/para>\s*$/) {
4709         return 1;
4710     }
4712     return 0;
4715 #############################################################################
4716 # Function    : ConvertMarkDown
4717 # Description : Converts mark down syntax to the respective docbook.
4718 #               http://de.wikipedia.org/wiki/Markdown
4719 #               Inspired by the design of ParseDown
4720 #               http://parsedown.org/
4721 #               Copyright (c) 2013 Emanuil Rusev, erusev.com
4722 # Arguments   : the symbol name, the doc-string
4723 #############################################################################
4725 sub ConvertMarkDown {
4726     my ($symbol, $text) = @_;
4728     $text = &MarkDownParse ($text, $symbol);
4730     return $text
4733 # SUPPORTED MARKDOWN
4734 # ==================
4736 # Atx-style Headers
4737 # -----------------
4739 # # Header 1
4741 # ## Header 2 ##
4743 # Setext-style Headers
4744 # --------------------
4746 # Header 1
4747 # ========
4749 # Header 2
4750 # --------
4752 # Ordered (unnested) Lists
4753 # ------------------------
4755 # 1. item 1
4757 # 1. item 2 with loooong
4758 #    description
4760 # 3. item 3
4762 # Note: we require a blank line above the list items
4765 # TODO(ensonic): it would be nice to add id parameters to the refsect2 elements
4767 sub MarkDownParseBlocks {
4768   my ($linesref, $symbol, $context) = @_;
4769   my $line;
4770   my @md_blocks = ();
4771   my $md_block = { type => "" };
4773  OUTER: foreach $line (@$linesref) {
4774     my $first_char = substr ($line, 0, 1);
4775     my $deindented_line;
4777     @TRACE@("in '".$md_block->{"type"}."' state, parsing '$line'");
4779     if ($md_block->{"type"} eq "markup") {
4780       if (!$md_block->{"closed"}) {
4781         if (index ($line, $md_block->{"start"}) != -1) {
4782           $md_block->{"depth"}++;
4783         }
4784         if (index ($line, $md_block->{"end"}) != -1) {
4785           if ($md_block->{"depth"} > 0) {
4786             $md_block->{"depth"}--;
4787           } else {
4788             @TRACE@("closing tag '$line'");
4789             $md_block->{"closed"} = 1;
4790             # TODO(ensonic): reparse inner text with MarkDownParseLines?
4791           }
4792         }
4793         $md_block->{"text"} .= "\n" . $line;
4794         @TRACE@("add to markup");
4795         next OUTER;
4796       }
4797     }
4799     $deindented_line = $line;
4800     $deindented_line =~ s/^\s+//;
4802     if ($md_block->{"type"} eq "heading") {
4803       # a heading is ended by any level less than or equal
4804       if ($md_block->{"level"} == 1) {
4805         if ($line =~ /^={4,}[ \t]*$/) {
4806           my $text = pop @{$md_block->{"lines"}};
4807           $md_block->{"interrupted"} = 0;
4808           push @md_blocks, $md_block;
4810           $md_block = { type => "heading",
4811                         text => $text,
4812                         lines => [],
4813                         level => 1 };
4814           next OUTER;
4815         } elsif ($line =~ /^[#][ \t]+(.+?)[ \t]*[#]*[ \t]*(?:{#([^}]+)})?[ \t]*$/) {
4816           $md_block->{"interrupted"} = 0;
4817           push @md_blocks, $md_block;
4819           $md_block = { type => "heading",
4820                         text => $1,
4821                         id => $2,
4822                         lines => [],
4823                         level => 1 };
4824           next OUTER;
4825         } else {
4826           # push lines into the block until the end is reached
4827           push @{$md_block->{"lines"}}, $line;
4828           next OUTER;
4829         }
4830       } else {
4831         if ($line =~ /^[=]{4,}[ \t]*$/) {
4832           my $text = pop @{$md_block->{"lines"}};
4833           $md_block->{"interrupted"} = 0;
4834           push @md_blocks, $md_block;
4836           $md_block = { type => "heading",
4837                         text => $text,
4838                         lines => [],
4839                         level => 1 };
4840           next OUTER;
4841         } elsif ($line =~ /^[-]{4,}[ \t]*$/) {
4842           my $text = pop @{$md_block->{"lines"}};
4843           $md_block->{"interrupted"} = 0;
4844           push @md_blocks, $md_block;
4846           $md_block = { type => "heading",
4847                         text => $text,
4848                         lines => [],
4849                         level => 2 };
4850           next OUTER;
4851         } elsif ($line =~ /^([#]{1,2})[ \t]+(.+?)[ \t]*[#]*[ \t]*(?:{#([^}]+)})?[ \t]*$/) {
4852           $md_block->{"interrupted"} = 0;
4853           push @md_blocks, $md_block;
4855           $md_block = { type => "heading",
4856                         text => $2,
4857                         id => $3,
4858                         lines => [],
4859                         level => length($1) };
4860           next OUTER;
4861         } else {
4862           # push lines into the block until the end is reached
4863           push @{$md_block->{"lines"}}, $line;
4864           next OUTER;
4865         }
4866       }
4867     } elsif ($md_block->{"type"} eq "code") {
4868       if ($line =~ /^[ \t]*\]\|(.*)/) {
4869         push @md_blocks, $md_block;
4870         $md_block = { type => "paragraph",
4871                       text => "$1",
4872                       lines => [] };
4873       } else {
4874         push @{$md_block->{"lines"}}, $line;
4875       }
4876       next OUTER;
4877     }
4879     if ($deindented_line eq "") {
4880       $md_block->{"interrupted"} = 1;
4881       next;
4882     }
4884     if ($md_block->{"type"} eq "quote") {
4885       if (!$md_block->{"interrupted"}) {
4886         $line =~ s/^[ ]*>[ ]?//;
4887         push @{$md_block->{"lines"}}, $line;
4888         next OUTER;
4889       }
4890     } elsif ($md_block->{"type"} eq "li") {
4891       my $marker = $md_block->{"marker"};
4892       if ($line =~ /^([ ]{0,3})($marker)[ ](.*)/) {
4893         my $indentation = $1;
4894         if ($md_block->{"indentation"} ne $indentation) {
4895           push @{$md_block->{"lines"}}, $line;
4896         } else {
4897           my $lines = $3;
4898           my $ordered = $md_block->{"ordered"};
4899           $lines =~ s/^[ ]{0,4}//;
4900           $md_block->{"last"} = 0;
4901           push @md_blocks, $md_block;
4902           $md_block = { type => "li",
4903                         ordered => $ordered,
4904                         indentation => $indentation,
4905                         marker => $marker,
4906                         first => 0,
4907                         last => 1,
4908                         lines => [ $lines ] };
4909         }
4910         next OUTER;
4911       }
4913       if ($md_block->{"interrupted"}) {
4914         if ($first_char eq " ") {
4915           push @{$md_block->{"lines"}}, "";
4916           $line =~ s/^[ ]{0,4}//;
4917           push @{$md_block->{"lines"}}, $line;
4918           $md_block->{"interrupted"} = 0;
4919           next OUTER;
4920         }
4921       } else {
4922         $line =~ s/^[ ]{0,4}//;
4923         push @{$md_block->{"lines"}}, $line;
4924         next OUTER;
4925       }
4926     }
4928     # indentation sensitive types
4929     @TRACE@("parsing '$line'");
4931     if ($line =~ /^([#]{1,2})[ \t]+(.+?)[ \t]*[#]*[ \t]*(?:{#([^}]+)})?[ \t]*$/) {
4932       # atx heading (#)
4933       push @md_blocks, $md_block;
4935       $md_block = { type => "heading",
4936                     text => $2,
4937                     id => $3,
4938                     lines => [],
4939                     level => length($1) };
4941       next OUTER;
4942     } elsif ($line =~ /^={4,}[ \t]*$/) {
4943       # setext heading (====)
4945       if ($md_block->{"type"} eq "paragraph" && $md_block->{"interrupted"}) {
4946         push @md_blocks, $md_block;
4947         $md_block->{"type"} = "heading";
4948         $md_block->{"lines"} = [];
4949         $md_block->{"level"} = 1;
4950       }
4952       next OUTER;
4953     } elsif ($line =~ /^-{4,}[ \t]*$/) {
4954       # setext heading (-----)
4956       if ($md_block->{"type"} eq "paragraph" && $md_block->{"interrupted"}) {
4957         push @md_blocks, $md_block;
4958         $md_block->{"type"} = "heading";
4959         $md_block->{"lines"} = [];
4960         $md_block->{"level"} = 2;
4961       }
4963       next OUTER;
4964     } elsif ($line =~ /^[ \t]*\|\[[ ]*(?:<!-- language="([^"]+?)" -->)?/) {
4965       # code
4966       $md_block->{"interrupted"} = 1;
4967       push @md_blocks, $md_block;
4968       $md_block = { type => "code",
4969                     language => $1,
4970                     lines => [] };
4971       next OUTER;
4972     }
4974     # indentation insensitive types
4975     if ($line =~ /^[ ]*<!DOCTYPE/) {
4976       push @md_blocks, $md_block;
4978       $md_block = { type   => "markup",
4979                     text   => $deindented_line,
4980                     start  => "<",
4981                     end    => ">",
4982                     closed => 0,
4983                     depth  => 0 };
4985     } elsif ($line =~ /^[ ]*<\??(\w+)[^>]*([\/\?])?[ \t]*>/) {
4986       # markup, including <?xml version="1.0"?>
4987       my $tag = $1;
4988       my $is_self_closing = defined($2);
4989       
4990       # skip link markdown
4991       # TODO(ensonic): consider adding more uri schemes (ftp, ...)
4992       if ($tag =~ /^https?/) {
4993         @TRACE@("skipping link '$tag'");
4994       } else {
4995         # for TEXT_LEVEL_ELEMENTS, we want to keep them as-is in the paragraph
4996         # instead of creation a markdown block.
4997         my $scanning_for_end_of_text_level_tag = (
4998             $md_block->{"type"} eq "paragraph" && 
4999             defined($md_block->{"start"}) &&
5000             !$md_block->{"closed"}); 
5001         @TRACE@("markup found '$tag', scanning $scanning_for_end_of_text_level_tag ?");
5002         if (!$MD_TEXT_LEVEL_ELEMENTS{$tag} && !$scanning_for_end_of_text_level_tag) {
5003           push @md_blocks, $md_block;
5004   
5005           if ($is_self_closing) {
5006             @TRACE@("self-closing docbook '$tag'");
5007             $md_block = { type => "self-closing tag",
5008                           text => $deindented_line };
5009             $is_self_closing = 0;
5010             next OUTER;
5011           }
5012   
5013           @TRACE@("new markup '$tag'");
5014           $md_block = { type   => "markup",
5015                         text   => $deindented_line,
5016                         start  => "<" . $tag . ">",
5017                         end    => "</" . $tag . ">",
5018                         closed => 0,
5019                         depth  => 0 };
5020           if ($deindented_line =~ /<\/$tag>/) {
5021             $md_block->{"closed"} = 1;
5022           }
5023           next OUTER;
5024         } else {
5025           if ($MD_TEXT_LEVEL_ELEMENTS{$tag}) {
5026             @TRACE@("text level docbook '$tag' in '".$md_block->{"type"}."' state");
5027             # TODO(ensonic): handle nesting
5028             if (!$scanning_for_end_of_text_level_tag) {
5029               if ($deindented_line !~ /<\/$tag>/) {
5030                 @TRACE@("new text level markup '$tag'");
5031                 $md_block->{"start"} = "<" . $tag . ">";
5032                 $md_block->{"end"} = "</" . $tag . ">";
5033                 $md_block->{"closed"} = 0;
5034                 @TRACE@("scanning for end of '$tag'");
5035               }
5036             } else {
5037               if ($deindented_line =~ /$md_block->{"end"}/) {
5038                 $md_block->{"closed"} = 1;
5039                 @TRACE@("found end of '$tag'");
5040               }
5041             }
5042           }
5043         }
5044       }
5045     } elsif ($line =~ /^([ ]*)[*+-][ ](.*)/) {
5046       # li
5047       push @md_blocks, $md_block;
5048       my $lines = $2;
5049       my $indentation = $1;
5050       $lines =~ s/^[ ]{0,4}//;
5051       $md_block = { type => "li",
5052                     ordered => 0,
5053                     indentation => $indentation,
5054                     marker => "[*+-]",
5055                     first => 1,
5056                     last => 1,
5057                     lines => [ $lines ] };
5058       next OUTER;
5059     } elsif ($line =~ /^[ ]*>[ ]?(.*)/) {
5060       push @md_blocks, $md_block;
5061       $md_block = { type => "quote",
5062                     lines => [ $1 ] };
5063       next OUTER;
5064     }
5066     # list item
5067     if ($line =~ /^([ ]{0,4})\d+[.][ ]+(.*)/) {
5068       push @md_blocks, $md_block;
5069       my $lines = $2;
5070       my $indentation = $1;
5071       $lines =~ s/^[ ]{0,4}//;
5073       $md_block = { type => "li",
5074                     ordered => 1,
5075                     indentation => $indentation,
5076                     marker => "\\d+[.]",
5077                     first => 1,
5078                     last => 1,
5079                     lines => [ $lines ] };
5081       next;
5082     }
5084     # paragraph
5085     if ($md_block->{"type"} eq "paragraph") {
5086       if ($md_block->{"interrupted"}) {
5087         push @md_blocks, $md_block;
5088         $md_block = { type => "paragraph",
5089                       interrupted => 0,
5090                       text => $line };
5091         @TRACE@("new paragraph due to interrupted");
5092       } else {
5093         $md_block->{"text"} .= "\n" . $line;
5094         @TRACE@("add to paragraph");
5095       }
5096     } else {
5097       push @md_blocks, $md_block;
5098       $md_block = { type => "paragraph",
5099                     text => $line };
5100       @TRACE@("new paragraph due to different block type");
5101     }
5102   }
5104   push @md_blocks, $md_block;
5106   shift @md_blocks;
5108   return @md_blocks;
5111 sub MarkDownParseSpanElementsInner {
5112   my ($text, $markersref) = @_;
5113   my $markup = "";
5114   my %markers = map { $_ => 1 } @$markersref;
5116   while ($text ne "") {
5117     my $closest_marker = "";
5118     my $closest_marker_index = 0;
5119     my $closest_marker_position = -1;
5120     my $text_marker = "";
5121     my $i = 0;
5122     my $offset = 0;
5123     my @markers_rest;
5124     my $marker;
5125     my $use;
5127     while ( ($marker, $use) = each %markers ) {
5128       my $marker_position;
5130       if (!$use) {
5131         next;
5132       }
5134       $marker_position = index ($text, $marker);
5136       if ($marker_position < 0) {
5137         $markers{$marker} = 0;
5138         next;
5139       }
5141       if ($closest_marker eq "" || $marker_position < $closest_marker_position) {
5142         $closest_marker = $marker;
5143         $closest_marker_index = $i;
5144         $closest_marker_position = $marker_position;
5145       }
5146     }
5148     if ($closest_marker_position >= 0) {
5149       $text_marker = substr ($text, $closest_marker_position);
5150     }
5152     if ($text_marker eq "") {
5153       $markup .= $text;
5154       $text = "";
5155       next; # last
5156     }
5158     $markup .= substr ($text, 0, $closest_marker_position);
5159     $text = substr ($text, $closest_marker_position);
5160     @markers_rest = map { $markers{$_} ? ($_ eq $closest_marker ? () : $_) : () } keys %markers;
5162     if ($closest_marker eq "![" || $closest_marker eq "[") {
5163       my %element;
5165       if (index ($text, "]") && $text =~ /\[((?:[^][]|(?R))*)\]/) {
5166         my $remaining_text;
5168         %element = ( "!" => (substr ($text, 0, 1) eq "!"),
5169                      "a" => $1 );
5171         $offset = length ($&);
5172         if ($element{"!"}) {
5173           $offset++;
5174         }
5176         $remaining_text = substr ($text, $offset);
5177         if ($remaining_text =~ /^\([ ]*([^)'"]*?)(?:[ ]+['"](.+?)['"])?[ ]*\)/) {
5178           $element{"»"} = $1;
5179           if (defined ($2)) {
5180             $element{"#"} = $2;
5181           }
5182           $offset += length ($&);
5183         } elsif ($remaining_text =~ /^\s*\[([^\]<]*?)\]/) {
5184           $element{"ref"} = $1;
5185           $offset += length ($&);
5186         } else {
5187           undef %element;
5188         }
5189       }
5191       if (%element) {
5192         if ($element{"»"}) {
5193           $element{"»"} =~ s/&/&amp;/g;
5194           $element{"»"} =~ s/</&lt;/g;
5195         }
5196         if ($element{"!"}) {
5197           $markup .= "<inlinemediaobject><imageobject><imagedata fileref=\"" . $element{"»"} . "\"></imagedata></imageobject>";
5199           if (defined ($element{"a"})) {
5200             $markup .= "<textobject><phrase>" . $element{"a"} . "</phrase></textobject>";
5201           }
5203           $markup .= "</inlinemediaobject>";
5204         } elsif ($element{"ref"}) {
5205           $element{"a"} = &MarkDownParseSpanElementsInner ($element{"a"}, \@markers_rest);
5206           $markup .= "<link linkend=\"" . $element{"ref"} . "\"";
5208           if (defined ($element{"#"})) {
5209             # title attribute not supported
5210           }
5212           $markup .= ">" . $element{"a"} . "</link>";
5213         } else {
5214           $element{"a"} = &MarkDownParseSpanElementsInner ($element{"a"}, \@markers_rest);
5215           $markup .= "<ulink url=\"" . $element{"»"} . "\"";
5217           if (defined ($element{"#"})) {
5218             # title attribute not supported
5219           }
5221           $markup .= ">" . $element{"a"} . "</ulink>";
5222         }
5223       } else {
5224         $markup .= $closest_marker;
5225         if ($closest_marker eq "![") {
5226           $offset = 2;
5227         } else {
5228           $offset = 1;
5229         }
5230       }
5231     } elsif ($closest_marker eq "<") {
5232       if ($text =~ /^<(https?:[\/]{2}[^\s]+?)>/i) {
5233         my $element_url = $1;
5234         $element_url =~ s/&/&amp;/g;
5235         $element_url =~ s/</&lt;/g;
5237         $markup .= "<ulink url=\"" . $element_url . "\">" . $element_url . "</ulink>";
5238         $offset = length ($&);
5239       } elsif ($text =~ /^<([A-Za-z0-9._-]+?@[A-Za-z0-9._-]+?)>/) {
5240         $markup .= "<ulink url=\"mailto:" . $1 . "\">" . $1 . "</ulink>";
5241         $offset = length ($&);
5242       } elsif ($text =~ /^<[^>]+?>/) {
5243         $markup .= $&;
5244         $offset = length ($&);
5245       } else {
5246         $markup .= "&lt;";
5247         $offset = 1;
5248       }
5249     } elsif ($closest_marker eq "\\") {
5250       my $special_char = substr ($text, 1, 1);
5251       if ($MD_ESCAPABLE_CHARS{$special_char} ||
5252           $MD_GTK_ESCAPABLE_CHARS{$special_char}) {
5253         $markup .= $special_char;
5254         $offset = 2;
5255       } else {
5256         $markup .= "\\";
5257         $offset = 1;
5258       }
5259     } elsif ($closest_marker eq "`") {
5260       if ($text =~ /^(`+)([^`]+?)\1(?!`)/) {
5261         my $element_text = $2;
5262         $markup .= "<literal>" . $element_text . "</literal>";
5263         $offset = length ($&);
5264       } else {
5265         $markup .= "`";
5266         $offset = 1;
5267       }
5268     } elsif ($closest_marker eq "@") {
5269       # Convert '@param()'
5270       # FIXME: we could make those also links ($symbol.$2), but that would be less
5271       # useful as the link target is a few lines up or down
5272       if ($text =~ /^(\A|[^\\])\@(\w+((\.|->)\w+)*)\s*\(\)/) {
5273         $markup .= $1 . "<parameter>" . $2 . "()</parameter>\n";
5274         $offset = length ($&);
5275       } elsif ($text =~ /^(\A|[^\\])\@(\w+((\.|->)\w+)*)/) {
5276         # Convert '@param', but not '\@param'.
5277         $markup .= $1 . "<parameter>" . $2 . "</parameter>\n";
5278         $offset = length ($&);
5279       } elsif ($text =~ /^\\\@/) {
5280         $markup .= "\@";
5281         $offset = length ($&);
5282       } else {
5283         $markup .= "@";
5284         $offset = 1;
5285       }
5286     } elsif ($closest_marker eq "#") {
5287       if ($text =~ /^(\A|[^\\])#([\w\-:\.]+[\w]+)\s*\(\)/) {
5288         # handle #Object.func()
5289         $markup .= $1 . &MakeXRef ($2, &tagify ($2 . "()", "function"));
5290         $offset = length ($&);
5291       } elsif ($text =~ /^(\A|[^\\])#([\w\-:\.]+[\w]+)/) {
5292         # Convert '#symbol', but not '\#symbol'.
5293         $markup .= $1 . &MakeHashXRef ($2, "type");
5294         $offset = length ($&);
5295       } elsif ($text =~ /^\\#/) {
5296         $markup .= "#";
5297         $offset = length ($&);
5298       } else {
5299         $markup .= "#";
5300         $offset = 1;
5301       }
5302     } elsif ($closest_marker eq "%") {
5303       if ($text =~ /^(\A|[^\\])\%(-?\w+)/) {
5304         # Convert '%constant', but not '\%constant'.
5305         # Also allow negative numbers, e.g. %-1.
5306         $markup .= $1 . &MakeXRef ($2, &tagify ($2, "literal"));
5307         $offset = length ($&);
5308       } elsif ($text =~ /^\\%/) {
5309         $markup .= "\%";
5310         $offset = length ($&);
5311       } else {
5312         $markup .= "%";
5313         $offset = 1;
5314       }
5315     }
5317     if ($offset > 0) {
5318       $text = substr ($text, $offset);
5319     }
5320   }
5322   return $markup;
5325 sub MarkDownParseSpanElements {
5326   my ($text) = @_;
5327   my @markers = ( "\\", "<", "![", "[", "`", "%", "#", "@" );
5329   $text = &MarkDownParseSpanElementsInner ($text, \@markers);
5331   # Convert 'function()' or 'macro()'.
5332   # if there is abc_*_def() we don't want to make a link to _def()
5333   # FIXME: also handle abc(def(....)) : but that would need to be done recursively :/
5334   $text =~ s/([^\*.\w])(\w+)\s*\(\)/$1.&MakeXRef($2, &tagify($2 . "()", "function"));/eg;
5336   return $text;
5339 sub ReplaceEntities {
5340   my ($text, $symbol) = @_;
5341   my $warn = "";
5342   my @entities = ( [ "&lt;", "<" ],
5343                    [ "&gt;", ">" ],
5344                    [ "&ast;", "*" ],
5345                    [ "&num;", "#" ],
5346                    [ "&percnt;", "%"],
5347                    [ "&colon;", ":" ],
5348                    [ "&quot;", "\"" ],
5349                    [ "&apos;", "'" ],
5350                    [ "&nbsp;", " " ],
5351                    [ "&amp;", "&" ] ); # Do this last, or the others get messed up.
5352   my $i;
5354   # Expand entities in <programlisting> even inside CDATA since
5355   # we changed the definition of |[ to add CDATA
5356   for ($i = 0; $i <= $#entities; $i++) {
5357     $text =~ s/$entities[$i][0]/$entities[$i][1]/g;
5358   }
5360   return $text;
5363 sub MarkDownOutputDocBook {
5364   my ($blocksref, $symbol, $context) = @_;
5365   my $output = "";
5366   my $block;
5367   my @blocks = @$blocksref;
5369   foreach $block (@blocks) {
5370     my $text;
5371     my $title;
5373     #$output .= "\n<!-- beg type='" . $block->{"type"} . "'-->\n";
5375     if ($block->{"type"} eq "paragraph") {
5376       $text = &MarkDownParseSpanElements ($block->{"text"});
5377       if ($context eq "li" && $output eq "") {
5378         if ($block->{"interrupted"}) {
5379           $output .= "\n<para>$text</para>\n";
5380         } else {
5381           $output .= "<para>$text</para>";
5382           if ($#blocks > 0) {
5383             $output .= "\n";
5384           }
5385         }
5386       } else {
5387         $output .= "<para>$text</para>\n";
5388       }
5390     } elsif ($block->{"type"} eq "heading") {
5391       my $tag;
5393       $title = &MarkDownParseSpanElements ($block->{"text"});
5395       if ($block->{"level"} == 1) {
5396         $tag = "refsect2";
5397       } else {
5398         $tag = "refsect3";
5399       }
5401       $text = &MarkDownParseLines ($block->{"lines"}, $symbol, "heading");
5402       if (defined ($block->{"id"})) {
5403         $output .= "<$tag id=\"" . $block->{"id"} . "\">";
5404       } else {
5405         $output .= "<$tag>";
5406       }
5408       $output .= "<title>$title</title>$text</$tag>\n";
5409     } elsif ($block->{"type"} eq "li") {
5410       my $tag = "itemizedlist";
5412       if ($block->{"first"}) {
5413         if ($block->{"ordered"}) {
5414           $tag = "orderedlist";
5415         }
5416         $output .= "<$tag>\n";
5417       }
5419       if ($block->{"interrupted"}) {
5420         push @{$block->{"lines"}}, "";
5421       }
5423       $text = &MarkDownParseLines ($block->{"lines"}, $symbol, "li");
5424       $output .= "<listitem>".$text."</listitem>\n";
5425       if ($block->{"last"}) {
5426         if ($block->{"ordered"}) {
5427           $tag = "orderedlist";
5428         }
5429         $output .= "</$tag>\n";
5430       }
5431     } elsif ($block->{"type"} eq "quote") {
5432       $text = &MarkDownParseLines ($block->{"lines"}, $symbol, "quote");
5433       $output .= "<blockquote>\n$text</blockquote>\n";
5434     } elsif ($block->{"type"} eq "code") {
5435       if ($block->{"language"}) {
5436         $output .= "<informalexample><programlisting language=\"" . $block->{"language"} . "\"><![CDATA[\n";
5437       } else {
5438         $output .= "<informalexample><programlisting><![CDATA[\n";
5439       }
5440       foreach (@{$block->{"lines"}}) {
5441         $output .= &ReplaceEntities ($_, $symbol) . "\n";
5442       }
5443       $output .= "]]></programlisting></informalexample>\n";
5444     } elsif ($block->{"type"} eq "markup") {
5445       $text = &ExpandAbbreviations($symbol, $block->{"text"});
5446       $output .= $text."\n";
5447     } else {
5448       $output .= $block->{"text"}."\n";
5449     }
5450     #$output .= "\n<!-- end type='" . $block->{"type"} . "'-->\n";
5451   }
5453   return $output;
5456 sub MarkDownParseLines {
5457   my ($linesref, $symbol, $context) = @_;
5458   my $output;
5459   my @lines = @$linesref;
5460   my @blocks;
5462   @blocks = &MarkDownParseBlocks (\@lines, $symbol, $context);
5463   $output = &MarkDownOutputDocBook (\@blocks, $symbol, $context);
5465   return $output;
5468 sub MarkDownParse {
5469   my ($text, $symbol) = @_;
5470   my @lines;
5472   # take out some variability in line endings
5473   $text =~ s%\r\n%\n%g;
5474   $text =~ s%\r%\n%g;
5476   # split lines
5477   @lines = split("\n", $text);
5478   $text = MarkDownParseLines(\@lines, $symbol, "");
5480   return $text;
5483 #############################################################################
5484 # LIBRARY FUNCTIONS -        These functions are used in both gtkdoc-mkdb and
5485 #                        gtkdoc-mktmpl and should eventually be moved to a
5486 #                        separate library.
5487 #############################################################################
5489 #############################################################################
5490 # Function    : ReadDeclarationsFile
5491 # Description : This reads in a file containing the function/macro/enum etc.
5492 #                declarations.
5494 #                Note that in some cases there are several declarations with
5495 #                the same name, e.g. for conditional macros. In this case we
5496 #                set a flag in the %DeclarationConditional hash so the
5497 #                declaration is not shown in the docs.
5499 #                If a macro and a function have the same name, e.g. for
5500 #                gtk_object_ref, the function declaration takes precedence.
5502 #                Some opaque structs are just declared with 'typedef struct
5503 #                _name name;' in which case the declaration may be empty.
5504 #                The structure may have been found later in the header, so
5505 #                that overrides the empty declaration.
5507 # Arguments   : $file - the declarations file to read
5508 #                $override - if declarations in this file should override
5509 #                        any current declaration.
5510 #############################################################################
5512 sub ReadDeclarationsFile {
5513     my ($file, $override) = @_;
5515     if ($override == 0) {
5516         %Declarations = ();
5517         %DeclarationTypes = ();
5518         %DeclarationConditional = ();
5519         %DeclarationOutput = ();
5520     }
5522     open (INPUT, $file)
5523         || die "Can't open $file: $!";
5524     my $declaration_type = "";
5525     my $declaration_name;
5526     my $declaration;
5527     my $is_deprecated = 0;
5528     while (<INPUT>) {
5529         if (!$declaration_type) {
5530             if (m/^<([^>]+)>/) {
5531                 $declaration_type = $1;
5532                 $declaration_name = "";
5533                 @TRACE@("Found declaration: $declaration_type\n");
5534                 $declaration = "";
5535             }
5536         } else {
5537             if (m%^<NAME>(.*)</NAME>%) {
5538                 $declaration_name = $1;
5539             } elsif (m%^<DEPRECATED/>%) {
5540                 $is_deprecated = 1;
5541             } elsif (m%^</$declaration_type>%) {
5542                 @TRACE@("Found end of declaration: $declaration_name\n");
5543                 # Check that the declaration has a name
5544                 if ($declaration_name eq "") {
5545                     &LogWarning ($file, $., "$declaration_type has no name.\n");
5546                 }
5548                 # If the declaration is an empty typedef struct _XXX XXX
5549                 # set the flag to indicate the struct has a typedef.
5550                 if (($declaration_type eq 'STRUCT' || $declaration_type eq 'UNION')
5551                     && $declaration =~ m/^\s*$/) {
5552                     @TRACE@("Struct has typedef: $declaration_name\n");
5553                     $StructHasTypedef{$declaration_name} = 1;
5554                 }
5556                 # Check if the symbol is already defined.
5557                 if (defined ($Declarations{$declaration_name})
5558                     && $override == 0) {
5559                     # Function declarations take precedence.
5560                     if ($DeclarationTypes{$declaration_name} eq 'FUNCTION') {
5561                         # Ignore it.
5562                     } elsif ($declaration_type eq 'FUNCTION') {
5563                         if ($is_deprecated) {
5564                             $Deprecated{$declaration_name} = "";
5565                         }
5566                         $Declarations{$declaration_name} = $declaration;
5567                         $DeclarationTypes{$declaration_name} = $declaration_type;
5568                     } elsif ($DeclarationTypes{$declaration_name}
5569                               eq $declaration_type) {
5570                         # If the existing declaration is empty, or is just a
5571                         # forward declaration of a struct, override it.
5572                         if ($declaration_type eq 'STRUCT' || $declaration_type eq 'UNION') {
5573                             if ($Declarations{$declaration_name} =~ m/^\s*((struct|union)\s+\w+\s*;)?\s*$/) {
5574                                 if ($is_deprecated) {
5575                                     $Deprecated{$declaration_name} = "";
5576                                 }
5577                                 $Declarations{$declaration_name} = $declaration;
5578                             } elsif ($declaration =~ m/^\s*((struct|union)\s+\w+\s*;)?\s*$/) {
5579                                 # Ignore an empty or forward declaration.
5580                             } else {
5581                                 &LogWarning ($file, $., "Structure $declaration_name has multiple definitions.");
5582                             }
5583                         } else {
5584                             # set flag in %DeclarationConditional hash for
5585                             # multiply defined macros/typedefs.
5586                             $DeclarationConditional{$declaration_name} = 1;
5587                         }
5588                     } else {
5589                         &LogWarning ($file, $., "$declaration_name has multiple definitions.");
5590                     }
5591                 } else {
5592                     if ($is_deprecated) {
5593                         $Deprecated{$declaration_name} = "";
5594                     }
5595                     $Declarations{$declaration_name} = $declaration;
5596                     $DeclarationTypes{$declaration_name} = $declaration_type;
5597                 }
5599                 $declaration_type = "";
5600                 $is_deprecated = 0;
5601             } else {
5602                 $declaration .= $_;
5603             }
5604         }
5605     }
5606     close (INPUT);
5610 #############################################################################
5611 # Function    : ReadSignalsFile
5612 # Description : This reads in an existing file which contains information on
5613 #                all GTK signals. It creates the arrays @SignalNames and
5614 #                @SignalPrototypes containing info on the signals. The first
5615 #                line of the SignalPrototype is the return type of the signal
5616 #                handler. The remaining lines are the parameters passed to it.
5617 #                The last parameter, "gpointer user_data" is always the same
5618 #                so is not included.
5619 # Arguments   : $file - the file containing the signal handler prototype
5620 #                        information.
5621 #############################################################################
5623 sub ReadSignalsFile {
5624     my ($file) = @_;
5626     my $in_signal = 0;
5627     my $signal_object;
5628     my $signal_name;
5629     my $signal_returns;
5630     my $signal_flags;
5631     my $signal_prototype;
5633     # Reset the signal info.
5634     @SignalObjects = ();
5635     @SignalNames = ();
5636     @SignalReturns = ();
5637     @SignalFlags = ();
5638     @SignalPrototypes = ();
5640     if (! -f $file) {
5641         return;
5642     }
5643     if (!open (INPUT, $file)) {
5644         warn "Can't open $file - skipping signals\n";
5645         return;
5646     }
5647     while (<INPUT>) {
5648         if (!$in_signal) {
5649             if (m/^<SIGNAL>/) {
5650                 $in_signal = 1;
5651                 $signal_object = "";
5652                 $signal_name = "";
5653                 $signal_returns = "";
5654                 $signal_prototype = "";
5655             }
5656         } else {
5657             if (m/^<NAME>(.*)<\/NAME>/) {
5658                 $signal_name = $1;
5659                 if ($signal_name =~ m/^(.*)::(.*)$/) {
5660                     $signal_object = $1;
5661                     ($signal_name = $2) =~ s/_/-/g;
5662                     @TRACE@("Found signal: $signal_name\n");
5663                 } else {
5664                     &LogWarning ($file, $., "Invalid signal name: $signal_name.");
5665                 }
5666             } elsif (m/^<RETURNS>(.*)<\/RETURNS>/) {
5667                 $signal_returns = $1;
5668             } elsif (m/^<FLAGS>(.*)<\/FLAGS>/) {
5669                 $signal_flags = $1;
5670             } elsif (m%^</SIGNAL>%) {
5671                 @TRACE@("Found end of signal: ${signal_object}::${signal_name}\nReturns: ${signal_returns}\n${signal_prototype}");
5672                 push (@SignalObjects, $signal_object);
5673                 push (@SignalNames, $signal_name);
5674                 push (@SignalReturns, $signal_returns);
5675                 push (@SignalFlags, $signal_flags);
5676                 push (@SignalPrototypes, $signal_prototype);
5677                 $in_signal = 0;
5678             } else {
5679                 $signal_prototype .= $_;
5680             }
5681         }
5682     }
5683     close (INPUT);
5687 #############################################################################
5688 # Function    : ReadTemplateFile
5689 # Description : This reads in the manually-edited documentation file
5690 #               corresponding to the file currently being created, so we can
5691 #               insert the documentation at the appropriate places.
5692 #               It outputs %SymbolTypes, %SymbolDocs and %SymbolParams, which
5693 #               is a hash of arrays.
5694 #               NOTE: This function is duplicated in gtkdoc-mktmpl (but
5695 #               slightly different).
5696 # Arguments   : $docsfile - the template file to read in.
5697 #               $skip_unused_params - 1 if the unused parameters should be
5698 #                 skipped.
5699 #############################################################################
5701 sub ReadTemplateFile {
5702     my ($docsfile, $skip_unused_params) = @_;
5704     my $template = "$docsfile.sgml";
5705     if (! -f $template) {
5706         @TRACE@("File doesn't exist: $template\n");
5707         return 0;
5708     }
5710     # start with empty hashes, we merge the source comment for each file
5711     # afterwards
5712     %SymbolDocs = ();
5713     %SymbolTypes = ();
5714     %SymbolParams = ();
5716     my $current_type = "";        # Type of symbol being read.
5717     my $current_symbol = "";        # Name of symbol being read.
5718     my $symbol_doc = "";                # Description of symbol being read.
5719     my @params;                        # Parameter names and descriptions of current
5720                                 #   function/macro/function typedef.
5721     my $current_param = -1;        # Index of parameter currently being read.
5722                                 #   Note that the param array contains pairs
5723                                 #   of param name & description.
5724     my $in_unused_params = 0;        # True if we are reading in the unused params.
5725     my $in_deprecated = 0;
5726     my $in_since = 0;
5727     my $in_stability = 0;
5729     open (DOCS, "$template")
5730         || die "Can't open $template: $!";
5732     @TRACE@("reading template $template");
5734     while (<DOCS>) {
5735         if (m/^<!-- ##### ([A-Z_]+) (\S+) ##### -->/) {
5736             my $type = $1;
5737             my $symbol = $2;
5738             if ($symbol eq "Title"
5739                 || $symbol eq "Short_Description"
5740                 || $symbol eq "Long_Description"
5741                 || $symbol eq "See_Also"
5742                 || $symbol eq "Stability_Level"
5743                 || $symbol eq "Include"
5744                 || $symbol eq "Image") {
5746                 $symbol = $docsfile . ":" . $symbol;
5747             }
5749             @TRACE@("Found symbol: $symbol\n");
5750             # Remember file and line for the symbol
5751             $SymbolSourceFile{$symbol} = $template;
5752             $SymbolSourceLine{$symbol} = $.;
5754             # Store previous symbol, but remove any trailing blank lines.
5755             if ($current_symbol ne "") {
5756                 $symbol_doc =~ s/\s+$//;
5757                 $SymbolTypes{$current_symbol} = $current_type;
5758                 $SymbolDocs{$current_symbol} = $symbol_doc;
5760                 # Check that the stability level is valid.
5761                 if ($StabilityLevel{$current_symbol}) {
5762                     $StabilityLevel{$current_symbol} = &ParseStabilityLevel($StabilityLevel{$current_symbol}, $template, $., "Stability level for $current_symbol");
5763                 }
5765                 if ($current_param >= 0) {
5766                     $SymbolParams{$current_symbol} = [ @params ];
5767                 } else {
5768                     # Delete any existing params in case we are overriding a
5769                     # previously read template.
5770                     delete $SymbolParams{$current_symbol};
5771                 }
5772             }
5773             $current_type = $type;
5774             $current_symbol = $symbol;
5775             $current_param = -1;
5776             $in_unused_params = 0;
5777             $in_deprecated = 0;
5778             $in_since = 0;
5779             $in_stability = 0;
5780             $symbol_doc = "";
5781             @params = ();
5783         } elsif (m/^<!-- # Unused Parameters # -->/) {
5784             @TRACE@("Found unused parameters\n");
5785             $in_unused_params = 1;
5786             next;
5788         } elsif ($in_unused_params && $skip_unused_params) {
5789             # When outputting the DocBook we skip unused parameters.
5790             @TRACE@("Skipping unused param: $_");
5791             next;
5793         } else {
5794             # Check if param found. Need to handle "..." and "format...".
5795             if (s/^\@([\w\.]+):\040?//) {
5796                 my $param_name = $1;
5797                 my $param_desc = $_;
5798                 # Allow variations of 'Returns'
5799                 if ($param_name =~ m/^[Rr]eturns?$/) {
5800                     $param_name = "Returns";
5801                 }
5802                 # Allow varargs variations
5803                 if ($param_name =~ m/^.*\.\.\.$/) {
5804                     $param_name = "...";
5805                 }
5807                 # strip trailing whitespaces and blank lines
5808                 s/\s+\n$/\n/m;
5809                 s/\n+$/\n/sm;
5810                 @TRACE@("Found param for symbol $current_symbol : '$param_name'= '$_'");
5812                 if ($param_name eq "Deprecated") {
5813                     $in_deprecated = 1;
5814                     $Deprecated{$current_symbol} = $_;
5815                 } elsif ($param_name eq "Since") {
5816                     $in_since = 1;
5817                     chomp;
5818                     $Since{$current_symbol} = $_;
5819                 } elsif ($param_name eq "Stability") {
5820                     $in_stability = 1;
5821                     $StabilityLevel{$current_symbol} = $_;
5822                 } else {
5823                     push (@params, $param_name);
5824                     push (@params, $param_desc);
5825                     $current_param += $PARAM_FIELD_COUNT;
5826                 }
5827             } else {
5828                 # strip trailing whitespaces and blank lines
5829                 s/\s+\n$/\n/m;
5830                 s/\n+$/\n/sm;
5832                 if (!m/^\s+$/) {
5833                     if ($in_deprecated) {
5834                         $Deprecated{$current_symbol} .= $_;
5835                     } elsif ($in_since) {
5836                         &LogWarning ($template, $., "multi-line since docs found");
5837                         #$Since{$current_symbol} .= $_;
5838                     } elsif ($in_stability) {
5839                         $StabilityLevel{$current_symbol} .= $_;
5840                     } elsif ($current_param >= 0) {
5841                         $params[$current_param] .= $_;
5842                     } else {
5843                         $symbol_doc .= $_;
5844                     }
5845                 }
5846             }
5847         }
5848     }
5850     # Remember to finish the current symbol doccs.
5851     if ($current_symbol ne "") {
5853         $symbol_doc =~ s/\s+$//;
5854         $SymbolTypes{$current_symbol} = $current_type;
5855         $SymbolDocs{$current_symbol} = $symbol_doc;
5857         # Check that the stability level is valid.
5858         if ($StabilityLevel{$current_symbol}) {
5859             $StabilityLevel{$current_symbol} = &ParseStabilityLevel($StabilityLevel{$current_symbol}, $template, $., "Stability level for $current_symbol");
5860         }
5862         if ($current_param >= 0) {
5863             $SymbolParams{$current_symbol} = [ @params ];
5864         } else {
5865             # Delete any existing params in case we are overriding a
5866             # previously read template.
5867             delete $SymbolParams{$current_symbol};
5868         }
5869     }
5871     close (DOCS);
5872     return 1;
5876 #############################################################################
5877 # Function    : ReadObjectHierarchy
5878 # Description : This reads in the $MODULE-hierarchy.txt file containing all
5879 #               the GtkObject subclasses described in this module (and their
5880 #               ancestors).
5881 #               It places them in the @Objects array, and places their level
5882 #               in the object hierarchy in the @ObjectLevels array, at the
5883 #               same index. GtkObject, the root object, has a level of 1.
5885 #               This also generates tree_index.sgml as it goes along.
5887 # Arguments   : none
5888 #############################################################################
5890 sub ReadObjectHierarchy {
5891     @Objects = ();
5892     @ObjectLevels = ();
5894     if (! -f $OBJECT_TREE_FILE) {
5895         return;
5896     }
5897     if (!open (INPUT, $OBJECT_TREE_FILE)) {
5898         warn "Can't open $OBJECT_TREE_FILE - skipping object tree\n";
5899         return;
5900     }
5902     # Only emit objects if they are supposed to be documented, or if
5903     # they have documented children. To implement this, we maintain a
5904     # stack of pending objects which will be emitted if a documented
5905     # child turns up.
5906     my @pending_objects = ();
5907     my @pending_levels = ();
5908     my $root;
5909     my @tree = ();
5910     while (<INPUT>) {
5911         if (m/\S+/) {
5912             my $object = $&;
5913             my $level = (length($`)) / 2 + 1;
5914             my $xref = "";
5916             if ($level == 1) {
5917                 $root = $object;
5918             }
5920             while (($#pending_levels >= 0) && ($pending_levels[$#pending_levels] >= $level)) {
5921                 my $pobject = pop(@pending_objects);
5922                 my $plevel = pop(@pending_levels);
5923             }
5925             push (@pending_objects, $object);
5926             push (@pending_levels, $level);
5928             if (exists($KnownSymbols{$object})) {
5929                 while ($#pending_levels >= 0) {
5930                     $object = shift @pending_objects;
5931                     $level = shift @pending_levels;
5932                     $xref = &MakeXRef ($object);
5934                     push (@tree, ' ' x ($level * 4) . "$xref");
5935                     push (@Objects, $object);
5936                     push (@ObjectLevels, $level);
5937                     $ObjectRoots{$object} = $root;
5938                 }
5939             }
5940             #else {
5941             #    LogWarning ($OBJECT_TREE_FILE, $., "unknown type $object");
5942             #}
5943         }
5944     }
5945     close (INPUT);
5947     # FIXME: use xml
5948     # my $old_tree_index = "$DB_OUTPUT_DIR/tree_index.$xml";
5949     my $old_tree_index = "$DB_OUTPUT_DIR/tree_index.sgml";
5950     my $new_tree_index = "$DB_OUTPUT_DIR/tree_index.new";
5952     open (OUTPUT, ">$new_tree_index")
5953         || die "Can't create $new_tree_index: $!";
5955     print (OUTPUT &MakeDocHeader ("screen")."\n<screen>\n".&AddTreeLineArt(\@tree)."\n</screen>\n");
5956     close (OUTPUT);
5958     &UpdateFileIfChanged ($old_tree_index, $new_tree_index, 0);
5960     &OutputObjectList;
5963 #############################################################################
5964 # Function    : ReadInterfaces
5965 # Description : This reads in the $MODULE.interfaces file.
5967 # Arguments   : none
5968 #############################################################################
5970 sub ReadInterfaces {
5971     %Interfaces = ();
5973     if (! -f $INTERFACES_FILE) {
5974         return;
5975     }
5976     if (!open (INPUT, $INTERFACES_FILE)) {
5977         warn "Can't open $INTERFACES_FILE - skipping interfaces\n";
5978         return;
5979     }
5981     while (<INPUT>) {
5982        chomp;
5983        my ($object, @ifaces) = split;
5984        if (exists($KnownSymbols{$object}) && $KnownSymbols{$object} == 1) {
5985            my @knownIfaces = ();
5987            # filter out private interfaces, but leave foreign interfaces
5988            foreach my $iface (@ifaces) {
5989                if (!exists($KnownSymbols{$iface}) || $KnownSymbols{$iface} == 1) {
5990                    push (@knownIfaces, $iface);
5991                }
5992              }
5994            $Interfaces{$object} = join(' ', @knownIfaces);
5995            @TRACE@("Interfaces for $object: $Interfaces{$object}\n");
5996        } else {
5997          @TRACE@("skipping interfaces for unknown symbol: $object\n");
5998        }
5999     }
6000     close (INPUT);
6003 #############################################################################
6004 # Function    : ReadPrerequisites
6005 # Description : This reads in the $MODULE.prerequisites file.
6007 # Arguments   : none
6008 #############################################################################
6010 sub ReadPrerequisites {
6011     %Prerequisites = ();
6013     if (! -f $PREREQUISITES_FILE) {
6014         return;
6015     }
6016     if (!open (INPUT, $PREREQUISITES_FILE)) {
6017         warn "Can't open $PREREQUISITES_FILE - skipping prerequisites\n";
6018         return;
6019     }
6021     while (<INPUT>) {
6022        chomp;
6023        my ($iface, @prereqs) = split;
6024        if (exists($KnownSymbols{$iface}) && $KnownSymbols{$iface} == 1) {
6025            my @knownPrereqs = ();
6027            # filter out private prerequisites, but leave foreign prerequisites
6028            foreach my $prereq (@prereqs) {
6029                if (!exists($KnownSymbols{$prereq}) || $KnownSymbols{$prereq} == 1) {
6030                   push (@knownPrereqs, $prereq);
6031                }
6032            }
6034            $Prerequisites{$iface} = join(' ', @knownPrereqs);
6035        }
6036     }
6037     close (INPUT);
6040 #############################################################################
6041 # Function    : ReadArgsFile
6042 # Description : This reads in an existing file which contains information on
6043 #                all GTK args. It creates the arrays @ArgObjects, @ArgNames,
6044 #                @ArgTypes, @ArgFlags, @ArgNicks and @ArgBlurbs containing info
6045 #               on the args.
6046 # Arguments   : $file - the file containing the arg information.
6047 #############################################################################
6049 sub ReadArgsFile {
6050     my ($file) = @_;
6052     my $in_arg = 0;
6053     my $arg_object;
6054     my $arg_name;
6055     my $arg_type;
6056     my $arg_flags;
6057     my $arg_nick;
6058     my $arg_blurb;
6059     my $arg_default;
6060     my $arg_range;
6062     # Reset the args info.
6063     @ArgObjects = ();
6064     @ArgNames = ();
6065     @ArgTypes = ();
6066     @ArgFlags = ();
6067     @ArgNicks = ();
6068     @ArgBlurbs = ();
6069     @ArgDefaults = ();
6070     @ArgRanges = ();
6072     if (! -f $file) {
6073         return;
6074     }
6075     if (!open (INPUT, $file)) {
6076         warn "Can't open $file - skipping args\n";
6077         return;
6078     }
6079     while (<INPUT>) {
6080         if (!$in_arg) {
6081             if (m/^<ARG>/) {
6082                 $in_arg = 1;
6083                 $arg_object = "";
6084                 $arg_name = "";
6085                 $arg_type = "";
6086                 $arg_flags = "";
6087                 $arg_nick = "";
6088                 $arg_blurb = "";
6089                 $arg_default = "";
6090                 $arg_range = "";
6091             }
6092         } else {
6093             if (m/^<NAME>(.*)<\/NAME>/) {
6094                 $arg_name = $1;
6095                 if ($arg_name =~ m/^(.*)::(.*)$/) {
6096                     $arg_object = $1;
6097                     ($arg_name = $2) =~ s/_/-/g;
6098                     @TRACE@("Found arg: $arg_name\n");
6099                 } else {
6100                     &LogWarning ($file, $., "Invalid argument name: $arg_name");
6101                 }
6102             } elsif (m/^<TYPE>(.*)<\/TYPE>/) {
6103                 $arg_type = $1;
6104             } elsif (m/^<RANGE>(.*)<\/RANGE>/) {
6105                 $arg_range = $1;
6106             } elsif (m/^<FLAGS>(.*)<\/FLAGS>/) {
6107                 $arg_flags = $1;
6108             } elsif (m/^<NICK>(.*)<\/NICK>/) {
6109                 $arg_nick = $1;
6110             } elsif (m/^<BLURB>(.*)<\/BLURB>/) {
6111                 $arg_blurb = $1;
6112                 if ($arg_blurb eq "(null)") {
6113                   $arg_blurb = "";
6114                   &LogWarning ($file, $., "Property ${arg_object}:${arg_name} has no documentation.");
6115                 }
6116             } elsif (m/^<DEFAULT>(.*)<\/DEFAULT>/) {
6117                 $arg_default = $1;
6118             } elsif (m%^</ARG>%) {
6119                 @TRACE@("Found end of arg: ${arg_object}::${arg_name}\n${arg_type} : ${arg_flags}\n");
6120                 push (@ArgObjects, $arg_object);
6121                 push (@ArgNames, $arg_name);
6122                 push (@ArgTypes, $arg_type);
6123                 push (@ArgRanges, $arg_range);
6124                 push (@ArgFlags, $arg_flags);
6125                 push (@ArgNicks, $arg_nick);
6126                 push (@ArgBlurbs, $arg_blurb);
6127                 push (@ArgDefaults, $arg_default);
6128                 $in_arg = 0;
6129             }
6130         }
6131     }
6132     close (INPUT);
6135 #############################################################################
6136 # Function    : AddTreeLineArt
6137 # Description : Add unicode lineart to a pre-indented string array and returns
6138 #               it as as multiline string.
6139 # Arguments   : @tree - array of indented strings.
6140 #############################################################################
6142 sub AddTreeLineArt {
6143   my @tree = @{$_[0]};
6144   my $i;
6145   my $j;
6146   my $indent;
6147   
6148   # iterate bottom up over the tree 
6149   for ($i = $#tree; $i >= 0; $i--) {
6150     # count leading spaces
6151     $tree[$i] =~ /^([^<A-Za-z]*)/;
6152     $indent = length( $1 );
6153     # replace with ╰───, if place of ╰ is not space insert ├
6154     if ($indent > 4) {
6155       if (substr($tree[$i],$indent-4,1) eq " ") {
6156         substr($tree[$i],$indent-4,4) = "--- ";
6157       } else {
6158         substr($tree[$i],$indent-4,4) = "+-- ";
6159       }
6160       # go lines up while space and insert |
6161       for ($j = $i - 1; ($j >= 0 && substr($tree[$j],$indent-4,1) eq ' '); $j--) {
6162         substr($tree[$j],$indent-4,1) = '|';
6163       }
6164     }
6165   }
6166   
6167   my $res = join("\n", @tree);
6168   # unicode chars for: ╰──
6169   $res =~ s%---%<phrase role=\"lineart\">&#9584;&#9472;&#9472;</phrase>%g;
6170   # unicde chars for: ├──
6171   $res =~ s%\+--%<phrase role=\"lineart\">&#9500;&#9472;&#9472;</phrase>%g;
6172   # unicode char for: │
6173   $res =~ s%\|%<phrase role=\"lineart\">&#9474;</phrase>%g;
6174   
6175   return $res;
6179 #############################################################################
6180 # Function    : CheckIsObject
6181 # Description : Returns 1 if the given name is a GObject or a subclass.
6182 #                It uses the global @Objects array.
6183 #                Note that the @Objects array only contains classes in the
6184 #                current module and their ancestors - not all GObject classes.
6185 # Arguments   : $name - the name to check.
6186 #############################################################################
6188 sub CheckIsObject {
6189     my ($name) = @_;
6190     my $root = $ObjectRoots{$name};
6191     # Let GBoxed pass as an object here to get -struct appended to the id
6192     # and prevent conflicts with sections.
6193     return (defined($root) and $root ne 'GEnum' and $root ne 'GFlags');
6197 #############################################################################
6198 # Function    : MakeReturnField
6199 # Description : Pads a string to $RETURN_TYPE_FIELD_WIDTH.
6200 # Arguments   : $str - the string to pad.
6201 #############################################################################
6203 sub MakeReturnField {
6204     my ($str) = @_;
6206     return $str . (' ' x ($RETURN_TYPE_FIELD_WIDTH - length ($str)));
6209 #############################################################################
6210 # Function    : GetSymbolSourceFile
6211 # Description : Get the filename where the symbol docs where taken from.
6212 # Arguments   : $symbol - the symbol name
6213 #############################################################################
6215 sub GetSymbolSourceFile {
6216     my ($symbol) = @_;
6218     if (defined($SourceSymbolSourceFile{$symbol})) {
6219         return $SourceSymbolSourceFile{$symbol};
6220     } elsif (defined($SymbolSourceFile{$symbol})) {
6221         return $SymbolSourceFile{$symbol};
6222     } else {
6223         return "";
6224     }
6227 #############################################################################
6228 # Function    : GetSymbolSourceLine
6229 # Description : Get the file line where the symbol docs where taken from.
6230 # Arguments   : $symbol - the symbol name
6231 #############################################################################
6233 sub GetSymbolSourceLine {
6234     my ($symbol) = @_;
6236     if (defined($SourceSymbolSourceLine{$symbol})) {
6237         return $SourceSymbolSourceLine{$symbol};
6238     } elsif (defined($SymbolSourceLine{$symbol})) {
6239         return $SymbolSourceLine{$symbol};
6240     } else {
6241         return 0;
6242     }