tests: add a test for the attributes annotation
[gtk-doc.git] / gtkdoc-mkdb.in
blob6ee0eba580be2dc3448f4fb945717a9a037e3d5b
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     'not nullable' => "NULL must not be passed as the value in, out, in-out; or as a return value.",
161     'optional' => "NULL may be passed instead of a pointer to a location.",
162     'array' => "Parameter points to an array of items.",
163     'attribute' => "Deprecated free-form custom annotation, replaced by (attributes) annotation.",
164     'attributes' => "Free-form key-value pairs.",
165     'closure' => "This parameter is a 'user_data', for callbacks; many bindings can pass NULL here.",
166     'constructor' => "This symbol is a constructor, not a static method.",
167     'destroy' => "This parameter is a 'destroy_data', for callbacks.",
168     'default' => "Default parameter value (for in case the <acronym>shadows</acronym>-to function has less parameters).",
169     'element-type' => "Generics and defining elements of containers and arrays.",
170     'error-domains' => "Typed errors. Similar to throws in Java.",
171     'foreign' => "This is a foreign struct.",
172     'get-value-func' => "The specified function is used to convert a struct from a GValue, must be a GTypeInstance.",
173     'in' => "Parameter for input. Default is <acronym>transfer none</acronym>.",
174     'inout' => "Parameter for input and for returning results. Default is <acronym>transfer full</acronym>.",
175     'in-out' => "Parameter for input and for returning results. Default is <acronym>transfer full</acronym>.",
176     'method' => "This is a method",
177     'not-error' => "A GError parameter is not to be handled like a normal GError.",
178     'out' => "Parameter for returning results. Default is <acronym>transfer full</acronym>.",
179     'out caller-allocates' => "Out parameter, where caller must allocate storage.",
180     'out callee-allocates' => "Out parameter, where caller must allocate storage.",
181     'ref-func' => "The specified function is used to ref a struct, must be a GTypeInstance.",
182     'rename-to' => "Rename the original symbol's name to SYMBOL.",
183     'scope call' => "The callback is valid only during the call to the method.",
184     'scope async' => "The callback is valid until first called.",
185     'scope notified' => "The callback is valid until the GDestroyNotify argument is called.",
186     'set-value-func' => "The specified function is used to convert from a struct to a GValue, must be a GTypeInstance.",
187     'skip' => "Exposed in C code, not necessarily available in other languages.",
188     'transfer container' => "Free data container after the code is done.",
189     'transfer floating' => "Alias for <acronym>transfer none</acronym>, used for objects with floating refs.",
190     'transfer full' => "Free data after the code is done.",
191     'transfer none' => "Don't free data after the code is done.",
192     'type' => "Override the parsed C type with given type.",
193     'unref-func' => "The specified function is used to unref a struct, must be a GTypeInstance.",
194     'virtual' => "This is the invoker for a virtual method.",
195     'value' => "The specified value overrides the evaluated value of the constant.",
196     # Stability Level definition
197     # https://bugzilla.gnome.org/show_bug.cgi?id=170860
198     'Stable' => <<EOF,
199 The intention of a Stable interface is to enable arbitrary third parties to
200 develop applications to these interfaces, release them, and have confidence that
201 they will run on all minor releases of the product (after the one in which the
202 interface was introduced, and within the same major release). Even at a major
203 release, incompatible changes are expected to be rare, and to have strong
204 justifications.
206     'Unstable' => <<EOF,
207 Unstable interfaces are experimental or transitional. They are typically used to
208 give outside developers early access to new or rapidly changing technology, or
209 to provide an interim solution to a problem where a more general solution is
210 anticipated. No claims are made about either source or binary compatibility from
211 one minor release to the next.
213 The Unstable interface level is a warning that these interfaces are  subject to
214 change without warning and should not be used in unbundled products.
216 Given such caveats, customer impact need not be a factor when considering
217 incompatible changes to an Unstable interface in a major or minor release.
218 Nonetheless, when such changes are introduced, the changes should still be
219 mentioned in the release notes for the affected release.
221     'Private' => <<EOF
222 An interface that can be used within the GNOME stack itself, but that is not
223 documented for end-users.  Such functions should only be used in specified and
224 documented ways.
228 # Elements to consider non-block items in MarkDown parsing
229 my %MD_TEXT_LEVEL_ELEMENTS = ( "literal" => 1,
230                                "emphasis" => 1,
231                                "envar" => 1,
232                                "filename" => 1,
233                                "firstterm" => 1,
234                                "footnote" => 1,
235                                "function" => 1,
236                                "manvolnum" => 1,
237                                "option" => 1,
238                                "replaceable" => 1,
239                                "structfield" => 1,
240                                "structname" => 1,
241                                "title" => 1,
242                                "varname" => 1 );
243 my %MD_ESCAPABLE_CHARS = ( "\\" => 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                            "!" => 1 );
259 my %MD_GTK_ESCAPABLE_CHARS = ( "@" => 1,
260                                "%" => 1 );
262 # Function and other declaration output settings.
263 my $RETURN_TYPE_FIELD_WIDTH = 20;
264 my $SYMBOL_FIELD_WIDTH = 36;
265 my $MAX_SYMBOL_FIELD_WIDTH = 40;
266 my $SIGNAL_FIELD_WIDTH = 16;
267 my $PARAM_FIELD_COUNT = 2;
269 # XML, SGML formatting helper
270 my $doctype_header;
273 run() unless caller; # Run program unless loaded as a module
276 sub run {
277     my %optctl = ('module' => \$MODULE,
278                   'source-dir' => \@SOURCE_DIRS,
279                   'source-suffixes' => \$SOURCE_SUFFIXES,
280                   'ignore-files' => \$IGNORE_FILES,
281                   'output-dir' => \$DB_OUTPUT_DIR,
282                   'tmpl-dir' => \$TMPL_DIR,
283                   'version' => \$PRINT_VERSION,
284                   'help' => \$PRINT_HELP,
285                   'main-sgml-file' => \$MAIN_SGML_FILE,
286                   'expand-content-files' => \$EXPAND_CONTENT_FILES,
287                   'sgml-mode' => \$INLINE_MARKUP_MODE,
288                   'xml-mode' => \$INLINE_MARKUP_MODE,
289                   'default-stability' => \$DEFAULT_STABILITY,
290                   'default-includes' => \$DEFAULT_INCLUDES,
291                   'output-format' => \$OUTPUT_FORMAT,
292                   'name-space' => \$NAME_SPACE,
293                   'outputallsymbols' => \$OUTPUT_ALL_SYMBOLS,
294                   'outputsymbolswithoutsince' => \$OUTPUT_SYMBOLS_WITHOUT_SINCE
295                   );
296     GetOptions(\%optctl, "module=s", "source-dir:s", "source-suffixes:s",
297         "ignore-files:s", "output-dir:s", "tmpl-dir:s", "version",
298         "outputallsymbols", "outputsymbolswithoutsince",
299         "expand-content-files:s", "main-sgml-file:s", "extra-db-files:s", "help",
300         "sgml-mode", "xml-mode", "default-stability:s", "default-includes:s",
301         "output-format:s", "name-space:s");
303     if ($PRINT_VERSION) {
304         print "@VERSION@\n";
305         exit 0;
306     }
308     if (!$MODULE) {
309         $PRINT_HELP = 1;
310     }
312     if ($DEFAULT_STABILITY && $DEFAULT_STABILITY ne "Stable"
313         && $DEFAULT_STABILITY ne "Private" && $DEFAULT_STABILITY ne "Unstable") {
314         $PRINT_HELP = 1;
315     }
317     if ($PRINT_HELP) {
318         print <<EOF;
319 gtkdoc-mkdb version @VERSION@ - generate docbook files
321 --module=MODULE_NAME       Name of the doc module being parsed
322 --source-dir=DIRNAME       Directories which contain inline reference material
323 --source-suffixes=SUFFIXES Suffixes of source files to scan, comma-separated
324 --ignore-files=FILES       A space-separated list of header files/dirs not to
325                            scan
326 --output-dir=DIRNAME       Directory to put the generated DocBook files in
327 --tmpl-dir=DIRNAME         Directory in which template files may be found
328 --main-sgml-file=FILE      File containing the toplevel DocBook file.
329 --expand-content-files=FILES Extra DocBook files to expand abbreviations in.
330 --output-format=FORMAT     Format to use for the generated docbook, XML or SGML.
331 --{xml,sgml}-mode          Allow DocBook markup in inline documentation.
332 --default-stability=LEVEL  Specify default stability Level. Valid values are
333                            Stable, Unstable, or Private.
334 --default-includes=FILENAMES Specify default includes for section Synopsis
335 --name-space=NS            Omit namespace in index.
336 --version                  Print the version of this program
337 --help                     Print this help
339         exit 0;
340     }
342     @TRACE@(" ignore files: [$IGNORE_FILES]\n");
344     # check output format
345     if (! defined($OUTPUT_FORMAT) || ($OUTPUT_FORMAT eq "")) {
346         $OUTPUT_FORMAT = "xml";
347     } else {
348         $OUTPUT_FORMAT = lc($OUTPUT_FORMAT);
349     }
350     if ($OUTPUT_FORMAT ne "xml") {
351         die "Invalid format '$OUTPUT_FORMAT' passed to --output.format"
352     }
354     if (!$MAIN_SGML_FILE) {
355         # backwards compatibility
356         if (-e "${MODULE}-docs.sgml") {
357             $MAIN_SGML_FILE = "${MODULE}-docs.sgml";
358         } else {
359             $MAIN_SGML_FILE = "${MODULE}-docs.xml";
360         }
361     }
363     # extract docbook header or define default
364     if (-e $MAIN_SGML_FILE) {
365         open(INPUT, "<$MAIN_SGML_FILE") || die "Can't open $MAIN_SGML_FILE";
366         $doctype_header = "";
367         while (<INPUT>) {
368             if (/^\s*<(book|chapter|article)/) {
369                 # check that the top-level tagSYSTEM or the doctype decl contain the xinclude namespace decl
370                 if (($_ !~ m/http:\/\/www.w3.org\/200[13]\/XInclude/) && ($doctype_header !~ m/http:\/\/www.w3.org\/200[13]\/XInclude/m)) {
371                     $doctype_header = "";
372                 }
373                 last;
374             }
375             # if there are SYSTEM ENTITIES here, we should prepend "../" to the path
376             # FIXME: not sure if we can do this now, as people already work-around the problem
377             # s#<!ENTITY % ([a-zA-Z-]+) SYSTEM \"([^/][a-zA-Z./]+)\">#<!ENTITY % $1 SYSTEM \"../$2\">#;
378             s#<!ENTITY % gtkdocentities SYSTEM \"([^"]*)\">#<!ENTITY % gtkdocentities SYSTEM \"../$1\">#;
379             $doctype_header .= $_;
380         }
381         close(INPUT);
382     } else {
383         $doctype_header = <<EOF;
384 <?xml version="1.0"?>
385 <!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
386                "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"
388   <!ENTITY % local.common.attrib "xmlns:xi  CDATA  #FIXED 'http://www.w3.org/2003/XInclude'">
389   <!ENTITY % gtkdocentities SYSTEM "../xml/gtkdocentities.ent">
390   %gtkdocentities;
393     }
394     chomp($doctype_header);
396     # All the files are written in subdirectories beneath here.
397     $TMPL_DIR = $TMPL_DIR ? $TMPL_DIR : "$ROOT_DIR/tmpl";
399     # This is where we put all the DocBook output.
400     $DB_OUTPUT_DIR = $DB_OUTPUT_DIR ? $DB_OUTPUT_DIR : "$ROOT_DIR/xml";
402     # This file contains the object hierarchy.
403     $OBJECT_TREE_FILE = "$ROOT_DIR/$MODULE.hierarchy";
405     # This file contains the interfaces.
406     $INTERFACES_FILE = "$ROOT_DIR/$MODULE.interfaces";
408     # This file contains the prerequisites.
409     $PREREQUISITES_FILE = "$ROOT_DIR/$MODULE.prerequisites";
411     # This file contains signal arguments and names.
412     $SIGNALS_FILE = "$ROOT_DIR/$MODULE.signals";
414     # The file containing Arg information.
415     $ARGS_FILE = "$ROOT_DIR/$MODULE.args";
417     # Create the root DocBook output directory if it doens't exist.
418     if (! -e $DB_OUTPUT_DIR) {
419         mkdir ("$DB_OUTPUT_DIR", 0777)
420             || die "Can't create directory: $DB_OUTPUT_DIR";
421     }
423     &ReadKnownSymbols ("$ROOT_DIR/$MODULE-sections.txt");
424     &ReadSignalsFile ($SIGNALS_FILE);
425     &ReadArgsFile ($ARGS_FILE);
426     &ReadObjectHierarchy;
427     &ReadInterfaces;
428     &ReadPrerequisites;
430     &ReadDeclarationsFile ("$ROOT_DIR/$MODULE-decl.txt", 0);
431     if (-f "$ROOT_DIR/$MODULE-overrides.txt") {
432         &ReadDeclarationsFile ("$ROOT_DIR/$MODULE-overrides.txt", 1);
433     }
435     for my $dir (@SOURCE_DIRS) {
436         &ReadSourceDocumentation ($dir);
437     }
439     my $changed = &OutputDB ("$ROOT_DIR/$MODULE-sections.txt");
441     # If any of the DocBook files have changed, update the timestamp file (so
442     # it can be used for Makefile dependencies).
443     if ($changed || ! -e "$ROOT_DIR/sgml.stamp") {
445         # try to detect the common prefix
446         # GtkWidget, GTK_WIDGET, gtk_widget -> gtk
447         if ($NAME_SPACE eq "") {
448             $NAME_SPACE="";
449             my $pos=0;
450             my $ratio=0.0;
451             do {
452                 my %prefix;
453                 my $letter="";
454                 foreach my $symbol (keys(%IndexEntriesFull)) {
455                     if(($NAME_SPACE eq "") || $symbol =~ /^$NAME_SPACE/i) {
456                         if (length($symbol)>$pos) {
457                             $letter=substr($symbol,$pos,1);
458                             # stop prefix scanning
459                             if ($letter eq "_") {
460                                 # stop on "_"
461                                 last;
462                             }
463                             # Should we also stop on a uppercase char, if last was lowercase
464                             #   GtkWidget, if we have the 'W' and had the 't' before
465                             # or should we count upper and lowercase, and stop one 2nd uppercase, if we already had a lowercase
466                             #   GtkWidget, the 'W' would be the 2nd uppercase and with 't','k' we had lowercase chars before
467                             # need to recound each time as this is per symbol
468                             $prefix{uc($letter)}++;
469                         }
470                     }
471                 }
472                 if ($letter ne "" && $letter ne "_") {
473                     my $maxletter="";
474                     my $maxsymbols=0;
475                     foreach $letter (keys(%prefix)) {
476                         #print "$letter: $prefix{$letter}.\n";
477                         if ($prefix{$letter}>$maxsymbols) {
478                             $maxletter=$letter;
479                             $maxsymbols=$prefix{$letter};
480                         }
481                     }
482                     $ratio = scalar(keys(%IndexEntriesFull)) / $prefix{$maxletter};
483                     #print "most symbols start with $maxletter, that is ". (100 * $ratio) ." %\n";
484                     if ($ratio > 0.9) {
485                         # do another round
486                         $NAME_SPACE .= $maxletter;
487                     }
488                     $pos++;
489                 }
490                 else {
491                     $ratio=0.0;
492                 }
493             } while ($ratio > 0.9);
494             #print "most symbols start with $NAME_SPACE\n";
495         }
497         &OutputIndexFull;
498         &OutputDeprecatedIndex;
499         &OutputSinceIndexes;
500         &OutputAnnotationGlossary;
502         open (TIMESTAMP, ">$ROOT_DIR/sgml.stamp")
503             || die "Can't create $ROOT_DIR/sgml.stamp: $!";
504         print (TIMESTAMP "timestamp");
505         close (TIMESTAMP);
506     }
509 #############################################################################
510 # Function    : OutputObjectList
511 # Description : This outputs the alphabetical list of objects, in a columned
512 #                table.
513 #               FIXME: Currently this also outputs ancestor objects
514 #                which may not actually be in this module.
515 # Arguments   : none
516 #############################################################################
518 sub OutputObjectList {
519     my $cols = 3;
521     # FIXME: use .xml
522     # my $old_object_index = "$DB_OUTPUT_DIR/object_index.xml";
523     my $old_object_index = "$DB_OUTPUT_DIR/object_index.sgml";
524     my $new_object_index = "$DB_OUTPUT_DIR/object_index.new";
526     open (OUTPUT, ">$new_object_index")
527         || die "Can't create $new_object_index: $!";
529     print (OUTPUT <<EOF);
530 ${\( MakeDocHeader ("informaltable") )}
531 <informaltable pgwide="1" frame="none">
532 <tgroup cols="$cols">
533 <colspec colwidth="1*"/>
534 <colspec colwidth="1*"/>
535 <colspec colwidth="1*"/>
536 <tbody>
539     my $count = 0;
540     my $object;
541     foreach $object (sort (@Objects)) {
542         my $xref = &MakeXRef ($object);
543         if ($count % $cols == 0) { print (OUTPUT "<row>\n"); }
544         print (OUTPUT "<entry>$xref</entry>\n");
545         if ($count % $cols == ($cols - 1)) { print (OUTPUT "</row>\n"); }
546         $count++;
547     }
548     if ($count == 0) {
549         # emit an empty row, since empty tables are invalid
550         print (OUTPUT "<row><entry> </entry></row>\n");
551     }
552     else {
553         if ($count % $cols > 0) {
554             print (OUTPUT "</row>\n");
555         }
556     }
558     print (OUTPUT <<EOF);
559 </tbody></tgroup></informaltable>
561     close (OUTPUT);
563     &UpdateFileIfChanged ($old_object_index, $new_object_index, 0);
566 #############################################################################
567 # Function    : TrimTextBlock
568 # Description : Trims extra whitespace. Empty lines inside a block are
569 #                preserved.
570 # Arguments   : $desc - the text block to trim. May contain newlines.
571 #############################################################################
573 sub TrimTextBlock {
574   my ($desc) = @_;
576   # strip leading spaces on the block
577   $desc =~ s/^\s+//s;
578   # strip trailing spaces on every line
579   $desc =~ s/\s+$/\n/mg;
581   return $desc;
585 #############################################################################
586 # Function    : OutputDB
587 # Description : This collects the output for each section of the docs, and
588 #                outputs each file when the end of the section is found.
589 # Arguments   : $file - the $MODULE-sections.txt file which contains all of
590 #                the functions/macros/structs etc. being documented, organised
591 #                into sections and subsections.
592 #############################################################################
594 sub OutputDB {
595     my ($file) = @_;
597     @TRACE@("Reading: $file\n");
598     open (INPUT, $file)
599         || die "Can't open $file: $!";
600     my $filename = "";
601     my $book_top = "";
602     my $book_bottom = "";
603     my $includes = (defined $DEFAULT_INCLUDES) ? $DEFAULT_INCLUDES : "";
604     my $section_includes = "";
605     my $in_section = 0;
606     my $title = "";
607     my $section_id = "";
608     my $subsection = "";
609     my $num_symbols;
610     my $changed = 0;
611     my $functions_synop = "";
612     my $other_synop = "";
613     my $functions_details = "";
614     my $other_details = "";
615     my $signals_synop = "";
616     my $signals_desc = "";
617     my $args_synop = "";
618     my $child_args_synop = "";
619     my $style_args_synop = "";
620     my $args_desc = "";
621     my $child_args_desc = "";
622     my $style_args_desc = "";
623     my $hierarchy_str = "";
624     my @hierarchy = ();
625     my $interfaces = "";
626     my $implementations = "";
627     my $prerequisites = "";
628     my $derived = "";
629     my @file_objects = ();
630     my %templates = ();
631     my %symbol_def_line = ();
633     # merge the source docs, in case there are no templates
634     &MergeSourceDocumentation;
636     while (<INPUT>) {
637         if (m/^#/) {
638             next;
640         } elsif (m/^<SECTION>/) {
641             $num_symbols = 0;
642             $in_section = 1;
643             @file_objects = ();
644             %symbol_def_line = ();
646         } elsif (m/^<SUBSECTION\s*(.*)>/i) {
647             $other_synop .= "\n";
648             $functions_synop .= "\n";
649             $subsection = $1;
651         } elsif (m/^<SUBSECTION>/) {
653         } elsif (m/^<TITLE>(.*)<\/TITLE>/) {
654             $title = $1;
655             @TRACE@("Section: $title\n");
657             # We don't want warnings if object & class structs aren't used.
658             $DeclarationOutput{$title} = 1;
659             $DeclarationOutput{"${title}Class"} = 1;
660             $DeclarationOutput{"${title}Iface"} = 1;
661             $DeclarationOutput{"${title}Interface"} = 1;
663         } elsif (m/^<FILE>(.*)<\/FILE>/) {
664             $filename = $1;
665             if (! defined $templates{$filename}) {
666                if (&ReadTemplateFile ("$TMPL_DIR/$filename", 1)) {
667                    &MergeSourceDocumentation;
668                    $templates{$filename}=$.;
669                }
670             } else {
671                 &LogWarning ($file, $., "Double <FILE>$filename</FILE> entry. ".
672                     "Previous occurrence on line ".$templates{$filename}.".");
673             }
674             if (($title eq "") and (defined $SourceSymbolDocs{"$TMPL_DIR/$filename:Title"})) {
675                 $title = $SourceSymbolDocs{"$TMPL_DIR/$filename:Title"};
676                  # Remove trailing blanks
677                 $title =~ s/\s+$//;
678            }
680         } elsif (m/^<INCLUDE>(.*)<\/INCLUDE>/) {
681             if ($in_section) {
682                 $section_includes = $1;
683             } else {
684                 if (defined $DEFAULT_INCLUDES) {
685                     &LogWarning ($file, $., "Default <INCLUDE> being overridden by command line option.");
686                 }
687                 else {
688                     $includes = $1;
689                 }
690             }
692         } elsif (m/^<\/SECTION>/) {
693             @TRACE@("End of section: $title\n");
694             if ($num_symbols > 0) {
695                 # collect documents
696                 $book_bottom .= "    <xi:include href=\"xml/$filename.xml\"/>\n";
698                 if (defined ($SourceSymbolDocs{"$TMPL_DIR/$filename:Include"})) {
699                     if ($section_includes) {
700                         &LogWarning ($file, $., "Section <INCLUDE> being overridden by inline comments.");
701                     }
702                     $section_includes = $SourceSymbolDocs{"$TMPL_DIR/$filename:Include"};
703                 }
704                 if ($section_includes eq "") {
705                     $section_includes = $includes;
706                 }
708                  $signals_synop =~ s/^\n*//g;
709                  $signals_synop =~ s/\n+$/\n/g;
710                 if ($signals_synop ne '') {
711                     $signals_synop = <<EOF;
712 <refsect1 id="$section_id.signals" role="signal_proto">
713 <title role="signal_proto.title">Signals</title>
714 <informaltable frame="none">
715 <tgroup cols="3">
716 <colspec colname="signals_return" colwidth="150px"/>
717 <colspec colname="signals_name" colwidth="300px"/>
718 <colspec colname="signals_flags" colwidth="200px"/>
719 <tbody>
720 ${signals_synop}
721 </tbody>
722 </tgroup>
723 </informaltable>
724 </refsect1>
726                      $signals_desc = TrimTextBlock($signals_desc);
727                     $signals_desc  = <<EOF;
728 <refsect1 id="$section_id.signal-details" role="signals">
729 <title role="signals.title">Signal Details</title>
730 $signals_desc
731 </refsect1>
733                 }
735                 $args_synop =~ s/^\n*//g;
736                 $args_synop =~ s/\n+$/\n/g;
737                 if ($args_synop ne '') {
738                     $args_synop = <<EOF;
739 <refsect1 id="$section_id.properties" role="properties">
740 <title role="properties.title">Properties</title>
741 <informaltable frame="none">
742 <tgroup cols="3">
743 <colspec colname="properties_type" colwidth="150px"/>
744 <colspec colname="properties_name" colwidth="300px"/>
745 <colspec colname="properties_flags" colwidth="200px"/>
746 <tbody>
747 ${args_synop}
748 </tbody>
749 </tgroup>
750 </informaltable>
751 </refsect1>
753                      $args_desc = TrimTextBlock($args_desc);
754                     $args_desc  = <<EOF;
755 <refsect1 id="$section_id.property-details" role="property_details">
756 <title role="property_details.title">Property Details</title>
757 $args_desc
758 </refsect1>
760                 }
762                 $child_args_synop =~ s/^\n*//g;
763                 $child_args_synop =~ s/\n+$/\n/g;
764                 if ($child_args_synop ne '') {
765                     $args_synop .= <<EOF;
766 <refsect1 id="$section_id.child-properties" role="child_properties">
767 <title role="child_properties.title">Child Properties</title>
768 <informaltable frame="none">
769 <tgroup cols="3">
770 <colspec colname="child_properties_type" colwidth="150px"/>
771 <colspec colname="child_properties_name" colwidth="300px"/>
772 <colspec colname="child_properties_flags" colwidth="200px"/>
773 <tbody>
774 ${child_args_synop}
775 </tbody>
776 </tgroup>
777 </informaltable>
778 </refsect1>
780                      $child_args_desc = TrimTextBlock($child_args_desc);
781                      $args_desc .= <<EOF;
782 <refsect1 id="$section_id.child-property-details" role="child_property_details">
783 <title role="child_property_details.title">Child Property Details</title>
784 $child_args_desc
785 </refsect1>
787                 }
789                 $style_args_synop =~ s/^\n*//g;
790                 $style_args_synop =~ s/\n+$/\n/g;
791                 if ($style_args_synop ne '') {
792                     $args_synop .= <<EOF;
793 <refsect1 id="$section_id.style-properties" role="style_properties">
794 <title role="style_properties.title">Style Properties</title>
795 <informaltable frame="none">
796 <tgroup cols="3">
797 <colspec colname="style_properties_type" colwidth="150px"/>
798 <colspec colname="style_properties_name" colwidth="300px"/>
799 <colspec colname="style_properties_flags" colwidth="200px"/>
800 <tbody>
801 ${style_args_synop}
802 </tbody>
803 </tgroup>
804 </informaltable>
805 </refsect1>
807                      $style_args_desc = TrimTextBlock($style_args_desc);
808                     $args_desc .= <<EOF;
809 <refsect1 id="$section_id.style-property-details" role="style_properties_details">
810 <title role="style_properties_details.title">Style Property Details</title>
811 $style_args_desc
812 </refsect1>
814                 }
816                 $hierarchy_str = &AddTreeLineArt(\@hierarchy);
817                 if ($hierarchy_str ne "") {
818                     $hierarchy_str = <<EOF;
819 <refsect1 id="$section_id.object-hierarchy" role="object_hierarchy">
820 <title role="object_hierarchy.title">Object Hierarchy</title>
821 <screen>$hierarchy_str
822 </screen>
823 </refsect1>
825                 }
827                  $interfaces =~ TrimTextBlock($interfaces);
828                 if ($interfaces ne "") {
829                     $interfaces = <<EOF;
830 <refsect1 id="$section_id.implemented-interfaces" role="impl_interfaces">
831 <title role="impl_interfaces.title">Implemented Interfaces</title>
832 $interfaces
833 </refsect1>
835                 }
837                  $implementations = TrimTextBlock($implementations);
838                 if ($implementations ne "") {
839                     $implementations = <<EOF;
840 <refsect1 id="$section_id.implementations" role="implementations">
841 <title role="implementations.title">Known Implementations</title>
842 $implementations
843 </refsect1>
845                 }
847                  $prerequisites = TrimTextBlock($prerequisites);
848                 if ($prerequisites ne "") {
849                     $prerequisites = <<EOF;
850 <refsect1 id="$section_id.prerequisites" role="prerequisites">
851 <title role="prerequisites.title">Prerequisites</title>
852 $prerequisites
853 </refsect1>
855                 }
857                  $derived = TrimTextBlock($derived);
858                 if ($derived ne "") {
859                     $derived = <<EOF;
860 <refsect1 id="$section_id.derived-interfaces" role="derived_interfaces">
861 <title role="derived_interfaces.title">Known Derived Interfaces</title>
862 $derived
863 </refsect1>
865                 }
867                 $functions_synop =~ s/^\n*//g;
868                 $functions_synop =~ s/\n+$/\n/g;
869                 if ($functions_synop ne '') {
870                   $functions_synop = <<EOF;
871 <refsect1 id="$section_id.functions" role="functions_proto">
872 <title role="functions_proto.title">Functions</title>
873 <informaltable pgwide="1" frame="none">
874 <tgroup cols="2">
875 <colspec colname="functions_return" colwidth="150px"/>
876 <colspec colname="functions_name"/>
877 <tbody>
878 ${functions_synop}
879 </tbody>
880 </tgroup>
881 </informaltable>
882 </refsect1>
884                 }
886                 $other_synop =~ s/^\n*//g;
887                 $other_synop =~ s/\n+$/\n/g;
888                 if ($other_synop ne '') {
889                   $other_synop = <<EOF;
890 <refsect1 id="$section_id.other" role="other_proto">
891 <title role="other_proto.title">Types and Values</title>
892 <informaltable role="enum_members_table" pgwide="1" frame="none">
893 <tgroup cols="2">
894 <colspec colname="name" colwidth="150px"/>
895 <colspec colname="description"/>
896 <tbody>
897 ${other_synop}
898 </tbody>
899 </tgroup>
900 </informaltable>
901 </refsect1>
903                 }
905                 my $file_changed = &OutputDBFile ($filename, $title, $section_id,
906                                                     $section_includes,
907                                                     \$functions_synop, \$other_synop,
908                                                     \$functions_details, \$other_details,
909                                                     \$signals_synop, \$signals_desc,
910                                                     \$args_synop, \$args_desc,
911                                                     \$hierarchy_str, \$interfaces,
912                                                     \$implementations,
913                                                     \$prerequisites, \$derived,
914                                                     \@file_objects);
915                 if ($file_changed) {
916                     $changed = 1;
917                 }
918             }
919             $title = "";
920             $section_id = "";
921             $subsection = "";
922             $in_section = 0;
923             $section_includes = "";
924             $functions_synop = "";
925             $other_synop = "";
926             $functions_details = "";
927             $other_details = "";
928             $signals_synop = "";
929             $signals_desc = "";
930             $args_synop = "";
931             $child_args_synop = "";
932             $style_args_synop = "";
933             $args_desc = "";
934             $child_args_desc = "";
935             $style_args_desc = "";
936             $hierarchy_str = "";
937             @hierarchy = ();
938             $interfaces = "";
939             $implementations = "";
940             $prerequisites = "";
941             $derived = "";
943         } elsif (m/^(\S+)/) {
944             my $symbol = $1;
945             @TRACE@("  Symbol: $symbol in subsection: $subsection\n");
947             # check for duplicate entries
948             if (! defined $symbol_def_line{$symbol}) {
949                 my $declaration = $Declarations{$symbol};
950                 if (defined ($declaration)) {
951                     if (&CheckIsObject ($symbol)) {
952                         push @file_objects, $symbol;
953                     }
954                     # We don't want standard macros/functions of GObjects,
955                     # or private declarations.
956                     if ($subsection ne "Standard" && $subsection ne "Private") {
957                         my ($synop, $desc) = &OutputDeclaration ($symbol,
958                                                                  $declaration);
959                         my $type = $DeclarationTypes {$symbol};
961                         if ($type eq 'FUNCTION' || $type eq 'USER_FUNCTION') {
962                           $functions_synop .= $synop;
963                           $functions_details .= $desc;
964                         } elsif ($type eq 'MACRO' && $declaration =~ /$symbol\(/) {
965                           $functions_synop .= $synop;
966                           $functions_details .= $desc;
967                         } else {
968                           $other_synop .= $synop;
969                           $other_details .= $desc;
970                         }
971                     }
972                     my ($sig_synop, $sig_desc) = &GetSignals ($symbol);
973                     my ($arg_synop, $child_arg_synop, $style_arg_synop,
974                         $arg_desc, $child_arg_desc, $style_arg_desc) = &GetArgs ($symbol);
975                     my $ifaces = &GetInterfaces ($symbol);
976                     my $impls = &GetImplementations ($symbol);
977                     my $prereqs = &GetPrerequisites ($symbol);
978                     my $der = &GetDerived ($symbol);
979                     @hierarchy = &GetHierarchy ($symbol, \@hierarchy);
981                     $signals_synop .= $sig_synop;
982                     $signals_desc .= $sig_desc;
983                     $args_synop .= $arg_synop;
984                     $child_args_synop .= $child_arg_synop;
985                     $style_args_synop .= $style_arg_synop;
986                     $args_desc .= $arg_desc;
987                     $child_args_desc .= $child_arg_desc;
988                     $style_args_desc .= $style_arg_desc;
989                     $interfaces .= $ifaces;
990                     $implementations .= $impls;
991                     $prerequisites .= $prereqs;
992                     $derived .= $der;
994                     # Note that the declaration has been output.
995                     $DeclarationOutput{$symbol} = 1;
996                 } elsif ($subsection ne "Standard" && $subsection ne "Private") {
997                     $UndeclaredSymbols{$symbol} = 1;
998                     &LogWarning ($file, $., "No declaration found for $symbol.");
999                 }
1000                 $num_symbols++;
1001                 $symbol_def_line{$symbol}=$.;
1003                 if ($section_id eq "") {
1004                     if($title eq "" && $filename eq "") {
1005                         &LogWarning ($file, $., "Section has no title and no file.");
1006                     }
1007                     # FIXME: one of those would be enough
1008                     # filename should be an internal detail for gtk-doc
1009                     if ($title eq "") {
1010                         $title = $filename;
1011                     } elsif ($filename eq "") {
1012                         $filename = $title;
1013                     }
1014                     $filename =~ s/\s/_/g;
1016                     $section_id = $SourceSymbolDocs{"$TMPL_DIR/$filename:Section_Id"};
1017                     if (defined ($section_id) && $section_id !~ m/^\s*$/) {
1018                         # Remove trailing blanks and use as is
1019                         $section_id =~ s/\s+$//;
1020                     } elsif (&CheckIsObject ($title)) {
1021                         # GObjects use their class name as the ID.
1022                         $section_id = &CreateValidSGMLID ($title);
1023                     } else {
1024                         $section_id = &CreateValidSGMLID ("$MODULE-$title");
1025                     }
1026                 }
1027                 $SymbolSection{$symbol}=$title;
1028                 $SymbolSectionId{$symbol}=$section_id;
1029             }
1030             else {
1031                 &LogWarning ($file, $., "Double symbol entry for $symbol. ".
1032                     "Previous occurrence on line ".$symbol_def_line{$symbol}.".");
1033             }
1034         }
1035     }
1036     close (INPUT);
1038     &OutputMissingDocumentation;
1039     &OutputUndeclaredSymbols;
1040     &OutputUnusedSymbols;
1042     if ($OUTPUT_ALL_SYMBOLS) {
1043         &OutputAllSymbols;
1044     }
1045     if ($OUTPUT_SYMBOLS_WITHOUT_SINCE) {
1046         &OutputSymbolsWithoutSince;
1047     }
1049     for $filename (split (' ', $EXPAND_CONTENT_FILES)) {
1050         my $file_changed = &OutputExtraFile ($filename);
1051         if ($file_changed) {
1052             $changed = 1;
1053         }
1054     }
1056     &OutputBook ($book_top, $book_bottom);
1058     return $changed;
1061 #############################################################################
1062 # Function    : OutputIndex
1063 # Description : This writes an indexlist that can be included into the main-
1064 #               document into an <index> tag.
1065 #############################################################################
1067 sub OutputIndex {
1068     my ($basename, $apiindexref ) = @_;
1069     my %apiindex = %{$apiindexref};
1070     my $old_index = "$DB_OUTPUT_DIR/$basename.xml";
1071     my $new_index = "$DB_OUTPUT_DIR/$basename.new";
1072     my $lastletter = " ";
1073     my $divopen = 0;
1074     my $symbol;
1075     my $short_symbol;
1077     open (OUTPUT, ">$new_index")
1078         || die "Can't create $new_index";
1080     print (OUTPUT &MakeDocHeader ("indexdiv")."\n<indexdiv id=\"$basename\">\n");
1082     @TRACE@("generate $basename index (".%apiindex." entries)\n");
1084     # do a case insensitive sort while chopping off the prefix
1085     foreach my $hash (
1086         sort { $$a{criteria} cmp $$b{criteria} or $$a{original} cmp $$b{original} }
1087         map { my $x = uc($_); $x =~ s/^$NAME_SPACE\_?(.*)/$1/i; { criteria => $x, original => $_, short => $1 } }
1088         keys %apiindex) {
1090         $symbol = $$hash{original};
1091         if (defined($$hash{short}) && $$hash{short} ne "") {
1092             $short_symbol = $$hash{short};
1093         } else {
1094             $short_symbol = $symbol;
1095         }
1097         # generate a short symbol description
1098         my $symbol_desc = "";
1099         my $symbol_section = "";
1100         my $symbol_section_id = "";
1101         my $symbol_type = "";
1102         if (defined($DeclarationTypes{$symbol})) {
1103           $symbol_type = lc($DeclarationTypes{$symbol});
1104         }
1105         if ($symbol_type eq "") {
1106             @TRACE@("trying symbol $symbol\n");
1107             if ($symbol =~ m/(.*)::(.*)/) {
1108                 my $oname = $1;
1109                 my $osym = $2;
1110                 my $i;
1111                 @TRACE@("  trying object signal ${oname}:$osym in ".$#SignalNames." signals\n");
1112                 for ($i = 0; $i <= $#SignalNames; $i++) {
1113                     if ($SignalNames[$i] eq $osym) {
1114                         $symbol_type = "object signal";
1115                         if (defined($SymbolSection{$oname})) {
1116                            $symbol_section = $SymbolSection{$oname};
1117                            $symbol_section_id = $SymbolSectionId{$oname};
1118                         }
1119                         last;
1120                     }
1121                 }
1122             } elsif ($symbol =~ m/(.*):(.*)/) {
1123                 my $oname = $1;
1124                 my $osym = $2;
1125                 my $i;
1126                 @TRACE@("  trying object property ${oname}::$osym in ".$#ArgNames." properties\n");
1127                 for ($i = 0; $i <= $#ArgNames; $i++) {
1128                     @TRACE@("    ".$ArgNames[$i]."\n");
1129                     if ($ArgNames[$i] eq $osym) {
1130                         $symbol_type = "object property";
1131                         if (defined($SymbolSection{$oname})) {
1132                            $symbol_section = $SymbolSection{$oname};
1133                            $symbol_section_id = $SymbolSectionId{$oname};
1134                         }
1135                         last;
1136                     }
1137                 }
1138             }
1139         } else {
1140            if (defined($SymbolSection{$symbol})) {
1141                $symbol_section = $SymbolSection{$symbol};
1142                $symbol_section_id = $SymbolSectionId{$symbol};
1143            }
1144         }
1145         if ($symbol_type ne "") {
1146            $symbol_desc=", $symbol_type";
1147            if ($symbol_section ne "") {
1148                $symbol_desc.=" in <link linkend=\"$symbol_section_id\">$symbol_section</link>";
1149                #$symbol_desc.=" in ". &ExpandAbbreviations($symbol, "#$symbol_section");
1150            }
1151         }
1153         my $curletter = uc(substr($short_symbol,0,1));
1154         my $id = $apiindex{$symbol};
1156         @TRACE@("  add symbol $symbol with $id to index in section '$curletter' (derived from $short_symbol)\n");
1158         if ($curletter ne $lastletter) {
1159             $lastletter = $curletter;
1161             if ($divopen == 1) {
1162                 print (OUTPUT "</indexdiv>\n");
1163             }
1164             print (OUTPUT "<indexdiv><title>$curletter</title>\n");
1165             $divopen = 1;
1166         }
1168         print (OUTPUT <<EOF);
1169 <indexentry><primaryie linkends="$id"><link linkend="$id">$symbol</link>$symbol_desc</primaryie></indexentry>
1171     }
1173     if ($divopen == 1) {
1174         print (OUTPUT "</indexdiv>\n");
1175     }
1176     print (OUTPUT "</indexdiv>\n");
1177     close (OUTPUT);
1179     &UpdateFileIfChanged ($old_index, $new_index, 0);
1183 #############################################################################
1184 # Function    : OutputIndexFull
1185 # Description : This writes the full api indexlist that can be included into the
1186 #               main document into an <index> tag.
1187 #############################################################################
1189 sub OutputIndexFull {
1190     &OutputIndex ("api-index-full", \%IndexEntriesFull);
1194 #############################################################################
1195 # Function    : OutputDeprecatedIndex
1196 # Description : This writes the deprecated api indexlist that can be included
1197 #               into the main document into an <index> tag.
1198 #############################################################################
1200 sub OutputDeprecatedIndex {
1201     &OutputIndex ("api-index-deprecated", \%IndexEntriesDeprecated);
1205 #############################################################################
1206 # Function    : OutputSinceIndexes
1207 # Description : This writes the 'since' api indexlists that can be included into
1208 #               the main document into an <index> tag.
1209 #############################################################################
1211 sub OutputSinceIndexes {
1212     my @sinces = keys %{{ map { $_ => 1 } values %Since }};
1214     foreach my $version (@sinces) {
1215         @TRACE@("Since : [$version]\n");
1216         # TODO make filtered hash
1217         #my %index = grep { $Since{$_} eq $version } %IndexEntriesSince;
1218         my %index = map { $_ => $IndexEntriesSince{$_} } grep { $Since{$_} eq $version } keys %IndexEntriesSince;
1220         &OutputIndex ("api-index-$version", \%index);
1221     }
1224 #############################################################################
1225 # Function    : OutputAnnotationGlossary
1226 # Description : This writes a glossary of the used annotation terms into a
1227 #               separate glossary file that can be included into the main
1228 #               document.
1229 #############################################################################
1231 sub OutputAnnotationGlossary {
1232     my $old_glossary = "$DB_OUTPUT_DIR/annotation-glossary.xml";
1233     my $new_glossary = "$DB_OUTPUT_DIR/annotation-glossary.new";
1234     my $lastletter = " ";
1235     my $divopen = 0;
1237     # if there are no annotations used return
1238     return if (! keys(%AnnotationsUsed));
1240     # add acronyms that are referenced from acronym text
1241 rerun:
1242     foreach my $annotation (keys(%AnnotationsUsed)) {
1243         if(defined($AnnotationDefinition{$annotation})) {
1244             if($AnnotationDefinition{$annotation} =~ m/<acronym>([\w ]+)<\/acronym>/) {
1245                 if (!exists($AnnotationsUsed{$1})) {
1246                     $AnnotationsUsed{$1} = 1;
1247                     goto rerun;
1248                 }
1249             }
1250         }
1251     }
1253     open (OUTPUT, ">$new_glossary")
1254         || die "Can't create $new_glossary";
1256     print (OUTPUT  <<EOF);
1257 ${\( MakeDocHeader ("glossary") )}
1258 <glossary id="annotation-glossary">
1259   <title>Annotation Glossary</title>
1262     foreach my $annotation (sort({lc $a cmp lc $b} keys(%AnnotationsUsed))) {
1263         if(defined($AnnotationDefinition{$annotation})) {
1264             my $def = $AnnotationDefinition{$annotation};
1265             my $curletter = uc(substr($annotation,0,1));
1267             if ($curletter ne $lastletter) {
1268                 $lastletter = $curletter;
1270                 if ($divopen == 1) {
1271                     print (OUTPUT "</glossdiv>\n");
1272                 }
1273                 print (OUTPUT "<glossdiv><title>$curletter</title>\n");
1274                 $divopen = 1;
1275             }
1276             print (OUTPUT <<EOF);
1277     <glossentry>
1278       <glossterm><anchor id="annotation-glossterm-$annotation"/>$annotation</glossterm>
1279       <glossdef>
1280         <para>$def</para>
1281       </glossdef>
1282     </glossentry>
1284         }
1285     }
1287     if ($divopen == 1) {
1288         print (OUTPUT "</glossdiv>\n");
1289     }
1290     print (OUTPUT "</glossary>\n");
1291     close (OUTPUT);
1293     &UpdateFileIfChanged ($old_glossary, $new_glossary, 0);
1296 #############################################################################
1297 # Function    : ReadKnownSymbols
1298 # Description : This collects the names of non-private symbols from the
1299 #               $MODULE-sections.txt file.
1300 # Arguments   : $file - the $MODULE-sections.txt file which contains all of
1301 #                the functions/macros/structs etc. being documented, organised
1302 #                into sections and subsections.
1303 #############################################################################
1305 sub ReadKnownSymbols {
1306     my ($file) = @_;
1308     my $subsection = "";
1310     @TRACE@("Reading: $file\n");
1311     open (INPUT, $file)
1312         || die "Can't open $file: $!";
1314     while (<INPUT>) {
1315         if (m/^#/) {
1316             next;
1318         } elsif (m/^<SECTION>/) {
1319             $subsection = "";
1321         } elsif (m/^<SUBSECTION\s*(.*)>/i) {
1322             $subsection = $1;
1324         } elsif (m/^<SUBSECTION>/) {
1325             next;
1327         } elsif (m/^<TITLE>(.*)<\/TITLE>/) {
1328             next;
1330         } elsif (m/^<FILE>(.*)<\/FILE>/) {
1331             $KnownSymbols{"$TMPL_DIR/$1:Long_Description"} = 1;
1332             $KnownSymbols{"$TMPL_DIR/$1:Short_Description"} = 1;
1333             next;
1335         } elsif (m/^<INCLUDE>(.*)<\/INCLUDE>/) {
1336             next;
1338         } elsif (m/^<\/SECTION>/) {
1339             next;
1341         } elsif (m/^(\S+)/) {
1342             my $symbol = $1;
1344             if ($subsection ne "Standard" && $subsection ne "Private") {
1345                 $KnownSymbols{$symbol} = 1;
1346             }
1347             else {
1348                 $KnownSymbols{$symbol} = 0;
1349             }
1350         }
1351     }
1352     close (INPUT);
1356 #############################################################################
1357 # Function    : OutputDeclaration
1358 # Description : Returns the synopsis and detailed description DocBook
1359 #                describing one function/macro etc.
1360 # Arguments   : $symbol - the name of the function/macro begin described.
1361 #                $declaration - the declaration of the function/macro.
1362 #############################################################################
1364 sub OutputDeclaration {
1365     my ($symbol, $declaration) = @_;
1367     my $type = $DeclarationTypes {$symbol};
1368     if ($type eq 'MACRO') {
1369         return &OutputMacro ($symbol, $declaration);
1370     } elsif ($type eq 'TYPEDEF') {
1371         return &OutputTypedef ($symbol, $declaration);
1372     } elsif ($type eq 'STRUCT') {
1373         return &OutputStruct ($symbol, $declaration);
1374     } elsif ($type eq 'ENUM') {
1375         return &OutputEnum ($symbol, $declaration);
1376     } elsif ($type eq 'UNION') {
1377         return &OutputUnion ($symbol, $declaration);
1378     } elsif ($type eq 'VARIABLE') {
1379         return &OutputVariable ($symbol, $declaration);
1380     } elsif ($type eq 'FUNCTION') {
1381         return &OutputFunction ($symbol, $declaration, $type);
1382     } elsif ($type eq 'USER_FUNCTION') {
1383         return &OutputFunction ($symbol, $declaration, $type);
1384     } else {
1385         die "Unknown symbol type";
1386     }
1390 #############################################################################
1391 # Function    : OutputSymbolTraits
1392 # Description : Returns the Since and StabilityLevel paragraphs for a symbol.
1393 # Arguments   : $symbol - the name of the function/macro begin described.
1394 #############################################################################
1396 sub OutputSymbolTraits {
1397     my ($symbol) = @_;
1398     my $desc = "";
1400     if (exists $Since{$symbol}) {
1401         my $link_id = "api-index-".$Since{$symbol};
1402         $desc .= "<para role=\"since\">Since: <link linkend=\"$link_id\">$Since{$symbol}</link></para>";
1403     }
1404     if (exists $StabilityLevel{$symbol}) {
1405         my $stability = $StabilityLevel{$symbol};
1406         $AnnotationsUsed{$stability} = 1;
1407         $desc .= "<para role=\"stability\">Stability Level: <acronym>$stability</acronym></para>";
1408     }
1409     return $desc;
1412 #############################################################################
1413 # Function    : Output{Symbol,Section}ExtraLinks
1414 # Description : Returns extralinks for the symbol (if enabled).
1415 # Arguments   : $symbol - the name of the function/macro begin described.
1416 #############################################################################
1418 sub uri_escape {
1419     my $text = $_[0];
1420     return undef unless defined $text;
1422     # Build a char to hex map
1423     my %escapes = ();
1424     for (0..255) {
1425             $escapes{chr($_)} = sprintf("%%%02X", $_);
1426     }
1428     # Default unsafe characters.  RFC 2732 ^(uric - reserved)
1429     $text =~ s/([^A-Za-z0-9\-_.!~*'()])/$escapes{$1}/g;
1431     return $text;
1434 sub OutputSymbolExtraLinks {
1435     my ($symbol) = @_;
1436     my $desc = "";
1438     if (0) { # NEW FEATURE: needs configurability
1439     my $sstr = &uri_escape($symbol);
1440     my $mstr = &uri_escape($MODULE);
1441     $desc .= <<EOF;
1442 <ulink role="extralinks" url="http://www.google.com/codesearch?q=$sstr">code search</ulink>
1443 <ulink role="extralinks" url="http://library.gnome.org/edit?module=$mstr&amp;symbol=$sstr">edit documentation</ulink>
1445     }
1446     return $desc;
1449 sub OutputSectionExtraLinks {
1450     my ($symbol,$docsymbol) = @_;
1451     my $desc = "";
1453     if (0) { # NEW FEATURE: needs configurability
1454     my $sstr = &uri_escape($symbol);
1455     my $mstr = &uri_escape($MODULE);
1456     my $dsstr = &uri_escape($docsymbol);
1457     $desc .= <<EOF;
1458 <ulink role="extralinks" url="http://www.google.com/codesearch?q=$sstr">code search</ulink>
1459 <ulink role="extralinks" url="http://library.gnome.org/edit?module=$mstr&amp;symbol=$dsstr">edit documentation</ulink>
1461     }
1462     return $desc;
1466 #############################################################################
1467 # Function    : OutputMacro
1468 # Description : Returns the synopsis and detailed description of a macro.
1469 # Arguments   : $symbol - the macro.
1470 #                $declaration - the declaration of the macro.
1471 #############################################################################
1473 sub OutputMacro {
1474     my ($symbol, $declaration) = @_;
1475     my $id = &CreateValidSGMLID ($symbol);
1476     my $condition = &MakeConditionDescription ($symbol);
1477     my $synop = "<row><entry role=\"define_keyword\">#define</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link>";
1478     my $desc;
1480     my @fields = ParseMacroDeclaration($declaration, \&CreateValidSGML);
1481     my $title = $symbol . (@fields ? "()" : "");
1483     $desc = "<refsect2 id=\"$id\" role=\"macro\"$condition>\n<title>$title</title>\n";
1484     $desc .= MakeIndexterms($symbol, $id);
1485     $desc .= "\n";
1486     $desc .= OutputSymbolExtraLinks($symbol);
1488     if (@fields) {
1489         $synop .= "<phrase role=\"c_punctuation\">()</phrase>";
1490     }
1491     $synop .= "</entry></row>\n";
1493     # Don't output the macro definition if is is a conditional macro or it
1494     # looks like a function, i.e. starts with "g_" or "_?gnome_", or it is
1495     # longer than 2 lines, otherwise we get lots of complicated macros like
1496     # g_assert.
1497     if (!defined ($DeclarationConditional{$symbol}) && ($symbol !~ m/^g_/)
1498         && ($symbol !~ m/^_?gnome_/) && (($declaration =~ tr/\n//) < 2)) {
1499         my $decl_out = &CreateValidSGML ($declaration);
1500         $desc .= "<programlisting language=\"C\">$decl_out</programlisting>\n";
1501     } else {
1502         $desc .= "<programlisting language=\"C\">" . &MakeReturnField("#define") . "$symbol";
1503         if ($declaration =~ m/^\s*#\s*define\s+\w+(\([^\)]*\))/) {
1504             my $args = $1;
1505             my $pad = ' ' x ($RETURN_TYPE_FIELD_WIDTH - length ("#define "));
1506             # Align each line so that if should all line up OK.
1507             $args =~ s/\n/\n$pad/gm;
1508             $desc .= &CreateValidSGML ($args);
1509         }
1510         $desc .= "</programlisting>\n";
1511     }
1513     $desc .= &MakeDeprecationNote($symbol);
1515     my $parameters = &OutputParamDescriptions ("MACRO", $symbol, @fields);
1517     if (defined ($SymbolDocs{$symbol})) {
1518         my $symbol_docs = &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1519         $desc .= $symbol_docs;
1520     }
1522     $desc .= $parameters;
1523     $desc .= OutputSymbolTraits ($symbol);
1524     $desc .= "</refsect2>\n";
1525     return ($synop, $desc);
1529 #############################################################################
1530 # Function    : OutputTypedef
1531 # Description : Returns the synopsis and detailed description of a typedef.
1532 # Arguments   : $symbol - the typedef.
1533 #                $declaration - the declaration of the typedef,
1534 #                  e.g. 'typedef unsigned int guint;'
1535 #############################################################################
1537 sub OutputTypedef {
1538     my ($symbol, $declaration) = @_;
1539     my $id = &CreateValidSGMLID ($symbol);
1540     my $condition = &MakeConditionDescription ($symbol);
1541     my $desc = "<refsect2 id=\"$id\" role=\"typedef\"$condition>\n<title>$symbol</title>\n";
1542     my $synop = "<row><entry role=\"typedef_keyword\">typedef</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
1544     $desc .= MakeIndexterms($symbol, $id);
1545     $desc .= "\n";
1546     $desc .= OutputSymbolExtraLinks($symbol);
1548     if (!defined ($DeclarationConditional{$symbol})) {
1549         my $decl_out = &CreateValidSGML ($declaration);
1550         $desc .= "<programlisting language=\"C\">$decl_out</programlisting>\n";
1551     }
1553     $desc .= &MakeDeprecationNote($symbol);
1555     if (defined ($SymbolDocs{$symbol})) {
1556         $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1557     }
1558     $desc .= OutputSymbolTraits ($symbol);
1559     $desc .= "</refsect2>\n";
1560     return ($synop, $desc);
1564 #############################################################################
1565 # Function    : OutputStruct
1566 # Description : Returns the synopsis and detailed description of a struct.
1567 #                We check if it is a object struct, and if so we only output
1568 #                parts of it that are noted as public fields.
1569 #                We also use a different IDs for object structs, since the
1570 #                original ID is used for the entire RefEntry.
1571 # Arguments   : $symbol - the struct.
1572 #                $declaration - the declaration of the struct.
1573 #############################################################################
1575 sub OutputStruct {
1576     my ($symbol, $declaration) = @_;
1578     my $is_gtype = 0;
1579     my $default_to_public = 1;
1580     if (&CheckIsObject ($symbol)) {
1581         @TRACE@("Found struct gtype: $symbol\n");
1582         $is_gtype = 1;
1583         $default_to_public = $ObjectRoots{$symbol} eq 'GBoxed';
1584     }
1586     my $id;
1587     my $condition;
1588     if ($is_gtype) {
1589         $id = &CreateValidSGMLID ($symbol . "_struct");
1590         $condition = &MakeConditionDescription ($symbol . "_struct");
1591     } else {
1592         $id = &CreateValidSGMLID ($symbol);
1593         $condition = &MakeConditionDescription ($symbol);
1594     }
1596     # Determine if it is a simple struct or it also has a typedef.
1597     my $has_typedef = 0;
1598     if ($StructHasTypedef{$symbol} || $declaration =~ m/^\s*typedef\s+/) {
1599       $has_typedef = 1;
1600     }
1602     my $type_output;
1603     my $desc;
1604     if ($has_typedef) {
1605         # For structs with typedefs we just output the struct name.
1606         $type_output = "";
1607         $desc = "<refsect2 id=\"$id\" role=\"struct\"$condition>\n<title>$symbol</title>\n";
1608     } else {
1609         $type_output = "struct";
1610         $desc = "<refsect2 id=\"$id\" role=\"struct\"$condition>\n<title>struct $symbol</title>\n";
1611     }
1612     my $synop = "<row><entry role=\"datatype_keyword\">${type_output}</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
1614     $desc .= MakeIndexterms($symbol, $id);
1615     $desc .= "\n";
1616     $desc .= OutputSymbolExtraLinks($symbol);
1618     # Form a pretty-printed, private-data-removed form of the declaration
1620     my $decl_out = "";
1621     if ($declaration =~ m/^\s*$/) {
1622         @TRACE@("Found opaque struct: $symbol\n");
1623         $decl_out = "typedef struct _$symbol $symbol;";
1624     } elsif ($declaration =~ m/^\s*struct\s+\w+\s*;\s*$/) {
1625         @TRACE@("Found opaque struct: $symbol\n");
1626         $decl_out = "struct $symbol;";
1627     } else {
1628         my $public = $default_to_public;
1629         my $new_declaration = "";
1630         my $decl_line;
1631         my $decl = $declaration;
1633         if ($decl =~ m/^\s*(typedef\s+)?struct\s*\w*\s*(?:\/\*.*\*\/)?\s*{(.*)}\s*\w*\s*;\s*$/s) {
1634             my $struct_contents = $2;
1636             foreach $decl_line (split (/\n/, $struct_contents)) {
1637                 @TRACE@("Struct line: $decl_line\n");
1638                 if ($decl_line =~ m%/\*\s*<\s*public\s*>\s*\*/%) {
1639                     $public = 1;
1640                 } elsif ($decl_line =~ m%/\*\s*<\s*(private|protected)\s*>\s*\*/%) {
1641                     $public = 0;
1642                 } elsif ($public) {
1643                     $new_declaration .= $decl_line . "\n";
1644                 }
1645             }
1647             if ($new_declaration) {
1648                 # Strip any blank lines off the ends.
1649                 $new_declaration =~ s/^\s*\n//;
1650                 $new_declaration =~ s/\n\s*$/\n/;
1652                 if ($has_typedef) {
1653                     $decl_out = "typedef struct {\n" . $new_declaration
1654                       . "} $symbol;\n";
1655                 } else {
1656                     $decl_out = "struct $symbol {\n" . $new_declaration
1657                       . "};\n";
1658                 }
1659             }
1660         } else {
1661             &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1662                 "Couldn't parse struct:\n$declaration");
1663         }
1665         # If we couldn't parse the struct or it was all private, output an
1666         # empty struct declaration.
1667         if ($decl_out eq "") {
1668             if ($has_typedef) {
1669                 $decl_out = "typedef struct _$symbol $symbol;";
1670             } else {
1671                 $decl_out = "struct $symbol;";
1672             }
1673         }
1674     }
1676     $decl_out = &CreateValidSGML ($decl_out);
1677     $desc .= "<programlisting language=\"C\">$decl_out</programlisting>\n";
1679     $desc .= &MakeDeprecationNote($symbol);
1681     if (defined ($SymbolDocs{$symbol})) {
1682         $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1683     }
1685     # Create a table of fields and descriptions
1687     # FIXME: Inserting &#160's into the produced type declarations here would
1688     #        improve the output in most situations ... except for function
1689     #        members of structs!
1690     my @fields = ParseStructDeclaration($declaration, !$default_to_public,
1691                                         0, \&MakeXRef,
1692                                         sub {
1693                                             "<structfield id=\"".&CreateValidSGMLID("$id.$_[0]")."\">$_[0]</structfield>";
1694                                         });
1695     my $params = $SymbolParams{$symbol};
1697     # If no parameters are filled in, we don't generate the description
1698     # table, for backwards compatibility.
1700     my $found = 0;
1701     if (defined $params) {
1702         for (my $i = 1; $i <= $#$params; $i += $PARAM_FIELD_COUNT) {
1703             if ($params->[$i] =~ /\S/) {
1704                 $found = 1;
1705                 last;
1706             }
1707         }
1708     }
1710     if ($found) {
1711         my %field_descrs = @$params;
1712         my $missing_parameters = "";
1713         my $unused_parameters = "";
1714         my $id = &CreateValidSGMLID ("$symbol".".members");
1716         $desc .= <<EOF;
1717 <refsect3 id="$id" role="struct_members">\n<title>Members</title>
1718 <informaltable role="struct_members_table" pgwide="1" frame="none">
1719 <tgroup cols="3">
1720 <colspec colname="struct_members_name" colwidth="300px"/>
1721 <colspec colname="struct_members_description"/>
1722 <colspec colname="struct_members_annotations" colwidth="200px"/>
1723 <tbody>
1726         while (@fields) {
1727             my $field_name = shift @fields;
1728             my $text = shift @fields;
1729             my $field_descr = $field_descrs{$field_name};
1730             my $param_annotations = "";
1732             $desc .= "<row role=\"member\"><entry role=\"struct_member_name\"><para>$text</para></entry>\n";
1733             if (defined $field_descr) {
1734                 ($field_descr,$param_annotations) = &ExpandAnnotation($symbol, $field_descr);
1735                 $field_descr = &ConvertMarkDown($symbol, $field_descr);
1736                 # trim
1737                 $field_descr =~ s/^(\s|\n)+//msg;
1738                 $field_descr =~ s/(\s|\n)+$//msg;
1739                 $desc .= "<entry role=\"struct_member_description\">$field_descr</entry>\n<entry role=\"struct_member_annotations\">$param_annotations</entry>\n";
1740                 delete $field_descrs{$field_name};
1741             } else {
1742                 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1743                     "Field description for $symbol"."::"."$field_name is missing in source code comment block.");
1744                 if ($missing_parameters ne "") {
1745                   $missing_parameters .= ", ".$field_name;
1746                 } else {
1747                     $missing_parameters = $field_name;
1748                 }
1749                 $desc .= "<entry /><entry />\n";
1750             }
1751             $desc .= "</row>\n";
1752         }
1753         $desc .= "</tbody></tgroup></informaltable>\n</refsect3>\n";
1754         foreach my $field_name (keys %field_descrs) {
1755             # Documenting those standard fields is not required anymore, but
1756             # we don't want to warn if they are documented anyway.
1757             if ($field_name =~ /(g_iface|parent_instance|parent_class)/) {
1758                 next;
1759             }
1760             &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1761                 "Field description for $symbol"."::"."$field_name is not used from source code comment block.");
1762             if ($unused_parameters ne "") {
1763               $unused_parameters .= ", ".$field_name;
1764             } else {
1765                $unused_parameters = $field_name;
1766             }
1767         }
1769         # remember missing/unused parameters (needed in tmpl-free build)
1770         if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) {
1771             $AllIncompleteSymbols{$symbol}=$missing_parameters;
1772         }
1773         if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) {
1774             $AllUnusedSymbols{$symbol}=$unused_parameters;
1775         }
1776     }
1777     else {
1778         if (scalar(@fields) > 0) {
1779             if (! exists ($AllIncompleteSymbols{$symbol})) {
1780                 $AllIncompleteSymbols{$symbol}="<items>";
1781                 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1782                     "Field descriptions for struct $symbol are missing in source code comment block.");
1783                 @TRACE@("Remaining structs fields: ".@fields.":".join(',',@fields)."\n");
1784             }
1785         }
1786     }
1788     $desc .= OutputSymbolTraits ($symbol);
1789     $desc .= "</refsect2>\n";
1790     return ($synop, $desc);
1794 #############################################################################
1795 # Function    : OutputUnion
1796 # Description : Returns the synopsis and detailed description of a union.
1797 # Arguments   : $symbol - the union.
1798 #                $declaration - the declaration of the union.
1799 #############################################################################
1801 sub OutputUnion {
1802     my ($symbol, $declaration) = @_;
1804     my $is_gtype = 0;
1805     if (&CheckIsObject ($symbol)) {
1806         @TRACE@("Found union gtype: $symbol\n");
1807         $is_gtype = 1;
1808     }
1810     my $id;
1811     my $condition;
1812     if ($is_gtype) {
1813         $id = &CreateValidSGMLID ($symbol . "_union");
1814         $condition = &MakeConditionDescription ($symbol . "_union");
1815     } else {
1816         $id = &CreateValidSGMLID ($symbol);
1817         $condition = &MakeConditionDescription ($symbol);
1818     }
1820     # Determine if it is a simple struct or it also has a typedef.
1821     my $has_typedef = 0;
1822     if ($StructHasTypedef{$symbol} || $declaration =~ m/^\s*typedef\s+/) {
1823       $has_typedef = 1;
1824     }
1826     my $type_output;
1827     my $desc;
1828     if ($has_typedef) {
1829         # For unions with typedefs we just output the union name.
1830         $type_output = "";
1831         $desc = "<refsect2 id=\"$id\" role=\"union\"$condition>\n<title>$symbol</title>\n";
1832     } else {
1833         $type_output = "union";
1834         $desc = "<refsect2 id=\"$id\" role=\"union\"$condition>\n<title>union $symbol</title>\n";
1835     }
1836     my $synop = "<row><entry role=\"datatype_keyword\">${type_output}</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
1838     $desc .= MakeIndexterms($symbol, $id);
1839     $desc .= "\n";
1840     $desc .= OutputSymbolExtraLinks($symbol);
1841     $desc .= &MakeDeprecationNote($symbol);
1843     if (defined ($SymbolDocs{$symbol})) {
1844         $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1845     }
1847     # Create a table of fields and descriptions
1849     # FIXME: Inserting &#160's into the produced type declarations here would
1850     #        improve the output in most situations ... except for function
1851     #        members of structs!
1852     my @fields = ParseStructDeclaration($declaration, 0,
1853                                         0, \&MakeXRef,
1854                                         sub {
1855                                             "<structfield id=\"".&CreateValidSGMLID("$id.$_[0]")."\">$_[0]</structfield>";
1856                                         });
1857     my $params = $SymbolParams{$symbol};
1859     # If no parameters are filled in, we don't generate the description
1860     # table, for backwards compatibility
1862     my $found = 0;
1863     if (defined $params) {
1864         for (my $i = 1; $i <= $#$params; $i += $PARAM_FIELD_COUNT) {
1865             if ($params->[$i] =~ /\S/) {
1866                 $found = 1;
1867                 last;
1868             }
1869         }
1870     }
1872     if ($found) {
1873         my %field_descrs = @$params;
1874         my $missing_parameters = "";
1875         my $unused_parameters = "";
1876         my $id = &CreateValidSGMLID ("$symbol".".members");
1878         $desc .= <<EOF;
1879 <refsect3 id="$id" role="union_members">\n<title>Members</title>
1880 <informaltable role="union_members_table" pgwide="1" frame="none">
1881 <tgroup cols="3">
1882 <colspec colname="union_members_name" colwidth="300px"/>
1883 <colspec colname="union_members_description"/>
1884 <colspec colname="union_members_annotations" colwidth="200px"/>
1885 <tbody>
1888         while (@fields) {
1889             my $field_name = shift @fields;
1890             my $text = shift @fields;
1891             my $field_descr = $field_descrs{$field_name};
1892             my $param_annotations = "";
1894             $desc .= "<row><entry role=\"union_member_name\"><para>$text</para></entry>\n";
1895             if (defined $field_descr) {
1896                 ($field_descr,$param_annotations) = &ExpandAnnotation($symbol, $field_descr);
1897                 $field_descr = &ConvertMarkDown($symbol, $field_descr);
1899                 # trim
1900                 $field_descr =~ s/^(\s|\n)+//msg;
1901                 $field_descr =~ s/(\s|\n)+$//msg;
1902                 $desc .= "<entry role=\"union_member_description\">$field_descr</entry>\n<entry role=\"union_member_annotations\">$param_annotations</entry>\n";
1903                 delete $field_descrs{$field_name};
1904             } else {
1905                 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1906                     "Field description for $symbol"."::"."$field_name is missing in source code comment block.");
1907                 if ($missing_parameters ne "") {
1908                     $missing_parameters .= ", ".$field_name;
1909                 } else {
1910                     $missing_parameters = $field_name;
1911                 }
1912                 $desc .= "<entry /><entry />\n";
1913             }
1914             $desc .= "</row>\n";
1915         }
1916         $desc .= "</tbody></tgroup></informaltable>\n</refsect3>";
1917         foreach my $field_name (keys %field_descrs) {
1918             &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1919                 "Field description for $symbol"."::"."$field_name is not used from source code comment block.");
1920             if ($unused_parameters ne "") {
1921               $unused_parameters .= ", ".$field_name;
1922             } else {
1923                $unused_parameters = $field_name;
1924             }
1925         }
1927         # remember missing/unused parameters (needed in tmpl-free build)
1928         if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) {
1929             $AllIncompleteSymbols{$symbol}=$missing_parameters;
1930         }
1931         if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) {
1932             $AllUnusedSymbols{$symbol}=$unused_parameters;
1933         }
1934     }
1935     else {
1936         if (scalar(@fields) > 0) {
1937             if (! exists ($AllIncompleteSymbols{$symbol})) {
1938                 $AllIncompleteSymbols{$symbol}="<items>";
1939                 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
1940                     "Field descriptions for union $symbol are missing in source code comment block.");
1941                 @TRACE@("Remaining union fields: ".@fields.":".join(',',@fields)."\n");
1942             }
1943         }
1944     }
1946     $desc .= OutputSymbolTraits ($symbol);
1947     $desc .= "</refsect2>\n";
1948     return ($synop, $desc);
1952 #############################################################################
1953 # Function    : OutputEnum
1954 # Description : Returns the synopsis and detailed description of a enum.
1955 # Arguments   : $symbol - the enum.
1956 #                $declaration - the declaration of the enum.
1957 #############################################################################
1959 sub OutputEnum {
1960     my ($symbol, $declaration) = @_;
1962     my $is_gtype = 0;
1963     if (&CheckIsObject ($symbol)) {
1964         @TRACE@("Found enum gtype: $symbol\n");
1965         $is_gtype = 1;
1966     }
1968     my $id;
1969     my $condition;
1970     if ($is_gtype) {
1971         $id = &CreateValidSGMLID ($symbol . "_enum");
1972         $condition = &MakeConditionDescription ($symbol . "_enum");
1973     } else {
1974         $id = &CreateValidSGMLID ($symbol);
1975         $condition = &MakeConditionDescription ($symbol);
1976     }
1978     my $synop = "<row><entry role=\"datatype_keyword\">enum</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
1979     my $desc = "<refsect2 id=\"$id\" role=\"enum\"$condition>\n<title>enum $symbol</title>\n";
1981     $desc .= MakeIndexterms($symbol, $id);
1982     $desc .= "\n";
1983     $desc .= OutputSymbolExtraLinks($symbol);
1984     $desc .= &MakeDeprecationNote($symbol);
1986     if (defined ($SymbolDocs{$symbol})) {
1987         $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
1988     }
1990     # Create a table of fields and descriptions
1992     my @fields = ParseEnumDeclaration($declaration);
1993     my $params = $SymbolParams{$symbol};
1995     # If nothing at all is documented log a single summary warning at the end.
1996     # Otherwise, warn about each undocumented item.
1998     my $found = 0;
1999     if (defined $params) {
2000         for (my $i = 1; $i <= $#$params; $i += $PARAM_FIELD_COUNT) {
2001             if ($params->[$i] =~ /\S/) {
2002                 $found = 1;
2003                 last;
2004             }
2005         }
2006     }
2008     my %field_descrs = (defined $params ? @$params : ());
2009     my $missing_parameters = "";
2010     my $unused_parameters = "";
2012     $id = &CreateValidSGMLID ("$symbol".".members");
2013     $desc .= <<EOF;
2014 <refsect3 id="$id" role="enum_members">\n<title>Members</title>
2015 <informaltable role="enum_members_table" pgwide="1" frame="none">
2016 <tgroup cols="3">
2017 <colspec colname="enum_members_name" colwidth="300px"/>
2018 <colspec colname="enum_members_description"/>
2019 <colspec colname="enum_members_annotations" colwidth="200px"/>
2020 <tbody>
2023     for my $field_name (@fields) {
2024         my $field_descr = $field_descrs{$field_name};
2025         my $param_annotations = "";
2027         $id = &CreateValidSGMLID ($field_name);
2028         $condition = &MakeConditionDescription ($field_name);
2029         $desc .= "<row role=\"constant\"><entry role=\"enum_member_name\"><para id=\"$id\">$field_name</para></entry>\n";
2030         if (defined $field_descr) {
2031             ($field_descr,$param_annotations) = &ExpandAnnotation($symbol, $field_descr);
2032             $field_descr = &ConvertMarkDown($symbol, $field_descr);
2033             $desc .= "<entry role=\"enum_member_description\">$field_descr</entry>\n<entry role=\"enum_member_annotations\">$param_annotations</entry>\n";
2034             delete $field_descrs{$field_name};
2035         } else {
2036             if ($found) {
2037                 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2038                     "Value description for $symbol"."::"."$field_name is missing in source code comment block.");
2039                 if ($missing_parameters ne "") {
2040                     $missing_parameters .= ", ".$field_name;
2041                 } else {
2042                     $missing_parameters = $field_name;
2043                 }
2044             }
2045             $desc .= "<entry /><entry />\n";
2046         }
2047         $desc .= "</row>\n";
2048     }
2049     $desc .= "</tbody></tgroup></informaltable>\n</refsect3>";
2050     foreach my $field_name (keys %field_descrs) {
2051         &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2052             "Value description for $symbol"."::"."$field_name is not used from source code comment block.");
2053         if ($unused_parameters ne "") {
2054             $unused_parameters .= ", ".$field_name;
2055         } else {
2056             $unused_parameters = $field_name;
2057         }
2058     }
2060     # remember missing/unused parameters (needed in tmpl-free build)
2061     if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) {
2062         $AllIncompleteSymbols{$symbol}=$missing_parameters;
2063     }
2064     if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) {
2065         $AllUnusedSymbols{$symbol}=$unused_parameters;
2066     }
2068     if (!$found) {
2069         if (scalar(@fields) > 0) {
2070             if (! exists ($AllIncompleteSymbols{$symbol})) {
2071                 $AllIncompleteSymbols{$symbol}="<items>";
2072                 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2073                     "Value descriptions for $symbol are missing in source code comment block.");
2074             }
2075         }
2076     }
2078     $desc .= OutputSymbolTraits ($symbol);
2079     $desc .= "</refsect2>\n";
2080     return ($synop, $desc);
2084 #############################################################################
2085 # Function    : OutputVariable
2086 # Description : Returns the synopsis and detailed description of a variable.
2087 # Arguments   : $symbol - the extern'ed variable.
2088 #                $declaration - the declaration of the variable.
2089 #############################################################################
2091 sub OutputVariable {
2092     my ($symbol, $declaration) = @_;
2093     my $id = &CreateValidSGMLID ($symbol);
2094     my $condition = &MakeConditionDescription ($symbol);
2096     @TRACE@("ouputing variable: '$symbol' '$declaration'");
2098     my $type_output;
2099     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*;/) {
2100         my $mod1 = defined ($1) ? $1 : "";
2101         my $ptr = defined ($3) ? $3 : "";
2102         my $space = defined ($4) ? $4 : "";
2103         my $mod2 = defined ($5) ? $5 : "";
2104         $type_output = "extern $mod1$ptr$space$mod2";
2105     } 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*=/) {
2106         my $mod1 = defined ($1) ? $1 : "";
2107         my $ptr = defined ($3) ? $3 : "";
2108         my $space = defined ($4) ? $4 : "";
2109         my $mod2 = defined ($5) ? $5 : "";
2110         $type_output = "$mod1$ptr$space$mod2";
2111     } else {
2112         $type_output = "extern";
2113     }
2114     my $synop = "<row><entry role=\"variable_type\">${type_output}</entry><entry role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
2116     my $desc = "<refsect2 id=\"$id\" role=\"variable\"$condition>\n<title>$symbol</title>\n";
2118     $desc .= MakeIndexterms($symbol, $id);
2119     $desc .= "\n";
2120     $desc .= OutputSymbolExtraLinks($symbol);
2122     my $decl_out = &CreateValidSGML ($declaration);
2123     $desc .= "<programlisting language=\"C\">$decl_out</programlisting>\n";
2125     $desc .= &MakeDeprecationNote($symbol);
2127     if (defined ($SymbolDocs{$symbol})) {
2128         $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
2129     }
2130     if (defined ($SymbolAnnotations{$symbol})) {
2131         my $param_desc = $SymbolAnnotations{$symbol};
2132         my $param_annotations = "";
2133         ($param_desc,$param_annotations) = &ExpandAnnotation($symbol, $param_desc);
2134         if ($param_annotations ne "") {
2135             $desc .= "\n<para>$param_annotations</para>";
2136         }
2137     }
2139     $desc .= OutputSymbolTraits ($symbol);
2140     $desc .= "</refsect2>\n";
2141     return ($synop, $desc);
2145 #############################################################################
2146 # Function    : OutputFunction
2147 # Description : Returns the synopsis and detailed description of a function.
2148 # Arguments   : $symbol - the function.
2149 #                $declaration - the declaration of the function.
2150 #############################################################################
2152 sub OutputFunction {
2153     my ($symbol, $declaration, $symbol_type) = @_;
2154     my $id = &CreateValidSGMLID ($symbol);
2155     my $condition = &MakeConditionDescription ($symbol);
2157     # Take out the return type     $1                                                                                       $2   $3
2158     $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//;
2159     my $type_modifier = defined($1) ? $1 : "";
2160     my $type = $2;
2161     my $pointer = $3;
2162     # Trim trailing spaces as we are going to pad to $RETURN_TYPE_FIELD_WIDTH below anyway
2163     $pointer =~ s/\s+$//;
2164     my $xref = &MakeXRef ($type, &tagify($type, "returnvalue"));
2165     my $start = "";
2166     #if ($symbol_type eq 'USER_FUNCTION') {
2167     #    $start = "typedef ";
2168     #}
2170     # We output const rather than G_CONST_RETURN.
2171     $type_modifier =~ s/G_CONST_RETURN/const/g;
2172     $pointer =~ s/G_CONST_RETURN/const/g;
2173     $pointer =~ s/^\s+/&#160;/g;
2175     my $ret_type_output;
2176     $ret_type_output = "$start$type_modifier$xref$pointer\n";
2178     my $indent_len;
2179     $indent_len = length ($symbol) + 2;
2180     my $char1 = my $char2 = my $char3 = "";
2181     if ($symbol_type eq 'USER_FUNCTION') {
2182         $indent_len += 3;
2183         $char1 = "<phrase role=\"c_punctuation\">(</phrase>";
2184         $char2 = "*";
2185         $char3 = "<phrase role=\"c_punctuation\">)</phrase>";
2186     }
2188     my ($symbol_output, $symbol_desc_output);
2189     $symbol_output = "$char1<link linkend=\"$id\">$char2$symbol</link>$char3";
2190     if ($indent_len < $MAX_SYMBOL_FIELD_WIDTH) {
2191         $symbol_desc_output = "$char1$char2$symbol$char3 ";
2192     } else {
2193         $indent_len = $MAX_SYMBOL_FIELD_WIDTH - 8;
2194         $symbol_desc_output = "$char1$char2$symbol$char3\n"
2195           . (' ' x ($indent_len - 1));
2196     }
2198     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";
2200     my $desc = "<refsect2 id=\"$id\" role=\"function\"$condition>\n<title>${symbol}&#160;()</title>\n";
2202     $desc .= MakeIndexterms($symbol, $id);
2203     $desc .= "\n";
2204     $desc .= OutputSymbolExtraLinks($symbol);
2206     $desc  .= "<programlisting language=\"C\">${ret_type_output}$symbol_desc_output(";
2208     my @fields = ParseFunctionDeclaration($declaration, \&MakeXRef,
2209                                         sub {
2210                                             &tagify($_[0],"parameter");
2211                                         });
2213     for (my $i = 1; $i <= $#fields; $i += 2) {
2214         my $field_name = $fields[$i];
2216         if ($i == 1) {
2217             $desc  .= "$field_name";
2218         } else {
2219             $desc  .= ",\n"
2220                 . (' ' x $indent_len)
2221                 . "$field_name";
2222         }
2224     }
2226     $desc  .= ");</programlisting>\n";
2228     $desc .= &MakeDeprecationNote($symbol);
2230     if (defined ($SymbolDocs{$symbol})) {
2231         $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
2232     }
2233     if (defined ($SymbolAnnotations{$symbol})) {
2234         my $param_desc = $SymbolAnnotations{$symbol};
2235         my $param_annotations = "";
2236         ($param_desc,$param_annotations) = &ExpandAnnotation($symbol, $param_desc);
2237         if ($param_annotations ne "") {
2238             $desc .= "\n<para>$param_annotations</para>";
2239         }
2240     }
2242     $desc .= &OutputParamDescriptions ("FUNCTION", $symbol, @fields);
2243     $desc .= OutputSymbolTraits ($symbol);
2244     $desc .= "</refsect2>\n";
2245     return ($synop, $desc);
2249 #############################################################################
2250 # Function    : OutputParamDescriptions
2251 # Description : Returns the DocBook output describing the parameters of a
2252 #                function, macro or signal handler.
2253 # Arguments   : $symbol_type - 'FUNCTION', 'MACRO' or 'SIGNAL'. Signal
2254 #                  handlers have an implicit user_data parameter last.
2255 #                $symbol - the name of the function/macro being described.
2256 #               @fields - parsed fields from the declaration, used to determine
2257 #                  undocumented/unused entries
2258 #############################################################################
2260 sub OutputParamDescriptions {
2261     my ($symbol_type, $symbol, @fields) = @_;
2262     my $output = "";
2263     my $params = $SymbolParams{$symbol};
2264     my $num_params = 0;
2265     my %field_descrs = ();
2267     if (@fields) {
2268         %field_descrs = @fields;
2269         delete $field_descrs{"void"};
2270         delete $field_descrs{"Returns"};
2271     }
2273     if (defined $params) {
2274         my $returns = "";
2275         my $params_desc = "";
2276         my $missing_parameters = "";
2277         my $unused_parameters = "";
2278         my $j;
2280         for ($j = 0; $j <= $#$params; $j += $PARAM_FIELD_COUNT) {
2281             my $param_name = $$params[$j];
2282             my $param_desc = $$params[$j + 1];
2283             my $param_annotations = "";
2285             ($param_desc,$param_annotations) = &ExpandAnnotation($symbol, $param_desc);
2286             $param_desc = &ConvertMarkDown($symbol, $param_desc);
2287             # trim
2288             $param_desc =~ s/^(\s|\n)+//msg;
2289             $param_desc =~ s/(\s|\n)+$//msg;
2290             if ($param_name eq "Returns") {
2291                 $returns = $param_desc;
2292                 if ($param_annotations ne "") {
2293                     $returns .= "\n<para>$param_annotations</para>";
2294                 }
2295             } elsif ($param_name eq "void") {
2296                 # FIXME: &LogWarning()?
2297                 @TRACE@("!!!! void in params for $symbol?\n");
2298             } else {
2299                 if (@fields) {
2300                     if (!defined $field_descrs{$param_name}) {
2301                         &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2302                             "Parameter description for $symbol"."::"."$param_name is not used from source code comment block.");
2303                         if ($unused_parameters ne "") {
2304                           $unused_parameters .= ", ".$param_name;
2305                         } else {
2306                            $unused_parameters = $param_name;
2307                         }
2308                     } else {
2309                         delete $field_descrs{$param_name};
2310                     }
2311                 }
2312                 if($param_desc ne "") {
2313                     $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";
2314                     $num_params++;
2315                 }
2316             }
2317         }
2318         foreach my $param_name (keys %field_descrs) {
2319             &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
2320                 "Parameter description for $symbol"."::"."$param_name is missing in source code comment block.");
2321             if ($missing_parameters ne "") {
2322               $missing_parameters .= ", ".$param_name;
2323             } else {
2324                $missing_parameters = $param_name;
2325             }
2326         }
2328         # Signals have an implicit user_data parameter which we describe.
2329         if ($symbol_type eq "SIGNAL") {
2330             $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";
2331         }
2333         # Start a table if we need one.
2334         if ($params_desc ne "") {
2335           my $id = &CreateValidSGMLID ("$symbol".".parameters");
2337           $output .= <<EOF;
2338 <refsect3 id="$id" role="parameters">\n<title>Parameters</title>
2339 <informaltable role="parameters_table" pgwide="1" frame="none">
2340 <tgroup cols="3">
2341 <colspec colname="parameters_name" colwidth="150px"/>
2342 <colspec colname="parameters_description"/>
2343 <colspec colname="parameters_annotations" colwidth="200px"/>
2344 <tbody>
2346           $output .= $params_desc;
2347           $output .= "</tbody></tgroup></informaltable>\n</refsect3>";
2348         }
2350         # Output the returns info last
2351         if ($returns ne "") {
2352           my $id = &CreateValidSGMLID ("$symbol".".returns");
2354           $output .= <<EOF;
2355 <refsect3 id="$id" role=\"returns\">\n<title>Returns</title>
2357           $output .= $returns;
2358           $output .= "\n</refsect3>";
2359         }
2361         # remember missing/unused parameters (needed in tmpl-free build)
2362         if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) {
2363             $AllIncompleteSymbols{$symbol}=$missing_parameters;
2364         }
2365         if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) {
2366             $AllUnusedSymbols{$symbol}=$unused_parameters;
2367         }
2368     }
2369     if (($num_params == 0) && @fields && (scalar(keys(%field_descrs)) > 0)) {
2370         if (! exists ($AllIncompleteSymbols{$symbol})) {
2371             $AllIncompleteSymbols{$symbol}="<parameters>";
2372         }
2373     }
2375     return $output;
2379 #############################################################################
2380 # Function    : ParseStabilityLevel
2381 # Description : Parses a stability level and outputs a warning if it isn't
2382 #               valid.
2383 # Arguments   : $stability - the stability text.
2384 #                $file, $line - context for error message
2385 #                $message - description of where the level is from, to use in
2386 #               any error message.
2387 # Returns     : The parsed stability level string.
2388 #############################################################################
2390 sub ParseStabilityLevel {
2391     my ($stability, $file, $line, $message) = @_;
2393     $stability =~ s/^\s*//;
2394     $stability =~ s/\s*$//;
2395     if ($stability =~ m/^stable$/i) {
2396         $stability = "Stable";
2397     } elsif ($stability =~ m/^unstable$/i) {
2398         $stability = "Unstable";
2399     } elsif ($stability =~ m/^private$/i) {
2400         $stability = "Private";
2401     } else {
2402         &LogWarning ($file, $line, "$message is $stability.".
2403             "It should be one of these: Stable, Unstable, or Private.");
2404     }
2405     return $stability;
2409 #############################################################################
2410 # Function    : OutputDBFile
2411 # Description : Outputs the final DocBook file for one section.
2412 # Arguments   : $file - the name of the file.
2413 #               $title - the title from the $MODULE-sections.txt file, which
2414 #                 will be overridden by the title in the template file.
2415 #               $section_id - the id to use for the toplevel tag.
2416 #               $includes - comma-separates list of include files added at top of
2417 #                 synopsis, with '<' '>' around them (if not already enclosed in "").
2418 #               $functions_synop - reference to the DocBook for the Functions Synopsis part.
2419 #               $other_synop - reference to the DocBook for the Types and Values Synopsis part.
2420 #               $functions_details - reference to the DocBook for the Functions Details part.
2421 #               $other_details - reference to the DocBook for the Types and Values Details part.
2422 #               $signal_synop - reference to the DocBook for the Signal Synopsis part
2423 #               $signal_desc - reference to the DocBook for the Signal Description part
2424 #               $args_synop - reference to the DocBook for the Arg Synopsis part
2425 #               $args_desc - reference to the DocBook for the Arg Description part
2426 #               $hierarchy - reference to the DocBook for the Object Hierarchy part
2427 #               $interfaces - reference to the DocBook for the Interfaces part
2428 #               $implementations - reference to the DocBook for the Known Implementations part
2429 #               $prerequisites - reference to the DocBook for the Prerequisites part
2430 #               $derived - reference to the DocBook for the Derived Interfaces part
2431 #               $file_objects - reference to an array of objects in this file
2432 #############################################################################
2434 sub OutputDBFile {
2435     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) = @_;
2437     @TRACE@("Output docbook for file $file with title '$title'\n");
2439     # The edited title overrides the one from the sections file.
2440     my $new_title = $SymbolDocs{"$TMPL_DIR/$file:Title"};
2441     if (defined ($new_title) && $new_title !~ m/^\s*$/) {
2442         $title = $new_title;
2443         @TRACE@("Found title: $title\n");
2444     }
2445     my $short_desc = $SymbolDocs{"$TMPL_DIR/$file:Short_Description"};
2446     if (!defined ($short_desc) || $short_desc =~ m/^\s*$/) {
2447         $short_desc = "";
2448     } else {
2449         # Don't use ConvertMarkDown here for now since we don't want blocks
2450         $short_desc = &ExpandAbbreviations("$title:Short_description",
2451                                            $short_desc);
2452         @TRACE@("Found short_desc: $short_desc");
2453     }
2454     my $long_desc = $SymbolDocs{"$TMPL_DIR/$file:Long_Description"};
2455     if (!defined ($long_desc) || $long_desc =~ m/^\s*$/) {
2456         $long_desc = "";
2457     } else {
2458         $long_desc = &ConvertMarkDown("$title:Long_description",
2459                                           $long_desc);
2460         @TRACE@("Found long_desc: $long_desc");
2461     }
2462     my $see_also = $SymbolDocs{"$TMPL_DIR/$file:See_Also"};
2463     if (!defined ($see_also) || $see_also =~ m%^\s*(<para>)?\s*(</para>)?\s*$%) {
2464         $see_also = "";
2465     } else {
2466         $see_also = &ConvertMarkDown("$title:See_Also", $see_also);
2467         @TRACE@("Found see_also: $see_also");
2468     }
2469     if ($see_also) {
2470         $see_also = "<refsect1 id=\"$section_id.see-also\">\n<title>See Also</title>\n$see_also\n</refsect1>\n";
2471     }
2472     my $stability = $SymbolDocs{"$TMPL_DIR/$file:Stability_Level"};
2473     if (!defined ($stability) || $stability =~ m/^\s*$/) {
2474         $stability = "";
2475     } else {
2476         $stability = &ParseStabilityLevel($stability, $file, $., "Section stability level");
2477         @TRACE@("Found stability: $stability");
2478     }
2479     if ($stability) {
2480         $AnnotationsUsed{$stability} = 1;
2481         $stability = "<refsect1 id=\"$section_id.stability-level\">\n<title>Stability Level</title>\n<acronym>$stability</acronym>, unless otherwise indicated\n</refsect1>\n";
2482     } elsif ($DEFAULT_STABILITY) {
2483         $AnnotationsUsed{$DEFAULT_STABILITY} = 1;
2484         $stability = "<refsect1 id=\"$section_id.stability-level\">\n<title>Stability Level</title>\n<acronym>$DEFAULT_STABILITY</acronym>, unless otherwise indicated\n</refsect1>\n";
2485     }
2487     my $image = $SymbolDocs{"$TMPL_DIR/$file:Image"};
2488     if (!defined ($image) || $image =~ m/^\s*$/) {
2489       $image = "";
2490     } else {
2491       $image =~ s/^\s*//;
2492       $image =~ s/\s*$//;
2494       my $format;
2496       if ($image =~ /jpe?g$/i) {
2497         $format = "format='JPEG'";
2498       } elsif ($image =~ /png$/i) {
2499         $format = "format='PNG'";
2500       } elsif ($image =~ /svg$/i) {
2501         $format = "format='SVG'";
2502       } else {
2503         $format = "";
2504       }
2506       $image = "  <inlinegraphic fileref='$image' $format/>\n"
2507     }
2509     my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) =
2510         gmtime (time);
2511     my $month = (qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec))[$mon];
2512     $year += 1900;
2514     my $include_output = "";
2515     if ($includes) {
2516       $include_output .= "<refsect1 id=\"$section_id.includes\"><title>Includes</title><synopsis>";
2517       my $include;
2518       foreach $include (split (/,/, $includes)) {
2519         if ($include =~ m/^\".+\"$/) {
2520           $include_output .= "#include ${include}\n";
2521         }
2522         else {
2523           $include =~ s/^\s+|\s+$//gs;
2524           $include_output .= "#include &lt;${include}&gt;\n";
2525         }
2526       }
2527       $include_output .= "</synopsis></refsect1>\n";
2528     }
2530     my $extralinks = OutputSectionExtraLinks($title,"Section:$file");
2532     my $old_db_file = "$DB_OUTPUT_DIR/$file.xml";
2533     my $new_db_file = "$DB_OUTPUT_DIR/$file.xml.new";
2535     open (OUTPUT, ">$new_db_file")
2536         || die "Can't create $new_db_file: $!";
2538     my $object_anchors = "";
2539     foreach my $object (@$file_objects) {
2540         next if ($object eq $section_id);
2541         my $id = CreateValidSGMLID($object);
2542         @TRACE@("Adding anchor for $object\n");
2543         $object_anchors .= "<anchor id=\"$id\"/>";
2544     }
2546     # Make sure we produce valid docbook
2547     $$functions_details ||= "<para />";
2549     # We used to output this, but is messes up our UpdateFileIfChanged code
2550     # since it changes every day (and it is only used in the man pages):
2551     # "<refentry id="$section_id" revision="$mday $month $year">"
2553     print OUTPUT <<EOF;
2554 ${\( MakeDocHeader ("refentry") )}
2555 <refentry id="$section_id">
2556 <refmeta>
2557 <refentrytitle role="top_of_page" id="$section_id.top_of_page">$title</refentrytitle>
2558 <manvolnum>3</manvolnum>
2559 <refmiscinfo>\U$MODULE\E Library$image</refmiscinfo>
2560 </refmeta>
2561 <refnamediv>
2562 <refname>$title</refname>
2563 <refpurpose>$short_desc</refpurpose>
2564 </refnamediv>
2565 $stability
2566 $$functions_synop$$args_synop$$signals_synop$object_anchors$$other_synop$$hierarchy$$prerequisites$$derived$$interfaces$$implementations
2567 $include_output
2568 <refsect1 id="$section_id.description" role="desc">
2569 <title role="desc.title">Description</title>
2570 $extralinks$long_desc
2571 </refsect1>
2572 <refsect1 id="$section_id.functions_details" role="details">
2573 <title role="details.title">Functions</title>
2574 $$functions_details
2575 </refsect1>
2576 <refsect1 id="$section_id.other_details" role="details">
2577 <title role="details.title">Types and Values</title>
2578 $$other_details
2579 </refsect1>
2580 $$args_desc$$signals_desc$see_also
2581 </refentry>
2583     close (OUTPUT);
2585     return &UpdateFileIfChanged ($old_db_file, $new_db_file, 0);
2588 #############################################################################
2589 # Function    : OutputProgramDBFile
2590 # Description : Outputs the final DocBook file for one program.
2591 # Arguments   : $file - the name of the file.
2592 #               $section_id - the id to use for the toplevel tag.
2593 #############################################################################
2595 sub OutputProgramDBFile {
2596     my ($program, $section_id) = @_;
2598     @TRACE@("Output program docbook for $program\n");
2600     my $short_desc = $SourceSymbolDocs{"$TMPL_DIR/$program:Short_Description"};
2601     if (!defined ($short_desc) || $short_desc =~ m/^\s*$/) {
2602         $short_desc = "";
2603     } else {
2604         # Don't use ConvertMarkDown here for now since we don't want blocks
2605         $short_desc = &ExpandAbbreviations("$program", $short_desc);
2606         @TRACE@("Found short_desc: $short_desc");
2607     }
2609     my $synopsis = $SourceSymbolDocs{"$TMPL_DIR/$program:Synopsis"};
2610     if (defined ($synopsis) && $synopsis !~ m/^\s*$/) {
2611         my $i;
2612         my @items = split(' ', $synopsis);
2613         for ($i = 0; $i <= $#items; $i++) {
2614             my $parameter = $items[$i];
2615             my $choice = "plain";
2616             my $rep = "";
2618             # first parameter is the command name
2619             if ($i == 0) {
2620                 $synopsis = "<command>$parameter</command>\n";
2621                 next;
2622             }
2624             # square brackets indicate optional parameters, curly brackets
2625             # indicate required parameters ("plain" parameters are also
2626             # mandatory, but do not get extra decoration)
2627             if ($parameter =~ s/^\[(.+?)\]$/$1/) {
2628                 $choice = "opt";
2629             } elsif ($parameter =~ s/^\{(.+?)\}$/$1/) {
2630                 $choice = "req";
2631             }
2633             # parameters ending in "..." are repeatable
2634             if ($parameter =~ s/\.\.\.$//) {
2635                 $rep = " rep=\"repeat\"";
2636             }
2638             # italic parameters are replaceable parameters
2639             if ($parameter =~ s/\*(.+?)\*/$1/) {
2640                 $parameter = "<replaceable>$parameter</replaceable>";
2641             }
2643             $synopsis .= "<arg choice=\"$choice\"$rep>";
2644             $synopsis .= $parameter;
2645             $synopsis .= "</arg>\n";
2646         }
2648         @TRACE@("Found synopsis: $synopsis");
2649     } else {
2650         $synopsis = "<command>$program</command>";
2651     }
2653     my $long_desc = $SourceSymbolDocs{"$TMPL_DIR/$program:Long_Description"};
2654     if (!defined ($long_desc) || $long_desc =~ m/^\s*$/) {
2655         $long_desc = "";
2656     } else {
2657         $long_desc = &ConvertMarkDown("$program:Long_description", $long_desc);
2658         @TRACE@("Found long_desc: $long_desc");
2659     }
2661     my $options = "";
2662     if (defined ($SourceSymbolDocs{"$TMPL_DIR/$program:Options"})) {
2663         my @opts = @{ $SourceSymbolDocs{"$TMPL_DIR/$program:Options"} };
2664         my $k;
2666         $options = "<refsect1>\n<title>Options</title>\n<variablelist>\n";
2667         for ($k = 0; $k <= $#opts; $k += 2) {
2668             my $opt_desc = $opts[$k+1];
2669             my @opt_names;
2670             my $i;
2672             $opt_desc =~ s/\*(.+?)\*/<replaceable>$1<\/replaceable>/g;
2674             $options .= "<varlistentry>\n<term>";
2675             @opt_names = split (',', $opts[$k]);
2676             for ($i = 0; $i <= $#opt_names; $i++) {
2677                 my $prefix = ($i > 0) ? ", " : "";
2678                 $opt_names[$i] =~ s/\*(.+?)\*/<replaceable>$1<\/replaceable>/g;
2680                 $options .= "$prefix<option>$opt_names[$i]</option>\n";
2681             }
2682             $options .= "</term>\n";
2683             $options .= "<listitem><para>$opt_desc</para></listitem>\n";
2684             $options .= "</varlistentry>\n";
2685         }
2686         $options .= "</variablelist></refsect1>\n";
2687     }
2689     my $exit_status = $SourceSymbolDocs{"$TMPL_DIR/$program:Returns"};
2690     if (defined ($exit_status) && $exit_status ne "") {
2691         $exit_status = &ConvertMarkDown("$program:Returns", $exit_status);
2692         $exit_status = "<refsect1 id=\"$section_id.exit-status\">\n<title>Exit Status</title>\n$exit_status\n</refsect1>\n";
2693     } else {
2694         $exit_status = "";
2695     }
2697     my $see_also = $SourceSymbolDocs{"$TMPL_DIR/$program:See_Also"};
2698     if (!defined ($see_also) || $see_also =~ m%^\s*(<para>)?\s*(</para>)?\s*$%) {
2699         $see_also = "";
2700     } else {
2701         $see_also = &ConvertMarkDown("$program:See_Also", $see_also);
2702         @TRACE@("Found see_also: $see_also");
2703     }
2704     if ($see_also) {
2705         $see_also = "<refsect1 id=\"$section_id.see-also\">\n<title>See Also</title>\n$see_also\n</refsect1>\n";
2706     }
2708     my $old_db_file = "$DB_OUTPUT_DIR/$program.xml";
2709     my $new_db_file = "$DB_OUTPUT_DIR/$program.xml.new";
2711     open (OUTPUT, ">$new_db_file")
2712         || die "Can't create $new_db_file: $!";
2714     print OUTPUT <<EOF;
2715 ${\( MakeDocHeader ("refentry") )}
2716 <refentry id="$section_id">
2717 <refmeta>
2718 <refentrytitle role="top_of_page" id="$section_id.top_of_page">$program</refentrytitle>
2719 <manvolnum>1</manvolnum>
2720 <refmiscinfo>User Commands</refmiscinfo>
2721 </refmeta>
2722 <refnamediv>
2723 <refname>$program</refname>
2724 <refpurpose>$short_desc</refpurpose>
2725 </refnamediv>
2726 <refsynopsisdiv>
2727 <cmdsynopsis>$synopsis</cmdsynopsis>
2728 </refsynopsisdiv>
2729 <refsect1 id="$section_id.description" role="desc">
2730 <title role="desc.title">Description</title>
2731 $long_desc
2732 </refsect1>
2733 $options$exit_status$see_also
2734 </refentry>
2736     close (OUTPUT);
2738     return &UpdateFileIfChanged ($old_db_file, $new_db_file, 0);
2743 #############################################################################
2744 # Function    : OutputExtraFile
2745 # Description : Copies an "extra" DocBook file into the output directory,
2746 #               expanding abbreviations
2747 # Arguments   : $file - the source file.
2748 #############################################################################
2749 sub OutputExtraFile {
2750     my ($file) = @_;
2752     my $basename;
2754     ($basename = $file) =~ s!^.*/!!;
2756     my $old_db_file = "$DB_OUTPUT_DIR/$basename";
2757     my $new_db_file = "$DB_OUTPUT_DIR/$basename.new";
2759     my $contents;
2761     open(EXTRA_FILE, "<$file") || die "Can't open $file";
2763     {
2764         local $/;
2765         $contents = <EXTRA_FILE>;
2766     }
2768     open (OUTPUT, ">$new_db_file")
2769         || die "Can't create $new_db_file: $!";
2771     print OUTPUT &ExpandAbbreviations ("$basename file", $contents);
2772     close (OUTPUT);
2774     return &UpdateFileIfChanged ($old_db_file, $new_db_file, 0);
2776 #############################################################################
2777 # Function    : OutputBook
2778 # Description : Outputs the entities that need to be included into the
2779 #                main docbook file for the module.
2780 # Arguments   : $book_top - the declarations of the entities, which are added
2781 #                  at the top of the main docbook file.
2782 #                $book_bottom - the references to the entities, which are
2783 #                  added in the main docbook file at the desired position.
2784 #############################################################################
2786 sub OutputBook {
2787     my ($book_top, $book_bottom) = @_;
2789     my $old_file = "$DB_OUTPUT_DIR/$MODULE-doc.top";
2790     my $new_file = "$DB_OUTPUT_DIR/$MODULE-doc.top.new";
2792     open (OUTPUT, ">$new_file")
2793         || die "Can't create $new_file: $!";
2794     print OUTPUT $book_top;
2795     close (OUTPUT);
2797     &UpdateFileIfChanged ($old_file, $new_file, 0);
2800     $old_file = "$DB_OUTPUT_DIR/$MODULE-doc.bottom";
2801     $new_file = "$DB_OUTPUT_DIR/$MODULE-doc.bottom.new";
2803     open (OUTPUT, ">$new_file")
2804         || die "Can't create $new_file: $!";
2805     print OUTPUT $book_bottom;
2806     close (OUTPUT);
2808     &UpdateFileIfChanged ($old_file, $new_file, 0);
2811     # If the main docbook file hasn't been created yet, we create it here.
2812     # The user can tweak it later.
2813     if ($MAIN_SGML_FILE && ! -e $MAIN_SGML_FILE) {
2814         open (OUTPUT, ">$MAIN_SGML_FILE")
2815           || die "Can't create $MAIN_SGML_FILE: $!";
2817         print OUTPUT <<EOF;
2818 ${\( MakeDocHeader ("book") )}
2819 <book id="index">
2820   <bookinfo>
2821     <title>&package_name; Reference Manual</title>
2822     <releaseinfo>
2823       for &package_string;.
2824       The latest version of this documentation can be found on-line at
2825       <ulink role="online-location" url="http://[SERVER]/&package_name;/index.html">http://[SERVER]/&package_name;/</ulink>.
2826     </releaseinfo>
2827   </bookinfo>
2829   <chapter>
2830     <title>[Insert title here]</title>
2831     $book_bottom
2832   </chapter>
2834         if (-e $OBJECT_TREE_FILE) {
2835             print OUTPUT <<EOF;
2836   <chapter id="object-tree">
2837     <title>Object Hierarchy</title>
2838     <xi:include href="xml/tree_index.sgml"/>
2839   </chapter>
2841         } else {
2842             print OUTPUT <<EOF;
2843   <!-- enable this when you use gobject types
2844   <chapter id="object-tree">
2845     <title>Object Hierarchy</title>
2846     <xi:include href="xml/tree_index.sgml"/>
2847   </chapter>
2848   -->
2850         }
2851         print OUTPUT <<EOF;
2852   <index id="api-index-full">
2853     <title>API Index</title>
2854     <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
2855   </index>
2856   <index id="deprecated-api-index" role="deprecated">
2857     <title>Index of deprecated API</title>
2858     <xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include>
2859   </index>
2861         if (keys(%AnnotationsUsed)) {
2862             print OUTPUT <<EOF;
2863   <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
2865         } else {
2866             print OUTPUT <<EOF;
2867   <!-- enable this when you use gobject introspection annotations
2868   <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
2869   -->
2871         }
2872         print OUTPUT <<EOF;
2873 </book>
2876         close (OUTPUT);
2877     }
2881 #############################################################################
2882 # Function    : CreateValidSGML
2883 # Description : This turns any chars which are used in SGML into entities,
2884 #                e.g. '<' into '&lt;'
2885 # Arguments   : $text - the text to turn into proper SGML.
2886 #############################################################################
2888 sub CreateValidSGML {
2889     my ($text) = @_;
2890     $text =~ s/&/&amp;/g;        # Do this first, or the others get messed up.
2891     $text =~ s/</&lt;/g;
2892     $text =~ s/>/&gt;/g;
2893     # browers render single tabs inconsistently
2894     $text =~ s/([^\s])\t([^\s])/$1&#160;$2/g;
2895     return $text;
2898 #############################################################################
2899 # Function    : ConvertSGMLChars
2900 # Description : This is used for text in source code comment blocks, to turn
2901 #               chars which are used in SGML into entities, e.g. '<' into
2902 #               '&lt;'. Depending on $INLINE_MARKUP_MODE, this is done
2903 #               unconditionally or only if the character doesn't seem to be
2904 #               part of an SGML construct (tag or entity reference).
2905 # Arguments   : $text - the text to turn into proper SGML.
2906 #############################################################################
2908 sub ConvertSGMLChars {
2909     my ($symbol, $text) = @_;
2911     if ($INLINE_MARKUP_MODE) {
2912         # For the XML/SGML mode only convert to entities outside CDATA sections.
2913         return &ModifyXMLElements ($text, $symbol,
2914                                    "<!\\[CDATA\\[|<programlisting[^>]*>",
2915                                    \&ConvertSGMLCharsEndTag,
2916                                    \&ConvertSGMLCharsCallback);
2917     } else {
2918         # For the simple non-sgml mode, convert to entities everywhere.
2920         # First, convert freestanding & to &amp;
2921         $text =~ s/&(?![a-zA-Z#]+;)/&amp;/g;
2922         $text =~ s/</&lt;/g;
2923         # Allow ">" at beginning of string for blockquote markdown
2924         $text =~ s/(?<=[^\w\n"'\/-])>/&gt;/g;
2926         return $text;
2927     }
2931 sub ConvertSGMLCharsEndTag {
2932   if ($_[0] eq "<!\[CDATA\[") {
2933     return "]]>";
2934   } else {
2935     return "</programlisting>";
2936   }
2939 sub ConvertSGMLCharsCallback {
2940   my ($text, $symbol, $tag) = @_;
2942   if ($tag =~ m/^<programlisting/) {
2943     # We can handle <programlisting> specially here.
2944     return &ModifyXMLElements ($text, $symbol,
2945                                "<!\\[CDATA\\[",
2946                                \&ConvertSGMLCharsEndTag,
2947                                \&ConvertSGMLCharsCallback2);
2948   } elsif ($tag eq "") {
2949     # If we're not in CDATA convert to entities.
2950     $text =~ s/&(?![a-zA-Z#]+;)/&amp;/g;        # Do this first, or the others get messed up.
2951     $text =~ s/<(?![a-zA-Z\/!])/&lt;/g;
2952     # Allow ">" at beginning of string for blockquote markdown
2953     $text =~ s/(?<=[^\w\n"'\/-])>/&gt;/g;
2955     # Handle "#include <xxxxx>"
2956     $text =~ s/#include(\s+)<([^>]+)>/#include$1&lt;$2&gt;/g;
2957   }
2959   return $text;
2962 sub ConvertSGMLCharsCallback2 {
2963   my ($text, $symbol, $tag) = @_;
2965   # If we're not in CDATA convert to entities.
2966   # We could handle <programlisting> differently, though I'm not sure it helps.
2967   if ($tag eq "") {
2968     # replace only if its not a tag
2969     $text =~ s/&(?![a-zA-Z#]+;)/&amp;/g;        # Do this first, or the others get messed up.
2970     $text =~ s/<(?![a-zA-Z\/!])/&lt;/g;
2971     $text =~ s/(?<![a-zA-Z0-9"'\/-])>/&gt;/g;
2973     # Handle "#include <xxxxx>"
2974     $text =~ s/#include(\s+)<([^>]+)>/#include$1&lt;$2&gt;/g;
2975   }
2977   return $text;
2980 #############################################################################
2981 # Function    : ExpandAnnotation
2982 # Description : This turns annotations into acronym tags.
2983 # Arguments   : $symbol - the symbol being documented, for error messages.
2984 #                $text - the text to expand.
2985 #############################################################################
2986 sub ExpandAnnotation {
2987     my ($symbol, $param_desc) = @_;
2988     my $param_annotations = "";
2990     # look for annotations at the start of the comment part
2991     # function level annotations don't end with a colon ':'
2992     if ($param_desc =~ m%^\s*\((.*?)\)(:|$)%) {
2993         my @annotations;
2994         my $annotation;
2995         $param_desc = $';
2997         @annotations = split(/\)\s*\(/,$1);
2998         @TRACE@("annotations for $symbol: '$1'\n");
2999         foreach $annotation (@annotations) {
3000             # need to search for the longest key-match in %AnnotationDefinition
3001             my $match_length=0;
3002             my $match_annotation="";
3003             my $annotationdef;
3004             foreach $annotationdef (keys %AnnotationDefinition) {
3005                 if ($annotation =~ m/^$annotationdef/) {
3006                     if (length($annotationdef)>$match_length) {
3007                         $match_length=length($annotationdef);
3008                         $match_annotation=$annotationdef;
3009                     }
3010                 }
3011             }
3012             my $annotation_extra = "";
3013             if ($match_annotation ne "") {
3014                 if ($annotation =~ m%$match_annotation\s+(.*)%) {
3015                     $annotation_extra = " $1";
3016                 }
3017                 $AnnotationsUsed{$match_annotation} = 1;
3018                 $param_annotations .= "[<acronym>$match_annotation</acronym>$annotation_extra]";
3019             }
3020             else {
3021                 &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
3022                     "unknown annotation \"$annotation\" in documentation for $symbol.");
3023                 $param_annotations .= "[$annotation]";
3024             }
3025         }
3026         chomp($param_desc);
3027         $param_desc =~ m/^(.*?)\.*\s*$/s;
3028         $param_desc = "$1. ";
3029     }
3030     if ($param_annotations ne "") {
3031         $param_annotations = "<emphasis role=\"annotation\">$param_annotations</emphasis>";
3032     }
3033     return ($param_desc, $param_annotations);
3036 #############################################################################
3037 # Function    : ExpandAbbreviations
3038 # Description : This turns the abbreviations function(), macro(), @param,
3039 #                %constant, and #symbol into appropriate DocBook markup.
3040 #               CDATA sections and <programlisting> parts are skipped.
3041 # Arguments   : $symbol - the symbol being documented, for error messages.
3042 #                $text - the text to expand.
3043 #############################################################################
3045 sub ExpandAbbreviations {
3046   my ($symbol, $text) = @_;
3048   # Note: This is a fallback and normally done in the markdown parser
3050   # Convert "|[" and "]|" into the start and end of program listing examples.
3051   # Support \[<!-- language="C" --> modifiers
3052   $text =~ s%\|\[<!-- language="([^"]+)" -->%<informalexample><programlisting language="$1"><![CDATA[%g;
3053   $text =~ s%\|\[%<informalexample><programlisting><![CDATA[%g;
3054   $text =~ s%\]\|%]]></programlisting></informalexample>%g;
3056   # keep CDATA unmodified, preserve ulink tags (ideally we preseve all tags
3057   # as such)
3058   return &ModifyXMLElements ($text, $symbol,
3059                              "<!\\[CDATA\\[|<ulink[^>]*>|<programlisting[^>]*>|<!DOCTYPE",
3060                              \&ExpandAbbreviationsEndTag,
3061                              \&ExpandAbbreviationsCallback);
3065 # Returns the end tag (as a regexp) corresponding to the given start tag.
3066 sub ExpandAbbreviationsEndTag {
3067   my ($start_tag) = @_;
3069   if ($start_tag eq "<!\[CDATA\[") {
3070     return "]]>";
3071   } elsif ($start_tag eq "<!DOCTYPE") {
3072     return ">";
3073   } elsif ($start_tag =~ m/<(\w+)/) {
3074     return "</$1>";
3075   }
3078 # Called inside or outside each CDATA or <programlisting> section.
3079 sub ExpandAbbreviationsCallback {
3080   my ($text, $symbol, $tag) = @_;
3082   if ($tag =~ m/^<programlisting/) {
3083     # Handle any embedded CDATA sections.
3084     return &ModifyXMLElements ($text, $symbol,
3085                                "<!\\[CDATA\\[",
3086                                \&ExpandAbbreviationsEndTag,
3087                                \&ExpandAbbreviationsCallback2);
3088   } elsif ($tag eq "") {
3089     # NOTE: this is a fallback. It is normally done by the Markdown parser.
3091     # We are outside any CDATA or <programlisting> sections, so we expand
3092     # any gtk-doc abbreviations.
3094     # Convert '@param()'
3095     # FIXME: we could make those also links ($symbol.$2), but that would be less
3096     # useful as the link target is a few lines up or down
3097     $text =~ s/(\A|[^\\])\@(\w+((\.|->)\w+)*)\s*\(\)/$1<parameter>$2()<\/parameter>/g;
3099     # Convert 'function()' or 'macro()'.
3100     # if there is abc_*_def() we don't want to make a link to _def()
3101     # FIXME: also handle abc(def(....)) : but that would need to be done recursively :/
3102     $text =~ s/([^\*.\w])(\w+)\s*\(\)/$1.&MakeXRef($2, &tagify($2 . "()", "function"));/eg;
3103     # handle #Object.func()
3104     $text =~ s/(\A|[^\\])#([\w\-:\.]+[\w]+)\s*\(\)/$1.&MakeXRef($2, &tagify($2 . "()", "function"));/eg;
3106     # Convert '@param', but not '\@param'.
3107     $text =~ s/(\A|[^\\])\@(\w+((\.|->)\w+)*)/$1<parameter>$2<\/parameter>/g;
3108     $text =~ s/\\\@/\@/g;
3110     # Convert '%constant', but not '\%constant'.
3111     # Also allow negative numbers, e.g. %-1.
3112     $text =~ s/(\A|[^\\])\%(-?\w+)/$1.&MakeXRef($2, &tagify($2, "literal"));/eg;
3113     $text =~ s/\\\%/\%/g;
3115     # Convert '#symbol', but not '\#symbol'.
3116     $text =~ s/(\A|[^\\])#([\w\-:\.]+[\w]+)/$1.&MakeHashXRef($2, "type");/eg;
3117     $text =~ s/\\#/#/g;
3118   }
3120   return $text;
3123 # This is called inside a <programlisting>
3124 sub ExpandAbbreviationsCallback2 {
3125   my ($text, $symbol, $tag) = @_;
3127   if ($tag eq "") {
3128     # We are inside a <programlisting> but outside any CDATA sections,
3129     # so we expand any gtk-doc abbreviations.
3130     # FIXME: why is this different from &ExpandAbbreviationsCallback(),
3131     #        why not just call it
3132     $text =~ s/#(\w+)/&MakeHashXRef($1, "");/eg;
3133   } elsif ($tag eq "<![CDATA[") {
3134     # NOTE: this is a fallback. It is normally done by the Markdown parser.
3135     $text = &ReplaceEntities ($text, $symbol);
3136   }
3138   return $text;
3141 sub MakeHashXRef {
3142     my ($symbol, $tag) = @_;;
3143     my $text = $symbol;
3145     # Check for things like '#include', '#define', and skip them.
3146     if ($PreProcessorDirectives{$symbol}) {
3147       return "#$symbol";
3148     }
3150     # Get rid of special suffixes ('-struct','-enum').
3151     $text =~ s/-struct$//;
3152     $text =~ s/-enum$//;
3154     # If the symbol is in the form "Object::signal", then change the symbol to
3155     # "Object-signal" and use "signal" as the text.
3156     if ($symbol =~ s/::/-/) {
3157       $text = "“$'”";
3158     }
3160     # If the symbol is in the form "Object:property", then change the symbol to
3161     # "Object--property" and use "property" as the text.
3162     if ($symbol =~ s/:/--/) {
3163       $text = "“$'”";
3164     }
3166     if ($tag ne "") {
3167       $text = tagify ($text, $tag);
3168     }
3170     return &MakeXRef($symbol, $text);
3174 #############################################################################
3175 # Function    : ModifyXMLElements
3176 # Description : Looks for given XML element tags within the text, and calls
3177 #               the callback on pieces of text inside & outside those elements.
3178 #               Used for special handling of text inside things like CDATA
3179 #               and <programlisting>.
3180 # Arguments   : $text - the text.
3181 #               $symbol - the symbol currently being documented (only used for
3182 #                      error messages).
3183 #               $start_tag_regexp - the regular expression to match start tags.
3184 #                      e.g. "<!\\[CDATA\\[|<programlisting[^>]*>" to match
3185 #                      CDATA sections or programlisting elements.
3186 #               $end_tag_func - function which is passed the matched start tag
3187 #                      and should return the appropriate end tag string regexp.
3188 #               $callback - callback called with each part of the text. It is
3189 #                      called with a piece of text, the symbol being
3190 #                      documented, and the matched start tag or "" if the text
3191 #                      is outside the XML elements being matched.
3192 #############################################################################
3193 sub ModifyXMLElements {
3194     my ($text, $symbol, $start_tag_regexp, $end_tag_func, $callback) = @_;
3195     my ($before_tag, $start_tag, $end_tag_regexp, $end_tag);
3196     my $result = "";
3198     while ($text =~ m/$start_tag_regexp/s) {
3199       $before_tag = $`; # Prematch for last successful match string
3200       $start_tag = $&;  # Last successful match
3201       $text = $';       # Postmatch for last successful match string
3203       $result .= &$callback ($before_tag, $symbol, "");
3204       $result .= $start_tag;
3206       # get the matching end-tag for current tag
3207       $end_tag_regexp = &$end_tag_func ($start_tag);
3209       if ($text =~ m/$end_tag_regexp/s) {
3210         $before_tag = $`;
3211         $end_tag = $&;
3212         $text = $';
3214         $result .= &$callback ($before_tag, $symbol, $start_tag);
3215         $result .= $end_tag;
3216       } else {
3217         &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
3218             "Can't find tag end: $end_tag_regexp in docs for: $symbol.");
3219         # Just assume it is all inside the tag.
3220         $result .= &$callback ($text, $symbol, $start_tag);
3221         $text = "";
3222       }
3223     }
3225     # Handle any remaining text outside the tags.
3226     $result .= &$callback ($text, $symbol, "");
3228     return $result;
3231 sub noop {
3232   return $_[0];
3235 # Adds a tag around some text.
3236 # e.g tagify("Text", "literal") => "<literal>Text</literal>".
3237 sub tagify {
3238    my ($text, $elem) = @_;
3239    return "<" . $elem . ">" . $text . "</" . $elem . ">";
3242 #############################################################################
3243 # Function    : MakeDocHeader
3244 # Description : Builds a docbook header for the given tag
3245 # Arguments   : $tag - doctype tag
3246 #############################################################################
3248 sub MakeDocHeader {
3249     my ($tag) = @_;
3250     my $header = $doctype_header;
3251     $header =~ s/<!DOCTYPE \w+/<!DOCTYPE $tag/;
3253     # fix the path for book since this is one level up
3254     if ($tag eq "book") {
3255         $header =~ s#<!ENTITY % gtkdocentities SYSTEM \"../([a-zA-Z./]+)\">#<!ENTITY % gtkdocentities SYSTEM \"$1\">#;
3256     }
3258     return $header;
3262 #############################################################################
3263 # Function    : MakeXRef
3264 # Description : This returns a cross-reference link to the given symbol.
3265 #                Though it doesn't try to do this for a few standard C types
3266 #                that it        knows won't be in the documentation.
3267 # Arguments   : $symbol - the symbol to try to create a XRef to.
3268 #               $text - text text to put inside the XRef, defaults to $symbol
3269 #############################################################################
3271 sub MakeXRef {
3272     my ($symbol, $text) = ($_[0], $_[1]);
3274     $symbol =~ s/^\s+//;
3275     $symbol =~ s/\s+$//;
3277     if (!defined($text)) {
3278         $text = $symbol;
3280         # Get rid of special suffixes ('-struct','-enum').
3281         $text =~ s/-struct$//;
3282         $text =~ s/-enum$//;
3283     }
3285     if ($symbol =~ m/ /) {
3286         return "$text";
3287     }
3289     @TRACE@("Getting type link for $symbol -> $text\n");
3291     my $symbol_id = &CreateValidSGMLID ($symbol);
3292     return "<link linkend=\"$symbol_id\">$text</link>";
3296 #############################################################################
3297 # Function    : MakeIndexterms
3298 # Description : This returns a indexterm elements for the given symbol
3299 # Arguments   : $symbol - the symbol to create indexterms for
3300 #############################################################################
3302 sub MakeIndexterms {
3303   my ($symbol, $id) = @_;
3304   my $terms =  "";
3305   my $sortas = "";
3307   # make the index useful, by ommiting the namespace when sorting
3308   if ($NAME_SPACE ne "") {
3309     if ($symbol =~ m/^$NAME_SPACE\_?(.*)/i) {
3310        $sortas=" sortas=\"$1\"";
3311     }
3312   }
3314   if (exists $Deprecated{$symbol}) {
3315       $terms .= "<indexterm zone=\"$id\" role=\"deprecated\"><primary$sortas>$symbol</primary></indexterm>";
3316       $IndexEntriesDeprecated{$symbol}=$id;
3317       $IndexEntriesFull{$symbol}=$id;
3318   }
3319   if (exists $Since{$symbol}) {
3320      my $since = $Since{$symbol};
3321      $since =~ s/^\s+//;
3322      $since =~ s/\s+$//;
3323      if ($since ne "") {
3324          $terms .= "<indexterm zone=\"$id\" role=\"$since\"><primary$sortas>$symbol</primary></indexterm>";
3325      }
3326      $IndexEntriesSince{$symbol}=$id;
3327      $IndexEntriesFull{$symbol}=$id;
3328   }
3329   if ($terms eq "") {
3330      $terms .= "<indexterm zone=\"$id\"><primary$sortas>$symbol</primary></indexterm>";
3331      $IndexEntriesFull{$symbol}=$id;
3332   }
3334   return $terms;
3337 #############################################################################
3338 # Function    : MakeDeprecationNote
3339 # Description : This returns a deprecation warning for the given symbol.
3340 # Arguments   : $symbol - the symbol to try to create a warning for.
3341 #############################################################################
3343 sub MakeDeprecationNote {
3344     my ($symbol) = $_[0];
3345     my $desc = "";
3346     if (exists $Deprecated{$symbol}) {
3347         my $note;
3349         $desc .= "<warning><para><literal>$symbol</literal> ";
3351         $note = $Deprecated{$symbol};
3353         if ($note =~ /^\s*([0-9\.]+)\s*:?/) {
3354                 $desc .= "has been deprecated since version $1 and should not be used in newly-written code.</para>";
3355         } else {
3356                 $desc .= "is deprecated and should not be used in newly-written code.</para>";
3357         }
3358         $note =~ s/^\s*([0-9\.]+)\s*:?\s*//;
3359         $note =~ s/^\s+//;
3360         $note =~ s/\s+$//;
3361         if ($note ne "") {
3362             $note = &ConvertMarkDown($symbol, $note);
3363             $desc .= " " . $note;
3364         }
3365         $desc .= "</warning>\n";
3366     }
3367     return $desc;
3370 #############################################################################
3371 # Function    : MakeConditionDescription
3372 # Description : This returns a sumary of conditions for the given symbol.
3373 # Arguments   : $symbol - the symbol to try to create the sumary.
3374 #############################################################################
3376 sub MakeConditionDescription {
3377     my ($symbol) = $_[0];
3378     my $desc = "";
3380     if (exists $Deprecated{$symbol}) {
3381         if ($desc ne "") {
3382             $desc .= "|";
3383         }
3385         if ($Deprecated{$symbol} =~ /^\s*(.*?)\s*$/) {
3386                 $desc .= "deprecated:$1";
3387         } else {
3388                 $desc .= "deprecated";
3389         }
3390     }
3392     if (exists $Since{$symbol}) {
3393         if ($desc ne "") {
3394             $desc .= "|";
3395         }
3397         if ($Since{$symbol} =~ /^\s*(.*?)\s*$/) {
3398                 $desc .= "since:$1";
3399         } else {
3400                 $desc .= "since";
3401         }
3402     }
3404     if (exists $StabilityLevel{$symbol}) {
3405         if ($desc ne "") {
3406             $desc .= "|";
3407         }
3408         $desc .= "stability:".$StabilityLevel{$symbol};
3409     }
3411     if ($desc ne "") {
3412         my $cond = $desc;
3413         $cond =~ s/\"/&quot;/g;
3414         $desc=" condition=\"".$cond."\"";
3415         @TRACE@("condition for '$symbol' = '$desc'\n");
3416     }
3417     return $desc;
3420 #############################################################################
3421 # Function    : GetHierarchy
3422 # Description : Returns the DocBook output describing the ancestors and
3423 #               immediate children of a GObject subclass. It uses the
3424 #               global @Objects and @ObjectLevels arrays to walk the tree.
3426 # Arguments   : $object - the GtkObject subclass.
3427 #               @hierarchy - previous hierarchy
3428 #############################################################################
3430 sub GetHierarchy {
3431     my ($object,$hierarchy_ref) = @_;
3432     my @hierarchy = @{$hierarchy_ref};
3434     # Find object in the objects array.
3435     my $found = 0;
3436     my @children = ();
3437     my $i;
3438     my $level;
3439     my $j;
3440     for ($i = 0; $i < @Objects; $i++) {
3441         if ($found) {
3442             if ($ObjectLevels[$i] <= $level) {
3443             last;
3444         }
3445             elsif ($ObjectLevels[$i] == $level + 1) {
3446                 push (@children, $Objects[$i]);
3447             }
3448         }
3449         elsif ($Objects[$i] eq $object) {
3450             $found = 1;
3451             $j = $i;
3452             $level = $ObjectLevels[$i];
3453         }
3454     }
3455     if (!$found) {
3456         return @hierarchy;
3457     }
3459     # Walk up the hierarchy, pushing ancestors onto the ancestors array.
3460     my @ancestors = ();
3461     push (@ancestors, $object);
3462     @TRACE@("Level: $level\n");
3463     while ($level > 1) {
3464         $j--;
3465         if ($ObjectLevels[$j] < $level) {
3466             push (@ancestors, $Objects[$j]);
3467             $level = $ObjectLevels[$j];
3468             @TRACE@("Level: $level\n");
3469         }
3470     }
3472     # Output the ancestors, indented and with links.
3473     my $last_index = 0;
3474     $level = 1;
3475     for ($i = $#ancestors; $i >= 0; $i--) {
3476         my $entry_text;
3477         my $alt_text;
3478         my $ancestor = $ancestors[$i];
3479         my $ancestor_id = &CreateValidSGMLID ($ancestor);
3480         my $indent = ' ' x ($level * 4);
3481         # Don't add a link to the current object, i.e. when i == 0.
3482         if ($i > 0) {
3483             $entry_text = $indent . "<link linkend=\"$ancestor_id\">$ancestor</link>";
3484             $alt_text = $indent . $ancestor;
3485         } else {
3486             $entry_text = $indent . $ancestor;
3487             $alt_text = $indent . "<link linkend=\"$ancestor_id\">$ancestor</link>";
3488         }
3489         @TRACE@("Checking for '$entry_text' or '$alt_text'");
3490         # Check if we already have this object
3491         my $index = -1;
3492         for ($j = 0; $j <= $#hierarchy; $j++) {
3493             if (($hierarchy[$j] eq $entry_text) or ($hierarchy[$j] eq $alt_text)) {
3494                 $index = $j;
3495                 last;
3496             }
3497         }
3498         if ($index == -1) {
3499             # We have a new entry, find insert position in alphabetical order
3500             my $found = 0;
3501             for ($j = $last_index; $j <= $#hierarchy; $j++) {
3502                 if ($hierarchy[$j] !~ m/^${indent}/) {
3503                     $last_index = $j;
3504                     $found = 1;
3505                     last;
3506                 } elsif ($hierarchy[$j] =~ m/^${indent}[^ ]/) {
3507                     my $stripped_text = $hierarchy[$j];
3508                     if ($entry_text !~ m/<link linkend/) {
3509                         $stripped_text =~ s%<link linkend="[A-Za-z]*">%%;
3510                         $stripped_text =~ s%</link>%%;
3511                     }
3512                     if ($entry_text lt $stripped_text) {
3513                         $last_index = $j;
3514                         $found = 1;
3515                         last;
3516                     }
3517                 }
3518             }
3519             # Append to bottom
3520             if (!$found) {
3521               $last_index = 1 + $#hierarchy;
3522             }
3523             splice @hierarchy, $last_index, 0, ($entry_text);
3524             $last_index++;
3525         } else {
3526             # Already have this one, make sure we use the not linked version
3527             if ($entry_text !~ m/<link linkend=/) {
3528               $hierarchy[$j] = $entry_text;
3529             }
3530             # Remember index as base insert point
3531             $last_index = $index + 1;
3532         }
3533         $level++;
3534     }
3535     # Output the children, indented and with links.
3536     for ($i = 0; $i <= $#children; $i++) {
3537         my $id = &CreateValidSGMLID ($children[$i]);
3538         my $indented_text = ' ' x ($level * 4) . "<link linkend=\"$id\">$children[$i]</link>";
3539         splice @hierarchy, $last_index, 0, ($indented_text);
3540         $last_index++;
3541     }
3543     return @hierarchy;
3546 #############################################################################
3547 # Function    : GetInterfaces
3548 # Description : Returns the DocBook output describing the interfaces
3549 #               implemented by a class. It uses the global %Interfaces hash.
3550 # Arguments   : $object - the GtkObject subclass.
3551 #############################################################################
3553 sub GetInterfaces {
3554     my ($object) = @_;
3555     my $text = "";
3556     my $i;
3558     # Find object in the objects array.
3559     if (exists($Interfaces{$object})) {
3560         my @ifaces = split(' ', $Interfaces{$object});
3561         $text = <<EOF;
3562 <para>
3563 $object implements
3565         for ($i = 0; $i <= $#ifaces; $i++) {
3566             my $id = &CreateValidSGMLID ($ifaces[$i]);
3567             $text .= " <link linkend=\"$id\">$ifaces[$i]</link>";
3568             if ($i < $#ifaces - 1) {
3569                 $text .= ', ';
3570             }
3571             elsif ($i < $#ifaces) {
3572                 $text .= ' and ';
3573             }
3574             else {
3575                 $text .= '.';
3576             }
3577         }
3578         $text .= <<EOF;
3579 </para>
3581     }
3583     return $text;
3586 #############################################################################
3587 # Function    : GetImplementations
3588 # Description : Returns the DocBook output describing the implementations
3589 #               of an interface. It uses the global %Interfaces hash.
3590 # Arguments   : $object - the GtkObject subclass.
3591 #############################################################################
3593 sub GetImplementations {
3594     my ($object) = @_;
3595     my @impls = ();
3596     my $text = "";
3597     my $i;
3598     foreach my $key (keys %Interfaces) {
3599         if ($Interfaces{$key} =~ /\b$object\b/) {
3600             push (@impls, $key);
3601         }
3602     }
3603     if ($#impls >= 0) {
3604         @impls = sort @impls;
3605         $text = <<EOF;
3606 <para>
3607 $object is implemented by
3609         for ($i = 0; $i <= $#impls; $i++) {
3610             my $id = &CreateValidSGMLID ($impls[$i]);
3611             $text .= " <link linkend=\"$id\">$impls[$i]</link>";
3612             if ($i < $#impls - 1) {
3613                 $text .= ', ';
3614             }
3615             elsif ($i < $#impls) {
3616                 $text .= ' and ';
3617             }
3618             else {
3619                 $text .= '.';
3620             }
3621         }
3622         $text .= <<EOF;
3623 </para>
3625     }
3626     return $text;
3630 #############################################################################
3631 # Function    : GetPrerequisites
3632 # Description : Returns the DocBook output describing the prerequisites
3633 #               of an interface. It uses the global %Prerequisites hash.
3634 # Arguments   : $iface - the interface.
3635 #############################################################################
3637 sub GetPrerequisites {
3638     my ($iface) = @_;
3639     my $text = "";
3640     my $i;
3642     if (exists($Prerequisites{$iface})) {
3643         $text = <<EOF;
3644 <para>
3645 $iface requires
3647         my @prereqs = split(' ', $Prerequisites{$iface});
3648         for ($i = 0; $i <= $#prereqs; $i++) {
3649             my $id = &CreateValidSGMLID ($prereqs[$i]);
3650             $text .= " <link linkend=\"$id\">$prereqs[$i]</link>";
3651             if ($i < $#prereqs - 1) {
3652                 $text .= ', ';
3653             }
3654             elsif ($i < $#prereqs) {
3655                 $text .= ' and ';
3656             }
3657             else {
3658                 $text .= '.';
3659             }
3660         }
3661         $text .= <<EOF;
3662 </para>
3664     }
3665     return $text;
3668 #############################################################################
3669 # Function    : GetDerived
3670 # Description : Returns the DocBook output describing the derived interfaces
3671 #               of an interface. It uses the global %Prerequisites hash.
3672 # Arguments   : $iface - the interface.
3673 #############################################################################
3675 sub GetDerived {
3676     my ($iface) = @_;
3677     my $text = "";
3678     my $i;
3680     my @derived = ();
3681     foreach my $key (keys %Prerequisites) {
3682         if ($Prerequisites{$key} =~ /\b$iface\b/) {
3683             push (@derived, $key);
3684         }
3685     }
3686     if ($#derived >= 0) {
3687         @derived = sort @derived;
3688         $text = <<EOF;
3689 <para>
3690 $iface is required by
3692         for ($i = 0; $i <= $#derived; $i++) {
3693             my $id = &CreateValidSGMLID ($derived[$i]);
3694             $text .= " <link linkend=\"$id\">$derived[$i]</link>";
3695             if ($i < $#derived - 1) {
3696                 $text .= ', ';
3697             }
3698             elsif ($i < $#derived) {
3699                 $text .= ' and ';
3700             }
3701             else {
3702                 $text .= '.';
3703             }
3704         }
3705         $text .= <<EOF;
3706 </para>
3708     }
3709     return $text;
3713 #############################################################################
3714 # Function    : GetSignals
3715 # Description : Returns the synopsis and detailed description DocBook output
3716 #                for the signal handlers of a given GtkObject subclass.
3717 # Arguments   : $object - the GtkObject subclass, e.g. 'GtkButton'.
3718 #############################################################################
3720 sub GetSignals {
3721     my ($object) = @_;
3722     my $synop = "";
3723     my $desc = "";
3725     my $i;
3726     for ($i = 0; $i <= $#SignalObjects; $i++) {
3727         if ($SignalObjects[$i] eq $object) {
3728             @TRACE@("Found signal: $SignalNames[$i]\n");
3729             my $name = $SignalNames[$i];
3730             my $symbol = "${object}::${name}";
3731             my $id = &CreateValidSGMLID ("$object-$name");
3733             $desc .= "<refsect2 id=\"$id\" role=\"signal\"><title>The <literal>“$name”</literal> signal</title>\n";
3734             $desc .= MakeIndexterms($symbol, $id);
3735             $desc .= "\n";
3736             $desc .= OutputSymbolExtraLinks($symbol);
3738             $desc .= "<programlisting language=\"C\">";
3740             $SignalReturns[$i] =~ m/\s*(const\s+)?(\w+)\s*(\**)/;
3741             my $type_modifier = defined($1) ? $1 : "";
3742             my $type = $2;
3743             my $pointer = $3;
3744             my $xref = &MakeXRef ($type, &tagify($type, "returnvalue"));
3746             my $ret_type_output = "$type_modifier$xref$pointer";
3747             my $callback_name = "user_function";
3748             $desc  .= "${ret_type_output}\n${callback_name} (";
3750             my $indentation = ' ' x (length($callback_name) + 2);
3751             my $pad = $indentation;
3753             my $sourceparams = $SourceSymbolParams{$symbol};
3754             my @params = split ("\n", $SignalPrototypes[$i]);
3755             my $j;
3756             my $l;
3757             my $type_len = length("gpointer");
3758             my $name_len = length("user_data");
3759             # do two passes, the first one is to calculate padding
3760             for ($l = 0; $l < 2; $l++) {
3761                 for ($j = 0; $j <= $#params; $j++) {
3762                     my $param_name;
3763                     # allow alphanumerics, '_', '[' & ']' in param names
3764                     if ($params[$j] =~ m/^\s*(\w+)\s*(\**)\s*([\w\[\]]+)\s*$/) {
3765                         $type = $1;
3766                         $pointer = $2;
3767                         if (defined($sourceparams)) {
3768                             $param_name = $$sourceparams[$PARAM_FIELD_COUNT * $j];
3769                         }
3770                         else {
3771                             $param_name = $3;
3772                         }
3773                         if (!defined($param_name)) {
3774                             $param_name = "arg$j";
3775                         }
3776                         if ($l == 0) {
3777                             if (length($type) + length($pointer) > $type_len) {
3778                                 $type_len = length($type) + length($pointer);
3779                             }
3780                             if (length($param_name) > $name_len) {
3781                                 $name_len = length($param_name);
3782                             }
3783                         }
3784                         else {
3785                             $xref = &MakeXRef ($type, &tagify($type, "type"));
3786                             $pad = ' ' x ($type_len - length($type) - length($pointer));
3787                             $desc .= "$xref$pad $pointer${param_name},\n";
3788                             $desc .= $indentation;
3789                         }
3790                     } else {
3791                         &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
3792                              "Can't parse arg: $params[$j]\nArgs:$SignalPrototypes[$i]");
3793                     }
3794                 }
3795             }
3796             $xref = &MakeXRef ("gpointer", &tagify("gpointer", "type"));
3797             $pad = ' ' x ($type_len - length("gpointer"));
3798             $desc  .= "$xref$pad user_data)";
3799             $desc  .= "</programlisting>\n";
3801             my $flags = $SignalFlags[$i];
3802             my $flags_string = "";
3804             if (defined ($flags)) {
3805               if ($flags =~ m/f/) {
3806                 $flags_string = "<link linkend=\"G-SIGNAL-RUN-FIRST:CAPS\">Run First</link>";
3807               }
3808               elsif ($flags =~ m/l/) {
3809                 $flags_string = "<link linkend=\"G-SIGNAL-RUN-LAST:CAPS\">Run Last</link>";
3810               }
3811               elsif ($flags =~ m/c/) {
3812                 $flags_string = "<link linkend=\"G-SIGNAL-RUN-CLEANUP:CAPS\">Cleanup</link>";
3813                 $flags_string = "Cleanup";
3814               }
3815               if ($flags =~ m/r/) {
3816                 if ($flags_string) { $flags_string .= " / "; }
3817                 $flags_string = "<link linkend=\"G-SIGNAL-NO-RECURSE:CAPS\">No Recursion</link>";
3818               }
3819               if ($flags =~ m/d/) {
3820                 if ($flags_string) { $flags_string .= " / "; }
3821                 $flags_string = "<link linkend=\"G-SIGNAL-DETAILED:CAPS\">Has Details</link>";
3822               }
3823               if ($flags =~ m/a/) {
3824                 if ($flags_string) { $flags_string .= " / "; }
3825                 $flags_string = "<link linkend=\"G-SIGNAL-ACTION:CAPS\">Action</link>";
3826               }
3827               if ($flags =~ m/h/) {
3828                 if ($flags_string) { $flags_string .= " / "; }
3829                 $flags_string = "<link linkend=\"G-SIGNAL-NO-HOOKS:CAPS\">No Hooks</link>";
3830               }
3831             }
3833             $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";
3835             my $parameters = &OutputParamDescriptions ("SIGNAL", $symbol);
3837             $AllSymbols{$symbol} = 1;
3838             if (defined ($SymbolDocs{$symbol})) {
3839                 my $symbol_docs = &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
3841                 $desc .= $symbol_docs;
3843                 if (!IsEmptyDoc($SymbolDocs{$symbol})) {
3844                     $AllDocumentedSymbols{$symbol} = 1;
3845                 }
3846             }
3847             if (defined ($SymbolAnnotations{$symbol})) {
3848                 my $param_desc = $SymbolAnnotations{$symbol};
3849                 my $param_annotations = "";
3850                 ($param_desc,$param_annotations) = &ExpandAnnotation($symbol, $param_desc);
3851                 if ($param_annotations ne "") {
3852                     $desc .= "\n<para>$param_annotations</para>";
3853                 }
3854             }
3855             $desc .= &MakeDeprecationNote($symbol);
3857             $desc .= $parameters;
3858             if ($flags_string) {
3859                 $desc  .= "<para>Flags: $flags_string</para>\n";
3860             }
3861             $desc .= OutputSymbolTraits ($symbol);
3862             $desc .= "</refsect2>";
3863         }
3864     }
3865     return ($synop, $desc);
3869 #############################################################################
3870 # Function    : GetArgs
3871 # Description : Returns the synopsis and detailed description DocBook output
3872 #                for the Args of a given GtkObject subclass.
3873 # Arguments   : $object - the GtkObject subclass, e.g. 'GtkButton'.
3874 #############################################################################
3876 sub GetArgs {
3877     my ($object) = @_;
3878     my $synop = "";
3879     my $desc = "";
3880     my $child_synop = "";
3881     my $child_desc = "";
3882     my $style_synop = "";
3883     my $style_desc = "";
3885     my $i;
3886     for ($i = 0; $i <= $#ArgObjects; $i++) {
3887         if ($ArgObjects[$i] eq $object) {
3888             @TRACE@("Found arg: $ArgNames[$i]\n");
3889             my $name = $ArgNames[$i];
3890             my $flags = $ArgFlags[$i];
3891             my $flags_string = "";
3892             my $kind = "";
3893             my $id_sep = "";
3895             if ($flags =~ m/c/) {
3896                 $kind = "child property";
3897                 $id_sep = "c-";
3898             }
3899             elsif ($flags =~ m/s/) {
3900                 $kind = "style property";
3901                 $id_sep = "s-";
3902             }
3903             else {
3904                 $kind = "property";
3905             }
3907             # Remember only one colon so we don't clash with signals.
3908             my $symbol = "${object}:${name}";
3909             # use two dashes and ev. an extra separator here for the same reason.
3910             my $id = &CreateValidSGMLID ("$object--$id_sep$name");
3912             my $type = $ArgTypes[$i];
3913             my $type_output;
3914             my $range = $ArgRanges[$i];
3915             my $range_output = CreateValidSGML ($range);
3916             my $default = $ArgDefaults[$i];
3917             my $default_output = CreateValidSGML ($default);
3919             if ($type eq "GtkString") {
3920                 $type = "char&#160;*";
3921             }
3922             if ($type eq "GtkSignal") {
3923                 $type = "GtkSignalFunc, gpointer";
3924                 $type_output = &MakeXRef ("GtkSignalFunc") . ", "
3925                     . &MakeXRef ("gpointer");
3926             } elsif ($type =~ m/^(\w+)\*$/) {
3927                 $type_output = &MakeXRef ($1, &tagify($1, "type")) . "&#160;*";
3928             } else {
3929                 $type_output = &MakeXRef ($type, &tagify($type, "type"));
3930             }
3932             if ($flags =~ m/r/) {
3933                 $flags_string = "Read";
3934             }
3935             if ($flags =~ m/w/) {
3936                 if ($flags_string) { $flags_string .= " / "; }
3937                 $flags_string .= "Write";
3938             }
3939             if ($flags =~ m/x/) {
3940                 if ($flags_string) { $flags_string .= " / "; }
3941                 $flags_string .= "Construct";
3942             }
3943             if ($flags =~ m/X/) {
3944                 if ($flags_string) { $flags_string .= " / "; }
3945                 $flags_string .= "Construct Only";
3946             }
3948             $AllSymbols{$symbol} = 1;
3949             my $blurb = "";
3950             if (defined($SymbolDocs{$symbol}) &&
3951                 !IsEmptyDoc($SymbolDocs{$symbol})) {
3952                 $blurb = &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
3953                 @TRACE@(".. [$SymbolDocs{$symbol}][$blurb]\n");
3954                 $AllDocumentedSymbols{$symbol} = 1;
3955             }
3956             else {
3957                 if ($ArgBlurbs[$i] ne "") {
3958                     $blurb = "<para>" . &CreateValidSGML ($ArgBlurbs[$i]) . "</para>";
3959                     $AllDocumentedSymbols{$symbol} = 1;
3960                 } else {
3961                     # FIXME: print a warning?
3962                     @TRACE@(".. no description\n");
3963                 }
3964             }
3966             my $pad1 = "";
3967             if (length ($name) < 24) {
3968               $pad1 = " " x (24 - length ($name));
3969             }
3971             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";
3972             my $arg_desc = "<refsect2 id=\"$id\" role=\"property\"><title>The <literal>“$name”</literal> $kind</title>\n";
3973             $arg_desc .= MakeIndexterms($symbol, $id);
3974             $arg_desc .= "\n";
3975             $arg_desc .= OutputSymbolExtraLinks($symbol);
3977             $arg_desc .= "<programlisting>  “$name”$pad1 $type_output</programlisting>\n";
3978             $arg_desc .= $blurb;
3979             if (defined ($SymbolAnnotations{$symbol})) {
3980                 my $param_desc = $SymbolAnnotations{$symbol};
3981                 my $param_annotations = "";
3982                 ($param_desc,$param_annotations) = &ExpandAnnotation($symbol, $param_desc);
3983                 if ($param_annotations ne "") {
3984                     $arg_desc .= "\n<para>$param_annotations</para>";
3985                 }
3986             }
3987             $arg_desc .= &MakeDeprecationNote($symbol);
3989             if ($flags_string) {
3990               $arg_desc  .= "<para>Flags: $flags_string</para>\n";
3991             }
3992             if ($range ne "") {
3993                 $arg_desc .= "<para>Allowed values: $range_output</para>\n";
3994             }
3995             if ($default ne "") {
3996                 $arg_desc .= "<para>Default value: $default_output</para>\n";
3997             }
3998             $arg_desc .= OutputSymbolTraits ($symbol);
3999             $arg_desc .= "</refsect2>\n";
4001             if ($flags =~ m/c/) {
4002                 $child_synop .= $arg_synop;
4003                 $child_desc .= $arg_desc;
4004             }
4005             elsif ($flags =~ m/s/) {
4006                 $style_synop .= $arg_synop;
4007                 $style_desc .= $arg_desc;
4008             }
4009             else {
4010                 $synop .= $arg_synop;
4011                 $desc .= $arg_desc;
4012             }
4013         }
4014     }
4015     return ($synop, $child_synop, $style_synop, $desc, $child_desc, $style_desc);
4019 #############################################################################
4020 # Function    : ReadSourceDocumentation
4021 # Description : This reads in the documentation embedded in comment blocks
4022 #                in the source code (for Gnome).
4024 #                Parameter descriptions override any in the template files.
4025 #                Function descriptions are placed before any description from
4026 #                the template files.
4028 #                It recursively descends the source directory looking for .c
4029 #                files and scans them looking for specially-formatted comment
4030 #                blocks.
4032 # Arguments   : $source_dir - the directory to scan.
4033 #############m###############################################################
4035 sub ReadSourceDocumentation {
4036     my ($source_dir) = @_;
4037     my ($file, $dir, @suffix_list, $suffix);
4039     # prepend entries from @SOURCE_DIR
4040     for my $dir (@SOURCE_DIRS) {
4041         # Check if the filename is in the ignore list.
4042         if ($source_dir =~ m%^\Q$dir\E/(.*)$% and $IGNORE_FILES =~ m/(\s|^)\Q$1\E(\s|$)/) {
4043             @TRACE@("Skipping source directory: $source_dir");
4044             return;
4045         } else {
4046             @TRACE@("No match for: ".($1 || $source_dir));
4047         }
4048     }
4050     @TRACE@("Scanning source directory: $source_dir");
4052     # This array holds any subdirectories found.
4053     my (@subdirs) = ();
4055     @suffix_list = split (/,/, $SOURCE_SUFFIXES);
4057     opendir (SRCDIR, $source_dir)
4058         || die "Can't open source directory $source_dir: $!";
4060     foreach $file (readdir (SRCDIR)) {
4061       if ($file =~ /^\./) {
4062         next;
4063       } elsif (-d "$source_dir/$file") {
4064         push (@subdirs, $file);
4065       } elsif (@suffix_list) {
4066         foreach $suffix (@suffix_list) {
4067           if ($file =~ m/\.\Q${suffix}\E$/) {
4068             &ScanSourceFile ("$source_dir/$file");
4069           }
4070         }
4071       } elsif ($file =~ m/\.[ch]$/) {
4072         &ScanSourceFile ("$source_dir/$file");
4073       }
4074     }
4075     closedir (SRCDIR);
4077     # Now recursively scan the subdirectories.
4078     foreach $dir (@subdirs) {
4079         &ReadSourceDocumentation ("$source_dir/$dir");
4080     }
4084 #############################################################################
4085 # Function    : ScanSourceFile
4086 # Description : Scans one source file looking for specially-formatted comment
4087 #                blocks. Later &MergeSourceDocumentation is used to merge any
4088 #                documentation found with the documentation already read in
4089 #                from the template files.
4091 # Arguments   : $file - the file to scan.
4092 #############################################################################
4094 sub ScanSourceFile {
4095     my ($file) = @_;
4096     my $basename;
4098     # prepend entries from @SOURCE_DIR
4099     for my $dir (@SOURCE_DIRS) {
4100         # Check if the filename is in the ignore list.
4101         if ($file =~ m%^\Q$dir\E/(.*)$% and $IGNORE_FILES =~ m/(\s|^)\Q$1\E(\s|$)/) {
4102             @TRACE@("Skipping source file: $file");
4103             return;
4104         }
4105     }
4107     if ($file =~ m/^.*[\/\\]([^\/\\]*)$/) {
4108         $basename = $1;
4109     } else {
4110         &LogWarning ($file, 1, "Can't find basename for this filename.");
4111         $basename = $file;
4112     }
4114     # Check if the basename is in the list of files to ignore.
4115     if ($IGNORE_FILES =~ m/(\s|^)\Q${basename}\E(\s|$)/) {
4116         @TRACE@("Skipping source file: $file");
4117         return;
4118     }
4120     @TRACE@("Scanning source file: $file");
4122     open (SRCFILE, $file)
4123         || die "Can't open $file: $!";
4124     my $in_comment_block = 0;
4125     my $symbol;
4126     my $in_part = "";
4127     my ($description, $return_desc);
4128     my ($since_desc, $stability_desc, $deprecated_desc);
4129     my $current_param;
4130     my @params;
4131     while (<SRCFILE>) {
4132         # Look for the start of a comment block.
4133         if (!$in_comment_block) {
4134             if (m%^\s*/\*.*\*/%) {
4135                 #one-line comment - not gtkdoc
4136             } elsif (m%^\s*/\*\*\s%) {
4137                 @TRACE@("Found comment block start\n");
4139                 $in_comment_block = 1;
4141                 # Reset all the symbol data.
4142                 $symbol = "";
4143                 $in_part = "";
4144                 $description = "";
4145                 $return_desc = "";
4146                 $since_desc = "";
4147                 $deprecated_desc = "";
4148                 $stability_desc = "";
4149                 $current_param = -1;
4150                 @params = ();
4151             }
4152             next;
4153         }
4155         # We're in a comment block. Check if we've found the end of it.
4156         if (m%^\s*\*+/%) {
4157             if (!$symbol) {
4158                 # maybe its not even meant to be a gtk-doc comment?
4159                 &LogWarning ($file, $., "Symbol name not found at the start of the comment block.");
4160             } else {
4161                 # Add the return value description onto the end of the params.
4162                 if ($return_desc) {
4163                     # TODO(ensonic): check for duplicated Return docs
4164                     # &LogWarning ($file, $., "Multiple Returns for $symbol.");
4165                     push (@params, "Returns");
4166                     push (@params, $return_desc);
4167                 }
4168                 # Convert special characters
4169                 $description = &ConvertSGMLChars ($symbol, $description);
4170                 my $k;
4171                 for ($k = 1; $k <= $#params; $k += $PARAM_FIELD_COUNT) {
4172                     $params[$k] = &ConvertSGMLChars ($symbol, $params[$k]);
4173                 }
4175                 # Handle Section docs
4176                 if ($symbol =~ m/SECTION:\s*(.*)/) {
4177                     my $real_symbol=$1;
4178                     my $key;
4180                     if (scalar %KnownSymbols) {
4181                         if ((! defined($KnownSymbols{"$TMPL_DIR/$real_symbol:Long_Description"})) || $KnownSymbols{"$TMPL_DIR/$real_symbol:Long_Description"} != 1) {
4182                             &LogWarning ($file, $., "Section $real_symbol is not defined in the $MODULE-sections.txt file.");
4183                         }
4184                     }
4186                     @TRACE@("SECTION DOCS found in source for : '$real_symbol'\n");
4187                     for ($k = 0; $k <= $#params; $k += $PARAM_FIELD_COUNT) {
4188                         @TRACE@("   '".$params[$k]."'\n");
4189                         $params[$k] = "\L$params[$k]";
4190                         undef $key;
4191                         if ($params[$k] eq "short_description") {
4192                             $key = "$TMPL_DIR/$real_symbol:Short_Description";
4193                         } elsif ($params[$k] eq "see_also") {
4194                             $key = "$TMPL_DIR/$real_symbol:See_Also";
4195                         } elsif ($params[$k] eq "title") {
4196                             $key = "$TMPL_DIR/$real_symbol:Title";
4197                         } elsif ($params[$k] eq "stability") {
4198                             $key = "$TMPL_DIR/$real_symbol:Stability_Level";
4199                         } elsif ($params[$k] eq "section_id") {
4200                             $key = "$TMPL_DIR/$real_symbol:Section_Id";
4201                         } elsif ($params[$k] eq "include") {
4202                             $key = "$TMPL_DIR/$real_symbol:Include";
4203                         } elsif ($params[$k] eq "image") {
4204                             $key = "$TMPL_DIR/$real_symbol:Image";
4205                         }
4206                         if (defined($key)) {
4207                             $SourceSymbolDocs{$key}=$params[$k+1];
4208                             $SourceSymbolSourceFile{$key} = $file;
4209                             $SourceSymbolSourceLine{$key} = $.;
4210                         }
4211                     }
4212                     $SourceSymbolDocs{"$TMPL_DIR/$real_symbol:Long_Description"}=$description;
4213                     $SourceSymbolSourceFile{"$TMPL_DIR/$real_symbol:Long_Description"} = $file;
4214                     $SourceSymbolSourceLine{"$TMPL_DIR/$real_symbol:Long_Description"} = $.;
4215                     #$SourceSymbolTypes{$symbol} = "SECTION";
4216                 } elsif ($symbol =~ m/PROGRAM:\s*(.*)/) {
4217                     my $real_symbol = $1;
4218                     my $key;
4219                     my $section_id;
4221                     @TRACE@("PROGRAM DOCS found in source for '$real_symbol'\n");
4222                     for ($k = 0; $k <= $#params; $k += $PARAM_FIELD_COUNT) {
4223                         @TRACE@("   '".$params[$k]."'\n");
4224                         $params[$k] = "\L$params[$k]";
4225                         undef $key;
4227                         if ($params[$k] eq "short_description") {
4228                             $key = "$TMPL_DIR/$real_symbol:Short_Description";
4229                         } elsif ($params[$k] eq "see_also") {
4230                             $key = "$TMPL_DIR/$real_symbol:See_Also";
4231                         } elsif ($params[$k] eq "section_id") {
4232                             $key = "$TMPL_DIR/$real_symbol:Section_Id";
4233                         } elsif ($params[$k] eq "synopsis") {
4234                             $key = "$TMPL_DIR/$real_symbol:Synopsis";
4235                         } elsif ($params[$k] eq "returns") {
4236                             $key = "$TMPL_DIR/$real_symbol:Returns";
4237                         } elsif ($params[$k] =~ m/^(-.*)/) {
4238                             $key = "$TMPL_DIR/$real_symbol:Options";
4239                             my $opts;
4240                             if (defined($SourceSymbolDocs{$key})) {
4241                                 $opts = $SourceSymbolDocs{$key};
4242                             } else {
4243                                 $opts = [];
4244                             }
4245                             push (@{ $opts }, $1);
4246                             push (@{ $opts }, $params[$k+1]);
4248                             $SourceSymbolDocs{$key} = $opts;
4249                             next;
4250                         }
4251                         if (defined($key)) {
4252                             $SourceSymbolDocs{$key}=$params[$k+1];
4253                             $SourceSymbolSourceFile{$key} = $file;
4254                             $SourceSymbolSourceLine{$key} = $.;
4255                         }
4256                     }
4257                     $SourceSymbolDocs{"$TMPL_DIR/$real_symbol:Long_Description"}=$description;
4258                     $SourceSymbolSourceFile{"$TMPL_DIR/$real_symbol:Long_Description"} = $file;
4259                     $SourceSymbolSourceLine{"$TMPL_DIR/$real_symbol:Long_Description"} = $.;
4261                     $section_id = $SourceSymbolDocs{"$TMPL_DIR/$real_symbol:Section_Id"};
4262                     if (defined ($section_id) && $section_id !~ m/^\s*$/) {
4263                         # Remove trailing blanks and use as is
4264                         $section_id =~ s/\s+$//;
4265                     } else {
4266                         $section_id = &CreateValidSGMLID ("$MODULE-$real_symbol");
4267                     }
4269                     &OutputProgramDBFile ($real_symbol, $section_id);
4271                 } else {
4272                     @TRACE@("SYMBOL DOCS found in source for : '$symbol' ",length($description), "\n");
4273                     $SourceSymbolDocs{$symbol} = $description;
4274                     $SourceSymbolParams{$symbol} = [ @params ];
4275                     # FIXME $SourceSymbolTypes{$symbol} = "STRUCT,SIGNAL,ARG,FUNCTION,MACRO";
4276                     #if (defined $DeclarationTypes{$symbol}) {
4277                     #    $SourceSymbolTypes{$symbol} = $DeclarationTypes{$symbol}
4278                     #}
4279                     $SourceSymbolSourceFile{$symbol} = $file;
4280                     $SourceSymbolSourceLine{$symbol} = $.;
4281                 }
4283                 if ($since_desc) {
4284                      ($since_desc, my @extra_lines) = split ("\n", $since_desc);
4285                      $since_desc =~ s/^\s+//;
4286                      $since_desc =~ s/\s+$//;
4287                      @TRACE@("Since($symbol) : [$since_desc]\n");
4288                      $Since{$symbol} = &ConvertSGMLChars ($symbol, $since_desc);
4289                      if(scalar @extra_lines) {
4290                          &LogWarning ($file, $., "multi-line since docs found");
4291                      }
4292                 }
4294                 if ($stability_desc) {
4295                     $stability_desc = &ParseStabilityLevel($stability_desc, $file, $., "Stability level for $symbol");
4296                     $StabilityLevel{$symbol} = &ConvertSGMLChars ($symbol, $stability_desc);
4297                 }
4299                 if ($deprecated_desc) {
4300                     if (!exists $Deprecated{$symbol}) {
4301                          # don't warn for signals and properties
4302                          #if ($symbol !~ m/::?(.*)/) {
4303                          if (defined $DeclarationTypes{$symbol}) {
4304                              &LogWarning ($file, $.,
4305                                  "$symbol is deprecated in the inline comments, but no deprecation guards were found around the declaration.".
4306                                  " (See the --deprecated-guards option for gtkdoc-scan.)");
4307                          }
4308                     }
4309                     $Deprecated{$symbol} = &ConvertSGMLChars ($symbol, $deprecated_desc);
4310                 }
4311             }
4313             $in_comment_block = 0;
4314             next;
4315         }
4317         # Get rid of ' * ' at start of every line in the comment block.
4318         s%^\s*\*\s?%%;
4319         # But make sure we don't get rid of the newline at the end.
4320         if (!$_) {
4321             $_ = "\n";
4322         }
4323         @TRACE@("scanning :$_");
4325         # If we haven't found the symbol name yet, look for it.
4326         if (!$symbol) {
4327             if (m%^\s*(SECTION:\s*\S+)%) {
4328                 $symbol = $1;
4329                 @TRACE@("SECTION DOCS found in source for : '$symbol'\n");
4330             } elsif (m%^\s*(PROGRAM:\s*\S+)%) {
4331                 $symbol = $1;
4332                 @TRACE@("PROGRAM DOCS found in source for : '$symbol'\n");
4333             } elsif (m%^\s*([\w:-]*\w)\s*:?\s*(\([-A-Za-z0-9._() ]+?\)\s*)*$%) {
4334                 $symbol = $1;
4335                 my $annotation = $2;
4336                 @TRACE@("SYMBOL DOCS found in source for : '$symbol'\n");
4337                 if (defined($annotation)) {
4338                     chomp($annotation);
4339                     if ($annotation ne "") {
4340                         $SymbolAnnotations{$symbol} = $annotation;
4341                         @TRACE@("remaining text for $symbol: '$annotation'\n");
4342                     }
4343                 }
4344             }
4345             next;
4346         }
4348         if ($in_part eq "description") {
4349             # Get rid of 'Description:'
4350             s%^\s*Description:%%;
4351         }
4353         if (m%^\s*(returns|return\s+value):%i) {
4354             # we're in param section and have not seen the blank line
4355             if($in_part ne "") {
4356               $return_desc = $';
4357               $in_part = "return";
4358               next;
4359             }
4360         } elsif (m%^\s*since:%i) {
4361             # we're in param section and have not seen the blank line
4362             if($in_part ne "param") {
4363               $since_desc = $';
4364               $in_part = "since";
4365               next;
4366             }
4367         } elsif (m%^\s*deprecated:%i) {
4368             # we're in param section and have not seen the blank line
4369             if($in_part ne "param") {
4370               $deprecated_desc = $';
4371               $in_part = "deprecated";
4372               next;
4373             }
4374         } elsif (m%^\s*stability:%i) {
4375             $stability_desc = $';
4376             $in_part = "stability";
4377             next;
4378         }
4380         if ($in_part eq "description") {
4381             $description .= $_;
4382             next;
4383         } elsif ($in_part eq "return") {
4384             $return_desc .= $_;
4385             next;
4386         } elsif ($in_part eq "since") {
4387             $since_desc .= $_;
4388             next;
4389         } elsif ($in_part eq "stability") {
4390             $stability_desc .= $_;
4391             next;
4392         } elsif ($in_part eq "deprecated") {
4393             $deprecated_desc .= $_;
4394             next;
4395         }
4397         # We must be in the parameters. Check for the empty line below them.
4398         if (m%^\s*$%) {
4399             $in_part = "description";
4400             next;
4401         }
4403         # Look for a parameter name.
4404         if (m%^\s*@(.+?)\s*:\s*%) {
4405             my $param_name = $1;
4406             my $param_desc = $';
4408             @TRACE@("Found parameter: $param_name\n");
4409             # Allow varargs variations
4410             if ($param_name =~ m/^\.\.\.$/) {
4411                 $param_name = "...";
4412             }
4413             @TRACE@("Found param for symbol $symbol : '$param_name'= '$_'");
4415             push (@params, $param_name);
4416             push (@params, $param_desc);
4417             $current_param += $PARAM_FIELD_COUNT;
4418             $in_part = "param";
4419             next;
4420         } elsif ($in_part eq "") {
4421             @TRACE@("continuation for $symbol annotation '$_'");
4422             my $annotation = $_;
4423             $annotation =~ s/^\s+|\s+$//g ;
4424             $SymbolAnnotations{$symbol} .= $annotation;
4425             next;
4426         }
4428         # We must be in the middle of a parameter description, so add it on
4429         # to the last element in @params.
4430         if ($current_param == -1) {
4431             &LogWarning ($file, $., "Parsing comment block file : parameter expected, but got '$_'");
4432         } else {
4433             $params[$#params] .= $_;
4434         }
4435     }
4436     close (SRCFILE);
4439 #############################################################################
4440 # Function    : OutputMissingDocumentation
4441 # Description : Outputs report of documentation coverage to a file
4443 # Arguments   : none
4444 #############################################################################
4446 sub OutputMissingDocumentation {
4447     my $old_undocumented_file = "$ROOT_DIR/$MODULE-undocumented.txt";
4448     my $new_undocumented_file = "$ROOT_DIR/$MODULE-undocumented.new";
4450     my $n_documented = 0;
4451     my $n_incomplete = 0;
4452     my $total = 0;
4453     my $symbol;
4454     my $percent;
4455     my $msg;
4456     my $buffer = "";
4457     my $buffer_deprecated = "";
4458     my $buffer_descriptions = "";
4460     open(UNDOCUMENTED, ">$new_undocumented_file")
4461       || die "Can't create $new_undocumented_file";
4463     foreach $symbol (sort (keys (%AllSymbols))) {
4464         # FIXME: should we print LogWarnings for undocumented stuff?
4465         # DEBUG
4466         #my $ssfile = &GetSymbolSourceFile($symbol);
4467         #my $ssline = &GetSymbolSourceLine($symbol);
4468         #my $location = "defined at " . (defined($ssfile)?$ssfile:"?") . ":" . (defined($ssline)?$ssline:"0") . "\n";
4469         # DEBUG
4470         if ($symbol !~ /:(Title|Long_Description|Short_Description|See_Also|Stability_Level|Include|Section_Id|Image)/) {
4471             $total++;
4472             if (exists ($AllDocumentedSymbols{$symbol})) {
4473                 $n_documented++;
4474                 if (exists ($AllIncompleteSymbols{$symbol})) {
4475                     $n_incomplete++;
4476                     $buffer .= $symbol . " (" . $AllIncompleteSymbols{$symbol} . ")\n";
4477                     #$buffer .= "\t0: ".$location;
4478                 }
4479             } elsif (exists $Deprecated{$symbol}) {
4480                 if (exists ($AllIncompleteSymbols{$symbol})) {
4481                     $n_incomplete++;
4482                     $buffer_deprecated .= $symbol . " (" . $AllIncompleteSymbols{$symbol} . ")\n";
4483                     #$buffer .= "\t1a: ".$location;
4484                 } else {
4485                     $buffer_deprecated .= $symbol . "\n";
4486                     #$buffer .= "\t1b: ".$location;
4487                 }
4488             } else {
4489                 if (exists ($AllIncompleteSymbols{$symbol})) {
4490                     $n_incomplete++;
4491                     $buffer .= $symbol . " (" . $AllIncompleteSymbols{$symbol} . ")\n";
4492                     #$buffer .= "\t2a: ".$location;
4493                 } else {
4494                     $buffer .= $symbol . "\n";
4495                     #$buffer .= "\t2b: ".$location;
4496                 }
4497             }
4498         } elsif ($symbol =~ /:(Long_Description|Short_Description)/) {
4499             $total++;
4500             if (((exists ($SymbolDocs{$symbol})) && (length ($SymbolDocs{$symbol}) > 0))
4501             || ((exists ($AllDocumentedSymbols{$symbol})) && (length ($AllDocumentedSymbols{$symbol}) > 0))) {
4502               $n_documented++;
4503             } else {
4504               # cut off the leading namespace ($TMPL_DIR)
4505               $symbol =~ m/^.*\/(.*)$/;
4506               $buffer_descriptions .= $1 . "\n";
4507             }
4508         }
4509     }
4511     if ($total == 0) {
4512       $percent = 100;
4513     } else {
4514       $percent = ($n_documented / $total) * 100.0;
4515     }
4517     printf UNDOCUMENTED "%.0f%% symbol docs coverage.\n", $percent;
4518     print UNDOCUMENTED "$n_documented symbols documented.\n";
4519     print UNDOCUMENTED "$n_incomplete symbols incomplete.\n";
4520     print UNDOCUMENTED ($total - $n_documented) . " not documented.\n";
4522     if ($buffer_deprecated ne "") {
4523       $buffer .= "\n" . $buffer_deprecated;
4524     }
4525     if ($buffer_descriptions ne "") {
4526       $buffer .= "\n" . $buffer_descriptions;
4527     }
4528     if ($buffer ne "") {
4529       print UNDOCUMENTED "\n\n$buffer";
4530     }
4531     close (UNDOCUMENTED);
4533     return &UpdateFileIfChanged ($old_undocumented_file, $new_undocumented_file, 0);
4535     printf "%.0f%% symbol docs coverage", $percent;
4536     print "($n_documented symbols documented, $n_incomplete symbols incomplete, " . ($total - $n_documented) . " not documented)\n";
4537     print "See $MODULE-undocumented.txt for a list of missing docs.\nThe doc coverage percentage doesn't include intro sections.\n";
4541 #############################################################################
4542 # Function    : OutputUndeclaredSymbols
4543 # Description : Outputs symbols that are listed in the section file, but not
4544 #               declaration is found in the sources
4546 # Arguments   : none
4547 #############################################################################
4549 sub OutputUndeclaredSymbols {
4550     my $old_undeclared_file = "$ROOT_DIR/$MODULE-undeclared.txt";
4551     my $new_undeclared_file = "$ROOT_DIR/$MODULE-undeclared.new";
4553     open(UNDECLARED, ">$new_undeclared_file")
4554         || die "Can't create $new_undeclared_file";
4556     if (%UndeclaredSymbols) {
4557         print UNDECLARED (join("\n", sort keys %UndeclaredSymbols));
4558         print UNDECLARED "\n";
4559         print "See $MODULE-undeclared.txt for the list of undeclared symbols.\n"
4560     }
4561     close(UNDECLARED);
4563     return &UpdateFileIfChanged ($old_undeclared_file, $new_undeclared_file, 0);
4566 #############################################################################
4567 # Function    : OutputUnusedSymbols
4568 # Description : Outputs symbols that are documented in comments, but not
4569 #               declared in the sources
4571 # Arguments   : none
4572 #############################################################################
4574 sub OutputUnusedSymbols {
4575     my $num_unused = 0;
4576     my $old_unused_file = "$ROOT_DIR/$MODULE-unused.txt";
4577     my $new_unused_file = "$ROOT_DIR/$MODULE-unused.new";
4579     open (UNUSED, ">$new_unused_file")
4580         || die "Can't open $new_unused_file";
4581     my ($symbol);
4582     foreach $symbol (sort keys (%Declarations)) {
4583         if (!defined ($DeclarationOutput{$symbol})) {
4584             print (UNUSED "$symbol\n");
4585             $num_unused++;
4586         }
4587     }
4588     foreach $symbol (sort (keys (%AllUnusedSymbols))) {
4589         print (UNUSED "$symbol(" . $AllUnusedSymbols{$symbol} . ")\n");
4590         $num_unused++;
4591     }
4592     close (UNUSED);
4593     if ($num_unused != 0) {
4594         &LogWarning ($old_unused_file, 1, "$num_unused unused declarations.".
4595             "They should be added to $MODULE-sections.txt in the appropriate place.");
4596     }
4598     return &UpdateFileIfChanged ($old_unused_file, $new_unused_file, 0);
4602 #############################################################################
4603 # Function    : OutputAllSymbols
4604 # Description : Outputs list of all symbols to a file
4606 # Arguments   : none
4607 #############################################################################
4609 sub OutputAllSymbols {
4610      my $n_documented = 0;
4611      my $total = 0;
4612      my $symbol;
4613      my $percent;
4614      my $msg;
4616      open (SYMBOLS, ">$ROOT_DIR/$MODULE-symbols.txt")
4617           || die "Can't create $ROOT_DIR/$MODULE-symbols.txt: $!";
4619      foreach $symbol (sort (keys (%AllSymbols))) {
4620           print SYMBOLS $symbol . "\n";
4621      }
4623      close (SYMBOLS);
4626 #############################################################################
4627 # Function    : OutputSymbolsWithoutSince
4628 # Description : Outputs list of all symbols without a since tag to a file
4630 # Arguments   : none
4631 #############################################################################
4633 sub OutputSymbolsWithoutSince {
4634      my $n_documented = 0;
4635      my $total = 0;
4636      my $symbol;
4637      my $percent;
4638      my $msg;
4640      open (SYMBOLS, ">$ROOT_DIR/$MODULE-nosince.txt")
4641           || die "Can't create $ROOT_DIR/$MODULE-nosince.txt: $!";
4643      foreach $symbol (sort (keys (%SourceSymbolDocs))) {
4644          if (!defined $Since{$symbol}) {
4645              print SYMBOLS $symbol . "\n";
4646          }
4647      }
4649      close (SYMBOLS);
4653 #############################################################################
4654 # Function    : MergeSourceDocumentation
4655 # Description : This merges documentation read from a source file into the
4656 #                documentation read in from a template file.
4658 #                Parameter descriptions override any in the template files.
4659 #                Function descriptions are placed before any description from
4660 #                the template files.
4662 # Arguments   : none
4663 #############################################################################
4665 sub MergeSourceDocumentation {
4666     my $symbol;
4667     my @Symbols;
4669     if (scalar %SymbolDocs) {
4670         @Symbols=keys (%SymbolDocs);
4671         @TRACE@("num existing entries: ".(scalar @Symbols)."\n");
4672     }
4673     else {
4674         # filter scanned declarations, with what we suppress from -sections.txt
4675         my %tmp = ();
4676         foreach $symbol (keys (%Declarations)) {
4677             if (defined($KnownSymbols{$symbol}) && $KnownSymbols{$symbol} == 1) {
4678                 $tmp{$symbol}=1;
4679             }
4680         }
4681         # , add the rest from -sections.txt
4682         foreach $symbol (keys (%KnownSymbols)) {
4683             if ($KnownSymbols{$symbol} == 1) {
4684                 $tmp{$symbol}=1;
4685             }
4686         }
4687         # and add whats found in the source
4688         foreach $symbol (keys (%SourceSymbolDocs)) {
4689             $tmp{$symbol}=1;
4690         }
4691         @Symbols = keys (%tmp);
4692         @TRACE@("num source entries: ".(scalar @Symbols)."\n");
4693     }
4694     foreach $symbol (@Symbols) {
4695         $AllSymbols{$symbol} = 1;
4697         my $have_tmpl_docs = 0;
4699         ## see if the symbol is documented in template
4700         my $tmpl_doc = defined ($SymbolDocs{$symbol}) ? $SymbolDocs{$symbol} : "";
4701         my $check_tmpl_doc =$tmpl_doc;
4702         # remove all xml-tags and whitespaces
4703         $check_tmpl_doc =~ s/<.*?>//g;
4704         $check_tmpl_doc =~ s/\s//g;
4705         # anything left ?
4706         if ($check_tmpl_doc ne "") {
4707             $have_tmpl_docs = 1;
4708         } else {
4709             # if the docs have just an empty para, don't merge that.
4710             $check_tmpl_doc = $tmpl_doc;
4711             $check_tmpl_doc =~ s/(\s|\n)//msg;
4712             if ($check_tmpl_doc eq "<para></para>") {
4713                $tmpl_doc = "";
4714             }
4715         }
4717         if (exists ($SourceSymbolDocs{$symbol})) {
4718             my $type = $DeclarationTypes {$symbol};
4720             @TRACE@("merging [$symbol] from source\n");
4722             my $item = "Parameter";
4723             if (defined ($type)) {
4724                 if ($type eq 'STRUCT') {
4725                     $item = "Field";
4726                 } elsif ($type eq 'ENUM') {
4727                     $item = "Value";
4728                 } elsif ($type eq 'UNION') {
4729                     $item = "Field";
4730                 }
4731             } else {
4732                 $type="SIGNAL";
4733             }
4735             my $src_doc = $SourceSymbolDocs{$symbol};
4736             # remove leading and training whitespaces
4737             $src_doc =~ s/^\s+//;
4738             $src_doc =~ s/\s+$//;
4740             # Don't output warnings for overridden titles as titles are
4741             # automatically generated in the -sections.txt file, and thus they
4742             # are often overridden.
4743             if ($have_tmpl_docs && $symbol !~ m/:Title$/) {
4744                 # check if content is different
4745                 if ($tmpl_doc ne $src_doc) {
4746                     #print "[$tmpl_doc] [$src_doc]\n";
4747                     &LogWarning ($SourceSymbolSourceFile{$symbol}, $SourceSymbolSourceLine{$symbol},
4748                         "Documentation in template ".$SymbolSourceFile{$symbol}.":".$SymbolSourceLine{$symbol}." for $symbol being overridden by inline comments.");
4749                 }
4750             }
4752             if ($src_doc ne "") {
4753                  $AllDocumentedSymbols{$symbol} = 1;
4754             }
4756             # Do not add <para> to nothing, it breaks missing docs checks.
4757             my $src_doc_para = "";
4758             if ($src_doc ne "") {
4759                 $src_doc_para = $src_doc;
4760             }
4762             if ($symbol =~ m/$TMPL_DIR\/.+:Long_Description/) {
4763                 $SymbolDocs{$symbol} = "$src_doc_para$tmpl_doc";
4764             } elsif ($symbol =~ m/$TMPL_DIR\/.+:.+/) {
4765                 # For the title/summary/see also section docs we don't want to
4766                 # add any <para> tags.
4767                 $SymbolDocs{$symbol} = "$src_doc"
4768             } else {
4769                 $SymbolDocs{$symbol} = "$src_doc_para$tmpl_doc";
4770             }
4772             # merge parameters
4773             if ($symbol =~ m/.*::.*/) {
4774                 # For signals we prefer the param names from the source docs,
4775                 # since the ones from the templates are likely to contain the
4776                 # artificial argn names which are generated by gtkdoc-scangobj.
4777                 $SymbolParams{$symbol} = $SourceSymbolParams{$symbol};
4778                 # FIXME: we need to check for empty docs here as well!
4779             } else {
4780                 # The templates contain the definitive parameter names and order,
4781                 # so we will not change that. We only override the actual text.
4782                 my $tmpl_params = $SymbolParams{$symbol};
4783                 if (!defined ($tmpl_params)) {
4784                     @TRACE@("No merge needed for $symbol\n");
4785                     $SymbolParams{$symbol} = $SourceSymbolParams{$symbol};
4786                     #  FIXME: we still like to get the number of params and merge
4787                     #  1) we would noticed that params have been removed/renamed
4788                     #  2) we would catch undocumented params
4789                     #  params are not (yet) exported in -decl.txt so that we
4790                     #  could easily grab them :/
4791                 } else {
4792                     my $params = $SourceSymbolParams{$symbol};
4793                     my $j;
4794                     @TRACE@("Merge needed for $symbol, tmpl_params: ",$#$tmpl_params,", source_params: ",$#$params," \n");
4795                     for ($j = 0; $j <= $#$tmpl_params; $j += $PARAM_FIELD_COUNT) {
4796                         my $tmpl_param_name = $$tmpl_params[$j];
4798                         # Try to find the param in the source comment documentation.
4799                         my $found = 0;
4800                         my $k;
4801                         @TRACE@("  try merge param $tmpl_param_name\n");
4802                         for ($k = 0; $k <= $#$params; $k += $PARAM_FIELD_COUNT) {
4803                             my $param_name = $$params[$k];
4804                             my $param_desc = $$params[$k + 1];
4806                             @TRACE@("    test param  $param_name\n");
4807                             # We accept changes in case, since the Gnome source
4808                             # docs contain a lot of these.
4809                             if ("\L$param_name" eq "\L$tmpl_param_name") {
4810                                 $found = 1;
4812                                 # Override the description.
4813                                 $$tmpl_params[$j + 1] = $param_desc;
4815                                 # Set the name to "" to mark it as used.
4816                                 $$params[$k] = "";
4817                                 last;
4818                             }
4819                         }
4821                         # If it looks like the parameters are there, but not
4822                         # in the right place, try to explain a bit better.
4823                         if ((!$found) && ($src_doc =~ m/\@$tmpl_param_name:/)) {
4824                             &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4825                                 "Parameters for $symbol must start on the line immediately after the function or macro name.");
4826                         }
4827                     }
4829                     # Now we output a warning if parameters have been described which
4830                     # do not exist.
4831                     for ($j = 0; $j <= $#$params; $j += $PARAM_FIELD_COUNT) {
4832                         my $param_name = $$params[$j];
4833                         if ($param_name) {
4834                             # the template builder cannot detect if a macro returns
4835                             # a result or not
4836                             if(($type eq "MACRO") && ($param_name eq "Returns")) {
4837                                 # FIXME: do we need to add it then to tmpl_params[] ?
4838                                 my $num=$#$tmpl_params;
4839                                 @TRACE@("  adding Returns: to macro docs for $symbol.\n");
4840                                 $$tmpl_params[$num+1]="Returns";
4841                                 $$tmpl_params[$num+2]=$$params[$j+1];
4842                                 next;
4843                             }
4844                             &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4845                                 "$item described in source code comment block but does not exist. $type: $symbol $item: $param_name.");
4846                         }
4847                     }
4848                 }
4849             }
4850         } else {
4851             if ($have_tmpl_docs) {
4852                 $AllDocumentedSymbols{$symbol} = 1;
4853                 @TRACE@("merging [$symbol] from template\n");
4854             }
4855             else {
4856                 @TRACE@("[$symbol] undocumented\n");
4857             }
4858         }
4860         # if this symbol is documented, check if docs are complete
4861         $check_tmpl_doc = defined ($SymbolDocs{$symbol}) ? $SymbolDocs{$symbol} : "";
4862         # remove all xml-tags and whitespaces
4863         $check_tmpl_doc =~ s/<.*?>//g;
4864         $check_tmpl_doc =~ s/\s//g;
4865         if ($check_tmpl_doc ne "") {
4866             my $tmpl_params = $SymbolParams{$symbol};
4867             if (defined ($tmpl_params)) {
4868                 my $type = $DeclarationTypes {$symbol};
4870                 my $item = "Parameter";
4871                 if (defined ($type)) {
4872                     if ($type eq 'STRUCT') {
4873                         $item = "Field";
4874                     } elsif ($type eq 'ENUM') {
4875                         $item = "Value";
4876                     } elsif ($type eq 'UNION') {
4877                         $item = "Field";
4878                     }
4879                 } else {
4880                     $type="SIGNAL";
4881                 }
4883                 @TRACE@("Check param docs for $symbol, tmpl_params: ",$#$tmpl_params," entries, type=$type\n");
4885                 if ($#$tmpl_params > 0) {
4886                     my $j;
4887                     for ($j = 0; $j <= $#$tmpl_params; $j += $PARAM_FIELD_COUNT) {
4888                         # Output a warning if the parameter is empty and
4889                         # remember for stats.
4890                         my $tmpl_param_name = $$tmpl_params[$j];
4891                         my $tmpl_param_desc = $$tmpl_params[$j + 1];
4892                         if ($tmpl_param_name ne "void" && $tmpl_param_desc !~ m/\S/) {
4893                             if (exists ($AllIncompleteSymbols{$symbol})) {
4894                                 $AllIncompleteSymbols{$symbol}.=", ".$tmpl_param_name;
4895                             } else {
4896                                 $AllIncompleteSymbols{$symbol}=$tmpl_param_name;
4897                             }
4898                             &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4899                                 "$item description for $symbol"."::"."$tmpl_param_name is missing in source code comment block.");
4900                         }
4901                     }
4902                 }
4903                 else {
4904                     if ($#$tmpl_params == 0) {
4905                         $AllIncompleteSymbols{$symbol}="<items>";
4906                         &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
4907                             "$item descriptions for $symbol are missing in source code comment block.");
4908                     }
4909                     # $#$tmpl_params==-1 means we don't know about parameters
4910                     # this unfortunately does not tell if there should be some
4911                 }
4912             }
4913         }
4914    }
4915    @TRACE@("num doc entries: ".(scalar %SymbolDocs)."\n");
4918 #############################################################################
4919 # Function    : IsEmptyDoc
4920 # Description : Check if a doc-string is empty. Its also regarded as empty if
4921 #               it only consist of whitespace or e.g. FIXME.
4922 # Arguments   : the doc-string
4923 #############################################################################
4924 sub IsEmptyDoc {
4925     my ($doc) = @_;
4927     if ($doc =~ /^\s*$/) {
4928         return 1;
4929     }
4931     if ($doc =~ /^\s*<para>\s*(FIXME)?\s*<\/para>\s*$/) {
4932         return 1;
4933     }
4935     return 0;
4938 #############################################################################
4939 # Function    : ConvertMarkDown
4940 # Description : Converts mark down syntax to the respective docbook.
4941 #               http://de.wikipedia.org/wiki/Markdown
4942 #               Inspired by the design of ParseDown
4943 #               http://parsedown.org/
4944 #               Copyright (c) 2013 Emanuil Rusev, erusev.com
4945 # Arguments   : the symbol name, the doc-string
4946 #############################################################################
4948 sub ConvertMarkDown {
4949     my ($symbol, $text) = @_;
4951     $text = &MarkDownParse ($text, $symbol);
4953     return $text
4956 # SUPPORTED MARKDOWN
4957 # ==================
4959 # Atx-style Headers
4960 # -----------------
4962 # # Header 1
4964 # ## Header 2 ##
4966 # Setext-style Headers
4967 # --------------------
4969 # Header 1
4970 # ========
4972 # Header 2
4973 # --------
4975 # Ordered (unnested) Lists
4976 # ------------------------
4978 # 1. item 1
4980 # 1. item 2 with loooong
4981 #    description
4983 # 3. item 3
4985 # Note: we require a blank line above the list items
4988 # TODO(ensonic): it would be nice to add id parameters to the refsect2 elements
4990 sub MarkDownParseBlocks {
4991   my ($linesref, $symbol, $context) = @_;
4992   my $line;
4993   my @md_blocks = ();
4994   my $md_block = { type => "" };
4996  OUTER: foreach $line (@$linesref) {
4997     my $first_char = substr ($line, 0, 1);
4998     my $deindented_line;
5000     @TRACE@("in '".$md_block->{"type"}."' state, parsing '$line'");
5002     if ($md_block->{"type"} eq "markup") {
5003       if (!$md_block->{"closed"}) {
5004         if (index ($line, $md_block->{"start"}) != -1) {
5005           $md_block->{"depth"}++;
5006         }
5007         if (index ($line, $md_block->{"end"}) != -1) {
5008           if ($md_block->{"depth"} > 0) {
5009             $md_block->{"depth"}--;
5010           } else {
5011             @TRACE@("closing tag '$line'");
5012             $md_block->{"closed"} = 1;
5013             # TODO(ensonic): reparse inner text with MarkDownParseLines?
5014           }
5015         }
5016         $md_block->{"text"} .= "\n" . $line;
5017         @TRACE@("add to markup");
5018         next OUTER;
5019       }
5020     }
5022     $deindented_line = $line;
5023     $deindented_line =~ s/^\s+//;
5025     if ($md_block->{"type"} eq "heading") {
5026       # a heading is ended by any level less than or equal
5027       if ($md_block->{"level"} == 1) {
5028         if ($line =~ /^={4,}[ \t]*$/) {
5029           my $text = pop @{$md_block->{"lines"}};
5030           $md_block->{"interrupted"} = 0;
5031           push @md_blocks, $md_block;
5033           $md_block = { type => "heading",
5034                         text => $text,
5035                         lines => [],
5036                         level => 1 };
5037           next OUTER;
5038         } elsif ($line =~ /^[#][ \t]+(.+?)[ \t]*[#]*[ \t]*(?:{#([^}]+)})?[ \t]*$/) {
5039           $md_block->{"interrupted"} = 0;
5040           push @md_blocks, $md_block;
5042           $md_block = { type => "heading",
5043                         text => $1,
5044                         id => $2,
5045                         lines => [],
5046                         level => 1 };
5047           next OUTER;
5048         } else {
5049           # push lines into the block until the end is reached
5050           push @{$md_block->{"lines"}}, $line;
5051           next OUTER;
5052         }
5053       } else {
5054         if ($line =~ /^[=]{4,}[ \t]*$/) {
5055           my $text = pop @{$md_block->{"lines"}};
5056           $md_block->{"interrupted"} = 0;
5057           push @md_blocks, $md_block;
5059           $md_block = { type => "heading",
5060                         text => $text,
5061                         lines => [],
5062                         level => 1 };
5063           next OUTER;
5064         } elsif ($line =~ /^[-]{4,}[ \t]*$/) {
5065           my $text = pop @{$md_block->{"lines"}};
5066           $md_block->{"interrupted"} = 0;
5067           push @md_blocks, $md_block;
5069           $md_block = { type => "heading",
5070                         text => $text,
5071                         lines => [],
5072                         level => 2 };
5073           next OUTER;
5074         } elsif ($line =~ /^([#]{1,2})[ \t]+(.+?)[ \t]*[#]*[ \t]*(?:{#([^}]+)})?[ \t]*$/) {
5075           $md_block->{"interrupted"} = 0;
5076           push @md_blocks, $md_block;
5078           $md_block = { type => "heading",
5079                         text => $2,
5080                         id => $3,
5081                         lines => [],
5082                         level => length($1) };
5083           next OUTER;
5084         } else {
5085           # push lines into the block until the end is reached
5086           push @{$md_block->{"lines"}}, $line;
5087           next OUTER;
5088         }
5089       }
5090     } elsif ($md_block->{"type"} eq "code") {
5091       if ($line =~ /^[ \t]*\]\|(.*)/) {
5092         push @md_blocks, $md_block;
5093         $md_block = { type => "paragraph",
5094                       text => "$1",
5095                       lines => [] };
5096       } else {
5097         push @{$md_block->{"lines"}}, $line;
5098       }
5099       next OUTER;
5100     }
5102     if ($deindented_line eq "") {
5103       $md_block->{"interrupted"} = 1;
5104       next;
5105     }
5107     if ($md_block->{"type"} eq "quote") {
5108       if (!$md_block->{"interrupted"}) {
5109         $line =~ s/^[ ]*>[ ]?//;
5110         push @{$md_block->{"lines"}}, $line;
5111         next OUTER;
5112       }
5113     } elsif ($md_block->{"type"} eq "li") {
5114       my $marker = $md_block->{"marker"};
5115       if ($line =~ /^([ ]{0,3})($marker)[ ](.*)/) {
5116         my $indentation = $1;
5117         if ($md_block->{"indentation"} ne $indentation) {
5118           push @{$md_block->{"lines"}}, $line;
5119         } else {
5120           my $lines = $3;
5121           my $ordered = $md_block->{"ordered"};
5122           $lines =~ s/^[ ]{0,4}//;
5123           $md_block->{"last"} = 0;
5124           push @md_blocks, $md_block;
5125           $md_block = { type => "li",
5126                         ordered => $ordered,
5127                         indentation => $indentation,
5128                         marker => $marker,
5129                         first => 0,
5130                         last => 1,
5131                         lines => [ $lines ] };
5132         }
5133         next OUTER;
5134       }
5136       if ($md_block->{"interrupted"}) {
5137         if ($first_char eq " ") {
5138           push @{$md_block->{"lines"}}, "";
5139           $line =~ s/^[ ]{0,4}//;
5140           push @{$md_block->{"lines"}}, $line;
5141           $md_block->{"interrupted"} = 0;
5142           next OUTER;
5143         }
5144       } else {
5145         $line =~ s/^[ ]{0,4}//;
5146         push @{$md_block->{"lines"}}, $line;
5147         next OUTER;
5148       }
5149     }
5151     # indentation sensitive types
5152     @TRACE@("parsing '$line'");
5154     if ($line =~ /^([#]{1,2})[ \t]+(.+?)[ \t]*[#]*[ \t]*(?:{#([^}]+)})?[ \t]*$/) {
5155       # atx heading (#)
5156       push @md_blocks, $md_block;
5158       $md_block = { type => "heading",
5159                     text => $2,
5160                     id => $3,
5161                     lines => [],
5162                     level => length($1) };
5164       next OUTER;
5165     } elsif ($line =~ /^={4,}[ \t]*$/) {
5166       # setext heading (====)
5168       if ($md_block->{"type"} eq "paragraph" && $md_block->{"interrupted"}) {
5169         push @md_blocks, $md_block;
5170         $md_block->{"type"} = "heading";
5171         $md_block->{"lines"} = [];
5172         $md_block->{"level"} = 1;
5173       }
5175       next OUTER;
5176     } elsif ($line =~ /^-{4,}[ \t]*$/) {
5177       # setext heading (-----)
5179       if ($md_block->{"type"} eq "paragraph" && $md_block->{"interrupted"}) {
5180         push @md_blocks, $md_block;
5181         $md_block->{"type"} = "heading";
5182         $md_block->{"lines"} = [];
5183         $md_block->{"level"} = 2;
5184       }
5186       next OUTER;
5187     } elsif ($line =~ /^[ \t]*\|\[[ ]*(?:<!-- language="([^"]+?)" -->)?/) {
5188       # code
5189       $md_block->{"interrupted"} = 1;
5190       push @md_blocks, $md_block;
5191       $md_block = { type => "code",
5192                     language => $1,
5193                     lines => [] };
5194       next OUTER;
5195     }
5197     # indentation insensitive types
5198     if ($line =~ /^[ ]*<!DOCTYPE/) {
5199       push @md_blocks, $md_block;
5201       $md_block = { type   => "markup",
5202                     text   => $deindented_line,
5203                     start  => "<",
5204                     end    => ">",
5205                     closed => 0,
5206                     depth  => 0 };
5208     } elsif ($line =~ /^[ ]*<\??(\w+)[^>]*([\/\?])?[ \t]*>/) {
5209       # markup, including <?xml version="1.0"?>
5210       my $tag = $1;
5211       my $is_self_closing = defined($2);
5213       # skip link markdown
5214       # TODO(ensonic): consider adding more uri schemes (ftp, ...)
5215       if ($tag =~ /^https?/) {
5216         @TRACE@("skipping link '$tag'");
5217       } else {
5218         # for TEXT_LEVEL_ELEMENTS, we want to keep them as-is in the paragraph
5219         # instead of creation a markdown block.
5220         my $scanning_for_end_of_text_level_tag = (
5221             $md_block->{"type"} eq "paragraph" &&
5222             defined($md_block->{"start"}) &&
5223             !$md_block->{"closed"});
5224         @TRACE@("markup found '$tag', scanning $scanning_for_end_of_text_level_tag ?");
5225         if (!$MD_TEXT_LEVEL_ELEMENTS{$tag} && !$scanning_for_end_of_text_level_tag) {
5226           push @md_blocks, $md_block;
5228           if ($is_self_closing) {
5229             @TRACE@("self-closing docbook '$tag'");
5230             $md_block = { type => "self-closing tag",
5231                           text => $deindented_line };
5232             $is_self_closing = 0;
5233             next OUTER;
5234           }
5236           @TRACE@("new markup '$tag'");
5237           $md_block = { type   => "markup",
5238                         text   => $deindented_line,
5239                         start  => "<" . $tag . ">",
5240                         end    => "</" . $tag . ">",
5241                         closed => 0,
5242                         depth  => 0 };
5243           if ($deindented_line =~ /<\/$tag>/) {
5244             $md_block->{"closed"} = 1;
5245           }
5246           next OUTER;
5247         } else {
5248           if ($MD_TEXT_LEVEL_ELEMENTS{$tag}) {
5249             @TRACE@("text level docbook '$tag' in '".$md_block->{"type"}."' state");
5250             # TODO(ensonic): handle nesting
5251             if (!$scanning_for_end_of_text_level_tag) {
5252               if ($deindented_line !~ /<\/$tag>/) {
5253                 @TRACE@("new text level markup '$tag'");
5254                 $md_block->{"start"} = "<" . $tag . ">";
5255                 $md_block->{"end"} = "</" . $tag . ">";
5256                 $md_block->{"closed"} = 0;
5257                 @TRACE@("scanning for end of '$tag'");
5258               }
5259             } else {
5260               if ($deindented_line =~ /$md_block->{"end"}/) {
5261                 $md_block->{"closed"} = 1;
5262                 @TRACE@("found end of '$tag'");
5263               }
5264             }
5265           }
5266         }
5267       }
5268     } elsif ($line =~ /^([ ]*)[*+-][ ](.*)/) {
5269       # li
5270       push @md_blocks, $md_block;
5271       my $lines = $2;
5272       my $indentation = $1;
5273       $lines =~ s/^[ ]{0,4}//;
5274       $md_block = { type => "li",
5275                     ordered => 0,
5276                     indentation => $indentation,
5277                     marker => "[*+-]",
5278                     first => 1,
5279                     last => 1,
5280                     lines => [ $lines ] };
5281       next OUTER;
5282     } elsif ($line =~ /^[ ]*>[ ]?(.*)/) {
5283       push @md_blocks, $md_block;
5284       $md_block = { type => "quote",
5285                     lines => [ $1 ] };
5286       next OUTER;
5287     }
5289     # list item
5290     if ($line =~ /^([ ]{0,4})\d+[.][ ]+(.*)/) {
5291       push @md_blocks, $md_block;
5292       my $lines = $2;
5293       my $indentation = $1;
5294       $lines =~ s/^[ ]{0,4}//;
5296       $md_block = { type => "li",
5297                     ordered => 1,
5298                     indentation => $indentation,
5299                     marker => "\\d+[.]",
5300                     first => 1,
5301                     last => 1,
5302                     lines => [ $lines ] };
5304       next;
5305     }
5307     # paragraph
5308     if ($md_block->{"type"} eq "paragraph") {
5309       if ($md_block->{"interrupted"}) {
5310         push @md_blocks, $md_block;
5311         $md_block = { type => "paragraph",
5312                       interrupted => 0,
5313                       text => $line };
5314         @TRACE@("new paragraph due to interrupted");
5315       } else {
5316         $md_block->{"text"} .= "\n" . $line;
5317         @TRACE@("add to paragraph");
5318       }
5319     } else {
5320       push @md_blocks, $md_block;
5321       $md_block = { type => "paragraph",
5322                     text => $line };
5323       @TRACE@("new paragraph due to different block type");
5324     }
5325   }
5327   push @md_blocks, $md_block;
5329   shift @md_blocks;
5331   return @md_blocks;
5334 sub MarkDownParseSpanElementsInner {
5335   my ($text, $markersref) = @_;
5336   my $markup = "";
5337   my %markers = map { $_ => 1 } @$markersref;
5339   while ($text ne "") {
5340     my $closest_marker = "";
5341     my $closest_marker_index = 0;
5342     my $closest_marker_position = -1;
5343     my $text_marker = "";
5344     my $i = 0;
5345     my $offset = 0;
5346     my @markers_rest;
5347     my $marker;
5348     my $use;
5350     while ( ($marker, $use) = each %markers ) {
5351       my $marker_position;
5353       if (!$use) {
5354         next;
5355       }
5357       $marker_position = index ($text, $marker);
5359       if ($marker_position < 0) {
5360         $markers{$marker} = 0;
5361         next;
5362       }
5364       if ($closest_marker eq "" || $marker_position < $closest_marker_position) {
5365         $closest_marker = $marker;
5366         $closest_marker_index = $i;
5367         $closest_marker_position = $marker_position;
5368       }
5369     }
5371     if ($closest_marker_position >= 0) {
5372       $text_marker = substr ($text, $closest_marker_position);
5373     }
5375     if ($text_marker eq "") {
5376       $markup .= $text;
5377       $text = "";
5378       next; # last
5379     }
5381     $markup .= substr ($text, 0, $closest_marker_position);
5382     $text = substr ($text, $closest_marker_position);
5383     @markers_rest = map { $markers{$_} ? ($_ eq $closest_marker ? () : $_) : () } keys %markers;
5385     if ($closest_marker eq "![" || $closest_marker eq "[") {
5386       my %element;
5388       if (index ($text, "]") && $text =~ /\[((?:[^][]|(?R))*)\]/) {
5389         my $remaining_text;
5391         %element = ( "!" => (substr ($text, 0, 1) eq "!"),
5392                      "a" => $1 );
5394         $offset = length ($&);
5395         if ($element{"!"}) {
5396           $offset++;
5397         }
5399         $remaining_text = substr ($text, $offset);
5400         if ($remaining_text =~ /^\([ ]*([^)'"]*?)(?:[ ]+['"](.+?)['"])?[ ]*\)/) {
5401           $element{"»"} = $1;
5402           if (defined ($2)) {
5403             $element{"#"} = $2;
5404           }
5405           $offset += length ($&);
5406         } elsif ($remaining_text =~ /^\s*\[([^\]<]*?)\]/) {
5407           $element{"ref"} = $1;
5408           $offset += length ($&);
5409         } else {
5410           undef %element;
5411         }
5412       }
5414       if (%element) {
5415         if ($element{"»"}) {
5416           $element{"»"} =~ s/&/&amp;/g;
5417           $element{"»"} =~ s/</&lt;/g;
5418         }
5419         if ($element{"!"}) {
5420           $markup .= "<inlinemediaobject><imageobject><imagedata fileref=\"" . $element{"»"} . "\"></imagedata></imageobject>";
5422           if (defined ($element{"a"})) {
5423             $markup .= "<textobject><phrase>" . $element{"a"} . "</phrase></textobject>";
5424           }
5426           $markup .= "</inlinemediaobject>";
5427         } elsif ($element{"ref"}) {
5428           $element{"a"} = &MarkDownParseSpanElementsInner ($element{"a"}, \@markers_rest);
5429           $markup .= "<link linkend=\"" . $element{"ref"} . "\"";
5431           if (defined ($element{"#"})) {
5432             # title attribute not supported
5433           }
5435           $markup .= ">" . $element{"a"} . "</link>";
5436         } else {
5437           $element{"a"} = &MarkDownParseSpanElementsInner ($element{"a"}, \@markers_rest);
5438           $markup .= "<ulink url=\"" . $element{"»"} . "\"";
5440           if (defined ($element{"#"})) {
5441             # title attribute not supported
5442           }
5444           $markup .= ">" . $element{"a"} . "</ulink>";
5445         }
5446       } else {
5447         $markup .= $closest_marker;
5448         if ($closest_marker eq "![") {
5449           $offset = 2;
5450         } else {
5451           $offset = 1;
5452         }
5453       }
5454     } elsif ($closest_marker eq "<") {
5455       if ($text =~ /^<(https?:[\/]{2}[^\s]+?)>/i) {
5456         my $element_url = $1;
5457         $element_url =~ s/&/&amp;/g;
5458         $element_url =~ s/</&lt;/g;
5460         $markup .= "<ulink url=\"" . $element_url . "\">" . $element_url . "</ulink>";
5461         $offset = length ($&);
5462       } elsif ($text =~ /^<([A-Za-z0-9._-]+?@[A-Za-z0-9._-]+?)>/) {
5463         $markup .= "<ulink url=\"mailto:" . $1 . "\">" . $1 . "</ulink>";
5464         $offset = length ($&);
5465       } elsif ($text =~ /^<[^>]+?>/) {
5466         $markup .= $&;
5467         $offset = length ($&);
5468       } else {
5469         $markup .= "&lt;";
5470         $offset = 1;
5471       }
5472     } elsif ($closest_marker eq "\\") {
5473       my $special_char = substr ($text, 1, 1);
5474       if ($MD_ESCAPABLE_CHARS{$special_char} ||
5475           $MD_GTK_ESCAPABLE_CHARS{$special_char}) {
5476         $markup .= $special_char;
5477         $offset = 2;
5478       } else {
5479         $markup .= "\\";
5480         $offset = 1;
5481       }
5482     } elsif ($closest_marker eq "`") {
5483       if ($text =~ /^(`+)([^`]+?)\1(?!`)/) {
5484         my $element_text = $2;
5485         $markup .= "<literal>" . $element_text . "</literal>";
5486         $offset = length ($&);
5487       } else {
5488         $markup .= "`";
5489         $offset = 1;
5490       }
5491     } elsif ($closest_marker eq "@") {
5492       # Convert '@param()'
5493       # FIXME: we could make those also links ($symbol.$2), but that would be less
5494       # useful as the link target is a few lines up or down
5495       if ($text =~ /^(\A|[^\\])\@(\w+((\.|->)\w+)*)\s*\(\)/) {
5496         $markup .= $1 . "<parameter>" . $2 . "()</parameter>\n";
5497         $offset = length ($&);
5498       } elsif ($text =~ /^(\A|[^\\])\@(\w+((\.|->)\w+)*)/) {
5499         # Convert '@param', but not '\@param'.
5500         $markup .= $1 . "<parameter>" . $2 . "</parameter>\n";
5501         $offset = length ($&);
5502       } elsif ($text =~ /^\\\@/) {
5503         $markup .= "\@";
5504         $offset = length ($&);
5505       } else {
5506         $markup .= "@";
5507         $offset = 1;
5508       }
5509     } elsif ($closest_marker eq "#") {
5510       if ($text =~ /^(\A|[^\\])#([\w\-:\.]+[\w]+)\s*\(\)/) {
5511         # handle #Object.func()
5512         $markup .= $1 . &MakeXRef ($2, &tagify ($2 . "()", "function"));
5513         $offset = length ($&);
5514       } elsif ($text =~ /^(\A|[^\\])#([\w\-:\.]+[\w]+)/) {
5515         # Convert '#symbol', but not '\#symbol'.
5516         $markup .= $1 . &MakeHashXRef ($2, "type");
5517         $offset = length ($&);
5518       } elsif ($text =~ /^\\#/) {
5519         $markup .= "#";
5520         $offset = length ($&);
5521       } else {
5522         $markup .= "#";
5523         $offset = 1;
5524       }
5525     } elsif ($closest_marker eq "%") {
5526       if ($text =~ /^(\A|[^\\])\%(-?\w+)/) {
5527         # Convert '%constant', but not '\%constant'.
5528         # Also allow negative numbers, e.g. %-1.
5529         $markup .= $1 . &MakeXRef ($2, &tagify ($2, "literal"));
5530         $offset = length ($&);
5531       } elsif ($text =~ /^\\%/) {
5532         $markup .= "\%";
5533         $offset = length ($&);
5534       } else {
5535         $markup .= "%";
5536         $offset = 1;
5537       }
5538     }
5540     if ($offset > 0) {
5541       $text = substr ($text, $offset);
5542     }
5543   }
5545   return $markup;
5548 sub MarkDownParseSpanElements {
5549   my ($text) = @_;
5550   my @markers = ( "\\", "<", "![", "[", "`", "%", "#", "@" );
5552   $text = &MarkDownParseSpanElementsInner ($text, \@markers);
5554   # Convert 'function()' or 'macro()'.
5555   # if there is abc_*_def() we don't want to make a link to _def()
5556   # FIXME: also handle abc(def(....)) : but that would need to be done recursively :/
5557   $text =~ s/([^\*.\w])(\w+)\s*\(\)/$1.&MakeXRef($2, &tagify($2 . "()", "function"));/eg;
5559   return $text;
5562 sub ReplaceEntities {
5563   my ($text, $symbol) = @_;
5564   my $warn = "";
5565   my @entities = ( [ "&lt;", "<" ],
5566                    [ "&gt;", ">" ],
5567                    [ "&ast;", "*" ],
5568                    [ "&num;", "#" ],
5569                    [ "&percnt;", "%"],
5570                    [ "&colon;", ":" ],
5571                    [ "&quot;", "\"" ],
5572                    [ "&apos;", "'" ],
5573                    [ "&nbsp;", " " ],
5574                    [ "&amp;", "&" ] ); # Do this last, or the others get messed up.
5575   my $i;
5577   # Expand entities in <programlisting> even inside CDATA since
5578   # we changed the definition of |[ to add CDATA
5579   for ($i = 0; $i <= $#entities; $i++) {
5580     $text =~ s/$entities[$i][0]/$entities[$i][1]/g;
5581   }
5583   return $text;
5586 sub MarkDownOutputDocBook {
5587   my ($blocksref, $symbol, $context) = @_;
5588   my $output = "";
5589   my $block;
5590   my @blocks = @$blocksref;
5592   foreach $block (@blocks) {
5593     my $text;
5594     my $title;
5596     #$output .= "\n<!-- beg type='" . $block->{"type"} . "'-->\n";
5598     if ($block->{"type"} eq "paragraph") {
5599       $text = &MarkDownParseSpanElements ($block->{"text"});
5600       if ($context eq "li" && $output eq "") {
5601         if ($block->{"interrupted"}) {
5602           $output .= "\n<para>$text</para>\n";
5603         } else {
5604           $output .= "<para>$text</para>";
5605           if ($#blocks > 0) {
5606             $output .= "\n";
5607           }
5608         }
5609       } else {
5610         $output .= "<para>$text</para>\n";
5611       }
5613     } elsif ($block->{"type"} eq "heading") {
5614       my $tag;
5616       $title = &MarkDownParseSpanElements ($block->{"text"});
5618       if ($block->{"level"} == 1) {
5619         $tag = "refsect2";
5620       } else {
5621         $tag = "refsect3";
5622       }
5624       $text = &MarkDownParseLines ($block->{"lines"}, $symbol, "heading");
5625       if (defined ($block->{"id"})) {
5626         $output .= "<$tag id=\"" . $block->{"id"} . "\">";
5627       } else {
5628         $output .= "<$tag>";
5629       }
5631       $output .= "<title>$title</title>$text</$tag>\n";
5632     } elsif ($block->{"type"} eq "li") {
5633       my $tag = "itemizedlist";
5635       if ($block->{"first"}) {
5636         if ($block->{"ordered"}) {
5637           $tag = "orderedlist";
5638         }
5639         $output .= "<$tag>\n";
5640       }
5642       if ($block->{"interrupted"}) {
5643         push @{$block->{"lines"}}, "";
5644       }
5646       $text = &MarkDownParseLines ($block->{"lines"}, $symbol, "li");
5647       $output .= "<listitem>".$text."</listitem>\n";
5648       if ($block->{"last"}) {
5649         if ($block->{"ordered"}) {
5650           $tag = "orderedlist";
5651         }
5652         $output .= "</$tag>\n";
5653       }
5654     } elsif ($block->{"type"} eq "quote") {
5655       $text = &MarkDownParseLines ($block->{"lines"}, $symbol, "quote");
5656       $output .= "<blockquote>\n$text</blockquote>\n";
5657     } elsif ($block->{"type"} eq "code") {
5658       my $tag = "programlisting";
5660       if ($block->{"language"}) {
5661         if ($block->{"language"} eq "plain") {
5662           $output .= "<informalexample><screen><![CDATA[\n";
5663           $tag = "screen";
5664         } else {
5665           $output .= "<informalexample><programlisting language=\"" . $block->{"language"} . "\"><![CDATA[\n";
5666         }
5667       } else {
5668         $output .= "<informalexample><programlisting><![CDATA[\n";
5669       }
5670       foreach (@{$block->{"lines"}}) {
5671         $output .= &ReplaceEntities ($_, $symbol) . "\n";
5672       }
5673       $output .= "]]></$tag></informalexample>\n";
5674     } elsif ($block->{"type"} eq "markup") {
5675       $text = &ExpandAbbreviations($symbol, $block->{"text"});
5676       $output .= $text."\n";
5677     } else {
5678       $output .= $block->{"text"}."\n";
5679     }
5680     #$output .= "\n<!-- end type='" . $block->{"type"} . "'-->\n";
5681   }
5683   return $output;
5686 sub MarkDownParseLines {
5687   my ($linesref, $symbol, $context) = @_;
5688   my $output;
5689   my @lines = @$linesref;
5690   my @blocks;
5692   @blocks = &MarkDownParseBlocks (\@lines, $symbol, $context);
5693   $output = &MarkDownOutputDocBook (\@blocks, $symbol, $context);
5695   return $output;
5698 sub MarkDownParse {
5699   my ($text, $symbol) = @_;
5700   my @lines;
5702   # take out some variability in line endings
5703   $text =~ s%\r\n%\n%g;
5704   $text =~ s%\r%\n%g;
5706   # split lines
5707   @lines = split("\n", $text);
5708   $text = MarkDownParseLines(\@lines, $symbol, "");
5710   return $text;
5713 #############################################################################
5714 # LIBRARY FUNCTIONS -        These functions are used in both gtkdoc-mkdb and
5715 #                        gtkdoc-mktmpl and should eventually be moved to a
5716 #                        separate library.
5717 #############################################################################
5719 #############################################################################
5720 # Function    : ReadDeclarationsFile
5721 # Description : This reads in a file containing the function/macro/enum etc.
5722 #                declarations.
5724 #                Note that in some cases there are several declarations with
5725 #                the same name, e.g. for conditional macros. In this case we
5726 #                set a flag in the %DeclarationConditional hash so the
5727 #                declaration is not shown in the docs.
5729 #                If a macro and a function have the same name, e.g. for
5730 #                gtk_object_ref, the function declaration takes precedence.
5732 #                Some opaque structs are just declared with 'typedef struct
5733 #                _name name;' in which case the declaration may be empty.
5734 #                The structure may have been found later in the header, so
5735 #                that overrides the empty declaration.
5737 # Arguments   : $file - the declarations file to read
5738 #                $override - if declarations in this file should override
5739 #                        any current declaration.
5740 #############################################################################
5742 sub ReadDeclarationsFile {
5743     my ($file, $override) = @_;
5745     if ($override == 0) {
5746         %Declarations = ();
5747         %DeclarationTypes = ();
5748         %DeclarationConditional = ();
5749         %DeclarationOutput = ();
5750     }
5752     open (INPUT, $file)
5753         || die "Can't open $file: $!";
5754     my $declaration_type = "";
5755     my $declaration_name;
5756     my $declaration;
5757     my $is_deprecated = 0;
5758     while (<INPUT>) {
5759         if (!$declaration_type) {
5760             if (m/^<([^>]+)>/) {
5761                 $declaration_type = $1;
5762                 $declaration_name = "";
5763                 @TRACE@("Found declaration: $declaration_type\n");
5764                 $declaration = "";
5765             }
5766         } else {
5767             if (m%^<NAME>(.*)</NAME>%) {
5768                 $declaration_name = $1;
5769             } elsif (m%^<DEPRECATED/>%) {
5770                 $is_deprecated = 1;
5771             } elsif (m%^</$declaration_type>%) {
5772                 @TRACE@("Found end of declaration: $declaration_name\n");
5773                 # Check that the declaration has a name
5774                 if ($declaration_name eq "") {
5775                     &LogWarning ($file, $., "$declaration_type has no name.\n");
5776                 }
5778                 # If the declaration is an empty typedef struct _XXX XXX
5779                 # set the flag to indicate the struct has a typedef.
5780                 if (($declaration_type eq 'STRUCT' || $declaration_type eq 'UNION')
5781                     && $declaration =~ m/^\s*$/) {
5782                     @TRACE@("Struct has typedef: $declaration_name\n");
5783                     $StructHasTypedef{$declaration_name} = 1;
5784                 }
5786                 # Check if the symbol is already defined.
5787                 if (defined ($Declarations{$declaration_name})
5788                     && $override == 0) {
5789                     # Function declarations take precedence.
5790                     if ($DeclarationTypes{$declaration_name} eq 'FUNCTION') {
5791                         # Ignore it.
5792                     } elsif ($declaration_type eq 'FUNCTION') {
5793                         if ($is_deprecated) {
5794                             $Deprecated{$declaration_name} = "";
5795                         }
5796                         $Declarations{$declaration_name} = $declaration;
5797                         $DeclarationTypes{$declaration_name} = $declaration_type;
5798                     } elsif ($DeclarationTypes{$declaration_name}
5799                               eq $declaration_type) {
5800                         # If the existing declaration is empty, or is just a
5801                         # forward declaration of a struct, override it.
5802                         if ($declaration_type eq 'STRUCT' || $declaration_type eq 'UNION') {
5803                             if ($Declarations{$declaration_name} =~ m/^\s*((struct|union)\s+\w+\s*;)?\s*$/) {
5804                                 if ($is_deprecated) {
5805                                     $Deprecated{$declaration_name} = "";
5806                                 }
5807                                 $Declarations{$declaration_name} = $declaration;
5808                             } elsif ($declaration =~ m/^\s*((struct|union)\s+\w+\s*;)?\s*$/) {
5809                                 # Ignore an empty or forward declaration.
5810                             } else {
5811                                 &LogWarning ($file, $., "Structure $declaration_name has multiple definitions.");
5812                             }
5813                         } else {
5814                             # set flag in %DeclarationConditional hash for
5815                             # multiply defined macros/typedefs.
5816                             $DeclarationConditional{$declaration_name} = 1;
5817                         }
5818                     } else {
5819                         &LogWarning ($file, $., "$declaration_name has multiple definitions.");
5820                     }
5821                 } else {
5822                     if ($is_deprecated) {
5823                         $Deprecated{$declaration_name} = "";
5824                     }
5825                     $Declarations{$declaration_name} = $declaration;
5826                     $DeclarationTypes{$declaration_name} = $declaration_type;
5827                 }
5829                 $declaration_type = "";
5830                 $is_deprecated = 0;
5831             } else {
5832                 $declaration .= $_;
5833             }
5834         }
5835     }
5836     close (INPUT);
5840 #############################################################################
5841 # Function    : ReadSignalsFile
5842 # Description : This reads in an existing file which contains information on
5843 #                all GTK signals. It creates the arrays @SignalNames and
5844 #                @SignalPrototypes containing info on the signals. The first
5845 #                line of the SignalPrototype is the return type of the signal
5846 #                handler. The remaining lines are the parameters passed to it.
5847 #                The last parameter, "gpointer user_data" is always the same
5848 #                so is not included.
5849 # Arguments   : $file - the file containing the signal handler prototype
5850 #                        information.
5851 #############################################################################
5853 sub ReadSignalsFile {
5854     my ($file) = @_;
5856     my $in_signal = 0;
5857     my $signal_object;
5858     my $signal_name;
5859     my $signal_returns;
5860     my $signal_flags;
5861     my $signal_prototype;
5863     # Reset the signal info.
5864     @SignalObjects = ();
5865     @SignalNames = ();
5866     @SignalReturns = ();
5867     @SignalFlags = ();
5868     @SignalPrototypes = ();
5870     if (! -f $file) {
5871         return;
5872     }
5873     if (!open (INPUT, $file)) {
5874         warn "Can't open $file - skipping signals\n";
5875         return;
5876     }
5877     while (<INPUT>) {
5878         if (!$in_signal) {
5879             if (m/^<SIGNAL>/) {
5880                 $in_signal = 1;
5881                 $signal_object = "";
5882                 $signal_name = "";
5883                 $signal_returns = "";
5884                 $signal_prototype = "";
5885             }
5886         } else {
5887             if (m/^<NAME>(.*)<\/NAME>/) {
5888                 $signal_name = $1;
5889                 if ($signal_name =~ m/^(.*)::(.*)$/) {
5890                     $signal_object = $1;
5891                     ($signal_name = $2) =~ s/_/-/g;
5892                     @TRACE@("Found signal: $signal_name\n");
5893                 } else {
5894                     &LogWarning ($file, $., "Invalid signal name: $signal_name.");
5895                 }
5896             } elsif (m/^<RETURNS>(.*)<\/RETURNS>/) {
5897                 $signal_returns = $1;
5898             } elsif (m/^<FLAGS>(.*)<\/FLAGS>/) {
5899                 $signal_flags = $1;
5900             } elsif (m%^</SIGNAL>%) {
5901                 @TRACE@("Found end of signal: ${signal_object}::${signal_name}\nReturns: ${signal_returns}\n${signal_prototype}");
5902                 push (@SignalObjects, $signal_object);
5903                 push (@SignalNames, $signal_name);
5904                 push (@SignalReturns, $signal_returns);
5905                 push (@SignalFlags, $signal_flags);
5906                 push (@SignalPrototypes, $signal_prototype);
5907                 $in_signal = 0;
5908             } else {
5909                 $signal_prototype .= $_;
5910             }
5911         }
5912     }
5913     close (INPUT);
5917 #############################################################################
5918 # Function    : ReadTemplateFile
5919 # Description : This reads in the manually-edited documentation file
5920 #               corresponding to the file currently being created, so we can
5921 #               insert the documentation at the appropriate places.
5922 #               It outputs %SymbolTypes, %SymbolDocs and %SymbolParams, which
5923 #               is a hash of arrays.
5924 #               NOTE: This function is duplicated in gtkdoc-mktmpl (but
5925 #               slightly different).
5926 # Arguments   : $docsfile - the template file to read in.
5927 #               $skip_unused_params - 1 if the unused parameters should be
5928 #                 skipped.
5929 #############################################################################
5931 sub ReadTemplateFile {
5932     my ($docsfile, $skip_unused_params) = @_;
5934     my $template = "$docsfile.sgml";
5935     if (! -f $template) {
5936         @TRACE@("File doesn't exist: $template\n");
5937         return 0;
5938     }
5940     # start with empty hashes, we merge the source comment for each file
5941     # afterwards
5942     %SymbolDocs = ();
5943     %SymbolTypes = ();
5944     %SymbolParams = ();
5946     my $current_type = "";        # Type of symbol being read.
5947     my $current_symbol = "";        # Name of symbol being read.
5948     my $symbol_doc = "";                # Description of symbol being read.
5949     my @params;                        # Parameter names and descriptions of current
5950                                 #   function/macro/function typedef.
5951     my $current_param = -1;        # Index of parameter currently being read.
5952                                 #   Note that the param array contains pairs
5953                                 #   of param name & description.
5954     my $in_unused_params = 0;        # True if we are reading in the unused params.
5955     my $in_deprecated = 0;
5956     my $in_since = 0;
5957     my $in_stability = 0;
5959     open (DOCS, "$template")
5960         || die "Can't open $template: $!";
5962     @TRACE@("reading template $template");
5964     while (<DOCS>) {
5965         if (m/^<!-- ##### ([A-Z_]+) (\S+) ##### -->/) {
5966             my $type = $1;
5967             my $symbol = $2;
5968             if ($symbol eq "Title"
5969                 || $symbol eq "Short_Description"
5970                 || $symbol eq "Long_Description"
5971                 || $symbol eq "See_Also"
5972                 || $symbol eq "Stability_Level"
5973                 || $symbol eq "Include"
5974                 || $symbol eq "Image") {
5976                 $symbol = $docsfile . ":" . $symbol;
5977             }
5979             @TRACE@("Found symbol: $symbol\n");
5980             # Remember file and line for the symbol
5981             $SymbolSourceFile{$symbol} = $template;
5982             $SymbolSourceLine{$symbol} = $.;
5984             # Store previous symbol, but remove any trailing blank lines.
5985             if ($current_symbol ne "") {
5986                 $symbol_doc =~ s/\s+$//;
5987                 $SymbolTypes{$current_symbol} = $current_type;
5988                 $SymbolDocs{$current_symbol} = $symbol_doc;
5990                 # Check that the stability level is valid.
5991                 if ($StabilityLevel{$current_symbol}) {
5992                     $StabilityLevel{$current_symbol} = &ParseStabilityLevel($StabilityLevel{$current_symbol}, $template, $., "Stability level for $current_symbol");
5993                 }
5995                 if ($current_param >= 0) {
5996                     $SymbolParams{$current_symbol} = [ @params ];
5997                 } else {
5998                     # Delete any existing params in case we are overriding a
5999                     # previously read template.
6000                     delete $SymbolParams{$current_symbol};
6001                 }
6002             }
6003             $current_type = $type;
6004             $current_symbol = $symbol;
6005             $current_param = -1;
6006             $in_unused_params = 0;
6007             $in_deprecated = 0;
6008             $in_since = 0;
6009             $in_stability = 0;
6010             $symbol_doc = "";
6011             @params = ();
6013         } elsif (m/^<!-- # Unused Parameters # -->/) {
6014             @TRACE@("Found unused parameters\n");
6015             $in_unused_params = 1;
6016             next;
6018         } elsif ($in_unused_params && $skip_unused_params) {
6019             # When outputting the DocBook we skip unused parameters.
6020             @TRACE@("Skipping unused param: $_");
6021             next;
6023         } else {
6024             # Check if param found. Need to handle "..." and "format...".
6025             if (s/^\@([\w\.]+):\040?//) {
6026                 my $param_name = $1;
6027                 my $param_desc = $_;
6028                 # Allow variations of 'Returns'
6029                 if ($param_name =~ m/^[Rr]eturns?$/) {
6030                     $param_name = "Returns";
6031                 }
6032                 # Allow varargs variations
6033                 if ($param_name =~ m/^.*\.\.\.$/) {
6034                     $param_name = "...";
6035                 }
6037                 # strip trailing whitespaces and blank lines
6038                 s/\s+\n$/\n/m;
6039                 s/\n+$/\n/sm;
6040                 @TRACE@("Found param for symbol $current_symbol : '$param_name'= '$_'");
6042                 if ($param_name eq "Deprecated") {
6043                     $in_deprecated = 1;
6044                     $Deprecated{$current_symbol} = $_;
6045                 } elsif ($param_name eq "Since") {
6046                     $in_since = 1;
6047                     chomp;
6048                     $Since{$current_symbol} = $_;
6049                 } elsif ($param_name eq "Stability") {
6050                     $in_stability = 1;
6051                     $StabilityLevel{$current_symbol} = $_;
6052                 } else {
6053                     push (@params, $param_name);
6054                     push (@params, $param_desc);
6055                     $current_param += $PARAM_FIELD_COUNT;
6056                 }
6057             } else {
6058                 # strip trailing whitespaces and blank lines
6059                 s/\s+\n$/\n/m;
6060                 s/\n+$/\n/sm;
6062                 if (!m/^\s+$/) {
6063                     if ($in_deprecated) {
6064                         $Deprecated{$current_symbol} .= $_;
6065                     } elsif ($in_since) {
6066                         &LogWarning ($template, $., "multi-line since docs found");
6067                         #$Since{$current_symbol} .= $_;
6068                     } elsif ($in_stability) {
6069                         $StabilityLevel{$current_symbol} .= $_;
6070                     } elsif ($current_param >= 0) {
6071                         $params[$current_param] .= $_;
6072                     } else {
6073                         $symbol_doc .= $_;
6074                     }
6075                 }
6076             }
6077         }
6078     }
6080     # Remember to finish the current symbol doccs.
6081     if ($current_symbol ne "") {
6083         $symbol_doc =~ s/\s+$//;
6084         $SymbolTypes{$current_symbol} = $current_type;
6085         $SymbolDocs{$current_symbol} = $symbol_doc;
6087         # Check that the stability level is valid.
6088         if ($StabilityLevel{$current_symbol}) {
6089             $StabilityLevel{$current_symbol} = &ParseStabilityLevel($StabilityLevel{$current_symbol}, $template, $., "Stability level for $current_symbol");
6090         }
6092         if ($current_param >= 0) {
6093             $SymbolParams{$current_symbol} = [ @params ];
6094         } else {
6095             # Delete any existing params in case we are overriding a
6096             # previously read template.
6097             delete $SymbolParams{$current_symbol};
6098         }
6099     }
6101     close (DOCS);
6102     return 1;
6106 #############################################################################
6107 # Function    : ReadObjectHierarchy
6108 # Description : This reads in the $MODULE-hierarchy.txt file containing all
6109 #               the GtkObject subclasses described in this module (and their
6110 #               ancestors).
6111 #               It places them in the @Objects array, and places their level
6112 #               in the object hierarchy in the @ObjectLevels array, at the
6113 #               same index. GtkObject, the root object, has a level of 1.
6115 #               This also generates tree_index.sgml as it goes along.
6117 # Arguments   : none
6118 #############################################################################
6120 sub ReadObjectHierarchy {
6121     @Objects = ();
6122     @ObjectLevels = ();
6124     if (! -f $OBJECT_TREE_FILE) {
6125         return;
6126     }
6127     if (!open (INPUT, $OBJECT_TREE_FILE)) {
6128         warn "Can't open $OBJECT_TREE_FILE - skipping object tree\n";
6129         return;
6130     }
6132     # Only emit objects if they are supposed to be documented, or if
6133     # they have documented children. To implement this, we maintain a
6134     # stack of pending objects which will be emitted if a documented
6135     # child turns up.
6136     my @pending_objects = ();
6137     my @pending_levels = ();
6138     my $root;
6139     my @tree = ();
6140     while (<INPUT>) {
6141         if (m/\S+/) {
6142             my $object = $&;
6143             my $level = (length($`)) / 2 + 1;
6144             my $xref = "";
6146             if ($level == 1) {
6147                 $root = $object;
6148             }
6150             while (($#pending_levels >= 0) && ($pending_levels[$#pending_levels] >= $level)) {
6151                 my $pobject = pop(@pending_objects);
6152                 my $plevel = pop(@pending_levels);
6153             }
6155             push (@pending_objects, $object);
6156             push (@pending_levels, $level);
6158             if (exists($KnownSymbols{$object})) {
6159                 while ($#pending_levels >= 0) {
6160                     $object = shift @pending_objects;
6161                     $level = shift @pending_levels;
6162                     $xref = &MakeXRef ($object);
6164                     push (@tree, ' ' x ($level * 4) . "$xref");
6165                     push (@Objects, $object);
6166                     push (@ObjectLevels, $level);
6167                     $ObjectRoots{$object} = $root;
6168                 }
6169             }
6170             #else {
6171             #    LogWarning ($OBJECT_TREE_FILE, $., "unknown type $object");
6172             #}
6173         }
6174     }
6175     close (INPUT);
6177     # FIXME: use xml
6178     # my $old_tree_index = "$DB_OUTPUT_DIR/tree_index.$xml";
6179     my $old_tree_index = "$DB_OUTPUT_DIR/tree_index.sgml";
6180     my $new_tree_index = "$DB_OUTPUT_DIR/tree_index.new";
6182     open (OUTPUT, ">$new_tree_index")
6183         || die "Can't create $new_tree_index: $!";
6185     print (OUTPUT &MakeDocHeader ("screen")."\n<screen>\n".&AddTreeLineArt(\@tree)."\n</screen>\n");
6186     close (OUTPUT);
6188     &UpdateFileIfChanged ($old_tree_index, $new_tree_index, 0);
6190     &OutputObjectList;
6193 #############################################################################
6194 # Function    : ReadInterfaces
6195 # Description : This reads in the $MODULE.interfaces file.
6197 # Arguments   : none
6198 #############################################################################
6200 sub ReadInterfaces {
6201     %Interfaces = ();
6203     if (! -f $INTERFACES_FILE) {
6204         return;
6205     }
6206     if (!open (INPUT, $INTERFACES_FILE)) {
6207         warn "Can't open $INTERFACES_FILE - skipping interfaces\n";
6208         return;
6209     }
6211     while (<INPUT>) {
6212        chomp;
6213        my ($object, @ifaces) = split;
6214        if (exists($KnownSymbols{$object}) && $KnownSymbols{$object} == 1) {
6215            my @knownIfaces = ();
6217            # filter out private interfaces, but leave foreign interfaces
6218            foreach my $iface (@ifaces) {
6219                if (!exists($KnownSymbols{$iface}) || $KnownSymbols{$iface} == 1) {
6220                    push (@knownIfaces, $iface);
6221                }
6222              }
6224            $Interfaces{$object} = join(' ', @knownIfaces);
6225            @TRACE@("Interfaces for $object: $Interfaces{$object}\n");
6226        } else {
6227          @TRACE@("skipping interfaces for unknown symbol: $object\n");
6228        }
6229     }
6230     close (INPUT);
6233 #############################################################################
6234 # Function    : ReadPrerequisites
6235 # Description : This reads in the $MODULE.prerequisites file.
6237 # Arguments   : none
6238 #############################################################################
6240 sub ReadPrerequisites {
6241     %Prerequisites = ();
6243     if (! -f $PREREQUISITES_FILE) {
6244         return;
6245     }
6246     if (!open (INPUT, $PREREQUISITES_FILE)) {
6247         warn "Can't open $PREREQUISITES_FILE - skipping prerequisites\n";
6248         return;
6249     }
6251     while (<INPUT>) {
6252        chomp;
6253        my ($iface, @prereqs) = split;
6254        if (exists($KnownSymbols{$iface}) && $KnownSymbols{$iface} == 1) {
6255            my @knownPrereqs = ();
6257            # filter out private prerequisites, but leave foreign prerequisites
6258            foreach my $prereq (@prereqs) {
6259                if (!exists($KnownSymbols{$prereq}) || $KnownSymbols{$prereq} == 1) {
6260                   push (@knownPrereqs, $prereq);
6261                }
6262            }
6264            $Prerequisites{$iface} = join(' ', @knownPrereqs);
6265        }
6266     }
6267     close (INPUT);
6270 #############################################################################
6271 # Function    : ReadArgsFile
6272 # Description : This reads in an existing file which contains information on
6273 #                all GTK args. It creates the arrays @ArgObjects, @ArgNames,
6274 #                @ArgTypes, @ArgFlags, @ArgNicks and @ArgBlurbs containing info
6275 #               on the args.
6276 # Arguments   : $file - the file containing the arg information.
6277 #############################################################################
6279 sub ReadArgsFile {
6280     my ($file) = @_;
6282     my $in_arg = 0;
6283     my $arg_object;
6284     my $arg_name;
6285     my $arg_type;
6286     my $arg_flags;
6287     my $arg_nick;
6288     my $arg_blurb;
6289     my $arg_default;
6290     my $arg_range;
6292     # Reset the args info.
6293     @ArgObjects = ();
6294     @ArgNames = ();
6295     @ArgTypes = ();
6296     @ArgFlags = ();
6297     @ArgNicks = ();
6298     @ArgBlurbs = ();
6299     @ArgDefaults = ();
6300     @ArgRanges = ();
6302     if (! -f $file) {
6303         return;
6304     }
6305     if (!open (INPUT, $file)) {
6306         warn "Can't open $file - skipping args\n";
6307         return;
6308     }
6309     while (<INPUT>) {
6310         if (!$in_arg) {
6311             if (m/^<ARG>/) {
6312                 $in_arg = 1;
6313                 $arg_object = "";
6314                 $arg_name = "";
6315                 $arg_type = "";
6316                 $arg_flags = "";
6317                 $arg_nick = "";
6318                 $arg_blurb = "";
6319                 $arg_default = "";
6320                 $arg_range = "";
6321             }
6322         } else {
6323             if (m/^<NAME>(.*)<\/NAME>/) {
6324                 $arg_name = $1;
6325                 if ($arg_name =~ m/^(.*)::(.*)$/) {
6326                     $arg_object = $1;
6327                     ($arg_name = $2) =~ s/_/-/g;
6328                     @TRACE@("Found arg: $arg_name\n");
6329                 } else {
6330                     &LogWarning ($file, $., "Invalid argument name: $arg_name");
6331                 }
6332             } elsif (m/^<TYPE>(.*)<\/TYPE>/) {
6333                 $arg_type = $1;
6334             } elsif (m/^<RANGE>(.*)<\/RANGE>/) {
6335                 $arg_range = $1;
6336             } elsif (m/^<FLAGS>(.*)<\/FLAGS>/) {
6337                 $arg_flags = $1;
6338             } elsif (m/^<NICK>(.*)<\/NICK>/) {
6339                 $arg_nick = $1;
6340             } elsif (m/^<BLURB>(.*)<\/BLURB>/) {
6341                 $arg_blurb = $1;
6342                 if ($arg_blurb eq "(null)") {
6343                   $arg_blurb = "";
6344                   &LogWarning ($file, $., "Property ${arg_object}:${arg_name} has no documentation.");
6345                 }
6346             } elsif (m/^<DEFAULT>(.*)<\/DEFAULT>/) {
6347                 $arg_default = $1;
6348             } elsif (m%^</ARG>%) {
6349                 @TRACE@("Found end of arg: ${arg_object}::${arg_name}\n${arg_type} : ${arg_flags}\n");
6350                 push (@ArgObjects, $arg_object);
6351                 push (@ArgNames, $arg_name);
6352                 push (@ArgTypes, $arg_type);
6353                 push (@ArgRanges, $arg_range);
6354                 push (@ArgFlags, $arg_flags);
6355                 push (@ArgNicks, $arg_nick);
6356                 push (@ArgBlurbs, $arg_blurb);
6357                 push (@ArgDefaults, $arg_default);
6358                 $in_arg = 0;
6359             }
6360         }
6361     }
6362     close (INPUT);
6365 #############################################################################
6366 # Function    : AddTreeLineArt
6367 # Description : Add unicode lineart to a pre-indented string array and returns
6368 #               it as as multiline string.
6369 # Arguments   : @tree - array of indented strings.
6370 #############################################################################
6372 sub AddTreeLineArt {
6373   my @tree = @{$_[0]};
6374   my $i;
6375   my $j;
6376   my $indent;
6378   # iterate bottom up over the tree
6379   for ($i = $#tree; $i >= 0; $i--) {
6380     # count leading spaces
6381     $tree[$i] =~ /^([^<A-Za-z]*)/;
6382     $indent = length( $1 );
6383     # replace with ╰───, if place of ╰ is not space insert ├
6384     if ($indent > 4) {
6385       if (substr($tree[$i],$indent-4,1) eq " ") {
6386         substr($tree[$i],$indent-4,4) = "--- ";
6387       } else {
6388         substr($tree[$i],$indent-4,4) = "+-- ";
6389       }
6390       # go lines up while space and insert |
6391       for ($j = $i - 1; ($j >= 0 && substr($tree[$j],$indent-4,1) eq ' '); $j--) {
6392         substr($tree[$j],$indent-4,1) = '|';
6393       }
6394     }
6395   }
6397   my $res = join("\n", @tree);
6398   # unicode chars for: ╰──
6399   $res =~ s%---%<phrase role=\"lineart\">&#9584;&#9472;&#9472;</phrase>%g;
6400   # unicde chars for: ├──
6401   $res =~ s%\+--%<phrase role=\"lineart\">&#9500;&#9472;&#9472;</phrase>%g;
6402   # unicode char for: │
6403   $res =~ s%\|%<phrase role=\"lineart\">&#9474;</phrase>%g;
6405   return $res;
6409 #############################################################################
6410 # Function    : CheckIsObject
6411 # Description : Returns 1 if the given name is a GObject or a subclass.
6412 #                It uses the global @Objects array.
6413 #                Note that the @Objects array only contains classes in the
6414 #                current module and their ancestors - not all GObject classes.
6415 # Arguments   : $name - the name to check.
6416 #############################################################################
6418 sub CheckIsObject {
6419     my ($name) = @_;
6420     my $root = $ObjectRoots{$name};
6421     # Let GBoxed pass as an object here to get -struct appended to the id
6422     # and prevent conflicts with sections.
6423     return (defined($root) and $root ne 'GEnum' and $root ne 'GFlags');
6427 #############################################################################
6428 # Function    : MakeReturnField
6429 # Description : Pads a string to $RETURN_TYPE_FIELD_WIDTH.
6430 # Arguments   : $str - the string to pad.
6431 #############################################################################
6433 sub MakeReturnField {
6434     my ($str) = @_;
6436     return $str . (' ' x ($RETURN_TYPE_FIELD_WIDTH - length ($str)));
6439 #############################################################################
6440 # Function    : GetSymbolSourceFile
6441 # Description : Get the filename where the symbol docs where taken from.
6442 # Arguments   : $symbol - the symbol name
6443 #############################################################################
6445 sub GetSymbolSourceFile {
6446     my ($symbol) = @_;
6448     if (defined($SourceSymbolSourceFile{$symbol})) {
6449         return $SourceSymbolSourceFile{$symbol};
6450     } elsif (defined($SymbolSourceFile{$symbol})) {
6451         return $SymbolSourceFile{$symbol};
6452     } else {
6453         return "";
6454     }
6457 #############################################################################
6458 # Function    : GetSymbolSourceLine
6459 # Description : Get the file line where the symbol docs where taken from.
6460 # Arguments   : $symbol - the symbol name
6461 #############################################################################
6463 sub GetSymbolSourceLine {
6464     my ($symbol) = @_;
6466     if (defined($SourceSymbolSourceLine{$symbol})) {
6467         return $SourceSymbolSourceLine{$symbol};
6468     } elsif (defined($SymbolSourceLine{$symbol})) {
6469         return $SymbolSourceLine{$symbol};
6470     } else {
6471         return 0;
6472     }